@cyanheads/ensembl-mcp-server 0.4.3 → 0.5.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.
Files changed (39) hide show
  1. package/AGENTS.md +45 -19
  2. package/CLAUDE.md +45 -19
  3. package/README.md +95 -68
  4. package/changelog/0.4.x/0.4.4.md +27 -0
  5. package/changelog/0.5.x/0.5.0.md +25 -0
  6. package/changelog/template.md +9 -26
  7. package/dist/index.js +7 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp-server/prompts/definitions/gene-dossier.prompt.d.ts.map +1 -1
  10. package/dist/mcp-server/prompts/definitions/gene-dossier.prompt.js +12 -5
  11. package/dist/mcp-server/prompts/definitions/gene-dossier.prompt.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/get-homology.tool.d.ts.map +1 -1
  13. package/dist/mcp-server/tools/definitions/get-homology.tool.js +2 -0
  14. package/dist/mcp-server/tools/definitions/get-homology.tool.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/get-sequence.tool.d.ts +14 -2
  16. package/dist/mcp-server/tools/definitions/get-sequence.tool.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/get-sequence.tool.js +164 -49
  18. package/dist/mcp-server/tools/definitions/get-sequence.tool.js.map +1 -1
  19. package/dist/mcp-server/tools/definitions/get-xrefs.tool.d.ts.map +1 -1
  20. package/dist/mcp-server/tools/definitions/get-xrefs.tool.js +2 -0
  21. package/dist/mcp-server/tools/definitions/get-xrefs.tool.js.map +1 -1
  22. package/dist/mcp-server/tools/definitions/lookup-gene.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/lookup-gene.tool.js +10 -2
  24. package/dist/mcp-server/tools/definitions/lookup-gene.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/predict-variant.tool.d.ts.map +1 -1
  26. package/dist/mcp-server/tools/definitions/predict-variant.tool.js +4 -0
  27. package/dist/mcp-server/tools/definitions/predict-variant.tool.js.map +1 -1
  28. package/dist/mcp-server/tools/definitions/query-region.tool.d.ts +6 -1
  29. package/dist/mcp-server/tools/definitions/query-region.tool.d.ts.map +1 -1
  30. package/dist/mcp-server/tools/definitions/query-region.tool.js +119 -24
  31. package/dist/mcp-server/tools/definitions/query-region.tool.js.map +1 -1
  32. package/dist/services/ensembl/ensembl-service.d.ts +11 -0
  33. package/dist/services/ensembl/ensembl-service.d.ts.map +1 -1
  34. package/dist/services/ensembl/ensembl-service.js +26 -0
  35. package/dist/services/ensembl/ensembl-service.js.map +1 -1
  36. package/dist/services/ensembl/types.d.ts +11 -0
  37. package/dist/services/ensembl/types.d.ts.map +1 -1
  38. package/package.json +10 -9
  39. package/server.json +3 -3
@@ -0,0 +1,27 @@
1
+ ---
2
+ summary: "mcp-ts-core 0.13.6 adoption: an unrecognized or misspelled argument key now succeeds instead of failing, argument rejections carry a reason and recovery hint under Invalid params, and every declared tool error closes with a (reason ...) suffix"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.4.4 — 2026-09-19
8
+
9
+ ## Changed
10
+
11
+ - **Tool calls tolerate minor argument mismatches instead of rejecting them** — an unrecognized top-level key is dropped, a differently-cased spelling of a declared parameter (`dbName` for `dbname`, `maxTranscriptConsequences` for `max_transcript_consequences`) is rewritten onto the real key, and a JSON-stringified array is parsed and retried once. Advertised `inputSchema` is unchanged; only calls that previously failed now succeed.
12
+ - **Argument rejections report `Invalid params` (`-32602`)** instead of the previous validation-error code, with `data.reason: "invalid_arguments"` and a synthesized `Recovery: …` hint derived from the offending field and the root schema.
13
+ - **Every declared tool error closes with `(reason <reason>)`** — e.g. `not_found`, `invalid_notation` — on the `content[]` text. `structuredContent.error.data.reason` already carried the reason and is unchanged; resource errors are unaffected.
14
+ - **A caller that disconnects mid-call is reported as a cancellation**, not an internal server error.
15
+ - **`sessionMode: 'stateless'`** is declared directly in `src/index.ts`, matching the deployment default it was previously left to.
16
+ - **Bun engines floor raised to `>=1.4.0`**, matching the framework requirement.
17
+ - **Framework scripts, skills, agent protocol, and issue templates** sync to `@cyanheads/mcp-ts-core` 0.13.6 — the development skill tree moves from `skills/` to `framework-skills/` so a plugin install no longer surfaces it to the installing agent, `devcheck` gains a Worker typecheck step and a README version-badge check, and a CodeQL workflow is added.
18
+
19
+ ## Dependencies
20
+
21
+ - `@cyanheads/mcp-ts-core` `^0.12.3` → `^0.13.6`
22
+ - `zod` `^4.4.3` → `^4.6.5`
23
+ - `@biomejs/biome` `^2.5.9` → `^2.5.14`
24
+ - `@types/node` `^26.2.0` → `^26.6.1`
25
+ - `ignore` `^7.0.6` → `^7.0.9`
26
+ - `tsc-alias` `^1.9.2` → `^1.9.5`
27
+ - `vitest` `^4.1.11` → `^5.0.1`
@@ -0,0 +1,25 @@
1
+ ---
2
+ summary: "ensembl_query_region caps its feature list (max_results) and returns assemblyName; ensembl_get_sequence returns bounded offset/max_length windows; oversized, reversed, and out-of-bounds regions classify as invalid_region; blank required identifiers are rejected at the schema across all tools"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.5.0 — 2026-09-23
8
+
9
+ ## Changed
10
+
11
+ - **`ensembl_get_sequence` returns bounded windows** — default 10,000 characters, with new `offset`/`max_length` params to page through the rest; `truncated`/`nextOffset` say how to continue, `length` stays the full sequence length ([#19](https://github.com/cyanheads/ensembl-mcp-server/issues/19)).
12
+ - **`ensembl_query_region` caps its feature list** — `max_results` defaults to 100 (`0` for uncapped); `totalCount` always reports the true pre-cap count ([#17](https://github.com/cyanheads/ensembl-mcp-server/issues/17)).
13
+ - **`ensembl_query_region` returns `assemblyName`** — read from the overlap rows or resolved via `/info/assembly` and cached per species for the process ([#23](https://github.com/cyanheads/ensembl-mcp-server/issues/23)).
14
+
15
+ ## Fixed
16
+
17
+ - **`ensembl_query_region` rejects an empty `feature` array** at the schema instead of forwarding it to Ensembl ([#21](https://github.com/cyanheads/ensembl-mcp-server/issues/21)).
18
+ - **Oversized, reversed, or out-of-bounds regions classify as `invalid_region`** on both region tools, with actionable recovery guidance — also undecodable regions on `ensembl_query_region` (limit 5,000,000 bases) and unknown sequence regions on `ensembl_get_sequence` (limit 10,000,000 bases), which rejects reversed coordinates before any request ([#24](https://github.com/cyanheads/ensembl-mcp-server/issues/24), [#27](https://github.com/cyanheads/ensembl-mcp-server/issues/27), [#28](https://github.com/cyanheads/ensembl-mcp-server/issues/28)).
19
+ - **Both region tools classify Ensembl's error text in linear time** — the patterns matched against the echoed id or region no longer backtrack, and on `ensembl_get_sequence` an unknown stable ID whose echo reads like a type mismatch now reports `not_found` ([#27](https://github.com/cyanheads/ensembl-mcp-server/issues/27), [#28](https://github.com/cyanheads/ensembl-mcp-server/issues/28)).
20
+ - **`ensembl_get_sequence` rejects a non-genomic `type` for a region id** as `type_mismatch` — `/sequence/region` ignores `type` and always serves genomic DNA, so the tool now fails the request instead of mislabeling it ([#25](https://github.com/cyanheads/ensembl-mcp-server/issues/25)).
21
+ - **Blank or whitespace-only required identifiers are rejected at the schema** — `id`, `region`, `species`, `variant`, and batch `ids`/`symbols` entries across every tool, instead of reaching Ensembl unfiltered ([#22](https://github.com/cyanheads/ensembl-mcp-server/issues/22)).
22
+
23
+ ## Dependencies
24
+
25
+ - `@types/node` `^26.6.1` → `^26.6.2`
@@ -6,8 +6,8 @@
6
6
 
7
7
  # Required. One-line GitHub Release-style headline. 350 character cap — a
8
8
  # ceiling, not a target. Default short and scannable. Don't pad, don't stitch
9
- # unrelated changes with commas/semicolons into an inventory — pick the
10
- # headline, like a tag's theme line. Quotes required: unquoted YAML treats
9
+ # unrelated changes with commas/semicolons into an inventory — pick the one
10
+ # headline the release is about. Quotes required: unquoted YAML treats
11
11
  # `: ` inside the value as a key separator and fails GitHub's strict parser.
12
12
  summary: ""
13
13
 
@@ -117,30 +117,13 @@ security: false
117
117
  in that unrelated item's metadata.
118
118
 
119
119
  TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
120
- via `gh release create --notes-from-tag`. The tag is a derivative of this
121
- changelog entry — a condensed, scannable version, not a copy. Format:
122
-
123
- <theme — omit version number, GitHub prepends it>
124
- ← blank line
125
- <1-2 sentence context: what this release does>
126
- ← blank line
127
- Dependency bumps: ← section header
128
- ← blank line
129
- - `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
130
- ← blank line
131
- Changed: ← only sections with entries
132
- ← blank line
133
- - `format()` output includes `query` in text mode
134
- ← blank line
135
- Added:
136
- ← blank line
137
- - `manifest.json` scaffolded for MCPB bundle support
138
- - Install badges (Claude Desktop, Cursor, VS Code)
139
- ← blank line
140
- <N> tests pass; `bun run devcheck` clean. ← footer
141
-
142
- Never a flat comma-separated string. Always structured markdown with
143
- sections. The tag must scan well as a rendered GitHub Release page.
120
+ via `gh release create --notes-from-tag`. It is a condensed digest of this
121
+ entry, never a copy, and its format is owned by the `release-and-publish`
122
+ skill (step 4, "Create the annotated tag"): a short subject line without the
123
+ version, flat headline bullets — no Keep-a-Changelog section headers, no
124
+ gates line — at most one deps line, issue backlinks, and the changelog link
125
+ last. In release-PR mode the `git-wrapup` skill authors those bullets as the
126
+ PR body's `## Changes` and the tag copies them.
144
127
  -->
145
128
 
146
129
  ## Added
package/dist/index.js CHANGED
@@ -19,6 +19,13 @@ import { initEnsemblService } from './services/ensembl/ensembl-service.js';
19
19
  await createApp({
20
20
  name: 'ensembl-mcp-server',
21
21
  title: 'ensembl-mcp-server',
22
+ /**
23
+ * Every tool here is a read-only Ensembl REST lookup — no handler calls
24
+ * `ctx.requestInput`, so nothing needs a session to come back to. `stateless`
25
+ * is declared in source rather than left to the deployment's
26
+ * `MCP_SESSION_MODE`, which still wins when it carries a meaningful value.
27
+ */
28
+ sessionMode: 'stateless',
22
29
  cacheHints: {
23
30
  'prompts/list': { ttlMs: 3_600_000, cacheScope: 'public' },
24
31
  'resources/list': { ttlMs: 3_600_000, cacheScope: 'public' },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yDAAyD,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,qDAAqD,CAAC;AAC1F,OAAO,EACL,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,wDAAwD,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2DAA2D,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,oBAAoB;IAC3B,UAAU,EAAE;QACV,cAAc,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC1D,gBAAgB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC5D,gBAAgB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC1D,0BAA0B,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACtE,iBAAiB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;KACzD;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iBAAiB;QACjB,kBAAkB;QAClB,kBAAkB;QAClB,qBAAqB;QACrB,kBAAkB;QAClB,eAAe;KAChB;IACD,SAAS,EAAE;QACT,mBAAmB;QACnB,yBAAyB;QACzB,sBAAsB;QACtB,gCAAgC;KACjC;IACD,OAAO,EAAE,CAAC,wBAAwB,CAAC;IACnC,YAAY,EAAE;;;;oHAIoG;IAClH,KAAK,CAAC,IAAI;QACR,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yDAAyD,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,qDAAqD,CAAC;AAC1F,OAAO,EACL,gCAAgC,EAChC,sBAAsB,GACvB,MAAM,wDAAwD,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2DAA2D,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qDAAqD,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,oBAAoB;IAC3B;;;;;OAKG;IACH,WAAW,EAAE,WAAW;IACxB,UAAU,EAAE;QACV,cAAc,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC1D,gBAAgB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC5D,gBAAgB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC1D,0BAA0B,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACtE,iBAAiB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC7D,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;KACzD;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iBAAiB;QACjB,kBAAkB;QAClB,kBAAkB;QAClB,qBAAqB;QACrB,kBAAkB;QAClB,eAAe;KAChB;IACD,SAAS,EAAE;QACT,mBAAmB;QACnB,yBAAyB;QACzB,sBAAsB;QACtB,gCAAgC;KACjC;IACD,OAAO,EAAE,CAAC,wBAAwB,CAAC;IACnC,YAAY,EAAE;;;;oHAIoG;IAClH,KAAK,CAAC,IAAI;QACR,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;CACF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"gene-dossier.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/gene-dossier.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,wBAAwB;;;kBA8DnC,CAAC"}
1
+ {"version":3,"file":"gene-dossier.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/gene-dossier.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,wBAAwB;;;kBAqEnC,CAAC"}
@@ -30,12 +30,18 @@ export const ensemblGeneDossierPrompt = prompt('ensembl_gene_dossier', {
30
30
  `Record the stable ID (ENSG…), genomic coordinates (chr:start-end:strand:assembly), ` +
31
31
  `biotype, and the canonical transcript ID (ENST…).\n\n` +
32
32
  `2. **Fetch the protein sequence** — call \`ensembl_get_sequence\` with the canonical ` +
33
- `transcript ID from step 1 and type="protein". Record the amino acid sequence and its length.\n\n` +
33
+ `transcript ID from step 1 and type="protein". Record the amino acid sequence and its length. ` +
34
+ `A protein longer than 10,000 residues comes back as a first window (truncated=true); length ` +
35
+ `is still the full length, and the window already holds the leading residues step 7 needs.\n\n` +
34
36
  `3. **Find variants in the locus** — call \`ensembl_query_region\` with ` +
35
37
  `species="${args.species}", the gene's chromosomal region (chr:start-end from step 1), ` +
36
- `and feature=["variation"]. Record the variant IDs (rsIDs), positions, consequence type, ` +
37
- `and any clinical significance. Functional impact (HIGH/MODERATE/LOW) is not reported by ` +
38
- `this step — it comes from VEP in step 4.\n\n` +
38
+ `and feature=["variation"]. A gene locus can hold tens of thousands of variants, so the ` +
39
+ `response returns the first 100 (max_results) while totalCount reports the full count — ` +
40
+ `record totalCount as the locus variant count. To see other variants, query a narrower ` +
41
+ `window (e.g. one exon's coordinates) or raise max_results. Record the variant IDs (rsIDs), ` +
42
+ `positions, consequence type, and any clinical significance of the returned variants. ` +
43
+ `Functional impact (HIGH/MODERATE/LOW) is not reported by this step — it comes from VEP ` +
44
+ `in step 4.\n\n` +
39
45
  `4. **Predict variant consequences** — for up to 3 variants from step 3, ` +
40
46
  `call \`ensembl_predict_variant\` with each variant's rsID (e.g. rs334), HGVS notation, ` +
41
47
  `or region+allele. This step returns the functional impact. ` +
@@ -50,7 +56,8 @@ export const ensemblGeneDossierPrompt = prompt('ensembl_gene_dossier', {
50
56
  `7. **Synthesize the dossier** — compile your findings into a structured report with sections:\n` +
51
57
  ` - Gene overview (ID, location, biotype, description)\n` +
52
58
  ` - Protein sequence summary (length, first 50 aa, key domains if known)\n` +
53
- ` - Variant landscape (count, highest-impact findings, clinical significance)\n` +
59
+ ` - Variant landscape (count from step 3's totalCount, highest-impact findings, ` +
60
+ `clinical significance)\n` +
54
61
  ` - Conservation across species (ortholog table with perc_id)\n` +
55
62
  ` - External IDs for follow-up (UniProt → pubchem for structure, ` +
56
63
  `HGNC/EntrezGene → pubmed for literature, OMIM for disease associations)\n\n` +
@@ -1 +1 @@
1
- {"version":3,"file":"gene-dossier.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/gene-dossier.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,sBAAsB,EAAE;IACrE,WAAW,EACT,gGAAgG;QAChG,8FAA8F;QAC9F,0EAA0E;IAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,CACP,oDAAoD;YAClD,4DAA4D,CAC/D;QACH,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,OAAO,CAAC,cAAc,CAAC;aACvB,QAAQ,CACP,wEAAwE;YACtE,6EAA6E,CAChF;KACJ,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM;gBACZ,IAAI,EACF,0CAA0C,IAAI,CAAC,WAAW,WAAW,IAAI,CAAC,OAAO,KAAK;oBACtF,+DAA+D;oBAC/D,uEAAuE,IAAI,CAAC,WAAW,IAAI;oBAC3F,gBAAgB,IAAI,CAAC,OAAO,sCAAsC;oBAClE,qFAAqF;oBACrF,uDAAuD;oBACvD,uFAAuF;oBACvF,kGAAkG;oBAClG,yEAAyE;oBACzE,YAAY,IAAI,CAAC,OAAO,gEAAgE;oBACxF,0FAA0F;oBAC1F,0FAA0F;oBAC1F,8CAA8C;oBAC9C,0EAA0E;oBAC1E,yFAAyF;oBACzF,6DAA6D;oBAC7D,wEAAwE;oBACxE,2EAA2E;oBAC3E,WAAW,IAAI,CAAC,WAAW,eAAe,IAAI,CAAC,OAAO,6BAA6B;oBACnF,yFAAyF;oBACzF,wFAAwF;oBACxF,qDAAqD;oBACrD,gEAAgE;oBAChE,iFAAiF;oBACjF,iGAAiG;oBACjG,2DAA2D;oBAC3D,6EAA6E;oBAC7E,kFAAkF;oBAClF,kEAAkE;oBAClE,oEAAoE;oBACpE,6EAA6E;oBAC7E,4EAA4E;oBAC5E,uDAAuD;aAC1D;SACF;KACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"gene-dossier.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/gene-dossier.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,sBAAsB,EAAE;IACrE,WAAW,EACT,gGAAgG;QAChG,8FAA8F;QAC9F,0EAA0E;IAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,CACP,oDAAoD;YAClD,4DAA4D,CAC/D;QACH,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,OAAO,CAAC,cAAc,CAAC;aACvB,QAAQ,CACP,wEAAwE;YACtE,6EAA6E,CAChF;KACJ,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM;gBACZ,IAAI,EACF,0CAA0C,IAAI,CAAC,WAAW,WAAW,IAAI,CAAC,OAAO,KAAK;oBACtF,+DAA+D;oBAC/D,uEAAuE,IAAI,CAAC,WAAW,IAAI;oBAC3F,gBAAgB,IAAI,CAAC,OAAO,sCAAsC;oBAClE,qFAAqF;oBACrF,uDAAuD;oBACvD,uFAAuF;oBACvF,+FAA+F;oBAC/F,8FAA8F;oBAC9F,+FAA+F;oBAC/F,yEAAyE;oBACzE,YAAY,IAAI,CAAC,OAAO,gEAAgE;oBACxF,yFAAyF;oBACzF,yFAAyF;oBACzF,wFAAwF;oBACxF,6FAA6F;oBAC7F,uFAAuF;oBACvF,yFAAyF;oBACzF,gBAAgB;oBAChB,0EAA0E;oBAC1E,yFAAyF;oBACzF,6DAA6D;oBAC7D,wEAAwE;oBACxE,2EAA2E;oBAC3E,WAAW,IAAI,CAAC,WAAW,eAAe,IAAI,CAAC,OAAO,6BAA6B;oBACnF,yFAAyF;oBACzF,wFAAwF;oBACxF,qDAAqD;oBACrD,gEAAgE;oBAChE,iFAAiF;oBACjF,iGAAiG;oBACjG,2DAA2D;oBAC3D,6EAA6E;oBAC7E,mFAAmF;oBACnF,0BAA0B;oBAC1B,kEAAkE;oBAClE,oEAAoE;oBACpE,6EAA6E;oBAC7E,4EAA4E;oBAC5E,uDAAuD;aAC1D;SACF;KACF;CACF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-homology.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-homology.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA0CjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkP7B,CAAC"}
1
+ {"version":3,"file":"get-homology.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-homology.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA0CjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoP7B,CAAC"}
@@ -57,6 +57,8 @@ export const ensemblGetHomology = tool('ensembl_get_homology', {
57
57
  'Cannot be combined with symbol.'),
58
58
  species: z
59
59
  .string()
60
+ .trim()
61
+ .min(1)
60
62
  .default('homo_sapiens')
61
63
  .describe('Source species (the species the query gene belongs to) in Ensembl internal format. ' +
62
64
  'Default is homo_sapiens. Use ensembl_list_species to discover valid values.'),
@@ -1 +1 @@
1
- {"version":3,"file":"get-homology.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-homology.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-homology.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-homology.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG1E,MAAM,cAAc,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,CAAU,CAAC;AAErE,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iEAAiE,CAAC;IAChG,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,gEAAgE,CAAC;IAC7E,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,0EAA0E;QACxE,wDAAwD,CAC3D;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,wEAAwE;QACtE,kDAAkD,CACrD;IACH,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,iEAAiE;QAC/D,oEAAoE,CACvE;IACH,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,sEAAsE;QACpE,wCAAwC,CAC3C;CACJ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,EAAE;IAC7D,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACT,sGAAsG;QACtG,4GAA4G;QAC5G,wGAAwG;QACxG,6FAA6F;QAC7F,0GAA0G;QAC1G,gDAAgD;IAClD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;IAC9E,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,MAAM,EAAE,CAAC;aACN,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,wDAAwD;YACtD,qEAAqE;YACrE,6BAA6B,CAChC;QACH,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,iDAAiD;YAC/C,8DAA8D;YAC9D,iCAAiC,CACpC;QACH,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,CAAC;aACN,OAAO,CAAC,cAAc,CAAC;aACvB,QAAQ,CACP,qFAAqF;YACnF,6EAA6E,CAChF;QACH,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,+EAA+E;YAC7E,wDAAwD;YACxD,oDAAoD,CACvD;QACH,IAAI,EAAE,CAAC;aACJ,IAAI,CAAC,cAAc,CAAC;aACpB,OAAO,CAAC,aAAa,CAAC;aACtB,QAAQ,CACP,8BAA8B;YAC5B,wEAAwE;YACxE,uEAAuE;YACvE,mCAAmC,CACtC;QACH,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,OAAO,CAAC,EAAE,CAAC;aACX,QAAQ,CACP,gEAAgE;YAC9D,8EAA8E;YAC9E,gEAAgE;YAChE,sEAAsE,CACzE;KACJ,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,QAAQ,EAAE,CAAC;aACR,KAAK,CACJ,mBAAmB,CAAC,QAAQ,CAC1B,qGAAqG,CACtG,CACF;aACA,QAAQ,CACP,oEAAoE;YAClE,6DAA6D,CAChE;QACH,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,QAAQ,CACP,iEAAiE;YAC/D,+DAA+D,CAClE;QACH,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;QACzF,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;QAC3E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;KAC/F,CAAC;IACF,UAAU,EAAE;QACV,MAAM,EAAE,CAAC;aACN,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,6DAA6D,CAAC;QAC1E,SAAS,EAAE,CAAC;aACT,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,uDAAuD,CAAC;QACpE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;QAC/F,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;KAC1F;IAED,MAAM,EAAE;QACN;YACE,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,wDAAwD;YAC9D,QAAQ,EACN,oFAAoF;gBACpF,2DAA2D;SAC9D;QACD;YACE,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,gBAAgB,CAAC,eAAe;YACtC,IAAI,EAAE,qCAAqC;YAC3C,QAAQ,EAAE,mEAAmE;SAC9E;QACD;YACE,MAAM,EAAE,mBAAmB;YAC3B,IAAI,EAAE,gBAAgB,CAAC,eAAe;YACtC,IAAI,EAAE,mCAAmC;YACzC,QAAQ,EACN,yFAAyF;SAC5F;KACF;IAED,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,aAAa,EAAE,KAAK,CAAC,cAAc;YACnC,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;QAEpC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YAC/C,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,2DAA2D,EAAE;gBACtF,GAAG,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;YAC7C,MAAM,GAAG,CAAC,IAAI,CACZ,mBAAmB,EACnB,8EAA8E,EAC9E,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,EAAE,CAC5C,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QAC3C,IAAI,OAAe,CAAC;QACpB,IAAI,QAAyB,CAAC;QAE9B,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,OAAO;iBACzB,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC;iBAChF,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,IAAI,2CAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1D,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,SAAS,yBAAyB,EAAE;wBAC1E,GAAG,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC;qBAChC,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC,CAAC,CAAC;YACL,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC3B,0FAA0F;YAC1F,OAAO,GAAG,MAAM,CAAC,eAAe,IAAI,SAAS,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,eAAe,GAAG,aAAa,IAAI,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,OAAO;iBACzB,mBAAmB,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC;iBAC1F,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,2FAA2F;gBAC3F,IAAI,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;oBACpE,MAAM,GAAG,CAAC,IAAI,CACZ,WAAW,EACX,gBAAgB,eAAe,kBAAkB,KAAK,CAAC,OAAO,GAAG,EACjE,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CACpC,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC,CAAC,CAAC;YACL,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC3B,mFAAmF;YACnF,kFAAkF;YAClF,OAAO,GAAG,MAAM,CAAC,eAAe,IAAI,eAAe,CAAC;QACtD,CAAC;QAED,0EAA0E;QAC1E,6EAA6E;QAC7E,6EAA6E;QAC7E,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;QACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAEzF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,GAAG,CAAC,MAAM,CAAC,MAAM,CACf,MAAM,KAAK,CAAC,IAAI,eAAe,OAAO,QAAQ,KAAK,CAAC,OAAO,EAAE;gBAC3D,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClE,qDAAqD,CACxD,CAAC;QACJ,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,QAAQ,CAAC,MAAM;gBACtB,GAAG,EAAE,KAAK,CAAC,WAAW;gBACtB,QAAQ,EACN,WAAW,QAAQ,CAAC,MAAM,OAAO,cAAc,aAAa;oBAC5D,mFAAmF;aACtF,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,cAAc;YAC1B,OAAO;YACP,YAAY,EAAE,KAAK,CAAC,OAAO;YAC3B,SAAS,EAAE,KAAK,CAAC,IAAI;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QACrC,MAAM,UAAU,GACd,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1F,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,iBAAiB,UAAU,IAAI,CAAC,CAAC;QAEzE,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;YACnF,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChC,IAAI,CAAC,CAAC,aAAa;gBAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,CAAC,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACvD,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAClF,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACrF,IAAI,CAAC,CAAC,aAAa;gBAAE,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;YAChF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
@@ -15,11 +15,16 @@ export declare const ensemblGetSequence: import("@cyanheads/mcp-ts-core").ToolDe
15
15
  species: z.ZodOptional<z.ZodString>;
16
16
  expand_5prime: z.ZodDefault<z.ZodNumber>;
17
17
  expand_3prime: z.ZodDefault<z.ZodNumber>;
18
+ offset: z.ZodDefault<z.ZodNumber>;
19
+ max_length: z.ZodDefault<z.ZodNumber>;
18
20
  }, z.core.$strip>, z.ZodObject<{
19
21
  id: z.ZodString;
20
22
  type: z.ZodString;
21
23
  seq: z.ZodString;
22
24
  length: z.ZodNumber;
25
+ offset: z.ZodNumber;
26
+ truncated: z.ZodBoolean;
27
+ nextOffset: z.ZodOptional<z.ZodNumber>;
23
28
  description: z.ZodOptional<z.ZodString>;
24
29
  }, z.core.$strip>, readonly [{
25
30
  readonly reason: "not_found";
@@ -29,12 +34,19 @@ export declare const ensemblGetSequence: import("@cyanheads/mcp-ts-core").ToolDe
29
34
  }, {
30
35
  readonly reason: "type_mismatch";
31
36
  readonly code: JsonRpcErrorCode.ValidationError;
32
- readonly when: "The requested sequence type is incompatible with the provided ID type.";
37
+ readonly when: "A non-genomic type (cdna, cds, or protein) was requested for a region id or a gene ID.";
33
38
  readonly recovery: string;
34
39
  }, {
35
40
  readonly reason: "missing_species";
36
41
  readonly code: JsonRpcErrorCode.ValidationError;
37
42
  readonly when: "A bare chr:start-end region was given without a species.";
38
43
  readonly recovery: string;
39
- }], undefined>;
44
+ }, {
45
+ readonly reason: "invalid_region";
46
+ readonly code: JsonRpcErrorCode.ValidationError;
47
+ readonly when: string;
48
+ readonly recovery: string;
49
+ }], {
50
+ readonly notice: z.ZodOptional<z.ZodString>;
51
+ }>;
40
52
  //# sourceMappingURL=get-sequence.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-sequence.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-sequence.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoM7B,CAAC"}
1
+ {"version":3,"file":"get-sequence.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-sequence.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAqBjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgU7B,CAAC"}
@@ -6,24 +6,44 @@ import { tool, z } from '@cyanheads/mcp-ts-core';
6
6
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { getEnsemblService } from '../../../services/ensembl/ensembl-service.js';
8
8
  const SEQUENCE_TYPES = ['genomic', 'cdna', 'cds', 'protein'];
9
+ /**
10
+ * Linear-time `/first.*second/i`: `first`, then `second` later on the same line.
11
+ * Ensembl echoes the caller's ID into its error text, and the backtracking regex
12
+ * is quadratic in that echo.
13
+ */
14
+ function mentionsInOrder(msg, first, second) {
15
+ return msg
16
+ .toLowerCase()
17
+ .split(/[\n\r\u2028\u2029]/)
18
+ .some((line) => {
19
+ const at = line.indexOf(first);
20
+ return at !== -1 && line.includes(second, at + first.length);
21
+ });
22
+ }
9
23
  export const ensemblGetSequence = tool('ensembl_get_sequence', {
10
24
  title: 'Get Sequence',
11
25
  description: 'Fetch the DNA, cDNA, CDS, or protein sequence for a gene, transcript, protein, or genomic region. ' +
12
- 'Returns the sequence with its stable ID, molecule type, and character count — large sequences are ' +
13
- 'returned in full but the length is stated so callers can budget context. The type parameter selects ' +
14
- 'which sequence is fetched: genomic (default, includes introns), cdna (spliced transcript), ' +
15
- 'cds (coding sequence only), protein. For region mode, set id to a region — either ' +
16
- 'species:chr:start-end (e.g. homo_sapiens:13:32315086-32400268) or a bare chr:start-end with ' +
17
- 'species set (e.g. id 13:32315086-32400268, species homo_sapiens). Protein sequences require a transcript or ' +
18
- 'protein stable ID (ENST…/ENSP…), not a gene ID — use ensembl_lookup_gene with expand_transcripts=true ' +
19
- 'to get the canonical transcript ID first.',
26
+ 'Returns a window of the sequence — the first 10,000 characters by default — with its stable ID, ' +
27
+ 'molecule type, and full length. When more follows the window, truncated is true and nextOffset is the ' +
28
+ 'offset to request next; walking nextOffset reconstructs the whole sequence, and max_length 0 returns ' +
29
+ 'everything from offset to the end. The type parameter selects which sequence is fetched: genomic ' +
30
+ '(default, includes introns), cdna (spliced transcript), cds (coding sequence only), protein. ' +
31
+ 'For region mode, set id to a region — either species:chr:start-end ' +
32
+ '(e.g. homo_sapiens:13:32315086-32400268) or a bare chr:start-end with species set ' +
33
+ '(e.g. id 13:32315086-32400268, species homo_sapiens), spanning at most 10,000,000 bases; ' +
34
+ 'regions return genomic DNA only. Protein sequences require a transcript or protein stable ID ' +
35
+ '(ENST…/ENSP…), not a gene ID — use ' +
36
+ 'ensembl_lookup_gene with expand_transcripts=true to get the canonical transcript ID first.',
20
37
  annotations: { readOnlyHint: true, openWorldHint: true, idempotentHint: true },
21
38
  input: z.object({
22
39
  id: z
23
40
  .string()
41
+ .trim()
42
+ .min(1)
24
43
  .describe('Ensembl stable ID (ENSG…, ENST…, ENSP…) or a genomic region for region mode. ' +
25
44
  'Region accepts species:chr:start-end (e.g. homo_sapiens:13:32315086-32400268) or a bare ' +
26
- 'chr:start-end (e.g. 13:32315086-32400268) when the species field is set.'),
45
+ 'chr:start-end (e.g. 13:32315086-32400268) when the species field is set. A region needs ' +
46
+ 'start at or below end, within the sequence region, and spans at most 10,000,000 bases.'),
27
47
  type: z
28
48
  .enum(SEQUENCE_TYPES)
29
49
  .default('genomic')
@@ -31,7 +51,8 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
31
51
  'genomic: full genomic DNA including introns (default). ' +
32
52
  'cdna: spliced transcript sequence (requires ENST… ID). ' +
33
53
  'cds: coding sequence only, no UTRs (requires ENST… ID with coding transcript). ' +
34
- 'protein: amino acid sequence (requires ENST… or ENSP… ID).'),
54
+ 'protein: amino acid sequence (requires ENST… or ENSP… ID). ' +
55
+ 'Region ids are genomic-only — request cdna, cds, or protein from a transcript or protein stable ID.'),
35
56
  species: z
36
57
  .string()
37
58
  .optional()
@@ -53,21 +74,52 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
53
74
  .default(0)
54
75
  .describe("Number of base pairs to extend downstream (3' direction) of the requested feature. " +
55
76
  'Default 0. Only applies to genomic sequences and region queries.'),
77
+ offset: z
78
+ .number()
79
+ .int()
80
+ .min(0)
81
+ .default(0)
82
+ .describe('0-based character offset where the returned window starts, counted in the resolved sequence ' +
83
+ '(including any expand_5prime/expand_3prime flank). Default 0. Pass nextOffset from a truncated ' +
84
+ 'response to fetch the following window; an offset at or past the end returns an empty window.'),
85
+ max_length: z
86
+ .number()
87
+ .int()
88
+ .min(0)
89
+ .default(10_000)
90
+ .describe('Maximum number of characters in the returned window. Default 10000. ' +
91
+ 'Set to 0 to return everything from offset to the end, uncapped.'),
56
92
  }),
57
93
  output: z.object({
58
94
  id: z.string().describe('The stable ID or region used for the lookup.'),
59
95
  type: z.string().describe('Sequence type returned (genomic, cdna, cds, or protein).'),
60
96
  seq: z
61
97
  .string()
62
- .describe('The full sequence. DNA sequences use IUPAC nucleotide codes (ACGT + ambiguity codes). ' +
63
- 'Protein sequences use single-letter amino acid codes. ' +
64
- 'Large genomic sequences (e.g. 85 kb for BRCA2) are returned in full.'),
98
+ .describe('The requested window of the sequence: at most max_length characters starting at offset. ' +
99
+ 'DNA sequences use IUPAC nucleotide codes (ACGT + ambiguity codes); protein sequences use ' +
100
+ 'single-letter amino acid codes. Empty when offset is at or past the end.'),
65
101
  length: z
66
102
  .number()
67
- .describe('Sequence length in characters — nucleotides for genomic/cdna/cds, amino-acid residues for protein. ' +
68
- 'Use this to budget context window usage before processing the sequence.'),
103
+ .describe('Full sequence length in characters, not the window size — nucleotides for genomic/cdna/cds, ' +
104
+ 'amino-acid residues for protein. Includes any expand_5prime/expand_3prime flank.'),
105
+ offset: z.number().describe('0-based character offset where this window starts.'),
106
+ truncated: z
107
+ .boolean()
108
+ .describe('True when more sequence follows this window; request nextOffset to continue.'),
109
+ nextOffset: z
110
+ .number()
111
+ .optional()
112
+ .describe('Offset of the first character after this window — pass it as offset to fetch the next ' +
113
+ 'window. Present only when truncated.'),
69
114
  description: z.string().optional().describe('Sequence description from Ensembl, if provided.'),
70
115
  }),
116
+ enrichment: {
117
+ notice: z
118
+ .string()
119
+ .optional()
120
+ .describe('Guidance about the window: how to continue when truncated, or why it is empty when the ' +
121
+ 'offset is past the end.'),
122
+ },
71
123
  errors: [
72
124
  {
73
125
  reason: 'not_found',
@@ -79,10 +131,11 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
79
131
  {
80
132
  reason: 'type_mismatch',
81
133
  code: JsonRpcErrorCode.ValidationError,
82
- when: 'The requested sequence type is incompatible with the provided ID type.',
83
- recovery: 'protein and cds sequences require a transcript ID (ENST…) or protein ID (ENSP…), not a gene ID. ' +
134
+ when: 'A non-genomic type (cdna, cds, or protein) was requested for a region id or a gene ID.',
135
+ recovery: 'Region ids are genomic-only, and a gene ID serves only its genomic sequence. Request cdna or ' +
136
+ 'cds from a transcript ID (ENST…) and protein from a transcript or protein ID (ENST…/ENSP…). ' +
84
137
  'Use ensembl_lookup_gene with expand_transcripts=true to find the canonical transcript ID, ' +
85
- 'then request the protein or cds sequence from that transcript ID.',
138
+ 'then request the sequence from that transcript ID.',
86
139
  },
87
140
  {
88
141
  reason: 'missing_species',
@@ -91,9 +144,24 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
91
144
  recovery: 'Set species (e.g. homo_sapiens) alongside the chr:start-end region, ' +
92
145
  'or use the combined species:chr:start-end id form.',
93
146
  },
147
+ {
148
+ reason: 'invalid_region',
149
+ code: JsonRpcErrorCode.ValidationError,
150
+ when: 'A region id has its start after its end, starts past the end of its sequence region, ' +
151
+ 'spans more than the 10,000,000-base maximum, or names a sequence region the species lacks.',
152
+ recovery: 'Give start at or below end, within the sequence region length for the target assembly, ' +
153
+ 'spanning at most 10,000,000 bases; split a longer region into windows of at most ' +
154
+ '10,000,000 bases. Name the chromosome as Ensembl does (13, X, MT; chr13 is also ' +
155
+ 'accepted) — ensembl_lookup_gene reports valid coordinates for any gene.',
156
+ },
94
157
  ],
95
158
  async handler(input, ctx) {
96
- ctx.log.info('Fetching sequence', { id: input.id, type: input.type });
159
+ ctx.log.info('Fetching sequence', {
160
+ id: input.id,
161
+ type: input.type,
162
+ offset: input.offset,
163
+ maxLength: input.max_length,
164
+ });
97
165
  const service = getEnsemblService();
98
166
  // Region mode accepts two id shapes:
99
167
  // species:chr:start-end embedded species (e.g. homo_sapiens:13:32315086-32400268)
@@ -104,7 +172,13 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
104
172
  // colon) matches neither, routing to stable-ID mode below.
105
173
  const isPrefixedRegion = /^[a-z_]+:[\w.]+:\d+-\d+$/i.test(input.id);
106
174
  const isBareRegion = /^[\w.]+:\d+-\d+$/.test(input.id);
175
+ let resolved;
107
176
  if (isPrefixedRegion || isBareRegion) {
177
+ // Ensembl's region endpoint serves genomic DNA only and ignores a type parameter,
178
+ // so a cdna/cds/protein request would come back as genomic — reject it up front.
179
+ if (input.type !== 'genomic') {
180
+ throw ctx.fail('type_mismatch', `Region ids are genomic-only — type "${input.type}" is not available for region ${input.id}.`, { ...ctx.recoveryFor('type_mismatch') });
181
+ }
108
182
  // For the prefixed form the species is the segment before the first colon and
109
183
  // the region is everything after it; the bare form takes its species from the
110
184
  // species field and uses the whole id as the region.
@@ -114,10 +188,25 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
114
188
  if (!species) {
115
189
  throw ctx.fail('missing_species', `Region ${input.id} needs a species — set species (e.g. homo_sapiens) or use the species:chr:start-end id form.`, { ...ctx.recoveryFor('missing_species') });
116
190
  }
117
- const seq = await service
191
+ // Both region shapes end in start-end after the last colon. Comparing the digit
192
+ // strings (length, then lexically) stays exact past double precision and linear
193
+ // in the caller's input, where BigInt parsing is not.
194
+ const [start = '', end = ''] = region
195
+ .slice(region.lastIndexOf(':') + 1)
196
+ .split('-')
197
+ .map((digits) => digits.replace(/^0+(?=\d)/, ''));
198
+ if (start.length > end.length || (start.length === end.length && start > end)) {
199
+ throw ctx.fail('invalid_region', `Region ${input.id} is reversed: start ${start} is greater than its end ${end}.`, { ...ctx.recoveryFor('invalid_region') });
200
+ }
201
+ resolved = await service
118
202
  .getSequenceByRegion(species, region, input.expand_5prime, input.expand_3prime, ctx)
119
203
  .catch((err) => {
120
204
  const msg = err instanceof Error ? err.message : String(err);
205
+ if (/cannot request a slice|maximum allowed length|no slice found/i.test(msg)) {
206
+ throw ctx.fail('invalid_region', `Invalid region ${input.id}: ${msg}`, {
207
+ ...ctx.recoveryFor('invalid_region'),
208
+ });
209
+ }
121
210
  if (/not found|invalid|no stable id/i.test(msg)) {
122
211
  throw ctx.fail('not_found', `Region ${input.id} not found: ${msg}`, {
123
212
  ...ctx.recoveryFor('not_found'),
@@ -125,27 +214,54 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
125
214
  }
126
215
  throw err;
127
216
  });
128
- return seq;
129
217
  }
130
- // Stable ID mode
131
- const seq = await service
132
- .getSequenceById(input.id.trim(), input.type, input.expand_5prime, input.expand_3prime, ctx)
133
- .catch((err) => {
134
- const msg = err instanceof Error ? err.message : String(err);
135
- if (/protein.*gene|cds.*gene|type.*mismatch|incompatible/i.test(msg) ||
136
- /requesting a gene and type not equal/i.test(msg) ||
137
- /multiple sequences detected/i.test(msg)) {
138
- throw ctx.fail('type_mismatch', `Cannot request type "${input.type}" from a gene ID — use a transcript or protein stable ID instead. ` +
139
- `Call ensembl_lookup_gene with expand_transcripts=true to get transcript IDs.`, { ...ctx.recoveryFor('type_mismatch') });
140
- }
141
- if (/not found|no stable id/i.test(msg)) {
142
- throw ctx.fail('not_found', `ID ${input.id} not found in Ensembl.`, {
143
- ...ctx.recoveryFor('not_found'),
144
- });
145
- }
146
- throw err;
147
- });
148
- return seq;
218
+ else {
219
+ resolved = await service
220
+ .getSequenceById(input.id, input.type, input.expand_5prime, input.expand_3prime, ctx)
221
+ .catch((err) => {
222
+ const msg = err instanceof Error ? err.message : String(err);
223
+ // Ensembl echoes the requested ID into "ID '<id>' not found", and the echo can
224
+ // contain any word — so the looser type heuristics never override a not-found.
225
+ const notFound = /not found|no stable id/i.test(msg);
226
+ if (/requesting a gene and type not equal|multiple sequences detected/i.test(msg) ||
227
+ (!notFound &&
228
+ (mentionsInOrder(msg, 'protein', 'gene') ||
229
+ mentionsInOrder(msg, 'cds', 'gene') ||
230
+ mentionsInOrder(msg, 'type', 'mismatch') ||
231
+ /incompatible/i.test(msg)))) {
232
+ throw ctx.fail('type_mismatch', `Cannot request type "${input.type}" from a gene ID — use a transcript or protein stable ID instead. ` +
233
+ `Call ensembl_lookup_gene with expand_transcripts=true to get transcript IDs.`, { ...ctx.recoveryFor('type_mismatch') });
234
+ }
235
+ if (notFound) {
236
+ throw ctx.fail('not_found', `ID ${input.id} not found in Ensembl.`, {
237
+ ...ctx.recoveryFor('not_found'),
238
+ });
239
+ }
240
+ throw err;
241
+ });
242
+ }
243
+ // The window is a post-fetch slice of the resolved sequence, so it indexes past any
244
+ // expansion flank and `length` stays the full length. Ensembl's own start/end trim
245
+ // would hide the full length and cannot be combined with expansion.
246
+ const { length } = resolved;
247
+ const end = input.max_length > 0 ? Math.min(input.offset + input.max_length, length) : length;
248
+ const truncated = end < length;
249
+ if (input.offset >= length) {
250
+ ctx.enrich.notice(`No characters returned: offset ${input.offset} is at or past the end of the sequence, ` +
251
+ `which is ${length.toLocaleString()} characters long. Request an offset below the length.`);
252
+ }
253
+ else if (truncated) {
254
+ ctx.enrich.notice(`Showing ${(end - input.offset).toLocaleString()} of ${length.toLocaleString()} characters ` +
255
+ `from offset ${input.offset}. Call again with offset ${end} for the next window, or set ` +
256
+ 'max_length to 0 for everything from offset to the end.');
257
+ }
258
+ return {
259
+ ...resolved,
260
+ seq: resolved.seq.slice(input.offset, end),
261
+ offset: input.offset,
262
+ truncated,
263
+ ...(truncated && { nextOffset: end }),
264
+ };
149
265
  },
150
266
  format: (result) => {
151
267
  const lines = [];
@@ -154,18 +270,17 @@ export const ensemblGetSequence = tool('ensembl_get_sequence', {
154
270
  lines.push(`**Type:** ${result.type} | **Length:** ${result.length.toLocaleString()} ${unit}`);
155
271
  if (result.description)
156
272
  lines.push(`**Description:** ${result.description}`);
273
+ const extent = result.truncated
274
+ ? `truncated; next offset ${result.nextOffset}`
275
+ : 'not truncated (nothing follows this window)';
276
+ lines.push(`**Window:** ${result.seq.length.toLocaleString()} characters from offset ${result.offset} ` +
277
+ `of ${result.length.toLocaleString()} — ${extent}`);
157
278
  lines.push('');
158
- // Show first 200 chars + truncation note for large sequences
159
- if (result.seq.length > 200) {
160
- lines.push('```');
161
- lines.push(result.seq.slice(0, 200));
162
- lines.push(`… (${result.length.toLocaleString()} total characters)`);
163
- lines.push('```');
279
+ if (result.seq) {
280
+ lines.push('```', result.seq, '```');
164
281
  }
165
282
  else {
166
- lines.push('```');
167
- lines.push(result.seq);
168
- lines.push('```');
283
+ lines.push('_No sequence characters at this offset._');
169
284
  }
170
285
  return [{ type: 'text', text: lines.join('\n') }];
171
286
  },