
Large-language-model apps don’t need a thousand bespoke integrations—they need one great port. That’s the promise of the Model Context Protocol (MCP): a standardized way to expose tools and data to AI clients securely. In this guide, you’ll build a minimal MCP server, wire up a couple of useful tools, and connect it to an MCP-compatible client—so your AI can actually do things, not just talk about them. (MCP is an open standard with official SDKs in Node and Python, using JSON-RPC over stdio or HTTP transports.)
What You’ll Learn
What MCP is and how it fits into modern AI apps (clients, servers, transports)
How to build a working MCP server in Node.js with two tools and one resource modelcontextprotocol.io
How to connect your server to a desktop client for local testing (e.g., Claude Desktop) modelcontextprotocol.io
What is MCP?
MCP is an open protocol that standardizes how applications provide context and capabilities to LLMs—think “USB-C for AI”: one protocol, many interchangeable tools and data sources. Servers expose tools (actions) and resources (read-only context) to clients over JSON-RPC, typically via stdio (local) or streamable HTTP (remote). OpenAI GitHub+2modelcontextprotocol.io+2
What You’ll Build / Use
- Language/Framework: Node.js (TypeScript optional)
- Protocol/Transport: MCP over stdio (local) modelcontextprotocol.io
- Storage: Plain JSON file for demo
- Optional Tools: Docker, Postman/curl (for HTTP variant later)
Project Structure
mcp-hello-server/
├─ package.json
├─ src/
│ ├─ server.ts
│ └─ data.json
└─ tsconfig.json (optional if you use TS)
Step-by-Step Guide
Step 1: Initialize the project
mkdir mcp-hello-server && cd mcp-hello-server
npm init -y
npm i @modelcontextprotocol/sdk
# Optional TypeScript
npm i -D typescript ts-node @types/node
npx tsc --init
We’ll use the official Node SDK to implement the MCP server. npm
Step 2: Create a tiny dataset
src/data.json
{ "motto": "Ship useful tools to your AI, safely." }
Step 3: Implement the server (stdio transport)
src/server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { Tool, Resource } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs";
const server = new Server(
{
name: "mcp-hello-server",
version: "0.1.0",
// advertise tools and resources
tools: [
{
name: "say_hello",
description: "Greets a user by name",
inputSchema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"]
}
} as Tool,
{
name: "summarize_text",
description: "Summarize given text to a few bullet points",
inputSchema: {
type: "object",
properties: { text: { type: "string" }, bullets: { type: "integer", minimum: 1, maximum: 10 } },
required: ["text"]
}
} as Tool
],
resources: [
{
uri: "file://data.json",
name: "Local Demo Data",
description: "Small JSON with a motto",
mimeType: "application/json"
} as Resource
]
},
{
// Tool implementations
callTool: async ({ name, args }) => {
if (name === "say_hello") {
const person = String(args?.name ?? "friend");
return { content: [{ type: "text", text: `Hello, ${person}!` }] };
}
if (name === "summarize_text") {
const text = String(args?.text ?? "");
const bullets = Number(args?.bullets ?? 3);
const points = text
.split(/[.!?]\s+/)
.filter(Boolean)
.slice(0, bullets)
.map(s => `• ${s.trim()}`);
return { content: [{ type: "text", text: points.join("\n") || "• (no content)" }] };
}
// Unknown tool → protocol error handled by the SDK/JSON-RPC
throw new Error(`Unknown tool: ${name}`);
},
// Resource retrieval
readResource: async ({ uri }) => {
if (uri === "file://data.json") {
const body = fs.readFileSync(new URL("./data.json", import.meta.url)).toString("utf-8");
return { contents: [{ uri, mimeType: "application/json", text: body }] };
}
throw new Error(`Unknown resource: ${uri}`);
}
}
);
// Bridge via stdio (the common local transport for MCP)
const transport = new StdioServerTransport();
server.connect(transport);
This uses the SDK’s server primitives and stdio transport, which is recommended for local clients and development. npm+1
Step 4: Add scripts and build
package.json (scripts section)
{
"type": "module",
"scripts": {
"dev": "ts-node src/server.ts",
"start": "node dist/server.js",
"build": "tsc"
}
}
Step 5: Run it locally
npm run dev
# (The process now speaks MCP over stdin/stdout.)
Step 6: Connect from an MCP client
Many tools can host MCP servers locally. A common path is to register your server in Claude Desktop (or another MCP-compatible host) and let the client discover your tools/resources automatically. Follow the client/host’s instructions for adding a local stdio server, then test tools/list and tools/call (e.g., call say_hello with { "name": "Bala" }). modelcontextprotocol.io
Under the hood, the client and server exchange JSON-RPC messages. Your server must list tools and resolve tools/call requests correctly; the SDK handles framing and error types aligned with the spec. modelcontextprotocol.io+2modelcontextprotocol.io+2
Testing the Setup
From your MCP host/client:
- List tools: confirm
say_helloandsummarize_textappear. - Call a tool: run
say_hellowith{ "name": "World" }→ expect a friendly greeting. - Read resource: request
file://data.json→ expect your motto JSON.
For deeper testing, the “Build an MCP server” tutorial outlines end-to-end validation against a desktop host. modelcontextprotocol.io
Optional Features to Add
- Auth & policy: if you expose network APIs, gate them behind API keys/allow-lists.
- HTTP transport: switch to the streamable HTTP transport for remote hosting. modelcontextprotocol.io
- Observability: structured logs + metrics around
tools/calllatency, error rates. - Resources at scale: expose database schemas, file trees, or domain docs via resource URIs. modelcontextprotocol.io
Security Considerations
- Principle of least privilege: do not ship tools that can read arbitrary files or call arbitrary shells.
- Validate inputs and sanitize parameters; return protocol vs. execution errors appropriately. modelcontextprotocol.io
- Supply-chain hygiene: pin/verify your server dependencies and review what permissions they imply. Recent reports show the risk of malicious MCP servers masquerading as legitimate packages—treat servers like any other sensitive connector (rotate creds, review configs).
Final Thoughts
You now have a working MCP server that exposes tools and resources over stdio and connects to a real client. From here, wrap your existing services (Jira, Git, databases), model safe policies, and grow a consistent, testable interface your AI apps can trust—without rewriting integrations for every new host.
Don’t miss the next tutorial: Subscribe to SecureBytesBlog now.


