A minimal, well-documented Model Context Protocol (MCP) server template. Use this as a starting point to build your own MCP tools.
The Model Context Protocol is an open standard that lets AI assistants (like Claude or Cursor) use external tools. Think of it like giving Claude hands to interact with the world:
┌─────────────┐ MCP Protocol ┌─────────────┐
│ AI │ ◄──────────────────► │ Your Server │
│ (Client) │ JSON-RPC over │ (Tools) │
└─────────────┘ stdio └─────────────┘
Key concepts:
- Server: Your code that exposes tools (this repo)
- Client: The AI assistant that calls your tools (Cursor, Claude Code, etc.)
- Tools: Functions the AI can invoke with structured inputs/outputs
- Transport: How client and server communicate (usually stdio)
git clone https://github.com/fellanH/klar-mcp.git
cd klar-mcp
npm installnpm run buildAdd this server to your MCP client configuration:
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"klar-mcp": {
"command": "node",
"args": ["/absolute/path/to/klar-mcp/dist/index.js"]
}
}
}Claude Code
Run:
claude mcp add klar-mcp node /absolute/path/to/klar-mcp/dist/index.jsCursor
- Open Cursor Settings (
Cmd+,on macOS,Ctrl+,on Windows/Linux) - Search for "MCP" or navigate to Features > MCP Servers
- Click Add new MCP server
- Enter:
- Name:
klar-mcp - Type:
command - Command:
node /absolute/path/to/klar-mcp/dist/index.js
- Name:
Or edit ~/.cursor/mcp.json directly:
{
"mcpServers": {
"klar-mcp": {
"command": "node",
"args": ["/absolute/path/to/klar-mcp/dist/index.js"]
}
}
}Restart your client (Claude Desktop, Claude Code, or Cursor). You should now be able to ask the AI to use your tools:
"Use the hello_world tool to greet me"
klar-mcp/
├── src/
│ ├── index.ts # Server entry point - handles MCP protocol
│ ├── types.ts # Shared TypeScript types
│ └── tools/
│ ├── index.ts # Tool registry - exports all tools
│ ├── hello-world.ts # Example: simplest possible tool
│ ├── timestamp.ts # Example: tool with enum options
│ ├── uuid.ts # Example: tool with optional params
│ └── ... # More example tools
├── dist/ # Compiled JavaScript (generated)
├── package.json
└── tsconfig.json
The server does three things:
- Creates an MCP server with a name and version
- Registers handlers for listing and calling tools
- Connects via stdio so clients can communicate with it
// Simplified version of src/index.ts
const server = new Server({ name: "klar-mcp", version: "1.0.0" }, {
capabilities: { tools: {} }
});
// When client asks "what tools do you have?"
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: definitions // Return all tool definitions
}));
// When client says "call this tool with these arguments"
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const handler = handlers.get(request.params.name);
return handler(request.params.arguments);
});
// Start listening on stdin/stdout
const transport = new StdioServerTransport();
await server.connect(transport);Each tool has two parts:
- Definition: Name, description, and input schema (what the AI sees)
- Handler: The actual code that runs (what you implement)
export const helloWorld: ToolModule = {
definition: {
name: "hello_world",
description: "A simple greeting tool",
inputSchema: {
type: "object",
properties: {
name: { type: "string", description: "Name to greet" }
}
}
},
handler: async (args) => {
const name = args?.name || "World";
return {
content: [{ type: "text", text: `Hello, ${name}!` }]
};
}
};All tools are collected into two exports:
definitions: Array of tool schemas (forListTools)handlers: Map of name → handler function (forCallTool)
See docs/ADDING-TOOLS.md for a step-by-step guide.
Quick version:
- Create
src/tools/my-tool.ts:
import { ToolModule } from "../types.js";
export const myTool: ToolModule = {
definition: {
name: "my_tool",
description: "What this tool does",
inputSchema: {
type: "object",
properties: {
input: { type: "string", description: "The input" }
},
required: ["input"]
}
},
handler: async (args) => {
// Your logic here
return {
content: [{ type: "text", text: `Result: ${args.input}` }]
};
}
};- Register in
src/tools/index.ts:
import { myTool } from "./my-tool.js";
// ... add to tools array- Rebuild:
npm run build
| Tool | Description | Demonstrates |
|---|---|---|
hello_world |
Simple greeting | Minimal tool structure |
timestamp |
Get current time | Enum parameters |
uuid |
Generate UUIDs | Optional params with defaults |
base64 |
Encode/decode | Required params, error handling |
hash |
Generate hashes | Multiple algorithms |
json_format |
Format/validate JSON | Complex input validation |
env_info |
Environment details | System interaction |
text_stats |
Text analysis | Object response |
regex_test |
Test regex patterns | Pattern matching |
random_string |
Generate strings | Character sets |
shell_command |
Run shell commands | Subprocess execution |
# Build TypeScript
npm run build
# Build and run
npm run dev
# Run tests (if you add them)
npm testMIT
Go forth and build! You have my full permission to tweak, share, edit this project as you see fit. All I ask is that if you create something cool, you share it with me. I’m always excited to see what you create.