mcphost

Host your MCP tool over streamable HTTP in one command.

Community: Submitted by a user or imported; check the owner before granting accessOnlineNo sign-inGlobalFreeRead-only

What it can do

    What data it sees

    Do you need an account

    No: the server works without sign-in

    Host your MCP tool over streamable HTTP in one command.

    Server tool list (76)

    Raw names from tools/list. Only developers need these.

    signupCreate a tenant and receive a bearer key and namespace. Unauthenticated. Recommended: pass handoff: true to receive a short-lived, single-use handoff_token instead of the raw key -- redeem it once with host.redeem to get the key, so a transcript of this call and the redeem call, if it leaks, carries a dead credential. The raw-key path (handoff omitted) stays fully supported.
    host.whoamiReturn the calling tenant's identity, including key_age_s and key_rotated_at for auditing credential hygiene.
    host.redeemExchange a signup(handoff: true) handoff_token for the tenant key it was issued for. Single-use: a second redemption fails with handoff_token_redeemed; past its expiry it fails with handoff_token_expired. Unauthenticated -- the token itself is the proof.
    host.key_rotateIssue a new tenant key and invalidate the current one immediately: every other call using the old key fails as unauthenticated from this point on. Returns the new key exactly once -- use it (as tenant_key or Authorization) for every call after this one.
    host.tool_publishPublish a tool of a registered kind (chain, echo, http, python) under this tenant's namespace. Call host.quickstart(kind) first for a filled-in example spec and the full publish-to-call sequence. Name must match ^[a-z][a-z0-9_]{1,40}$; a rejection names the failing field and a corrected example. Try host.tool_test before a real call.
    host.quickstartReturn 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 and a try_before_call table naming the one dry-run tool for each case. 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.
    host.tool_listList this tenant's published tools.
    host.tool_removeRemove a published tool by its local name.
    host.tool_logsReturn the most recent log lines for one of this tenant's tools.
    host.tool_testDry-run an already-published tool by name, no calls row written; for the other cases see host.quickstart.
    host.bridge_testDry-run an unpublished http spec against its real upstream; for the other cases see host.quickstart.
    host.tool_runDebug-run a published python tool for stdout, stderr and exit code; for the other cases see host.quickstart.
    host.tool_callInvoke 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. Pass async: true for a tool that needs more than the call deadline: returns {run_id, status: "queued"} immediately instead of running inline -- see host.runs.get/wait.
    host.usageCalls, errors and duration percentiles for this tenant over a window.
    host.tool_shareShare one of this tenant's published tools with everyone (visibility: "public") or with a named group this tenant owns (visibility: "group", group: <name>). The tool keeps running in this tenant's own sandbox with this tenant's own secrets; a caller reaches it as <this tenant's namespace>.<name>.
    host.tool_unshareTake a shared tool back to private.
    host.group.createCreate a named group this tenant owns, for host.tool_share(visibility: "group").
    host.group.addAdd a tenant (by namespace) to a group this tenant owns.
    host.group.removeRemove a tenant (by namespace) from a group this tenant owns.
    host.group.listList the groups this tenant owns and their members.
    host.catalog.searchSearch public tools across every tenant by name/description substring.
    host.catalog.getReturn one public tool's descriptor and args_schema by its full name (<namespace>.<name>).
    host.secret_setStore an encrypted secret value under this tenant's namespace.
    host.secret_listList this tenant's secret names (never their values).
    host.registry_publishPublish this tenant's server.json to the configured MCP registry (requires --registry-url and admin.tenant_verify_namespace first).
    host.state.getRead one key from this tenant's key-value state namespace. Returns found: false (not an error) if the key was never set.
    host.state.setWrite 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.
    host.state.deleteDelete one key from this tenant's key-value state namespace.
    host.state.listList keys (with their current values) in this tenant's key-value state namespace, optionally filtered by prefix.
    host.state.table_createDeclare (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.
    host.state.table_dropDrop a declared table and every row it holds.
    host.state.insertInsert 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.
    host.state.queryRead 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).
    host.state.delete_rowsDelete rows from a declared table matching an optional where filter (same grammar as host.state.query); omitting where deletes every row in the table.
    host.table.createDeclare a table in this tenant's SQL table store -- a different store from host.state.*'s key-value namespace and its own tables: use host.state.* for a handful of small values, host.table.* when you want real SQL (joins, aggregates, read-only queries) over rows. columns is {"column": "text"|"integer"|"real"| "timestamp"|"boolean"|"json"}; primary_key, if given, must name one of columns's own entries.
    host.table.appendAppend one row (an object) or several (an array of objects) to a declared table. Each row is validated against the table's schema first -- a type mismatch fails the whole call with table_schema_violation and writes nothing.
    host.table.queryRun a single read-only SQL SELECT (CTEs allowed) against this tenant's own tables. Structurally rejected (not by string matching): anything but exactly one SELECT statement, a result over 1,000 rows, or a query running past 5 seconds -- each refusal names the rule or bound it hit.
    host.table.listList this tenant's declared tables, each with its current row count, plus the tenant's whole table-store byte usage.
    host.table.dropDrop a declared table and every row it holds.
    host.table.schemaReturn one table's columns, types, row count and byte count, without running a query -- how an agent discovers its own table shape.
    host.runs.getRead one run's status, progress and (once done) result by id -- the same run a host.tool_call(..., async=true) or a scheduled/triggered execution created.
    host.runs.listList this tenant's recent runs, newest first, optionally filtered by tool, status (queued|running|done|error|timeout|cancelled) or trigger (call|job|schedule|event|chain).
    host.runs.cancelStop a queued or running job: its sandbox process is killed within ~2s and the run reads cancelled. A run that already finished fails with run_not_cancellable.
    host.runs.purgeDelete the stored results of every done run finished at or before before_unix; each then reads done with result: null, purged: true. Frees state_bytes_max quota the results were counted against.
    host.runs.waitLong-poll one run until it finalizes or timeout_s elapses (max 25s), returning its current status either way -- for a client with no polling loop of its own.
    host.trigger.setRun a published tool on a cron schedule (5-field: minute hour day-of-month month day-of-week, UTC), give it a public webhook URL (kind="event"): a signed POST to that URL runs the tool with the event as its argument, or fire it whenever this tenant receives a message (kind="message"): the tool runs with the message envelope as its argument. Each firing/delivery is a run visible in host.runs.list(trigger="schedule"|"event"|"message"). Refuses schedules_max/event_triggers_max (trigger_quota_exceeded, shared by event and message triggers) or a too-short schedule interval (trigger_interval_too_short); an invalid expression or verify config fails trigger_invalid naming the field.
    host.trigger.listList this tenant's triggers (optionally filtered by tool), each with next_unix, last_run_id and last_status (schedule), or url/verify/unverified (event).
    host.trigger.getRead one trigger's current schedule, next_unix, last_run_id and last_status.
    host.trigger.pauseStop a trigger from firing until resumed; still counts toward schedules_max.
    host.trigger.resumeRe-enable a paused trigger; if its scheduled time already passed, the next tick fires it once (a missed firing is never replayed).
    host.trigger.removeDelete a trigger outright (frees its schedules_max slot, unlike pause).
    host.trigger.fireRun a schedule once right now, for testing -- recorded as trigger: "schedule" with manual: true, independent of next_unix or pause state.
    host.trigger.testDry-run an event trigger's verify config against a payload you supply, without exposing its real URL -- verifies the signature exactly as POST /hooks/... would, then runs the tool with the event as its argument. Or, on a message trigger, runs the tool with a synthetic envelope (test: true, no messages row created). The run is marked test: true. A wrong signature fails signature_invalid, naming the header it checked.
    host.trigger.replayRe-run a past event- or message-triggered run's exact stored event/envelope (no re-verification -- the original delivery already passed it). The new run's trigger_ref names the original run id.
    billing.plansThe plan catalog (price and quotas per plan) and whether Stripe billing is configured on this host. Anonymous callers get the same answer as tenants.
    billing.statusThis tenant's plan, usage against each quota, and when the daily call quota resets.
    billing.checkoutCreate (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.
    host.agent.whoamiReturn this tenant's own agent-directory address: namespace, handle (if claimed), display name, contact_policy and plan. Never a key hash, billing field, or call log.
    host.agent.profile_setClaim or update this tenant's agent-directory card: an optional unique @handle (^[a-z][a-z0-9_]{2,31}$, stored lower-case), a description, up to 16 tags, and a contact_policy (open, contacts, or closed). Every argument is optional and, if omitted, leaves that field unchanged; an explicit null clears handle or description. A taken handle fails with handle_taken (names no one); a reserved one fails with handle_reserved.
    host.agent.lookupResolve another agent's namespace or @handle to its public card (address, handle, display_name, description, tags, contact_policy, last_seen, source_class). Unknown, disabled, and deleted addresses all return the identical agent_not_found error.
    host.agent.searchFind agents by exact tag or a case-insensitive substring of handle, display name, or description. Disabled tenants are excluded. Ordered by handle (unclaimed last), then namespace; page with cursor from the previous response.
    host.msg.sendSend a message to one or more agent-directory addresses, creating a new thread (or, with thread_id, adding to one you already participate in). Refused recipients (agent_not_found, contact_refused, recipient_inbox_full) are listed in refused rather than failing the whole call; from is always the authenticated tenant, never a caller argument.
    host.msg.replyReply in a thread you participate in; appends with the next seq. Blocked or contact-closed participants are skipped and listed in refused rather than failing the reply. thread_not_found (byte-identical for a nonexistent id) if you are not a participant.
    host.msg.inboxEvery unread-or-read message across every thread you participate in, excluding your own sends, ordered oldest first; page with cursor from the previous response's next_cursor.
    host.msg.threadEvery message in one thread you participate in, ordered by seq; thread_not_found if you are not (or no longer) a participant.
    host.msg.ackMark messages as read for you; unread_only inbox reads stop returning them. Per-recipient -- a sender never sees others' receipts.
    host.msg.blockBlock an address: its future sends to you are refused agent_not_found, byte-identical to sending to a nonexistent address. You can still send to it. Block lists are never exposed to the blocked party.
    host.msg.unblockRemove a block.
    host.msg.waitLong-poll for a new message until one past cursor arrives or timeout_s elapses (max 25s), returning the same shape as host.msg.inbox either way -- for a client with no polling loop of its own. On timeout, messages is empty and next_cursor is unchanged.
    host.agent.contact_requestRequest contact with a contacts-mode address; creates or returns the pending request. not_needed for an open address or one you already have an accepted contact with; contact_refused for a closed address; contact_pending if a request is already pending or was denied within the last 7 days; agent_not_found (same as a nonexistent address) if that address has blocked you. Quota contact_requests_per_day.
    host.agent.contactsList your accepted contacts and every pending/decided contact request in either direction; status optionally narrows incoming/outgoing to one of pending, accepted, denied, expired.
    host.agent.contact_acceptAccept a pending contact request addressed to you: both you and the requester become accepted contacts, visible from either side via host.agent.contacts().
    host.agent.contact_denyDeny a pending contact request addressed to you. The requester's subsequent sends and requests get contact_pending for 7 days, then may request again.
    host.agent.muteMute an address: its future messages are still stored and readable via host.msg.thread, but excluded from host.msg.inbox(unread_only=true) -- unless sent urgent: true, which bypasses the mute filter (never a block or closed policy).
    host.agent.unmuteRemove a mute.
    host.agent.contacts_importRequest contact with up to 50 addresses at once (e.g. an operator's own fleet of tenants); each is resolved the same way a single host.agent.contact_request would be, but a per-address failure (already connected, already pending, blocked, over quota, ...) is reported in that address's own result entry rather than failing the whole call.
    mcphost: connect to Claude, ChatGPT, Cursor · Connectors.fun