Skip to content

protomcp

Write MCP tools in any language. Hot-reload without restarting your AI host.
MCP Host
Claude, Cursor, etc.
JSON-RPC / stdio
pmcp
Go binary
protobuf / unix socket
Your Code
Python, TS, Go, Rust
hot reload dynamic tool lists progress reporting cancellation

Why protomcp?

Any Language

Write tools in Python, TypeScript, Go, Rust, or any language that can speak protobuf over a unix socket.

Hot Reload

Save your file and tools reload instantly — no need to restart Claude Desktop or your MCP host.

Dynamic Tool Lists

Tools can enable or disable themselves at runtime. Show only what’s relevant based on context.

Single Binary

One Go binary. No daemon, no registry, no config files. Just pmcp dev server.py.

How It Works

protomcp sits between your MCP host (Claude Desktop, etc.) and your tool process. It speaks MCP on one side and a simple protobuf protocol on the other.

MCP Host ←── MCP protocol ──→ protomcp (Go) ←── protobuf/unix socket ──→ tool process

Your tool process registers handlers using a decorator or function, and protomcp handles everything else: listing tools, routing calls, hot reload, and dynamic tool list management.

Get Started

Terminal window
# Install
brew install msilverblatt/tap/protomcp
# tools.py (Python)
from protomcp import tool, ToolResult
@tool("Add two numbers")
def add(a: int, b: int) -> ToolResult:
return ToolResult(result=str(a + b))
// tools.ts (TypeScript)
import { tool, ToolResult } from 'protomcp';
import { z } from 'zod';
tool({
description: 'Add two numbers',
args: z.object({ a: z.number(), b: z.number() }),
handler({ a, b }) {
return new ToolResult({ result: String(a + b) });
},
});
// tools.go (Go)
package main
import (
"fmt"
"github.com/msilverblatt/protomcp/sdk/go/protomcp"
)
func main() {
protomcp.Tool("add",
protomcp.Description("Add two numbers"),
protomcp.Args(protomcp.IntArg("a"), protomcp.IntArg("b")),
protomcp.Handler(func(ctx protomcp.ToolContext, args map[string]interface{}) protomcp.ToolResult {
a := int(args["a"].(float64))
b := int(args["b"].(float64))
return protomcp.Result(fmt.Sprintf("%d", a+b))
}),
)
protomcp.Run()
}
// src/main.rs (Rust)
use protomcp::{tool, ToolResult, ArgDef};
#[tokio::main]
async fn main() {
tool("add")
.description("Add two numbers")
.arg(ArgDef::int("a"))
.arg(ArgDef::int("b"))
.handler(|_ctx, args| {
let a = args["a"].as_i64().unwrap_or(0);
let b = args["b"].as_i64().unwrap_or(0);
ToolResult::new(format!("{}", a + b))
})
.register();
protomcp::run().await;
}
Terminal window
# Python
pmcp dev tools.py
# TypeScript
pmcp dev tools.ts
# Go
pmcp dev tools.go
# Rust
pmcp dev src/main.rs

Then add the command to your MCP client config. That’s it.