learn-mcp-by-building
This project implements the Model Context Protocol (MCP) for building AI tools, providing a modular framework for creating MCP-compatible servers and clients.
Model Context Protocol (MCP) Implementation
This project implements the Model Context Protocol (MCP) for building AI tools. It provides a modular framework that can be used to create MCP-compatible servers and clients.
๐ What is Model Context Protocol (MCP)?
The Model Context Protocol (MCP) is an open protocol that enables AI assistants to interact with external tools and data sources.
- List available tools and their capabilities
- Call tools with parameters
- Handle errors in a consistent way
- Process tool results in a standardized format
๐ For a detailed overview, see MCP Notes.
โจ Features
| Category | Features | |----------|----------| | Core | โ JSON-RPC 2.0 message handlingโ Protocol initializationโ Capability negotiation | | Tools | โ Tool registration with JSON Schemaโ Tool invocation and validationโ Standardized error handling | | Transport | โ STDIO supportโ HTTP+SSE Support | | Testing | โ Test clients |
๐ Project Structure
src/
โโโ core/ # Core MCP server implementation
โโโ transports/ # Transport layer implementations (stdio, HTTP+SSE)
โโโ tools/ # Tool definitions and handlers
โโโ examples/ # Example servers and clients
โ โโโ public/ # Static files for HTTP server
โโโ index.js # Main entry point for the library
๐ Getting Started
Prerequisites
- Node.js 20.x or later
- npm or pnpm
Installation
# Clone the repository
git clone https://github.com/AshikNesin/learn-mcp-by-building
cd learn-mcp-by-building
# Install dependencies
npm install
# or
pnpm install
๐โโ๏ธ Running the Examples
STDIO Server and Client
Run the STDIO server:
npm run server:stdio
# or
node src/examples/stdio-server.js
Test with the STDIO client:
npm run client:stdio
# or
node src/examples/stdio-client.js
Run both together to see a complete test:
npm run test:stdio
# or
node src/examples/stdio-client.js | node src/examples/stdio-server.js
HTTP+SSE Server and Client
Run the HTTP+SSE server:
npm run server:sse
# or
node src/examples/http-sse-server.js --port 5000
Available options:
--port: Port to listen on (default: 5000)--host: Host to bind to (default: localhost)--path: Endpoint path (default: /sse)--cors: Enable CORS (default: true)--serve-static: Serve static files from src/examples/public (default: true)
Test with the HTTP+SSE client:
npm run client:sse
# or
node src/examples/http-sse-client.js --server http://localhost:5000/sse
Once running, you can also access the web-based client interface in your browser at http://localhost:5000:

The interface provides a user-friendly way to interact with the MCP server, with a side-by-side layout showing the calculator controls and real-time logs.
๐ Using the MCP Inspector
You can use the official MCP Inspector to debug the server:
npm run debug
The MCP Inspector provides a visual interface for monitoring and debugging MCP servers:

๐งฎ Calculator Tool
Operations
- โ add
- โ subtract
- โ๏ธ multiply
- โ divide
Parameters
operation- Operation typea- First operandb- Second operand
Error Handling
- Division by zero
- Invalid operations
- Type validation
- Missing parameters
๐งโ๐ป Developing with the MCP Framework
Creating a New Server
import { McpServer } from '../core/index.js';
import { StdioTransport } from '../transports/index.js';
import { calculatorToolDefinition, handleCalculatorTool } from '../tools/index.js';
// Create server instance
const server = new McpServer(
{ name: 'my-server', version: '1.0.0' },
{ capabilities: { tools: { listChanged: true } } }
);
// Register tool handlers
server.setRequestHandler('tools/list', () => ({ tools: [calculatorToolDefinition] }));
server.setRequestHandler('tools/call', async (params) => {
if (params.name === 'calculator') {
return handleCalculatorTool(params.arguments);
}
throw new Error(`Tool ${params.name} not found`);
});
// Start the server
const transport = new StdioTransport();
server.connect(transport)
.then(() => console.error('Server ready!'));
Creating a New Tool
- Create a new file in
src/tools/:
// src/tools/my-tool.js
export const myToolDefinition = {
name: 'my-tool',
description: 'Description of my tool',
inputSchema: {
type: 'object',
properties: {
// Define parameters
},
required: []
}
};
export async function handleMyTool(args) {
// Implement tool logic
return {
content: [
{
type: 'text',
text: 'Result from my tool'
}
]
};
}
- Export the tool in
src/tools/index.js:
export * from './my-tool.js';
๐ ๏ธ Protocol Features
- โ Capability negotiation
- โ Tool list change notifications
- โ Standardized error handling
- โ JSON Schema validation
- โ Structured tool results
- โ Transport layer abstraction