MCP Integration
The Model Context Protocol (MCP) enables Koog agents to discover and use tools exposed by external MCP servers. Koog provides first-class MCP support through the McpToolRegistryProvider for both stdio and SSE transports.
Overview
MCP is an open protocol that standardizes how AI agents connect to external tool providers. With Koog's MCP integration, you can:
- Connect to any MCP-compliant server (stdio or SSE)
- Dynamically discover tools at runtime
- Bridge MCP tools into Koog's native
ToolRegistry - Use well-known MCP servers like Google Maps, Playwright, and more
Dependencies
Add the MCP integration dependency:
// build.gradle.kts
dependencies {
implementation("ai.koog:koog-agents:0.8.0")
implementation("ai.koog:agents-mcp:0.8.0")
}
McpToolRegistryProvider
The McpToolRegistryProvider is the central entry point for creating tool registries from MCP servers.
Creating a Tool Registry from a Transport
import ai.koog.agents.mcp.McpToolRegistryProvider
import ai.koog.agents.mcp.McpTransport
// Create a tool registry from an MCP transport
val toolRegistry = McpToolRegistryProvider.fromTransport(
transport = McpTransport.Stdio(process)
)
Factory Methods
| Method | Transport | Use Case |
|---|---|---|
fromTransport(transport) |
Any McpTransport |
Generic transport |
fromProcess(process) |
Stdio (subprocess) | Local MCP servers |
fromClient(client) |
Existing McpClient |
Pre-configured client |
fromSseUrl(url) |
SSE (HTTP) | Remote MCP servers |
Stdio Connection
Connect to MCP servers that communicate via standard input/output using a subprocess.
Basic Stdio Connection
import ai.koog.agents.mcp.McpToolRegistryProvider
import ai.koog.agents.mcp.McpTransport
// Start the MCP server as a subprocess
val process = ProcessBuilder("npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir")
.redirectErrorStream(true)
.start()
// Create a tool registry from the process
val toolRegistry = McpToolRegistryProvider.fromProcess(process)
// Use the registry with an agent
val agent = AIAgent(
promptExecutor = executor,
llmModel = model,
toolRegistry = toolRegistry
)
val result = agent.run("List all files in the directory")
println(result)
// Clean up
process.destroy()
Stdio with Custom Environment
val process = ProcessBuilder("npx", "-y", "@modelcontextprotocol/server-postgres")
.apply {
// Load credentials from environment variables — never hardcode them
environment()["DATABASE_URL"] = System.getenv("DATABASE_URL")
?: error("DATABASE_URL environment variable not set")
redirectErrorStream(true)
}
.start()
val toolRegistry = McpToolRegistryProvider.fromProcess(process)
Google Maps MCP Server Example
import ai.koog.agents.mcp.McpToolRegistryProvider
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.ext.llm.OpenAIModels
import ai.koog.agents.ext.simple.simpleOpenAIExecutor
suspend fun main() {
val apiKey = System.getenv("OPENAI_API_KEY")
val googleMapsKey = System.getenv("GOOGLE_MAPS_API_KEY")
// Start Google Maps MCP server
val process = ProcessBuilder(
"npx", "-y", "@modelcontextprotocol/server-google-maps"
).apply {
environment()["GOOGLE_MAPS_API_KEY"] = googleMapsKey
redirectErrorStream(true)
}.start()
// Create tool registry from the MCP server
val toolRegistry = McpToolRegistryProvider.fromProcess(process)
// Create agent with MCP tools
val agent = AIAgent(
promptExecutor = simpleOpenAIExecutor(apiKey),
llmModel = OpenAIModels.Chat.GPT4o,
toolRegistry = toolRegistry
)
val result = agent.run("Find coffee shops near the Eiffel Tower")
println(result)
process.destroy()
}
Playwright MCP Server Example
import ai.koog.agents.mcp.McpToolRegistryProvider
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.ext.llm.OpenAIModels
import ai.koog.agents.ext.simple.simpleOpenAIExecutor
suspend fun main() {
val apiKey = System.getenv("OPENAI_API_KEY")
// Start Playwright MCP server
val process = ProcessBuilder(
"npx", "-y", "@playwright/mcp@latest"
).apply {
redirectErrorStream(true)
}.start()
val toolRegistry = McpToolRegistryProvider.fromProcess(process)
val agent = AIAgent(
promptExecutor = simpleOpenAIExecutor(apiKey),
llmModel = OpenAIModels.Chat.GPT4o,
toolRegistry = toolRegistry
)
// The agent can now browse the web using Playwright
val result = agent.run("Go to example.com and extract the page title")
println(result)
process.destroy()
}
SSE Connection
Connect to remote MCP servers over HTTP using Server-Sent Events (SSE) transport.
Basic SSE Connection
import ai.koog.agents.mcp.McpToolRegistryProvider
// Connect to a remote MCP server via SSE
val toolRegistry = McpToolRegistryProvider.fromSseUrl(
url = "http://localhost:3000/mcp"
)
val agent = AIAgent(
promptExecutor = executor,
llmModel = model,
toolRegistry = toolRegistry
)
val result = agent.run("Use the available tools to complete this task")
println(result)
SSE with Authentication
val toolRegistry = McpToolRegistryProvider.fromSseUrl(
url = "https://mcp.example.com/sse",
headers = mapOf(
"Authorization" to "Bearer ${System.getenv("MCP_TOKEN")}",
"X-API-Key" to System.getenv("MCP_API_KEY")
)
)
McpTool Bridge
Koog bridges MCP tools into its native tool system via McpTool. This allows MCP-discovered tools to be used seamlessly with all Koog features.
import ai.koog.agents.mcp.McpTool
import ai.koog.agents.mcp.McpToolDescriptorParser
// Parse an MCP tool descriptor into a Koog tool
val mcpTool = McpTool(
name = "search_web",
description = "Search the web for information",
inputSchema = McpToolDescriptorParser.parse(inputJsonSchema)
)
// The McpTool implements Koog's Tool interface
// It can be registered in a ToolRegistry like any other tool
val registry = ToolRegistry {
tool(mcpTool)
}
McpToolDescriptorParser
The McpToolDescriptorParser converts MCP tool schemas into Koog's internal format:
import ai.koog.agents.mcp.McpToolDescriptorParser
// Parse an MCP JSON schema
val descriptor = McpToolDescriptorParser.parse("""
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
},
"maxResults": {
"type": "integer",
"description": "Maximum number of results",
"default": 10
}
},
"required": ["query"]
}
""")
McpServerInfo
When connecting to an MCP server, you can retrieve server information:
import ai.koog.agents.mcp.McpServerInfo
// After creating a client, inspect server capabilities
val serverInfo: McpServerInfo = client.getServerInfo()
println("Server name: ${serverInfo.name}")
println("Server version: ${serverInfo.version}")
println("Capabilities: ${serverInfo.capabilities}")
Multiple MCP Servers
Combine tools from multiple MCP servers in a single agent:
import ai.koog.agents.mcp.McpToolRegistryProvider
import ai.koog.agents.core.tools.ToolRegistry
// Connect to multiple MCP servers
val filesystemTools = McpToolRegistryProvider.fromProcess(
ProcessBuilder("npx", "-y", "@modelcontextprotocol/server-filesystem", "/data").start()
)
val webTools = McpToolRegistryProvider.fromSseUrl(
url = "http://localhost:3001/mcp"
)
val databaseTools = McpToolRegistryProvider.fromProcess(
ProcessBuilder("npx", "-y", "@modelcontextprotocol/server-postgres").apply {
environment()["DATABASE_URL"] = System.getenv("DATABASE_URL")
}.start()
)
// Combine all tool registries
val combinedRegistry = ToolRegistry {
registerAllFrom(filesystemTools)
registerAllFrom(webTools)
registerAllFrom(databaseTools)
}
val agent = AIAgent(
promptExecutor = executor,
llmModel = model,
toolRegistry = combinedRegistry
)
Java API
import ai.koog.agents.mcp.McpToolRegistryProvider;
import ai.koog.agents.core.agent.AIAgent;
public class McpExample {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = new ProcessBuilder(
"npx", "-y", "@modelcontextprotocol/server-filesystem", "/path"
);
pb.redirectErrorStream(true);
Process process = pb.start();
var toolRegistry = McpToolRegistryProvider.fromProcess(process);
var agent = AIAgent.builder(executor, model)
.setToolRegistry(toolRegistry)
.build();
String result = agent.run("List all files");
System.out.println(result);
process.destroy();
}
}
Best Practices
- Always clean up processes — Call
process.destroy()when done with stdio-based servers - Handle connection failures — Wrap MCP connections in try-catch for network errors
- Timeout configuration — Set appropriate timeouts for SSE connections
- Tool discovery — Use
McpToolRegistryProviderfor automatic tool discovery rather than manual registration - Error handling — MCP tool errors are propagated as Koog tool errors; handle them in your agent's error strategy
- Never hardcode credentials — Pass secrets to MCP server processes via environment variables from
System.getenv(), not as literal strings in code - Vet external MCP servers —
npx -y <package>downloads and executes npm packages without prompting for confirmation. Only use packages from trusted, well-maintained sources. Review the package source before adding it to production agents - Principle of least privilege for filesystem servers — When using
@modelcontextprotocol/server-filesystem, point it to the minimum required directory, not the entire filesystem
Common MCP Servers
| Server | Package | Transport |
|---|---|---|
| Filesystem | @modelcontextprotocol/server-filesystem |
Stdio |
| Google Maps | @modelcontextprotocol/server-google-maps |
Stdio |
| Playwright | @playwright/mcp |
Stdio |
| PostgreSQL | @modelcontextprotocol/server-postgres |
Stdio |
| GitHub | @modelcontextprotocol/server-github |
Stdio |
| Slack | @modelcontextprotocol/server-slack |
Stdio |
Troubleshooting
| Issue | Solution |
|---|---|
Process exited unexpectedly |
Check that the MCP server package is installed (npx -y <package>) |
Connection refused (SSE) |
Verify the MCP server is running and the URL is correct |
Tool not found |
Ensure the MCP server exposes the expected tools (check server docs) |
Schema parse error |
Validate the MCP tool's JSON schema against the MCP specification |
Timeout on SSE |
Increase timeout settings or check network connectivity |