MCP Coverage Gap and Tool Build Queue
North star (PRINCIPLES.md #7): every action a human can take in the dashboard must have an MCP-tool equivalent. The product's main surface is the collaborative job-market matrix; MCP lets an agent operate that same workspace table instead of creating a parallel workflow. The practical target is: a human can ask an agent to add an internet job offer, enrich and evaluate it against the workspace criteria, inspect or adjust the CVL tree, tailor the application material, and keep the row state understandable from the dashboard.
1. Inventory of current MCP tools
Source: mcp/src/tools/ (registered in tools/index.ts).
| # | Tool | One-line | Health |
|---|---|---|---|
| 1 | add_job |
Create a job in a workspace (requires title or organization). Wraps job.create op. |
OK |
| 2 | update_job |
Patch fields on an existing job. Wraps job.update op. |
OK |
| 3 | delete_job |
Remove a job. Wraps job.delete op. |
OK |
| 4 | list_jobs |
List all jobs in a workspace. Reads live DO snapshot, falls back to D1. | OK |
| 5 | get_job |
Fetch one job by id. | OK |
| 6 | tailor |
Persist a tailored Node tree snapshot to a job via tailored.snapshot. Accepts branch_path to target CV or CL. Replaced tailor_cv/tailor_cl (commit e771e7c). CVL unification — no per-document-type tool twins. |
OK |
| 7 | get_cv_typst |
Return Typst source text for a workspace CV tree (quarry) or a tailored CV. Agents compile locally. Client-only PDF posture (no server Typst). Shipped commit c04203e. |
OK |
| 8 | evaluate_job |
Persist evaluation results (boolean/numeric criterion values) to a job. Persist-only. | OK |
| 9 | add_node |
Insert a new Node into a CV/CL quarry tree. Wraps node.create op. Renamed from add_section (commit 2122c85). |
OK |
| 10 | update_node |
Patch mutable fields on a tree Node. Wraps node.update. Renamed from update_section (commit 2122c85). |
OK |
| 11 | move_node |
Move a Node between parents. Wraps node.move. Renamed from move_section (commit 2122c85). |
OK |
| 16 | delete_node |
Delete a Node from a CV/CL quarry tree. Wraps node.delete. Added commit 2122c85. |
OK |
| 17 | set_selection |
Update which children of a Node are selected for rendering and minCount/maxCount. Wraps selection.update. Added commit 2122c85. |
OK |
| 18 | translate |
Copy a node from one language tree to another with LLM translation. Wraps translate op. Added commit 15657aa. |
OK |
| 12 | set_score_criterion |
Insert/update a single boolean or numeric scoring criterion. Wraps settings.update. |
OK |
| 13 | list_views |
List view definitions + active view id. Reads D1. | OK |
| 14 | set_active_view |
Switch the dashboard active view. Wraps settings.update. |
OK |
| 15 | get_workspace_summary |
Return id, slug, name, created_at, job_count, active_view_id. | OK |
1a. Status of the section.* → node.* rename, tailor unification, get_cv_typst, delete_node, set_selection, translate
All renames and missing tools have now landed:
| Item | Status | Commit |
|---|---|---|
add_section → add_node |
✅ Shipped | 2122c85 |
update_section → update_node |
✅ Shipped | 2122c85 |
move_section → move_node |
✅ Shipped | 2122c85 |
tailor_cv + tailor_cl → tailor (CVL unification) |
✅ Shipped | e771e7c |
get_cv_typst (Typst source for agents) |
✅ Shipped | c04203e |
delete_node (wraps node.delete op) |
✅ Shipped | 2122c85 |
set_selection (wraps selection.update op) |
✅ Shipped | 2122c85 |
translate (cross-language node copy with LLM translation) |
✅ Shipped | 15657aa |
Remaining gaps (not yet resolved):
tailored.promoteis missing from MCP. Always adds as new variant; nomodearg.cv_profile.replace/cl_profile.replace— no tool exposes per-language full-tree replace; needed to seed a fresh language from agent code.mcp/test/tools.test.ts— confirm tests were updated alongside the renames; stale assertions againstsections.create/sections.updateop kinds would be a regression.
PDF posture is locked: client-only rendering. No analyze_pdf tool. No server-side Typst. Agents use get_cv_typst for source (compile locally), or download_file for cached PDF bytes from R2. See §5.1.
2. Inventory of human capabilities (v2 + v1 reference)
Walked through ui/src/routes, ui/src/lib/components/toolbar/modals/, ui/src/lib/components/cvl/, and ui/src/lib/components/dashboard/.
Capability areas:
A. Workspace lifecycle
A1. Create workspace (POST /api/workspaces).
A2. List workspaces — v1 had a landing-page list; v2 home page is a stub. No GET /api/workspaces.
A3. Rename workspace name / slug — v2 calls PUT /api/workspaces from layout but route does not exist (PARITY-AUDIT §3.1).
A4. Delete workspace — DELETE /api/workspaces/:id not ported (PARITY-AUDIT 4.13).
A5. Resolve / "switch" workspace by slug — GET /api/workspaces/:id accepts both id and slug; no list-and-pick tool for agents that don't already hold an id.
B. Job lifecycle
B1. Add by URL — POST /api/workspaces/:id/jobs with {url}. Pipeline auto-advance is unimplemented client-side (PARITY-AUDIT 4.1).
B2. Add manually — same endpoint, with {title, organization, ...}.
B3. Bulk import (CSV/JSON wizard) — UI is a 36-LoC stub (PARITY-AUDIT 4.10).
B4. Bulk URL paste (JobImport modal) — stubbed, blocked on scrape.ts lift status.
B5. JobRadar (job-board scraping) — stubbed, blocked on radar.ts lift.
B6. Edit job fields — PATCH /api/workspaces/:id/jobs/:jobId.
B7. Delete job — DELETE /api/workspaces/:id/jobs/:jobId.
B8. Move status / phase — _statusOnly flag on PATCH; kanban DnD ports the gesture.
B9. Archive job — represented by terminal status; no separate archive op.
C. PDF / file handling
C1. Upload CV / CL / misc — POST /api/workspaces/:id/files returns 501 (PARITY-AUDIT 4 / §3 row "File upload"). Op files.upload exists in catalog.
C2. Download tailored CV/CL or attachment — GET /api/files/:fileId?workspace=… works.
C3. Delete file — DELETE /api/files/:fileId?workspace=… works (ref-count + R2 cascade).
C4. Render tailored CV/CL to PDF — happens client-side via Typst WASM (lib/domain/src/typst.ts + typst-client.ts); there is no server endpoint and no MCP tool. Agents cannot get PDF bytes.
C5. PDF analysis (read-the-PDF) — no v1 or v2 surface; CV upload is treated as opaque bytes (this is consistent with how v1 worked).
D. CV / CL editing (the L0/L1/L2 tree)
D1. Personal info edits — pending design (PARITY-AUDIT §5.1: tree-of-choices says PersonalInfo collapses into a Node).
D2. Header layout (drag chip bar, H toggles) — InfoEditor stub.
D3. Format edits (font, colors, conventions, link styling, ortho, dateFormat) — FormatEditor stub.
D4. Section / Node CRUD — node.create / update / delete / move.
D5. Bullet / item CRUD — same Node ops at deeper depth.
D6. Selection: which children render, in which order, max-count — selection.update op.
D7. Variants: snapshot a tailored output, promote variant L2 → L1 / L0 — tailored.snapshot, tailored.promote.
D8. Per-language replace (seed a new lang) — cv_profile.replace / cl_profile.replace.
D9. Profile narrative (markdown blob injected into every tailoring prompt) — page is a stub; no Y.Doc root or REST path exists.
E. Pipeline
E1. Trigger extraction (URL → fields) — v1 /api/extract-fields endpoint, now 410 Gone with note "moved to MCP" — but no MCP tool actually exists.
E2. Trigger enrichment (salary estimation, commute) — /api/commute lifted; salary.ts not lifted (PARITY-AUDIT 4.2).
E3. Trigger evaluation — /api/evaluate 410s with note "moved to realtime worker"; not implemented in the worker either, no MCP tool.
E4. Trigger tailoring — tailor_cv / tailor_cl only persist; no tool that actually runs the tailoring chain. (Per principle #8 LLM is client-side; the agent is the LLM here, so trigger semantics on MCP = "run my own LLM and call tailor_*". This is okay but the agent needs the input context — there is no tool that returns "everything tailoring needs": job + quarry + cascade chain + format + language + narrative.)
E5. Monitor pipeline state — no read tool returns per-job stage / led status.
E6. Cancel pipeline run — n/a, no run state on the server side.
F. Cascade / BYOK
F1. List providers / capabilities — v2 has PROVIDERS config in @cv/domain; no MCP read tool.
F2. Set provider keys (BYOK) — PATCH /api/workspaces/:id/cloud-keys. Note: cloud_keys writes are blocked over the WebSocket op channel for secret-leak reasons (validate.ts); the cloud_keys.update op is reserved for service-binding (i.e. MCP) callers.
F3. Read cascade chain — GET /api/chain?workspace=… returns unmasked keys. No MCP tool wraps it (and probably shouldn't, see "cross-cutting").
F4. Edit chain order / promote provider — Models tab UI; no REST endpoint yet (it patches cloud_keys.chains via the cloud-keys PATCH).
F5. Health check a provider — POST/GET /api/health-log records timeline; no actual "ping this provider with my key" endpoint, no MCP tool.
G. Notes / scoring / annotations
G1. Workspace notes (collaborative ProseMirror) — v2 dropped the editor; /api/notes returns 410 (PARITY-AUDIT 4.6).
G2. Set scoring criterion — set_score_criterion (existing).
G3. Set evaluations — evaluate_job (existing).
G4. Set scoring config (aggregation mode, weights) — settings.update via Y.Doc; no granular MCP tool.
H. Views
H1. List views — list_views (existing).
H2. Set active view — set_active_view (existing).
H3. Create / edit / delete a view (groups, rules, sort) — Views modal in UI; no MCP tool.
H4. Column visibility, widths, layout — layout.update op exists; no MCP tool.
H5. Kanban config (phases, statuses, swimlanes) — Kanban modal; no MCP tool.
H6. Order state mutations (column / group / job manual order) — order_state.update op; no MCP tool.
I. Export
I1. Export to xlsx / csv — prepareExportData + exportToExcel in @cv/domain/export; UI button is a console.warn no-op (PARITY-AUDIT row "Export").
I2. Render PDF — see C4.
I3. Raw Typst source — stackToTypst lifted; no endpoint or tool.
J. Multi-language
J1. Switch active lang — handled in format.lang; UI in CvGenerator chip list.
J2. Add a new lang — UI uses a prompt() (PARITY-AUDIT row "Language switching"); no MCP tool.
J3. See L1 deltas — implicit in tree structure; no read tool exposes "delta of lang X vs base."
J4. Promote L2 → L1 / L0 — tailored.promote op exists, no MCP tool.
3. Gap matrix
Severity:
- P0 — blocks the chat-first workflow ("paste URL → ingest → tailor → export PDF" must work end-to-end via MCP).
- P1 — high-value editing or read capability the agent needs to be useful.
- P2 — nice-to-have, used rarely or by humans only.
| ID | Capability | MCP tool | REST endpoint | Notes | Sev |
|---|---|---|---|---|---|
| A1 | Create workspace | MISSING | POST /api/workspaces (works, but seeds empty defaults — PARITY-AUDIT 3.2) |
Agent has no way to make a workspace from chat. | P0 |
| A2 | List workspaces | MISSING | REST MISSING | Agent can't enumerate. Without this, "switch workspaces by slug" is the only path. | P1 |
| A3 | Rename workspace | MISSING | REST MISSING (PUT route absent) |
PARITY-AUDIT 3.1. | P2 |
| A4 | Delete workspace | MISSING | REST MISSING | PARITY-AUDIT 4.13. | P2 |
| A5 | Resolve workspace by slug | (covered by get_workspace_summary — accepts slug or id via getWorkspace) |
GET /api/workspaces/:id accepts both |
Works but agent must already know the slug. Pair with A2. | P1 |
| B1 | Add job by URL | add_job exists |
POST /api/workspaces/:id/jobs exists |
Pipeline auto-advance does not run (PARITY-AUDIT 4.1) — adding by URL inserts the row but extraction never fires. Need a separate extract_fields tool or a "wait for extraction" semantics. |
P0 |
| B2 | Add job manually | add_job |
works | OK | done |
| B3 | Bulk CSV/JSON import | MISSING | REST MISSING | UI stub. P1 because agents can loop add_job, but ergonomics matter. |
P2 |
| B4 | Bulk URL paste | MISSING | REST MISSING | Same as B3 + needs scrape.ts. |
P2 |
| B6 | Edit job | update_job |
PATCH /jobs/:id |
OK; check that _statusOnly / _salaryOnly etc. flags are exposable — currently they aren't surfaced in the tool's input schema. |
P1 |
| B7 | Delete job | delete_job |
DELETE /jobs/:id |
OK | done |
| B8 | Move status | covered by update_job (PATCH status_id) |
works | A dedicated move_job_status taking a status name instead of status_id would be friendlier. |
P2 |
| C1 | Upload CV / file | MISSING | POST /files/ returns 501 |
Op files.upload exists. PDF ingestion is required for the agent vision — agent must be able to give CareerVector a CV PDF. Blocked on r2.ts domain lift. |
P0 |
| C2 | Download file | MISSING (no download_file tool) |
GET /files/:id works |
Agents need to fetch tailored output bytes. | P0 |
| C3 | Delete file | MISSING | DELETE /files/:id works |
Op files.delete exists. |
P1 |
| C4 | Render PDF | MISSING | REST MISSING | PDF rendering is client-side Typst WASM today. No server-side render path exists in v1 or v2. Need a decision: lift Typst into a Worker, or have agents call get_typst_source and compile externally. See cross-cutting §5.1. |
P0 |
| C5 | Read uploaded PDF (vision/OCR) | MISSING | REST MISSING | Not present in v1 either. The agent (Claude) already has vision; the right move is "give the agent the file URL, it reads it itself" rather than building OCR. | P2 |
| D1 | Personal info edits | partly (update_section once tree lands) |
n/a (Y.Doc) | Pending design (PARITY-AUDIT 5.1). Block on tree-of-choices PersonalInfo decision before specifying tool. | P1 (blocked) |
| D2 | Header layout | MISSING | n/a | Stub editor; depends on D1. | P2 |
| D3 | Format edits | MISSING | n/a | Stub editor. Op is settings.update patching format. |
P1 |
| D4 | Node CRUD: create | add_section (rename → add_node) |
n/a | Renaming pending. Op works. | P1 |
| D4 | Node CRUD: update | update_section (rename → update_node) |
n/a | Renaming pending. | P1 |
| D4 | Node CRUD: delete | MISSING | n/a | node.delete op exists. Important — agents can create but not destroy. |
P0 |
| D4 | Node CRUD: move | move_section (rename → move_node) |
n/a | Renaming pending. | P1 |
| D6 | Selection update (active children, minCount/maxCount) | MISSING | n/a | selection.update op exists. Core to tree-of-choices loop. |
P0 |
| D7 | Tailored snapshot | tailor (commit e771e7c) |
n/a | Unified tool routes through tailored.snapshot, accepts branch_path for CV or CL. Replaces tailor_cv/tailor_cl. |
done |
| D7 | Tailored promote | MISSING | n/a | tailored.promote(path) op exists. Always adds as new variant; no mode arg. |
P1 |
| D8 | Per-lang full-tree replace | MISSING | n/a | cv_profile.replace / cl_profile.replace ops exist. |
P1 |
| D9 | Profile narrative | MISSING | REST MISSING | No Y.Doc root, no REST path (PARITY-AUDIT 4.7). The narrative is injected into every LLM tailoring prompt — without it, agent-driven tailoring degrades. | P0 |
| E1 | Trigger extraction | MISSING | /api/extract-fields 410'd ("moved to MCP" — but the MCP tool was never built) |
The 410 message lies. | P0 |
| E2 | Trigger salary enrichment | MISSING | n/a | salary.ts not lifted. |
P1 |
| E2 | Trigger commute calc | MISSING | POST /api/commute works |
A compute_commute tool would just wrap the existing endpoint. |
P1 |
| E3 | Trigger evaluation | MISSING | /api/evaluate 410'd ("moved to realtime worker" — not implemented either) |
evaluate_job only persists results, doesn't run them. |
P1 |
| E4 | Trigger tailoring | MISSING (only tailor_cv persist exists) |
n/a | The agent IS the LLM; what's missing is a tool that hands the agent the full tailoring context (job + quarry + format + lang + narrative + cascade hint) in one call, so it can run its own model and POST back. Call it get_tailoring_context. |
P0 |
| E5 | Read pipeline state | MISSING | REST MISSING | Per-job extraction-state / enrichment-state / tailored-state. | P1 |
| F1 | List providers | MISSING | REST MISSING | PROVIDERS is in @cv/domain. |
P2 |
| F2 | Set BYOK keys | MISSING | PATCH /cloud-keys works |
Sensitive — see cross-cutting §5.4. | P1 |
| F3 | Read cascade chain | MISSING | GET /api/chain returns unmasked keys |
Do not expose unmasked-key endpoint via MCP. A list_chain tool that returns chain order + masked keys is fine. |
P1 |
| F4 | Edit chain | MISSING | (currently via cloud-keys PATCH) | Wrap as set_chain_order. |
P1 |
| F5 | Provider health check | MISSING | REST MISSING | /api/health-log is the timeline; no "ping" endpoint exists. |
P2 |
| G1 | Notes | MISSING | /api/notes 410'd |
v1 had a 1057-LoC editor; v2 dropped it. Decision pending. | P2 (intent unclear) |
| G2 | Scoring criterion | set_score_criterion |
n/a | OK | done |
| H1 | List views | list_views |
n/a | OK | done |
| H2 | Set active view | set_active_view |
n/a | OK | done |
| H3 | CRUD a view definition | MISSING | n/a | settings.update op covers it. |
P1 |
| H4 | Layout / column visibility | MISSING | n/a | layout.update op exists. |
P2 |
| H5 | Kanban phases / statuses | MISSING | n/a | settings.update. |
P2 |
| H6 | Order state | MISSING | n/a | order_state.update. |
P2 |
| I1 | Export xlsx / csv / JSON | MISSING | REST MISSING | prepareExportData / exportToExcel lifted; Dashboard.svelte button is console.warn. Trivial to wrap. |
P1 |
| I2 | Render PDF | LOCKED: not building | — | PDF rendering is client-only. No server-side Typst, ever (PRINCIPLES.md #8, §5.1). Agents get PDF via download_file from R2 cached renders, or compile from get_cv_typst output. |
n/a |
| I3 | Raw Typst source | get_cv_typst (commit c04203e) |
— | Returns Typst source for quarry or tailored CV. This is the canonical agent path for offline PDF generation. | done |
| J2 | Add language | MISSING | n/a | cv_profile.replace with new lang. |
P1 |
| J4 | Promote variant | MISSING | n/a | tailored.promote. |
P1 |
Counts (updated): After 2122c85 and 15657aa, delete_node, set_selection, and translate are now "done". D4 delete (P0), D6 selection (P0), and translate (unlisted in original) are closed. P0 count drops to 10. Remaining P0 = 10, P1 = 21, P2 = 14.
4. Build queue (updated state)
Original top-10, with completed items marked. Ordered by north-star impact.
| # | Tool | Status | Notes |
|---|---|---|---|
| 1 | Rename add_section/update_section/move_section → add_node/update_node/move_node; add delete_node; add set_selection |
✅ DONE (2122c85) |
All renames landed. delete_node and set_selection added in same commit. |
| — | translate tool (cross-language node copy with LLM translation) |
✅ DONE (15657aa) |
Unlisted in original queue; shipped alongside the renames. |
| 2 | extract_fields |
OPEN | scrape.ts is lifted; MCP wiring still missing. The 410 message on /api/extract-fields lies — it claims "moved to MCP" but no tool exists yet. |
| 3 | upload_file |
OPEN | POST /api/workspaces/:id/files now works (c386fa3, 455ed27). MCP wrapper still missing. |
| 4 | download_file |
OPEN | GET /api/files/:fileId works. Wrapper is a small S-effort item. |
| 5 | get_tailoring_context |
OPEN | Profile narrative path still unresolved (OQ parked). Can ship with narrative: '' placeholder. |
| 6 | tailored_promote |
OPEN | Op exists; no MCP tool yet. |
| 7 | render_pdf |
LOCKED — not building. | Client-only PDF posture. See §5.1. |
| 8 | get_typst_source |
✅ DONE as get_cv_typst (c04203e) |
Returns Typst source for quarry or tailored CV. |
| 9 | create_workspace + list_workspaces + get_workspace_by_slug |
OPEN | POST /api/workspaces works; GET /api/workspaces (list) still needs a new endpoint. |
| 10 | update_selection |
✅ DONE as set_selection (2122c85) |
Shipped in item #1 rename batch. |
Implicit (deferred): set_chain_order + list_providers — important for BYOK flows, but agents will mostly receive keys out-of-band. Defer until P0 list is closed.
5. Cross-cutting concerns
5.1 PDF rendering boundary [LOCKED 2026-04-26]
Decision: client-only rendering. No server-side Typst on Cloudflare Workers.
Typst WASM (~25MB) loads from CDN, fonts preload into a virtual filesystem, stackToTypst() produces the source, $typst.pdf() produces bytes in the browser. Server-side Typst on Cloudflare Workers would burn CPU budget and fights PRINCIPLES.md #8 (client compute is the default).
Agent PDF access paths:
- PDF bytes: agents pull from the most recent cached client render stored in R2 via
download_file/GET /api/files/:fileId. - Typst source: agents call
get_cv_typstwhich returns the source text directly. Agents can compile locally (e.g.,typstCLI on the user's machine via Claude Code).
There is no render_pdf tool that produces bytes on-demand from the server. get_cv_typst (build queue #8, shipped at commit c04203e) is the correct approach for agent-driven PDF generation.
OCR / vision posture [LOCKED 2026-04-26]: CareerVector does NOT build OCR or PDF extraction. Agents (Claude, GPT-4o, Gemini, etc.) have vision built-in; when a user uploads a reference PDF, the agent reads it itself via the file URL. CareerVector exposes three pathways for the agent: structured Node tree, Typst source as text, and file bytes. Files are uniform — get_file(fileId) does not distinguish "rendered output" from "user-uploaded reference." A PDF is a PDF. There is no analyze_pdf tool and none will be added.
5.2 URL ingestion / scrape.ts
scrape.ts already lives at lib/domain/src/scrape.ts (with tests/scrape.test.ts). The lift is done. What's missing is the wiring: extract_fields tool, plus the client-side JobImport modal calling it. Build queue #2 is unblocked on the import side.
5.3 Output bytes — URL vs base64
MCP tools return text content. Both modes are legal:
- base64 in
structuredContent: simple, no URL plumbing, works without auth. Agents (Claude) handle base64 cleanly. Bad for files > a few MB (token cost in the agent's context window). - URL the agent fetches: forces the agent (or its host) to be allowed to make HTTP requests to our domain. Fine for Claude Code, less fine for sandboxed agents.
Recommendation: thresholded. For files ≤ 1 MB, return base64 in structuredContent. For larger (typical tailored PDFs are 50–200 KB so we're rarely there), return {url, expiresAt} and let the agent fetch. Document the threshold in the tool's description so agents can plan.
5.4 Streaming / long-running jobs
Tailoring takes 30–60 s. PDF compile is faster (<5 s on the client; unknown on the server). Options:
- Block. Tool waits until done, returns the result. Simple. Risks: MCP transport timeouts (varies by client), agent UX (no progress indicator).
- Job-id + poll. Tool returns
{jobId}; agent pollsget_job_status(jobId)until done. Familiar pattern; fits well with the singleflightcompute_lockpattern already in v1. Adds infra (a job table or D1 row). - Realtime fan-out. Mutation triggers a DO broadcast; agent subscribes via WebSocket. Powerful but MCP transports don't generally do WebSockets — the agent host would need an extra capability.
Recommendation: for tailoring, since the agent IS the LLM (per principle #8), the tool only persists the result — it's not waiting on a server-side LLM call. So tailoring tools (tailored_snapshot, tailor_cv) can block trivially (write completes in ms). For PDF rendering, recommend block (option 1) with a documented soft cap of ~30 s. For extract_fields: same logic — the agent runs the LLM, so the tool just persists, no wait. Job-id pattern not needed yet.
5.5 Workspace identity / agent default workspace
Per principle #5, possessing the workspace ID is the access credential. There is no default workspace and no auth.
Three patterns make sense:
- Agent connection pinned to one workspace. The MCP server URL includes the workspace id (
?ws=<id>or as a header). Every tool call inherits it; tools could omitwsIdfrom args. - Tools always require
wsId. Current state. Predictable but verbose. - Hybrid: connection-level default, tool-level override. Best of both.
Recommendation: keep current behavior (tools take wsId). Add a MCP_DEFAULT_WORKSPACE env var on the worker for single-tenant deployments (per agent install). Document in tool descriptions: "If your MCP host is configured with a default workspace, you may omit wsId." list_workspaces (P1) is the resolution mechanism for new agents that don't know the slug.
For the BYOK keys sensitive case: the cloud-keys.update op is intentionally allowed only over the service binding (i.e. MCP, not the public WS) per validate.ts. So a set_byok_keys tool can write keys without the leak risk. Read paths must remain masked through MCP — never expose /api/chain (unmasked) via a tool.
6. Things needing user input before #1 starts
- Notes editor decision. v1 had a 1057-LoC ProseMirror editor; v2 dropped it. Is this intentional (= remove
/api/notes+ don't add an MCP tool) or a port miss? Not on the critical path either way, but a "decide once, document" item. - Server-side PDF rendering posture. LOCKED 2026-04-26 — client-only. See §5.1. No action needed.
- Profile narrative storage. Where does the markdown blob live? A new Y.Doc root (
profile.narrative), an extension ofcv_profile's root Node, or a workspace settings field? Tree-of-choices is silent. Decide before #5 (get_tailoring_context) — without it, the tool returns an empty narrative and tailoring quality stays degraded. - PDF analysis (vision). LOCKED 2026-04-26 — NOT building OCR/vision. Agent reads PDFs itself. No
analyze_pdftool. See §5.1. - MCP per-host default workspace. Confirm pattern in §5.5 ("env var per worker install, tools still take
wsIdfor explicitness") matches Julian's deploy model.
7. Out of scope (this doc)
- Implementation. This is a plan.
- New product features beyond v1 / v2 today. Greenfield restraint per task brief.
- Touching files owned by other agents.
End of MCP-COVERAGE.md.