Setting Up MCP Servers with Claude Code: A Practical Walkthrough
A practical walkthrough of setting up MCP (Model Context Protocol) servers with Claude Code, covering installation, configuration, transport modes, tool definitions, and how to connect AI models to real-world APIs and services. Working examples you can copy and run today.
If you have spent any time in Claude Code and noticed the MCP section in settings, you have probably wondered what it actually does, how hard it is to wire up, and whether it is worth the effort. Short answer: yes, significantly worth it. MCP (Model Context Protocol) is the mechanism that lets Claude reach outside its context window and call real functions, query real databases, and interact with real APIs, all from inside a conversation.
This article walks through everything from understanding what MCP is to running your first custom server with Claude Code, including real configuration examples you can copy immediately.
What MCP Actually Is
MCP is an open protocol developed by Anthropic that standardizes how AI models communicate with external tools and data sources. Think of it as a structured handshake: your server declares what tools it offers, and Claude calls them with proper arguments and handles the results.
Before MCP, every integration was custom. You wrote special system prompts, hacked together function-calling schemas, and hoped the model followed the spec. MCP formalizes all of that into a single, predictable layer.
The protocol defines three core primitives:
Primitive
Description
Tools
Functions the model can call (e.g., search, fetch, write file)
Resources
Data the model can read (e.g., files, database records)
Prompts
Reusable prompt templates exposed by the server
These three primitives cover virtually every integration scenario you will encounter. Tools handle actions, resources handle data access, and prompts handle reusable interaction patterns. The protocol is transport-agnostic, meaning the same server code works over stdio for local development and over HTTP for production deployments.
Two Transport Modes
MCP servers run in one of two transport modes. Knowing the difference saves you hours of debugging.
stdio (Standard I/O)
The client (Claude Code) spawns your server as a subprocess and communicates over stdin/stdout. This is the simplest setup for local tools and personal workflows. No ports, no networking, no authentication needed.
Your server runs as an independent HTTP process. Claude Code connects to it over the network. This is the right choice for shared team servers, cloud deployments, or any server that needs to stay running between sessions.
💡 Start with stdio. It requires zero networking setup and is much easier to debug. Switch to HTTP transport only when you need shared access or persistent server state.
Installing the MCP SDK
Every MCP server starts the same way: install the official SDK and configure TypeScript for ESM.
Restart Claude Code. Open a new conversation and ask: "What is the weather in Paris?"
If everything is wired correctly, Claude will call get_weather with city: "Paris" and surface the result in its response. You will see the tool call appear in the conversation.
💡 Always use absolute paths in your MCP config. Relative paths break silently depending on how Claude Code resolves its working directory at launch time.
Structuring a Real Server
Real servers need clean separation between schema definitions, handlers, and service logic. Here is the file structure that scales without becoming unmaintainable:
src/
index.ts # Entry point and server setup
tools/
definitions.ts # Zod schemas for each tool input
handlers.ts # Business logic per tool
services/
api.ts # External API calls
db.ts # Database access layer
definitions.ts holds all Zod schemas:
import { z } from "zod";
export const searchInputSchema = {
query: z.string().min(1).describe("Search query text"),
limit: z.number().int().min(1).max(50).optional().default(10),
};
index.ts wires them together in a single server.tool() call per tool. This separation means you can test handlers without a running server and swap schemas without touching business logic.
5 Common Configuration Mistakes
These are the errors that trip up almost everyone building their first MCP server.
1. Missing .js file extensions in imports
Node16 module resolution requires explicit .js extensions even inside TypeScript source files. Omitting them causes runtime import failures that are confusing because the TypeScript compiler does not catch them.
// This fails at runtime with ERR_MODULE_NOT_FOUND
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
// This works correctly
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2. Using console.log() in stdio servers
In stdio mode, stdout is the protocol channel. Any console.log() call writes arbitrary text into that channel and corrupts the MCP stream. Use console.error() for all debug output.
3. Missing await on server connection
// Wrong: process may exit before connection completes
server.connect(transport);
// Correct: wait for connection handshake
await server.connect(transport);
4. Relative paths in Claude Code config
Claude Code launches from varying working directories. Always hardcode absolute paths or resolve them at startup using import.meta.url.
5. Overly broad tool descriptions
Claude uses your tool description to decide when to call it. Vague descriptions like "does stuff" lead to the tool being called too often or never. Be specific: "Fetch a GitHub pull request by owner, repo, and PR number."
HTTP Transport for Team Servers
When your server needs to be shared across a team or run in a cloud environment, HTTP with SSE is the right transport:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
const server = new McpServer({ name: "team-server", version: "1.0.0" });
const transports: Record<string, SSEServerTransport> = {};
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
transports[transport.sessionId] = transport;
await server.connect(transport);
});
app.post("/messages", express.json(), async (req, res) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
if (transport) await transport.handlePostMessage(req, res);
});
app.listen(3000, () => console.error("MCP server listening on :3000"));
Each Claude Code client stores its own session transport so multiple users can connect simultaneously without interference.
Exposing Resources
Resources let Claude read structured data without explicit tool calls. They are the right primitive for configuration, documentation, or cached state that Claude should be aware of without you needing to define a dedicated fetch tool.
Open http://localhost:5173. You will see all registered tools, their schemas, and a form to call each one directly with arbitrary inputs. The raw request and response JSON is visible, making it trivial to spot type mismatches or missing fields.
💡 Watch for schema mismatches. If Claude Code says a tool is registered but never calls it, the most common cause is a Zod schema that rejects the arguments the model provides. The Inspector lets you reproduce this without involving Claude at all.
Connecting to LLMs via MCP
One of the highest-value patterns is building MCP servers that orchestrate calls to multiple AI models. Your server becomes the middleware layer, and Claude Code becomes the coordinator.
You can expose a tool that routes requests to Deepseek R1 for deep reasoning, GPT 5 for creative writing, or Llama 4 Scout Instruct for fast document processing. Claude decides which tool to call based on the task at hand.
server.tool(
"route_to_model",
"Route a task to the most suitable language model for the job",
{
task: z.enum(["reasoning", "creative", "summarize"]),
input: z.string().describe("The text input to process"),
},
async ({ task, input }) => {
const modelMap = {
reasoning: "deepseek-r1",
creative: "gpt-5",
summarize: "llama-4-scout",
};
const result = await callModelApi(modelMap[task], input);
return { content: [{ type: "text", text: result }] };
}
);
With Claude Opus 4.7 as the orchestrator and your MCP server as the dispatch layer, you get a multi-model system that routes intelligently without complex infrastructure.
Picasso IA provides API access to models including Claude 4.5 Sonnet, Gemini 2.5 Flash, and Claude 4 Sonnet, making it practical to build multi-model MCP tools without managing separate API keys and client libraries for every provider.
Tool Error Handling
MCP tools should never throw unhandled exceptions. Return structured error content so Claude can report failures cleanly and decide what to do next:
The isError: true flag signals to Claude that the call failed. Claude will then decide whether to retry, use a fallback, or surface the error to the user.
What To Build Next
Once you have the basics working, the practical territory opens up. Here are the patterns teams are shipping with MCP today:
Use Case
What It Does
Database tool
Claude writes and runs scoped SQL queries safely
File system browser
Read/write access to specific project directories
API wrapper
Exposes Jira, GitHub, or Slack as callable tools
Image pipeline
Connects Claude to text-to-image generation APIs
Code sandbox
Runs and tests code snippets in an isolated container
RAG retriever
Searches a vector database and returns relevant chunks
The image pipeline use case is particularly powerful. You build an MCP server that accepts a text prompt from Claude, calls a text-to-image model, uploads the result to cloud storage, and returns the URL, all in a single tool call that Claude chains naturally in conversation without additional orchestration code.
For teams building workflows that include image generation, the 90+ models available on platforms like Picasso IA can be wired in as MCP tools, giving Claude direct access to diffusion models, upscalers, and editing pipelines from within a conversation.
Try It on Picasso IA
If you want to see what is possible with AI models before building your own MCP integrations, Picasso IA puts over 90 text-to-image models and dozens of large language models directly in your browser. Run Claude Opus 4.6, GPT 5, Deepseek R1, or Llama 4 Maverick Instruct without any setup.
It is the fastest way to test a model's output before committing to an API integration. Pick a model, send a prompt, and see exactly what you will be working with in your MCP toolchain. Whether you are generating images for a project, testing prompt structures, or exploring how different models handle the same input, the platform gives you fast access without the infrastructure overhead.
Create an account, pick a model, and start generating images or text today, no config files required.