# Directory Structure ``` ├── .claude │ └── settings.local.json ├── .github │ └── workflows │ ├── claude-code-review.yml │ └── claude.yml ├── .gitignore ├── CLAUDE.md ├── example-claude-config.json ├── package.json ├── README.md ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Dependencies node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* # Build output build/ *.js *.js.map *.d.ts *.d.ts.map # Keep TypeScript source files !src/**/*.ts # IDE files .vscode/ .idea/ *.swp *.swo *~ .DS_Store # Environment files .env .env.local .env.development.local .env.test.local .env.production.local # Logs logs/ *.log # Testing coverage/ .nyc_output/ # Temporary files tmp/ temp/ .cache/ temp_config.json # Package manager files package-lock.json yarn.lock pnpm-lock.yaml ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # gnomAD MCP Server A Model Context Protocol (MCP) server that provides access to the gnomAD (Genome Aggregation Database) GraphQL API. This server enables AI assistants to query genetic variant data, gene constraints, and population genetics information from gnomAD. ## Features - 🧬 **Gene Information**: Search and retrieve detailed gene data including constraint scores - 🔬 **Variant Analysis**: Query specific variants and their population frequencies - 📊 **Population Genetics**: Access allele frequencies across different populations - 🧮 **Constraint Scores**: Get pLI, LOEUF, and other constraint metrics - 🔍 **Region Queries**: Find variants within specific genomic regions - 🧪 **Transcript Data**: Access transcript-specific information and constraints - 📈 **Coverage Data**: Retrieve sequencing coverage statistics - 🔄 **Structural Variants**: Query structural variant data - 🧲 **Mitochondrial Variants**: Access mitochondrial genome variants ## Installation ### Prerequisites - Node.js 18 or higher - npm or yarn ### Install from source ```bash git clone https://github.com/yourusername/gnomad-mcp-server.git cd gnomad-mcp-server npm install npm run build ``` ## Configuration ### Claude Desktop Add to your Claude Desktop configuration file: **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "gnomad": { "command": "node", "args": ["/path/to/gnomad-mcp-server/dist/index.js"] } } } ``` ### With MCP CLI ```bash npx @modelcontextprotocol/cli gnomad-mcp-server ``` ## Available Tools ### 1. `search` Search for genes, variants, or regions in gnomAD. **Parameters:** - `query` (required): Search query (gene symbol, gene ID, variant ID, rsID) - `reference_genome`: Reference genome (GRCh37 or GRCh38, default: GRCh38) - `dataset`: Dataset ID (gnomad_r4, gnomad_r3, gnomad_r2_1, etc., default: gnomad_r4) **Example:** ```json { "query": "TP53", "reference_genome": "GRCh38" } ``` ### 2. `get_gene` Get detailed information about a gene including constraint scores. **Parameters:** - `gene_id`: Ensembl gene ID (e.g., ENSG00000141510) - `gene_symbol`: Gene symbol (e.g., TP53) - `reference_genome`: Reference genome (default: GRCh38) **Example:** ```json { "gene_symbol": "BRCA1", "reference_genome": "GRCh38" } ``` ### 3. `get_variant` Get detailed information about a specific variant. **Parameters:** - `variant_id` (required): Variant ID in format: chr-pos-ref-alt (e ``` -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- ```markdown # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a gnomAD MCP (Model Context Protocol) server that provides access to the Genome Aggregation Database through the MCP protocol. The server allows LLMs and other tools to query genomic variant data from gnomAD. ## Development Commands Note: This appears to be a new repository. Common commands will be added as the project develops. ## Architecture This MCP server will likely follow the standard MCP server pattern: - Server implementation handling MCP protocol messages - Resource providers for gnomAD data access - Tool handlers for genomic queries - Configuration for database connections and API endpoints ## Key Considerations - Handle genomic coordinates and variant identifiers carefully - Ensure proper error handling for invalid genomic queries - Consider rate limiting and caching for gnomAD API calls - Follow genomic data standards (VCF, HGVS notation, etc.) ``` -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- ```json { "permissions": { "allow": [ "WebFetch(domain:broadinstitute.github.io)", "WebFetch(domain:gnomad.broadinstitute.org)" ], "deny": [], "ask": [] } } ``` -------------------------------------------------------------------------------- /example-claude-config.json: -------------------------------------------------------------------------------- ```json { "mcpServers": { "gnomad": { "command": "node", "args": ["/absolute/path/to/gnomad-mcp-server/dist/index.js"], "env": {} } }, "_comment": "Replace /absolute/path/to/ with the actual path to your gnomad-mcp-server installation" } ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "sourceMap": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "gnomad-mcp-server", "version": "1.0.0", "description": "Model Context Protocol server for gnomAD (Genome Aggregation Database) GraphQL API", "main": "dist/index.js", "type": "module", "bin": { "gnomad-mcp-server": "./dist/index.js" }, "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsx src/index.ts", "prepare": "npm run build" }, "keywords": [ "mcp", "gnomad", "genomics", "variants", "graphql", "bioinformatics" ], "author": "", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^0.5.0", "node-fetch": "^3.3.2" }, "devDependencies": { "@types/node": "^20.10.0", "tsx": "^4.7.0", "typescript": "^5.3.0" }, "engines": { "node": ">=18.0.0" } } ``` -------------------------------------------------------------------------------- /.github/workflows/claude.yml: -------------------------------------------------------------------------------- ```yaml name: Claude Code on: issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened, assigned] pull_request_review: types: [submitted] jobs: claude: if: | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 1 - name: Run Claude Code id: claude uses: anthropics/claude-code-action@beta with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # This is an optional setting that allows Claude to read CI results on PRs additional_permissions: | actions: read # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) # model: "claude-opus-4-1-20250805" # Optional: Customize the trigger phrase (default: @claude) # trigger_phrase: "/claude" # Optional: Trigger when specific user is assigned to an issue # assignee_trigger: "claude-bot" # Optional: Allow Claude to run specific commands # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" # Optional: Add custom instructions for Claude to customize its behavior for your project # custom_instructions: | # Follow our coding standards # Ensure all new code has tests # Use TypeScript for new files # Optional: Custom environment variables for Claude # claude_env: | # NODE_ENV: test ``` -------------------------------------------------------------------------------- /.github/workflows/claude-code-review.yml: -------------------------------------------------------------------------------- ```yaml name: Claude Code Review on: pull_request: types: [opened, synchronize] # Optional: Only run on specific file changes # paths: # - "src/**/*.ts" # - "src/**/*.tsx" # - "src/**/*.js" # - "src/**/*.jsx" jobs: claude-review: # Optional: Filter by PR author # if: | # github.event.pull_request.user.login == 'external-contributor' || # github.event.pull_request.user.login == 'new-developer' || # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 1 - name: Run Claude Code Review id: claude-review uses: anthropics/claude-code-action@beta with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) # model: "claude-opus-4-1-20250805" # Direct prompt for automated review (no @claude mention needed) direct_prompt: | Please review this pull request and provide feedback on: - Code quality and best practices - Potential bugs or issues - Performance considerations - Security concerns - Test coverage Be constructive and helpful in your feedback. # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR # use_sticky_comment: true # Optional: Customize review based on file types # direct_prompt: | # Review this PR focusing on: # - For TypeScript files: Type safety and proper interface usage # - For API endpoints: Security, input validation, and error handling # - For React components: Performance, accessibility, and best practices # - For tests: Coverage, edge cases, and test quality # Optional: Different prompts for different authors # direct_prompt: | # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} # Optional: Add specific tools for running tests or linting # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" # Optional: Skip review for certain conditions # if: | # !contains(github.event.pull_request.title, '[skip-review]') && # !contains(github.event.pull_request.title, '[WIP]') ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import fetch, { Response } from "node-fetch"; // gnomAD GraphQL API endpoint const GNOMAD_API_URL = "https://gnomad.broadinstitute.org/api"; // Type definitions for gnomAD responses interface GnomadResponse { data?: any; errors?: Array<{ message: string; extensions?: any; }>; } interface GeneConstraint { exp_lof: number; exp_mis: number; exp_syn: number; obs_lof: number; obs_mis: number; obs_syn: number; oe_lof: number; oe_lof_lower: number; oe_lof_upper: number; oe_mis: number; oe_mis_lower: number; oe_mis_upper: number; oe_syn: number; oe_syn_lower: number; oe_syn_upper: number; lof_z: number; mis_z: number; syn_z: number; pLI: number; } // Helper function to make GraphQL requests async function makeGraphQLRequest(query: string, variables: Record<string, any> = {}): Promise<GnomadResponse> { const response: Response = await fetch(GNOMAD_API_URL, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query, variables, }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as GnomadResponse; } // GraphQL query templates const QUERIES = { searchGene: ` query SearchGene($query: String!, $referenceGenome: ReferenceGenomeId!) { searchResults(query: $query, referenceGenome: $referenceGenome) { label value: url } } `, getGene: ` query GetGene($geneId: String, $geneSymbol: String, $referenceGenome: ReferenceGenomeId!) { gene(gene_id: $geneId, gene_symbol: $geneSymbol, reference_genome: $referenceGenome) { gene_id symbol name canonical_transcript_id hgnc_id omim_id chrom start stop strand gnomad_constraint { exp_lof exp_mis exp_syn obs_lof obs_mis obs_syn oe_lof oe_lof_lower oe_lof_upper oe_mis oe_mis_lower oe_mis_upper oe_syn oe_syn_lower oe_syn_upper lof_z mis_z syn_z pLI } transcripts { transcript_id transcript_version reference_genome } } } `, getVariant: ` query GetVariant($variantId: String!, $datasetId: DatasetId!) { variant(variantId: $variantId, dataset: $datasetId) { variant_id reference_genome chrom pos ref alt rsids caid colocated_variants multi_nucleotide_variants { combined_variant_id changes_amino_acids n_individuals other_constituent_snvs } exome { ac an ac_hemi ac_hom faf95 { popmax popmax_population } filters populations { id ac an ac_hemi ac_hom } } genome { ac an ac_hemi ac_hom faf95 { popmax popmax_population } filters populations { id ac an ac_hemi ac_hom } } transcript_consequences { gene_id gene_symbol transcript_id consequence_terms is_canonical major_consequence polyphen_prediction sift_prediction lof lof_filter lof_flags } } } `, getVariantsInGene: ` query GetVariantsInGene($geneId: String, $geneSymbol: String, $datasetId: DatasetId!, $referenceGenome: ReferenceGenomeId!) { gene(gene_id: $geneId, gene_symbol: $geneSymbol, reference_genome: $referenceGenome) { variants(dataset: $datasetId) { variant_id pos rsids consequence hgvsc hgvsp lof exome { ac an af filters } genome { ac an af filters } } } } `, getTranscript: ` query GetTranscript($transcriptId: String!, $referenceGenome: ReferenceGenomeId!) { transcript(transcript_id: $transcriptId, reference_genome: $referenceGenome) { transcript_id transcript_version reference_genome chrom start stop strand gene_id gene_symbol gene_version gnomad_constraint { exp_lof exp_mis exp_syn obs_lof obs_mis obs_syn oe_lof oe_lof_lower oe_lof_upper oe_mis oe_mis_lower oe_mis_upper oe_syn oe_syn_lower oe_syn_upper lof_z mis_z syn_z pLI } } } `, getRegionVariants: ` query GetRegionVariants($chrom: String!, $start: Int!, $stop: Int!, $datasetId: DatasetId!, $referenceGenome: ReferenceGenomeId!) { region(chrom: $chrom, start: $start, stop: $stop, reference_genome: $referenceGenome) { variants(dataset: $datasetId) { variant_id pos rsids consequence hgvsc hgvsp lof exome { ac an af filters } genome { ac an af filters } } } } `, getCoverage: ` query GetCoverage($geneId: String, $geneSymbol: String, $datasetId: DatasetId!, $referenceGenome: ReferenceGenomeId!) { gene(gene_id: $geneId, gene_symbol: $geneSymbol, reference_genome: $referenceGenome) { coverage(dataset: $datasetId) { exome { pos mean median over_1 over_5 over_10 over_15 over_20 over_25 over_30 over_50 over_100 } genome { pos mean median over_1 over_5 over_10 over_15 over_20 over_25 over_30 over_50 over_100 } } } } `, getStructuralVariants: ` query GetStructuralVariants($chrom: String!, $start: Int!, $stop: Int!, $datasetId: DatasetId!, $referenceGenome: ReferenceGenomeId!) { region(chrom: $chrom, start: $start, stop: $stop, reference_genome: $referenceGenome) { structural_variants(dataset: $datasetId) { variant_id chrom pos end length type alts ac an af homozygote_count hemizygote_count filters } } } `, getMitochondrialVariants: ` query GetMitochondrialVariants($datasetId: DatasetId!) { mitochondrial_variants(dataset: $datasetId) { variant_id pos ref alt rsids ac_het ac_hom an af_het af_hom max_heteroplasmy filters } } `, searchVariant: ` query SearchVariant($query: String!, $datasetId: DatasetId!, $referenceGenome: ReferenceGenomeId!) { searchResults(query: $query, referenceGenome: $referenceGenome, dataset: $datasetId) { label value: url } } ` }; // Create the MCP server const server = new Server( { name: "gnomad-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // Helper function to validate and parse dataset ID function parseDatasetId(dataset: string): string { const validDatasets = [ "gnomad_r2_1", "gnomad_r3", "gnomad_r4", "gnomad_sv_r2_1", "gnomad_sv_r4", "gnomad_cnv_r4", "exac", ]; const datasetLower = dataset.toLowerCase(); if (!validDatasets.includes(datasetLower)) { return "gnomad_r4"; // Default to latest version } return datasetLower; } // Helper function to validate reference genome function parseReferenceGenome(genome: string): string { const validGenomes = ["GRCh37", "GRCh38"]; if (!validGenomes.includes(genome)) { return "GRCh38"; // Default to GRCh38 } return genome; } // Register tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "search", description: "Search for genes, variants, or regions in gnomAD", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query (gene symbol, gene ID, variant ID, rsID, etc.)", }, reference_genome: { type: "string", description: "Reference genome (GRCh37 or GRCh38)", default: "GRCh38", }, dataset: { type: "string", description: "Dataset ID (gnomad_r4, gnomad_r3, gnomad_r2_1, etc.)", default: "gnomad_r4", }, }, required: ["query"], }, }, { name: "get_gene", description: "Get detailed information about a gene including constraint scores", inputSchema: { type: "object", properties: { gene_id: { type: "string", description: "Ensembl gene ID (e.g., ENSG00000141510)", }, gene_symbol: { type: "string", description: "Gene symbol (e.g., TP53)", }, reference_genome: { type: "string", description: "Reference genome (GRCh37 or GRCh38)", default: "GRCh38", }, }, }, }, { name: "get_variant", description: "Get detailed information about a specific variant", inputSchema: { type: "object", properties: { variant_id: { type: "string", description: "Variant ID in format: chr-pos-ref-alt (e.g., 1-55516888-G-A)", }, dataset: { type: "string", description: "Dataset ID (gnomad_r4, gnomad_r3, gnomad_r2_1, etc.)", default: "gnomad_r4", }, }, required: ["variant_id"], }, }, { name: "get_variants_in_gene", description: "Get all variants in a specific gene", inputSchema: { type: "object", properties: { gene_id: { type: "string", description: "Ensembl gene ID", }, gene_symbol: { type: "string", description: "Gene symbol", }, dataset: { type: "string", description: "Dataset ID", default: "gnomad_r4", }, reference_genome: { type: "string", description: "Reference genome", default: "GRCh38", }, }, }, }, { name: "get_transcript", description: "Get information about a specific transcript", inputSchema: { type: "object", properties: { transcript_id: { type: "string", description: "Ensembl transcript ID (e.g., ENST00000269305)", }, reference_genome: { type: "string", description: "Reference genome", default: "GRCh38", }, }, required: ["transcript_id"], }, }, { name: "get_region_variants", description: "Get variants in a specific genomic region", inputSchema: { type: "object", properties: { chrom: { type: "string", description: "Chromosome (1-22, X, Y)", }, start: { type: "number", description: "Start position", }, stop: { type: "number", description: "Stop position", }, dataset: { type: "string", description: "Dataset ID", default: "gnomad_r4", }, reference_genome: { type: "string", description: "Reference genome", default: "GRCh38", }, }, required: ["chrom", "start", "stop"], }, }, { name: "get_coverage", description: "Get coverage information for a gene", inputSchema: { type: "object", properties: { gene_id: { type: "string", description: "Ensembl gene ID", }, gene_symbol: { type: "string", description: "Gene symbol", }, dataset: { type: "string", description: "Dataset ID", default: "gnomad_r4", }, reference_genome: { type: "string", description: "Reference genome", default: "GRCh38", }, }, }, }, { name: "get_structural_variants", description: "Get structural variants in a genomic region", inputSchema: { type: "object", properties: { chrom: { type: "string", description: "Chromosome", }, start: { type: "number", description: "Start position", }, stop: { type: "number", description: "Stop position", }, dataset: { type: "string", description: "Dataset ID (gnomad_sv_r4, gnomad_sv_r2_1)", default: "gnomad_sv_r4", }, reference_genome: { type: "string", description: "Reference genome", default: "GRCh38", }, }, required: ["chrom", "start", "stop"], }, }, { name: "get_mitochondrial_variants", description: "Get mitochondrial variants", inputSchema: { type: "object", properties: { dataset: { type: "string", description: "Dataset ID", default: "gnomad_r3", }, }, }, }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; // Type guard for arguments if (!args || typeof args !== 'object') { throw new Error("Invalid arguments provided"); } try { let result: GnomadResponse; let formattedResult: any; switch (name) { case "search": result = await makeGraphQLRequest(QUERIES.searchGene, { query: args.query as string, referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.searchResults || []; break; case "get_gene": if (!args.gene_id && !args.gene_symbol) { throw new Error("Either gene_id or gene_symbol must be provided"); } result = await makeGraphQLRequest(QUERIES.getGene, { geneId: (args.gene_id as string) || null, geneSymbol: (args.gene_symbol as string) || null, referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.gene || null; break; case "get_variant": result = await makeGraphQLRequest(QUERIES.getVariant, { variantId: args.variant_id as string, datasetId: parseDatasetId((args.dataset as string) || "gnomad_r4"), }); formattedResult = result.data?.variant || null; break; case "get_variants_in_gene": if (!args.gene_id && !args.gene_symbol) { throw new Error("Either gene_id or gene_symbol must be provided"); } result = await makeGraphQLRequest(QUERIES.getVariantsInGene, { geneId: (args.gene_id as string) || null, geneSymbol: (args.gene_symbol as string) || null, datasetId: parseDatasetId((args.dataset as string) || "gnomad_r4"), referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.gene?.variants || []; break; case "get_transcript": result = await makeGraphQLRequest(QUERIES.getTranscript, { transcriptId: args.transcript_id as string, referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.transcript || null; break; case "get_region_variants": result = await makeGraphQLRequest(QUERIES.getRegionVariants, { chrom: String(args.chrom), start: parseInt(String(args.start)), stop: parseInt(String(args.stop)), datasetId: parseDatasetId((args.dataset as string) || "gnomad_r4"), referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.region?.variants || []; break; case "get_coverage": if (!args.gene_id && !args.gene_symbol) { throw new Error("Either gene_id or gene_symbol must be provided"); } result = await makeGraphQLRequest(QUERIES.getCoverage, { geneId: (args.gene_id as string) || null, geneSymbol: (args.gene_symbol as string) || null, datasetId: parseDatasetId((args.dataset as string) || "gnomad_r4"), referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.gene?.coverage || null; break; case "get_structural_variants": result = await makeGraphQLRequest(QUERIES.getStructuralVariants, { chrom: String(args.chrom), start: parseInt(String(args.start)), stop: parseInt(String(args.stop)), datasetId: parseDatasetId((args.dataset as string) || "gnomad_sv_r4"), referenceGenome: parseReferenceGenome((args.reference_genome as string) || "GRCh38"), }); formattedResult = result.data?.region?.structural_variants || []; break; case "get_mitochondrial_variants": result = await makeGraphQLRequest(QUERIES.getMitochondrialVariants, { datasetId: parseDatasetId((args.dataset as string) || "gnomad_r3"), }); formattedResult = result.data?.mitochondrial_variants || []; break; default: throw new Error(`Unknown tool: ${name}`); } // Check for GraphQL errors if (result.errors && result.errors.length > 0) { const errorMessages = result.errors.map(e => e.message).join("; "); throw new Error(`GraphQL errors: ${errorMessages}`); } return { content: [ { type: "text", text: JSON.stringify(formattedResult, null, 2), }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error: ${errorMessage}`, }, ], }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("gnomAD MCP server started"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); }); ```