fix: tighten MCP and resume privacy boundaries#85
Conversation
Prevent implicit resume disclosure, require granular MCP scopes, and resolve downloads through owned resume records so external career adapters can operate without profile leakage. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Can't merge this — it breaks MCP tool calls for tokens already in use, and breaks two tools permanently for everyone. 1. Existing tokens lose
|
| Tool | Required after PR | Existing tokens | New tokens |
|---|---|---|---|
add_job |
jobs:create |
broken | works |
save_match_result |
matches:write |
broken | works |
add_question |
questions:write |
works | works |
review_resume |
resumes:read |
broken | broken |
save_resume_review |
resume-reviews:write |
broken | broken |
The failure is silent: the scope check returns plain text with no isError flag, and add_question keeps working, so a connector looks partially alive rather than broken.
2. review_resume and save_resume_review become unreachable
resumes:read and resume-reviews:write are gated but excluded from DEFAULT_MCP_TOKEN_SCOPES, and createMcpToken hardcodes those defaults — there's no scope-picker UI. No code path can mint a token holding either scope, so reissuing doesn't help. This is what the comment at src/app/api/mcp/route.ts:131 warned about ("Do not add one without also backfilling existing tokens").
3. The add_job → save_match_result flow is removed
add_job no longer emits the match directive, so save_match_result is never triggered even with a valid new token — it stays registered but nothing calls it. That two-call design is deliberate: it exists so the app performs zero LLM inference on the match path.
autoMatch is also added to the public tool schema but only ever returns "Automatic MCP matching is disabled" — a parameter exposed to external agents that does nothing.
4. Type errors in the new tests
npx tsc --noEmit reports 4 errors in __tests__/mcpAddJob.spec.ts (lines 33, 56, 74, 88). handleAddJob takes the schema's post-transform output type, where dueDate/appliedDate are required; the previous tests used as any. Vitest doesn't typecheck, so the suite passes green despite this.
5. No test covers what breaks
mcpScopes.spec.ts only exercises the pure helper. Nothing tests route-level scope gating against a legacy-shaped token, which is why all 1,248 tests pass while every existing token would fail in production.
Minor: structuredContent is returned without a corresponding outputSchema on any server.tool() registration — per the MCP spec, clients should ignore it absent a declared schema.
I'll handle #68 separately.
Reviewed by Claude Code
Summary
add_jobfrom returning default resume content; keepautoMatchfail-closedCloses #68.
Test plan
npm run lintnpm test(97 files, 1,248 tests)npm run build