{"record":{"id":"4c51c53b66ffded8","repo":"thedotmack/claude-mem","slug":"key-is-required","errorCode":null,"errorMessage":"\"${key}\" is required","messagePattern":"\"(.+?)\" is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/server/mcp/recall-mcp-server.ts","lineNumber":90,"sourceCode":"      type: 'object',\n      properties: {\n        projectId: { type: 'string', description: 'Project to list.' },\n        limit: { type: 'integer', minimum: 1, maximum: RECENT_LIMIT.max },\n      },\n      required: ['projectId'],\n    },\n  },\n];\n\nfunction clampLimit(raw: unknown, spec: { default: number; max: number }): number {\n  if (typeof raw !== 'number' || !Number.isFinite(raw)) return spec.default;\n  return Math.min(Math.max(1, Math.trunc(raw)), spec.max);\n}\n\nfunction requireString(args: Record<string, unknown>, key: string): string {\n  const value = args[key];\n  if (typeof value !== 'string' || value.trim().length === 0) {\n    throw new Error(`\"${key}\" is required`);\n  }\n  return value;\n}\n\nfunction jsonResult(payload: unknown): CallToolResult {\n  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };\n}\n\n// Dispatches a single tool call to the backend. Throws on unknown tools or\n// invalid arguments; `createRecallMcpServer` converts those into MCP errors.\nasync function dispatchToolCall(\n  backend: RecallBackend,\n  name: string,\n  args: Record<string, unknown>,\n): Promise<CallToolResult> {\n  if (name === 'search') {\n    const observations = await backend.search({\n      projectId: requireString(args, 'projectId'),","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/server/mcp/recall-mcp-server.ts#L72-L108","documentation":"requireString() in the recall MCP server validates tool-call arguments. If a required argument is absent, not a string, or an empty/whitespace-only string, it throws `\"<key>\" is required`. This gives tool callers a clear, per-key message instead of undefined behavior downstream.","triggerScenarios":"Calling a tool (e.g. the observations tool) without the 'projectId' argument, passing it as a non-string (number, object), or passing an empty/blank string.","commonSituations":"MCP client omitting arguments from the tool-call input object; sending numbers/UUIDs as non-string JSON types; test harnesses with partially filled argument maps; a client using a null projectId for 'all projects'.","solutions":["Include the required key in the tool-call arguments as a non-empty string (e.g. projectId: 'my-project')","Coerce client-side values to strings before invoking (String(projectId))","If 'all projects' is intended, use a tool designed for it rather than an empty projectId"],"exampleFix":"// before\nawait client.callTool('observations', { projectId: 42 });\n// after\nawait client.callTool('observations', { projectId: String(42) });","handlingStrategy":"validation","validationCode":"function assertArgs(args: Record<string, unknown>): void {\n  if (typeof args.projectId !== 'string' || args.projectId.trim().length === 0) {\n    throw new Error('\"projectId\" is required');\n  }\n}","typeGuard":"const hasProjectId = (a: Record<string, unknown>): a is Record<string, unknown> & { projectId: string } =>\n  typeof a.projectId === 'string' && a.projectId.trim().length > 0;","tryCatchPattern":"try {\n  const result = await callRecallTool('observations', args);\n} catch (e) {\n  if (e instanceof Error && e.message === '\"projectId\" is required') {\n    // prompt user for a projectId or return a clear client-side error\n  }\n  throw e;\n}","preventionTips":["Validate tool arguments against the tool's input schema before calling","Always send string-typed IDs (UUIDs as strings, never numbers)","Reject empty/whitespace strings client-side before dispatch"],"tags":["mcp","validation","arguments","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}