Building Your First Model Context Protocol Server
The Model Context Protocol acts as an intermediary that standardizes the way different tools and services interact with language models.
Apr 4th, 2025 6:13am by
Photo by Adi Goldstein on Unsplash.
Stream sponsored this post.
How Does MCP Solve the Problem?
The Model Context Protocol (MCP) addresses these challenges by providing a unified communication layer between LLMs and external services. At its core, MCP is an intermediary that standardizes the way different tools and services interact with language models. MCP’s key innovation is its ability to translate between different tool specifications and APIs. Rather than requiring each tool to adapt to a specific LLM’s requirements, MCP handles the translation layer, ensuring smooth communication between all components. This standardization eliminates the need for custom integrations and reduces implementation complexity. The MCP architecture consists of three main components:- MCP Client: Applications like Cursor or Windsurf, but also LLM providers that interface with the protocol.
- MCP Server: The core component that handles protocol translation and capability management, maintained by service providers (GitHub, Figma and others).
- Services: The actual tools and functionalities that the MCP server connects to.
- Resources: These are file-like data structures that clients can access, such as file contents or API responses.
- Tools: Function calls that LLMs can execute (with user approval), enabling interaction with external services.
- Prompts: Prewritten templates that guide users or LLMs in accomplishing specific tasks.
Let’s Build a Simple MCP Server
To demonstrate the practical implementation of an MCP server, we’ll create a basic example using Node.js with TypeScript. While official SDKs are available for Python, Java, Kotlin and C#, we’ve chosen Node.js for its widespread adoption and excellent TypeScript support. We’ll build a server for this tutorial that integrates with Stream’s services. Though we’ll be working with Stream-specific API calls, the core principles and patterns we’ll cover apply to any MCP server implementation. Our server will expose two essential tools:- `create-user`: A tool for user creation
- `get-token`: A tool for JWT generation
Project Setup
Let’s start by setting up our development environment. We’ll create a new Node.js project with TypeScript support and install all necessary dependencies to build our MCP server. First, create a new directory for your project and initialize it with npm:
mkdir sample-server && cd sample-server
npm init -y
npm install @modelcontextprotocol/sdk zod getstream
npm install -D @types/node typescript
mkdir src
touch src/index.ts
{
"type": "module",
"bin": {
"sample-server": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
],
}
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Setting Up the Basic Server
Now that we have our project structure in place, let’s create the foundation for our MCP server. We’ll use the official MCP SDK, which provides all the necessary building blocks for our implementation. First, let’s look at the basic server setup. We’ll import the required dependencies and create our server instance:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { StreamChat } from 'stream-chat';
// Create server instance
const server = new McpServer({
name: "sample-server",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Sample MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error in main():', error);
process.exit(1);
});
Adding the Tools to the Server
With our server foundation in place, it’s time to add the tools that will give our MCP server its functionality. In MCP, tools are functions that clients can call to perform specific actions. Let’s explore how to register these tools with our server. The MCP SDK provides a straightforward way to register tools using the `server.tool` method. This method requires four key components:- The tool name — A unique identifier for the tool.
- A description — Clear documentation of what the tool does.
- Input schema — Using Zod for type-safe input validation.
- Execution function — The actual implementation that processes the request.
server.tool(
'create-user',
'Create a new user on the Stream backend',
{
id: z.string().describe('The id of the user to create'),
username: z.string().describe('The username of the user to create'),
email: z
.string()
.email()
.optional()
.describe('The email of the user to create'),
},
async ({ id, username, email }) => {
const serverClient = StreamChat.getInstance(
STREAM_API_KEY,
STREAM_API_SECRET
);
const user = await serverClient.upsertUser({
id,
username,
email,
});
return {
content: [
{
type: 'text',
text: `User ${username} created successfully`,
},
],
};
}
);
server.tool(
'generate-token',
'Generate a token for a user',
{
userId: z.string().describe('The id of the user to generate a token for'),
},
async ({ userId }) => {
const serverClient = StreamChat.getInstance(
process.env.STREAM_API_KEY!,
process.env.STREAM_API_SECRET!
);
const token = serverClient.createToken(userId);
return {
content: [{ type: 'text', text: `Token: ${token}` }],
};
}
);
Adding Your Server to a Client
Now that our MCP server implementation is ready, let’s integrate it with a client application. We’ll use Cursor as our example client, though the process is similar for other MCP-compatible clients. First, we need to build our server. Thanks to our earlier configuration of the build command, this is as simple as running:npmrunbuild
Once the build is complete, follow these steps to integrate your server with Cursor:
- Open Cursor settings and navigate to the MCP section in the menu.
- Look for and click the “Add new global MCP server” option (Note: You can also configure this on a per-project basis if preferred).
- Locate your built server file and copy its absolute path.
{
"mcpServers": {
"stream-server": {
"command": "node",
"args": [
We "</path/to/server>/build/index.js"
]
}
}
}
Summary
This hands-on example showcased how MCP simplifies the process of building tool-augmented AI applications while maintaining type safety and providing a great developer experience. We used Stream’s backend SDK, but the logic can be adapted to any other code you want to deliver. Let us know what MCP server you will build!
YOUTUBE.COM/THENEWSTACK
Tech moves fast, don't miss an episode. Subscribe to our YouTube
channel to stream all our podcasts, interviews, demos, and more.