Troubleshooting and FAQ¶
17.1 The client shows no tools, or the server "disconnects" at once¶
Run the exact command from the client config in a terminal; if it exits, the error is on
stderr. Common causes: the command path is wrong for the client's environment (desktop apps
often have a minimal PATH, so use the absolute path cloudg mcp config prints); a
--dataset path that does not exist (the server refuses to start with
Dataset not found: ...); an unknown --policy name; or a Python older than 3.11. Then check
cloudg mcp tools with the same --policy and --role: a policy that hides everything for
the client's principal produces an empty list, not an error.
17.2 stdout corruption¶
Symptoms are parse errors in the client log or a dropped connection after a specific tool
call. The native and SDK stdio servers redirect stray print() and fd 1 writes to stderr,
but a library that writes to the real stdout file descriptor before the server starts can
still corrupt the stream. Do not wrap cloudg mcp serve in a script that echoes anything to
stdout. cloudg -v mcp serve is safe: the verbose logs go to stderr.
17.3 403 Forbidden: invalid Origin¶
The request carried an Origin header that is neither allowed (loopback by default) nor
same-origin with a validated Host header (on a non-loopback bind, same-origin counts only
with --allowed-host). Browsers always send it; most non-browser clients do not.
Add the page's origin with --allowed-origin (any flavor), and if a browser must read the
responses, with --cors-origin too (native flavor only).
17.4 421 Misdirected Request: Invalid Host header¶
The server is bound to loopback and received a Host other than localhost, 127.0.0.1 or
[::1] (or no Host at all), which is what DNS rebinding looks like. This also happens behind a reverse proxy that
forwards the public host name. Add it with --allowed-host 'name:*'.
17.5 outputSchema is missing under a privacy profile¶
tools_wire leaves out outputSchema for any tool whose output pipeline, for that
principal, is not empty. Masking and projection can change a value's type or drop a field,
and MCP clients must reject structured content that does not validate against an advertised
schema; the official SDK client raises an exception and the result is lost. Every built-in
profile except open has an output pipeline, so under them no tool advertises a schema, while
open shows 18. The structured content is still sent. If your client needs schemas, use a
policy without output transforms for that principal, and accept what that means for the data.
17.6 Large datasets and large results¶
- List tools are paginated (default 50 items, max 500). Prefer
find_assetsand other narrow queries over whole-graph resources such ascloudg://graph/d3. - The text block of a tool result is capped at
max_output_chars(200,000 by default; only settable from Python). With the cap set to 300 the text ended in... [truncated by cloudg mcp layer]andcloudg/transformscarried{"truncated_chars": 1821}, the number of characters removed, whilestructuredContentstill held all 5 items. The cap protects the context window of clients that read text; it does not shrink the payload. - The
standardprofile's projection step also caps output (max_chars: 120000,max_string: 20000); see MCP_PRIVACY.md. - The first graph or ontology query on a dataset builds the structure (
ontology_statsreports progress while it does). Later calls use the cache until the dataset changes. - More than 16 datasets evicts the oldest inactive one.
17.7 load_dataset says the path is outside the allowed roots¶
See 11.3. Set CLOUDG_MCP_ALLOWED_ROOTS in the client's env, or preload with
--dataset, which skips the check.
17.8 Pseudonyms change after a restart¶
The vault key is random per process unless CLOUDG_MCP_VAULT_KEY (or the policy's
vault.key) is set. Pseudonyms from an earlier session will not resolve after a restart
without it. With a vault.path in the policy, the server also saves the vault when it stops
(policy.save_vault()); see MCP_PRIVACY.md.
17.9 A timed-out tool keeps using CPU¶
Sync handlers run in worker threads, and Python cannot stop a thread. After a timeout the client gets its error result, but the thread finishes its work in the background. In the timeout example the thread kept reporting progress after the error was returned. Long operations should be async (they can be cancelled) or check a deadline themselves.
17.10 Hidden tool or denied tool?¶
With the default hide_denied: true, a denied tool is left out of tools/list and calling
it gives "Unknown tool". With hide_denied: false it stays listed and a call returns an error
result with -31001 and the reason. Either way the policy's audit trail records the attempt:
hidden or unknown names as not_found, denials as denied. Use cloudg mcp tools --role ROLE
to see what a role gets.
17.11 A token with a colon in it does not work¶
Specs are read from the right, so abc:def means token abc with role def. Write a token
that contains : in the full form, abc:def:: or abc:def:analyst: (6.3), or put it in an
environment variable and use env:VAR:ROLES:ID.
17.12 A live tool says "cooling down"¶
The live operation guard refused it (15.3). Query the dataset named in use_dataset, wait
retry_after_seconds, or, as an admin or operator, pass force=true. Call
rate_limit_status first to see which scopes are cooling down.
17.13 Which flavor answered?¶
The start-up line on stderr names it (native, mcp SDK 2.x, fastmcp), and the HTTP
Server header is cloudg-mcp for the native server and uvicorn for the others.