You need Node 22.18 or later (it runs TypeScript files directly), Bun, or Deno.
Scaffold a project#
npx picomcp init my-server
cd my-server
npm installThis creates a ready-to-run server:
| File | Purpose |
|---|---|
server.ts | Your server definition: tools, resources, prompts |
main.ts | Starts the server over stdio |
server.test.ts | A test that calls a tool in-process |
Prefer to start from nothing? Install the two packages and write server.ts yourself:
npm install picomcp zodWrite a tool#
Open server.ts:
import { pico } from "picomcp";
import { z } from "zod";
export const server = pico({ name: "my-server", version: "0.1.0" })
.tool("greet", {
description: "Greets someone by name.",
input: z.object({ name: z.string().describe("Who to greet") }),
run: ({ name }) => `Hello, ${name}!`,
});descriptionis what the model reads to decide when to call the tool. Write it for the model.inputis any Standard Schema.nameis typed asstringinsiderun.runreturns a string here. It can also return objects, content blocks or throw: see Tools.
Test it#
import assert from "node:assert/strict";
import { test } from "node:test";
import { server } from "./server.ts";
test("greet says hello", async () => {
const result = await server.call("greet", { name: "Ada" });
assert.deepEqual(result.content, [{ type: "text", text: "Hello, Ada!" }]);
});npm testserver.call is type-checked: a typo in the tool name or a missing argument fails to compile.
Connect it#
For Claude Code:
claude mcp add my-server -- node /absolute/path/to/my-server/main.tsThen ask Claude to greet someone. For Claude Desktop, Cursor, VS Code and others, see Connect a client.
Inspect it#
The MCP Inspector lets you call tools by hand and see the raw messages:
npx @modelcontextprotocol/inspector node main.ts