karthik.dev
Back to blogMCP

Building a Production MCP Server from Scratch

2026-08-20 · 4 min read

Why I Built an MCP Server

When I started building Career-Agents, I needed a way for LLM clients — Claude Desktop, Cursor, Windsurf — to invoke backend career-intelligence tools reliably. The Model Context Protocol (MCP) was the right abstraction: a standard interface for exposing tool definitions to LLM clients with typed inputs, typed outputs, and no hallucinated tool names.

This post covers what I learned building a production MCP server from scratch.

What is MCP?

MCP is an open protocol that defines how an LLM client (the "host") discovers and calls tools exposed by a server. The server publishes a list of tool definitions — each with a name, description, and JSON Schema for inputs and outputs — and the client calls them during inference.

The key insight: the tool schema is the contract. Get it right and your agent is reliable. Get it wrong and the LLM hallucinates arguments, calls the wrong tool, or misinterprets results.

Tool Schema Design

Each of my 10 tools follows this pattern:

{
  name: "assess_career",
  description: "Analyze a candidate's background and return career readiness scores across technical, communication, and portfolio dimensions.",
  inputSchema: {
    type: "object",
    properties: {
      resume_text: { type: "string", description: "Raw resume text" },
      target_role: { type: "string", description: "Target job title, e.g. 'Senior AI Engineer'" },
    },
    required: ["resume_text", "target_role"],
  },
}

Lessons learned on schema design:

  • Be specific in descriptions — "Analyze a candidate's background" is better than "Process resume". The LLM uses the description to decide when to call the tool.
  • Use required strictly — optional fields often cause the LLM to omit them even when they'd improve results.
  • Return structured JSON — not prose. Prose outputs are hard for the orchestrator to pass to the next tool.

The 10 Tools

Tool Function
assess_career Career readiness scoring
optimize_resume ATS keyword and bullet optimization
generate_interview_questions Role-specific interview preparation
analyze_job_fit Job description vs candidate match scoring
recommend_career_path Next role and skill gap recommendations
audit_github_portfolio Repository and profile analysis
optimize_linkedin Headline and summary optimization
search_companies Company intelligence lookup
recommend_skills Skill gap analysis and learning path
voice_lab Spoken voice agent interface (27 languages)

Error Handling

The biggest reliability issue I hit was query-matching quirks in search and recommendation tools. When the LLM passes a slightly malformed query, the tool would return an empty result set — and the agent would silently fail.

Fix: validate and normalize all inputs before hitting the backend, and return structured error objects (not thrown exceptions) so the agent can retry with a corrected query:

if (!input.resume_text || input.resume_text.trim().length < 50) {
  return {
    error: "resume_text_too_short",
    message: "Resume text must be at least 50 characters.",
    suggestion: "Ask the user to provide more resume detail.",
  };
}

Client Integration

The MCP server works with Claude Desktop, Cursor, and Windsurf out of the box:

// claude_desktop_config.json
{
  "mcpServers": {
    "career-agents": {
      "command": "npx",
      "args": ["-y", "career-agents", "mcp"]
    }
  }
}

Cursor / Windsurf: Add the same config to .cursor/mcp.json or .windsurf/mcp.json.

Publishing to NPM

The full Career-Agents platform — including the MCP server — is published as a global CLI:

npm install -g career-agents
career-agents mcp  # starts the MCP server
career-agents assess  # runs the career assessment agent

Publishing as a global CLI means any developer can add Career-Agents tools to their LLM client in seconds, without cloning the repo.

What I'd Do Differently

  1. Version tool schemas explicitly — when I changed an input field name, existing Claude Desktop configs broke silently.
  2. Add a health-check tool — a ping tool that returns server version and available tools makes debugging much easier.
  3. Log every invocation — structured logs of tool name, input hash, latency, and result type are invaluable in production.

Try It

npm install -g career-agents
career-agents doctor  # check your environment
career-agents assess  # run the career assessment

GitHub: github.com/karthikrshet/Career-Agents