MCP Server¶
Rhylthyme runs a hosted MCP server. There is nothing to install: connect your assistant to a URL and it can validate, analyze and publish schedules as live timelines.
| Endpoint | For |
|---|---|
https://mcp.rhylthyme.com/mcp | anything (cooking, lab, events, workouts) |
https://mcp.rhylthyme.com/kitchen/mcp | cooking; adds cook_recipe |
https://mcp.rhylthyme.com/lab/mcp | lab protocols; adds run_protocol |
https://mcp.rhylthyme.com/events/mcp | run-of-shows; adds plan_event |
https://mcp.rhylthyme.com/gym/mcp | workouts; adds start_workout |
Transport is Streamable HTTP. Every public tool works with no account and no API key. Saving to your own library, recorded runs and imports use your Rhylthyme account through OAuth 2.1 (the assistant shows a Connect button and you sign in with Google, Apple or email).
What it does¶
Rhylthyme schedules work that has to happen against a clock, with several things going at once: a dinner on one oven, a lab protocol around an incubator, a conference run-of-show, an interval workout. A schedule is a program: parallel tracks of timed steps, with dependencies between steps and limits on shared equipment. Through the MCP server an assistant can:
- validate a program and explain every problem with a suggested fix;
- analyze it: when each step starts and ends, the total length, the critical path, resource conflicts, and clock times worked back from a deadline such as "everything ready at 6 pm";
- publish it as a live timeline on rhylthyme.com that anyone can follow on a phone, or return a static Gantt image;
- import recipes and protocols from a URL or from pasted text, and check the import against its source;
- search a public catalog of recipes, lab protocols, event templates and workouts;
- with your account, save programs, look at recorded runs, and calibrate step durations from how long they really took.
Sign-in and accounts¶
The public tools need no account: validate, analyze, publish, preview, equipment limits, the public catalog, contributed runs, the renderer, and searching import sources. Importing (from a URL or pasted text), the import review, and your library (saved programs, recorded runs, calibration) use your free rhylthyme.com account.
Sign-in is OAuth 2.1 with PKCE and dynamic client registration, so your assistant registers itself and no client ID is needed. When a tool needs your account the server answers with an OAuth challenge and the assistant shows a Connect or sign-in button; you sign in with Google, Apple or an emailed one-time code and approve access. In Claude, choose Sign in when needed ("Required when the server asks") when adding the connector. Assistants that cannot connect accounts can use the login tool, which returns a sign-in link and accepts the token you paste back (it lasts about an hour).
Connect¶
Claude (claude.ai, desktop, mobile): Settings → Connectors → Add custom connector → paste an endpoint URL.
ChatGPT: Settings → Apps & Connectors → Advanced settings → turn on Developer mode, then Create a connector with an endpoint URL as the MCP server URL. Until a connector is added ChatGPT cannot call Rhylthyme's tools and will fall back to browsing the website.
Claude Code
Cursor (.cursor/mcp.json) and other clients that take a URL:
Claude Code plugin¶
The plugin is the quickest way in for Claude Code. One install connects the hosted server and adds a skill that teaches Claude how to write a schedule well: extract the steps before relating them, validate, check for conflicts, work back from a deadline, and hand you a live timeline.
In Claude Code:
Or from a shell:
| What you get | |
|---|---|
| MCP server | https://mcp.rhylthyme.com/mcp: validate_program, analyze_schedule, visualize_schedule, catalog search and the rest, with no account |
| Skill | rhylthyme: authoring order, a complete example, fixes for the commonest validation findings, and references for the program format, the command line and worked examples |
Update with claude plugin marketplace update rhylthyme then claude plugin update rhylthyme@rhylthyme; remove with claude plugin uninstall rhylthyme@rhylthyme. The marketplace is the rhylthyme-mcp repository (.claude-plugin/marketplace.json); the skill's source is in rhylthyme-cli-runner and can also be copied into ~/.claude/skills/ by hand.
Then ask for what you want: "Plan Thanksgiving dinner for 8 with one oven, eating at 6 pm." The assistant builds the program, checks it, and gives you a link to a live timeline.
No connector? (for AI agents and scripts)¶
If you are an assistant without a Rhylthyme connector, do not drive the website in a browser and do not look for a local server. Use one of these; neither needs an account.
One HTTP request. The server is stateless, so a single JSON-RPC POST works with no handshake:
curl -s https://mcp.rhylthyme.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"visualize_schedule","arguments":{"program": PROGRAM_JSON}}}'
The result's structuredContent.url is the live timeline. Call validate_program the same way first and fix what it reports; call analyze_schedule with finishAt for clock times; tools/list lists everything. The program format is on the schema page, and resources/read of rhylthyme://guide/authoring returns a one-page guide.
The command-line tool (Python 3.12+):
pip install rhylthyme
rhylthyme validate dinner.json
rhylthyme analyze dinner.json --finish-at 18:00
rhylthyme publish dinner.json # prints the live-timeline URL
Clients that only speak stdio, and self-hosting¶
A client that can only launch a command can use the stdio bridge, which passes every request through to the hosted server (so the tools are always current):
args is optional: kitchen, lab, events, gym, or nothing for the general endpoint. npx -y mcp-remote https://mcp.rhylthyme.com/mcp does the same job without Python.
The server is open source (Node 20+). To run your own:
git clone https://github.com/rhylthyme/rhylthyme-mcp && cd rhylthyme-mcp
npm install && PORT=3000 npm start # http://localhost:3000/mcp
Validation, timing analysis, the renderer, resources and prompts then run in your process; catalog search, publishing and account tools still call rhylthyme.com.
rhylthyme-mcp 0.1.0
Version 0.1.0 (February 2026) was a local server with one tool. 0.1.1 and later are the bridge above; pip install -U rhylthyme-mcp to upgrade. The pip install "rhylthyme[mcp]" line from older versions of this page no longer applies.
Available Tools¶
| Tool | What it does | Account |
|---|---|---|
validate_program | Check a program: ids, dangling or circular dependencies, overlapping steps, missing resource limits, durations; each finding has a fix | no |
analyze_schedule | Start and end of every step, total length, critical path, resource conflicts, slack; clock times from finishAt / startAt | no |
visualize_schedule | Publish a program as a live, shareable timeline and return its URL, an ASCII Gantt and an itinerary | no |
preview_timeline | A static Gantt image of a program (planned against actual with a run) | no |
create_environment | Describe a workspace's equipment limits (one oven, two centrifuges) as resource constraints | no |
search_public_recipes | Search the public catalog by name or keyword | no |
load_public_recipe | Open a catalog entry: summary and live-timeline URL | no |
list_public_runs | Anonymous runs others contributed for one exact program version | no |
get_renderer_source | The open-source timeline renderer (Apache-2.0, about 90 KB) for pages that cannot load scripts | no |
import_from_source | Search, import or pick a random recipe or protocol (TheMealDB, Spoonacular, protocols.io, Opentrons, Benchling, Cooklang) | import and random: yes; search: no |
import_text | Turn pasted text (recipe, protocol, run sheet, training plan) into a validated program | yes |
review_program | A model checks an imported program against its source and reports what looks wrong | yes |
login | Sign-in link and token, for assistants that cannot connect accounts | no |
save_program | Save a program to your library | yes |
list_my_programs / load_program | List and open your saved programs | yes |
list_runs / load_run | Your recorded runs of a program, planned against actual | yes |
calibrate_program | Propose durations from your recorded runs | yes |
cook_recipe, whats_for_dinner (kitchen); run_protocol, random_protocol (lab); plan_event, random_event_template (events); start_workout, surprise_workout (gym) | Find, or pick at random, a public catalog entry and return its live timeline in one call | no |
Every tool has a title and read-only or destructive annotations. The sections below describe the ones with more to them.
visualize_schedule¶
Creates an interactive timeline visualization from a Rhylthyme program JSON.
Claude builds the program JSON from your natural language description, then calls this tool to render it. You describe what you want ("Plan a Thanksgiving dinner for 8 people"), and Claude handles the JSON structure.
What it does:
- Validates the program structure
- Sends it to rhylthyme.com for D3.js rendering
- Returns a summary of tracks, steps, and timing
analyze_schedule¶
Resolves a program onto the clock without publishing anything: every step's start and end, the makespan, the critical path, and — the part that answers "why is it this long?" — which constraint gates each edge of that path.
What it reports:
criticalPath— the chain that sets the makespan.bindingConstraints— one entry per critical-path edge, withkindofinFlight,maxConcurrent,offsetordependency.resourceConflicts— every item taggedkind: "maxConcurrent"(more steps claim a task at one instant than itsmaxConcurrent) orkind: "inFlight"(more instances are between a replicated step and its barrier thanreplicates.maxInFlightallows).inFlight— the in-flight window of each instance of a capped replicated step: from the instance's start until the last of its per-instance (instances: "each") descendants ends.- Per-track slack, with instance sub-tracks as their own rows tagged
parentTrackId; steps carryinstanceOf/instanceIndex. - Wall-clock times for every step when you pass
finishAtorstartAt. alertson each step: the planned fire time of every step alert that can be placed on the plan (see Alerts and Notifications).
Analysing against your own history. Once a program has been run, the planned durations are checkable. Pass history (run records, as load_run returns them) — or just program_id and token, and the tool loads your own recorded runs of that program — and every step with enough measurements gains a predicted object beside its planned duration:
"predicted": { "seconds": 1230.6, "low": 1134.9, "high": 1304.2,
"basis": "model", "n": 20, "factors": [{"key": "turkeyKg", "coef": 86.9}] }
basis is identical (runs of the same program version, environment and variance factors — their median and P10/P90), model (a per-step regression on the factors whose correlation with the observed duration clears a threshold) or none (no usable measurement, or the step is one the executor decides rather than one the process determines). The result also carries predictedMakespan and predictedCriticalPath beside the planned ones.
useDurations: "predicted" goes further and replans: the makespan, the wall-clock itinerary, the critical path and the conflicts are all computed from the predicted durations instead of the authored ones. That is the honest answer to "when do I start if we eat at six?" once the history knows how long the roast really takes. The default stays "planned", so a program is analysed on what it says unless you ask otherwise, and with no history the output is exactly what it always was.
Example output — three trays of cookies, one oven, a cooling rack that holds two (replicates: { count: 3, mode: "serial", maxInFlight: 2 }):
**Makespan:** 1h 14m across 4 tracks, 8 steps.
**Critical path:** Mix dough → Bake tray (1 of 3) → Cool on rack (1 of 3) → Bake tray (3 of 3) → Cool on rack (3 of 3) → Box cookies
**Binding constraints:** `rack` (in-flight ≤ 2) gates `bake-r3`.
**Peak concurrency:** 2 steps at 27:00.
**Resource conflicts:** none.
**In-flight windows (1):**
- `bake` ×3 through `rack`: maxInFlight 2, peak 2 at 27:00 — #1 15:00–42:00, #2 27:00–54:00, #3 42:00–1:09:00
The third tray is held by the rack, not the oven: the oven is free at 39 minutes, but the first tray only leaves the rack at 42. Without the in-flight cap the same kitchen reports a maxConcurrent conflict on the rack instead — trays piling up with nowhere to go — and every binding constraint reads dependency.
import_from_source¶
Imports recipes or lab protocols from external databases.
Supported sources:
| Source | Content | Actions |
|---|---|---|
| Spoonacular | Recipes with nutrition and equipment | search, import, random |
| TheMealDB | Recipe database | search, import, random |
| protocols.io | Laboratory protocols | search, import |
Workflow: Claude searches for recipes, imports the best matches, then composes them into a unified multi-track schedule. For multi-dish meals, all dishes are combined into a single visualization with coordinated timing.
import_text¶
Turns a block of pasted text into a program. Use it when there is no URL and no supported service — a recipe off a card, a method copied out of a PDF, a run sheet in an email — and see Importing pasted text below for what it costs and what comes back.
list_runs¶
Lists the recorded executions of one saved program, newest first. A run record is written whenever somebody plays the live timeline (or runs rhylthyme run in the terminal): the durations the program planned, frozen at the moment the run started, beside the durations that actually happened. See the runs schema reference.
| argument | required | what it does |
|---|---|---|
program_id | yes | The program UUID, from list_my_programs or the program's URL |
token | yes | The user's Rhylthyme access token from login |
Each line gives the start time, the outcome (completed, aborted, abandoned), the actual makespan against the planned one with the percentage deviation, whether the clock was paused or the run was played at a speed other than 1, and the run id to pass to load_run.
load_run¶
Opens one recorded execution by run id and returns a planned-versus-actual table per step: duration kind, planned duration, actual duration with its deviation, what ended the step (executor — a person marked it done — timer, trigger or abort) and how long the clock was paused while it was running.
| argument | required | what it does |
|---|---|---|
run_id | yes | The run UUID from list_runs |
token | yes | The user's Rhylthyme access token from login |
Only steps a person ended, unpaused, at speed 1, in a completed run measure how long the work really takes; a timer ending only confirms that the timer worked. Runs are private to the person who ran them, even for a public program — both tools return the caller's own runs and nobody else's.
list_public_runs¶
Lists the runs other people have contributed for one exact program version, newest first, with the median actual total against the planned one. Contribution is opt-in per run: a contributed record carries no account, no program id and no step notes, only the timings and the answers the executor gave to the program's declared variance factors. Because the rows belong to nobody, this tool needs no login.
| argument | required | what it does |
|---|---|---|
program_hash | either this | The canonical program hash, sha256: plus 64 hex characters — a run record's programVersion, or programs.program_hash |
program | or this | The program JSON, hashed by the tool with the same canonical hash the runtimes record |
limit | no | How many runs to return (1–200, default 50) |
Contributed runs are keyed by the exact program JSON, so a program that has been edited since it was last run has no contributed history until somebody runs the new version. What is and is not stored is set out in privacy; the contribution checkbox itself is described in your account.
calibrate_program¶
Proposes new durations for one of your saved programs from its recorded runs, with the evidence, and never saves anything.
| argument | required | what it does |
|---|---|---|
program_id | usually | The program UUID whose runs are the evidence; with no program, also the JSON to calibrate |
program | or this | The program JSON to calibrate, when it differs from what is saved |
history | no | Run records to use instead of the ones stored against program_id |
k | no | Measurements a step needs before it gets a proposal (default 5) |
since | no | Only runs started on or after this date — "the last month's cooks only" |
accept | no | "all", or the step ids to write. The result then also carries the calibrated program |
token | yes | Your access token: the runs are private to whoever ran them |
For every step you ended by hand in enough runs, the median becomes the proposed default and the 10th/90th percentiles the proposed range — widened, never narrowed, so the range you wrote is always still inside the one proposed. An indefinite step gets a default and no invented range, because its end is your decision. A fixed step never gets a number at all: history can only say how late its planned end really was, so one that consistently overruns comes back with a "consider variable" note and its lag. A step with fewer than k measurements comes back as skipped with its statistics, so you can see how close it is.
The result is a table — step, type, n, current, proposed, median, IQR, delta, note — and one line saying what accepting everything would do to the makespan and the critical path. Nothing is written: with accept you get the calibrated program back as a value, each changed duration carrying calibratedFrom: {runs, asOf, programVersion}, and keeping it is a separate save_program call. The same thing behind a button is Calibrate from my runs in the player page.
preview_timeline¶
Renders a program as a static Gantt-chart PNG, no prose. Pass a recorded run as well (the run object from load_run) and the picture becomes a comparison: each step's real bar over a thin ghost bar at its planned position, outlined green where it finished early and amber where it ran late. See planned vs actual.
Resources and the plan_schedule prompt¶
Besides tools, the server publishes four kinds of MCP resource the model can pull on demand:
rhylthyme://schema/program— the full program JSON Schema (0.3.0-alpha).rhylthyme://guide/authoring— a one-page authoring cheat-sheet.rhylthyme://guide/extraction— the four turns ofplan_schedulewritten out as prose, with the expected output shape for each, for hosts that cannot run a multi-message prompt.rhylthyme://examples/<name>— complete, valid programs to pattern-match, includingcookies_three_trays, the three-trays program analysed above.
The authoring guide has a section "Repeating work: per-instance chains, barriers and in-flight limits". It documents replicates (count, mode, delay), the instances value on step-referencing triggers — "each" to run the next step once per instance, "all" for the barrier that waits for every instance, "any" for the first — and replicates.maxInFlight, the cap on how many instances may sit between a replicated step and its barrier. It contrasts maxInFlight with maxConcurrent ("hold upstream, don't strand downstream"), carries the three-trays-of-cookies program as a validated snippet, and lists the validator codes an author will see (E_INSTANCES_ON_SINGLE, E_EACH_WITH_REPLICATES, E_EACH_COUNT_MISMATCH, E_INFLIGHT_GT_COUNT, E_INFLIGHT_NO_CHAIN, W_UNBARRIERED_CHAIN and the informational I_IMPLICIT_BARRIER).
plan_schedule: four turns, not one¶
plan_schedule(goal, finishAt?, constraints?, sourceText?) returns four user messages. The host sends them in order in one conversation, so each turn sees the answers to the ones before it. Agents get step lists right and cross-track dependencies wrong, so extraction (turn 3) and relationship inference (turn 4) are deliberately separate turns:
| turn | what it asks for | expected output |
|---|---|---|
| T1 read-back | What is being made, for how many, by when, under what limits — in one paragraph, before any JSON exists. Catches a truncated source or a misread goal early. | {summary, servesOrScale, deadline?, constraints[]} |
| T2 model check | The program model restated in the model's own words (tracks are sequential; one duration kind per step; triggers link steps; every task needs a resourceConstraint; stepIds are global), plus the resource constraints it expects to declare. | acknowledgement + resourceConstraints[] |
| T3 extraction | Every timed activity, in the order the text gives it, with its duration, the resource it occupies, and the exact words it came from. No tracks, no triggers yet. | flat step list with sourceSpan and inferred |
| T4 relationships | Assign each step to a track, give it a trigger from the trigger vocabulary, then improve the dependency question ("which activities depend on which…") and answer the improved one, so implied waits — cooling, resting, preheating, proofing — surface. Then the program, then validate → analyze → visualize. | full program JSON |
T2 names replicates, instances: "each"/"all" and maxInFlight, so a goal like "12 samples, the rotor holds 6" or "three trays, one oven, the rack holds two" reaches for one replicated step with an in-flight cap instead of a hand-copied track per sample.
sourceText is the new optional argument: a recipe, a protocol, a run sheet. It is embedded in T1 and T3 — the two turns that read the source — and nowhere else. Without it, the steps are extracted from goal alone and both turns say so rather than inventing a source.
Provenance. Every step keeps what turn 3 found, under metadata:
"metadata": {
"sourceSpan": { "quote": "Roast the turkey for 3 hours", "occurrence": 1 },
"inferred": false
}
A sourceSpan is a quoted substring plus which occurrence of it is meant, so it survives whitespace edits and stays unambiguous when a phrase repeats. A step the source never stated — preheating, resting, a sound-check — carries "inferred": true and no span.
A short transcript (kitchen endpoint, goal "three trays of cookies", constraints "one oven, the cooling rack holds two"):
→ T1 Turn 1 of 4 — read-back. … Resource limits: one oven, the cooling
rack holds two. … Read all of it before you answer.
←
Three trays of cookies, baked one tray at a time in a single oven,
with a cooling rack that holds two trays.
{"summary": "…", "servesOrScale": "3 trays", "constraints":
["one oven", "cooling rack holds two"]}
→ T2 Turn 2 of 4 — model check. … restate the target data model …
← Tracks are sequential; steps in one track never overlap …
{"acknowledgement": "…", "resourceConstraints":
[{"task":"prep","maxConcurrent":1},{"task":"oven","maxConcurrent":1},
{"task":"rack","maxConcurrent":2}], "actors": 1}
→ T3 Turn 3 of 4 — extraction. Imagine you have to carry out this recipe
yourself, in a kitchen where you have: one oven, the cooling rack
holds two …
← {"steps": [{"stepId":"mix", …, "sourceSpan":{"quote":"Mix the
dough","occurrence":1}, "inferred": false}, …
{"stepId":"preheat", …, "sourceSpan": null, "inferred": true}]}
→ T4 Turn 4 of 4 — relationships … suggest a better version of the
question "which activities depend on which …"
← Improved question: "which steps are held not by the oven but by the
rack …" → bake gets replicates.count 3 with maxInFlight 2, cool
chains with instances "each", box waits with "all".
{"schemaVersion": "0.3.0-alpha", …}
→ validate_program → analyze_schedule → visualize_schedule
Hosts that cannot run a multi-message prompt get the same four turns, slots and output shapes from rhylthyme://guide/extraction.
The prompt structure follows Almuntashiri, Ibáñez & Chapman (ProvenanceWeek '25), who measured prompt patterns for extracting structured records from text: confirming the source and the target model before extracting helps, and relationships are the weak component. The evaluation harness in rhylthyme-cli-runner (rhylthyme eval-prompts) scores both this structure and the previous single-message prompt against an expert gold set. The measured numbers live in exactly one place: rhylthyme-cli-runner's README, "Evaluating prompts".
Example Usage¶
Try these prompts after connecting:
- "Create a schedule for making chicken tikka masala with naan bread"
- "Plan a Thanksgiving dinner with turkey, mashed potatoes, green beans, and pumpkin pie"
- "Import a random recipe from Spoonacular and visualize it"
- "Schedule an RNA extraction protocol"
- "Three trays of cookies, one oven, the cooling rack holds two"
Claude will confirm resource constraints (e.g., "You have 1 oven, 4 stovetop burners — correct?") before generating the visualization.
Data and privacy¶
- The MCP server calls only Rhylthyme's own API. Import tools fetch public recipes and protocols from their sources (TheMealDB, Spoonacular, protocols.io, Cooklang URLs) or read content you supply (Opentrons scripts, Benchling).
import_textandreview_programsend the text you paste (and the program being reviewed) to a model provider (OpenRouter) to extract and check steps; no other tool sends anything to a model provider. - Request logging records the method, tool name, argument names, a few fixed values (import source, action, environment type), timing and a hashed client id: no free text, programs or conversation content.
- Programs and runs are stored only when you save them, and only you can see them unless you publish or share.
visualize_scheduleandpreview_timelinecreate a public share link, as their descriptions say. - Timeline images are drawn from program data by the open-source renderer; no AI-generated images, audio or video. No payments, ads or sponsored content.
Details, retention and deletion are in the privacy policy.
Troubleshooting¶
| Symptom | What to do |
|---|---|
| A tool says it needs your Rhylthyme account | Connect the account when the assistant offers (in Claude, the connector's authentication must be Sign in when needed); otherwise ask the assistant to use login and paste the token back. Tokens from login last about an hour. |
| The connector was added with "No sign-in" | Remove it and add it again with Sign in when needed; public tools keep working either way. |
visualize_schedule refuses a program | It validates first. The reply lists each problem with a fix; validate_program shows the same list. |
| An import has odd durations or missing steps | Ask for a review (review_program); it compares the program with the source text and lists what looks wrong. |
| "Not a public program" or "not found" when opening an id | Catalog ids come from search_public_recipes; your own programs from list_my_programs. |
| A catalog search is slow for a very common word | Add a second word ("chicken curry" rather than "chicken"). |
| Your client only supports stdio | Use the stdio bridge. |
| Anything else | Check the server is up: curl -s https://mcp.rhylthyme.com/.well-known/oauth-protected-resource; then contact support (below). |
Support¶
- Email: support@rhylthyme.com
- Issues: github.com/rhylthyme/rhylthyme-mcp/issues
- Source (Apache-2.0): github.com/rhylthyme/rhylthyme-mcp
Compatibility¶
Any MCP client that supports Streamable HTTP can use the hosted server: Claude (all apps), Claude Code, ChatGPT (developer-mode connectors), Cursor, the Claude and OpenAI APIs' MCP connectors, and the MCP Inspector. The server is listed in the MCP Registry as com.rhylthyme/rhylthyme.
Importing pasted text¶
import_from_source needs a source with structure to read. When the user simply has the text, import_text runs the four turns above on the server and returns the program plus the source span every step came from.
{
"text": "Saturday Brunch for Four\n\nEverything on the table at ten…",
"environmentType": "kitchen",
"deadline": "10:00",
"hints": "one oven, two burners, one cook",
"token": "<from the login tool>"
}
text and environmentType are required; deadline and hints fill the same slots the plan_schedule arguments do — the deadline line in turn 1 and "everything must be ready at …" in turn 3, and the environment phrase the scenario prompt uses ("in a kitchen where you have: one oven, two burners, one cook").
What comes back
- The program: validated, multi-track, recording
metadata.source.type = "llm-text"with the model, the turn scores and the chunk count, andmetadata.importStepscarrying the span list. - A step → span table: every step with the exact words it came from, or marked inferred when the text never stated it. The span format is the one described above — a quoted substring plus which occurrence of it is meant — and each span is checked against the source, so a paraphrase is flagged rather than silently trusted.
- The turn scores (
read-back 2/2, model check 2/2) and, when the source was long enough to split, how many chunks turn 3 was run over.
Chunking. Turn 1's read-back is the check on whether the whole source arrived. If the text is long (over roughly 6,000 tokens) or the read-back never mentions the end of it, turn 3 is run once per section and the step lists are merged — deduplicated by source span, in the order the sections appear — before the single relationships turn.
Cost and limits. Four model calls at minimum, plus one per turn retry, one per extra chunk, and up to two validator fix rounds. Sign-in is required like every import, the daily cap is 20 per account under its own import_text quota, and a per-call token ceiling aborts a run that is looping. When a turn fails, the error names it — T1, T2, T3 or T4 — so you can tell "the model never read the source" from "the program would not validate".
Enriching a Structural Import¶
import_from_source converts a recipe or protocol URL into a program, but the importers read structure rather than meaning, so what comes back is a single track of steps chained head-to-tail. Passing enrich: true alongside action: "import" runs turn 4 of plan_schedule — the relationships turn — over that step list on the server and returns a multi-track program instead.
{
"source": "protocolsio",
"action": "import",
"query": "https://www.protocols.io/view/western-and-dot-blot-j569cq9h7",
"token": "<from the login tool>",
"enrich": true
}
Steps, durations and resources are kept exactly as imported, including the source words each step came from (metadata.sourceSpan). Track membership and every startTrigger are inferred, and a step the model adds that the source never stated is marked metadata.inferred: true. The program records metadata.source.enriched: true and the model that did it, and the result is validated with the same checks as validate_program, with a bounded fix loop.
Enrichment costs a model call, so it is login-gated like every import and capped at 20 per day per account under its own rate-limit quota. It can never lose the import: if the model is unavailable or the enriched program will not validate, the tool returns the plain one-track import with a one-line note saying why.
On one protocols.io protocol of 32 steps, enrichment turned the single imported track into three — the main western blot, a sequential detergent extraction and a set of dot blots — joined by a cross-track afterStep trigger, with the program still passing both validators.