ruvnet/ruflo · error

mcp-caller-auth-enabled-but-no-token

mcp-caller-auth-enabled-but-no-token

Error message

mcp-caller-auth-enabled-but-no-token

What it means

When MCP caller authentication is enabled, resolveMcpCallerIdentity requires both CLAUDE_FLOW_MCP_INVOCATION_TOKEN and CLAUDE_FLOW_MCP_CALLER_PUBKEY to be set. It throws this coded error when either variable is missing or empty, refusing to authorize the tool call rather than falling back to unauthenticated access.

Solutions

  1. Set both CLAUDE_FLOW_MCP_INVOCATION_TOKEN and CLAUDE_FLOW_MCP_CALLER_PUBKEY in the server environment
  2. Check the service/launch configuration actually exports the vars (systemd Environment=, docker -e, etc.)
  3. If auth was enabled unintentionally, disable MCP caller auth in policy config

Example fix

// before
# token not exported
// after
export CLAUDE_FLOW_MCP_INVOCATION_TOKEN=<token-envelope>
export CLAUDE_FLOW_MCP_CALLER_PUBKEY=<64-hex-pubkey>
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.CLAUDE_FLOW_MCP_INVOCATION_TOKEN || !process.env.CLAUDE_FLOW_MCP_CALLER_PUBKEY) { throw new Error('MCP caller auth enabled but token/pubkey env vars missing'); }

Type guard

const hasMcpAuthEnv = (): boolean => Boolean(process.env.CLAUDE_FLOW_MCP_INVOCATION_TOKEN && process.env.CLAUDE_FLOW_MCP_CALLER_PUBKEY);

Try / catch

try { await callMcpTool(tool, args); } catch (e) { if (e.message === 'mcp-caller-auth-enabled-but-no-token') { /* set env vars or disable caller auth */ } throw e; }

Prevention

When it happens

Trigger: authorizeMcpTool runs with auth enabled but process.env.CLAUDE_FLOW_MCP_INVOCATION_TOKEN or CLAUDE_FLOW_MCP_CALLER_PUBKEY is unset/empty.

Common situations: Enabling mcp caller auth in config without provisioning the corresponding env vars; running the server under a service manager that strips the operator's env; forgetting to pass the token to a child process.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-09-15). Data as JSON: /api/errors/8f9941f0907bf728. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/services/policy-runtime.ts:353

 * `verifyInvocationToken` here, and the tool-mismatch check is not exercised
 * for this use case. What this retains: a process that never received the
 * signed token cannot forge one, so it cannot impersonate `agent:<workerId>`.
 * What it does NOT retain: a compromised worker process can still use its
 * own valid token for every MCP call for the rest of its lifetime — the
 * token does not limit blast radius to a single call.
 */
function resolveMcpCallerIdentity(): { id: string; type: 'agent' | 'legacy' } {
  if (!isMcpCallerAuthEnabled()) {
    return {
      id: process.env.CLAUDE_FLOW_PRINCIPAL_ID ?? 'legacy-cli',
      type: process.env.CLAUDE_FLOW_PRINCIPAL_ID ? 'agent' : 'legacy',
    };
  }

  const encodedToken = process.env.CLAUDE_FLOW_MCP_INVOCATION_TOKEN;
  const publicKeyHex = process.env.CLAUDE_FLOW_MCP_CALLER_PUBKEY;
  if (!encodedToken || !publicKeyHex) {
    throw new Error('mcp-caller-auth-enabled-but-no-token');
  }

  const token = decodeTokenEnvelope(encodedToken);
  if (!token) {
    throw new Error('mcp-caller-auth-enabled-but-no-token');
  }

  let publicKey;
  try {
    publicKey = publicKeyFromHex(publicKeyHex);
  } catch {
    throw new Error('mcp-caller-auth-enabled-but-no-token');
  }

  const result = verifyInvocationToken(token, publicKey, {});
  if (!result.valid) {
    throw new Error(`mcp-caller-auth-verification-failed:${result.reason}`);
  }

View on GitHub (pinned to 2602b642d9)