MCP

Plug external tool servers into agents, without rewriting integrations.

What it is

MCP (Model Context Protocol) is a standard way for an LLM application to discover and call tools exposed by an external server. In practice, it lets you treat remote capabilities (APIs, databases, CLIs, internal services) as tools that your agent can call.

Phero’s mcp package adapts an MCP client session into Phero’s llm.Tool values, so the agent loop can call MCP tools the same way it calls in-process tools.

How it’s used

In practice, you connect to an MCP server, list its tools, convert them to agent tools, and then run the agent. The examples/mcp example shows the full flow.

// From examples/mcp (edited for brevity)

    ctx := context.Background()

    client := gomcp.NewClient(&gomcp.Implementation{Name: "myclient", Version: "1.0.0"}, nil)

    // Launch MCP server as a subprocess and connect over stdio
    command := "./examples/mcp/server/server"
    cmd := exec.CommandContext(ctx, command)
    transport := &gomcp.CommandTransport{Command: cmd}

    session, err := client.Connect(ctx, transport, nil)
    if err != nil {
      panic(err)
    }
    defer session.Close()

    mcpServer := mcp.New(session)

    tools, err := mcpServer.AsTools(ctx, nil)
    if err != nil {
      panic(err)
    }

    llmClient := /* any llm.LLM */
    a, _ := agent.New(llmClient, "MCP Agent", "An agent that uses tools from an MCP server.")
    for _, tool := range tools {
      _ = a.AddTool(tool)
    }

    res, _ := a.Run(ctx, "Give me a random quote.")
    fmt.Println(res)

Example: run an MCP server over stdio

The repo includes an end-to-end example under examples/mcp. The server exposes a single tool: get_random_quote.

1) Build the server

# from repo root
make -C ./examples/mcp/server build

# binary created at:
# ./examples/mcp/server/server

2) Run the client

# IMPORTANT: run from the repo root (client uses a relative path)
go run ./examples/mcp

Client side (connect + expose tools to an agent)

ctx := context.Background()

client := gomcp.NewClient(&gomcp.Implementation{Name: "myclient", Version: "1.0.0"}, nil)

// Launch MCP server as a subprocess and connect over stdio
command := "./examples/mcp/server/server"
cmd := exec.CommandContext(ctx, command)
transport := &gomcp.CommandTransport{Command: cmd}

session, err := client.Connect(ctx, transport, nil)
if err != nil {
    panic(err)
}
defer session.Close()

mcpServer := mcp.New(session)

tools, err := mcpServer.AsTools(ctx, nil)
if err != nil {
    panic(err)
}

a, _ := agent.New(llmClient, "MCP Agent", "An agent that uses tools from an MCP server.")
for _, tool := range tools {
    _ = a.AddTool(tool)
}

res, _ := a.Run(ctx, "Give me a random quote.")
fmt.Println(res)

Server side (define and run a tool)

server := mcp.NewServer(&mcp.Implementation{Name: "random_quote", Version: "v1.0.0"}, nil)

mcp.AddTool(
    server,
    &mcp.Tool{Name: "get_random_quote", Description: "Fetches a random inspirational quote"},
    getRandomQuoteHandler,
)

// Run over stdin/stdout until client disconnects
_ = server.Run(context.Background(), &mcp.StdioTransport{})

Filtering tools

If your MCP server exposes many tools, you can selectively expose them to an agent by passing a filter:

tools, err := mcpServer.AsTools(ctx, func(name string) bool {
    // allowlist only a subset
    return name == "get_random_quote" || strings.HasPrefix(name, "search_")
})

Notes & troubleshooting

Related packages