loading…
Search for a command to run...
loading…
A minimal learning-focused MCP server that demonstrates core primitives like tools and resources through simple greeting functions. It provides a foundational e
A minimal learning-focused MCP server that demonstrates core primitives like tools and resources through simple greeting functions. It provides a foundational example for connecting AI models to external data using both Streamable HTTP and stdio transports.
A minimal MCP server built as a learning exercise. Uses the Streamable HTTP transport and demonstrates the core MCP primitives: tools and resources.
The Model Context Protocol (MCP) is an open standard for connecting AI models to external data and capabilities. An MCP server exposes three types of primitives:
| Primitive | Purpose | Initiated by |
|---|---|---|
| Tools | Do things (side effects allowed) | The model |
| Resources | Expose data (read-only) | The client/user |
| Prompts | Reusable prompt templates | The client/user |
src/
server.js # Entry point — HTTP transport
stdio.js # Entry point — stdio transport (Claude Desktop)
mcp.js # MCP server + tool/resource registration
data/
greetings.js # Shared data (language → greeting mapping)
tools/
helloWorld.js # No-input tool
helloName.js # Tool with a required string input
greetName.js # Tool with error handling and MCP logging
listLanguages.js # Tool that exposes the languages list to the model
resources/
languages.js # Static resource exposing available languages
This server uses the StreamableHTTPServerTransport from @modelcontextprotocol/sdk. It runs as a plain Node.js HTTP server and handles MCP messages at POST /mcp.
Key details:
sessionIdGenerator: undefined) — each request gets a fresh transport instanceAccept: application/json, text/event-stream or the server will reject with 406Tools are registered with server.registerTool(name, config, handler).
config.description — shown to the model so it knows when to use the toolconfig.inputSchema — a plain object of Zod fields; the SDK wraps it in z.object() automatically-32602 without ever reaching your codeNo inputs. Always returns "Hello, World!".
Takes a required name string. Returns "Hello, {name}!".
Takes a name string and a language string. Returns a greeting in the specified language, or an isError response if the language isn't supported. Uses sendLoggingMessage to emit info, warning, and debug log messages through the MCP protocol — visible in the MCP Inspector notifications panel.
No inputs. Returns the list of supported languages. Useful for Claude Desktop where resources aren't surfaced to the model — the model can call this tool to discover valid options when greet_name returns an error.
Resources are registered with server.registerResource(name, uri, config, reader).
languages://list) that the client uses to fetch it{ contents: [{ uri, text }] }Returns a JSON array of supported language keys. Backed by the same greetings.js data used by the greet_name tool.
npm install
npm start
The MCP endpoint will be available at http://localhost:3000/mcp.
To run on a different port:
PORT=3001 npm start
Claude Desktop spawns the process itself — no server needs to be running beforehand. Add the following to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"hello-mcp": {
"command": "node",
"args": ["/absolute/path/to/hello-mcp/src/stdio.js"]
}
}
}
Quit and relaunch Claude Desktop after editing the config. Check Claude menu → Settings → Developer to confirm the server shows a green dot.
npx @modelcontextprotocol/inspector
Open the Inspector in Chrome (not Brave — its privacy settings block localhost requests), set the transport to Streamable HTTP, and enter http://localhost:3000/mcp.
You can also test with raw curl:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}'
mcp.js is transport-agnostic; server.js (HTTP) and stdio.js (stdio) are separate entry points that both import the same server instanceserver.js owns the HTTP transport, mcp.js owns the MCP server, each tool/resource lives in its own filesrc/data/ is imported by both tools and resources; no duplicationinputSchema gives you automatic input validation before your handler runsregisterTool over tool — the tool() method is deprecated; registerTool() uses a cleaner config object patternsessionIdGeneratorserver.sendLoggingMessage() can't import server directly (circular dependency). Instead, export a createHandler(server) factory that closes over the server instance and returns the handler function. mcp.js calls createHandler(server) at registration time.isError: true vs throwing — returning isError: true sends the error text back to the model as readable tool output it can reason about; throwing produces a protocol-level JSON-RPC error the model can't seeserver.sendLoggingMessage({ level, message }) sends log messages through the MCP protocol to the client; visible in MCP Inspector's notifications panelДобавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"hello-mcp": {
"command": "npx",
"args": []
}
}
}