← Plugin catalog
Scientific Research

Helical

Helical v1.0.5

Bio foundation models

Full description ↓

Save this plugin to Following and return to review observed changes. Follow up to 10 plugins free.

Description

From the marketplace listing

Helical lets you run biological foundation models on your own single-cell data, from the chat you're already in. Upload an .h5ad dataset into your private Helical project, pick a model — scGPT, Geneformer, TranscriptFormer, Cell2Sentence or Nicheformer — and compute embeddings on Helical's GPUs. Follow the run while it goes, read the error if it fails, and download the embeddings when it finishes. Nothing starts on its own. A run is quoted and queued first, and only begins once you approve it in a second, separate step. Every tool works inside the one project your account owns. Runs use the credits in your existing Helical account.

Language: English · Automatically detected from descriptions.

What changed

Helical

Oct 28, 2026 · 7 saved observations

Skill · Oct 28, 2026 · 13:02 UTC · Compared with Oct 24, 2026 · 13:41 UTC

WHAT CHANGED

Instructions updated for reconnect-helical

Skill instructions — Now says: → link. Relay that link exactly as it came — it belongs to this user and this request — alone in a fenced code block, never as a markdown link or a bare...

Observed in instructions or declared skills. Runtime behavior has not been tested.

Open full comparison.

Skill instructions

Before

Oct 24, 2026 · 13:41 UTC

Platform → link. Relay that link exactly as it came — it belongs to this user and this request. The user opens it and approves (normally one click, since they are already signed in), then you retry the same call. - **ChatGPT**: **Settin...

After

Oct 28, 2026 · 13:02 UTC

→ link. Relay that link exactly as it came — it belongs to this user and this request — alone in a fenced code block, never as a markdown link or a bare URL: ```text https://bedrock-agentcore.us-east-1.amazonaws.com/identities/oauth2/aut...

Read SKILL.md text changes →

Skill · Oct 28, 2026 · 13:02 UTC · Compared with Oct 24, 2026 · 13:41 UTC

WHAT CHANGED

Instructions updated for in-silico-perturbation

Skill instructions — Now says: Estimate the price Estimate before uploading anything: the cost depends on the combined file's cell count, so estimate and the total. Do not ask for a yes...

Observed in instructions or declared skills. Runtime behavior has not been tested.

Open full comparison.

Skill instructions

Before

Oct 24, 2026 · 13:41 UTC

Price it, then confirm Price before uploading anything: the cost depends on the combined file's cell count, so estimate and the total, and get their yes before spending. your estimate. against the reference centroid · check identity is p...

After

Oct 28, 2026 · 13:02 UTC

Estimate the price Estimate before uploading anything: the cost depends on the combined file's cell count, so estimate and the total. Do not ask for a yes here. Every run that spends credit needs the user's explicit approval of the platf...

Read SKILL.md text changes →

Skill · Oct 28, 2026 · 13:02 UTC · Compared with Oct 24, 2026 · 13:41 UTC

WHAT CHANGED

Instructions updated for fine-tune-model

Skill instructions — Now says: **Every run that spends credit needs the user's explicit approval.** That approval comes at `resolveConfirmation`, after the platform's quote. So do not ask...

Observed in instructions or declared skills. Runtime behavior has not been tested.

Open full comparison.

Skill instructions

Before

Oct 24, 2026 · 13:41 UTC

## 1. Select the dataset and confirm the label column **Confirm the label column with the user before queuing.** Training on a plausible-looking but wrong column wastes the whole run, and nothing downstream will catch it. Where you can s...

After

Oct 28, 2026 · 13:02 UTC

**Every run that spends credit needs the user's explicit approval.** That approval comes at `resolveConfirmation`, after the platform's quote. So do not ask the user to approve the configuration before queuing. Make the choices below, qu...

Read SKILL.md text changes →
3 more changes that day

Added instruction text: “Every run that spends credit needs that explicit approval. So do not ask the user to approve”. 2 additional added or edited lines are in the evidence.

Skill evidence →

Short description changed from “Single-cell foundation models” to “Bio foundation models”.

Metadata evidence →Listing evidence →

Package contents changed in 6 files: .claude-plugin/plugin.json, plugin.json, skills/compute-embeddings/SKILL.md, …. Open the file diff to inspect the edits.

Files evidence →
Earlier changes · 1 more observation days on this page

Competitor profile

From the developer’s listing and package; capabilities are not independently tested. Package observed Nov 3, 2026.

Declared capabilities

  • Upload datasets
  • Compute embeddings
  • Fine-tune models

Declared limitations

  • Searching for public datasets that are not in the user's project.
  • Billing actions: invoices, payment methods and refunds.
  • Deleting or renaming a registered dataset.

Review scenarios; not an exhaustive list.

Cost & access

Pricing and account requirements have not been established from the available package declarations. Unknown pricing does not mean free access.

Starter prompt

Embed my dataset with a Helical model.

View package source →

Keywords

Matches for “embedding”

Exact text from the indicated source. A mention alone does not establish support for your task.

Publisher capabilities · listing

Upload datasets Compute embeddings Fine-tune models Download results

Publisher keywords · listing

embeddings fine-tuning single-cell bioinformatics foundation-models mcp

Publisher description

Compute embeddings with Helical foundation models, hosted on Helical's GPUs or locally on your own GPU with the open-source package.

Publisher full description

Helical lets you run biological foundation models on your own single-cell data, from the chat you're already in. Upload an .h5ad dataset into your private Helical project, pick a model — scGPT, Geneformer, TranscriptFormer, Cell2Sentence or Nicheformer — and compute embeddings on Helical's GPUs. Follow the run while it goes, read the error if it fails, and download the embeddings when it finishes. Nothing starts on its own. A run is quoted and queued first, and only begins once you approve it in a second, separate step. Every tool works inside the one project your account owns. Runs use the credits in your existing Helical account.

Files & skills

File archives

Plugin package15 files · 20.5 KBBrowse files →
Skill instructions
check-credits5.06 KB

View saved version →

---
name: check-credits
description: >-
  Read the user's Helical credit balance, tell them what a run will cost and when they are
  actually charged, and explain a run refused for insufficient credit. Use when the user
  asks what their balance is, whether they can afford a run, what an embedding or
  fine-tune will cost, or why a run would not start.
---

# Credits: read the balance, explain the charge

## The balance is readable

`whoami()` returns the signed-in `email`, the current `credits_balance`, and the `console`
URL. When the user asks how many credits they have, that number is the answer — give it.

**One credit is one USD**, so a credit figure is a dollar figure and there is nothing to
convert.

The figure is current as of the call, and it moves as runs are charged and refunded. Read
it again rather than reusing one from earlier in the conversation.

## What a run costs

A run costs **a fixed amount per job plus a rate per processed cell**, not per token:

```
credits = jobs × (credits_per_job + cells × passes × credits_per_cell)
```

- `jobs` is how many jobs the launch runs, and `cells` is the cells in each. An embedding
  launch is **one job**.
- `credits_per_job` pays for the work a job does before its first cell, so it is the same
  for ten cells or a million. On a small dataset it is most of the cost.
- `passes` is **1 for an embedding**. For fine-tuning it is **6 × the epoch count**: a
  training step does far more work per cell than an embedding. A validation dataset adds
  its own cells **once per epoch**. So fine-tuning a dataset costs many times what
  embedding it does. The per-job amount is paid once, not once per epoch. The `listModels`
  tool description states the current pass counts; trust it over this page if they differ.
- Both rates are on every `listModels` entry, and differ per model. A model whose rates are
  `null` is not priced and cannot be run here; it is not free.
- A fine-tuned model is charged at the rates of the `base_model` it descends from; it has no
  rates of its own and could not have them, since the user creates it at run time.
- There is no deduplication — the same cell twice is two processed cells.

Use this when the user is deciding **whether** to run: `cellCount` from `listDatasets` or
`getDataset`, and the two rates from `listModels`. For example, with a model at
`credits_per_job` 0.03 and `credits_per_cell` 0.000003, embedding 300,000 cells is
`1 × (0.03 + 300000 × 1 × 0.000003)` = **0.93 credits**. Fine-tuning the same 300,000 cells
for 2 epochs, with a 30,000-cell validation dataset, is
`0.03 + (300000 × 2 × 6 + 30000 × 2) × 0.000003` = **11.01 credits**. Read the real rates from
`listModels` rather than reusing these.

**But your arithmetic is an estimate, not the quote.** When a run is actually queued,
`triggerEmbedding` or `triggerFinetuning` prices it and returns the platform's own figure,
which is what the charge will read. Queuing is free and starts nothing, so prefer that
figure once it exists, and relay it rather than recomputing.

## When the charge actually happens

This is usually the user's real question, and it is reassuring:

- Queuing a run charges nothing.
- The credit is taken **when the run starts**, at approval.
- A run that **fails or is cancelled is refunded in full**.

So a failed run costs nothing. Say so plainly when one fails — it is the first thing they
will want to know.

## When the balance will not cover it

A **402 `insufficient_balance`** can come back either when the run is queued or when it is
approved. Either way nothing launched and nothing was charged — but **the confirmation is spent**.
A later change to the balance does not make it approvable again; the run has to be re-requested
from the trigger tool, which mints a fresh quote.

Credits are managed in their Helical account, not through this plugin.

Retrying the same request unchanged will not help, and the error says so. Do not loop.

## Where the account lives

The account lives on the console, **console.helical.bio**: the site the user signed in to when
they connected this plugin. Use the `console` URL `whoami` returned. Account settings, usage
history and docs live there.

It is **not** `helical.bio`. That is the marketing site and holds nothing about their
account, so sending them there is the same wrong turn as inventing a menu. Beyond the pages
named above, describe the destination rather than guessing a deeper URL.

Do not describe a profile menu, an avatar dropdown, or a workspace billing screen. That is
a different Helical product, and inventing navigation sends a paying customer looking for
something that does not exist.


## Conventions

Read the balance with `whoami`, never guess it · one credit is one USD · re-read rather
than reuse · a fixed amount per job plus a rate per processed cell, no deduplication · your arithmetic estimates, the trigger
quotes — prefer the quote · charged at start, refunded in full on failure or cancellation ·
a 402 spends the confirmation, so re-request the run once the balance covers it ·
the console URL the platform gave you, never `helical.bio`, never an invented menu.

Referenced files: 1 — file references are metadata, not package contents.

compute-embeddings5.31 KB

View saved version →

---
name: compute-embeddings
description: >-
  Compute cell embeddings for a single-cell dataset with a Helical foundation model, then
  download the outputs. Use when the user wants embeddings, representations, or features
  from a dataset already in the platform catalogue. For training a model on labelled data,
  use fine-tune-model instead.
---

# Compute embeddings

Turns a catalogue dataset plus a foundation model into an embedding matrix.

**The one thing to understand before starting:** `triggerEmbedding` does not start
anything. It queues the run and returns a `confirmation_id` and the platform's price. The
run starts only when `resolveConfirmation` approves it, and **that call is the user's
approval**. State the dataset, the model and the price, and get an explicit yes before
making it.

Every run that spends credit needs that explicit approval. So do not ask the user to approve
the dataset or model before queuing. Choose them, queue the run, then ask once, showing the
choices and the price together. Queuing is free. To change a choice, reject and queue again.

You also cannot choose a project: scope comes from the signed-in user, and no tool accepts
a project or conversation identifier.

## 1. Select the dataset

`listDatasets` finds the dataset; its description covers the filters. `organism` matches
the full name exactly, so `"Homo sapiens"` matches but `"human"` does not.

`getDataset({ id })` for the full record, including `cellCount` and `geneCount`. Report
them: run time and price scale with the cell count.

A user's own `.h5ad` can be added with `initiateDatasetUpload` → `completeDatasetUpload`
→ `registerDataset`; follow those tools' descriptions for splitting the file. Once
registered, it is embedded like any catalogue dataset.

## 2. Choose a model

`listModels()`. It returns two lists:

- `base_models`: foundation models. Pass the `name` as `model` and omit `model_version`.
- `models`: the user's own fine-tuned models, promoted or not. Pass the `name` as `model`
  and the `version`, as a number, as `model_version`. Never pass `base_model`.

Any of them can run here; promotion is only a label. Only single-cell models are listed,
and only those can run here. Every entry carries its rates, `credits_per_job` and
`credits_per_cell`; a fine-tuned model is priced at its `base_model`'s rates. Pick one whose
`credits_per_cell` is not `null`; an unpriced model cannot run here.

## 3. Queue it, show the price, then approve

```
triggerEmbedding({ datasetId, model, model_version? })
```

Send only these fields (and `pretrained_embedding_species`, if the user asks for it). The
tool refuses any other field with a 400.

The reply carries the platform's quote: the cells it counts and the most it
will cost. Relay that figure; do not recompute it. Get an explicit yes, then call
`resolveConfirmation({ id: confirmation_id, decision: "approve" })`. If the user declines,
resolve it with `"reject"`.

Do not put several runs behind one approval, and do not treat an earlier "sounds good"
about the plan as approval for the run itself. Each model is its own run with its own
quote.

To change anything, reject the queued confirmation, then queue again. A 409 means the same
run is already queued. `listPendingConfirmations()` finds it, but its entry carries no
price, so if you do not still have that quote from this conversation, reject it and queue
again. For a 402 or an unusable quote, see `check-credits`.

## 4. Follow it through

On approval, `outcome.dag_run_id` names the run. Poll `getRunDetails({ runId })` until
`state` is `succeeded`, `failed` or `cancelled`. Runs take minutes to hours; poll at a
sensible interval and keep the user informed rather than going silent. On failure,
`getDagErrors({ runId })` says what went wrong. A failed or cancelled run is refunded in
full. `listDagRuns({ dagIds: "embedding" })` lists recent embedding runs if you lose track
of one.

## 5. Download the outputs

`getRunDetails({ runId })` returns `artifacts[]`, each with an `artifact_id`,
`artifact_type`, `display_name` and `s3_key`. The embedding matrix has `artifact_type`
`"embedding"`.

`downloadArtifact({ artifactId })` returns a presigned URL. Fetch it with
`curl -o '<fileName>' '<url>'`; it needs no auth header and expires within an hour. It
serves one file of any size, binary included, but not a directory.

If the matrix carries cell ids, join on them. If it is a bare matrix, check its row count
equals the dataset's `cellCount` before assuming its rows follow the dataset's order, and
say you assumed it.

## Hand-off

- Want the model trained on labels first? → `fine-tune-model`. Its result can be embedded
  here as soon as the run succeeds.
- Want to knock out, knock down or overexpress genes? → `in-silico-perturbation`: edit the
  counts locally, upload, and embed the perturbed file here.
- Cost and balance → `check-credits`.
- Judging whether an embedding is good (separation of the conditions of interest against a
  zero-shot baseline) is interpretation, not something these tools report.

## Conventions

`datasetId`, never a path · scope is derived from the signed-in user, you cannot pass a
project · send only the schema's fields · queue → relay the quote → explicit yes →
`resolveConfirmation` · one model, one run, one approval · poll the run · download with
`downloadArtifact` · never print credentials or raw upstream errors.

Referenced files: 1 — file references are metadata, not package contents.

fine-tune-model8.29 KB

View saved version →

---
name: fine-tune-model
description: >-
  Fine-tune a Helical foundation model on a labelled single-cell dataset. Use when the
  user wants to personalize, train, or adapt a model to their own labels. For
  representations from an existing model without training, use compute-embeddings
  instead.
---

# Fine-tune a model

Trains a foundation model on a labelled dataset and registers the result in the user's
model list.

`triggerFinetuning` does not start anything. It queues the run and returns a
`confirmation_id` and the platform's price. The run starts only when
`resolveConfirmation` approves it, and **that call is the user's approval**. This is the
most expensive run on this surface, so the preparation below matters more than it does for
embeddings.

**Every run that spends credit needs the user's explicit approval.** That approval comes at
`resolveConfirmation`, after the platform's quote. So do not ask the user to approve the
configuration before queuing. Make the choices below, queue the run, then ask once, showing
the choices and the price together. Queuing is free. To change a choice, reject the queued
run and queue again.

## 1. Select the dataset and choose the label column

`listDatasets(...)` → `getDataset({ id })`. The record names likely label columns:
`celltypeColumn`, `celltypeColumnLv2`, `diseaseColumn`, `donorColumn`. Note `cellCount`
too; the price scales with it.

These tools cannot list a dataset's other `.obs` columns or their values. If the user wants
a column the record does not name, ask them for its exact name, or read it from a local
copy of the file (the record's `linkData`, when it has one; an uploaded dataset usually
does not).

**Choose the label column, and name it in the approval request.** Training on a
plausible-looking but wrong column wastes the whole run, and nothing downstream will catch
it. Where you can see the classes, check they look like what the user expects. A column
with one dominant class or hundreds of singletons will not train usefully. Ask before
queuing only when you cannot choose: several columns fit the request, none does, or the
classes look unusable.

**Validation needs its own dataset.** This surface does not split the training data. To
validate, the user registers a separate held-out dataset and you pass it as `valDatasetId`.
Without one, the run trains with no validation.

## 2. Choose a base model

`listModels()`. Fine-tune from a `base_models` entry: pass its `name` as `model` and omit
`model_version`. To train an existing fine-tune further, pass a `models` entry's `name`
plus its `version`, as a number, in `model_version`, promoted or not. Pick a model whose
`credits_per_cell` is not `null`; an unpriced model cannot run here.

Before committing: if the user wants representations rather than a classifier, a
zero-shot embedding may already answer it. `compute-embeddings` is far cheaper and faster.
Say so rather than fine-tuning by default.

## 3. Queue it

These fields are required, and a missing one is a 400:

```
triggerFinetuning({
  datasetId, model,
  labels:      ["cell_type"],      # one entry per prediction task
  task_type:   ["prediction"],     # "prediction" | "contrastive"
  model_type:  ["classification"], # one per prediction task: "classification" | "regression"
  learning_rate, batch_size, epochs, logging_steps,
  lr_scheduler,                    # "constant" | "linear" | "cosine_with_min_lr"
  min_lr, seed, num_trainable_layers,
  registered_model_name,           # letters, digits, _ - { } only
  # optional: model_version, valDatasetId, weight_decay, warmup_steps,
  #           early_stopping_patience, lora_config, task_weights, ...
})
```

`labels` and `model_type` describe the **prediction tasks only**, one entry each per task,
in the same order. `task_type` lists one entry per task, contrastive included. The tool's
own description calls all three parallel; that holds only when every task is a prediction.

At most one task can be contrastive. It takes no `labels` entry; its columns go in its own
fields, and nothing checks them before the run starts:

- `loss_type: "supcon"` needs `supcon_label_columns`, `temperature` and
  `n_labels_per_batch` (at most `batch_size`).
- `loss_type: "pairwise"` needs `pairwise_similarity_columns` and `margin`.

Two optional fields carry conditions the server does not check either.
`early_stopping_patience` needs a `valDatasetId`, and `warmup_steps` needs a non-constant
`lr_scheduler`. A bad combination is charged and then fails, so get these right before
queuing.

**Send only fields the schema lists.** It refuses an unknown field with a 400. Three
common ones are not offered here: `val_split` (pass `valDatasetId` instead),
`val_steps` (validation runs once an epoch) and `hyperparameter_config` (one run trains
one configuration).

Reasonable starting values when the user has no preference: `learning_rate` 1e-4,
`batch_size` 16, `epochs` 1, `logging_steps` 10, `lr_scheduler` `"constant"`, `min_lr`
1e-6, `seed` 42, `num_trainable_layers` 2. Queue with them; do not stop to ask first. They
are your decisions, not platform defaults, so list them in the approval request.

Start with a single epoch. It is cheaper to run again than to find out after a long run
that the configuration was wrong.

`registered_model_name` must not be only a model prefix (`scGPT_` alone is rejected). Give
it a name that says what makes this model different.

## 4. Show the price, then approve

The reply carries the platform's quote: the cells it counts and the most it
will cost. Relay that figure; do not recompute it. It is usually many times an embedding
of the same dataset, because each epoch makes several passes over the training cells, and
validation adds a pass over the validation cells each epoch.

**Ask for the user's explicit yes in one message.** Name the dataset, the label column, the
base model, the values you chose and the quoted price. Say the run is queued and waits for
their yes, and that any value can be changed before it starts, with a new quote. An
approval for an embedding run is not an approval for this one, and an earlier "sounds good"
about the plan is not approval for the run. Then call `resolveConfirmation({ id: confirmation_id, decision: "approve" })`.
If they decline, resolve it with `"reject"`.

To change anything, reject the queued confirmation, then queue again with
`triggerFinetuning`; that mints a new quote. A confirmation left pending stays approvable
until it expires.

A 409 from `triggerFinetuning` means the same run is already queued.
`listPendingConfirmations()` finds it, but its entry carries no price and does not show
the epochs or learning rate. If you do not still have that confirmation's quote from this
conversation, reject it and queue again, so the user approves a price they have seen.

For a 402 or an unusable quote, see `check-credits`.

## 5. Follow it through

On approval, `outcome.dag_run_id` names the run. Poll `getRunDetails({ runId })` until
`state` is `succeeded`, `failed` or `cancelled`. Fine-tuning runs are long, so poll at a
sensible interval and keep the user informed. On failure, `getDagErrors({ runId })` says
what went wrong. A failed or cancelled run is refunded in full.

On success the model is registered under `registered_model_name` and shows in
`listModels` `models`, unpromoted. Promotion is only a label here: the model can be used
straight away for embeddings or further training. Pass its `name` and `version`. It is
charged at its `base_model`'s rates.

## Judging whether it worked

Be honest about the limit: these tools report run state and artifacts, not a training
curve or a metric. `getRunDetails` lists the artifacts. `downloadArtifact` fetches a
single file, such as a metrics file if the run wrote one, but not a checkpoint directory.

The practical comparison here is an embedding from the base model against one from the
fine-tuned model, judged on the UMAP. Offer that rather than
claiming a quality verdict the tools cannot support.

## Conventions

`datasetId`, never a path · scope is derived from the signed-in user · check the label
column before queuing · validate with a separate dataset, never a split · send only the
schema's fields · no approval before queuing · queue → choices and quote in one message →
explicit yes → `resolveConfirmation` · poll the
run · a new fine-tune can run here at once, promoted or not · never print credentials or
raw upstream errors.

Referenced files: 1 — file references are metadata, not package contents.

in-silico-perturbation7.47 KB

View saved version →

---
name: in-silico-perturbation
description: >-
  Run an in-silico gene knockout, knockdown or overexpression screen with Helical
  foundation models by editing the expression matrix locally, then embedding the
  perturbed cells on the platform. Use when the user wants to perturb genes, screen
  targets, or find genes whose perturbation shifts diseased cells toward a healthy state
  — even when no perturbation tool is available, because none is needed.
---

# In-silico perturbation

Finds which gene perturbations move cells from one state toward another, by showing a
foundation model perturbed cells and measuring where their embeddings land.

**The one thing to understand before starting:** a perturbation is an edit to the
expression matrix, and you can make it yourself. You do not need a perturbation, target-ID
or gene-validation tool, and their absence is not a reason to stop. The work splits in
two:

- **Locally:** download the dataset, edit the counts, build one file, and later score the
  embeddings.
- **On Helical:** upload that file, embed it with each model, download the embeddings.

**The model must see the perturbed counts.** Embedding the unperturbed dataset and then
"knocking down" a gene in embedding space, or scoring genes against centroids of the
original embeddings, measures nothing. Every perturbed condition goes through the model.

## 1. Get the expression matrix locally

`listDatasets(...)` → `getDataset({ id })`. Note `cellCount`, `geneCount`, and the
`.obs` columns it names: `celltypeColumn`, `diseaseColumn`, `donorColumn`. You need the
cell-type and state columns to choose cells and to score.

The platform does not hand back a dataset's own file. Get the `.h5ad` from the record's
`linkData` (the original download URL), or from the user if they have it. If neither
exists, say so plainly; do not substitute a different dataset.

Check before editing:

- **Raw counts.** The models normalise their input themselves. If `X` holds normalised or
  log values, use `layers["counts"]` or `raw` instead; if there are no counts, tell the
  user the perturbation will be less faithful.
- **Genes are present and expressed.** For each candidate, report the fraction of target
  cells with non-zero counts. Drop genes that are absent or barely detected — knocking out
  a gene the cells do not express changes nothing and wastes a slot.

## 2. Build one combined file

Put every condition in **one `.h5ad`**, so each model needs one embedding run, not one per
gene, and every condition goes through the same model pass. Tag each cell with a
`perturbation` column in `.obs`:

| Block | Cells | `perturbation` |
|---|---|---|
| Reference | target-state cells (e.g. healthy), unedited | `reference` |
| Control | source-state cells (e.g. injured), unedited | `control` |
| One per gene | a copy of the control cells with that gene edited | the gene name |
| Null | a copy of the control cells with a random, expression-matched gene edited | `null_<gene>` |

- **Include the null.** Any edit moves an embedding a little. Without 3–5 random genes,
  matched to the candidates on detection rate and edited the same way, there is nothing
  to say a candidate's shift is more than noise.
- **Subsample.** The file holds `reference + control × (1 + genes + nulls)` cells. A few
  hundred control cells per cell type is enough; balance donors where the metadata allows.
- **Keep the layout of the source.** Change only count values and add the `perturbation`
  column. Keep `.var` and the existing `.obs` columns as they were, so the same gene
  identifiers and column names apply. Give each copied cell a unique `obs_names`, such as
  `<cell>__<perturbation>`.

How to edit a gene, on raw counts:

- **Knockout:** set the gene to 0.
- **Knockdown:** multiply its counts by a factor, e.g. 0.25. Geneformer reads gene *rank*,
  so a partial knockdown may not change its input at all — use a knockout for it, and say so.
- **Overexpression:** raise it, e.g. to its 99th percentile across the dataset.

State the edit you chose; it is your decision, not a platform default.

## 3. Estimate the price

Estimate before uploading anything: the cost depends on the combined file's cell count, so
work it out with the rates from `listModels` as `check-credits` describes. Each model is
**one job** over the whole combined file. Show the user the cell count, the per-model
estimate and the total.

Do not ask for a yes here. Every run that spends credit needs the user's explicit approval
of the platform's quote in step 5, and uploading spends no credit. Stop to ask now only when
the total exceeds the user's balance (read it fresh with `whoami()`, per `check-credits`).

If the total is too much, cut cells or genes; do not split into per-gene runs, which pays
the per-job amount once per gene.

## 4. Upload and register

`initiateDatasetUpload({ sizeBytes })` → upload every part → `completeDatasetUpload` →
`registerDataset`. Follow the tool descriptions exactly: slice parts by **exact bytes**
from each part's `offsetBytes` and `lengthBytes`, and do not complete while a part is
outstanding.

Register with the source record's metadata, a new `name` that says what was perturbed
(names are unique per project; a clash is a 409), and `parentDatasetId` set to the source
dataset, so the derivation is on record.

## 5. Embed with each model

Follow `compute-embeddings` for each model on the registered dataset: queue the run, show
the platform's own quote, and approve only after the user says yes. That quote replaces
your estimate. Each model needs its own yes, against its own quote.

## 6. Download and score

`getRunDetails({ runId })` until `succeeded`, then `downloadArtifact({ artifactId })` for
the embedding matrix and fetch it with `curl`. If the artifact carries cell ids, join on
them. If it is a bare matrix, check its row count equals your file's cell count before
assuming the rows follow your file's order, and say you assumed it.

Score **within each cell type**, using cosine distance in each model's own space:

- `H` = centroid of the `reference` cells of that type.
- For each control cell `i` and condition `g`:
  `shift = (d(controlᵢ, H) − d(gᵢ, H)) / d(controlᵢ, H)`. Positive means toward the
  reference.
- A gene's score is its mean shift, compared with the null genes' shifts: report its
  margin over the null, not the raw number.
- **Identity check:** a perturbed cell should still be nearest its own cell type's
  centroid. A gene that "improves" cells by making them stop looking like their cell type
  is a red flag, not a hit.

Across models, a gene matters when it beats the null in at least two of them.

## 7. Report

A ranked table per cell type: gene, mean shift per model, margin over the null, models in
agreement, identity check passed or not. Lead with the genes that agree across models.

These are hypotheses for experimental follow-up, not validated targets. Say what was
edited and how, how many cells, and any assumption about row order.

## Hand-off

- Embedding mechanics and approval → `compute-embeddings`.
- Cost and balance → `check-credits`.
- Want a model adapted to the tissue first? → `fine-tune-model`, then embed with it here.

## Conventions

Edit counts locally, never embeddings · the model sees every condition · one combined file,
one run per model · always include an expression-matched null · score within cell type
against the reference centroid · check identity is preserved · estimate → upload → queue →
quote → explicit yes → run · hypotheses, not hits · never print credentials or raw upstream errors.

Referenced files: 1 — file references are metadata, not package contents.

reconnect-helical8.66 KB

View saved version →

---
name: reconnect-helical
description: >-
  Get a Helical connection working when it is not: the plugin was installed but never
  connected, a tool call came back with an authorization link, or a connection that worked
  before is now refused. Use when the helical tools are missing from the session or the
  host offers only an `authenticate` tool for it; when a Helical call returns "Authorization
  required before this tool can run", "This request requires more information" or a link to
  open; when it fails with invalid_grant, invalid_request, invalid_client, invalid_token,
  unauthorized_client, a 401, "OAuth authorization required", "Missing Bearer token" or
  "Invalid JWT"; when a call fails without saying why; or when the user asks how to sign in.
  For a run refused over credit, use check-credits.
---

# Reconnect Helical

Helical asks for two approvals, both in the browser: **connecting the plugin** in the
client, and **one authorization the first time a tool runs**. Each failure below is one of
them missing. Tell which from what you see, and put the user's next step in the same
message that reports the problem — never leave them to ask "so what do I do?".

| What you see | Case |
|---|---|
| No `helical` tools at all, or only an `authenticate` tool for it | 1 — not connected |
| A call returns a link to open, "Authorization required before this tool can run", or "This request requires more information" | 2 — authorization link |
| A connection that worked before now fails with a 401 or an OAuth code | 3 — connection refused |

Not these: **`402` / `insufficient_balance`** is credit (`check-credits`), and **`403`** is
a signed-in account without permission — reconnecting returns the same `403`.

## 1. Not connected

Installing the plugin and connecting it are two separate steps in every client. Skip the
second and the plugin shows as enabled with none of its tools. Confirm it before you fix
it, where the client lets you:

- **Claude Code**: `claude mcp list`. The plugin's server is
  `plugin:helical-platform:helical` (`claude.ai Helical` when added from the Claude
  directory, plain `helical` when added by hand). `! Needs authentication` is this case;
  `✔ Connected` is not.
- **Codex**, and the ChatGPT desktop app with a plugin added from GitHub: `codex mcp list`.
  `Not logged in` under **Auth** is this case; `OAuth` is not.
- **ChatGPT** from the public directory, **Claude Desktop** and **claude.ai** have no
  command: ask whether Helical still offers **Connect** where the fix below points.

Then connect it:

- **Claude Code** (CLI, and the Code tab in Claude Desktop). If the host offers a helical
  `authenticate` tool (e.g. `mcp__plugin_helical-platform_helical__authenticate`), call it
  and relay the link it returns; the tools load once the user approves, with no new
  session. If their browser then lands on a `localhost` page that will not load, ask for
  the address-bar URL and pass it to the matching `complete_authentication` tool.
  Otherwise the user runs `/mcp` → the Helical server → **Authenticate**, or
  `claude mcp login plugin:helical-platform:helical` in their own terminal. Do not run that
  command yourself: it needs an interactive terminal and refuses without one.
- **Codex**: `codex mcp login helical`. You can run this one: it opens their browser and
  waits for the approval. `/mcp` inside Codex only lists servers. In the ChatGPT desktop
  app the same step is **Settings** → **MCP servers** → **Authenticate**.
- **ChatGPT** from the public directory: **Settings** → **Plugins** → Helical →
  **Connect**.
- **Claude Desktop** and **claude.ai**: **Customize** → **Connectors** → Helical →
  **Connect**.

Except through the `authenticate` tool, say a new session may be needed: hosts that load
their tools at session start will not add them to this one. The Connect step of the
walkthrough at `<who-am-i-tool-console-url>docs?section=walkthrough` has the same steps
for every client.

## 2. Authorization link

The connection is fine; Helical needs one more approval before the tool can run. It is
expected on the first tool call after connecting, and again now and then — months apart,
or after the account was signed out everywhere. Reconnecting the client does not clear
it, and neither does anything in case 1 or 3.

The link reaches you in one of two shapes: the host shows its own prompt to open a URL, or
the call fails with "Authorization required before this tool can run" followed by the
link. Relay that link exactly as it came — it belongs to this user and this request —
alone in a fenced code block, never as a markdown link or a bare URL:

```text
https://bedrock-agentcore.us-east-1.amazonaws.com/identities/oauth2/authorize?request_uri=…
```

The link works once, for about ten minutes: whatever opens it first spends it, and the
user then gets `{"message":"Invalid request"}`. Some hosts open a link they render before
the user clicks it — the ChatGPT app does — so a rendered link arrives already spent; a
code block is not rendered as a link and reaches the user unused. For the same reason,
never open, fetch or preview it yourself. Ask the user to copy it into their browser and
approve (normally one click, since they are already signed in), then retry the same call.
If they get "Invalid request", the link was already spent or is too old: retry the call
once for a fresh link and relay that one the same way, rather than sending them back to
the old one.

## 3. Connection refused

A connection that worked before is rejected: a `401`, "OAuth authorization required",
"Missing Bearer token", "Invalid JWT", or an OAuth code (`invalid_grant`,
`invalid_request`, `invalid_client`, `invalid_token`, `unauthorized_client`). These come
from the client's sign-in, not from the tool you called, so its arguments are never the
problem. Never quote the code at the user; say what broke in plain words, and do not say
"expired" unless you know it expired.

The client holds that sign-in, so the fix is the client's — clear it and connect again:

- **Claude Code**: `claude mcp logout plugin:helical-platform:helical && claude mcp login
  plugin:helical-platform:helical`, in the user's own terminal, or `/mcp` → the Helical
  server → **Re-authenticate**.
- **Codex**: `codex mcp logout helical && codex mcp login helical`.
- **ChatGPT**: **Settings** → **Plugins** → Helical → **Reconnect** (or
  **Disconnect**, then **Connect**).
- **Claude Desktop** and **claude.ai**: **Customize** → **Connectors** → Helical →
  **Connect**.

On any other host, name that client's own reconnect affordance or say you do not know it —
do not invent menu items.

**When the error does not say** which case it is, do not end the turn on "I cannot tell".
Check the status commands in case 1, then give the step that matches.

## Never build a link

Assembling a sign-in or authorization URL, or registering a client with `curl` to get
something clickable, authorizes something the plugin does not use: the user approves it,
it seems to work, and the next call fails the same way. Relay only a link the host or a
Helical tool printed.

## Then

`health()` and `whoami()` confirm the connection is back: both take no arguments, spend no
credit and start nothing. Prefer `whoami` when the user was mid-workflow, since it also
returns the current balance.

Retry the failed call once, and stop rather than loop if it fails the same way again.

**Before re-issuing anything that starts work**, check what the failed attempt left
behind — starting over is how one job gets billed twice:

- `listPendingConfirmations()` for a run queued but not yet approved. A `triggerEmbedding`
  that landed before the failure has already queued a confirmation, and re-triggering
  returns a 409 without giving the id back — so list it and resolve that one instead.
- `listDagRuns({ state: "running" })` for a run already going, and
  `getConfirmationStatus({ id })` to settle whether an approval that timed out actually
  launched.

"Nothing was charged" holds only when the failure stopped something before it started. An
approval spent before the connection dropped has already launched the run, so check before
saying it.

## Conventions

The next step in the first message, always · tell the three cases apart before fixing ·
check status with `claude mcp list` / `codex mcp list` where the client has one · call the
host's `authenticate` tool when it offers one · run `codex mcp login` yourself, hand
`claude mcp login` to the user · relay an authorization link exactly as it came, alone in
a code block and never opened by you, then retry · plain words, never the OAuth code · never build a link · retry once, then stop ·
check for a queued confirmation or a running run before re-issuing anything that spends
credit.

Referenced files: 1 — file references are metadata, not package contents.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package license
AGPL-3.0-or-later
Package author
Helical
Keywords
See keywords
Review scenarios
5 positive · 3 negativeScenario counts, not test results.

Declared capabilities

  • Upload datasets
  • Compute embeddings
  • Fine-tune models
  • Download results

Package observed Nov 3, 2026.

Technical details
First seen
Oct 24, 2026 · 12:40 UTC
Last seen
Nov 3, 2026 · 18:00 UTC
Latest observed change
Oct 28, 2026 · 13:02 UTC
Collection status
Collected

plugin_asdk_app_6abe37bd8c6c8191afaf84a1b31585b4

Download plugin data (JSON)