agr-mcp-server 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js ADDED
@@ -0,0 +1,235 @@
1
+ import { SPECIES_MAP, SPECIES, } from "./types.js";
2
+ const AGR_API_URL = "https://www.alliancegenome.org/api";
3
+ const ALLIANCEMINE_URL = "https://www.alliancegenome.org/alliancemine/service";
4
+ export class AllianceClient {
5
+ agrApiUrl;
6
+ allianceMineUrl;
7
+ constructor(agrApiUrl, allianceMineUrl) {
8
+ this.agrApiUrl = agrApiUrl || AGR_API_URL;
9
+ this.allianceMineUrl = allianceMineUrl || ALLIANCEMINE_URL;
10
+ }
11
+ async fetch(url) {
12
+ const response = await fetch(url, {
13
+ headers: {
14
+ Accept: "application/json",
15
+ "Accept-Language": "en-US,en;q=0.9",
16
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
17
+ "Sec-Fetch-Dest": "empty",
18
+ "Sec-Fetch-Mode": "cors",
19
+ "Sec-Fetch-Site": "cross-site",
20
+ },
21
+ });
22
+ if (!response.ok) {
23
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
24
+ }
25
+ return response.json();
26
+ }
27
+ /**
28
+ * Search AGR using the main search API
29
+ */
30
+ async search(query, category, species, limit = 20) {
31
+ const params = new URLSearchParams({
32
+ q: query,
33
+ limit: String(limit),
34
+ });
35
+ if (category) {
36
+ params.set("category", category);
37
+ }
38
+ // Resolve species short name to full name
39
+ if (species) {
40
+ const speciesName = SPECIES_MAP[species.toLowerCase()] || species;
41
+ if (SPECIES.includes(speciesName)) {
42
+ params.set("species", speciesName);
43
+ }
44
+ }
45
+ const url = `${this.agrApiUrl}/search?${params.toString()}`;
46
+ try {
47
+ const response = await this.fetch(url);
48
+ return {
49
+ query,
50
+ results: response.results.map((r) => ({
51
+ id: r.id,
52
+ symbol: r.symbol,
53
+ name: r.name || r.name_key || r.id,
54
+ species: r.species?.name,
55
+ category: r.category,
56
+ description: r.highlights?.description?.[0],
57
+ highlights: r.highlights,
58
+ })),
59
+ total: response.total,
60
+ aggregations: response.aggregations,
61
+ };
62
+ }
63
+ catch (error) {
64
+ return { query, results: [], total: 0 };
65
+ }
66
+ }
67
+ /**
68
+ * Search using AllianceMine for more advanced queries
69
+ */
70
+ async searchMine(query, type, limit = 20) {
71
+ const params = new URLSearchParams({
72
+ q: query,
73
+ size: String(limit),
74
+ });
75
+ if (type) {
76
+ params.set(`facet_Category`, type);
77
+ }
78
+ const url = `${this.allianceMineUrl}/search?${params.toString()}`;
79
+ try {
80
+ const response = await this.fetch(url);
81
+ return {
82
+ query,
83
+ results: this.parseMineResults(response.results, limit),
84
+ total: response.totalHits,
85
+ };
86
+ }
87
+ catch (error) {
88
+ return { query, results: [], total: 0 };
89
+ }
90
+ }
91
+ parseMineResults(results, limit) {
92
+ return results.slice(0, limit).map((r) => ({
93
+ id: String(r.fields.primaryIdentifier || r.fields.secondaryIdentifier || r.id),
94
+ symbol: String(r.fields.symbol || ""),
95
+ name: String(r.fields.name || r.fields.symbol || r.fields.primaryIdentifier || ""),
96
+ species: String(r.fields["organism.name"] || ""),
97
+ category: r.type.toLowerCase(),
98
+ description: String(r.fields.briefDescription || r.fields.description || ""),
99
+ }));
100
+ }
101
+ /**
102
+ * Get detailed gene information
103
+ */
104
+ async getGene(geneId) {
105
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}`;
106
+ try {
107
+ const data = await this.fetch(url);
108
+ return data;
109
+ }
110
+ catch (error) {
111
+ return null;
112
+ }
113
+ }
114
+ /**
115
+ * Get disease associations for a gene
116
+ */
117
+ async getGeneDiseases(geneId, limit = 100) {
118
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/diseases?limit=${limit}`;
119
+ try {
120
+ const data = await this.fetch(url);
121
+ return data;
122
+ }
123
+ catch (error) {
124
+ return { results: [], total: 0 };
125
+ }
126
+ }
127
+ /**
128
+ * Search diseases
129
+ */
130
+ async searchDiseases(query, limit = 20) {
131
+ return this.search(query, "disease", undefined, limit);
132
+ }
133
+ /**
134
+ * Get expression data for a gene
135
+ */
136
+ async getGeneExpression(geneId, limit = 100) {
137
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/expression?limit=${limit}`;
138
+ try {
139
+ const data = await this.fetch(url);
140
+ return data;
141
+ }
142
+ catch (error) {
143
+ return { results: [], total: 0 };
144
+ }
145
+ }
146
+ /**
147
+ * Get orthologs for a gene
148
+ */
149
+ async getOrthologs(geneId) {
150
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/homologs`;
151
+ try {
152
+ const data = await this.fetch(url);
153
+ return data;
154
+ }
155
+ catch (error) {
156
+ return { results: [], total: 0 };
157
+ }
158
+ }
159
+ /**
160
+ * Get phenotypes for a gene
161
+ */
162
+ async getGenePhenotypes(geneId, limit = 100) {
163
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/phenotypes?limit=${limit}`;
164
+ try {
165
+ const data = await this.fetch(url);
166
+ return data;
167
+ }
168
+ catch (error) {
169
+ return { results: [], total: 0 };
170
+ }
171
+ }
172
+ /**
173
+ * Get interactions for a gene
174
+ */
175
+ async getGeneInteractions(geneId, limit = 100) {
176
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/interactions?limit=${limit}`;
177
+ try {
178
+ const data = await this.fetch(url);
179
+ return data;
180
+ }
181
+ catch (error) {
182
+ return { results: [], total: 0 };
183
+ }
184
+ }
185
+ /**
186
+ * Get alleles for a gene
187
+ */
188
+ async getGeneAlleles(geneId, limit = 100) {
189
+ const url = `${this.agrApiUrl}/gene/${encodeURIComponent(geneId)}/alleles?limit=${limit}`;
190
+ try {
191
+ const data = await this.fetch(url);
192
+ return data;
193
+ }
194
+ catch (error) {
195
+ return { results: [], total: 0 };
196
+ }
197
+ }
198
+ /**
199
+ * Search alleles/variants
200
+ */
201
+ async searchAlleles(query, limit = 20) {
202
+ return this.search(query, "allele", undefined, limit);
203
+ }
204
+ /**
205
+ * Get list of supported species
206
+ */
207
+ getSpeciesList() {
208
+ // Return hardcoded list of Alliance model organisms
209
+ const speciesList = [
210
+ { name: "Homo sapiens", shortName: "human" },
211
+ { name: "Mus musculus", shortName: "mouse" },
212
+ { name: "Rattus norvegicus", shortName: "rat" },
213
+ { name: "Danio rerio", shortName: "zebrafish" },
214
+ { name: "Drosophila melanogaster", shortName: "fly" },
215
+ { name: "Caenorhabditis elegans", shortName: "worm" },
216
+ { name: "Saccharomyces cerevisiae", shortName: "yeast" },
217
+ { name: "Xenopus laevis", shortName: "frog" },
218
+ { name: "Xenopus tropicalis", shortName: "xenopus" },
219
+ ];
220
+ return speciesList;
221
+ }
222
+ /**
223
+ * Helper to resolve species short name
224
+ */
225
+ resolveSpecies(input) {
226
+ const lower = input.toLowerCase();
227
+ if (SPECIES_MAP[lower]) {
228
+ return SPECIES_MAP[lower];
229
+ }
230
+ if (SPECIES.includes(input)) {
231
+ return input;
232
+ }
233
+ return null;
234
+ }
235
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { AllianceClient } from "./client.js";
6
+ import { ENTITY_TYPES } from "./types.js";
7
+ const client = new AllianceClient();
8
+ const server = new McpServer({
9
+ name: "agr-genomics",
10
+ version: "4.0.0",
11
+ });
12
+ // Tool: Search genes
13
+ server.tool("search_genes", "Search for genes across all Alliance of Genome Resources model organisms. Supports species filtering.", {
14
+ query: z
15
+ .string()
16
+ .describe("Search query - gene symbol (e.g., 'BRCA1', 'daf-2'), name, or keyword"),
17
+ species: z
18
+ .string()
19
+ .optional()
20
+ .describe("Filter by species: human, mouse, rat, zebrafish, fly, worm, yeast, frog, or full name like 'Homo sapiens'"),
21
+ limit: z
22
+ .number()
23
+ .optional()
24
+ .default(20)
25
+ .describe("Maximum number of results to return"),
26
+ }, async ({ query, species, limit }) => {
27
+ try {
28
+ const results = await client.search(query, "gene", species, limit);
29
+ return {
30
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
31
+ };
32
+ }
33
+ catch (error) {
34
+ return {
35
+ content: [{ type: "text", text: `Error searching genes: ${error}` }],
36
+ isError: true,
37
+ };
38
+ }
39
+ });
40
+ // Tool: Get Gene Info
41
+ server.tool("get_gene_info", "Get detailed information about a specific gene including symbol, name, location, species, and cross-references.", {
42
+ gene_id: z
43
+ .string()
44
+ .describe("Gene identifier - e.g., 'HGNC:1100' (human BRCA1), 'MGI:95892' (mouse), 'ZFIN:ZDB-GENE-990415-72'"),
45
+ }, async ({ gene_id }) => {
46
+ try {
47
+ const data = await client.getGene(gene_id);
48
+ if (!data) {
49
+ return {
50
+ content: [{ type: "text", text: `Gene not found: ${gene_id}` }],
51
+ isError: true,
52
+ };
53
+ }
54
+ return {
55
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
56
+ };
57
+ }
58
+ catch (error) {
59
+ return {
60
+ content: [{ type: "text", text: `Error fetching gene: ${error}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ // Tool: Get Gene Diseases
66
+ server.tool("get_gene_diseases", "Get disease associations for a gene, including human disease models and annotations.", {
67
+ gene_id: z.string().describe("Gene identifier"),
68
+ limit: z.number().optional().default(100).describe("Maximum results"),
69
+ }, async ({ gene_id, limit }) => {
70
+ try {
71
+ const data = await client.getGeneDiseases(gene_id, limit);
72
+ return {
73
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
74
+ };
75
+ }
76
+ catch (error) {
77
+ return {
78
+ content: [{ type: "text", text: `Error fetching diseases: ${error}` }],
79
+ isError: true,
80
+ };
81
+ }
82
+ });
83
+ // Tool: Search Diseases
84
+ server.tool("search_diseases", "Search for diseases in the Alliance database.", {
85
+ query: z
86
+ .string()
87
+ .describe("Disease name or keyword (e.g., 'breast cancer', 'diabetes')"),
88
+ limit: z.number().optional().default(20).describe("Maximum results"),
89
+ }, async ({ query, limit }) => {
90
+ try {
91
+ const results = await client.searchDiseases(query, limit);
92
+ return {
93
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
94
+ };
95
+ }
96
+ catch (error) {
97
+ return {
98
+ content: [
99
+ { type: "text", text: `Error searching diseases: ${error}` },
100
+ ],
101
+ isError: true,
102
+ };
103
+ }
104
+ });
105
+ // Tool: Get Gene Expression
106
+ server.tool("get_gene_expression", "Get expression data for a gene including tissue/cell type expression and developmental stages.", {
107
+ gene_id: z.string().describe("Gene identifier"),
108
+ limit: z.number().optional().default(100).describe("Maximum results"),
109
+ }, async ({ gene_id, limit }) => {
110
+ try {
111
+ const data = await client.getGeneExpression(gene_id, limit);
112
+ return {
113
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
114
+ };
115
+ }
116
+ catch (error) {
117
+ return {
118
+ content: [
119
+ { type: "text", text: `Error fetching expression: ${error}` },
120
+ ],
121
+ isError: true,
122
+ };
123
+ }
124
+ });
125
+ // Tool: Find Orthologs
126
+ server.tool("find_orthologs", "Find orthologous genes across species. Returns homologs from all Alliance model organisms.", {
127
+ gene_id: z.string().describe("Gene identifier"),
128
+ }, async ({ gene_id }) => {
129
+ try {
130
+ const data = await client.getOrthologs(gene_id);
131
+ return {
132
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
133
+ };
134
+ }
135
+ catch (error) {
136
+ return {
137
+ content: [
138
+ { type: "text", text: `Error fetching orthologs: ${error}` },
139
+ ],
140
+ isError: true,
141
+ };
142
+ }
143
+ });
144
+ // Tool: Get Gene Phenotypes
145
+ server.tool("get_gene_phenotypes", "Get phenotype annotations for a gene.", {
146
+ gene_id: z.string().describe("Gene identifier"),
147
+ limit: z.number().optional().default(100).describe("Maximum results"),
148
+ }, async ({ gene_id, limit }) => {
149
+ try {
150
+ const data = await client.getGenePhenotypes(gene_id, limit);
151
+ return {
152
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
153
+ };
154
+ }
155
+ catch (error) {
156
+ return {
157
+ content: [
158
+ { type: "text", text: `Error fetching phenotypes: ${error}` },
159
+ ],
160
+ isError: true,
161
+ };
162
+ }
163
+ });
164
+ // Tool: Get Gene Interactions
165
+ server.tool("get_gene_interactions", "Get molecular and genetic interactions for a gene.", {
166
+ gene_id: z.string().describe("Gene identifier"),
167
+ limit: z.number().optional().default(100).describe("Maximum results"),
168
+ }, async ({ gene_id, limit }) => {
169
+ try {
170
+ const data = await client.getGeneInteractions(gene_id, limit);
171
+ return {
172
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
173
+ };
174
+ }
175
+ catch (error) {
176
+ return {
177
+ content: [
178
+ { type: "text", text: `Error fetching interactions: ${error}` },
179
+ ],
180
+ isError: true,
181
+ };
182
+ }
183
+ });
184
+ // Tool: Get Gene Alleles
185
+ server.tool("get_gene_alleles", "Get alleles/variants associated with a gene.", {
186
+ gene_id: z.string().describe("Gene identifier"),
187
+ limit: z.number().optional().default(100).describe("Maximum results"),
188
+ }, async ({ gene_id, limit }) => {
189
+ try {
190
+ const data = await client.getGeneAlleles(gene_id, limit);
191
+ return {
192
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
193
+ };
194
+ }
195
+ catch (error) {
196
+ return {
197
+ content: [{ type: "text", text: `Error fetching alleles: ${error}` }],
198
+ isError: true,
199
+ };
200
+ }
201
+ });
202
+ // Tool: Search Alleles
203
+ server.tool("search_alleles", "Search for alleles/variants in the Alliance database.", {
204
+ query: z.string().describe("Allele name or keyword"),
205
+ limit: z.number().optional().default(20).describe("Maximum results"),
206
+ }, async ({ query, limit }) => {
207
+ try {
208
+ const results = await client.searchAlleles(query, limit);
209
+ return {
210
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
211
+ };
212
+ }
213
+ catch (error) {
214
+ return {
215
+ content: [{ type: "text", text: `Error searching alleles: ${error}` }],
216
+ isError: true,
217
+ };
218
+ }
219
+ });
220
+ // Tool: Get Species List
221
+ server.tool("get_species_list", "Get list of model organisms supported by Alliance of Genome Resources.", {}, async () => {
222
+ try {
223
+ const species = client.getSpeciesList();
224
+ return {
225
+ content: [
226
+ {
227
+ type: "text",
228
+ text: JSON.stringify({
229
+ description: "Model organisms in the Alliance of Genome Resources",
230
+ species,
231
+ }, null, 2),
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ catch (error) {
237
+ return {
238
+ content: [
239
+ { type: "text", text: `Error fetching species list: ${error}` },
240
+ ],
241
+ isError: true,
242
+ };
243
+ }
244
+ });
245
+ // Resource: Entity types
246
+ server.resource("entity-types", "agr://entity-types", async () => ({
247
+ contents: [
248
+ {
249
+ uri: "agr://entity-types",
250
+ mimeType: "application/json",
251
+ text: JSON.stringify({
252
+ description: "Entity types available in Alliance of Genome Resources",
253
+ types: ENTITY_TYPES.map((t) => ({
254
+ name: t,
255
+ description: getEntityDescription(t),
256
+ })),
257
+ }, null, 2),
258
+ },
259
+ ],
260
+ }));
261
+ // Resource: Species
262
+ server.resource("species", "agr://species", async () => ({
263
+ contents: [
264
+ {
265
+ uri: "agr://species",
266
+ mimeType: "application/json",
267
+ text: JSON.stringify({
268
+ description: "Model organisms in Alliance of Genome Resources",
269
+ species: client.getSpeciesList(),
270
+ }, null, 2),
271
+ },
272
+ ],
273
+ }));
274
+ function getEntityDescription(type) {
275
+ const descriptions = {
276
+ gene: "Genes across all model organisms",
277
+ allele: "Genetic variants and alleles",
278
+ disease: "Human diseases and disease models",
279
+ phenotype: "Observable characteristics and traits",
280
+ expression: "Gene expression data",
281
+ orthology: "Cross-species ortholog relationships",
282
+ interaction: "Molecular and genetic interactions",
283
+ variant: "Sequence variants",
284
+ transgenic_allele: "Transgenic constructs",
285
+ construct: "Molecular constructs",
286
+ affected_genomic_model: "Animal models with genetic modifications",
287
+ go_term: "Gene Ontology terms",
288
+ do_term: "Disease Ontology terms",
289
+ publication: "Scientific publications",
290
+ };
291
+ return descriptions[type] || `${type} entities`;
292
+ }
293
+ // Start server
294
+ async function main() {
295
+ const transport = new StdioServerTransport();
296
+ await server.connect(transport);
297
+ console.error("AGR Genomics MCP server running on stdio");
298
+ }
299
+ main().catch(console.error);
@@ -0,0 +1,167 @@
1
+ export declare const ENTITY_TYPES: readonly ["gene", "allele", "disease", "phenotype", "expression", "orthology", "interaction", "variant", "transgenic_allele", "construct", "affected_genomic_model", "go_term", "do_term", "publication"];
2
+ export type EntityType = (typeof ENTITY_TYPES)[number];
3
+ export declare const SPECIES: readonly ["Homo sapiens", "Mus musculus", "Rattus norvegicus", "Danio rerio", "Drosophila melanogaster", "Caenorhabditis elegans", "Saccharomyces cerevisiae", "Xenopus laevis", "Xenopus tropicalis"];
4
+ export type Species = (typeof SPECIES)[number];
5
+ export declare const SPECIES_MAP: Record<string, Species>;
6
+ export declare const GENE_ID_PREFIXES: readonly ["HGNC", "MGI", "RGD", "ZFIN", "FB", "WB", "SGD", "Xenbase"];
7
+ export interface SearchResult {
8
+ id: string;
9
+ symbol?: string;
10
+ name: string;
11
+ species?: string;
12
+ category: string;
13
+ description?: string;
14
+ highlights?: Record<string, string[]>;
15
+ }
16
+ export interface SearchResponse {
17
+ query: string;
18
+ results: SearchResult[];
19
+ total: number;
20
+ aggregations?: Record<string, unknown>;
21
+ }
22
+ export interface GeneBasicInfo {
23
+ id: string;
24
+ symbol: string;
25
+ name: string;
26
+ species: {
27
+ name: string;
28
+ commonNames?: string;
29
+ taxonId: string;
30
+ };
31
+ soTermName?: string;
32
+ geneSynopsis?: string;
33
+ geneSynopsisUrl?: string;
34
+ automatedGeneSynopsis?: string;
35
+ }
36
+ export interface GeneLocation {
37
+ chromosome: string;
38
+ start: number;
39
+ end: number;
40
+ strand?: string;
41
+ assembly?: string;
42
+ }
43
+ export interface CrossReference {
44
+ id: string;
45
+ displayName?: string;
46
+ globalCrossRefId?: string;
47
+ pages?: string[];
48
+ url?: string;
49
+ }
50
+ export interface GeneData extends GeneBasicInfo {
51
+ genomeLocations?: GeneLocation[];
52
+ crossReferences?: CrossReference[];
53
+ synonyms?: string[];
54
+ }
55
+ export interface DiseaseAnnotation {
56
+ id: string;
57
+ name: string;
58
+ doId?: string;
59
+ associationType?: string;
60
+ evidence?: string[];
61
+ publications?: PublicationRef[];
62
+ }
63
+ export interface DiseaseResponse {
64
+ results: DiseaseAnnotation[];
65
+ total: number;
66
+ }
67
+ export interface ExpressionAnnotation {
68
+ id: string;
69
+ assay?: string;
70
+ anatomicalStructure?: {
71
+ id: string;
72
+ name: string;
73
+ };
74
+ stage?: {
75
+ id: string;
76
+ name: string;
77
+ };
78
+ publications?: PublicationRef[];
79
+ }
80
+ export interface ExpressionResponse {
81
+ results: ExpressionAnnotation[];
82
+ total: number;
83
+ }
84
+ export interface Ortholog {
85
+ gene: {
86
+ id: string;
87
+ symbol: string;
88
+ species: {
89
+ name: string;
90
+ };
91
+ };
92
+ homologyType?: string;
93
+ methodCount?: number;
94
+ best?: boolean;
95
+ bestReverse?: boolean;
96
+ }
97
+ export interface OrthologyResponse {
98
+ results: Ortholog[];
99
+ total: number;
100
+ }
101
+ export interface PhenotypeAnnotation {
102
+ id: string;
103
+ phenotype: string;
104
+ phenotypeStatement?: string;
105
+ primaryAnnotatedEntities?: Array<{
106
+ id: string;
107
+ name: string;
108
+ type: string;
109
+ }>;
110
+ publications?: PublicationRef[];
111
+ }
112
+ export interface PhenotypeResponse {
113
+ results: PhenotypeAnnotation[];
114
+ total: number;
115
+ }
116
+ export interface Interaction {
117
+ interactorAGeneId: string;
118
+ interactorASymbol: string;
119
+ interactorBGeneId: string;
120
+ interactorBSymbol: string;
121
+ interactionType?: string;
122
+ detectionMethod?: string;
123
+ publications?: PublicationRef[];
124
+ }
125
+ export interface InteractionResponse {
126
+ results: Interaction[];
127
+ total: number;
128
+ }
129
+ export interface Allele {
130
+ id: string;
131
+ symbol: string;
132
+ name?: string;
133
+ synonyms?: string[];
134
+ alleleType?: string;
135
+ gene?: {
136
+ id: string;
137
+ symbol: string;
138
+ };
139
+ species?: {
140
+ name: string;
141
+ };
142
+ }
143
+ export interface AlleleResponse {
144
+ results: Allele[];
145
+ total: number;
146
+ }
147
+ export interface PublicationRef {
148
+ id: string;
149
+ pubMedId?: string;
150
+ citation?: string;
151
+ }
152
+ export interface AllianceMineResult {
153
+ type: string;
154
+ id: number;
155
+ relevance: number;
156
+ fields: Record<string, unknown>;
157
+ }
158
+ export interface AllianceMineResponse {
159
+ results: AllianceMineResult[];
160
+ totalHits: number;
161
+ facets?: Record<string, unknown>;
162
+ }
163
+ export interface APIError {
164
+ status: number;
165
+ message: string;
166
+ details?: string;
167
+ }