MCP Server & Client
Model Context Protocol (MCP)
Section titled “Model Context Protocol (MCP)”Dewee speaks MCP in both directions, and there are four distinct things called “MCP” in this codebase. Read this table first; conflating them is the most common integration mistake.
| # | Meaning | Endpoint | Who calls whom | Auth |
|---|---|---|---|---|
| 1 | Dewee consumes an external MCP server | /v1/mcp/servers/* | Dewee is the MCP client | Gateway token / API key with operator scopes |
| 2 | An external AI client consumes Dewee | POST /mcp/server | Dewee is the MCP server | Dewee API key with an mcp.* scope, or the server token |
| 3 | Internal Claude CLI bridge | /mcp/bridge | Dewee is the MCP server, for the local Claude CLI only | Gateway token + bridge HMAC headers |
| 4 | MCP server configuration backup | /v1/mcp/export, /v1/mcp/import | Operator backup/restore of saved MCP server configs | Gateway token / admin API key |
POST /mcp/server (meaning 2) and /v1/mcp/* (meanings 1 and 4) share a leading path segment.
They are unrelated surfaces: /v1/mcp/* manages external servers, while /mcp/server serves
Dewee’s own tools outward.
1 + 4. Managing external MCP servers
Section titled “1 + 4. Managing external MCP servers”Dewee connects to external MCP servers and registers their tools into its own registry.
Supported transports: stdio, sse, streamable-http (OAuth 2.1 + PKCE supported for the
HTTP transports).
dewee mcp listdewee mcp tools filesystemdewee mcp oauth config github-tools --registration-mode pre_registered --client-id <id>dewee mcp oauth start github-toolsdewee mcp oauth status github-tools --jsonEnv for stdio servers. A stdio server’s env (server config, per-user credentials, channel
context credentials and test connections) cannot set keys that load code or redirect the runtime,
such as NODE_OPTIONS, LD_PRELOAD, PYTHONPATH, PATH, JAVA_TOOL_OPTIONS, proxy or TLS-CA
overrides, or UV_/PIP_/GIT_ package-manager settings; such a key is refused with 400. Keys
already stored on a server are not refused again when the server is edited, but they are dropped when
the process is spawned. Servers defined in the gateway config file are not filtered. A test
connection (POST /v1/mcp/servers/test) runs the same command allowlist and URL checks as creating a
server.
Full CRUD, grants, and OAuth lifecycle: see the API reference. Tools coming from an external MCP
server are named mcp_<server>__<tool> and are never re-exported through the public endpoint
(meaning 2).
2. Connecting an AI client to Dewee (public MCP)
Section titled “2. Connecting an AI client to Dewee (public MCP)”This is how Claude Code, Cursor, VS Code, Gemini CLI and other MCP clients call Dewee.
POST /mcp/serverAuthorization: Bearer <credential>X-Dewee-Agent: <optional agent selector>Content-Type: application/jsonDisabled by default. Nothing is served on /mcp/server until you enable it:
{ "tools": { "mcp": { "public": { "enabled": true } } }}or GOCLAW_MCP_PUBLIC_ENABLED=1.
Endpoint path and the legacy /mcp
Section titled “Endpoint path and the legacy /mcp”/mcp/server is the canonical endpoint, and every snippet Dewee generates points there. Clients
configured with the original bare /mcp keep working: MCP requests there (every POST, a GET
asking for text/event-stream, anything carrying a bearer credential or an MCP header) are served by
the same handler, with the same auth and limits, plus a Deprecation: @1791676800 header (an
RFC 9745 date, 2026-10-11T00:00:00Z, when /mcp was deprecated) and
Link: </mcp/server>; rel="successor-version". A plain browser page load of /mcp gets the
dashboard’s MCP page instead, never the MCP handler. Move clients to /mcp/server; the legacy path
will be removed in a future release.
Credentials
Section titled “Credentials”Two credential forms are accepted, and only these two:
- A Dewee API key carrying an
mcp.*scope. The key must be owner-bound; an MCP-scoped key without an owner is rejected, because its user identity would otherwise come from a request header. - The instance-level server token (
GOCLAW_MCP_SERVER_TOKEN), for self-hosted and operator use. It is env-only, must be at least 32 characters, must be paired withGOCLAW_MCP_SERVER_TENANT_ID, receives a read-only export set, and never crosses tenants.
Rules that matter in practice:
- The gateway token is never accepted on the public MCP endpoint.
operator.read/operator.writekeys do not gain MCP access; an MCP scope is required.- An MCP-only key is rejected on every other route, including the tenant’s read APIs. It works on the public MCP endpoint and on key management (to rotate itself), nothing else.
- Tenant, user and workspace identity always come from the credential. Request headers such as
X-GoClaw-Tenant-Id,X-GoClaw-User-Id, andX-GoClaw-Sender-Idcarry no authority here. - Revocation and expiry take effect on the next request; no cache window applies.
- A revoked or expired key gets the same
401as a value that was never a key, but each attempt is recorded as a refusedmcp_publictrace in the key’s workspace (reasoncredential_revokedorcredential_expired, with the key prefix, never the key). If you revoked a key because it leaked, check Traces, ordewee traces list --channel mcp_public, to see whether it is still being used.
Scopes
Section titled “Scopes”| Scope | Grants |
|---|---|
| mcp.tools.read | Exported read-only tools: list and call |
| mcp.tools.write | The above, plus the explicitly exported mutation actions |
Exposure is explicit and default-deny. A tool is invisible unless it was annotated for export, and
tools/list and tools/call are governed by the same rule, so a hidden tool cannot be invoked by
naming it directly. Dangerous tools (exec, file mutation, browser, spawn, cron, package,
provider, admin and credential operations) are not exported.
The initial exported set is four read tools plus one narrowly gated mutation:
memory_search,memory_get,skill_searchweb_searchworkflowwithactionrestricted per action:draftandupdate_draftrequiremcp.tools.write;run,request_publish,publishanddry_runare never available through public MCP.
The session read tools (sessions_list, session_status, sessions_history) are deliberately
withheld. They are tenant-scoped but not user-scoped, while the equivalent HTTP routes are
user-scoped, so exporting them would expose every user’s sessions for the agents the credential can
reach.
Agent selection
Section titled “Agent selection”Resolution order, all inside the authenticated tenant:
X-Dewee-Agentheader?agent=query parameter (for clients that cannot set headers)- the tenant-local agent key
default
A selector that does not resolve — missing, archived, or belonging to another tenant — returns one
identical 404 {"error":"agent not found"} so the endpoint cannot be used to enumerate tenants.
When a selector is supplied, the default fallback is not applied.
Limits
Section titled “Limits”| Setting | Env var | Default |
|---|---|---|
| Request body cap | GOCLAW_MCP_PUBLIC_MAX_BODY_BYTES | 1 MiB (1048576) |
| Requests per minute per credential | GOCLAW_MCP_PUBLIC_REQUESTS_PER_MINUTE | 120 (burst 20) |
| Concurrent tool calls per credential | GOCLAW_MCP_PUBLIC_MAX_CONCURRENT_TOOL_CALLS | 4 |
| Tool-call deadline | GOCLAW_MCP_PUBLIC_TOOL_CALL_TIMEOUT_SECONDS | 60 |
| Authentication failures per minute per presented credential | GOCLAW_MCP_PUBLIC_FAILURES_PER_MINUTE | 60 |
| Authentication failures tracked per minute in total | GOCLAW_MCP_PUBLIC_GLOBAL_FAILURES_PER_MINUTE | 600 |
Every per-credential table is capped (4096 entries, idle entries swept after 10 minutes) regardless of configuration, so raising a limit cannot remove the bound.
3. Internal Claude CLI bridge
Section titled “3. Internal Claude CLI bridge”/mcp/bridge exposes a fixed allowlist of Dewee tools to the local Claude CLI. It is
gateway-token protected and relies on HMAC-signed context headers, and it is not an external
integration point. Do not expose it to the internet, and do not reuse it for third-party clients —
use POST /mcp/server instead.
The bridge is a hard precondition for tool-enabled Claude CLI turns: when the CLI reports the
dewee-bridge server as failed, needs-auth or disabled at startup, Dewee stops the turn with
a mcp_bridge_unavailable error rather than letting the CLI run without its managed tools. A Claude
CLI turn that exits with an error after streaming some text is reported as a failed turn, with the
partial text kept on the trace span; authentication, usage-limit and rate-limit failures are
classified like the same failures from HTTP providers, so model fallback and cooldown apply.
Protocol version
Section titled “Protocol version”The public endpoint implements MCP 2026-07-28 (stateless protocol core, server/discover, and
Mcp-Method / Mcp-Name routing headers) and negotiates down to supported 2025-era clients.
Requests are stateless: no protocol session store and no sticky routing are required.
Client compatibility
Section titled “Client compatibility”Render a ready-to-paste config for Claude Code, Cursor, VS Code, Gemini CLI or a generic HTTP
client with dewee mcp connect-config --client <client>.
The key is referenced through the DEWEE_MCP_TOKEN environment variable, never embedded in the
snippet.