{"record":{"id":"15c053fb703a8486","repo":"paperclipai/paperclip","slug":"path-must-start-with-and-be-relative-to-api-an","errorCode":null,"errorMessage":"path must start with / and be relative to /api, and must not contain '..'","messagePattern":"path must start with / and be relative to /api, and must not contain '\\.\\.'","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp-server/src/tools.ts","lineNumber":626,"sourceCode":"        return client.requestJson(\"POST\", path, { body });\n      },\n    ),\n    makeTool(\n      \"paperclipAddApprovalComment\",\n      \"Add a comment to an approval\",\n      z.object({ approvalId: approvalIdSchema, body: z.string().min(1) }),\n      async ({ approvalId, body }) =>\n        client.requestJson(\"POST\", `/approvals/${encodeURIComponent(approvalId)}/comments`, {\n          body: { body },\n        }),\n    ),\n    makeTool(\n      \"paperclipApiRequest\",\n      \"Make a JSON request to an existing Paperclip /api endpoint for unsupported operations\",\n      apiRequestSchema,\n      async ({ method, path, jsonBody }) => {\n        if (!path.startsWith(\"/\") || path.includes(\"..\")) {\n          throw new Error(\"path must start with / and be relative to /api, and must not contain '..'\");\n        }\n        return client.requestJson(method, path, {\n          body: parseOptionalJson(jsonBody),\n        });\n      },\n    ),\n  ];\n}\n","sourceCodeStart":608,"sourceCodeEnd":635,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/mcp-server/src/tools.ts#L608-L635","documentation":"The paperclipApiRequest escape-hatch tool sanitizes the caller-supplied path: it must start with '/' and must not contain '..'. The check is a deliberately conservative guard against path traversal, since path is forwarded verbatim (minus the leading slash) into client.requestJson against the /api base.","triggerScenarios":"An LLM or caller passes a path that does not start with '/' (e.g. 'issues/123') or contains '..' (e.g. '/../admin/users'). Both shapes throw before the HTTP call.","commonSituations":"Agent omits the leading slash by mistake; agent tries to escape /api to reach a sibling route; caller copy-pastes a URL path including the /api prefix already.","solutions":["Ensure path begins with '/' and is relative to /api (e.g. '/issues/123').","Remove any '..' segments — only forward slash paths within /api are allowed.","If you genuinely need an endpoint outside /api, use a different transport; this tool is scoped to /api on purpose."],"exampleFix":"// before\napiRequest({ method: 'GET', path: 'issues/123' })      // no leading '/'\napiRequest({ method: 'GET', path: '/api/../users' })     // contains '..'\n// after\napiRequest({ method: 'GET', path: '/issues/123' })","handlingStrategy":"validation","validationCode":"function isSafeApiPath(p: string): boolean {\n  return p.startsWith('/') && !p.includes('..');\n}\nif (!isSafeApiPath(input.path)) throw new Error('path must start with / and stay within /api');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always begin path with '/' and keep it relative to /api.","Never include '..' — escape attempts are rejected by design.","Strip any '/api' prefix from pasted URLs before passing as path."],"tags":["input-validation","path-traversal","security","paperclip-mcp"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}