{"record":{"id":"1401762d103cf851","repo":"paperclipai/paperclip","slug":"paperclip-api-origin-is-unavailable-project-tools","errorCode":null,"errorMessage":"Paperclip API origin is unavailable","messagePattern":"Paperclip API origin is unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/routes/project-tools.ts","lineNumber":24,"sourceCode":"import { forbidden } from \"../errors.js\";\n\n/** Mounted after actor middleware; connection-scoped tokens cannot authenticate here. */\nexport function projectToolRoutes(db: Db) {\n  const router = Router();\n  router.post(\"/mcp/project-tools\", async (req, res) => {\n    const context = await projectToolContext(db, req.actor);\n    assertCompanyAccess(req, context.run.companyId);\n    const { id = null, method, params } = req.body;\n    const send = (result: unknown) => res.json({ jsonrpc: \"2.0\", id, result });\n    if (method === \"initialize\") return send({ protocolVersion: \"2025-03-26\", capabilities: { tools: { listChanged: false } }, serverInfo: { name: \"paperclip-project-tools\", version: \"1\" } });\n    if (method === \"notifications/initialized\") return res.status(202).end();\n    const definitions = projectToolDefinitions(context.issue.workMode, true);\n    if (method === \"tools/list\") return send({ tools: definitions });\n    if (method !== \"tools/call\") return res.json({ jsonrpc: \"2.0\", id, error: { code: -32601, message: \"Method not found\" } });\n    try {\n      if (!definitions.some(tool => tool.name === params?.name)) throw forbidden(\"Tool is unavailable in this mode\");\n      const apiUrl = process.env.PAPERCLIP_API_URL;\n      if (!apiUrl) throw new Error(\"Paperclip API origin is unavailable\");\n      const result = await callProjectTool({\n        name: params.name, arguments: params.arguments ?? {}, apiUrl,\n        token: req.header(\"authorization\")!.replace(/^Bearer\\s+/i, \"\"),\n        companyId: context.run.companyId, issueId: context.issue.id, agentId: context.run.agentId,\n        conversation: Boolean(context.issue.conversationAgentId),\n      });\n      return send({ content: [{ type: \"text\", text: JSON.stringify(result) }], structuredContent: result });\n    } catch (error) {\n      return send({ isError: true, content: [{ type: \"text\", text: error instanceof Error ? error.message : \"Project tool failed\" }] });\n    }\n  });\n  return router;\n}\n","sourceCodeStart":6,"sourceCodeEnd":38,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/routes/project-tools.ts#L6-L38","documentation":"The project-tools MCP endpoint proxies tool calls to the Paperclip API via callProjectTool, and it requires the origin of that API from the PAPERCLIP_API_URL environment variable. When the variable is unset (empty string is falsy), the server cannot construct the callback URL and throws this error instead of making a doomed request. It is a startup/deployment configuration guard for the tool-call path only.","triggerScenarios":"A tools/call request reaches /api project-tools route in a deployment where process.env.PAPERCLIP_API_URL is not set; the tools/list path works because it never needs the origin, so the misconfiguration only surfaces on actual tool invocation.","commonSituations":"Self-hosted deployments behind a reverse proxy where only PORT/HOST were configured; running the server outside the standard dev script which sets PAPERCLIP_API_URL; Docker/K8s manifests missing the env entry; new env-var naming after a refactor.","solutions":["Set PAPERCLIP_API_URL to the externally reachable origin of the Paperclip API (e.g. https://paperclip.example.com) and restart the server.","In dev, use the standard `pnpm dev` flow which provisions the API origin automatically.","Add the variable to your deployment manifest/Dockerfile env and verify with `curl $PAPERCLIP_API_URL/api/health`.","If the server should default to localhost, export PAPERCLIP_API_URL=http://localhost:3100 in the shell profile."],"exampleFix":"// before (docker-compose.yml)\n// environment:\n//   - NODE_ENV=production\n// after\nenvironment:\n  - NODE_ENV=production\n  - PAPERCLIP_API_URL=https://paperclip.internal.example.com","handlingStrategy":"validation","validationCode":"if (!process.env.PAPERCLIP_API_URL) {\n  throw new Error(\"PAPERCLIP_API_URL must be set for project tool calls\");\n}","typeGuard":"null","tryCatchPattern":"try { await callMcpTool(name, args); }\ncatch (e) {\n  if (e.message === \"Paperclip API origin is unavailable\") {\n    process.exitCode = 1; // fail fast at boot, not at call time\n  }\n  throw e;\n}","preventionTips":["Assert PAPERCLIP_API_URL at server startup, not first tool call","Include the var in deployment manifests and health checks","Smoke-test with curl $PAPERCLIP_API_URL/api/health after deploy","Document the requirement in deployment runbooks"],"tags":["env-var","configuration","mcp"],"backgroundTag":"missing-env-var","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}