Skip to Content
Drive simulators from your coding agent

Guides

Drive simulators from your coding agent

rystic mcp is a local MCP server. Through it your coding agent starts a simulator, loads a scenario, seeds records, moves the clock, reads state and the calls and webhooks the simulator saw, diffs state against where it started, rolls back to a snapshot, saves the run as a test case and grades a later run against it, without writing a curl line.

Requires a current rystic CLI. If rystic help mcp reports an unknown command, run rystic self-update. New to Rystic? Install the CLI and sign in first.

The server speaks MCP over stdio: the client launches rystic mcp as a subprocess. Each tool is a thin wrapper over a CLI command or a control-plane route, so it answers what that command or route answers on your plan. A simulator it starts keeps running after the client exits; stop_twin or rystic stop ends it. The tool names say “twin”, meaning a simulator, and take the product names rystic list prints, such as stripe-twin.

Install

In Cursor or VS Code, one click adds the server: Add to Cursor, Add to VS Code or Add to VS Code Insiders. The editor opens and asks before it adds the entry. Like --print, the entry’s command is plain rystic, so the CLI must be on the editor’s PATH and signed in. When it is not on the PATH, use the command below.

One command writes the server entry into your client’s config:

rystic mcp install --client claude-code
# created /p/.mcp.json (claude-code, project scope)
# next: restart Claude Code in this directory and approve the project's rystic server when it asks

The entry’s command is the absolute path of the rystic binary you ran, so the client finds it even when rystic is not on its PATH. An existing config file is merged: other servers and keys stay. A file that is not valid JSON, comments and trailing commas included, is refused and left unmodified. A new user-scope file is created readable only by you (mode 0600).

rystic mcp install --client <claude-code|cursor|codex|vscode|windsurf> [--scope project|user] [--name rystic] [--command <path>] [--force] [--dry-run] [--print] [--json]
                   [--twin <simulator>] [--instance <name>] [--read-only] [--tools a,b,c]
FlagEffect
--clientclaude-code, cursor, codex, vscode or windsurf
--scope project|userwhere the entry goes; default project for Claude Code, Cursor, Codex and VS Code, user for Windsurf. The paths are in the table under Configure by hand
--namethe server’s name in the config, default rystic; another name adds a second entry beside an existing one
--command <path>the binary the client launches, instead of the absolute path of the running rystic
--forcereplace a different entry already under that name
--dry-runshow the file it would write; write nothing
--printprint the client’s copy-paste block, the same as the blocks under Configure by hand; write nothing. Its command is plain rystic, so rystic must be on the client’s PATH. Takes no other flag but --client, --json and the scope flags
--jsonthe result as JSON
--twin, --instance, --read-only, --toolsthe scope flags, checked as rystic mcp checks them and written into the entry’s args, --twin as the simulator’s full name: "args": ["mcp", "--twin", "stripe-twin", "--read-only"]

Running it again with nothing to change, or with a different entry already there:

unchanged /p/.mcp.json (claude-code, project scope): "rystic" is already installed
rystic: /p/.mcp.json: already has a different "rystic" server; pass --force to replace it, or --name to add another (not modified)

It exits 2 on a bad flag, 9 on a conflicting entry without --force, and 1 when the existing file cannot be read or is not valid.

The last line of a write names the next step:

Client and scopeNext step
Claude Code, projectrestart Claude Code in this directory and approve the project’s rystic server when it asks
Claude Code, userrestart Claude Code; /mcp lists the server
Cursorrestart Cursor, or turn the server on under Settings > MCP
Codex, projectrestart Codex in this directory; Codex reads .codex/config.toml only in a trusted project
Codex, userrestart Codex
VS Codereload VS Code, then start the server from the MCP: List Servers command
Windsurfrestart Windsurf, or refresh the MCP servers in Cascade

VS Code’s user scope is not written by rystic. code --add-mcp takes one entry with a name, not the whole file: the rystic entry from rystic mcp install --client vscode --print with "name": "rystic" added.

code --add-mcp '{"name":"rystic","type":"stdio","command":"rystic","args":["mcp"]}'

Or paste that entry, without name, under "servers" in the file MCP: Open User Configuration opens.

Configure by hand

Each block is a complete config file holding the rystic entry. Merge the entry into a file you already have.

ClientProject scopeUser scope
Claude Code.mcp.json~/.claude.json
Cursor.cursor/mcp.json~/.cursor/mcp.json
Codex.codex/config.toml (trusted projects only)~/.codex/config.toml
VS Code.vscode/mcp.jsoncode --add-mcp, or the file MCP: Open User Configuration opens (Install has the line)
Windsurfnone~/.config/devin/mcp_config.json; $XDG_CONFIG_HOME/devin/mcp_config.json when that is set, %AppData%\devin\mcp_config.json on Windows

Claude Code

{
  "mcpServers": {
    "rystic": {
      "type": "stdio",
      "command": "rystic",
      "args": [
        "mcp"
      ]
    }
  }
}

Cursor

{
  "mcpServers": {
    "rystic": {
      "type": "stdio",
      "command": "rystic",
      "args": [
        "mcp"
      ]
    }
  }
}

Codex

[mcp_servers.rystic]
command = "rystic"
args = ["mcp"]

VS Code

{
  "servers": {
    "rystic": {
      "type": "stdio",
      "command": "rystic",
      "args": [
        "mcp"
      ]
    }
  }
}

Windsurf

{
  "mcpServers": {
    "rystic": {
      "command": "rystic",
      "args": [
        "mcp"
      ]
    }
  }
}

If the client cannot find rystic, set command to its absolute path (command -v rystic prints it).

Scope the server

By default rystic mcp serves every tool on every simulator running on this machine. Four flags narrow it:

rystic mcp [--twin <simulator>] [--instance <name>] [--read-only] [--tools a,b,c]
FlagEffect
--twin <simulator>serves one simulator, named as rystic run takes it. Its tools take no instance or twin argument, and start_twin takes no name: each acts on that simulator’s one running instance, and list_twins lists only its instances. start_twin starts the instance, and refuses with name_conflict while one runs. A call naming another simulator or instance fails with out_of_scope
--instance <name>only with --twin: the instance to act on when several run, and the name start_twin gives the one it starts
--read-onlyserves only the read-only tools: list_twins, list_scenarios, inspect_state, list_calls, list_sent and diff_state
--tools a,b,cserves only the named tools. With --read-only, only read-only tools may be named

A bad scope flag exits 2 and names the valid choices. A scoped server’s instructions and its guide describe only what it serves, and a prompt that needs a tool it hides is hidden too. rystic mcp install takes the same flags and writes them into the client’s entry.

Call order

At startup the server hands the client instructions with the order of calls:

  1. list_twins for the running instances, or start_twin, which returns the URL.
  2. load_scenario, or seed, to set the starting state.
  3. Your own code acts against that URL. The simulator answers as the real API does; you do not call it through MCP.
  4. inspect_state, list_calls, list_sent and diff_state to read what happened.
  5. save_test_case to keep the run, or check_test_case to grade it against a saved case.

start_twin, load_scenario, reset and restore_twin set the baseline diff_state compares against; set_clock and advance_clock control time; snapshot_twin and restore_twin let an agent try one path, roll back and try another. The guide resource walks each step.

Tools

Every tool except list_twins and start_twin takes instance: the instance’s name, its simulator’s name when that simulator runs one instance, or <twin>/<name>. save_test_case and check_test_case take instances, one per simulator, in its place for a case across simulators. list_twins names them.

Each tool declares MCP annotations. Read-only tools change no state. Destructive tools replace or drop state; idempotent ones are safe to repeat with the same arguments. Every tool is closed-world except start_twin, which may download a simulator release and checks your license.

ToolArgumentsWhat it doesAnnotations
list_twinsnonethe instances running on this machine: name, simulator, version, URL, runner and status from a live checkread-only
start_twintwin (required), scenario, name, port, version, runner (native or docker), project, clock (RFC3339)starts a simulator in the background, as rystic run <twin> -d does, and returns its instance name and URL. A simulator that is not installed is pulled first, which can take a minute. A call that sends a progressToken gets progress notifications while it downloads, and canceling the call stops the download, or stops the simulator if it has already started. Takes a free port unless port is givenopen-world
stop_twininstancestops the instance and removes its record and diff baseline, as rystic stop does. Its snapshots staydestructive, idempotent
list_scenariosinstancethe named starting scenarios the simulator ships, each with a summaryread-only
load_scenarioinstance, namereplaces state with the named scenario, keeping the simulator’s built-in recordsdestructive, idempotent
seedinstance, bodymerges records into state. body is {register: {key: {field: value}}}; rystic describe <twin> -json shows each register with an example recorddestructive
set_clockinstance, time (RFC3339)pins the clock; events due by then fire. Returns the clock the simulator then readsdestructive, idempotent
advance_clockinstance, seconds or duration (exactly one)moves the clock by whole seconds, forward or, negative, back: 90s, 15m, 48h. Events fire as the clock passes themdestructive
resetinstancereturns the instance to the simulator’s built-in records, the state it boots in with no scenario, dropping scenario and seeded records, and clears the call and sent logsdestructive, idempotent
inspect_stateinstance, register, key, view, pagingthe state, resource then row key then row, a page at a time under state; view asks for one of the simulator’s named views instead, answered as {view, result}read-only
list_callsinstance, method, path_prefix, status, actor, pagingthe API calls served since the last reset, oldest first, each naming its callerread-only
list_sentinstance, effect, type, pagingwhat the simulator sent since the last reset: webhooks, emails and other outbound effects, oldest first, from the egress logread-only
diff_stateinstance, register, pagingrows created, changed and deleted per resource since the baseline; each changed field carries its JSON Pointer path, before and afterread-only
save_test_casename (required), instance or instances, prompt, out_dirsaves the run since the baseline as a test case and drafts its checks; see Save a run as a test casenone
check_test_casecase (required), instance or instances, out_dirgrades the run since the baseline against a saved case, with no model; see Check a later runnone
snapshot_twininstance, namesaves the instance’s state under name; omit name to list its snapshots. See Snapshotsnone
restore_twininstance, name (required)resets the instance and lands the named snapshotdestructive, idempotent

diff_state compares against the state the instance had when start_twin started it, load_scenario loaded a scenario, reset reset it or restore_twin restored a snapshot. seed leaves that baseline in place, so seeded rows read as created. For an instance started outside the server, with rystic run say, the first diff_state stores the current state as the baseline and answers "baseline": "first_seen" with an empty diff.

inspect_state, list_calls, list_sent and diff_state answer one page at a time: at most limit entries (default 50, at most 500) and about 20 KB. Pass a page’s next_cursor back as cursor for the next one. A cursor belongs to the baseline it was read against: after a reset, load_scenario or new baseline it is refused, and reading starts again without one.

Every page carries truncated. It is true when more pages remain or an entry was cut, and then hint names the exact call that gets the rest.

format sets how much of each entry a page carries:

Toolconcise (default)detailed
inspect_statestring values over 256 bytes cut; cut_rows lists the rows they were cut fromwhole rows
list_callseach call’s seq, at, method, path, status and actorthe simulator’s whole record
list_senteach event’s seq, effect, type and ownerthe payload too
diff_statecreated and deleted rows with string values over 256 bytes cut; changed rows as their fieldseach changed row’s state too

In either format an entry over 12 KB is cut and marked.

Filters narrow a page; matched counts the entries they match across every page:

ToolFilters
inspect_stateregister, and key with it for one row
list_callsmethod, path_prefix, status (a code such as 404, or a class such as "4xx"), actor
list_senteffect, type
diff_stateregister

list_calls and list_sent also report total, every entry since the last reset. In list_sent, delivery sums up webhook delivery with its newest 20 attempts in results, and results_total counts every attempt the simulator reports. tail, which starts the page at the last N entries, still works but is deprecated in favor of cursor.

A state larger than one page also comes with state_uri and a resource link to rystic://instances/{twin}/{name}/state, the whole state uncut. A named view that is a file, or larger than a page, comes back as its size and the URL to fetch it from.

Snapshots

snapshot_twin with a name saves the instance’s state, clock, id counters and pending scheduled events and, when your plan includes network shaping, the network profile. A snapshot of the same name is replaced, and the result’s overwrote says so. Names take letters, digits, ., _ and -, at most 64. Without a name it lists the instance’s snapshots.

restore_twin resets the instance, then lands the snapshot, so ids minted after the restore continue where the snapshot left off. The restored state becomes diff_state’s baseline. A snapshot taken on another version of the simulator is refused with snapshot_version_mismatch.

The CLI keeps snapshots on this machine, per simulator and instance name. They survive stop_twin and a restart of the same instance on the same version.

A snapshot does not hold, so a restore does not bring back:

  • the idempotency cache, so a replayed request makes a new object;
  • the request-id counter;
  • the calls and sent logs, which start empty;
  • the webhook delivery queue;
  • credential bindings;
  • upload sessions;
  • the rate limiter;
  • the clock’s wall offset;
  • scheduled events in full: only the pending events of the scenario’s own schedule come back, less any that uses a value a fired event bound, and a schedule loaded on its own replays from its start;
  • built-in records deleted before the snapshot, which the reset brings back;
  • whether a scenario installed the network profile: once restored, it stays through later resets.

snapshot_twin refuses an instance with a pending event that acts as a named caller (snapshot_unsupported) and a state too large to restore (snapshot_too_large). When the simulator refuses a snapshot after the reset, restore_twin fails with restore_failed and leaves the instance reset. On an older simulator release, every restore_twin result carries a warning: ids minted after the restore can collide with restored records, and per-account records can come back duplicated.

Save a run as a test case

save_test_case turns the run an agent just made into a regression test. It writes a grid cell directory, the same shape rystic grid writes: cell.json and, per simulator, twins/<grid name>/before.json, after.json, calls.json and egress.json, numbers exact. The grid name is the simulator’s name without -twin (linear for linear-twin), as rystic grade and the eval grid name it. Then it drafts the eval’s deterministic checks with rystic grade --draft into the cell’s eval.yaml, so rystic grade <cell_dir> passes on this run and fails on a later run that differs.

ArgumentMeaning
namethe case’s name: the eval name in cell.json and the temp directory’s prefix
instance or instancesone instance, or several, one per simulator, for a case across simulators
promptthe task the agent was given, the draft’s prompt. Omitted, the draft carries a placeholder and notes says to replace it
out_diran absolute path that does not exist yet or is an empty directory. Omitted, a new temp directory that is kept

It returns {name, cell_dir, eval, draft, twins, notes}; each twins entry carries instance, twin, scenario, baseline, baseline_taken_at, seeded and counts (created, changed, deleted).

  • Before is the instance’s baseline, from start_twin, load_scenario, reset, restore_twin or a first diff_state. Records seeded through seed after the baseline count as setup, not as the run’s. An instance with no baseline is refused with no_baseline.
  • After is the state now, with the calls and sent log since the last reset.
  • Rows a scenario’s own timeline seeds during the run count as the run’s changes.
  • The draft expects each row the run created or changed, forbids any other creation, change, caller or method, and allows each deletion the run made. It is a starting point: trim the checks to what the task requires before keeping them.

Check a later run

check_test_case grades a new run against a saved case, as rystic grade does, with no model. After save_test_case, reset or load_scenario the instance, run the agent again, then call check_test_case with case set to the cell_dir or eval path save_test_case returned, or any eval file, as an absolute path. It captures the run since the baseline into a new cell, under out_dir or a new temp directory, grades it and writes its verdict.json.

FieldMeaning
resultpass, fail, error, or incomplete when every graded check passed but one was skipped
reasonwhy the case got that result
cell_dirthe new run’s cell, with its verdict.json
checksper check: its id, result (pass, fail, error or skipped), detail and, for a failure, the rows behind it
notesanything to know about the run, such as a baseline scenario that differs from the case’s

A case directory’s checks/ YAML files are graded too. A script check is skipped, not run. The case’s world must name exactly the instances’ simulators, by full name (linear-twin) or grid name (linear).

Prompts

The server offers two prompts, which a client lists as prompts or slash commands:

PromptArguments
reproduce_webhook_bugtwin (required), scenario, description of what the webhook does wrong
write_test_for_flowtwin (required), scenario, flow (required), the flow in a sentence or two

Each walks the agent through start_twin, load_scenario or seed, the act step, list_sent, list_calls and diff_state, then save_test_case. twin takes a short name, stripe for stripe-twin. A simulator that is not running, installed or in your plan is refused with unknown_twin, and a scenario a running instance does not ship with unknown_scenario. Under --twin, the prompts take no twin argument.

Clients that support completion fill the arguments in:

  • twin completes from the running simulators first, then the installed ones and those your license names, without a network call.
  • scenario completes from a running instance of the simulator named in twin. With none running, it offers nothing.

Resources

URITypeHolds
rystic://guidetext/markdownthe call order step by step, the forms of the instance argument, every error code with its fix, and a worked example. A scoped server’s guide says what it serves
rystic://instances/{twin}/{name}/stateapplication/jsona running instance’s whole state, uncut: resource, then row key, then row
rystic://instances/{twin}/{name}/baselineapplication/jsonthe state diff_state compares the instance with, in the same shape, uncut: a deleted row and a changed field’s before value are whole here

{twin} is the full simulator name, such as stripe-twin, and {name} the instance’s name. Both template arguments complete from the running instances.

Example session

The agent’s prompt: “Test that our checkout code charges the customer once and sends one receipt webhook.”

start_twin {"twin": "stripe-twin", "scenario": "design_studio", "name": "stripe"}
→ {"instance": "stripe", "ref": "stripe-twin/stripe", "url": "http://localhost:53124", "port": 53124, "runner": "native", "baseline": "stored", …}

# the agent points its Stripe client at http://localhost:53124 and runs the checkout test

list_calls {"instance": "stripe", "method": "POST"}
→ {"calls": [the POST /v1/charges the test made, with its caller], "matched": 1, "truncated": false, …}

list_sent {"instance": "stripe"}
→ one charge.succeeded event

diff_state {"instance": "stripe"}
→ {"baseline": "start_twin", "counts": {"created": 1, "changed": 0, "deleted": 0}, "resources": […], "truncated": false, …}

advance_clock {"instance": "stripe", "duration": "48h"}
→ {"instance": "stripe", "clock": "…"}

save_test_case {"instance": "stripe", "name": "checkout-charges-once", "prompt": "Charge the customer once and send one receipt webhook."}
→ {"name": "checkout-charges-once", "cell_dir": "…/checkout-charges-once-…", "draft": "…", "twins": [{"instance": "stripe", "twin": "stripe-twin", "scenario": "design_studio", "counts": {"created": 1, "changed": 0, "deleted": 0}, …}], …}

# later, after a code change: load the scenario again, rerun the checkout test, grade the new run

load_scenario {"instance": "stripe", "name": "design_studio"}
check_test_case {"instance": "stripe", "case": "…/checkout-charges-once-…"}
→ {"result": "pass", "cell_dir": "…", "checks": [{"id": "nothing_destructive", "result": "pass", …}, …], …}

stop_twin {"instance": "stripe"}

Results and errors

Every tool declares an outputSchema describing its result shapes and the error object, and every result carries structuredContent that matches it, error results included. A successful result’s text content carries the same JSON, for a client that reads only text.

A failed call is an error result (isError: true), never a protocol error. Its structured content is {"error": {"code", "message", "cause", "fix", "retryable", …}}, and its text carries the same fields as lines. Extra fields appear where the code needs them, such as retry_after_ms, upgrade_url, twin_errors or snapshots. A malformed call is a bad_argument naming the argument: a wrong type, an unknown or missing argument, arguments that are not an object, a string over 64 KiB, or nesting over 64 levels.

A stdin line that is not one JSON-RPC message gets a JSON-RPC error, and the session goes on. That covers invalid JSON, a line nested over 10,000 levels, a line over 32 MiB, and any batch. Batches are not supported, so send each message on its own line. The error’s code is -32700 (parse error) or -32600 (invalid request). It carries the request’s id when the line names one and null otherwise, and each request in a batch gets its own error. The server logs each refused line to stderr. These are protocol errors, separate from the tool errors in the table below.

retryable says whether the same call, unchanged, may succeed later, and retry_after_ms, when present, how long to wait first. The table gives each code’s default. An error after a write that may have taken effect, such as a seed or advance_clock whose answer was lost, is not retryable, and its fix says how to check before repeating it. Tools that are safe to repeat (reset, load_scenario, set_clock) keep the default. rystic mcp -errors prints this table.

coderetryableretry_after_mscausefix
bad_argumentno-An argument is missing, unknown, of the wrong type, too large or out of range, or the arguments are not a JSON object.Correct the argument the message names; tools/list carries each tool’s input schema.
twin_not_runningno-No running instance has that name, or the instance did not answer.Call list_twins for the running instances; start one with start_twin, or rystic run <twin> -d where this server serves no start_twin.
instance_ambiguousno-The name matches more than one running instance.Pass the instance as /, one of the refs the message lists; a server started with —twin takes —instance instead.
feature_not_in_planno-The twin refused the call because your plan does not include the feature it needs.Upgrade at the error’s upgrade_url, or use a tool your plan includes; the error names the feature.
port_in_useno-Another instance or process holds the port start_twin asked for.Omit port, so the twin takes a free one, or name another port.
name_conflictno-An instance of this twin with that name is already running.Pass another name, or stop_twin the running instance first; under —twin, which pins the name, stop_twin it first.
unknown_scenariono-The twin has no scenario by that name.Call list_scenarios for the names this twin ships.
bad_seed_bodyno-The twin refused the seed body: not a non-empty object of {register: {key: {field: value}}}, or a register, key or field it does not accept.Fix what the error’s twin_errors name; rystic describe <twin> -json shows each register with an example record.
license_or_leaseno-The license or API key is missing, expired, refused, or does not cover this twin.Run rystic login (or rystic license activate <file>), then retry; rystic doctor names what is wrong.
unknown_twinno-No release of that twin exists, at that version or for this platform.Run rystic list for the twins your plan covers; omit version for the newest release.
download_failedyes-The downloaded release failed its checksum or would not unpack, so nothing was installed.Call start_twin again, which downloads afresh; if it fails again, rystic feedback with the message.
twin_unhealthyyes1000The twin ran but did not serve, or answered with a server error.Retry once after retry_after_ms; if it fails again, read the log lines the error carries, or rystic logs <name>, then stop_twin and start_twin again.
twin_refusedno-The twin refused the call with a status no other code covers.Read the twin’s message in the error; rystic docs <twin> describes its control routes.
out_of_scopeno-The call names a twin or instance other than the one this server serves (rystic mcp —twin, —instance).Call the tool with no twin or instance argument, so it acts on the served instance; for another twin, run another rystic mcp.
no_baselineno-The instance has no stored state to compare the run against: it was started outside this server and never reset, loaded or diffed through it.Call reset, load_scenario or diff_state on the instance before the run, then save_test_case or check_test_case after it.
unknown_snapshotno-The instance has no snapshot by that name; snapshots belong to one instance name of one twin.Pick one of the names the error’s snapshots lists, or call snapshot_twin without a name to list them; snapshot_twin takes a new one.
snapshot_version_mismatchno-The snapshot was taken on another version of the twin than the instance runs now, so its records may not fit this one.Take a new snapshot_twin on this version, or start_twin the instance at the version the message names and restore there.
snapshot_unsupportedno-The instance has a pending event that acts as a named caller, and a restore cannot bring back credential bindings, so a snapshot would not hold it.Let the event fire or cancel it, then call snapshot_twin again.
snapshot_too_largeno-The snapshot’s world document is over the size the twin’s seed route accepts, so a restore could not land it.Reduce the instance’s state (reset, or delete records) and snapshot again; this snapshot cannot be restored.
restore_failedno-The instance was reset, but the twin refused the snapshot’s world, so it now holds a reset state.Read the message and twin_errors; take the snapshot again on this twin, or rystic feedback with the message if it persists.
cannot_writeno-The cell’s files could not be written: out_dir holds files already, is a file, or is not writable.Pass an out_dir that does not exist yet or is an empty writable dir, or omit it for a new temp dir.
draft_failedno-rystic grade --draft could not draft an eval from the saved cell.Read the message; cell_dir keeps the files, and rystic grade --draft <cell_dir> reproduces it. If it persists, rystic feedback with the message.
case_not_foundno-The case check_test_case was given does not exist, or the dir holds no eval.yaml.Pass the cell_dir or eval path save_test_case returned, or an eval file, as an absolute path.
bad_caseno-The case’s eval does not parse or is not a valid eval, a checks/ file beside it is invalid, or every check it holds is a script.Fix what the message names, or save_test_case a run again for a fresh case.
case_mismatchno-The case’s world names other twins than the instances given run.Pass one instance of each twin the case’s world names: instance for one twin, instances for several.
grade_failedno-rystic grade could not grade the cell of the run check_test_case captured.Read the message; cell_dir keeps the files, and rystic grade <cell_dir> reproduces it. If it persists, rystic feedback with the message.
runtime_unavailableno-The docker runner needs a container runtime and none answered.Start Docker or Podman, or pass runner: native.
networkyes2000An endpoint Rystic needed (the release or registry) did not answer.Check the connection and retry after retry_after_ms.
internalyes-Something failed that no other code covers.Retry; if it persists, rystic feedback with the message.

The docs MCP is a different server

rystic mcpDocs MCP
Where it runson your machine, launched by the client over stdiohosted at https://www.rystic.ai/mcp, over HTTP
What it doesstarts and drives simulatorssearches and fetches these docs
Toolsthe 17 abovesearch_rystic_docs, fetch_rystic_doc
Needsthe rystic CLI and an API key or licensenothing

Add both: the docs MCP answers how a simulator behaves, rystic mcp runs one. Agent quick start → Machine-readable surfaces shows the one-line install for the docs MCP.

Last updated on