initializdocs
DeveloperForge runtimeMCP

MCP — Troubleshooting

Common Phase 1 failure modes mapped to fixes.

Walk the table top-to-bottom — most failures fall in the first few rows.

Reachability and config

SymptomLikely causeFix
forge validate rejects transport: stdioStdio is on the roadmap; Phase 1 supports HTTP onlyWait for the deferred stdio strategy or run an HTTP MCP
forge validate says at least one of allow or deny must be setDefault-deny — explicit opt-in requiredAdd tools: { allow: ["*"] } or list specific tool names
forge mcp list shows failed with transport unavailable: dialServer URL unreachable; DNS or firewallcurl <url> from the same network; check forge.yaml URL
forge mcp list shows failed with protocol version mismatchServer announces a different MCP protocolVersion than the pinned oneUpdate the MCP server or wait for a Forge release pinning newer

OAuth

SymptomLikely causeFix
forge mcp login opens browser but state mismatch after consentStale callback or malicious redirect attemptRe-run; if persistent, check for browser extensions injecting URLs
At runtime: mcp: oauth token revokedRefresh token denied — usually expired or user revokedforge mcp login <name> again; re-bundle the Secret
At runtime: mcp: no stored token for "<name>" — run 'forge mcp login ...'Token store empty inside the podMount the Secret correctly; set HOME so the store path resolves
forge mcp test reports no stored token right after a successful logintest read a different store than login wroteSet the same mcp.token_store_path (forge.yaml) or MCP_TOKEN_STORE_PATH for both — test now honors it (fixed)
could not discover an authorization server at loginServer has no RFC 9728 metadata / wrong endpointUse the Streamable HTTP /mcp URL (not /sse); or set authorize_url/token_url/client_id explicitly
advertises no registration_endpoint and no client_id at loginServer doesn't support RFC 7591 dynamic registrationConfigure client_id (+ authorize_url/token_url) explicitly for that server
issued a confidential client (client_secret) at loginServer returned a secret; Forge is public-PKCE onlyConfigure client_id/authorize_url/token_url explicitly (Forge won't store a client secret)
At runtime: mcp: oauth token revoked after the AS revoked the DCR clientPersisted registration is staleforge mcp logout <name> (clears the registration record) then forge mcp login <name> to re-discover + re-register

Tool name conflicts

SymptomLikely causeFix
tool name "foo__bar" contains '__' which is reserved for MCP namespacingA builtin / skill tool tried to use the __ separatorRename the tool; __ is reserved for <server>__<tool>
mcp_tool_conflict in audit logTwo MCP tools with the same name registeredThe second registration is rejected; check forge mcp list

Egress / NetworkPolicy

SymptomLikely causeFix
Locally the agent works; in K8s it fails with transport unavailableNetworkPolicy egress doesn't include the MCP serverRebuild — egress_stage.go merges MCPDomains automatically; re-apply manifests
OAuth refresh fails with transport: dial from the podtoken_url host blocked by NetworkPolicyThe build merges this too — verify the latest egress_allowlist.json
Discovery-based server: refresh blocked because the auth-server host isn't allowlistedThe build-time freeze can't know a discovered hostThe runtime learns it from the login-time mcp_reg_<name>.json and merges it — ensure that file is mounted alongside the token (see cli-reference)

Runtime behavior

SymptomLikely causeFix
Required server fails → pod exits with non-zeroThis is the intended behavior; K8s CrashLoopBackOff is the signalFix the upstream MCP, then redeploy
Required=false server logs a warning; agent has no tools from itOperator marked the server optional; agent continues without itSet required: true if the agent cannot run without those tools
Tool calls return [truncated] suffixTool result exceeded MaxResultChars (default 64 KiB)Reduce the upstream response or request smaller chunks

Where to look next

  • Audit log search: grep -E 'mcp_server_failed|mcp_token_refresh.*"ok":false' <stderr>
  • Per-server probe: forge mcp test <name>
  • Egress allowlist contents: cat .forge-output/compiled/egress_allowlist.json | jq '.entries[] | select(.source | startswith("mcp:"))'

On this page