Skip to content

fix: tighten MCP and resume privacy boundaries#85

Open
ManintheCrowds wants to merge 1 commit into
Gsync:devfrom
ManintheCrowds:career-bridge-privacy-seams
Open

fix: tighten MCP and resume privacy boundaries#85
ManintheCrowds wants to merge 1 commit into
Gsync:devfrom
ManintheCrowds:career-bridge-privacy-seams

Conversation

@ManintheCrowds

Copy link
Copy Markdown

Summary

  • resolve resume downloads by an owned resume ID and constrain file paths to the uploads root
  • stop add_job from returning default resume content; keep autoMatch fail-closed
  • split job creation, match persistence, and resume access scopes; exclude resume scopes from default tokens
  • add structured MCP results for job creation and match persistence

Closes #68.

Test plan

  • npm run lint
  • npm test (97 files, 1,248 tests)
  • npm run build
  • targeted MCP auth/rate-limit/privacy tests
  • loopback synthetic MCP pilot: job create, match persist, cross-user denial, token expiry, no resume rows, canary-clean rollback

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>
@Gsync

Gsync commented Jul 19, 2026

Copy link
Copy Markdown
Owner

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 add_job and save_match_result

Issued tokens hold ["jobs:write","questions:write","resume:write"]. The new gates require jobs:create and matches:write, with no migration or back-compat alias, so already-connected clients stop working:

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_jobsave_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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants