Skip to main content
Glama
mattjjahn-spec

resume-mcp

resume-mcp

A resume that answers questions instead of sitting in a PDF.

The problem

Recruiters and hiring managers skim resumes for 20 to 30 seconds. AI agents (Claude Desktop, Cursor, and similar tools) can do much better than skimming, but only if they have something structured to query. A PDF or a wall of markdown gives a model a pile of text to summarize. It does not give the model a way to ask a follow-up question, look up one specific claim, or take an action.

This repo turns a resume into a small local server that speaks the Model Context Protocol (MCP). Point an MCP-aware agent at it, and the agent can search work history by keyword, search it by meaning, pull a deep dive on a specific company, or open an email pre-filled with a scheduling request. It is the same resume data, exposed as tools instead of prose.

Related MCP server: jobjourney-claude-plugin

Approach and why

MCP over a custom API. MCP is the emerging standard for connecting agents (Claude Desktop, Cursor, and others) to external tools and data. Building on it means any MCP-compliant client can use this server without custom integration work. The tradeoff is that MCP is still young and the SDK's surface has moved between versions, so this repo pins a known-working SDK version rather than always tracking latest.

Structured JSON as the source of truth, not the resume file itself. resume.json holds the actual content: summary, skills, and per-job highlights. Both tools (keyword and semantic search) read from the same structure, so there is one place to update when the resume changes, and no risk of the two search modes drifting out of sync with each other.

Two search tools, not one. Keyword search (query_resume_history) is exact, fast, and has no dependencies beyond fs.readFileSync. It is the right tool when the caller already knows the word they are looking for ("LEAP", "Acquisition"). Semantic search (semantic_search_history) is for the more common case: a recruiter asking "has he managed hardware rollouts?" when the resume says "field teams" and "Starbucks locations" instead of "hardware." Keeping both means the fast, dependency-light path never regresses just because the semantic path exists. See "RAG design notes" below for how the semantic tool is built.

Local-first, no API key. The semantic search tool embeds text on the machine running the server, using a small quantized model (see below). There is no network call at query time and no vendor key to configure. That is a deliberate constraint: a portfolio artifact that a stranger can clone and run should not require them to sign up for anything.

Semantic search is opt-in, so the base install stays clean. The embedding library (@xenova/transformers) is not a default dependency. npm install pulls only the MCP SDK, so the base server has no native or ONNX toolchain and npm audit is clean. Keyword search, company deep dives, and scheduling all work out of the box. You enable semantic search explicitly with npm run add-semantic, and the server only advertises the semantic_search_history tool once it is installed.

One honest tradeoff worth knowing before you opt in: @xenova/transformers pulls in an ONNX runtime whose transitive protobufjs dependency carries a critical npm audit advisory. It only affects parsing untrusted protobuf input, and this server only uses the runtime to run inference on one pinned, trusted local model file, never to parse input from a network caller. The suggested fix downgrades transformers.js to a broken pre-release, a worse trade. Isolating this to an opt-in install keeps that advisory out of anyone who does not need the feature.

Stdio transport, not HTTP. MCP supports both. Stdio keeps the server a single local process with no port to manage or secure, which fits a personal, single-user tool. An HTTP transport would matter if this needed to serve multiple concurrent clients or run as a hosted service; it does not.

Tools exposed

Tool

Arguments

What it does

query_resume_history

query: string

Exact, case-insensitive substring search over the summary and work history.

semantic_search_history

query: string, topK?: number

Local embedding-based search over the same work history, for paraphrased or conceptual queries. Opt-in: only advertised after npm run add-semantic.

get_company_deep_dive

company: "nomad-go" | "tune" | "wide-orbit"

Returns expanded detail on a specific role that does not fit in a resume bullet.

send_scheduling_email

none

Opens the local default mail client with a pre-filled scheduling message.

Quickstart

Requires Node.js 18 or higher.

git clone <this-repo-url>
cd resume-mcp
npm install
npm test

npm test runs the unit tests for the keyword search and the semantic search's chunking, cosine similarity, and ranking logic (no model download required for the tests).

The base install gives you keyword search, company deep dives, and scheduling. To enable the semantic search tool:

npm run add-semantic

This installs @xenova/transformers and, on the next server start, the semantic_search_history tool is advertised. Without it, the server runs fine and simply does not list that tool.

To run the server directly and inspect it interactively:

npx @modelcontextprotocol/inspector node index.js

This opens a browser GUI where you can call each tool and see the raw request and response. The server itself talks JSON-RPC over stdio, so running node index.js in a plain terminal will appear to hang. That is expected: it is waiting for an MCP client to connect, not for keyboard input.

The first call to semantic_search_history downloads a small (about 23MB) quantized embedding model from Hugging Face and caches it locally. Every call after that, including in future sessions, uses the cached copy.

Connecting to an agent

Claude Desktop

Edit your Claude Desktop config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "resume": {
      "command": "node",
      "args": ["/absolute/path/to/resume-mcp/index.js"]
    }
  }
}

Restart Claude Desktop after saving. The four tools above will show up as available tools in a new conversation.

Cursor

  1. Open Cursor Settings > Features > MCP.

  2. Click + Add New MCP Server.

  3. Set:

    • Name: resume

    • Type: stdio

    • Command: node /absolute/path/to/resume-mcp/index.js

  4. Save.

Both clients launch the server as a subprocess and talk to it over stdio, so there is no port or URL to configure.

Example output

Captured from a real run against index.js, using the MCP SDK's own client library to call each tool over stdio (not hand-written):

=== tools/list ===
query_resume_history, semantic_search_history, get_company_deep_dive, send_scheduling_email

=== call: query_resume_history { query: 'Marketplace' } ===
Found 1 matching items for "Marketplace":

[2021 - 2023] Technical Product Manager at Wide Orbit (Greater Seattle Area):
 - Designed, built, and shipped the Wide Orbit Marketplace, empowering direct ad spend management.

=== call: semantic_search_history { query: 'experience scaling releases across hardware and retail locations' } ===
Top 3 semantic matches for "experience scaling releases across hardware and retail locations":

1. [score 0.506] Sr. Technical Program/Product Manager at Nomad Go (2025 - 2026)
   Product Manager leading core application code development and directing cross-organizational coordination to manage releases scaling to all North American Starbucks locations (US & Canada).

2. [score 0.488] Technical Product Manager at Wide Orbit (2021 - 2023)
   Managed regular software release cycles, coordinating timing, release notes, and deployments.

3. [score 0.486] Summary
   10+ years of technical product leadership, directing cross-functional teams, preparing SaaS platforms for acquisition, and scaling enterprise releases across retail and ad-tech spaces.

=== call: get_company_deep_dive { company: 'tune' } ===
TUNE / HasOffers Acquisition Details:
- Role: Product Manager II.
- Greenfield Product: Payout Structures rule builder.
- Business Value: Allowed direct advertisers to configure complex tiered, funnel-stage, geographic, and retroactive payout rate-cards.
- Valuation Impact: Transformed TUNE from serving solely affiliate networks to direct enterprise advertisers, massively expanding TAM/ARR and setting up the Constellation Software acquisition in 2020.

Notice the semantic query never uses the words "Starbucks," "field teams," or "release cadence" that appear in the top match. It asks about "hardware" and "retail locations," and the embedding model connects that to the bullet about North American retail rollouts anyway. A keyword search for the same phrase would have returned nothing.

Example agent conversation

This is the shape of what an agent sees and does when a person asks a question in plain language:

User: Has Matt ever managed a rollout involving physical hardware, not just software?

Agent (tool call):

{
  "name": "semantic_search_history",
  "arguments": { "query": "rollout involving physical hardware", "topK": 2 }
}

Tool response:

{
  "content": [
    {
      "type": "text",
      "text": "Top 2 semantic matches for \"rollout involving physical hardware\":\n\n1. [score 0.53] Sr. Technical Program/Product Manager at Nomad Go (2025 - 2026)\n   Product Manager leading core application code development and directing cross-organizational coordination to manage releases scaling to all North American Starbucks locations (US & Canada).\n\n2. [score 0.41] Technical Program/Project Manager at Point Inside (2014 - 2017)\n   Coordinated company-wide releases for 3D mapping and in-store Bluetooth location beacons."
    }
  ]
}

Agent (to user): Yes. At Nomad Go he coordinated releases across all North American Starbucks locations, working with hardware field teams. Earlier, at Point Inside, he coordinated releases for in-store Bluetooth location beacons, which is also a physical hardware rollout.

RAG design notes

The semantic search tool is a small, deliberately minimal retrieval-augmented generation (RAG) setup. Every choice below was made to keep it local, fast to start, and honest about its own limits.

Chunking: one highlight per chunk. resume.json is chunked in lib/semantic_search.js at the level of individual bullet points (plus one chunk for the summary), not whole jobs and not individual sentences within a bullet. A whole-job chunk would bury a specific claim ("positioned TUNE for acquisition") inside three unrelated bullets, diluting its embedding. Splitting below the bullet level would not help here: resume highlights are already single, dense claims, so there is nothing smaller worth retrieving on its own. The right chunk size depends on the shape of the source document, and for a resume that shape is the bullet point.

Embedding model: all-MiniLM-L6-v2, quantized, run through transformers.js. This is a 384-dimension sentence embedding model, small enough (about 23MB quantized) to download on first use and cache locally, and fast enough to embed a resume's worth of chunks in a few milliseconds after the model is loaded. It is not the most accurate embedding model available. It is accurate enough to separate "hardware rollout" from "software release" in a document with a few dozen chunks, which is the actual job here.

Why local instead of an API embedding call. This server is meant to be cloned and run by a stranger with minimal setup (npm install, then one npm run add-semantic to opt into embeddings). An API-based embedding model would mean an API key, a paid account, and a network dependency for a feature that is answering questions about a single resume. None of that cost buys meaningfully better retrieval at this scale. Local-first also means the tool works offline and the resume content never leaves the machine it runs on.

No vector database. Chunk embeddings are computed once per process (on the first semantic search call, then cached in memory) and compared with plain cosine similarity over an array. A resume has dozens of chunks, not millions. A vector database (Pinecone, Weaviate, pgvector, and similar) earns its keep when the corpus is large enough that a linear scan becomes slow, when chunks need to be updated incrementally without re-embedding everything, or when multiple processes need to share one index. None of those conditions apply to one person's work history. Reaching for a vector database here would add an infrastructure dependency to solve a problem this dataset does not have.

What this does not do. There is no re-ranking step, no hybrid keyword-plus-vector fusion, and no generation step: the tool returns ranked source chunks, not a synthesized answer. That is intentional. The calling agent (Claude, or whatever model is driving the conversation) is the one doing the reasoning over the retrieved chunks. This tool's only job is retrieval.

How this was built

Built with Claude Code as the primary development tool, using the Model Context Protocol SDK (@modelcontextprotocol/sdk) as the runtime this server implements against. The original three-tool version of this server (query_resume_history, get_company_deep_dive, send_scheduling_email) was extracted from a private portfolio project. The semantic search tool, its unit tests, and this README were built on top of that base in an agentic coding session: writing the chunking and retrieval logic, verifying the embedding model actually downloads and runs before committing to it, and capturing real tool output (shown above) rather than writing example output by hand.

Project layout

resume-mcp/
  index.js                    MCP server: tool definitions and request handlers
  resume.json                 The actual resume content (source of truth for both search tools)
  lib/
    keyword_search.js         Exact substring search, pure function, unit tested
    semantic_search.js        Chunking, local embeddings, cosine ranking, unit tested
  test/
    keyword_search.test.js
    semantic_search.test.js

License

MIT. See LICENSE.

Abstracted from a larger private system I built; more at mjportfolio.dev.

Available Tools

3 tools
get_company_deep_diveA

Fetches deeper details on specific companies Matt has worked at (e.g. Nomad Go scaling details, TUNE Payout Structures).

ParametersJSON Schema
NameRequiredDescriptionDefault
companyYesThe company name to retrieve details for.

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses that the tool 'Fetches deeper details' and gives examples of the content, implying a read-only operation. However, it does not describe the return format, pagination, or any other behavioral constraints, leaving some ambiguity for a fetch tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence that front-loads the core action and resource, with examples adding valuable specificity. No wasted words or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one enum parameter and no output schema, the description is mostly complete: it states the purpose, scope, and typical use cases. It could be more explicit about the returned data's structure, but the examples partially compensate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%: the only parameter 'company' is fully described with an enum and explanation. The description adds no extra parameter-level detail beyond the tool's overall purpose, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the verb 'Fetches' and the resource 'deeper details on specific companies Matt has worked at', with concrete examples (Nomad Go scaling details, TUNE Payout Structures) that clarify scope. This clearly distinguishes it from sibling tools like query_resume_history, which is for general history.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool: when specifics on one of Matt's past companies are needed, as opposed to general history (query_resume_history) or scheduling (send_scheduling_email). It does not explicitly mention alternatives or exclusions, but the 'specific companies' phrasing offers clear context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

query_resume_historyA

Search Matt's work history, role bullet points, and experience highlights for an exact keyword or phrase. Fast, deterministic substring matching. Use semantic_search_history instead for conceptual or paraphrased queries.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSearch term, technology, or topic (e.g. 'AI', 'LEAP', 'Acquisition', 'Marketplace')

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses key behavioral traits: 'Fast, deterministic substring matching' and 'exact keyword or phrase.' This goes beyond the minimal purpose statement but doesn't detail return format or edge cases like case sensitivity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, with the purpose front-loaded and the alternative mentioned succinctly. Every sentence earns its place with no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The simple tool has no output schema, and the description covers purpose, behavior, and alternative usage. It does not describe the return format or pagination, but these are less critical for a straightforward search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already fully describes the query parameter with 100% coverage, so the baseline is 3. The description adds meaning by emphasizing 'exact keyword or phrase' and 'substring matching,' which clarifies how the parameter should be used.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches Matt's work history, role bullet points, and experience highlights for an exact keyword or phrase. It also distinguishes itself from semantic_search_history, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs to use this tool for exact keyword or phrase matching and recommends semantic_search_history for conceptual or paraphrased queries. This provides clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_scheduling_emailA

Opens the default mail client pre-populated with a scheduling request to contact Matt.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description explicitly states it 'opens' the client rather than sending, which is a useful behavioral trait. However, with no annotations, it doesn't disclose whether sending requires user confirmation, what happens if no mail client is configured, or any security/side-effect considerations beyond opening a program.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence, front-loaded with the action ('Opens the default mail client'), no filler, and every word contributes meaningful information. It is appropriately sized for the tool's simplicity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter tool with no output schema, the description is mostly complete: it states the action, the content, and the recipient. It loses a point because it doesn't mention the side effect beyond opening the client (e.g., whether this is a draft or automatically sent), and lacks any guidance on when to use it, but overall it's adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With zero parameters and an empty input schema, the description is the only source of parameter-related meaning. It explains that the email will be 'pre-populated with a scheduling request' and directed 'to contact Matt,' providing useful semantic context that the schema cannot.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Opens') and resource ('default mail client pre-populated with a scheduling request'), clearly distinguishing it from sibling query tools like query_resume_history and get_company_deep_dive. It also names the intended recipient ('contact Matt'), making the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives, and no exclusions or prerequisites are mentioned. The sibling tools are data-retrieval operations, but the description doesn't explicitly address selection criteria or context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 3 tool updatesv1.0.0
    • First observedget_company_deep_dive
    • First observedquery_resume_history
    • First observedsend_scheduling_email

TDQS

A4.2/5.0
Disambiguation4/5

Mostly distinct: query_resume_history is keyword search, get_company_deep_dive provides company-specific details, and send_scheduling_email handles outreach. However, the reference to a non-existent semantic_search_history in the query tool description could mislead agents, and company deep dives may overlap with search terms.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with snake_case: query_resume_history, get_company_deep_dive, send_scheduling_email. This is clear and predictable.

Tool Count5/5

Three tools is well-scoped for a resume-focused server, covering search, deep dive, and contact. Each tool earns its place with no redundancy.

Completeness3/5

The surface covers search, company details, and scheduling but lacks an obvious 'get full resume' or overview tool, and a semantic search tool is referenced but not provided. Core workflows exist but with notable gaps.

Maintenance

ActivityStale
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that provides a structured API for AI agents to query a person's resume, including profile, projects, writing, and gated access to experience and skills.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables AI-assisted job search workflows including job discovery, application tracking, resume evaluation, and cover letter generation, with support for multiple job sources and scheduled scraping.
    18
    1
    AGPL 3.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    An AI-powered developer productivity server that enables managing emails, analyzing GitHub repositories, searching AI/ML papers, and tailoring resumes via MCP tools.
    1
    MIT
  • F
    license
    A
    quality
    B
    maintenance
    MCP server that exposes a resume as callable tools and resources, enabling AI agents to query experience, skills, projects, and contact information via natural language.
    3
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mattjjahn-spec/resume-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server