mcphost
MCP serverAI & modelsLets your agent connect to and manage a shared AI model host on its own, without a human.
Use mcphost in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add mcphost and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the signup tool from mcphost
No other account needed.
Details
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
About this server
One MCP endpoint an agent can join and administer without a human
Install mcphost
The server’s own address, for the clients that take one directly. Or connect ahel once and every client you use reads it from one address, with the account kept on ahel rather than in each client’s config.
Claude Code
claude mcp add --transport http --scope user mcphost 'https://mcphost.dev/mcp'Run it once in your project, then open /mcp to approve any sign-in the server asks for.
Claude Desktop
https://mcphost.dev/mcpAdd a custom connector in Settings, paste this address, and approve the sign-in.
Cursor
cursor://anysphere.cursor-deeplink/mcp/install?name=mcphost&config=eyJ1cmwiOiJodHRwczovL21jcGhvc3QuZGV2L21jcCJ9Open the link and Cursor adds the server at that address.
ChatGPT
https://mcphost.dev/mcpIn Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.
Codex
codex mcp add mcphost --url 'https://mcphost.dev/mcp'Run it once, then sign in with codex mcp login mcphost if the server asks for an account.
From the project's README
As published by j0yen/mcphost in README.md.
where agents host their own tools · mcphost.dev · status · llms.txt · llms-install.md
Find it in the registry: dev.mcphost/mcphost — connect with the one-URL quickstart at mcphost.dev.
Ship an MCP tool, not a deployment project.
mcphost lets an agent create the tool it needs, mid-task, without a human in the loop: sign up with one unauthenticated tool call, publish with the next, and the new tool is live immediately — no restart, no deploy, no review queue.
Measured (panel run 0.26.3-20260908T085001Z, 21 sessions): median
time from signup to a tenant's first successful host.tool.publish is
30.7s; median time from signup to a successful call on that tenant's
own tool is 42.4s.
Quickstart for agents
- 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. - Make your first call.
host.quickstart()is read-only and hands back a worked example before you commit to anything; any otherhost.*/billing.*call creates your tenant right then, on this connection, with nosignupcall at all --host.whoami()is the simplest:host.whoami() - Read
onboarding.urlonce, 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_hintnames where agents conventionally keep it. - Publish a tool.
host.quickstart()'s ownstarter_toolis a ready-to-publishpythonspec; itspublish_callis exactly:
Two real kinds: submit code (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"})python-- source required,args_schema/requirementsinferred if omitted) or wrap an API you already use (http-- url and method required,args_schemainferred if omitted).echois a stub for testing the pipes, not a real tool. Before publishing anything, dry-run withhost.tool_publish({..., dry_run: true})-- every gate (secrets, env, network, deps, name, kind, spec size) reported at once, no tool row written -- orhost.spec_test(kind, spec, invocations)for up to 5 example calls through the same sandbox a real call uses. - 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 yourtools/list):host.tool_test(name="table_note", args={"note": "hello"}) - Share it with a teammate:
host.invite.create()returns an/i/<code>/mcpURL -- whoever connects there and makes their own first call gets a fresh tenant already in contact with you, with any tool names passed assharealready shared (see "Share a tool, not a key" inwww/llms.txtfor sharing one tool while keeping its own key hidden):host.invite.create() - Relay the claim link to your human: hand them
onboarding.urland the sentence "Claim this backend so it belongs to you:<url>." Never print the personal URL or the key anywhere but that one handoff.
Also useful once you're past the first call: billing.plans()/
billing.status() for plan and quota; host.tool.list()/
host.tool.logs(name)/host.tool.remove(name)/host.usage(window) to
inspect and manage; host.secret.set/host.secret.list() for
credentials a published tool needs (secrets stored AES-256-GCM encrypted,
with a separate plain env map alongside for everything that isn't one).
Pass expose_spec: true on host.tool.share (or share names on
host.invite.create) to also let a sharee read a tool's source, not
just call it -- they read it with
host.tool.spec_shared(tool="<owner_namespace>.<name>"); shared without
expose_spec, the same call fails spec_not_exposed (see "Share a tool,
not a key" in www/llms.txt for the full worked example).
Explicit signup (clients that cannot keep a session)
A client that can't hold a persistent connection across calls -- so there is no session for mcphost to implicitly bind a tenant to -- signs up directly instead of relying on its first call to create one:
- Connect to the endpoint and call
tools/listwith no credentials. The only tool offered issignup. - Call
signup(name). The response containstenant,key(a bearer token, shown once),namespace,endpoint, andclaim_url. Signup is rate-limited to 5 per IP per hour. Passsource(e.g.signup(name, source: "hn")) to tag which channel this signup came from -- recommended values arehn,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 withsignup_pausedand aretry_after_secs; try again later. Recommended:signup(name, handoff: true)returns a short-lived, single-usehandoff_tokeninstead ofkey; callhost.redeem(handoff_token)once to get the key, so a transcript of this exchange carries a dead credential.host.key.rotateinvalidates the current key and issues a new one in one call, any time you suspect it leaked. - Pass
keyas thetenant_keyargument on everyhost.*call from here on -- e.g.host.tool.publish,host.tool.call. No reconnect orAuthorizationheader needed; a client that holds a persistent connection can useAuthorization: Bearer <key>instead.
Leaving
host.self.offboard() permanently closes your own tenant: no operator
ticket, no admin key, no arguments, no confirmation flag — the same
tenant_key channel you signed up through is the one you leave through,
and the call takes effect immediately.
What it does, in order: cancels any active Stripe subscription if you're
on the pro plan, disables the tenant (every call with this tenant_key
after this point — host.* or billing.* — gets the same
tenant_disabled/tenant_key_invalid error an admin-disabled tenant
already gets), then gives every registered kind a chance to tear down
whatever it's keeping alive for you (e.g. a python sandbox's warm pool).
What it does NOT do: scrub your data. tools, secrets, signup_events,
and usage history all stay in place for audit — exactly the same
retention an admin-disabled tenant gets today. If you want a copy of what
you built before leaving, run host.export() first; self_offboard
doesn't bundle one for you.
Irreversibility: calling it twice is a no-op, not an error or a crash —
but there is no self-service undo. A canceled Stripe subscription stays
canceled, and your tenant_key stops authenticating the instant the call
returns. (An operator can flip the underlying tenant row back on with
admin.tenant_enable, but that's an operator action taken on your behalf,
not something self_offboard itself offers back to you.)
Getting help
Every error payload carries code, a clean message, a request_id, and
(for every code in the table below) a help_url pointing at a generated
/help/<code> page -- meaning, likely cause, fix, no internal text.
host.whoami's links field names the same support/plans/status/
help pages directly, so an agent never has to guess the host to build
them from.
Support: support channel not configured (MCPHOST_SUPPORT_URL is unset).
Contributing: naming a new host.* tool
One rule now, written down and enforced by CI: host.<family>.<verb> --
see docs/tool-naming.md for the rule, its exceptions
(signup/billing.*, and singleton nouns like host.whoami), and how
aliases work. host.tool.call/host.tool.share/host.tool.list/
host.tool.remove/host.tool.logs/host.tool.rollback/host.tool.diff/
host.tool.history/host.tool.unshare and the other pre-rule underscored
names (mcphost-polish-p0-20260930 audit finding 5) are no longer drift:
each is registered under its dotted canonical name
(host.tool.call/host.tool.share/...) and kept working under its old
name as a deprecated alias (sunset date in tools/list's own
x-deprecated) -- scripts/tool-naming-lint.sh fails CI on any new
name that doesn't follow the rule.
Contributing: routing a new top-level path
Adding a new top-level directory or file to this repo (like www/,
deploy/, or .buildloop/) needs a matching [[lane]] entry in
agent/proof-lanes.toml, in the same PR that adds the path — autobuilder vti-plan (the branch gate's routing check) refuses any changed path that
resolves to zero lanes, and tests/lanecov_ac01_every_tracked_path_routes.rs
enforces the same rule locally via cargo test, so a missing lane fails
fast instead of turning every branch gate red after the path lands on
main. Give the lane an id, a one-line description naming the PRD or
reason the path exists, globs covering the new path ("<dir>/**" for a
directory), and required_commands — the cheapest command that actually
proves a change under that path, not necessarily the full test suite. The
loop-config lane (routing .buildloop/** to cargo test --workspace) is
a worked example: one glob, one required command, added in the same PR
that made the path matter.
Docs Q&A in a minute
Turn a folder of markdown into a checkable Q&A tool, end to end, in one script:
signup(name)— one fresh tenant.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).host.docs.status()— poll untilindex.lag_seconds == 0(usually one or two of the indexer's own 10s ticks; this recipe's corpus is ready well under 30s).host.tool_publish(name="ask_docs", kind="python", spec={"source": ...})— the one published tool. Itsmaincallsmcphost.docs.search(query, k)over the same zero-network sandbox loopbackmcphost.docs.getalready uses, so answering a question never spends a public tool call.<namespace>.ask_docs(query="...")— ask it. Every passage in the result carries aname:offsetcitation (the document's name and its character offset in that document), so an answer is checkable against its source.
Quota this recipe uses: 8 documents, ~22 KiB, ~30 chunks, 1 published
tool, up to 10 calls to that tool — comfortably inside the free plan's
docs_max (200) and calls_per_day (500).
examples/docs-qa/docs-qa.sh <endpoint> runs this recipe end to end
against a real endpoint and writes a receipt (per-question hit and
citation, index_ready_secs, the quota actually used);
examples/docs-qa/ask_docs.py is the tool itself, and
examples/docs-qa/corpus/ is the 8-document corpus plus its 6 gold
questions. docs-qa.sh --embeddings <provider-endpoint> <model> <secret-name> configures an embeddings provider first and reports both
lexical and embeddings hit rates in one receipt.
What it is
mcphost serve is one Rust binary that speaks streamable-HTTP MCP at a single endpoint. An agent signs up with one unauthenticated tool call, gets a namespace, and from then on everything is a tool call: publish, run, schedule, log, meter, store secrets, share with another agent, act as one of your end users over OAuth. There is no dashboard; /status is the one page, and the operator works through admin.* tools on the same endpoint.
Measured, not promised: p95 34.2 ms across 200 concurrent calls with zero errors, committed next to the test that produces it. The public site is mcphost.dev; the machine-readable summary at /llms.txt is generated from the same source as the quickstart above (docs/agent-quickstart.md, scripts/gen-agent-docs.sh).
Connect
Paste one line into your client and it has mcphost. No key needed to sign
up -- signup is the one unauthenticated tool; everything past it takes
the bearer key signup returns.
Claude Code
claude mcp add --transport http mcphost https://mcphost.dev/mcp
Docs: https://code.claude.com/docs/en/mcp (checked 2026-10-07)
Cursor
Add to Cursor, or add https://mcphost.dev/mcp to your MCP config directly.
Docs: https://docs.cursor.com/en/tools/mcp (checked 2026-10-07)
VS Code
Add to VS Code, or add https://mcphost.dev/mcp to your MCP config directly.
Docs: https://code.visualstudio.com/api/extension-guides/ai/mcp (checked 2026-10-07)
Claude.ai
- Open Settings, then Connectors, then Add custom connector.
- Name: mcphost
- Remote MCP server URL: https://mcphost.dev/mcp
- Save, then enable the connector in a chat to connect.
Docs: https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp (checked 2026-10-07)
Codex CLI
codex mcp add mcphost --url https://mcphost.dev/mcp
Docs: https://developers.openai.com/codex/mcp (checked 2026-10-07)
Gemini CLI
gemini mcp add --transport http mcphost https://mcphost.dev/mcp
Docs: https://geminicli.com/docs/tools/mcp-server/ (checked 2026-10-07)
OpenCode
opencode mcp add mcphost --url https://mcphost.dev/mcp
Docs: https://opencode.ai/docs/mcp-servers/ (checked 2026-10-07)
Amp
amp mcp add mcphost https://mcphost.dev/mcp
Docs: https://ampcode.com/manual#mcp (checked 2026-10-07)
Goose
- In a Goose session, type /extension.
- Choose Add Remote Extension (Streamable HTTP).
- Name: mcphost
- Endpoint URL: https://mcphost.dev/mcp
Docs: https://block.github.io/goose/docs/getting-started/using-extensions/ (checked 2026-10-07)
Warp
- In a Warp agent session, type /agent-add-mcp.
- Paste this server config: {"mcphost":{"url":"https://mcphost.dev/mcp"}}
- Save; Warp starts the server and lists its tools.
Docs: https://docs.warp.dev/agent-platform/capabilities/mcp (checked 2026-10-07)
Windsurf
{
"mcpServers": {
"mcphost": {
"url": "https://mcphost.dev/mcp"
}
}
}
Docs: https://docs.windsurf.com/windsurf/cascade/mcp (checked 2026-10-07)
Cline / Roo
{
"mcpServers": {
"mcphost": {
"type": "streamableHttp",
"url": "https://mcphost.dev/mcp"
}
}
}
Docs: https://docs.cline.bot/mcp/configuring-mcp-servers (checked 2026-10-07)
See the live status page and the
Acceptable Use Policy before you point production traffic at
it. Plans and limits: plans / /plans.json
(same numbers billing.plans returns, and docs/plans.md). Every error
payload carries a help_url pointing at a generated /help/<code> page
explaining it.
Support: support channel not configured (MCPHOST_SUPPORT_URL is unset).
Changes
Current release: v0.64.0 (2026-09-30); main is 0.65.0. Every change is in CHANGELOG.md and the git tags.
Operating and contributing
Everything below is for running your own mcphost or changing this one: install, environment, kinds, limits, metering, synthetic tenants, and the acceptance suite.
Install
cargo install --path .
Or build locally:
cargo build --release
./target/release/mcphost serve
Environment contract
| Variable | Meaning | Default |
|---|---|---|
MCPHOST_DATA_DIR | Directory holding mcphost.db (SQLite, WAL) | ./data |
MCPHOST_BIND | host:port to listen on | 127.0.0.1:8080 |
MCPHOST_PUBLIC_URL | URL returned by signup as the endpoint | http://<bind> |
MCPHOST_ADMIN_KEY | Bearer key that unlocks admin.* tools | unset (admin tools unreachable) |
MCPHOST_SECRET_KEY | Passphrase, SHA-256-derived into an AES-256 key for tenant secrets | dev default (set a real one in production) |
MCPHOST_LOG_LEVEL | tracing filter, e.g. info | info |
MCPHOST_REGISTRY_URL | Enables host.registry_publish (P1) and names the registry API's base URL; mcphost serve --registry-url <url> takes precedence | unset (registry-publish disabled) |
MCPHOST_SIGNUP_RATE_LIMIT_PER_HOUR | Overrides the per-source-IP signup rate limit (PRD-mcphost-signup-rate-configurable) — raise it for a many-session measure run from one IP; absent or non-integer falls back to the default. Effective value is logged once at startup. A source IP configured in MCPHOST_FLEET_IPS is exempt from this limit entirely | 5 |
MCPHOST_EGRESS_PROXY | http(s)://host:port of the operator's outbound HTTP(S) proxy. Required for a pro tenant's python/wasm tool published with network: "public" or "egress" to get any sandbox network at all — see "Egress proxy" below | unset (no pro tenant gets outbound network) |
MCPHOST_FLEET_IPS | Comma-separated list of IPv4/IPv6 addresses and/or CIDR blocks (e.g. 46.225.110.44,178.105.64.66,10.0.0.0/8) this operator's own fleet signs up from. A signup whose source IP matches gets source_class: fleet (synthetic: harness:fleet-ip) even without the x-mcphost-synthetic header — invalid entries are logged and skipped. See admin.reclassify_fleet_ips to backfill signups that predate this var | unset (no IP is ever classified fleet by address alone) |
mcphost migrate applies pending SQL migrations and exits. mcphost version
prints the version and exits. mcphost serve --registry-url <url> is the
CLI-flag form of MCPHOST_REGISTRY_URL above.
Egress proxy (network: "public" / "egress")
A free tenant can never publish a tool with network: "public" or
"egress" — both spellings grant the same sandbox access, and both are
refused at host.tool_publish with plan_required naming pro and the
network field. A pro tenant may publish one, but the sandbox still
gets no outbound network at call time unless $MCPHOST_EGRESS_PROXY is
configured on this host — with it unset, the call fails with
egress_unavailable before any sandboxed process is even spawned. An
existing tool that declared public/egress while its owner was on pro
starts failing with plan_required (not a silent downgrade to no network)
the moment that tenant drops to free; the error names network: "none" as
the republish fix, or upgrading back to pro.
With the proxy configured, a pro tenant's egress call runs with
--share-net (its outbound network shares the host's own network
namespace) and http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY all set
to $MCPHOST_EGRESS_PROXY in the sandboxed process's environment, plus
no_proxy="" so a stray NO_PROXY already in the operator's environment
can never let a sandboxed process route around the proxy. This crate does
not ship the proxy itself (mcphost-deploy owns that config) — it only
guarantees "no proxy, no network." Whatever proxy is deployed must
enforce a private/link-local deny list on every connection it forwards, the
same ranges the http kind's own VettingResolver already refuses for a
rendered request URL:
- RFC 1918 private ranges (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) 169.254.0.0/16(link-local, including the cloud metadata address169.254.169.254)127.0.0.0/8(loopback) and::1fc00::/7(IPv6 unique local addresses)
Registry publish (P1)
Off by default. Once --registry-url / $MCPHOST_REGISTRY_URL names a
registry API base (e.g. https://registry.modelcontextprotocol.io):
- The operator verifies a tenant's domain namespace by whatever method
they trust (the PRD leaves the verification METHOD itself — DNS vs
HTTP record — as an open question owned by Joe; this crate does not
implement one) and records the outcome with
admin.tenant_verify_namespace:admin.tenant_verify_namespace(tenant="t_xxxxxxxx", domain_namespace="io.github.example.myserver"). - That tenant can then call
host.registry_publish()(no arguments): it POSTs aserver.jsondocument (name/description/version/remotes: [{type: "streamable-http", url}]) to<registry-url>/v0/publish, and the same document becomes servable, unauthenticated, atGET /.well-known/mcp/<namespace>/server.json. host.registry_publishrefuses with a distinct, machine-readable error indata.error_code:registry_disabled(flag off),namespace_unverified(step 1 not done for this tenant), orregistry_rejected(the registry API answered non-2xx).
This host's own registry listing
Distinct from "Registry publish (P1)" above, which is a tenant-facing
tool for a TENANT's own namespace — this is mcphost's own listing.
mcphost registry-manifest builds the public MCP registry's
server.json entry from Cargo.toml (name under the DNS-verified
dev.mcphost namespace, description, version, license) and
$MCPHOST_PUBLIC_URL (default https://mcphost.dev) + /mcp as the
streamable-http remote:
- No flags: prints the entry to stdout.
--write <path>: writes it (used to regenerate the committedregistry/server.json, never hand-edited).--check: compares a committed file (defaultregistry/server.json) against a fresh render and exits non-zero with a diff on drift -- except in theversionfield, which it ignores. A release land commitsregistry/server.jsonand only afterwards bumpsCargo.toml's version, so the committed file is deliberately version-stale between a PRD land and the next tag;--checkwould otherwise fail every such land on this file alone, by construction. Any other drift (description, name, license, repository, remotes) still fails--checkas before.
.github/workflows/registry.yml runs --check plus schema validation
against the pinned registry/schema.json on every push, and on a v*
tag regenerates registry/server.json with --write (so the published
entry carries the real, just-tagged version even though the committed
copy in the repo stays version-stale) before publishing it to the
registry via the MCPHOST_REGISTRY_PRIVATE_KEY repository secret (the
dev.mcphost domain-namespace DNS login key for the registry publisher
CLI).
Kinds
Every registered kind's minimal example spec, below, and host.tool_publish's
on-wire description (visible from tools/list before signup) are both
rendered from the same docs/kinds/*.md files (PRD-mcphost-publish-first-try
requirement 6) -- tests/publishfirsttry_ac06_docs_shared_source.rs
regenerates this section from those files and fails CI if it's drifted from
what's checked in below. Call host.quickstart(kind) for the same example
with your own namespace already filled in.
echo
spec.schema is any JSON Schema; a call echoes back the arguments it was given, validated against it.
Example spec:
{
"schema": {
"properties": {
"msg": {
"type": "string"
}
},
"required": [
"msg"
],
"type": "object"
}
}
Shortened here. Read the whole README on GitHub.
Tools it offers (12)
What this server listed when ahel dialed its public endpoint in Oct 2026, with no key and no account of yours. The names are the server’s own.
signuphost.quickstarthost.redeembilling.planshost.whoamihost.tool_publishhost.tool_callhost.tool_testhost.state.sethost.state.gethost.state.listhost.tool_share
Signals
- Last commit
- Oct 2026
Advanced
- Delivery
- mcphost MCP server → your ahel connector (mcp.ahel.ai) → your AI.
- Item type
- mcp-server
- Key
dev-mcphost-mcphost- Source
- github.com/j0yen/mcphost
- Hosted endpoint
https://mcphost.dev/mcp
More in AI & models
MCP server · upstash
More in AI & modelsAdButler
MCP server · adbutler
More in AI & modelsatom-mcp-server
MCP server · a7om-ai
More in AI & modelsottasia
MCP server · aiweather-anurag
More in AI & modelsunphurl
MCP server · 123ergo
More in AI & modelsmicrosoft-learn-mcp
MCP server · microsoftdocs
More in AI & models