A two-vertex graph: a context-val op lifts the user's query, and a single
MCPCallOp over streamable HTTP calls Cloudflare's
search_cloudflare_documentation
tool. No subprocess, no API keys, no per-server SDK.
What this example teaches
MCPCallOp[In, Out] for a specific remote tool with typed In / Outtransport="http" against a streamable-HTTP MCP server with init_timeout_ms and call_timeout_msheaders param injects Authorization or other static headers for authenticated remote MCP serversEmbed library.MCPCallOp[In, Out] in a named
struct so each tool gets a registered op type. The SearchInput JSON tag
aligns with the Cloudflare tool's input schema; Out is just string
— the default dispatch returns the concatenated text content.
type SearchInput struct {
Query string `json:"query"`
}
type MCPCloudflareDocsSearchOp struct {
library.MCPCallOp[SearchInput, string]
}
func init() {
operator.RegisterOpFactory("search_input_const",
builtin.ContextValFactory[SearchInput](searchInputKey{}))
operator.RegisterOp[MCPCloudflareDocsSearchOp]()
}
buildGraphSet transport=http and point url
at the MCP endpoint. init_timeout_ms covers the protocol handshake;
call_timeout_ms covers the actual tool call. For private endpoints, add a
Bearer token (or any other static header) via the headers param.
cfParams := map[string]string{
"transport": "http",
"url": "https://docs.mcp.cloudflare.com/mcp",
"tool_name": "search_cloudflare_documentation",
"init_timeout_ms": "30000",
"call_timeout_ms": "60000",
"max_retries": "2",
// For authenticated remote MCP servers:
// "headers": "Authorization=Bearer ${TOKEN}",
}
graph.NewBuilder("mcp_cloudflare_docs_search").
Vertex("search_input").Op("search_input_const").
Output("Result", "search_input").
Vertex("cf_search").Op("MCPCloudflareDocsSearchOp").
Params(cfParams).
Input("Input", "search_input").
Output("Result", "search_results").
Build()
Just --query. The remote MCP server is public,
so no API keys are required.
go run ./examples/remote-mcp-server --query "workers durable objects"
The same binary is dual-mode. Pass --mcp and instead of running once and exiting it speaks the Model Context Protocol over stdin/stdout, exposing the workflow as a single MCP tool, search_cloudflare_docs. The result is a local stdio MCP server that proxies the public remote (HTTP) Cloudflare docs server — a thin, typed front door an agent can register without dealing with the streamable-HTTP transport itself.
# Speak MCP over stdin/stdout instead of running once
go run ./examples/remote-mcp-server --mcp
Register it with any MCP client by pointing the client at that command:
{
"mcpServers": {
"remote-mcp-server": {
"command": "go",
"args": ["run", "./examples/remote-mcp-server", "--mcp"]
}
}
}