draftshelf-mcp
v1.2.3
Published
MCP server giving an AI assistant direct read/write access to a DraftShelf account's resume Library and Versions.
Maintainers
Readme
DraftShelf MCP server
Lets an AI assistant (Claude Code, Claude Desktop, Codex, or any other MCP-compatible client) read and write your DraftShelf account's Library and Versions directly - add bullets, tailor a version for a specific job, export a PDF/DOCX, all through normal conversation instead of clicking through the web app.
Setup
draftshelf-mcp is published on npm - your MCP
client fetches it automatically the first time it launches.
Add it to your AI tool.
Claude Code - one command, no config file to edit by hand:
claude mcp add draftshelf -s user -- npx -y draftshelf-mcp(
-s usermakes it available in every project, not just the current one; drop it to scope it to the current project instead.)Codex CLI - also one command (per OpenAI's own docs):
codex mcp add draftshelf -- npx -y draftshelf-mcpClaude Desktop, or any other MCP client with no CLI - paste this into the client's own MCP config file (check its docs for exactly where that file lives):
{ "mcpServers": { "draftshelf": { "command": "npx", "args": ["-y", "draftshelf-mcp"] } } }Claude Desktop has no CLI at all today, so manual config is genuinely the only path for it - Anthropic's newer "Desktop Extensions" (
.dxt) format (double-click install, no JSON) exists but is a separate packaging effort this project hasn't built, not something skipped by accident.Restart your AI tool, then use it. The first time it actually launches the server, your browser opens on its own to a real DraftShelf sign-in + consent screen (the same one claude.ai/ ChatGPT use for their own connectors) - sign in there once. The server stores a token at
~/.draftshelf/credentials.json(owner-read-only) and every launch after that is instant, no browser involved.npx -y draftshelf-mcp login/logoutalso exist as manual commands, for signing in ahead of time or switching accounts, but neither is required.npxis also fetching the package itself and, the first time, a real Chromium browser forexport_pdf(a few hundred MB - the direct cost of PDF export actually measuring real page layout instead of guessing), so the very first launch takes noticeably longer than every one after it. If your AI tool gives up waiting before you finish signing in in the browser, finish signing in anyway (the server keeps waiting up to 5 minutes) and just ask it to try again - your tool sees the stored credentials immediately on the next attempt.The sign-in link itself stays valid for 10 minutes - reopening an old one past that (say, from browser history days later) shows a plain "Connection request expired" screen instead of letting you click through, since it can't complete either way. Just start the connection again (re-run the command above, or ask your AI tool to try again) for a fresh one.
Verify it's working. Ask it "what DraftShelf tools do you have access to?" - it should list things like
get_library,add_entry,export_pdf. Then ask it to add one real bullet to a real experience entry, and confirm in the DraftShelf web app (reload the page) that the bullet is actually there. That round trip - a change made in conversation showing up in the real app - is the actual point of this whole setup.
What it can do
37 tools, covering the same ground the web app itself does for tailoring a resume:
- Read:
get_library,get_header,list_tags,list_skill_groups,list_versions,get_version - Entries:
add_entry,edit_entry,remove_entry,add_bullet,edit_bullet,remove_bullet,update_header(kinds now includepublications/certifications, real citation-style entries alongside experience/projects/etc.) - Positions (multiple roles under one Experience entry, or multiple sub-entries under one
Custom Section heading):
add_position,edit_position,remove_position,move_position,convert_entry_to_positions - Versions:
create_version,duplicate_version,delete_version,restore_version,purge_version,update_version_selection(now also takesexcluded_position_ids),fill_version_by_tag,update_version,set_override,clear_override(both now also take an optionalposition_idto freeze one position's own field) - Export:
export_docx,export_pdf,get_page_layout(see exactly which section/entry landed on which page, and how full each page is, without exporting a PDF),get_full_export - Preferences:
get_preferences,update_preferences - GitHub backup:
get_github_backup_status(read-only),configure_github_backup(always refuses - see below) - Cover Letter:
generate_cover_letter(drafting inputs only, nothing saved)
What it deliberately can't do
- Set up GitHub backup.
configure_github_backupalways refuses and returns the manual setup steps instead. A GitHub personal access token is the single most sensitive secret this app ever handles - it's never allowed to touch an AI tool, only the browser → a locked-down server function → Supabase Vault. If you want backup, do it yourself in Settings → GitHub Manage. - Change your password, or manage sign-in. Not exposed at all.
- Import a JSON file wholesale. There's no import tool - but you don't need one: hand your
AI assistant a resume/PDF's text directly and ask it to add specific content via
add_entry/add_bullet, or ask it to tailor a specific version.get_full_exportcovers the export/backup half of what import used to be for.
Using it well
- Always ask it to check current state first (
get_library/get_version) before editing - someone may have changed the same account from the browser since your last message. - A "version" is a tailored resume for one real job application. Prefer asking for a duplicate of an existing version over editing one in place, unless you're still drafting it.
- Deletions require confirmation.
remove_entry/remove_bullet/delete_version/purge_versionall need an explicitconfirm:true- there's no undo dialog the way the web app has, so your assistant should always check with you in plain language first. export_pdfandget_page_layoutcan take 10-30 seconds. Both drive a real browser through the live app to guarantee correct pagination - a long pause there is normal, not a hang.- Ask it to check
get_page_layoutbefore and after trimming content for length, rather than guessing from bullet count alone - it reports the real, measured page each section/entry landed on and how full each page actually is. - If a write fails with a conflict error, someone else changed that data since your last read. Ask your assistant to re-fetch and re-apply, not retry blindly.
Every write reports a githubBackup status (not-configured / paused / pushed / failed)
if you have GitHub backup connected - your assistant should mention it if it ever comes back
failed, the same way the web app's own topbar status dot would.
Disconnecting
Revoke access any time from Settings → Connected Apps in DraftShelf itself - takes effect
immediately, no need to remove the config from your AI tool first. The access token this server
uses expires within the hour and silently renews itself while you're actively using it; revoking
just stops that renewal, so the very next request fails cleanly instead of quietly working
forever. npx draftshelf-mcp logout also clears the credentials stored locally at
~/.draftshelf/credentials.json, useful for switching accounts on the same machine.
Architecture notes (for anyone extending this)
- Every tool except
export_pdfandget_page_layoutis a straight HTTPS passthrough to themcp-apiSupabase Edge Function, authenticated with the token from your browser sign-in. This process never sees your Supabase session or any secret beyond that token. export_pdfandget_page_layoutare the two exceptions: both mint a real, narrowly-scoped session for your account (viamcp-api's internal_mint_sessiontool, itself gated by that same token) and drive real headless Chromium through the actual live DraftShelf app - the same techniquescripts/download_pdf.jsalready uses in this repo, generalized inexport-pdf.js/get-page-layout.js. This is deliberate, not incidental: a server-side "render the resume ourselves" approach can't measure real CSS layout (jsdom, for instance, has no layout engine at all), so it would silently report the wrong page count for every resume.get_page_layoutis the lighter of the two - it reads the samewindow.PAGE_UNIT_MAP/window.PAGE_FILL_RATIOSthe live app's own pagination engine already computes after a normal render, without ever callingwindow.downloadPdf()or touchingpdf-serviceat all.
