node-mcp-poc
A minimal Model Context Protocol server in Node.js/TypeScript. It runs over HTTP (Express + Streamable HTTP) by default, with stdio available as a fallback, and is attachable to Claude Code.
It exposes four demo tools:
| Tool | Input | Returns | | ----------- | --------------------------------- | ----------------------------------- | | add | { a: number, b: number } | the sum, e.g. 2 + 3 = 5 | | echo | { text: string } | the same text | | now | { timezone?: string } | current time (ISO + localized) | | fetch_url | { url: string, maxChars?: int } | HTTP GET body (JSON pretty-printed) |
Build
Requires Node.js 24 (see .nvmrc) and pnpm. Builds and tests run on Vite / Vitest.
pnpm install
pnpm build # vite build -> build/index.js (server) + build/lambda.js (Lambda)
Run
The server reads its config from the environment (a .env file is loaded automatically — see .env.example):
| Variable | Default | Purpose | | --------------- | ------- | -------------------------------------------------- | | MCP_TRANSPORT | http | http (Express) or stdio | | PORT | 3219 | HTTP port; endpoint is http://localhost:PORT/mcp | | LOG_LEVEL | info | pino log level | | NODE_ENV | — | development pretty-prints HTTP logs |
pnpm start # HTTP server on http://localhost:3219/mcp
pnpm start:stdio # stdio transport instead
Attach to Claude Code
This repo ships a project-level .mcp.json pointing at the HTTP endpoint, so within this directory the server is picked up automatically once it's running:
{
"mcpServers": {
"node-poc": { "type": "http", "url": "http://localhost:3219/mcp" },
},
}
Start the server (pnpm start), then start (or restart) a Claude Code session and try:
- "add 2 and 3"
- "echo hello"
- "what time is it in America/New_York"
- "fetch https://api.github.com/zen via node-poc"
Check it's connected with claude mcp list.
To register it from anywhere instead of relying on .mcp.json:
# HTTP (server must already be running)
claude mcp add --transport http node-poc http://localhost:3219/mcp
# or stdio (Claude Code launches the process for you)
claude mcp add node-poc -- node /ABSOLUTE/PATH/TO/node-mcp-poc/build/index.js
Remove it with:
claude mcp remove node-poc
Develop / debug
pnpm dev # rebuild + node --watch
pnpm test # vitest run
pnpm test:watch # vitest (watch mode)
pnpm typecheck # tsc --noEmit
pnpm smoke # build + stdio client smoke test (scripts/smoke.mjs)
pnpm smoke:http # HTTP client smoke test (scripts/smoke-http.mjs)
pnpm logs # tail logs/server.log through pino-pretty
pnpm inspect # open the MCP Inspector against the server
Deploy
Deploying to AWS? See docs/README.md.
- Lambda (SAM CLI) —
src/lambda.tsis a stateless variant of the server;
template.yaml deploys it on nodejs24.x behind a Function URL. pnpm sam:build && sam deploy --guided.
- Container (App Runner / ECS Fargate) — the prime target for the stateful
src/index.ts server (in-memory sessions + SSE), which Lambda can't host.
Notes
- The tools are defined once in
src/mcp.ts(createMcpServer) and shared by
both entry points: src/index.ts (long-running, stateful HTTP/stdio) and src/lambda.ts (stateless, for Lambda).
- In HTTP mode each MCP session gets its own server instance, keyed by the
Mcp-Session-Id header; logs go to stdout (pretty-printed when NODE_ENV=development).
- In stdio mode stdout is reserved for the JSON-RPC wire, so logs go to
stderr and logs/server.log (view with pnpm logs).
- Add new tools in
src/mcp.tsviaserver.registerTool(...).











