🔌 Your First MCP Server — in a Language, Not an SDK
Expose a plain function to Claude, Cursor, or any MCP client — in four lines, with zero dependencies.
> Part of the *Pipe in 30 Lines* series: RAG without a vector DB · Self-healing code · Parallel LLM calls
Building an MCP server usually means picking an SDK, wiring up JSON-RPC plumbing, and shipping a runtime with dependencies. Pipe is the first language with built-in MCP — ai_tool registers a function, mcp_server + mcp_serve_stdio start the server.
fn greet name
"Hello, " ++ name ++ "! Pipe speaks MCP natively."
ai_tool "greet" "Greet a person by name" {name: "Person's name"} greet
mcp_server "Pipe Greeting Server" "1.0.0"
mcp_serve_stdio
Point Claude Desktop (or Cursor, or any MCP client) at it:
{
"mcpServers": {
"pipe-greet": {
"command": "/path/to/pipe",
"args": ["examples/blog_mcp_server.pipe"]
}
}
}
What happens here:
ai_toolturns any Pipe function into a schema'd tool — name, description, and an argument map are all it needs.mcp_server+mcp_serve_stdiostart the server and serve the tool over stdio.- The same binary that *serves* MCP can also *consume* it:
mcp_use_stdioconnects to any MCP server off npm/uvx, so your LLM gets GitHub, filesystem, or database tools in the same pipeline.
There's no SDK, no package.json, no build step. A single ~7 MB binary is both server and client — the tool you expose to Claude is the same language you write your pipeline in. That's the whole idea behind the MCP Cell: when MCP is a language primitive, the sandbox can wrap the server, the client, and the tools.
Run it yourself: pipe examples/blog_mcp_server.pipe (it starts a stdio server — hit it with initialize, tools/list, then tools/call with {"name":"greet","arguments":{"name":"Harry"}}).
🔌 Dein erster MCP-Server — in einer Sprache, nicht in einem SDK
Eine normale Funktion für Claude, Cursor oder jeden MCP-Client bereitstellen — in vier Zeilen, mit null Abhängigkeiten.
> Teil der Serie *Pipe in 30 Lines*: RAG ohne Vektor-DB · Selbstheilender Code · Parallele LLM-Calls
Einen MCP-Server zu bauen heißt sonst: SDK auswählen, JSON-RPC-Verkabelung, und eine Runtime mit Dependencies ausliefern. Pipe ist die erste Sprache mit eingebautem MCP — ai_tool registriert eine Funktion, mcp_server + mcp_serve_stdio starten den Server.
fn greet name
"Hello, " ++ name ++ "! Pipe speaks MCP natively."
ai_tool "greet" "Greet a person by name" {name: "Person's name"} greet
mcp_server "Pipe Greeting Server" "1.0.0"
mcp_serve_stdio
Claude Desktop (oder Cursor, oder jeder MCP-Client) zeigt darauf:
{
"mcpServers": {
"pipe-greet": {
"command": "/path/to/pipe",
"args": ["examples/blog_mcp_server.pipe"]
}
}
}
Was hier passiert:
ai_toolmacht aus jeder Pipe-Funktion ein Tool mit Schema — Name, Beschreibung und Argument-Map reichen.mcp_server+mcp_serve_stdiostarten den Server und servieren das Tool über stdio.- Dieselbe Binary, die MCP *serviert*, kann es auch *konsumieren*:
mcp_use_stdioverbindet sich mit jedem MCP-Server von npm/uvx — dein LLM bekommt GitHub-, Dateisystem- oder Datenbank-Tools in derselben Pipeline.
Kein SDK, kein package.json, kein Build-Schritt. Eine einzelne ~7-MB-Binary ist Server *und* Client — das Tool, das du Claude gibst, ist dieselbe Sprache, in der du deine Pipeline schreibst. Genau das ist die Idee hinter der MCP-Zelle: Wenn MCP ein Sprach-Primitiv ist, kann die Sandbox Server, Client und Tools gemeinsam umschließen.
Selbst ausprobieren: pipe examples/blog_mcp_server.pipe (startet einen stdio-Server — teste mit initialize, tools/list, dann tools/call mit {"name":"greet","arguments":{"name":"Harry"}}).