Skip to content

mcphost llms.txt changelog

dev.mcphost/mcphost

4 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • llms.txt +56 −14

    https://mcphost.dev/llms.txt

    − 2. `host.docs.put(name, content)` — once per document (this recipe's own−    corpus: 8 documents, ~22 KiB total, well under the 2 MiB per-document−    cap).− | tools:write | tools:read, tools:call | 182 |− 1. Declare the table (typed columns: text, integer, real, timestamp, boolean, json):−    ```− 2. Append your rows in batches of 200 -- five calls covers 1,000 rows:−    ```− | `resources` | yes | yes | `resources` | `host.table.list` | `resources/list`, `resources/read`, `resources/templates/list`, `resources/subscribe`, `resources/unsubscribe` | - |− Generated by `scripts/protocol-frames-check.sh` from `FRAME_SEQUENCES`; do not hand-edit. A response is `application/json` when it is one frame and `text/event-stream` when more than one frame is sent (`{1}` exactly one, `{0,1}` optional, `{0,n}` any number). Take the last `result` or `error` frame. Per request type, in order: `initialize` = `result` {1}; `tools/list` = `result` {1}; `tools/call:keyed` = `notifications/tools/list_changed` {0,1}, `notifications/prompts/list_changed` {0,1}, `resul− 3. `host.sync.get(sync_id="<step1.sync_id>")` -- the sync row; polled until the first receipt (`last_run`) exists− 4. `host.sync.list()`− 5. `host.sync.delete(sync_id="<step1.sync_id>")` -- stops the schedule; the `orders` table stays− Expected cost: 6 calls (the 1 signup included), within 90 s.+    An operator pauses and resumes signups without a shell with the admin+    tool `admin.signup_pause` (`message` to pause, optional `until`;+    `resume: true` to lift), which writes the same pause file.+ ### Sub-agents+ <!-- quickstart-recipe:sub_agent_key -->+ 1. As the lead: `host.key.delegate` `{"can_delegate":true,"label":"helper","scopes":["tools:call"],"ttl_s":3600}` returns `key`, `label`.+ 2. As the helper, with the `key` call 1 returned: `host.key.delegate` `{"scopes":["read","write","tools:write","state:read","state:write","secrets:write","keys:manage","admin"],"ttl_s":60}` fails `scope_escalation` (asking for `read`, `write`, `tools:write`, `state:read`, `state:write`, `secrets:write`, `keys:manage`, `admin`).+ 3. As the lead: `host.key.revoke` `{"cascade":true,"label":"helper"}` `revoked` >= 1.+ Hand the helper the `key` call 1 returned, never your own. It holds `tools:call` only: any other scope fails `scope_escalation`, as call 2 shows.+ Call 3 revokes it and its children; the helper's next call fails `key_revoked`.+ <!-- /quickstart-recipe:sub_agent_key -->+ Documents are text the host indexes itself; a vector index built elsewhere (LlamaIndex, Chroma, pgvector) is not importable: export its source documents and put them.+ 2. `host.docs.put_many(documents)` — up to 50 documents per call, each+    `{name, content}` (or `content_base64`); this recipe's own corpus is one+    call: 8 documents, ~22 KiB total, well under the 2 MiB per-document cap.+    Every document gets its own `stored` / `replaced` / `skipped` / `refused`+    row, so one refusal never undoes another.+ | tools:write | tools:read, tools:call | 184 |+ 1. One call, no file tool needed -- the server fetches the 1,000-row fixture itself:+    host.table.create(name="expenses", source_url="https://mcphost.dev/examples/sample-1000.csv")+    ```+ 2. Or declare the table yourself (typed columns: text, integer, real, timestamp,+    boolean, json) and append your rows in batches of 200 -- five calls covers 1,000 rows:+    ```+ | `resources` | yes | no | `resources` | `host.table.list` | `resources/list`, `resources/read`, `resources/templates/list`, `resources/subscribe`, `resources/unsubscribe` | - |+ Generated by `scripts/protocol-frames-check.sh` from `FRAME_SEQUENCES`; do not hand-edit. A response is `application/json` when it is one frame and `text/event-stream` when more than one frame is sent (`{1}` exactly one, `{0,1}` optional, `{0,n}` any number). Take the last `result` or `error` frame. Per request type, in order: `initialize` = `result` {1}; `tools/list` = `result` {1}; `tools/call:keyed` = `notifications/tools/list_changed` {0,1}, `notifications/prompts/list_changed` {0,1}, `resul+ - `host.docs.put_many`+ - `host.probe.progress`+ - `host_docs_put_many`+ - `host_probe_progress`+ | `chart_invalid_mark` | no | none | 1 | fix-and-resend |+ | `handle_not_found` | no | none | 1 | fix-and-resend |+ | `handle_quota_exceeded` | no | none | 1 | do-not-retry |+ | `lineage_blocked` | no | none | 1 | fix-and-resend |+ | `not_found` | no | none | 1 | fix-and-resend |+ | `quota_exceeded` | no | none | 1 | do-not-retry |+ | `table_already_exists` | no | none | 1 | fix-and-resend |+ | `table_bound_exceeded` | no | none | 1 | fix-and-resend |+ | `table_column_not_found` | no | none | 1 | fix-and-resend |+ | `table_not_found` | no | none | 1 | fix-and-resend |

  • llms.txt +200 −24

    https://mcphost.dev/llms.txt

    − mcphost.dev is the server's address; the Python module is 'import mcphost' (mcphost.state, mcphost.table, mcphost.docs, mcphost.blob, mcphost.lineage, mcphost.drift, mcphost.channel, mcphost.msg, mcphost.elicit).− 4. Publish a tool. A later call on this SAME connection without a key is−    refused, naming the tenant you already are -- reconnect through−    `onboarding.url` first, then call `host.tool_publish` there.−    `host.quickstart()`'s own `starter_tool` is a ready-to-publish `python`−    spec; its `publish_call` is exactly:−    Two real kinds: submit code (`python` -- source required,−    `args_schema`/`requirements` inferred if omitted) or wrap an API you−    already use (`http` -- url and method required, `args_schema` inferred−    if omitted). `echo` is a stub for testing the pipes, not a real tool.− client_secret, scopes)` -- `preset: "slack" | "github" | "google"` fills− `auth_url`/`token_url` from a built-in table; an explicit `auth_url:`/− Generated by `mcphost capabilities-section` from the `CAPABILITIES` registry -- the same rows `initialize.capabilities` and `GET /capabilities.json` are built from; do not hand-edit. A method of a primitive marked `no` answers `-32601` with `data.help_url` (`/help/capability_unsupported`) and `data.alternative`.− | primitive | implemented | list_changed | probe | alternative | methods |− |---|---|---|---|---|---|− | `tools` | yes | yes | `tools` | - | `tools/list`, `tools/call` |− | `resources` | no | no | `resources` | `host.table.list` | `resources/list`, `resources/read`, `resources/templates/list`, `resources/subscribe`, `resources/unsubscribe` |− | `prompts` | no | no | `prompts` | `host.quickstart` | `prompts/list`, `prompts/get` |− | `completions` | no | no | `completions` | `host.help.search` | `completion/complete` |− | `sampling` | no | no | `sampling` | - | `sampling/createMessage` |− | `elicitation` | yes | no | `elicitation` | `host.ask.create` | `elicitation/create` |− | `logging` | no | no | `logging` | - | `logging/setLevel` |− | `roots` | no | no | `roots` | - | `roots/list` |− Generated by `scripts/protocol-frames-check.sh` from `FRAME_SEQUENCES`; do not hand-edit. A response is `application/json` when it is one frame and `text/event-stream` when more than one frame is sent (`{1}` exactly one, `{0,1}` optional, `{0,n}` any number). Take the last `result` or `error` frame. Per request type, in order: `initialize` = `result` {1}; `tools/list` = `result` {1}; `tools/call:keyed` = `result` {1}; `tools/call:keyless_signup` = `notifications/tools/list_changed` {0,1}, `resul+    Reconnect rule: a later call on this SAME connection without a key is+    refused, naming the tenant you already are (`data.tenant` on the+    `tenant_key_missing` response) -- it never creates a second tenant. Use+    `onboarding.url` for every call from here on.+ mcphost.dev is the server's address; the Python module is 'import mcphost' (mcphost.state, mcphost.table, mcphost.docs, mcphost.index, mcphost.blob, mcphost.lineage, mcphost.drift, mcphost.channel, mcphost.msg, mcphost.sampling, mcphost.email, mcphost.elicit).+ 4. Publish a tool, reconnected through `onboarding.url`. `host.quickstart()`'s+    own `starter_tool` is a ready-to-publish `python` spec; its+    `publish_call` is exactly:+    Three real kinds: submit code (`python` -- source required,+    `args_schema`/`requirements` inferred if omitted; `js` -- JavaScript+    source defining `main(args)`, `args_schema` inferred if omitted) or wrap+    an API you already use (`http` -- url and method required, `args_schema`+    inferred if omitted). `echo` is a stub for testing the pipes, not a real+    tool.+ `host.tool.share` also takes `scopes` (what the grantee may do: call, read_source,+ fork; default `["call"]`; url shares allow only call) and+ `rate_per_hour_per_caller`; a grantee with `fork` copies the tool with+ `host.tool.fork(owner, tool, as)`, and after `host.tool.unshare` a past caller's+ next call fails `share_revoked`.+ <!-- recipe:docs-qa begin -->+ ## Recipe calls: Docs Q&A in a minute+ The exact calls, in order (generated from `recipes/docs-qa.toml`; do not hand-edit -- run `cargo xtask recipes`). Plan fit: free.+ Sign up first (`signup`), once per tenant: `owner`.+ 1. `host.docs.put(content="# VPN setup\n\nThe VPN client connects on port 1194 over UDP.\n", name="vpn-setup.md")`+ 2. `host.docs.put(content="# Onboarding\n\nOn day one you receive a laptop and a badge.\n", name="onboarding.md")`+ 3. `host.docs.status()` -- poll until index.lag_seconds == 0+    expect `$.index.lag_seconds` = 0+ 4. `host.tool_publish(kind="python", name="ask_docs", spec={"source":"<contents of examples/docs-qa/ask_docs.py>"})`+ 5. `host.tool_call(args={"k":5,"query":"What port does the VPN client connect on?"}, name="ask_docs")` -- every passage carries a name:offset citation+    expect `$.passages[0].name` = "vpn-setup.md"+ Expected cost: 6 calls (the 1 signup included), within 90 s.+ <!-- recipe:docs-qa end -->+ <!-- recipe:first-run begin -->+ ## Recipe calls: First run: plugin walk-through+ The exact calls, in order (generated from `recipes/first-run.toml`; do not hand-edit -- run `cargo xtask recipes`). Plan fit: free.+ Sign up first (`signup`), once per tenant: `owner`.+ 1. `host.tool_publish(kind="python", name="table_note", spec={"source":"<contents of examples/first-run/tools/table_note.py>"})` -- a real python tool, not a stub+    expect `$.kind` = "python"+ 2. `host.tool_test(args={"note":"hello"}, name="table_note")` -- a real write through the sandbox API+    expect `$.result.appended` = 1

    Showing the first 40 of each.

  • llms.txt +200 −42

    https://mcphost.dev/llms.txt

    − Irreversibility: calling it twice is a no-op, not an error or a crash —−   lists only the issuer(s) you registered with `host.oauth.issuer_set`,−   under `authorization_servers` -- never another tenant's.− - Concurrent calls: 20 concurrent calls host-wide; per tenant, 4 per tenant on the free plan (10 on pro) -- a refusal past your own cap is `capacity`/`scope: "tenant"` with a `retry_after_ms`; past the host cap it's `scope: "host"`− - `host.table.join_paths`− without a monitoring SaaS account or an unmonitored box polling for it.− Two tables, two tools, one schedule.− 1. Create the tables, on the monitoring tenant:−    host.state.table_create(name="targets", schema={"url": "text", "added_at": "real"}, primary_key="url")−    host.state.table_create(name="checks", schema={"url": "text", "code": "integer", "ms": "real", "at": "real"})− 2. Add up to 20 targets (a 21st is accepted but never probed -- `probe`'s−    own result reports the cap):−    host.state.insert(table="targets", rows=[{"url": "https://api.example.com/health", "added_at": 0}])− 3. Publish `probe` (`python`, `network: "public"`; source omitted below−    for space -- `examples/uptime-probes/tools/probe.py`): fetches every−    target with a 10 s per-URL timeout, writes one `checks` row per target,−    deletes `checks` rows older than 24 h so the table never outgrows 20−    targets x 288 runs/day, and caps a run at the first 20 targets by−    `added_at`:−    ```−    host.tool_publish(name="probe", kind="python", spec={"source": "...", "network": "public"})−    ```− 4. Schedule it every 5 minutes -- the free plan's floor:−    ```−    host.trigger.set(tool="probe", kind="schedule", schedule="*/5 * * * *")−    ```−    `host.trigger.fire(id)` runs it once immediately instead of waiting 5−    minutes; a real deployment just waits.− 5. Publish `status` (`python`; source omitted -- `examples/uptime-probes/tools/status.py`):−    reads `targets`/`checks` only, never fetches live, and returns each−    target's last result plus its 24 h up-percentage and (if currently−    down) `down_since`:−    ```−    host.tool_publish(name="status", kind="python", spec={"source": "..."})−    host.tool_call(name="status", args={})−    ```− Free-plan fit: 2 tools (of 50), 2 tables, 1 schedule (of 3); a 5-minute− schedule fires 1440 / 5 = 288 times a day -- 288 of the free plan's 500− calls/day, leaving headroom for `status` calls too. Alerting on a flip is− a one-line hand-off to the agent-inbox recipe: `probe` calls+ Installing mcphost from inside an MCP client: fetch+ [https://mcphost.dev/llms-install.md](https://mcphost.dev/llms-install.md) -- it lists the exact+ install step for every supported client; apply yours, then make the first call.+ <!-- domain-hint:start -->+ mcphost.dev is the server's address; the Python module is 'import mcphost' (mcphost.state, mcphost.table, mcphost.docs, mcphost.blob, mcphost.lineage, mcphost.drift, mcphost.channel, mcphost.msg, mcphost.elicit).+ <!-- domain-hint:end -->+    try_before_call: `args` may be omitted on host.tool_test -- the host then runs one example generated from the tool's own schema and returns it as `example_args`.+ Irreversibility: calling it twice returns `{disabled: true,+ already_offboarded: true}`, not an error or a crash —+ While an incident is open, every host-fault error (`storage`, `internal`,+ `exec_unavailable`, `timeout`, `rate_limited_host`) also carries+ `data.incident {id, since, status_url, impact, retry_after_s}`: the failure+ is the host's, not yours, so wait `retry_after_s`, then retry once, and+ report `status_url` instead of repeating the call. Errors that are your+ own (`tool_exception`, `args_invalid`) never carry it. A 5xx HTTP+ response carries the same seconds in `Retry-After`.+ ## Tell the host what you needed+ `host.feedback.send {kind, text, context?}` carries a sentence to the host's humans, and works before signup. Kinds: `missing_tool`, `wrong_error`, `docs_gap`, `bug`, `praise`. `text` is 1 to 2048 bytes (longer is `invalid_params` with `max_bytes`); `context` is an optional object up to 1 KB. It returns `feedback_id`. Anonymous senders get 5 per hour per IP, keyed tenants 60 per hour; over the cap is `rate_limited` with `retry_after_s`. `host.feedback.list` returns your own rows.+ ## Hand work to another agent+ `host.task.post {to, payload, deadline_s, dedupe_key?}` sends a task to one agent and returns `task_id`, `thread_id` and `status`. The recipient's `host.msg.wait` returns it with `data.task.task_id`; it calls `host.task.claim {task_id}`, then `host.task.complete {task_id, result}`, and your own `host.msg.wait` returns a message whose `data.task` carries the status and the result. `host.task.get {task_id}` reads one task. A task nobody finishes by its `deadline_s` goes `dead`, a `task.dead` messa+ - `mcphost.blob` -- read or store files of any type under this tenant (same store host.blob.* uses)+ - `mcphost.elicit` -- ask the user a typed question mid-call: the client's form when it can, the ask-user link otherwise+ ## Install in your client+ One line per client; the same table `/connect` and `host.quickstart` render.+ <!-- clients:start -->+ <!-- install-links:start -->+ **Claude Code**+ ```+ claude mcp add --transport http mcphost https://mcphost.dev/mcp+ ```+ Docs: <https://code.claude.com/docs/en/mcp> (checked 2026-10-09)+ **Cursor**+ [Add to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=mcphost&config=eyJ1cmwiOiJodHRwczovL21jcGhvc3QuZGV2L21jcCJ9), or add `https://mcphost.dev/mcp` to your MCP config directly.+ Docs: <https://docs.cursor.com/en/tools/mcp> (checked 2026-10-09)+ **VS Code**+ [Add to VS Code](vscode:mcp/install?%7B%22name%22%3A%22mcphost%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcphost%2Edev%2Fmcp%22%7D), or add `https://mcphost.dev/mcp` to your MCP config directly.+ Docs: <https://code.visualstudio.com/api/extension-guides/ai/mcp> (checked 2026-10-09)+ **Claude.ai**+ 1. Open Settings, then Connectors, then Add custom connector.+ 2. Name: mcphost

    Showing the first 40 of each.

  • llms.txt +185 −127

    https://mcphost.dev/llms.txt

    − time from signup to a tenant's first successful `host.tool_publish` is−    token, shown once), `namespace`, and `endpoint`. Signup is rate-limited−    to 5 per IP per hour. Pass `source` (e.g. `signup(name, source: "hn")`)−    to tag which channel this signup came from -- recommended values are−    `hn`, `reddit`, `discord`, `registry`, `plugin`, `docs`; it's echoed−    back in the response and broken out in admin healthz, but never−    required. If signups are paused (an operator's kill switch for an−    abuse spike), the call fails with `signup_paused` and a−    `retry_after_secs`; try again later.−    this exchange carries a dead credential. `host.key_rotate` invalidates−    here on -- e.g. `host.tool_publish`, `host.tool_call`. No reconnect or− 4. Publish a tool: `host.tool_publish(name, kind, spec)`. Call−    `host.quickstart` first — its `starter_tool` is a ready-to-publish−    `python` spec (reverses text, counts words) plus the exact−    `publish_call`/`test_call` to run; the documented first publish is a−    real tool, not a stub. Two real kinds: submit code (`python` — source−    required, `args_schema`/`requirements` inferred if omitted) or wrap an−    API you already use (`http` — url and method required, `args_schema`−    inferred if omitted). `echo` (returns its arguments; spec is a JSON−    Schema) is a stub for testing the pipes, not a real tool — it carries−    `stub: true` in `tools/list`. Before publishing anything, dry-run with−    `host.tool_publish({..., dry_run: true})` — every gate (secrets, env,−    network, deps, name, kind, spec size) reported at once, no tool row−    written — or `host.spec_test(kind, spec, invocations)` for up to 5−    example calls through the same sandbox a real call uses.− 5. Call your tool. Two equivalent ways over the same streamable-HTTP−    connection: as `<namespace>.<tool_name>` (its own entry in−    `tools/list`), or `host.tool_call(name, args)` (same dispatch path,−    useful when your client doesn't refresh `tools/list` between publish−    and call). `host.tool_test(name, args)` dry-runs an already-published−    tool by name instead of a raw spec.− 6. Check the plan and quota before you rely on volume:−    `billing.plans()` — the plan catalog, works anonymously.−    `billing.status()` — this tenant's plan and usage against each quota.− 7. Inspect and manage: `host.tool_list()`, `host.tool_logs(name)`,−    `host.tool_remove(name)`, `host.usage(window)`,−    `host.secret_set`/`host.secret_list()` (secrets stored AES-256-GCM−    encrypted). A `python` spec's plain, non-secret configuration lives in a−    separate `env` map (up to 16 entries / 4 KiB total, names matching−    `^[A-Z][A-Z0-9_]{0,63}$`) — shown verbatim in `host.tool_test`, unlike+ time from signup to a tenant's first successful `host.tool.publish` is+ 1. Connect to `https://mcphost.dev/mcp` -- paste that URL into your MCP+    client's config. No signup call, no credentials, nothing to paste into+    a header.+ 2. Make your first call. `host.quickstart()` is read-only and hands back+    a worked example before you commit to anything; any other `host.*`/+    `billing.*` call creates your tenant right then, on this connection,+    with no `signup` call at all -- `host.whoami()` is the simplest:+    ```+    host.whoami()+    ```+ 3. Read `onboarding.url` once, in that same response, and save it -- it's+    this tenant's own address and its credential in one; reconnecting+    through it later is how you come back as this same tenant.+    `onboarding.memory_hint` names where agents conventionally keep it.+ 4. Publish a tool. A later call on this SAME connection without a key is+    refused, naming the tenant you already are -- reconnect through+    `onboarding.url` first, then call `host.tool_publish` there.+    `host.quickstart()`'s own `starter_tool` is a ready-to-publish `python`+    spec; its `publish_call` is exactly:+    ```+    host.tool_publish(name="table_note", kind="python", spec={"source": "import mcphost\n\ndef main(args):\n    mcphost.table.create(name='quickstart_notes', columns={'note': 'text'})\n    mcphost.table.append(table='quickstart_notes', rows=[{'note': args.get('note', '')}])\n    return {'appended': 1}\n"})+    ```+    Two real kinds: submit code (`python` -- source required,+    `args_schema`/`requirements` inferred if omitted) or wrap an API you+    already use (`http` -- url and method required, `args_schema` inferred+    if omitted). `echo` is a stub for testing the pipes, not a real tool.+    Before publishing anything, dry-run with `host.tool_publish({...,+    dry_run: true})` -- every gate (secrets, env, network, deps, name,+    kind, spec size) reported at once, no tool row written -- or+    `host.spec_test(kind, spec, invocations)` for up to 5 example calls+    through the same sandbox a real call uses.+ 5. Call it -- `host.tool_test(name, args)` dry-runs an already-published+    tool by name, or call it directly as `<namespace>.<tool_name>` (its+    own entry in your `tools/list`):+    ```+    host.tool_test(name="table_note", args={"note": "hello"})+    ```+ 6. Share it with a teammate: `host.invite.create()` returns an+    `/i/<code>/mcp` URL -- whoever connects there and makes their own

    Showing the first 40 of each.