MCP Server Tutorial: Build, Run & Debug Locally with Claude Desktop

MCP Server Tutorial: Build, Run, and Test a Local MCP Server with Claude Desktop

If you are building an MCP server and want to test it locally with Claude Desktop, this guide will walk you through the complete process — from understanding MCP and creating your first tool to running a local server with stdio, connecting it to Claude Desktop, testing the tool, and troubleshooting common MCP server problems.

By the end of this tutorial, you will have a local MCP server exposing a get_weather tool that Claude Desktop can discover and call.

In Part 2, we will take the same MCP server and move it to a remote Streamable HTTP setup with OAuth-based authorization and other security considerations.

What You'll Build

In this tutorial, we will build a simple MCP server with a get_weather tool.

The final setup flow:

Claude Desktop
(MCP / stdio)
Local MCP Server
get_weather tool
Weather API

Once everything is connected, you will be able to ask Claude something like:

"What is the weather in Tokyo?"

Claude can then discover the get_weather tool and call your local MCP server.

What is MCP?

Model Context Protocol (MCP) is an open protocol that standardizes how AI applications communicate with external capabilities such as tools, resources, and other data sources.

Think of MCP like USB-C for AI. Before USB-C, different devices often needed different connectors. MCP provides a common protocol so an AI application and an MCP server can communicate without every integration requiring its own custom protocol.

Three Important MCP Primitives

MCP supports several capabilities, but three important primitives to understand when starting out are Tools, Resources, and Prompts.

Concept What it is
Tools Functions or actions the AI can invoke, such as send_email, get_weather, or create_expense.
Resources Data that an MCP server can expose, such as files, documents, or application data.
Prompts Reusable prompt templates that clients can use for specific tasks or workflows.

What Problem Does MCP Solve?

Imagine you have a toy robot.

This robot is very smart and can help you with many things. But to do real work, it needs to communicate with other systems — such as a database, weather service, file system, or online store. Each system may expose its own API and integration rules.

Without a standard protocol, every AI application may need a separate integration for every external service.

MCP solves this integration problem. It defines a common protocol that allows an MCP client and MCP server to communicate in a standardized way.

What is an MCP Tool?

A tool is a capability that an MCP server exposes for a client to invoke. In a typical application, the tool is backed by a function that performs an action, calls an API, queries a database, or performs some other operation.

Tool Name What it does
send_email Sends an email message
search_web Searches the web and returns results
get_weather Gets current weather for a city
create_note Saves a note in a database or application
delete_file Deletes a file from local or cloud storage

For this tutorial, we will use get_weather.

"What is the weather in Tokyo?"

Claude can then discover the get_weather tool and call your local MCP server.

Build Your First MCP Tool

Writing a tool is straightforward. Every tool needs a clear name and description, an input schema, and the code that handles the request.

  1. Name and Description: Helps the client understand what the tool does and when it may be useful.
  2. Input Schema: Defines the arguments the tool accepts. In this example, we use Zod.
  3. Handler Function: Contains the code that actually performs the operation.

Step 1 — Create the Tool File

Here is an example of a simple weather tool using the MCP TypeScript SDK and Zod.

// src/tools/get-weather.js import { z } from "zod"; export function registerGetWeatherTool(server, context) { server.registerTool( "get_weather", { title: "Get Weather", description: "Get current weather for any city. Use when the user asks about current weather or temperature.", inputSchema: { city: z.string().describe("The name of the city, for example 'London' or 'Tokyo'") } }, async ({ city }) => { // Use the API token from context const res = await fetch( `https://api.weather.com/v1/current?city=${encodeURIComponent(city)}`, { headers: { Authorization: `Bearer ${context.upstreamToken}` } } ); if (!res.ok) { throw new Error(`Weather API error: ${res.statusText}`); } const data = await res.json(); return { content: [ { type: "text", text: `Current weather in ${city}: ${data.condition}, ${data.temp}°C` } ] }; } ); }

Example API:

The weather API URL above is an example placeholder. Replace it with the endpoint and authentication method provided by the weather service you actually use.

You may also notice context.upstreamToken. In this example, context represents values supplied by your server application, such as an upstream API token. MCP does not automatically create this specific context object; its structure depends on how you build your server.

Step 2 — Register Your Tool in the Server

Next, register the tool inside your main server factory. This keeps the tool definition separate from the code that creates the MCP server.

// src/create-mcp-server.js import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerGetWeatherTool } from "./tools/get-weather.js"; export function createMcpServer(context) { const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }); registerGetWeatherTool(server, context); return server; }

SDK note: The code in this tutorial follows the TypeScript SDK API used by the example project. MCP SDK versions can change over time, so check the SDK documentation if your installed version uses different import paths or server setup APIs.

A Few Good Tool Design Rules

Rule Why it helps
One tool = one job Focused tools are easier for the AI client to understand and select.
Clear description A clear description helps the model understand when the tool should be used.
Validate inputs Invalid input can be rejected before it reaches your API or database.
Return useful errors Useful errors make local testing and debugging much easier.

MCP Transports: stdio vs Streamable HTTP

MCP uses transports to carry protocol messages between the client and server. For this tutorial, the important distinction is between stdio for local process integrations and Streamable HTTP for remote integrations.

Feature stdio Streamable HTTP
Typical use Local integrations Remote integrations
Process model Client launches the MCP server as a local process Client connects to an MCP endpoint over HTTP
Authentication Usually handled by the local process environment Application-dependent; HTTPS and appropriate authentication are commonly used for remote deployments

Key takeaway: We use stdio for this local tutorial because it avoids the need for HTTPS, ports, and authorization while learning MCP fundamentals.

Build the Local stdio MCP Server

A common architecture pattern is to keep your core MCP logic separate from the transport entry points.

File Purpose
src/create-mcp-server.js Creates the MCP server instance and registers tools and resources.
src/http-server.js Remote HTTP transport entry point.
src/stdio-server.js Local stdio entry point for a desktop client such as Claude Desktop.

The important part is that both entry points can create the same MCP server and register the same tools.

Test That the Server Process Starts

From the project directory, run your built entry point:

PS D:\MCP Server\MYMCPTEST> node build/index.js

An stdio server waits for protocol messages on standard input and writes protocol responses to standard output. It does not behave like a normal HTTP server with a port that you can open in a browser.

Important for stdio servers:

Standard output is used by the MCP protocol. Avoid writing ordinary logs with console.log() because they can corrupt the JSON-RPC stream. Send diagnostic logs to stderr, for example with console.error().

You do not need to start the process manually every time. Once Claude Desktop is configured, it can launch the local MCP server process for you.

Connect Your Local MCP Server to Claude Desktop

Claude Desktop can launch a local MCP server as a child process and communicate with it over stdio.

UI note: Claude Desktop's settings and configuration interface can change between versions. The screenshots in this tutorial reflect the interface used when this article was prepared.

Step 1 — Open Claude Desktop and Go to Settings

Open Claude Desktop, click the menu in the top left, and select Settings.

Step 2 — Open the Developer Settings

Inside Settings, open the Developer section.

Step 3 — Open the MCP Configuration

Open the option that lets you edit the MCP server configuration. Depending on your Claude Desktop version, the wording or location may differ.

Step 4 — Add Your Local MCP Server

Add your server under mcpServers. Use the absolute path to your built entry file.

{ "mcpServers": { "my-mcp-server": { "command": "node", "args": [ "D:/MCP Server/MYMCPTEST/build/index.js" ], "env": { "API_URL": "http://localhost:8080/api/v1", "USERNAME": "admin", "PASSWORD": "your_password" } } } }
  • command: The executable used to start the server.
  • args: The absolute path to your built MCP server entry file.
  • env: Environment variables passed to the server process.

Security warning:

Do not commit passwords, API keys, access tokens, or other secrets to your source repository. Use environment variables or a proper secret-management solution for real applications.

If you are using a local self-signed HTTPS service during development, you may encounter TLS certificate errors. Avoid disabling certificate verification unless you specifically need it for controlled local testing.

Development only:

Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables TLS certificate verification in Node.js. If you temporarily use it for a local self-signed certificate, remove it before using the application in production.

Step 5 — Restart Claude Desktop

After saving the configuration, completely close and reopen Claude Desktop so it reloads the MCP configuration.

  • Mac: Quit Claude Desktop completely.
  • Windows: Exit Claude Desktop completely from the system tray if necessary.

Step 6 — Verify Your Connected MCP Server

Open Claude Desktop and return to the Developer settings. Your configured MCP server should appear there.

Click your server to inspect the tools that the client has loaded.

Test Your MCP Tool

Once the server and its tools are visible, ask Claude to perform an action that should use your tool.

"What is the weather in Tokyo?"

The expected flow is:

Tool Execution Sequence Flow:

1. User Prompt: "What is the weather in Tokyo?"
2. Claude Desktop: Evaluates prompt & triggers get_weather
3. Local MCP Server: Executes handler with arguments { city: "Tokyo" }
4. Weather API: Fetches live data and returns JSON result
5. Claude Response: Formats data → "The weather in Tokyo is Sunny, 22°C."

Test Your MCP Server with MCP Inspector

If your MCP server is not working correctly, you do not always need Claude Desktop to find the problem. MCP Inspector provides a way to connect directly to an MCP server and inspect and call its tools.

This makes Inspector especially useful when you are developing and debugging a local MCP server.

Run MCP Inspector

From your project directory, you can launch Inspector with your local server command:

PS D:\MCP Server\MYMCPTEST> npx @modelcontextprotocol/inspector node build/index.js

Inspector launches your server and provides a local interface where you can connect to it and inspect its available capabilities.

Open the Inspector interface, connect to your server, and look for your registered get_weather tool.

Why use Inspector? It lets you test the MCP server independently from Claude Desktop. If the tool works in Inspector but not in Claude Desktop, the problem is more likely to be in the client configuration or host integration.

How to Debug Common MCP Server Problems

One of the most frustrating parts of developing an MCP server is knowing whether a problem is coming from your tool, the MCP server, the transport, or the client configuration.

The easiest approach is to debug one layer at a time.

MCP Architecture Layers (Debug from top to bottom):

1. Claude Desktop (Client UI)
2. MCP Client / Host (Process Launcher)
3. stdio Transport (stdin / stdout stream)
4. MCP Server (McpServer instance)
5. Tool Handler (Registered Tool Logic)
6. External API / Database (Upstream Service)

Problem 1 — The MCP Server Does Not Appear

If Claude Desktop does not show your server, start with the configuration rather than the tool itself.

  • Check that the JSON configuration is valid.
  • Check that command points to an executable available to the client.
  • Check that the path in args points to a real file.
  • Run the same command manually from your terminal.
  • Restart Claude Desktop after changing the configuration.

Problem 2 — The Server Appears but No Tools Are Visible

If the server starts but your tool does not appear, check whether the tool is actually registered.

registerGetWeatherTool(server, context);

If this registration code is never executed, the MCP server can start successfully while exposing no get_weather tool.

This is a good case for using MCP Inspector: connect directly to the server and check whether the tool is exposed there.

Problem 3 — The Tool Appears but Execution Fails

If the tool is visible but calling it produces an error, follow the request through the system:

Error Propagation Flow:

1. Claude (Sends tool call)
2. get_weather (Tool triggered)
3. MCP Server (Dispatches call)
4. Weather API (External request)
❌ Error Response: 401 Unauthorized / 404 Not Found / 500 Server Error

At this point, check the external API independently:

  • Is the API URL correct?
  • Is the API token valid?
  • Are the required environment variables available?
  • Is the city input valid?
  • Does the API response match the structure expected by your code?

Problem 4 — The Server Starts and Immediately Exits

Check for errors during startup:

  • Missing npm dependencies
  • Incorrect import paths
  • Invalid environment variables
  • Exceptions thrown while creating the MCP server
  • Incorrect stdio transport setup

Also remember that an stdio server is supposed to wait for messages from its client. A terminal that appears to be "doing nothing" may simply mean the server is waiting for an MCP client to communicate with it.

Problem 5 — Your Logs Break the MCP Connection

This is a common mistake when developing stdio servers.

Do not print ordinary debugging messages to stdout:

console.log("Server started"); // ❌ Incorrect for stdio (breaks protocol)

For an stdio MCP server, stdout carries protocol messages. Use stderr for diagnostic logging instead:

console.error("Server started"); // ✓ Correct for stdio (sends logs to stderr)

This keeps your diagnostic messages separate from the MCP protocol stream.

A Simple MCP Debugging Strategy

When something does not work, test the system from the bottom up instead of changing everything at once.

  1. Run the server: Confirm that the Node.js process starts.
  2. Check the transport: Make sure the server is using the expected stdio or HTTP transport.
  3. Use MCP Inspector: Test the server independently from Claude Desktop.
  4. Check tool registration: Confirm that your tool appears.
  5. Call the tool directly: Verify that the handler executes.
  6. Check external services: Verify API URLs, authentication, and responses.
  7. Connect Claude Desktop: Once the server works independently, troubleshoot the client configuration if necessary.

Best practice: If the server works in MCP Inspector but fails in Claude Desktop, avoid changing the tool code immediately. First inspect the Claude Desktop configuration and how the client launches the server.

Local MCP Server Checklist

Before moving to a remote deployment, make sure these pieces work:

  • ✓ MCP server starts successfully
  • ✓ stdio transport is configured correctly
  • ✓ stdout is reserved for MCP protocol messages
  • ✓ Diagnostic logs go to stderr
  • ✓ MCP Inspector can connect to the server
  • get_weather appears as a registered tool
  • ✓ Tool arguments pass schema validation
  • ✓ Tool execution succeeds
  • ✓ External API credentials work
  • ✓ Claude Desktop can launch the local server
  • ✓ Claude Desktop can discover and call the tool

What's Next?

You now have a local MCP server that can be tested independently with MCP Inspector and connected to Claude Desktop through stdio.

This local setup is a good starting point for learning MCP because you can build and test tools without first dealing with remote deployment, HTTPS, and authorization.

In Part 2, we will take this local MCP server and move it to a remote Streamable HTTP deployment. We will then look at authorization, OAuth-based authentication, PKCE, JWT validation, and the current MCP approach to client registration.

The exact authorization and client-registration flow can change as the MCP specification evolves, so Part 2 will follow the MCP specification and SDK version being used at the time of publication.

Coming next:

Deploying an MCP Server with Streamable HTTP and OAuth

If you have questions or run into problems while setting up your local MCP server, feel free to leave a comment below.

Post a Comment

0 Comments