thedotmack/claude-mem · error

"${key}" is required

Error message

"${key}" is required

What it means

Thrown by requireString() in the recall MCP server when a tool argument named by `key` is missing, not a string, or whitespace-only. It guards the search/context/recent tool dispatch: projectId is required for all three, and query is required for search/context. The server converts this thrown error into an MCP error response via createRecallMcpServer.

Source

Thrown at src/server/mcp/recall-mcp-server.ts:90

      type: 'object',
      properties: {
        projectId: { type: 'string', description: 'Project to list.' },
        limit: { type: 'integer', minimum: 1, maximum: RECENT_LIMIT.max },
      },
      required: ['projectId'],
    },
  },
];

function clampLimit(raw: unknown, spec: { default: number; max: number }): number {
  if (typeof raw !== 'number' || !Number.isFinite(raw)) return spec.default;
  return Math.min(Math.max(1, Math.trunc(raw)), spec.max);
}

function requireString(args: Record<string, unknown>, key: string): string {
  const value = args[key];
  if (typeof value !== 'string' || value.trim().length === 0) {
    throw new Error(`"${key}" is required`);
  }
  return value;
}

function jsonResult(payload: unknown): CallToolResult {
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
}

// Dispatches a single tool call to the backend. Throws on unknown tools or
// invalid arguments; `createRecallMcpServer` converts those into MCP errors.
async function dispatchToolCall(
  backend: RecallBackend,
  name: string,
  args: Record<string, unknown>,
): Promise<CallToolResult> {
  if (name === 'search') {
    const observations = await backend.search({
      projectId: requireString(args, 'projectId'),

View on GitHub (pinned to d768ba3643)

Solutions

  1. Pass projectId (and query for search/context) as non-empty strings in the tool arguments.
  2. Validate args against the tool's inputSchema before calling (required: ['projectId','query'] for search/context; ['projectId'] for recent).
  3. Update the client to the current tool list so it sends all required fields.
  4. If driving the tool from an LLM, ensure the tool description makes the required fields explicit.

Example fix

// before: query omitted -> "\"query\" is required"
client.callTool({ name: 'search', arguments: { projectId: 'p1' } });
// after
client.callTool({ name: 'search', arguments: { projectId: 'p1', query: 'auth flow' } });
Defensive patterns

Strategy: validation

Validate before calling

function validateToolArgs(name: string, args: Record<string, unknown>): void {
  const needs = name === 'recent' ? ['projectId'] : ['projectId', 'query'];
  for (const key of needs) {
    const v = args[key];
    if (typeof v !== 'string' || v.trim().length === 0) {
      throw new Error(`"${key}" is required`);
    }
  }
}
// call validateToolArgs(name, args) before dispatchToolCall

Type guard

function isSearchArgs(a: unknown): a is { projectId: string; query: string; limit?: number } {
  return typeof (a as { projectId?: unknown })?.projectId === 'string'
    && typeof (a as { query?: unknown })?.query === 'string';
}

Try / catch

// createRecallMcpServer already converts thrown errors to MCP error results.
// In a custom wrapper:
try {
  result = await dispatchToolCall(backend, name, args);
} catch (error) {
  return { content: [{ type: 'text', text: (error as Error).message }], isError: true };
}

Prevention

When it happens

Trigger: An MCP client calls `search` or `context` without projectId or query, or with an empty/whitespace string; `recent` is called without projectId; the client sends a number/object where a string is expected.

Common situations: A client built against an older tool schema omits a field. An LLM-driven MCP client hallucinates partial args. A manual JSON-RPC call forgets a required key. A client passes query as a number.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/4c51c53b66ffded8. Report an issue: GitHub.