Skip to content

Troubleshooting and FAQ

Section 17 · From docs/MCP.md on GitHub

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_assets and other narrow queries over whole-graph resources such as cloudg://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] and cloudg/transforms carried {"truncated_chars": 1821}, the number of characters removed, while structuredContent still held all 5 items. The cap protects the context window of clients that read text; it does not shrink the payload.
  • The standard profile'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_stats reports 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.