Skip to main content
Glama
by shukwong
index.tsโ€ข20.6 kB
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); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/shukwong/gnomad-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server