# mcphost

Host your MCP tool over streamable HTTP in one command.

## signup

Create a tenant and receive a bearer key and namespace. Unauthenticated.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | display name |

## host.whoami

Return the calling tenant's identity.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_publish

Publish a tool of a registered kind under this tenant's namespace. Minimal example spec per kind: chain -- spec: {"steps":[{"args":{"since":"$.input.since"},"tool":"fetch_rows"},{"args":{"rows":"$.prev.result.rows"},"tool":"write_rows"}]}. steps run in order; each step's args may pull from $.input (this call's own args), $.prev (the previous step's result), or $.steps[i] (any earlier step's result by 0-based index). echo -- spec: {"schema":{"properties":{"msg":{"type":"string"}},"required":["msg"],"type":"object"}}. spec.schema is any JSON Schema; a call echoes back the arguments it was given, validated against it. http -- spec: {"method":"GET","url":"https://api.example.com/items/{{id}}"}. url must be an absolute https URL; method and url are the only required fields -- args_schema is inferred from the url/header/body templates when omitted. python -- spec: {"source":"def main(args):\n    return {\"doubled\": args[\"n\"] * 2}\n"}. only source is required -- args_schema and requirements are both inferred from it (tool-infer, v0.4.0); source must define main(args). Name must match ^[a-z][a-z0-9_]{1,40}$. A rejection names the failing field, what was expected, and a corrected example -- fix it and resubmit. Try `host.tool_test` on a published tool before a real call, or call `host.quickstart(kind)` for a filled-in worked example.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `kind` | string | yes |  |
| `name` | string | yes |  |
| `spec` | object | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.quickstart

Return the shortest ordered sequence of calls to a working tool of `kind`, with your namespace and a filled-in example already substituted in, plus the current limits. Read-only. Call this before host.tool_publish if you're not sure what a spec should look like. Unauthenticated callers get the signup step first.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `kind` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_list

List this tenant's published tools.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_remove

Remove a published tool by its local name.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_logs

Return the most recent log lines for one of this tenant's tools.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no |  |
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_test

Dry-run a published tool: performs the real call but records no `calls` row and echoes the rendered request back with secrets redacted, for debugging a spec.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `args` | object | yes |  |
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.bridge_test

Dry-run an `http`-kind spec (typically a declarative REST-bridge `upstream` spec) against its real upstream without publishing it: no tool is created, no `calls` row is written, and the rendered request is echoed back with secrets redacted, same as host.tool_test but for a spec you haven't published yet. An invalid spec reports the same failure class host.tool_publish would.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `args` | object | yes |  |
| `spec` | object | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_run

Debug run of a published tool: the same sandbox and limits as a real call, but returns full stdout and stderr (each capped at 64 KiB) and the exit code alongside the result, and records no `calls` row and no metering. Only kinds with a notion of a subprocess (`python`) support this; other kinds return `tool_run_unsupported`. Rate-limited to 30 calls per tenant per minute, independent of `host.usage`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `args` | object | yes |  |
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.tool_call

Invoke a tool this tenant has already published, by its local name -- the same real, metered call as calling it directly by its namespaced name (<namespace>.<name>), for a session that has no way to see its own namespaced tool name yet. Unlike host.tool_test, this counts toward host.usage and appears in host.tool_logs.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `args` | object | yes |  |
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.usage

Calls, errors and duration percentiles for this tenant over a window.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |
| `window` | string | no |  |

## host.secret_set

Store an encrypted secret value under this tenant's namespace.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |
| `value` | string | yes |  |

## host.secret_list

List this tenant's secret names (never their values).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.registry_publish

Publish this tenant's server.json to the configured MCP registry (requires --registry-url and admin.tenant_verify_namespace first).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.get

Read one key from this tenant's key-value state namespace. Returns found: false (not an error) if the key was never set.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `key` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.set

Write one key in this tenant's key-value state namespace; value may be any JSON value. Overrun of the plan's state_bytes_max quota fails with state_quota_exceeded and writes nothing.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `key` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |
| `value` | any | yes |  |

## host.state.delete

Delete one key from this tenant's key-value state namespace.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `key` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.list

List keys (with their current values) in this tenant's key-value state namespace, optionally filtered by prefix.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no |  |
| `prefix` | string | no |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.table_create

Declare (or replace the schema of) a table in this tenant's state store. schema is {"column": "text"|"integer"|"real"|"boolean"|"json"}; primary_key, if given, must name one of schema's columns -- an insert whose row matches an existing row's primary_key value replaces it.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes |  |
| `primary_key` | string | no |  |
| `schema` | object | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.table_drop

Drop a declared table and every row it holds.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.insert

Insert one row (an object) or several (an array of objects) into a declared table. Each row is validated against the table's schema first -- a type mismatch fails the whole call with state_schema_violation and writes nothing.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `rows` | any | yes |  |
| `table` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## host.state.query

Read rows from a declared table, optionally filtered (where: "field op value", ops = != < <= > >=, clauses joined by ' and '), ordered (order_by: "field" or "field desc") and capped (limit).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no |  |
| `order_by` | string | no |  |
| `table` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |
| `where` | string | no |  |

## host.state.delete_rows

Delete rows from a declared table matching an optional where filter (same grammar as host.state.query); omitting where deletes every row in the table.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `table` | string | yes |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |
| `where` | string | no |  |

## billing.plans

The plan catalog (price and quotas per plan) and whether Stripe billing is configured on this host. Anonymous callers get the same answer as tenants.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## billing.status

This tenant's plan, usage against each quota, and when the daily call quota resets.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## billing.checkout

Create (or reuse an open one for the same plan) a Stripe Checkout URL to upgrade this tenant, defaulting to the pro plan. Returns billing_unavailable if this host has no Stripe key configured -- call billing.plans first to check.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `plan` | string | no |  |
| `tenant_key` | string | no | The key `signup` returned. Required only when this connection carries no Authorization: Bearer header -- when both are present, the header wins. |

## Plans

- **free**: Early access: full API, 50 tools per tenant, 500 calls/day, community support. No card required.
- **pro**: Pro: 50 tools, 50,000 calls/month included then usage-billed per call. $19/month.
