node-mcp-poc
by gurunate
README.md
# node-mcp-poc
A minimal [Model Context Protocol](https://modelcontextprotocol.io) 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](https://vite.dev) / [Vitest](https://vitest.dev).
```bash
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 |
```bash
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:
```jsonc
{
"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`:
```bash
# 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:
```bash
claude mcp remove node-poc
```
## Develop / debug
```bash
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](docs/README.md)**.
- **Lambda (SAM CLI)** — `src/lambda.ts` is 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.ts` via `server.registerTool(...)`.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues