get_single_tissue_eqtls
Retrieve expression quantitative trait loci (eQTL) data for specific genes in individual human tissues from the GTEx dataset to analyze gene expression associations.
Instructions
Get single-tissue eQTL results for a gene
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| datasetId | No | GTEx dataset ID (default: gtex_v8) | gtex_v8 |
| gencodeId | Yes | GENCODE gene ID (e.g., ENSG00000223972.5) | |
| tissueSiteDetailId | Yes | Tissue site detail ID (e.g., Muscle_Skeletal, Brain_Cortex) |
Implementation Reference
- Main handler function that validates input arguments, calls the API client to fetch single tissue eQTL data, processes the results, and formats them into a structured markdown output for the MCP tool response.async getSingleTissueEQTLs(args: any) { if (!args.geneIds && !args.variantIds && !args.tissueIds) { throw new Error('At least one of geneIds, variantIds, or tissueIds must be provided'); } const result = await this.apiClient.getSingleTissueEQTLs({ gencodeId: args.geneIds, variantId: args.variantIds, tissueSiteDetailId: args.tissueIds, datasetId: args.datasetId || 'gtex_v8', page: args.page || 0, itemsPerPage: args.itemsPerPage || 250 }); if (result.error) { return { content: [{ type: "text", text: `Error retrieving single tissue eQTLs: ${result.error}` }], isError: true }; } const eqtls = result.data || []; if (eqtls.length === 0) { return { content: [{ type: "text", text: "No significant single tissue eQTLs found for the specified parameters." }] }; } let output = `**Single Tissue eQTLs (${eqtls.length} results)**\n`; output += `Dataset: ${eqtls[0]?.datasetId}\n\n`; // Group by gene or tissue depending on query if (args.geneIds && args.geneIds.length === 1) { // Single gene query - group by tissue const tissueGroups: { [key: string]: any[] } = {}; eqtls.forEach(eqtl => { if (!tissueGroups[eqtl.tissueSiteDetailId]) { tissueGroups[eqtl.tissueSiteDetailId] = []; } tissueGroups[eqtl.tissueSiteDetailId].push(eqtl); }); const geneName = eqtls[0]?.geneSymbol; output += `**Gene:** ${geneName} (${eqtls[0]?.gencodeId})\n\n`; Object.entries(tissueGroups).forEach(([tissueId, tissueEqtls]) => { const tissueDisplayName = this.getTissueDisplayName(tissueId); output += `### ${tissueDisplayName} (${tissueEqtls.length} eQTLs)\n`; // Sort by significance const sortedEqtls = tissueEqtls.sort((a, b) => a.pValue - b.pValue); const topEqtls = sortedEqtls.slice(0, 5); topEqtls.forEach((eqtl, index) => { output += `${index + 1}. **${eqtl.snpId}** (${eqtl.variantId})\n`; output += ` • Position: ${eqtl.chromosome}:${eqtl.pos.toLocaleString()}\n`; output += ` • p-value: ${eqtl.pValue.toExponential(2)}\n`; output += ` • NES: ${eqtl.nes.toFixed(3)}\n`; }); if (sortedEqtls.length > 5) { output += ` ... and ${sortedEqtls.length - 5} more eQTLs\n`; } output += '\n'; }); } else { // Multiple genes or other query types - group by gene const geneGroups: { [key: string]: any[] } = {}; eqtls.forEach(eqtl => { const key = `${eqtl.geneSymbol} (${eqtl.gencodeId})`; if (!geneGroups[key]) { geneGroups[key] = []; } geneGroups[key].push(eqtl); }); Object.entries(geneGroups).forEach(([geneKey, geneEqtls]) => { output += `### ${geneKey}\n`; // Group by tissue within gene const tissueGroups: { [key: string]: any[] } = {}; geneEqtls.forEach(eqtl => { if (!tissueGroups[eqtl.tissueSiteDetailId]) { tissueGroups[eqtl.tissueSiteDetailId] = []; } tissueGroups[eqtl.tissueSiteDetailId].push(eqtl); }); // Show most significant eQTL per tissue Object.entries(tissueGroups).forEach(([tissueId, tissueEqtls]) => { const tissueDisplayName = this.getTissueDisplayName(tissueId); const bestEqtl = tissueEqtls.sort((a, b) => a.pValue - b.pValue)[0]; output += ` **${tissueDisplayName}**: ${bestEqtl.snpId} (p=${bestEqtl.pValue.toExponential(2)}, NES=${bestEqtl.nes.toFixed(3)})`; if (tissueEqtls.length > 1) { output += ` + ${tissueEqtls.length - 1} more`; } output += '\n'; }); output += '\n'; }); } if (result.paging_info && result.paging_info.totalNumberOfItems > eqtls.length) { output += `**Note:** Showing ${eqtls.length} of ${result.paging_info.totalNumberOfItems} total results.\n`; } return { content: [{ type: "text", text: output.trim() }] }; }
- src/index.ts:250-270 (registration)Tool registration in the MCP server: defines the tool name, description, and input schema in the list of tools.name: "get_single_tissue_eqtls", description: "Get single-tissue eQTL results for a gene", inputSchema: { type: "object", properties: { gencodeId: { type: "string", description: "GENCODE gene ID (e.g., ENSG00000223972.5)" }, tissueSiteDetailId: { type: "string", description: "Tissue site detail ID (e.g., Muscle_Skeletal, Brain_Cortex)" }, datasetId: { type: "string", description: "GTEx dataset ID (default: gtex_v8)", default: "gtex_v8" } }, required: ["gencodeId", "tissueSiteDetailId"] }
- src/index.ts:678-683 (registration)Tool call routing in the MCP server request handler: maps the tool name to the associationHandlers.getSingleTissueEQTLs method.if (name === "get_single_tissue_eqtls") { return await associationHandlers.getSingleTissueEQTLs({ geneIds: args?.gencodeId ? [args.gencodeId] : undefined, tissueIds: args?.tissueSiteDetailId ? [args.tissueSiteDetailId] : undefined, datasetId: args?.datasetId });
- src/types/gtex-types.ts:409-416 (schema)TypeScript interface defining the parameters for the getSingleTissueEQTLs API call.export interface GetSingleTissueEQTLsParams { gencodeId?: string[]; variantId?: string[]; tissueSiteDetailId?: string[]; datasetId?: string; page?: number; itemsPerPage?: number; }
- src/utils/api-client.ts:480-498 (helper)API client method that makes the HTTP request to the GTEx Portal API for single tissue eQTL data, used by the handler.async getSingleTissueEQTLs(params: GetSingleTissueEQTLsParams): Promise<GTExApiResponse<SingleTissueEQTL[]>> { try { const queryParams = this.buildQueryParams({ gencodeId: params.gencodeId, variantId: params.variantId, tissueSiteDetailId: params.tissueSiteDetailId, datasetId: params.datasetId || 'gtex_v8', page: params.page || 0, itemsPerPage: params.itemsPerPage || 250 }); const response = await this.axiosInstance.get(`/association/singleTissueEqtl?${queryParams}`); return { data: response.data.data, paging_info: response.data.paging_info }; } catch (error) { return error as GTExApiResponse<SingleTissueEQTL[]>; } }