mcphost MCP server
Host your MCP tool over streamable HTTP in one command.
Little public usage data yet
Reviews
Write oneNobody has reviewed mcphost yet.
If you have run it, two minutes of your experience saves the next person an afternoon.
mcphost tools (77, 25 write)
write = sends, deletes, buys or postsbilling.checkoutwrite actionFreeCreate (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.
billing.plansFreeThe 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.statusFreeThis tenant's plan, usage against each quota, and when the daily call quota resets.
host.agent.contact_acceptFreeAccept 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_denyFreeDeny 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.contact_requestFreeRequest 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.contactsFreeList 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.contacts_importFreeRequest 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.
host.agent.lookupFreeResolve 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.muteFreeMute 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.profile_setFreeClaim 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.searchFreeFind 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.agent.unmutewrite actionFreeRemove a mute.
host.agent.whoamiFreeReturn 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.bridge_testFreeDry-run an unpublished http spec against its real upstream; for the other cases see host.quickstart.
host.catalog.getFreeReturn one public tool's descriptor and args_schema by its full name (<namespace>.<name>).
host.catalog.searchFreeSearch public tools across every tenant by name/description substring.
host.group.addFreeAdd a tenant (by namespace) to a group this tenant owns.
host.group.createwrite actionFreeCreate a named group this tenant owns, for host.tool_share(visibility: "group").
host.group.listFreeList the groups this tenant owns and their members.
host.group.removewrite actionFreeRemove a tenant (by namespace) from a group this tenant owns.
host.key_rotateFreeIssue 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.msg.ackFreeMark messages as read for you; unread_only inbox reads stop returning them. Per-recipient -- a sender never sees others' receipts.
host.msg.blockFreeBlock 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.inboxFreeEvery 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.replyFreeReply 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.sendwrite actionFreeSend 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.threadFreeEvery message in one thread you participate in, ordered by seq; thread_not_found if you are not (or no longer) a participant.
host.msg.unblockwrite actionFreeRemove a block.
host.msg.waitFreeLong-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.quickstartFreeReturn 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.redeemFreeExchange 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.registry_publishwrite actionFreePublish this tenant's server.json to the configured MCP registry (requires --registry-url and admin.tenant_verify_namespace first).
host.runs.cancelwrite actionFreeStop 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.getFreeRead 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.listFreeList 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.purgewrite actionFreeDelete 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.waitFreeLong-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.secret_listFreeList this tenant's secret names (never their values).
host.secret_setFreeStore an encrypted secret value under this tenant's namespace.
host.self_offboardFreePermanently close your own account: disables the tenant, cancels any active Stripe subscription (pro plan), and stops your key from authenticating anything further -- same as an admin-disabled tenant. Idempotent: an already-offboarded key gets the same tenant_disabled/tenant_key_invalid error every other host.*/ billing.* call already gets from it, not a crash. This does not scrub historical usage/signup records -- those stay for audit, same as today's admin-disabled tenants.
host.state.deletewrite actionFreeDelete one key from this tenant's key-value state namespace.
host.state.delete_rowswrite actionFreeDelete 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.state.getFreeRead one key from this tenant's key-value state namespace. Returns found: false (not an error) if the key was never set.
host.state.insertwrite actionFreeInsert 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.listFreeList keys (with their current values) in this tenant's key-value state namespace, optionally filtered by prefix.
host.state.queryFreeRead 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.setwrite actionFreeWrite 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.table_createwrite actionFreeDeclare (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_dropwrite actionFreeDrop a declared table and every row it holds.
host.table.appendFreeAppend 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.createwrite actionFreeDeclare 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.dropwrite actionFreeDrop a declared table and every row it holds.
host.table.listFreeList this tenant's declared tables, each with its current row count, plus the tenant's whole table-store byte usage.
host.table.querywrite actionFreeRun 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.schemaFreeReturn one table's columns, types, row count and byte count, without running a query -- how an agent discovers its own table shape.
host.tool_callFreeInvoke 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.tool_listFreeList this tenant's published tools.
host.tool_logsFreeReturn the most recent log lines for one of this tenant's tools.
host.tool_publishwrite actionFreePublish 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.tool_removewrite actionFreeRemove a published tool by its local name.
host.tool_runwrite actionFreeDebug-run a published python tool for stdout, stderr and exit code; for the other cases see host.quickstart.
host.tool_shareFreeShare 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_testFreeDry-run an already-published tool by name, no calls row written; for the other cases see host.quickstart.
host.tool_unshareFreeTake a shared tool back to private.
host.trigger.firewrite actionFreeRun a schedule once right now, for testing -- recorded as trigger: "schedule" with manual: true, independent of next_unix or pause state.
host.trigger.getFreeRead one trigger's current schedule, next_unix, last_run_id and last_status.
host.trigger.listFreeList 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.pauseFreeStop a trigger from firing until resumed; still counts toward schedules_max.
host.trigger.removewrite actionFreeDelete a trigger outright (frees its schedules_max slot, unlike pause).
host.trigger.replayFreeRe-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.
host.trigger.resumeFreeRe-enable a paused trigger; if its scheduled time already passed, the next tick fires it once (a missed firing is never replayed).
host.trigger.setwrite actionFreeRun 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.testFreeDry-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.usageFreeCalls, errors and duration percentiles for this tenant over a window.
host.whoamiFreeReturn the calling tenant's identity, including key_age_s and key_rotated_at for auditing credential hygiene.
signupwrite actionFreeCreate 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.
Public scan report
scanner v0.1.9 · 2026-09-19 · same rubric, same numbers if you re-run it
- –Code scanremote-only server, no package to scann/a
- Live reliabilityremote reachable in 2091ms17/20
- Tool poisoning77 tool descriptions checked15/15
- Auth qualityopen endpoint exposes 25 write-action tools with no auth3/15
- Maintenanceno repository listed3/15
- Maintainer identityverified namespace with website, no repo4/10
Findings (2)
- highWrite-action tools reachable without authentication
auth.open-write - lowNo source repository listed
maint.no-repo
Install directly
claude mcp add --transport http mcphost https://mcphost.dev/mcp
mcphost: common questions
- Is mcphost MCP server safe?
- With care: it is graded C, so read the findings first (56/100). Read the mcphost safety report
- How do I install mcphost?
- It runs remotely at mcphost.dev. Add it to Claude Code, Claude Desktop or Cursor with the snippets above, or call it through the mcp.market gateway without installing anything.
- Does mcphost need an API key?
- Not as far as the registry entry and our scan can tell: no credentials are declared or required.
- Is mcphost maintained?
- The latest release is v0.27.0.
- Is mcphost up?
- 100% of our last 5 checks got an answer. We check remote servers about four times a day.