Building a custom MCP server is the fastest way to expose your internal APIs to Claude or Cursor. This walkthrough uses the official TypeScript SDK and STDIO transport.
Register tools with clear names and JSON Schema so agents call them reliably.
Architecture in one minute
- Client spawns your process.
- Communication is JSON-RPC over STDIO.
- You advertise tools via
ListTools, handleCallTool.
Critical rule: never pollute stdout
console.log on stdout breaks the protocol. Use stderr for debug logs.
Minimal Node server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "system-status-checker", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_disk_space",
description: "Report approximate free disk capacity.",
inputSchema: { type: "object", properties: {}, required: [] },
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_disk_space") {
console.error("Running disk check...");
return {
content: [{ type: "text", text: "Disk status: ok (example)" }],
};
}
throw new Error("Unknown tool");
});
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server online");
Keep the server package small; one responsibility per tool works best for agents.