{"slug": "thursdays-with-koog-mcp", "title": "Thursdays with Koog: MCP", "summary": "Knosh 0.4.0, an agent harness from CommonsWare, now supports the Model Context Protocol (MCP) via a Koog-supplied bridge, allowing developers to configure remote or local MCP servers in its knosh.json file. The update introduces an 'mcp' object that maps server identifiers to MCP server details, with support for both remote servers over Streamable HTTP and local servers spawned as child processes.", "body_md": "When working with coding agents, you will from time to time see sites that tell you to \"add an MCP server\". Here, MCP is [the Model Context Protocol](https://en.wikipedia.org/wiki/Model_Context_Protocol), a specification from Anthropic. MCP is a way to extend agent harnesses with new tools, where the protocol itself describes how the harness discovers available tools and how it invokes them.\n\nDevelopers creating agent harnesses like [Knosh](https://knosh.commonsware.com) or otherwise interacting with LLMs though libraries like [Koog](https://docs.koog.ai) have two main angles for considering MCP:\n\nKnosh `0.4.0`\n\nhandles the former, by way of a Koog-supplied MCP bridge, surfacing MCP-supplied tools into [Koog's own tool system](https://pac.commonsware.com/archive/thursdays-with-koog-tools/).\n\nKnosh's config file (by default at `~/.config/knosh/knosh.json`\n\n) now supports an `mcp`\n\nobject:\n\n```\n{\n    \"mcp\": {\n        \"composables\": {\n            \"type\": \"remote\",\n            \"url\": \"https://composables.com/mcp\"\n        }\n    }\n\n    // rest of configuration\n}\n```\n\nThat gets turned into a `Map`\n\ntying string identifiers (e.g., `composables`\n\n) to details of the MCP server that should be made available to agents.\n\nMCP supports two types of servers:\n\nThe data being exchanged and the general JSON structure of that data is the same in both cases. The difference is mostly in the transport layer. However, that difference requires dedicated configuration structures, as a remote server requires a URL, while a local server requires the command to run to start the server. In Knosh's case, the JSON configuration eventually turns into [ McpServerEntry instances](https://codeberg.org/commonsguy/knosh/src/tag/0.4.0/lib/knosh-agents/src/main/kotlin/com/commonsware/knosh/agents/McpServerEntry.kt):\n\n```\n/**\n * A resolved MCP server, built from a [KnoshConfig.mcp] entry. Either a [LocalMcpServerEntry] (spawned as a child\n * process) or a [RemoteMcpServerEntry] (reached over MCP Streamable HTTP).\n */\npublic sealed interface McpServerEntry {\n  /** The in-Knosh server identifier (the key under `mcp` in `knosh.json`). */\n  public val name: String\n\n  /** The server type: `\"local\"` or `\"remote\"`. */\n  public val type: String\n\n  /** Whether this server is active. */\n  public val enabled: Boolean\n\n  /** The server's timeout in seconds, or `null` for no configured timeout. */\n  public val timeout: Int?\n\n  /** Whether a startup failure is fatal. */\n  public val required: Boolean\n}\n\n/**\n * A resolved local MCP server, spawned as a child process.\n *\n * @property name the in-Knosh server identifier (the key under `mcp` in `knosh.json`)\n * @property command the server's resolved argv (from [McpServerConfig.effectiveCommand])\n * @property enabled whether this server is active\n * @property environment environment variables to pass to the server process\n * @property timeout the server's timeout in seconds, or `null` for no configured timeout\n * @property required whether a startup failure is fatal\n */\n@Poko\npublic class LocalMcpServerEntry(\n  public override val name: String,\n  public val command: List<String>,\n  public override val enabled: Boolean,\n  public val environment: Map<String, String>,\n  public override val timeout: Int?,\n  public override val required: Boolean,\n) : McpServerEntry {\n  public override val type: String = \"local\"\n}\n\n/**\n * A resolved remote MCP server, reached over MCP Streamable HTTP.\n *\n * @property name the in-Knosh server identifier (the key under `mcp` in `knosh.json`)\n * @property url the remote server's URL\n * @property enabled whether this server is active\n * @property headers HTTP headers sent to the remote server, raw and uninterpolated (see `McpHeaderResolver`)\n * @property timeout the server's timeout in seconds, or `null` for no configured timeout\n * @property required whether a startup failure is fatal\n */\n@Poko\npublic class RemoteMcpServerEntry(\n  public override val name: String,\n  public val url: String,\n  public override val enabled: Boolean,\n  public val headers: Map<String, String>,\n  public override val timeout: Int?,\n  public override val required: Boolean,\n) : McpServerEntry {\n  public override val type: String = \"remote\"\n}\n```\n\n(Knosh links in this issue point to `0.4.0`\n\n, released a few days ago)\n\nAgent frontmatter can also declare `mcp`\n\nstructures, either for MCP servers to be uniquely used by that agent, or to enable/disable servers defined in the main Knosh configuration. Knosh has a bunch of logic to determine which `McpServerEntry`\n\nobjects are relevant for a given run, based on the chosen agent and the overall configuration.\n\nThe primary MCP client support is not directly through Koog, but rather through [the MCP Kotlin SDK](https://github.com/modelcontextprotocol/kotlin-sdk), which JetBrains helps to maintain. That SDK provides `StdioClientTransport`\n\nand `StreamableHttpClientTransport`\n\nfor talking to local and remote servers, respectively. Knosh creates an instance of those based on what type of MCP server was configured and its details, such as the remote server URL. Those transports get wrapped into a `Client`\n\ninstance, which is the in-app representation of the MCP server itself.\n\nKoog then layers atop the MCP Kotlin SDK. It offers `McpToolRegistryProvider.fromClient()`\n\nthat takes a `Client`\n\n, finds out what MCP-style tools it offers, and wraps those in Koog `Tool`\n\ninstances, bundling them all up into a `ToolRegistry`\n\n. Knosh uses that in [its discoverMcpTools() function](https://codeberg.org/commonsguy/knosh/src/tag/0.4.0/lib/knosh-agents/src/main/kotlin/com/commonsware/knosh/agents/McpSessionOpener.kt#L145-L154):\n\n```\n/** Fetches [entry]'s tools from [client]. Production default for [McpToolDiscoverer], shared by local and remote. */\ninternal suspend fun discoverMcpTools(client: Client, entry: McpServerEntry): ToolRegistry =\n  McpToolRegistryProvider.fromClient(\n    mcpClient = client,\n    serverInfo =\n      McpServerInfo(\n        url = (entry as? RemoteMcpServerEntry)?.url,\n        command = (entry as? LocalMcpServerEntry)?.command?.firstOrNull(),\n      ),\n  )\n```\n\nAfter going through some permission checks, those tools wind up being blended with Knosh's own tools to create the overall `ToolRegistry`\n\nthat gets passed to the Koog `AIAgent`\n\nthat will process our prompt and return the LLM response.\n\nThe vast majority of Knosh's MCP-related code is tied up in handling the configuration and using that to create the MCP `Client`\n\nobjects. After that, Koog's `Tool`\n\nabstraction, and its supplied way of mapping MCP tools to `Tool`\n\ninstances, saves Knosh from having to do that work itself.\n\nThe net is that you can add MCP servers to Knosh, globally or per-agent, and then be able to use those servers' tools alongside the ones Knosh provides intrinsically.\n\nKnosh was not the only one of my projects to get an update recently. [koverGate](https://codeberg.org/commonsguy/koverGate), the Gradle task to help agents interpret [Kover](https://github.com/Kotlin/kotlinx-kover) reports, is up to `0.7.0`\n\n. This adds [a bunch of new features](https://codeberg.org/commonsguy/koverGate/src/branch/main/CHANGELOG.md#0-7-0-2026-08-31) that help agents make effective use of coverage reports, from sorting results by leverage (so the agent knows where to focus its efforts to boost coverage) to delta-tracking (so the agent knows how much its last round of work affected coverage).\n\nNext week, I will look at projects that extend Koog, such as for allowing on-device inference using mobile local models, as well as some Koog alternatives.", "url": "https://wpnews.pro/news/thursdays-with-koog-mcp", "canonical_source": "https://pac.commonsware.com/archive/thursdays-with-koog-mcp/", "published_at": "2026-09-03 13:00:00+00:00", "updated_at": "2026-09-03 13:24:27.495738+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Knosh", "Koog", "CommonsWare", "Anthropic", "Model Context Protocol"], "alternates": {"html": "https://wpnews.pro/news/thursdays-with-koog-mcp", "markdown": "https://wpnews.pro/news/thursdays-with-koog-mcp.md", "text": "https://wpnews.pro/news/thursdays-with-koog-mcp.txt", "jsonld": "https://wpnews.pro/news/thursdays-with-koog-mcp.jsonld"}}