In Koog, how does McpToolRegistryProvider expose an external MCP server's tools?
answer
- a bridge into ToolRegistry
- stdio for local process, SSE for remote
- suspending connect at startup
- tool list is a snapshot
- foreign names and descriptions merge in
basics
~20 sMcpToolRegistryProvider connects to an MCP server over a transport, reads the tools that server advertises, and wraps each one as a Koog tool inside a ToolRegistry you merge with your own. The tool list is captured when you build the registry, not per call.
solid answer
~50 sYou create a transport and hand it to the provider: McpToolRegistryProvider.defaultStdioTransport(process) for a server you launched yourself as a local process, or defaultSseTransport(url) for one reachable over HTTP. McpToolRegistryProvider.fromTransport(transport) is a suspending call that connects, retrieves the server's advertised tools, and returns a ToolRegistry whose entries proxy calls out to that server. You then merge it with the registry of your own Kotlin tools and pass the result to the agent. Three operational consequences follow. The tool list is a snapshot taken at build time, so a server that gains tools later will not be seen until you rebuild. The names and descriptions come from someone else's server and land in your prompt budget and your namespace, so collisions and bloat are real. And you own the process or connection lifecycle: if a stdio server dies mid-run, every tool it contributed fails.
code
kotlin · 13 linesval process = ProcessBuilder("my-mcp-server", "--stdio").start()
try {
val mcpRegistry = McpToolRegistryProvider.fromTransport(
transport = McpToolRegistryProvider.defaultStdioTransport(process)
)
val agent = AIAgent(
promptExecutor = executor,
toolRegistry = mcpRegistry
)
agent.run("check the build status")
} finally {
process.destroy()
}go deeper
Know that Koog can pull tools from an external MCP server via McpToolRegistryProvider, and that the result is an ordinary ToolRegistry the agent uses like any other.
Explain the two transports, that fromTransport suspends while it connects, and that the returned registry gets merged with your own Kotlin tools.
Own the operational story: snapshot semantics, process lifecycle for stdio, remote failures surfacing as tool errors, and the prompt-budget cost of foreign descriptions.
Frame adding a server as a capability grant and a runtime dependency. Decide which tools are exposed, how the registry is refreshed, and what the agent does when the server is down.
## What the integration actually is Koog's MCP support is a bridge, not a protocol you interact with. From the agent's point of view nothing new exists: it still calls tools out of a ToolRegistry. McpToolRegistryProvider's job is to produce that registry from a running MCP server instead of from your Kotlin code. ## Transports and connection Two shapes are common. For a locally launched server, typically a binary or a container you start yourself, you build the process and pass it to defaultStdioTransport, which talks to it over its standard input and output. For a networked server you use defaultSseTransport with the server's URL. Either transport goes into fromTransport, which suspends while it connects and reads the server's advertised tools, and returns a ToolRegistry. There is also a path that accepts an already-constructed client when you need to configure the connection yourself. Because fromTransport suspends and does real I/O, it belongs in your startup path, not lazily in the middle of a request. A failure there is a startup failure you can report cleanly; the same failure discovered mid-agent-run is a half-finished conversation. ## Snapshot semantics This is the detail interviewers probe. The registry is built from what the server advertised at connection time. Koog does not re-read that list per turn. If the server is redeployed with a new tool, or drops one, your agent's view is stale until you build a new registry. For long-lived services that means treating registry construction as something you can redo, on reconnect, on a schedule, or on an explicit refresh, rather than a one-shot at boot you never revisit. ## Namespace and prompt budget Every tool the server advertises brings a name, a description and a parameter schema, and all of it is serialized into your requests alongside your own tools. Two things go wrong at scale. Names can collide with your local tools or with a second MCP server's, and the winner of a collision is not something you want decided implicitly. And descriptions written for someone else's product may be verbose or vague, quietly consuming context and degrading selection accuracy for your own tools. The mitigation is to filter: build the MCP registry, take only the tools you actually want, and give the merged registry a deliberate shape instead of accepting whatever the server offers. ## Failure and trust A proxied tool call is a call into another process or another host, so it fails in ways local tools do not: the process exits, the socket drops, the remote hangs. Those surface as tool errors inside the agent loop, which means your error handling has to cope with a tool that worked a minute ago and does not now. With stdio you own the process: start it before building the registry and destroy it on shutdown, or you leak child processes across restarts. Trust matters too. Tools from a third-party server are third-party code paths reachable by a model that will call them if the description sounds relevant. Registering an MCP server is a capability grant, and reviewing which of its tools you actually expose is where that grant is bounded. ## Where the boundary sits Everything above is Koog's surface: transports, provider calls, registry merging, lifecycle. The protocol underneath is the Model Context Protocol's own concern and not something you need to reason about to wire a Koog agent to a server.
- Your MCP server gains a new tool after a redeploy. Does the running Koog agent pick it up?No. The registry was built from the tools advertised when you connected, and Koog does not re-read that list per turn. You need to build a fresh registry, on reconnect, on a refresh trigger, or at the next restart, and construct the agent with it. Designing registry construction as a repeatable step rather than a boot-time one-off is what makes that painless.
- With a stdio transport, who is responsible for the server process?You are. You start the process yourself and pass it to the transport, so you must also stop it on shutdown or you leak children across restarts. If the process exits mid-run, every tool it contributed starts failing inside the agent loop, so treat process liveness as part of the agent's health rather than a detail of setup.
- How would you keep a chatty MCP server from swamping your own tools?Filter before merging. Build the MCP registry, keep only the tools you actually intend to expose, and merge that subset with your local tools. That bounds the prompt cost of foreign descriptions, removes accidental name collisions with your own tools, and makes the capability grant explicit rather than whatever the server happens to advertise.
saying these in an interview costs you the question
- Assuming the tool list refreshes on every agent turn
- Letting the transport connect lazily inside a request
- Ignoring stdio process lifecycle and leaking children
- Merging every advertised tool without filtering
- Treating remote tool failures as impossible