Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ Server.run() ← dispatch loop
| `gomcp/protocol.go` | Protocol versions, `_meta` negotiation, pagination |
| `gomcp/path.go` | `SafeJoin` — path-traversal-safe filesystem helper |
| `gomcp/server.go` | Server struct, Run(), all JSON-RPC method handlers |
| `gomcp/httpserver.go` | MCP Streamable HTTP transport (`Handler`, `ListenAndServe`, `Serve`) |
| `gomcp/client.go` | MCP client (`Client` interface, `NewHTTPClient`, `NewStdioClient`) |
| `gomcp/server_test.go` | Unit + integration tests (pipe-based) |
| `gomcp/protocol_test.go` | 2026-07-28 + security tests |
| `gomcp/httpserver_test.go`, `gomcp/client_test.go`, `gomcp/http_e2e_test.go` | HTTP transport, client, and loopback E2E tests |
| `gomcp/e2e_test.go` | Subprocess E2E test |
| `examples/greet/main.go` | Canonical example MCP server |

Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# go-mcp

Zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) server framework for Go. Expose your Go code as **tools**, **resources**, and **prompts** that AI agents can call. Stdio transport. Single binary. No runtime.
> Zero-dependency Model Context Protocol (MCP) server framework for Go — stdio and Streamable HTTP transports, plus MCP clients for both.

Zero-dependency [Model Context Protocol](https://modelcontextprotocol.io) server framework for Go. Expose your Go code as **tools**, **resources**, and **prompts** that AI agents can call. Stdio and Streamable HTTP transports, plus clients for both. Single binary. No runtime.

```bash
go get github.com/BackendStack21/go-mcp
Expand Down Expand Up @@ -304,6 +306,45 @@ func NewTextContent(text string) map[string]any

Starts the server loop. Reads JSON-RPC 2.0 from `os.Stdin`, writes responses to `os.Stdout`. Blocks until stdin closes (EOF).

### `srv.Handler() http.Handler` / `srv.ListenAndServe(addr) error` / `srv.Serve(l net.Listener) error`

Serves the same JSON-RPC dispatch over the MCP **Streamable HTTP** transport. Each `POST` carries one JSON-RPC message; responses are `application/json` or SSE (when the client's `Accept` prefers it), notifications get `202`, and anything but `POST` (with a JSON body) is `405`/`415`. Reuses `MaxRequestBytes` and `HandlerTimeout`. Stateless — no session management, no server-initiated streams.

```go
srv := gomcp.NewServer("my-server", "1.0.0")
// ... register tools ...
srv.ListenAndServe("localhost:8080") // blocks
```

### Client — `gomcp.NewHTTPClient(url)` / `gomcp.NewStdioClient(path, args...)`

Both return the same `Client` interface, safe for concurrent use:

```go
c := gomcp.NewHTTPClient("http://localhost:8080")
// or: c, err := gomcp.NewStdioClient("./my-server")
res, _ := c.Initialize(ctx, "my-client", "1.0.0")
c.NotifyInitialized(ctx)
tools, _ := c.ListTools(ctx)
out, _ := c.CallTool(ctx, "echo", map[string]any{"message": "hi"})
```

Methods: `Initialize`, `NotifyInitialized`, `ListTools`, `CallTool`, `ListResources`, `ReadResource`, `ListPrompts`, `GetPrompt`, `Close`.

### `srv.SetAuthToken(token string)` / `srv.SetAllowedOrigins(origins []string)`

Optional HTTP-transport hardening:

- **Bearer auth** — when a token is set, every request must carry `Authorization: Bearer <token>`; anything else gets `401` with a `WWW-Authenticate: Bearer` challenge. Comparison is constant-time (`crypto/subtle`). Pair with any external token issuer (OAuth resource server, API gateway, or a plain static key).
- **Origin allowlist** — when set, browser requests with an `Origin` not on the list get `403` (DNS-rebinding / CSRF defense, per 2025-11-25 spec guidance). Non-browser clients (no `Origin` header) are unaffected.

Both default to off; stdio is never affected — its trust model is the user launching the process.

```go
srv.SetAuthToken(os.Getenv("MCP_TOKEN"))
srv.SetAllowedOrigins([]string{"https://claude.ai"})
```

### `srv.SetInstructions(text string)`

Optional natural-language guidance returned by `initialize` and `server/discover`.
Expand Down
62 changes: 56 additions & 6 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>go-mcp — MCP Server Framework for Go</title>
<meta name="description" content="Zero-dependency Model Context Protocol server framework for Go. Tools, resources, and prompts over stdio. Single binary. No runtime.">
<meta name="description" content="Zero-dependency Model Context Protocol server framework for Go. Tools, resources, and prompts over stdio and Streamable HTTP — plus MCP clients for both. Single binary. No runtime.">
<meta property="og:title" content="go-mcp — MCP Server Framework for Go">
<meta property="og:description" content="Zero-dependency MCP server framework. Build AI-accessible tools in Go.">
<meta property="og:type" content="website">
Expand All @@ -15,7 +15,7 @@
<meta property="og:image:alt" content="21no.de logo">
<meta property="og:url" content="https://go-mcp.21no.de">
<link rel="canonical" href="https://go-mcp.21no.de">
<meta name="keywords" content="MCP, Model Context Protocol, Go, server framework, zero-dependency, AI tools, stdio, JSON-RPC">
<meta name="keywords" content="MCP, Model Context Protocol, Go, server framework, zero-dependency, AI tools, stdio, HTTP, Streamable HTTP, client, JSON-RPC">
<meta name="author" content="21no.de">
<meta name="theme-color" content="#0a0a0b">
<link rel="icon" type="image/png" href="https://21no.de/logo-v2.png">
Expand Down Expand Up @@ -48,6 +48,8 @@
/* Grid */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .grid-3 { grid-template-columns: 1fr; } }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
@media (max-width: 900px) { .grid-2 { grid-template-columns: 1fr; } }

/* Card icon blue = accent */
.card-icon.blue { background: var(--accent-subtle); color: var(--accent); }
Expand Down Expand Up @@ -156,7 +158,7 @@
<div class="container">
<div class="hero-badge"><span class="dot"></span> Zero dependencies. Single binary. Go-native.</div>
<h1>Build <span class="accent">AI tools</span><br>in Go</h1>
<p>A zero-dependency MCP server framework that turns any Go program into an AI-accessible service. Tools, resources, and prompts over stdio. Compile, ship, connect.</p>
<p>A zero-dependency MCP framework that turns any Go program into an AI-accessible service. Tools, resources, and prompts over stdio or Streamable HTTP — with clients for both. Compile, ship, connect.</p>
<div class="hero-actions">
<a href="#install" class="btn btn-primary">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2v10M4 8l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
Expand Down Expand Up @@ -197,7 +199,7 @@ <h1>Build <span class="accent">AI tools</span><br>in Go</h1>
},
})

srv.<span class="fn">Run</span>() <span class="cmt">// blocks, reading JSON-RPC from stdin</span>
srv.<span class="fn">Run</span>() <span class="cmt">// stdio — or srv.ListenAndServe(":8080") for HTTP</span>
}</pre>
</div>
</div>
Expand All @@ -215,6 +217,10 @@ <h1>Build <span class="accent">AI tools</span><br>in Go</h1>
<div class="num">11</div>
<div class="label">MCP methods</div>
</div>
<div class="stat">
<div class="num">2</div>
<div class="label">transports — stdio &amp; HTTP</div>
</div>
<div class="stat">
<div class="num">&lt;1ms</div>
<div class="label">cold start</div>
Expand All @@ -232,7 +238,7 @@ <h1>Build <span class="accent">AI tools</span><br>in Go</h1>
<div class="container">
<div class="section-label">What You Can Build</div>
<h2>Turn any Go program into an<br>AI-accessible service</h2>
<p class="lead">Register tools, resources, and prompts. The AI client discovers them automatically. All over stdin/stdout.</p>
<p class="lead">Register tools, resources, and prompts. The AI client discovers them automatically over stdio or HTTP. Or connect to other MCP servers yourself with the built-in clients.</p>
<div class="grid-3">
<div class="card">
<div class="card-icon blue">
Expand Down Expand Up @@ -262,6 +268,20 @@ <h3>Kubernetes Operator</h3>
<h3>API Integration</h3>
<p>Bridge any REST API to AI. Give agents access to GitHub, Stripe, Slack — any HTTP endpoint becomes a callable tool.</p>
</div>
<div class="card">
<div class="card-icon green">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M5 12.55a11 11 0 0114.08 0M1.42 9a16 16 0 0121.16 0M8.53 16.11a6 6 0 016.95 0M12 20h.01"/></svg>
</div>
<h3>HTTP-Native Server</h3>
<p>Serve MCP over Streamable HTTP — one handler, any port. Optional Bearer auth and Origin allowlist built in.</p>
</div>
<div class="card">
<div class="card-icon purple">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
</div>
<h3>MCP Client Too</h3>
<p>Connect to any MCP server over HTTP or spawn one over stdio — the same typed Client interface for both.</p>
</div>
<div class="card">
<div class="card-icon green">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>
Expand Down Expand Up @@ -359,6 +379,36 @@ <h3>Prompts</h3>
<p>Pre-defined conversation templates. The AI requests them by name with arguments via <code>prompts/get</code>. Returns formatted messages ready for the chat.</p>
</div>
</div>

<h2 style="margin-top: 48px;">Two transports. One framework.</h2>
<p class="lead">The same server runs over stdio or Streamable HTTP — and the built-in clients talk to either.</p>
<div class="grid-2" style="margin-top: 32px;">
<div class="code-block">
<div class="bar"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span><span class="title">server — stdio or HTTP</span></div>
<pre><span class="cmt">// stdio — the default, for local AI clients</span>
srv.<span class="fn">Run</span>()

<span class="cmt">// Streamable HTTP — for remote access</span>
srv.<span class="fn">SetAuthToken</span>(token) <span class="cmt">// optional</span>
srv.<span class="fn">SetAllowedOrigins</span>(origins) <span class="cmt">// optional</span>
srv.<span class="fn">ListenAndServe</span>(<span class="str">":8080"</span>)

<span class="cmt">// or mount on your own mux:</span>
mux.<span class="fn">Handle</span>(<span class="str">"/mcp"</span>, srv.<span class="fn">Handler</span>())</pre>
</div>
<div class="code-block">
<div class="bar"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span><span class="title">client — HTTP or subprocess</span></div>
<pre><span class="cmt">// connect to a remote server</span>
c := gomcp.<span class="fn">NewHTTPClientWithToken</span>(url, token)

<span class="cmt">// or spawn a local one</span>
c, _ := gomcp.<span class="fn">NewStdioClient</span>(<span class="str">"./my-server"</span>)

c.<span class="fn">Initialize</span>(ctx, <span class="str">"client"</span>, <span class="str">"1.0"</span>)
tools, _ := c.<span class="fn">ListTools</span>(ctx)
out, _ := c.<span class="fn">CallTool</span>(ctx, <span class="str">"greet"</span>, args)</pre>
</div>
</div>
</div>
</section>

Expand Down Expand Up @@ -403,7 +453,7 @@ <h2>One command. Zero dependencies.</h2>
<code>go get github.com/BackendStack21/go-mcp</code>
</div>
<p style="margin-top: 16px; font-size: 14px; color: var(--text-tertiary);">
15 tests. 4 example servers. <a href="https://github.com/BackendStack21/go-mcp" target="_blank" rel="noopener">View on GitHub →</a>
102 tests. 91.9% coverage. <a href="https://github.com/BackendStack21/go-mcp" target="_blank" rel="noopener">View on GitHub →</a>
</p>
</div>
</section>
Expand Down
Loading
Loading