Introduction
The Model Context Protocol (MCP) allows you to build servers that expose tools, resources, and prompts to LLM clients like Claude Desktop. In this lesson, you will install the official TypeScript SDK, create your first MCP server, and connect it to Claude Desktop.
Key Concepts
- MCP Server: A process that exposes capabilities (tools, resources, prompts) to an MCP client via the Model Context Protocol.
- McpServer class: The main entry point in the
@modelcontextprotocol/sdkpackage for building servers. - Transport: The communication layer that carries MCP messages between server and client.
- StdioServerTransport: A transport that uses standard input/output, ideal for local server processes managed by a client like Claude Desktop.
Real World Context
When you build an MCP server, you are creating a bridge between an AI assistant and your own tools and data. For example, a DevOps team might build an MCP server that exposes deployment tools, letting Claude trigger deployments or check service status through natural conversation.
Deep Dive
Start by initializing a new Node.js project and installing the required dependencies.
The following command sets up a new project with the MCP SDK and Zod for input validation:
bashmkdir my-mcp-server && cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk zod
This creates a minimal project with the two packages you need: the MCP server SDK and Zod for schema definitions.
Now create the server entry point. The following code initializes an MCP server with metadata and connects it to a stdio transport:
typescriptimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new McpServer({ name: 'my-server', version: '1.0.0' }); const transport = new StdioServerTransport(); await server.connect(transport);
The McpServer constructor takes a server info object with name and version. These fields identify your server to clients during the initialization handshake. The StdioServerTransport reads from stdin and writes to stdout, which is the standard way Claude Desktop communicates with local MCP servers.
To connect this server to Claude Desktop, you need to edit the Claude Desktop configuration file. On macOS this lives at ~/Library/Application Support/Claude/claude_desktop_config.json, and on Windows at %APPDATA%\Claude\claude_desktop_config.json.
Here is an example configuration that tells Claude Desktop how to launch your server:
json{ "mcpServers": { "my-server": { "command": "node", "args": ["path/to/server.js"] } } }
Each entry under mcpServers defines a server by name. The command field is the executable to run, and args passes arguments to it. Claude Desktop will spawn this process and communicate with it over stdio.
Common Pitfalls
- Logging to stdout: Never use
console.log()in an MCP server using stdio transport. Stdout is reserved for MCP protocol messages. Useconsole.error()for debug output instead. - Missing await on connect: The
server.connect(transport)call is async. Forgetting to await it can cause the server to exit before it starts listening. - Wrong path in config: The
argspath inclaude_desktop_config.jsonmust point to the compiled JavaScript file, not the TypeScript source.
Best Practices
- Always specify a semantic version string in your server metadata so clients can track compatibility.
- Use TypeScript with strict mode for type safety across your server implementation.
- Keep your server entry point minimal and import handler logic from separate modules.
Summary
You learned how to install the @modelcontextprotocol/sdk package, create an McpServer instance with name and version metadata, connect it to a StdioServerTransport, and configure Claude Desktop to launch your server. This foundation supports everything you will build in the following lessons.
Code Examples
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new McpServer({
name: 'my-server',
version: '1.0.0'
});
const transport = new StdioServerTransport();
await server.connect(transport);{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["path/to/server.js"]
}
}
}