Skip to content

MCP ​

Point an MCP client, such as VS Code or Claude Code, at this host and it lists and runs the host's tools that need no session. The first tools are the session tools: list the sessions here, start one, message it, read it, rename it and delete it. A client can therefore start a session without being one.

The door is /mcp, on the listener that carries the HTTP API. It takes a person's ahpd token in the Authorization header, and nothing else. It is the same code that serves a session's own tools endpoint, so both answer initialize, tools/list and tools/call the same way.

Turning it on ​

mcpTools says what /mcp serves. none, the default, answers 404 there, so an install that never asked has no MCP door. all serves every sessionless tool the caller's grants allow:

json
{ "httpApi": true, "mcpTools": "all" }

/mcp is served on the same listener as /api, so httpApi has to be on. With httpApi off it answers 404 as /api does. With httpApi.port it moves to the API's own port with /api.

Pointing a client at it ​

A local daemon answers at http://127.0.0.1:9187/mcp. One reached through a tunnel or another machine answers at the name it is reached by, such as https://ahpd.example.com/mcp.

In VS Code's mcp.json:

json
{
  "servers": {
    "ahpd": {
      "type": "http",
      "url": "https://ahpd.example.com/mcp",
      "headers": { "Authorization": "Bearer <ahpd token>" }
    }
  }
}

In Claude Code:

bash
claude mcp add --transport http ahpd https://ahpd.example.com/mcp --header "Authorization: Bearer $AHPD_TOKEN"

The token is the one ahpd user token <id> prints, or one minted with ahpd token mint. A token in ?tkn= is not read: the credential is a header, as the transport's authorization says.

What it serves ​

ToolDoes
list_sessionsThe sessions here, filtered by status, agent, title, path, workspace, changes, unread, pull request, created or modified time, newest first, up to limit; a session with a nested chat also lists its chats, each with a title and an openLink
list_agentsThe agents a new session may be started on, each with its id, its name, its settings and its first five models
list_modelsThe models the agents offer, one agent's or every agent's
create_sessionStart a session, owned by the caller
create_chatOpen another chat in a session
rename_chatRename a chat
archive_chatArchive a chat, or a whole session, so it no longer appears among active sessions
send_messageSend, replace, cancel or steer a message in a session
get_session_contextRead a session's recent context
delete_sessionDelete a session
move_sessionReorder, move or promote a session's chats, while moving chats is on

get_current_session, set_workspace, isolate_session and the artifact tools act on the calling session, so there is no sessionless form of them and /mcp does not offer them. Connector actions and a schedule are not served here either.

Each tool checks its own grants, so the list is only what the caller may run. A member sees all eleven. A role with mcp:call and session:read only sees list_sessions, list_agents, list_models and get_session_context, and a call to create_session gets No tool named create_session.

move_session names what to move and where: into is the destination session, after is the chat of it to sit behind (the end when left out), and relationship: "independent" makes a nested session its own instead. It is refused while chatMoves.enabled is off.

send_message takes an operation of send (the default), replace or cancel. A send starts a turn, queues behind what the chat already runs, or updates the turn running now with delivery: "steer". It answers the message's messageId and revision. A replace or cancel names that id and expectedRevision. Either acts only on a queued message the same chat sent, while it is still pending and at that revision.

create_session takes an agent, a provider id such as claude-deepseek-build, and a config, the new session's settings checked against that agent's schema. Both are optional. Its agent input is an enum of the agents the caller may start, each named in its description. See Tools.

list_agents and list_models are the readers beside it: both are cut to the agents the caller may start, and list_models answers the id and name that create_session and create_chat take as model. Use them to choose an agent, its model and its settings before a start.

A session made here belongs to the caller and has no parent. Its turns are the caller's, so the caller's grants follow them.

Who may call ​

The door asks for mcp:call. It is in member, and admin holds it through everything. guest does not hold it. A token that reaches the door sees only the tools its other grants allow, because the door and each tool are two checks. The door charges nothing, so a person in no team is not refused.

TokenAnswer
The deployment tokenRoot: every sessionless tool
A person's token holding mcp:callThe tools their grants allow
A person's token without it403
A team's key403: a team key opens the proxy and nothing else
A session's own token401: a session has its own tools endpoint
No token, or one this host does not know401

Each 401 carries WWW-Authenticate: Bearer realm="ahpd", with error="invalid_token" when a token came that the door does not take.

OAuth is not served yet. A client that asks for it is told to use a bearer token.

What it does not do ​

Nothing is kept between requests. Each request names its caller again, so a token taken away stops the next call. The list a person sees is built for that request, from the grants they hold then.

No stream is opened. A GET answers 405, and a client finds a changed list on its next tools/list. That is enough for a client that re-lists when it needs to, and it is all a door with no state can promise.

A session's own server ​

Each session is offered its own endpoint at /ahp-mcp/<uuid>, with a token only that session holds. It serves the host's tools and the backend's client tools, and it stops answering when the session does. /mcp is for a person, and does not serve client tools. The two share one answer, so a session's endpoint answers exactly as it did before /mcp existed.