Skip to content

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

claude mcp add --transport http rhylthyme https://mcp.rhylthyme.com/mcp

Cursor (.cursor/mcp.json) and other clients that take a URL:

{ "mcpServers": { "rhylthyme": { "url": "https://mcp.rhylthyme.com/mcp" } } }

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:

/plugin marketplace add rhylthyme/rhylthyme-mcp
/plugin install rhylthyme@rhylthyme

Or from a shell:

claude plugin marketplace add rhylthyme/rhylthyme-mcp
claude plugin install rhylthyme@rhylthyme
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):

pip install rhylthyme-mcp        # or pipx install / uvx rhylthyme-mcp
{ "mcpServers": { "rhylthyme": { "command": "rhylthyme-mcp", "args": ["kitchen"] } } }

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, with kind of inFlight, maxConcurrent, offset or dependency.
  • resourceConflicts — every item tagged kind: "maxConcurrent" (more steps claim a task at one instant than its maxConcurrent) or kind: "inFlight" (more instances are between a replicated step and its barrier than replicates.maxInFlight allows).
  • 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 carry instanceOf / instanceIndex.
  • Wall-clock times for every step when you pass finishAt or startAt.
  • alerts on 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 of plan_schedule written 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, including cookies_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_text and review_program send 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_schedule and preview_timeline create 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

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, and metadata.importSteps carrying 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.