thedotmack/claude-mem · error · ServerClientError

transport

transport

Error message

${toolName} requires CLAUDE_MEM_RUNTIME=server. Current runtime is "worker"; use the existing search/timeline/get_observations tools for worker-mode memory access.

What it means

requireServerForObservationTool guards server-only observation tools (like observation_add) behind runtime resolution. If resolveServerToolContext() returns nothing — meaning CLAUDE_MEM_RUNTIME is 'worker' (the default) so no server context exists — it throws a ServerClientError with code 'transport' telling the caller the tool needs CLAUDE_MEM_RUNTIME=server and pointing them to the worker-mode tools (search, timeline, get_observations) instead. This is an intentional routing error: the capability exists only in server runtime.

Solutions

  1. Switch to server runtime: set CLAUDE_MEM_RUNTIME=server for the MCP server process and provide the required server config (API key, server URL).
  2. If you intend to stay in worker mode, use the worker-mode tools instead: search, timeline, get_observations.
  3. Verify the variable is exported in the environment where the MCP server itself launches (e.g. Claude Code MCP config env block), not just a shell.
  4. After changing the runtime, restart the MCP server so resolveServerToolContext() re-reads the environment.

Example fix

// before: calling server-only tool in worker mode
observation_add({ content: 'note' })  // ServerClientError: transport
// after: either set CLAUDE_MEM_RUNTIME=server in the MCP server env, or in worker mode use:
search({ query: 'note' })
Defensive patterns

Strategy: type-guard

Validate before calling

function canUseServerObservationTools(): boolean {
  return (process.env.CLAUDE_MEM_RUNTIME ?? '').trim() === 'server';
}
// call only if canUseServerObservationTools(), else use search/timeline/get_observations

Type guard

function isServerRuntime(env: NodeJS.ProcessEnv = process.env): boolean {
  return (env.CLAUDE_MEM_RUNTIME ?? '').trim() === 'server';
}

Try / catch

try {
  ctx = requireServerForObservationTool('observation_add');
} catch (err) {
  if (err instanceof ServerClientError && err.code === 'transport') {
    // fall back to worker-mode tooling (search/timeline/get_observations)
  }
  throw err;
}

Prevention

When it happens

Trigger: Invoking a server-only MCP tool (e.g. observation_add, handleSessionStartContext's ctx resolution) while the MCP server runs with CLAUDE_MEM_RUNTIME unset or = 'worker', so resolveServerToolContext() yields no server context.

Common situations: User configured claude-mem in default worker mode but an agent/workflow tries the newer server-only tools; docs or prompts referencing observation_add without mentioning the runtime requirement; env var typo'd or set only in the worker process and not the MCP server 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 thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/68c7930e49b855fb. Report an issue: GitHub.

Appendix: source

Thrown at src/servers/mcp-server.ts:207

      text: `Tool error: ${error instanceof Error ? error.message : String(error)}`,
    }],
    isError: true as const,
  };
}

function formatJsonResult(payload: unknown): { content: Array<{ type: 'text'; text: string }> } {
  return {
    content: [{
      type: 'text' as const,
      text: JSON.stringify(payload, null, 2),
    }],
  };
}

function requireServerForObservationTool(toolName: string): ServerAvailable {
  const resolution = resolveServerToolContext();
  if (!resolution) {
    throw new ServerClientError(
      'transport',
      `${toolName} requires CLAUDE_MEM_RUNTIME=server. Current runtime is "worker"; use the existing search/timeline/get_observations tools for worker-mode memory access.`,
    );
  }
  if (!resolution.available) {
    throw new ServerClientError('missing_api_key', `${toolName}: ${resolution.reason}`);
  }
  return resolution;
}

function wrapHandler<Args>(
  toolName: string,
  execute: (args: Args) => Promise<{ content: Array<{ type: 'text'; text: string }> }>,
): (args: Args) => Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {
  return async (args: Args) => {
    try {
      return await execute(args);
    } catch (error) {

View on GitHub (pinned to d8bc9755e7)