Skip to content
Hoody.com

These endpoints manage the agent’s Model Context Protocol (MCP) server pool: inspect the effective merged configuration together with live connection state, add or update entries, toggle their enabled flag, remove them, import a config from another tool, preview what an import would write, and reconcile the running pool after a settings change.

Every write that touches a settings file requires a single-use nonce minted by beginMCPWrite for the specific write op and settings layer, plus the expect_hash you last read. Present another writer’s hash and the call returns 409 mcp_conflict instead of overwriting a concurrent edit. A first write into a settings file that does not exist yet passes the empty-array hash that listMCPServers and beginMCPWrite return for a missing file; an empty expect_hash is refused, so always carry the value forward.

Returns the EFFECTIVE merged mcp_servers config for a live session, the per-layer settings files behind it, and the LIVE runtime state of each server (connected flag, negotiated protocol revision, tool count, pid, revocation reason, recent stderr tail). Credential values are never returned; env and headers are reported as key NAMES only, because a redacted placeholder invites a client to write the placeholder back as the real secret. Each file entry carries the content hash to pass as expect_hash on a write.

NameInTypeRequiredDescription
session_idquerystringYesLive session id. foldHookSession reads this from the query string, so it is the route’s most important parameter even though the agent’s broader header set is listed below.
X-Hoody-CwdheaderstringNoPer-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves.
X-Hoody-ContainerheaderstringNoPer-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-RealmheaderstringNoPer-request realm selector: global or a 24-hex id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes.
realmquerystringNoPer-request realm selector — the in-query alias of the X-Hoody-Realm header (read only when the header is absent).
{
"status": "ok",
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": true,
"connected": true,
"protocol_version": "2025-03-26",
"server_name": "filesystem",
"server_version": "0.5.0",
"tool_count": 8,
"pid": 48211,
"env_keys": ["PATH"],
"header_keys": []
},
{
"name": "remote-tools",
"transport": "http",
"require_confirmation": false,
"enabled": false,
"connected": false,
"env_keys": [],
"header_keys": ["Authorization"]
}
],
"files": [
{
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:9b1f0c…",
"layer": "user"
},
{
"path": "/home/me/proj/.hoody/settings.json",
"hash": "sha256:2a73ee…",
"layer": "project"
}
],
"warnings": []
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.listMCPServers({ session_id: 'sess_01HZX9…' });

Mints the single-use nonce every MCP write requires and returns the target settings path plus its current mcp_servers hash. The nonce binds {session, op, resolved path}: a write presenting a nonce minted for a different op or scope fails closed. Pass the returned hash back as expect_hash so a concurrent edit is reported as a conflict rather than silently overwritten.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id (MCP config is resolved against the session’s settings layers).
opstringYesWhich write the nonce authorizes. One of upsert, delete, set_enabled, import. The minted nonce is valid for this op alone.
scopestringNoSettings layer to write. One of user, project, local. Defaults to user, or project when there is no user layer (the case under --config-dir).
{
"session_id": "sess_01HZX9ABCDEF",
"op": "upsert",
"scope": "user"
}
{
"status": "ok",
"nonce": "nonce_01HZX9…",
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:9b1f0c…"
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.beginMCPWrite({
session_id: 'sess_01HZX9ABCDEF',
op: 'upsert',
scope: 'user'
});

Writes one mcp_servers entry, merging FIELD BY FIELD over any existing entry of the same name so fields you omit keep their stored value — including fields this build does not model. Requires the begin-write nonce for op:upsert and an expect_hash. A body carrying the redaction placeholder for a credential is REFUSED rather than stored.

On success a re-pointed server is REVOKED in every live session before the response, and the new config is APPLIED to the session you named before the response — its reconnect is AWAITED rather than merely started, so nothing is still pending when this returns. Awaited is NOT succeeded: a server that fails to start or handshake still returns 200, so read servers[].connected in this reply before calling its tools. A session that is MID-TURN keeps the tool set it was shown and picks the new one up at the next turn boundary. Other live sessions reconnect in the BACKGROUND, so this never waits out an unrelated session’s slow server; that background pass is best-effort and is skipped under sustained pressure, in which case those sessions pick the change up on the next write or an explicit reconnect. Revocation is never best-effort.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
noncestringYesSingle-use nonce from beginMCPWrite minted for op:upsert and this scope; the RPC fails closed without it.
scopestringNoSettings layer to write. One of user, project, local. Must match the scope the nonce was minted for.
expect_hashstringYesThe mcp_servers hash you last read, as returned by beginMCPWrite or listMCPServers. A mismatch returns 409 mcp_conflict instead of overwriting a concurrent edit. A first write into a file that does not exist yet states its expectation with the empty-array hash rather than omitting this.
serverobjectYesThe server entry. See field notes below.

The server object supports:

  • name — letters, digits, _ and -, max 64 chars, no __ (it separates the tool name), may not be hoody or mcp (both are reserved namespaces). The check is case-insensitive, so Hoody is also refused.
  • typestdio (default), http (aliases url, streamable, streamable-http) for Streamable HTTP, or sse for the deprecated 2024-11-05 HTTP+SSE transport. Matched case-insensitively.
  • command + args for stdio, or url for the remote transports.
  • env / headers — values support full ${VAR} expansion, so a token lives in your environment rather than in settings.json.
  • allowed_tools — restrict which of the server’s tools are advertised AND dispatchable.
  • require_confirmation — park every call from this server for human approval.
  • enabled — defaults to true. A disabled server keeps its config but is neither connected nor advertised.
{
"session_id": "sess_01HZX9ABCDEF",
"nonce": "nonce_01HZX9…",
"scope": "user",
"expect_hash": "sha256:9b1f0c…",
"server": {
"name": "filesystem",
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/me/projects"],
"env": {
"LOG_LEVEL": "warn"
}
}
}
{
"status": "ok",
"sessions": 3,
"revoked": 1,
"deferred_sessions": 2,
"deferred_started": true,
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": true,
"connected": true,
"protocol_version": "2025-03-26",
"server_name": "filesystem",
"server_version": "0.5.0",
"tool_count": 8,
"pid": 48211,
"env_keys": ["LOG_LEVEL", "PATH"],
"header_keys": []
}
],
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:7c4a1e…"
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.upsertMCPServer({
session_id: 'sess_01HZX9ABCDEF',
nonce: 'nonce_01HZX9…',
scope: 'user',
expect_hash: 'sha256:9b1f0c…',
server: {
name: 'filesystem',
type: 'stdio',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '/home/me/projects']
}
});

Removes one mcp_servers entry from the named settings layer. Requires the begin-write nonce for op:delete and an expect_hash. The server is REVOKED in every live session before the response returns, so a caller mid-turn cannot still reach it; a stdio child is reaped when its last holder releases.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
noncestringYesSingle-use nonce from beginMCPWrite minted for op:delete and this scope.
scopestringNoSettings layer to write. One of user, project, local. Must match the scope the nonce was minted for.
namestringYesThe server name to remove.
expect_hashstringYesThe mcp_servers hash you last read. A mismatch returns 409 mcp_conflict.
{
"session_id": "sess_01HZX9ABCDEF",
"nonce": "nonce_01HZX9…",
"scope": "user",
"name": "filesystem",
"expect_hash": "sha256:7c4a1e…"
}
{
"status": "ok",
"sessions": 3,
"revoked": 1,
"deferred_sessions": 2,
"deferred_started": true,
"servers": [],
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:2a73ee…"
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.deleteMCPServer({
session_id: 'sess_01HZX9ABCDEF',
nonce: 'nonce_01HZX9…',
scope: 'user',
name: 'filesystem',
expect_hash: 'sha256:7c4a1e…'
});

Flips one entry’s enabled flag without touching the rest of its config, so credentials and options survive a disable. Requires the begin-write nonce for op:set_enabled and an expect_hash. Disabling revokes the server in live sessions immediately.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
noncestringYesSingle-use nonce from beginMCPWrite minted for op:set_enabled and this scope.
scopestringNoSettings layer to write. One of user, project, local. Must match the scope the nonce was minted for.
namestringYesThe server name.
enabledbooleanYestrue to enable, false to disable.
expect_hashstringYesThe mcp_servers hash you last read. A mismatch returns 409 mcp_conflict.
{
"session_id": "sess_01HZX9ABCDEF",
"nonce": "nonce_01HZX9…",
"scope": "user",
"name": "filesystem",
"enabled": false,
"expect_hash": "sha256:9b1f0c…"
}
{
"status": "ok",
"sessions": 3,
"revoked": 1,
"deferred_sessions": 2,
"deferred_started": true,
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": false,
"connected": false,
"env_keys": ["LOG_LEVEL"],
"header_keys": []
}
],
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:5d8b12…"
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.setMCPServerEnabled({
session_id: 'sess_01HZX9ABCDEF',
nonce: 'nonce_01HZX9…',
scope: 'user',
name: 'filesystem',
enabled: false,
expect_hash: 'sha256:9b1f0c…'
});

Imports a batch of servers from a pasted config document or an explicit array. Understands the hoody (mcp_servers list), Claude/Cursor (mcpServers map), and VS Code (servers map) dialects; a document carrying more than one of them is REFUSED rather than guessed at. Validation is WHOLE-BATCH — one bad entry aborts everything, because a partial import leaves a config the operator did not author. Imported servers land DISABLED for review; enable them with setMCPServerEnabled. Requires the begin-write nonce for op:import and an expect_hash.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
noncestringYesSingle-use nonce from beginMCPWrite minted for op:import and this scope.
scopestringNoSettings layer to write. One of user, project, local. Must match the scope the nonce was minted for.
documentstringNoA pasted config document in any supported dialect. Mutually exclusive with the servers field. This is the ONLY import form the CLI exposes; the servers field has no CLI flag. The hoody dialect is accepted here, so anything expressible via servers can be passed as a document.
serversarrayNoExplicit server entries, in hoody’s own shape. Mutually exclusive with document. API/SDK only.
replacebooleanNoOverwrite entries whose name already exists. Without it, a collision aborts the whole import.
expect_hashstringYesThe mcp_servers hash you last read. A mismatch returns 409 mcp_conflict.
{
"session_id": "sess_01HZX9ABCDEF",
"nonce": "nonce_01HZX9…",
"scope": "user",
"document": "{\"mcpServers\": {\"filesystem\": {\"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/me/projects\"]}}}",
"expect_hash": "sha256:9b1f0c…"
}
{
"status": "ok",
"sessions": 3,
"revoked": 0,
"deferred_sessions": 2,
"deferred_started": true,
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": false,
"connected": false,
"env_keys": ["PATH"],
"header_keys": []
}
],
"path": "/home/me/.hoody/settings.json",
"hash": "sha256:5d8b12…",
"imported": 1
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.importMCPServers({
session_id: 'sess_01HZX9ABCDEF',
nonce: 'nonce_01HZX9…',
scope: 'user',
document: '{"mcpServers":{"filesystem":{"command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/home/me/projects"]}}}',
expect_hash: 'sha256:9b1f0c…'
});

Parses a pasted config document into the entries an import WOULD write, without touching any file. Credential values are stripped from the preview. Use it to show a user what they are about to import; it needs no nonce because it writes nothing.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
documentstringYesA config document in any supported dialect.
{
"session_id": "sess_01HZX9ABCDEF",
"document": "{\"mcpServers\": {\"filesystem\": {\"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/me/projects\"]}}}"
}
{
"status": "ok",
"dialect": "claude",
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": false,
"env_keys": ["PATH"],
"header_keys": []
}
],
"count": 1
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.parseMCPImport({
session_id: 'sess_01HZX9ABCDEF',
document: '{"mcpServers":{"filesystem":{"command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/home/me/projects"]}}}'
});

Connects to a candidate server config and reports the tools it advertises and the protocol revision it negotiated, then tears the connection down. Nothing is written. The deny list is enforced on the candidate config before anything is started.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id (supplies the deny list and transport policy).
serverobjectYesThe candidate entry, same shape as upsertMCPServer’s server.
{
"session_id": "sess_01HZX9ABCDEF",
"server": {
"name": "filesystem",
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/me/projects"]
}
}
{
"code": "bad_request",
"message": "invalid request"
}
Error CodeTitleDescriptionResolution
bad_requestBad requestThe request was malformed or carried invalid parameters.Correct the request body or query parameters.
realm_scope_unsupportedRealm scope unsupportedA per-request realm header was supplied to an active-only / global-no-realm RPC.Omit the realm header on this route, or open a session to scope by realm.
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
// Machine callers will receive 403 human_only — see the caution above.
await client.agent.mcp.probeMCPServer({
session_id: 'sess_01HZX9ABCDEF',
server: {
name: 'filesystem',
type: 'stdio',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '/home/me/projects']
}
});

Re-reads the settings layers and reconciles every live session’s MCP pool: servers that vanished or were re-pointed are revoked, the rest are reconnected, and a healthy unchanged server is NOT restarted. Revocation lands in EVERY live session before the response. Reconnection is AWAITED only for the session you named — awaited, not guaranteed: a server that will not start leaves connected false and still returns 200. Other sessions reconnect in the background, best-effort. Use it after editing a settings file by hand, or to recover a server that died.

NameInTypeRequiredDescription
X-Hoody-CwdheaderstringNoPer-request working-directory scope.
X-Hoody-Config-DirheaderstringNoPer-request --config-dir override.
X-Hoody-ContainerheaderstringNoPer-request bound remote container.
X-Hoody-RealmheaderstringNoPer-request realm selector.
realmquerystringNoIn-query alias of X-Hoody-Realm.
FieldTypeRequiredDescription
session_idstringYesLive session id.
{
"session_id": "sess_01HZX9ABCDEF"
}
{
"status": "ok",
"sessions": 3,
"revoked": 1,
"deferred_sessions": 2,
"deferred_started": true,
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"require_confirmation": false,
"enabled": true,
"connected": true,
"protocol_version": "2025-03-26",
"server_name": "filesystem",
"server_version": "0.5.0",
"tool_count": 8,
"pid": 48239,
"env_keys": ["LOG_LEVEL", "PATH"],
"header_keys": []
}
]
}
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.mcp.reconnectMCP({
session_id: 'sess_01HZX9ABCDEF'
});