Correct MCP tool signatures against the live server

fetch_ensembl_sequence takes gene (fetch_region handles coordinates),
load_demo_sequence requires name, and find_genes /
find_genes_and_predict_expression take a sequence handle rather than a region.
The annotation task is find_genes; there is no predict_annotation and no
load_local_fasta on the hosted server. Also drop dangling references/errors.md
pointers, add the 422 validation_failed case, and state the 9,198 bp window
consistently.
This commit is contained in:
Alexander Boldakov
2026-07-24 00:35:24 +00:00
parent 8f4fb7ae0f
commit aa04310650
4 changed files with 104 additions and 46 deletions

View File

@@ -98,7 +98,7 @@ in `references/tasks.md`.
Two hard rules the model enforces:
- **`expression` needs exactly 9,198 bp**, a window **centred on the TSS**
(2 × 4,599). Any other length is rejected. Use the acquisition helpers below to
(4,599 upstream + TSS + 4,598 downstream). Any other length is rejected. Use the acquisition helpers below to
build it — do not truncate by hand.
- **`expression` needs a `description`** — a cell-type / assay string (e.g.
`"K562 cells"`), passed as `options.description`.
@@ -107,16 +107,18 @@ Two hard rules the model enforces:
You rarely start from a raw 9,198 bp string. Acquire sequence first:
- **From a gene symbol or region** → fetch reference sequence from Ensembl
(public, no key). MCP: `fetch_ensembl_sequence`, `fetch_region`,
`find_genes`. REST users can query Ensembl REST directly.
- **From a gene symbol** → MCP `fetch_ensembl_sequence(gene=...)`; **from
coordinates** → `fetch_region(region=...)`. Both fetch public Ensembl reference
sequence (no key). REST users can query Ensembl REST directly. (`find_genes` is
the annotation task, not an acquisition tool.)
- **For `expression`** → use the TSS-centred fetch so the window is exactly
9,198 bp. MCP: `fetch_gene_for_expression` (handles the centring). Do not
build the window by hand.
- **From a local FASTA** → MCP `load_local_fasta` / `store_inline_sequence`, or
read the file yourself for REST.
- **A demo sequence** → MCP `load_demo_sequence` returns a ready handle (great
for a keyless smoke test).
- **From a local FASTA** → MCP `store_inline_sequence`, or read the file yourself
for REST. (`load_local_fasta` exists only in local deployments, not on the
hosted server.)
- **A demo sequence** → MCP `load_demo_sequence(name=...)` returns a ready handle
(great for a keyless smoke test); `name` is required.
See `references/sequence-acquisition.md` for the exact Ensembl calls and the
expression-window math.
@@ -178,9 +180,10 @@ On an MCP host, acquire a handle, then predict against it — sequences stay out
the context:
```
# 1. Acquire a sequence handle (any of these return a sequence_ref):
load_demo_sequence() # keyless smoke test
fetch_ensembl_sequence(region="TP53") # gene or region -> handle
# 1. Acquire a sequence handle (each returns a sequence_ref):
load_demo_sequence(name="promoter_tp53") # keyless smoke test; `name` is REQUIRED
fetch_ensembl_sequence(gene="TP53") # gene symbol or Ensembl ID -> handle
fetch_region(region="chr11:5,225,000-5,235,000") # coordinates -> handle
fetch_gene_for_expression(gene="HBB") # TSS-centred 9,198 bp handle for expression
# 2. Predict against the handle:
@@ -188,7 +191,11 @@ predict_promoter(sequence_ref=<ref>)
predict_expression(sequence_ref=<ref>, description="K562 cells")
predict_splice(sequence_ref=<ref>) # + predict_enhancer / predict_chromatin
# 3. Annotation is async: submit, then poll get_job(job_id) until terminal.
# 3. Annotation on MCP is `find_genes` (there is no predict_annotation).
# It takes a handle, not a region, and runs async internally:
find_genes(sequence_ref=<ref>) # wait=True (default) returns the result
find_genes(sequence_ref=<ref>, wait=False) # -> job_id; poll get_job(job_id)
# Discover models with list_models(task); reference context lives in the
# gi://models, gi://docs/tasks, and gi://account MCP resources.
```
@@ -198,8 +205,10 @@ predict_splice(sequence_ref=<ref>) # + predict_enhancer / predict_chromat
To answer "what genes are in this region and how are they expressed?", use the
composite:
- **MCP:** `find_genes_and_predict_expression(region=..., description=...)`
finds genes in the region and returns an expression prediction for each.
- **MCP:** `find_genes_and_predict_expression(sequence_ref=..., description=...)`
— takes a **handle, not a region** (acquire one with `fetch_region` first);
`description` is required. Finds genes in the sequence and returns an
expression prediction for each.
- **REST:** call gene discovery, then loop `expression` per gene (build each
TSS-centred 9,198 bp window via the acquisition helpers).
@@ -211,10 +220,9 @@ composite:
| 401 | Missing/invalid key (REST) | Set `GI_API_KEY`; or use the keyless MCP demo |
| 413 | Sequence too long | Stay within the task's length bound (≤500,000 bp) |
| 429 | Rate / concurrency cap | Back off and retry; ask GI to raise your tier |
| 422 | Validation failed (`validation_failed`) | The most common failure: expression not exactly 9,198 bp, or a sequence below the model's minimum length |
| 5xx | Server error | Retry; if persistent, contact support |
`references/errors.md` has the full list and the exact error strings.
## Reference files
- `references/tasks.md` — per-task output shapes, model registries, the async

View File

@@ -35,8 +35,9 @@ Request body: `{sequence, sequence_name, model?, options?}`. `options` is
task-specific — most notably `options.description` (required for `expression`).
Success is a `{data, meta}` envelope; `data` is task-specific (see `tasks.md`),
`meta` carries model + request info. Errors use a `{error}` envelope — see
`errors.md`.
`meta` carries model + request info. Errors use an `{error}` envelope carrying
`code`, `message`, `status` and `request_id`; the most common is `422`
`validation_failed` (wrong sequence length).
## Partner tiers

View File

@@ -6,35 +6,64 @@ GI hosts a Model Context Protocol server (Streamable HTTP) at:
https://mcp.genomicintelligence.ai/mcp
```
It works **keyless** against a capped public demo quota no setup. An optional
`gi_` bearer key (`GI_API_KEY`) raises the quota. Prefer MCP on agent hosts that
support it: the tools mirror the six tasks with agent-friendly, handle-based
schemas so large sequences never enter the context.
It works **keyless** against a capped public demo quota, with no setup. An
optional `gi_` bearer key (`GI_API_KEY`) raises the quota. Prefer MCP on agent
hosts that support it: the tools use agent-friendly, handle-based schemas so large
sequences never enter the context.
The hosted server exposes **15 tools**. Verify with `tools/list` rather than
assuming; the list below is a point-in-time snapshot.
## The handle-based flow
Acquire a **sequence handle** (`sequence_ref`), then predict against it:
Acquire a **sequence handle** (`sequence_ref`), then predict against it.
1. **Acquire** — any of these return a `sequence_ref`:
- `load_demo_sequence()` — a ready demo handle (keyless smoke test)
- `fetch_ensembl_sequence(region=...)` / `fetch_region(...)` — reference
sequence for a gene or region (public Ensembl)
- `fetch_gene_for_expression(gene=...)` — a **TSS-centred 9,198 bp** handle
for `expression` (does the centring for you)
- `find_genes(region=...)` — genes in a region
- `load_local_fasta(path=...)` / `store_inline_sequence(sequence=...)` — from
a local FASTA or an inline string
2. **Predict** — pass the handle:
- `predict_promoter`, `predict_splice`, `predict_enhancer`,
`predict_chromatin`, `predict_expression(..., description=...)`
3. **Async**`annotation` submits a job; poll `get_job(job_id)` (and
`list_jobs`) until terminal.
### 1. Acquire (each returns a handle)
## Discovery & composite
| Tool | Required | Notes |
|---|---|---|
| `fetch_ensembl_sequence` | `gene` | Gene **symbol or Ensembl ID** (e.g. `"TP53"`). Also `species`, `flank_bp`. Not for coordinates. |
| `fetch_region` | `region` | Coordinate range, e.g. `"chr8:127,680,000-127,800,000"`. Also `species`, `strand`, `flank_bp`. Plus strand by default, which is what gene finding expects. |
| `fetch_gene_for_expression` | `gene` | Builds the **TSS-centred 9,198 bp** window `expression` needs. Also `species`. |
| `load_demo_sequence` | `name` | **`name` is required.** Valid names: `promoter_tp53`, `splice_hbb`, `enhancer_eve`, `chromatin_active_promoter_chr19`, `expression_hbb_k562`, `annotation_hbb_chr11`. |
| `store_inline_sequence` | `sequence` | Store an inline string; optional `name`. |
- `list_models(task)` — the model registry for a task (don't invent IDs).
- `find_genes_and_predict_expression(region=..., description=...)` — the
composite: find genes in a region, predict each one's expression.
There is **no `load_local_fasta` on the hosted server** — it only exists in local
deployments. Over REST, read the file yourself.
### 2. Predict (pass the handle)
`predict_promoter`, `predict_splice`, `predict_enhancer`, `predict_chromatin`,
`predict_expression`. Each takes `sequence_ref` **or** `sequence` (mutually
exclusive), plus optional `model` and `sequence_name`.
`predict_expression` additionally needs `description` (cell type / assay, e.g.
`"K562 cells"`).
### 3. Gene finding (the annotation task on MCP)
**There is no `predict_annotation` tool.** The annotation task is surfaced as
**`find_genes`**, which takes `sequence_ref` or `sequence`**not** a `region`.
Acquire a region handle with `fetch_region` first, then pass the handle.
`find_genes` runs async internally (~8-25 s). With `wait=True` (the default) it
blocks and returns the result directly, never a job id. With `wait=False` it
returns `{data: {job_id, status}}` to poll with `get_job(job_id)`.
## Composite
`find_genes_and_predict_expression` takes `sequence_ref` or `sequence` plus a
**required** `description`. It has **no `region` parameter** — acquire a handle
with `fetch_region` first. It finds genes in the sequence, then predicts
expression off each discovered TSS. Use it whenever you want expression for a
whole region: `predict_expression` cannot run on one, because it needs a single
per-gene 9,198 bp window.
## Jobs and discovery
- `get_job(job_id)` (required `job_id`) and `list_jobs` — poll detached work.
- `list_models(task)` — the model registry for a task. Do not invent model IDs,
and do not hardcode a default; omit `model` and the server resolves it.
## Resources
@@ -44,5 +73,22 @@ bounds.
## Small sequences
Small sequences may be passed inline via a `sequence` argument on the
`predict_*` tools, but the handle flow above is preferred to keep context small.
Small sequences may be passed inline via `sequence` on the `predict_*` tools, but
the handle flow above is preferred to keep context small.
## Worked example
```
# region -> handle -> genes -> expression per gene
h = fetch_region(region="chr11:5,225,000-5,235,000")
find_genes(sequence_ref=h.ref)
find_genes_and_predict_expression(sequence_ref=h.ref, description="K562 cells")
# gene -> handle -> promoter
g = fetch_ensembl_sequence(gene="TP53")
predict_promoter(sequence_ref=g.ref)
# keyless smoke test
d = load_demo_sequence(name="promoter_tp53")
predict_promoter(sequence_ref=d.ref)
```

View File

@@ -48,7 +48,8 @@ binding). The default (DeepSEA) covers hundreds of features.
## expression
Expression as **log(TPM+1)** from a fixed window. Two enforced requirements:
1. **Exactly 9,198 bp** — a window **centred on the TSS** (2 × 4,599). Other
1. **Exactly 9,198 bp** — a window **centred on the TSS** (4,599 upstream +
TSS + 4,598 downstream). Other
lengths are rejected. Build it with the acquisition helpers
(`fetch_gene_for_expression` on MCP), not by hand — see
`sequence-acquisition.md`.
@@ -59,7 +60,7 @@ Result: `data.prediction.expression_log_tpm` (and `expression_tpm`).
## annotation
De-novo gene / transcript structure — transcript intervals and strand, no
reference annotation. **Async only** (see `errors.md`): submit with
reference annotation. **Async only**: submit with
`Prefer: respond-async``job_id`; poll `GET /v1/tasks/jobs/{job_id}` until it
returns `200`. `data.transcripts` lists each transcript with `name`, `start`,
`end`, `strand`, `score`, plus structure fields (`length`, `tss_position`,
@@ -67,6 +68,8 @@ returns `200`. `data.transcripts` lists each transcript with `name`, `start`,
## Composite: find genes + predict expression
"What genes are in this region, and how are they expressed?" — MCP
`find_genes_and_predict_expression(region, description)` finds genes in a region
`find_genes_and_predict_expression(sequence_ref, description)` takes a **handle,
not a region** (acquire one with `fetch_region` first); `description` is
required. It finds genes in the sequence
and returns an expression prediction per gene. Over REST, discover genes then
loop `expression` per gene (build each TSS-centred 9,198 bp window first).