{"record":{"id":"f0016ca5298a7ef5","repo":"koala73/worldmonitor","slug":"sign-in-to-revoke-mcp-clients","errorCode":null,"errorMessage":"Sign in to revoke MCP clients.","messagePattern":"Sign in to revoke MCP clients\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/services/mcp-clients.ts","lineNumber":77,"sourceCode":"  const rows = await settleAccountOperation(\n    userId,\n    'loading MCP clients',\n    () => client.query((api as any).mcpProTokens.listProMcpTokens, {}),\n  );\n  assertAccountStillCurrent(userId, 'loading MCP clients');\n  return rows as McpClientInfo[];\n}\n\n/**\n * Revoke a Pro MCP token by tokenId.\n *\n * Calls the edge endpoint (NOT the public Convex mutation directly) so the\n * negative-cache sentinel write is paired atomically with the Convex revoke.\n * Throws on non-2xx so the UI can surface the error.\n */\nexport async function revokeMcpClient(tokenId: string): Promise<void> {\n  const token = await getClerkToken();\n  if (!token) throw new Error('Sign in to revoke MCP clients.');\n\n  const resp = await fetch('/api/user/mcp-revoke', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: `Bearer ${token}`,\n    },\n    body: JSON.stringify({ tokenId }),\n  });\n\n  if (resp.ok) return;\n\n  if (resp.status === 404) {\n    throw new Error('This client was already revoked or no longer exists.');\n  }\n  if (resp.status === 409) {\n    throw new Error('This client was already revoked.');\n  }","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/src/services/mcp-clients.ts#L59-L95","documentation":"Thrown by revokeMcpClient() in src/services/mcp-clients.ts when getClerkToken() resolves to null before the POST to /api/user/mcp-revoke is attempted. Clerk returns no token when there is no active session: signed out, session expired, or Clerk not yet finished booting. The client-side guard exists because the edge handler would reject an unauthenticated request with 401 anyway.","triggerScenarios":"Clicking 'Revoke' on an MCP client row after the Clerk session expired (idle overnight tab), while signed out in another tab, before Clerk finishes loading after a page refresh, or with a Clerk JS load failure that leaves no session.","commonSituations":"Long-lived dashboard tabs whose Clerk session lapsed; multi-tab sign-out; restricted networks blocking Clerk's API so no session can be established; opening the settings deep link before Clerk hydration completes.","solutions":["Re-authenticate: redirect to sign-in or call Clerk's openSignIn(), then retry the revoke.","Only render enable-able revoke buttons after confirming getCurrentClerkUser() is non-null and a token is obtainable.","Verify the Clerk publishable key configuration if getClerkToken() is null even while visibly signed in (session/token domain mismatch).","Handle this message specially in the UI as a 'sign in required' state, not a generic error."],"exampleFix":"// before\nawait revokeMcpClient(tokenId);\n\n// after\nimport { getCurrentClerkUser } from '@/services/clerk';\nif (!getCurrentClerkUser()) {\n  await clerkOpenSignIn();\n  return;\n}\nawait revokeMcpClient(tokenId);","handlingStrategy":"validation","validationCode":"import { getCurrentClerkUser } from '@/services/clerk';\n\nif (!getCurrentClerkUser()) {\n  await openClerkSignIn();\n  return;\n}\nawait revokeMcpClient(tokenId);","typeGuard":"function isSignedIn(userId: string | null | undefined): userId is string {\n  return typeof userId === 'string' && userId.length > 0;\n}","tryCatchPattern":"try {\n  await revokeMcpClient(tokenId);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Sign in to revoke MCP clients.') {\n    await openClerkSignIn(); // re-authenticate, then let the user retry\n    return;\n  }\n  throw err;\n}","preventionTips":["Render revoke controls only for a confirmed Clerk session.","Treat this exact message as a 'sign-in required' UX branch, not a generic error.","Handle session-expiry events globally so long-open tabs re-auth before destructive actions."],"tags":["clerk","authentication","mcp","session-expired"],"backgroundTag":"missing-auth-token","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}