Skip to content

feat(config): add MCP server configuration support#239

Merged
feloy merged 3 commits intoopenkaiden:mainfrom
feloy:mcps
Apr 10, 2026
Merged

feat(config): add MCP server configuration support#239
feloy merged 3 commits intoopenkaiden:mainfrom
feloy:mcps

Conversation

@feloy
Copy link
Copy Markdown
Contributor

@feloy feloy commented Apr 10, 2026

Allows users to configure MCP servers (command-based and URL-based) in workspace.json and in the multi-level config system (projects.json, agents.json). The manager calls SetMCPServers() on the registered agent after SetModel(), writing the merged MCP config into agent settings.

  • Upgrade kdn-api/workspace-configuration/go to v0.2.0 which adds McpConfiguration, McpCommand, and McpServer types
  • Add SetMCPServers() to the Agent interface; Claude writes command servers as type "stdio" and URL servers as type "sse" into the top-level mcpServers key of .claude.json (user scope); Goose and Cursor are no-ops
  • Add MCP merge logic to ConfigMerger (merge by name, override wins)
  • Add MCP validation: non-empty name/command/url required; names must be unique across both commands and servers combined since they are written to a single flat mcpServers map
  • Wire SetMCPServers() into manager.Add() after SetModel()
  • Add tests for all new functionality
  • Update README.md, CLAUDE.md, and skills documentation

Implementation for Claude agent only.

Closes #210

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

Warning

Rate limit exceeded

@feloy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 1 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 24 minutes and 1 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 469f2655-498d-4e63-8813-2a126e8bf88b

📥 Commits

Reviewing files that changed from the base of the PR and between 0b4c128 and 784f724.

📒 Files selected for processing (1)
  • pkg/config/merger.go
📝 Walkthrough

Walkthrough

Adds MCP (Model Context Protocol) server support: workspace schema for command-based (stdio) and SSE servers, validation and name-based merge behavior, Agent API extension with SetMCPServers, Claude implementation to update .claude.json, and manager wiring to apply MCP settings during instance creation. (45 words)

Changes

Cohort / File(s) Summary
Docs
AGENTS.md, README.md, skills/working-with-config-system/SKILL.md, skills/working-with-instances-manager/SKILL.md
Documented new mcp workspace section, examples for commands (stdio) and servers (sse), merge/precedence rules, validation constraints, and manager/agent workflow calling SetMCPServers() after model/onboarding steps.
Dependencies
go.mod
Bumped github.com/openkaiden/kdn-api/cli/go and .../workspace-configuration/go to v0.2.0.
Agent interface & impl
pkg/agent/agent.go, pkg/agent/claude.go, pkg/agent/cursor.go, pkg/agent/goose.go, pkg/agent/claude_test.go, pkg/agent/registry_test.go
Added Agent.SetMCPServers(map[string][]byte, *workspace.McpConfiguration) to the interface; Claude implements JSON merge/write to ClaudeJSONPath (.claude.json) populating mcpServers; Cursor/Goose are no-ops; tests and test doubles updated/added.
Config validation & tests
pkg/config/config.go, pkg/config/config_test.go
Added MCP validation: required non-empty name, required command for commands and url for servers, and uniqueness of name across both lists; added valid/invalid tests.
Config merging & tests
pkg/config/merger.go, pkg/config/merger_test.go
Implemented mergeMCP with deep-copy, dedupe and override by Name, cross-type collision resolution (override wins), integrated into Merge()/copyConfig(); added extensive merge and deep-copy tests.
Manager integration & tests
pkg/instances/manager.go, pkg/instances/manager_test.go
manager.Add() now invokes agent.SetMCPServers() when merged config contains Mcp; added tests verifying invocation and non-invocation paths.

Sequence Diagram

sequenceDiagram
    actor User
    participant Manager
    participant Config
    participant Agent as ClaudeAgent
    participant Settings as AgentSettings

    User->>Manager: Add(workspace with MCP)
    Manager->>Config: Merge(base, workspaceOverride)
    Config-->>Manager: mergedConfig (includes Mcp)
    Manager->>Agent: SetMCPServers(settings, mergedConfig.Mcp)
    Agent->>Settings: Read existing .claude.json (or init {})
    Agent->>Settings: Merge/Populate mcpServers from Commands (stdio) and Servers (sse)
    Agent->>Settings: Marshal JSON and write back into settings map
    Agent-->>Manager: Return updated settings
    Manager->>Manager: Inject settings into runtime context
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding MCP server configuration support to the config system.
Description check ✅ Passed The description is directly related to the changeset, explaining the new MCP server configuration capability, implementation details, and which agent implementations it covers.
Linked Issues check ✅ Passed The PR fully addresses issue #210 by implementing MCP server configuration (both remote URL-based and command-based), upgrading the workspace-configuration dependency, implementing SetMCPServers on agents, and integrating MCP settings into the manager.
Out of Scope Changes check ✅ Passed All changes are directly related to MCP server configuration support: interface additions, agent implementations, merge/validation logic, documentation updates, and tests for the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/agent/agent.go (1)

26-48: ⚠️ Potential issue | 🟠 Major

Agent interface is still missing required SkillsDir() string.

While adding SetMCPServers, the interface should also expose SkillsDir() to match the repository’s agent contract.

Suggested interface adjustment
 type Agent interface {
 	// Name returns the agent name (e.g., "claude", "goose").
 	Name() string
+	// SkillsDir returns the container path (using $HOME) where skills should be mounted.
+	SkillsDir() string
 	// SkipOnboarding modifies agent settings to skip onboarding prompts.
 	SkipOnboarding(settings map[string][]byte, workspaceSourcesPath string) (map[string][]byte, error)

Based on learnings: Applies to pkg/agent/**/*.go : The Agent interface (pkg/agent/agent.go) must expose SkillsDir() string which returns the container path using the $HOME variable where skill directories should be mounted.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/agent/agent.go` around lines 26 - 48, The Agent interface is missing the
required SkillsDir() string method; add SkillsDir() string to the Agent
interface declaration alongside existing methods (Name, SkipOnboarding,
SetModel, SetMCPServers) so implementations must return the container path
(using $HOME) where skill directories are mounted; update any implementing types
(e.g., concrete agent structs that implement Agent) to include a SkillsDir
method returning the appropriate path.
🧹 Nitpick comments (3)
pkg/instances/manager_test.go (1)

3552-3669: Add an MCP error-propagation test for manager.Add().

Current coverage checks call/no-call only. Please add a subtest where SetMCPServers() returns an error and assert Add() fails with the expected wrapped message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/instances/manager_test.go` around lines 3552 - 3669, Add a third subtest
to TestManager_Add_AppliesAgentMCPServers that registers an agent whose
SetMCPServers(ctx, ...) method returns a controlled error and then calls
manager.Add(...) with a workspace MCP config; assert that Add returns a non-nil
error and that its error string wraps/includes the expected message from
SetMCPServers (e.g., "SetMCPServers" or the specific error text). Locate the
tracking agent implementation used in tests (newTrackingAgent or create a small
failingTrackingAgent) and make it return the error from SetMCPServers, call
manager.Add with WorkspaceConfig.Mcp set, and check the returned error matches
the wrapped message to ensure propagation.
skills/working-with-config-system/SKILL.md (1)

72-82: Consider restructuring to improve readability.

The static analysis tool flagged that three successive sentences begin with "If". While the content is correct, consider restructuring for better flow.

📝 Suggested improvement
-**Implementation:** `manager.readAgentSettings(storageDir, agentName)` in `pkg/instances/manager.go` walks this directory and returns a `map[string][]byte` (relative forward-slash path → content). If the agent is registered in the agent registry, the manager calls the agent's `SkipOnboarding()` method to modify the settings. If a model ID is provided, the manager then calls the agent's `SetModel()` method. If the merged config contains MCP servers, the manager calls the agent's `SetMCPServers()` method. The final map is passed to the runtime via `runtime.CreateParams.AgentSettings`. The Podman runtime writes the files into the build context and adds a `COPY --chown=agent:agent agent-settings/. /home/agent/` instruction to the Containerfile. The model is also stored directly in the `instance` struct and persisted in `instances.json` via `InstanceData.Model`.
+**Implementation:** `manager.readAgentSettings(storageDir, agentName)` in `pkg/instances/manager.go` walks this directory and returns a `map[string][]byte` (relative forward-slash path → content). For registered agents, the manager calls `SkipOnboarding()` to modify the settings. When a model ID is provided, the manager then calls `SetModel()`. Similarly, when the merged config contains MCP servers, the manager calls `SetMCPServers()`. The final map is passed to the runtime via `runtime.CreateParams.AgentSettings`. The Podman runtime writes the files into the build context and adds a `COPY --chown=agent:agent agent-settings/. /home/agent/` instruction to the Containerfile. The model is also stored directly in the `instance` struct and persisted in `instances.json` via `InstanceData.Model`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/working-with-config-system/SKILL.md` around lines 72 - 82, Rewrite the
paragraph in SKILL.md to improve flow by combining or reordering the three
consecutive sentences that start with "If": reference the implementation steps
involving manager.readAgentSettings, the agent registry call to
SkipOnboarding(), the conditional SetModel(modelID) call, and the conditional
SetMCPServers() call so they read as a cohesive sequence rather than three
standalone "If" sentences; keep the same facts (map returned by
readAgentSettings, model stored in InstanceData.Model, agent settings passed via
runtime.CreateParams.AgentSettings, Podman COPY instruction) and preserve
mention of where MCP servers are merged and how they are applied to agents
(SetMCPServers), but restructure into 2–3 varied-start sentences for better
readability.
pkg/agent/claude_test.go (1)

499-516: Consider using JSON comparison instead of string comparison.

The test at line 513 compares the raw JSON bytes as strings. This works but is fragile - if the implementation changes JSON formatting (e.g., adds indentation or reorders keys), the test would fail even though the content is semantically identical. Consider parsing the result and comparing the hasCompletedOnboarding field directly, similar to other tests in this file.

♻️ Suggested improvement
 	// Settings should be returned unchanged
-	if string(result[ClaudeJSONPath]) != `{"hasCompletedOnboarding": true}` {
-		t.Errorf("SetMCPServers() with nil MCP modified settings unexpectedly: %s", result[ClaudeJSONPath])
+	var config map[string]interface{}
+	if err := json.Unmarshal(result[ClaudeJSONPath], &config); err != nil {
+		t.Fatalf("Failed to parse result JSON: %v", err)
+	}
+	if _, hasMCP := config["mcpServers"]; hasMCP {
+		t.Error("SetMCPServers() with nil MCP should not add mcpServers")
+	}
+	if config["hasCompletedOnboarding"] != true {
+		t.Error("hasCompletedOnboarding was not preserved")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/agent/claude_test.go` around lines 499 - 516, The test
TestClaude_SetMCPServers_NilMCP should stop comparing raw JSON strings and
instead parse the returned JSON bytes from result[ClaudeJSONPath] (from the
SetMCPServers call on NewClaude) and assert the hasCompletedOnboarding field
value; update the test to json.Unmarshal the bytes into a map or struct and
check that hasCompletedOnboarding == true (and fail the test if unmarshal errors
or the field isn't the expected boolean).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/config/merger.go`:
- Around line 166-185: The mergeMCP logic must prevent same-name entries from
different MCP types (Commands vs Servers) surviving and later colliding in the
flattened mcpServers key: update mergeMCP (and corresponding logic used in the
187-255 region) to detect cross-type name conflicts by building a name->type map
that prefers entries from the override side (respecting
Agent>Project>Global>Workspace precedence) and then filter out or shadow entries
of the other type from the base; specifically, run conflict resolution after
calling mergeMCPCommands and mergeMCPServers (or merge earlier by merging name
maps in mergeMCP), ensuring names present in override commands remove any base
servers with the same name and names present in override servers remove any base
commands with the same name so the final result cannot contain both types with
the same name that would later write to the same mcpServers key.
- Around line 257-274: copyMCP currently performs shallow copies of the Commands
and Servers slices which still alias nested pointer fields
(workspace.McpCommand.Args, workspace.McpCommand.Env, and
workspace.McpServer.Headers); update copyMCP to deep-copy each command and
server instead of using copy(): iterate over *mcp.Commands and for each
workspace.McpCommand allocate a new workspace.McpCommand, copy primitive fields,
and deep-copy any pointer fields (allocate new slices/maps for Args and Env and
copy their contents), then append to cmdsCopy; do the same for *mcp.Servers and
deep-copy Headers into new allocations before assigning result.Commands and
result.Servers so the returned McpConfiguration has no shared mutable references
with the input.

---

Outside diff comments:
In `@pkg/agent/agent.go`:
- Around line 26-48: The Agent interface is missing the required SkillsDir()
string method; add SkillsDir() string to the Agent interface declaration
alongside existing methods (Name, SkipOnboarding, SetModel, SetMCPServers) so
implementations must return the container path (using $HOME) where skill
directories are mounted; update any implementing types (e.g., concrete agent
structs that implement Agent) to include a SkillsDir method returning the
appropriate path.

---

Nitpick comments:
In `@pkg/agent/claude_test.go`:
- Around line 499-516: The test TestClaude_SetMCPServers_NilMCP should stop
comparing raw JSON strings and instead parse the returned JSON bytes from
result[ClaudeJSONPath] (from the SetMCPServers call on NewClaude) and assert the
hasCompletedOnboarding field value; update the test to json.Unmarshal the bytes
into a map or struct and check that hasCompletedOnboarding == true (and fail the
test if unmarshal errors or the field isn't the expected boolean).

In `@pkg/instances/manager_test.go`:
- Around line 3552-3669: Add a third subtest to
TestManager_Add_AppliesAgentMCPServers that registers an agent whose
SetMCPServers(ctx, ...) method returns a controlled error and then calls
manager.Add(...) with a workspace MCP config; assert that Add returns a non-nil
error and that its error string wraps/includes the expected message from
SetMCPServers (e.g., "SetMCPServers" or the specific error text). Locate the
tracking agent implementation used in tests (newTrackingAgent or create a small
failingTrackingAgent) and make it return the error from SetMCPServers, call
manager.Add with WorkspaceConfig.Mcp set, and check the returned error matches
the wrapped message to ensure propagation.

In `@skills/working-with-config-system/SKILL.md`:
- Around line 72-82: Rewrite the paragraph in SKILL.md to improve flow by
combining or reordering the three consecutive sentences that start with "If":
reference the implementation steps involving manager.readAgentSettings, the
agent registry call to SkipOnboarding(), the conditional SetModel(modelID) call,
and the conditional SetMCPServers() call so they read as a cohesive sequence
rather than three standalone "If" sentences; keep the same facts (map returned
by readAgentSettings, model stored in InstanceData.Model, agent settings passed
via runtime.CreateParams.AgentSettings, Podman COPY instruction) and preserve
mention of where MCP servers are merged and how they are applied to agents
(SetMCPServers), but restructure into 2–3 varied-start sentences for better
readability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 77a0c914-0877-4d04-ba42-6df11c3faeea

📥 Commits

Reviewing files that changed from the base of the PR and between f26d505 and 8c7e151.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • AGENTS.md
  • README.md
  • go.mod
  • pkg/agent/agent.go
  • pkg/agent/claude.go
  • pkg/agent/claude_test.go
  • pkg/agent/cursor.go
  • pkg/agent/goose.go
  • pkg/agent/registry_test.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/config/merger.go
  • pkg/config/merger_test.go
  • pkg/instances/manager.go
  • pkg/instances/manager_test.go
  • skills/working-with-config-system/SKILL.md
  • skills/working-with-instances-manager/SKILL.md

Comment thread pkg/config/merger.go
Allows users to configure MCP servers (command-based and URL-based) in
workspace.json and in the multi-level config system (projects.json,
agents.json). The manager calls SetMCPServers() on the registered agent
after SetModel(), writing the merged MCP config into agent settings.

- Upgrade kdn-api/workspace-configuration/go to v0.2.0 which adds
  McpConfiguration, McpCommand, and McpServer types
- Add SetMCPServers() to the Agent interface; Claude writes command
  servers as type "stdio" and URL servers as type "sse" into the
  top-level mcpServers key of .claude.json (user scope); Goose and
  Cursor are no-ops
- Add MCP merge logic to ConfigMerger (merge by name, override wins)
- Add MCP validation: non-empty name/command/url required; names must
  be unique across both commands and servers combined since they are
  written to a single flat mcpServers map
- Wire SetMCPServers() into manager.Add() after SetModel()
- Add tests for all new functionality
- Update README.md, CLAUDE.md, and skills documentation

Closes openkaiden#210

Co-Authored-By: Claude Code (claude-sonnet-4-6) <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 89.94709% with 19 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/config/merger.go 90.67% 5 Missing and 6 partials ⚠️
pkg/agent/claude.go 95.34% 1 Missing and 1 partial ⚠️
pkg/agent/cursor.go 0.00% 2 Missing ⚠️
pkg/agent/goose.go 0.00% 2 Missing ⚠️
pkg/instances/manager.go 50.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
pkg/config/merger.go (2)

217-250: ⚠️ Potential issue | 🟡 Minor

Deep-copy nested MCP fields before returning merged configs.

These helpers move workspace.McpCommand and workspace.McpServer values around by assignment/copy(), but their nested Args, Env, and Headers members are still shared. That means the returned config can alias the inputs even though these helpers are documented as producing independent copies. Please confirm the v0.2.0 workspace types still use those pointer-bearing fields, then clone each entry deeply in both the merge helpers and copyMCP().

Also applies to: 252-304

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/config/merger.go` around lines 217 - 250, The merge helpers currently
copy workspace.McpCommand and workspace.McpServer by value but leave nested
pointer-bearing fields (Args, Env, Headers) shared; update mergeMCPCommands, the
corresponding mergeMCPServers (lines ~252-304), and copyMCP to perform deep
clones of each entry: allocate a fresh workspace.McpCommand/McpServer for each
merged item and clone slice/map fields (Args, Env, Headers, etc.) into new
slices/maps so no returned struct aliases the inputs; first verify the v0.2.0
workspace types still define those fields as pointer-bearing/compound types,
then implement the deep-copy logic in those three functions.

193-215: ⚠️ Potential issue | 🟠 Major

Resolve cross-type MCP name collisions during merge.

mergeMCP() currently merges commands and servers independently, so a lower-precedence server and a higher-precedence command with the same Name can both survive. Claude later flattens both lists into one mcpServers map, which lets the lower-precedence entry silently overwrite the higher-precedence one depending on write order. Filter same-name command/server clashes as part of the merge, with the override side winning in both directions. A regression test for server→command and command→server clashes would pin this.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/working-with-config-system/SKILL.md`:
- Around line 186-195: Clarify that MCP server names must be globally unique
across both lists: update the field descriptions for `mcp` so `commands[].name`
and `servers[].name` explicitly state the name must be unique across all MCP
entries (not just within their respective `commands` or `servers` lists),
mentioning that the loader flattens entries into a single `mcpServers` map; edit
the bullets for `commands` and `servers` to include this cross-list uniqueness
requirement and reference the `mcp` top-level field and `mcpServers` map so
readers know why duplicates are rejected.

---

Duplicate comments:
In `@pkg/config/merger.go`:
- Around line 217-250: The merge helpers currently copy workspace.McpCommand and
workspace.McpServer by value but leave nested pointer-bearing fields (Args, Env,
Headers) shared; update mergeMCPCommands, the corresponding mergeMCPServers
(lines ~252-304), and copyMCP to perform deep clones of each entry: allocate a
fresh workspace.McpCommand/McpServer for each merged item and clone slice/map
fields (Args, Env, Headers, etc.) into new slices/maps so no returned struct
aliases the inputs; first verify the v0.2.0 workspace types still define those
fields as pointer-bearing/compound types, then implement the deep-copy logic in
those three functions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 97a2a8ab-c8b8-476c-b03e-0b4323f80152

📥 Commits

Reviewing files that changed from the base of the PR and between 8c7e151 and eb3f3ee.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • AGENTS.md
  • README.md
  • go.mod
  • pkg/agent/agent.go
  • pkg/agent/claude.go
  • pkg/agent/claude_test.go
  • pkg/agent/cursor.go
  • pkg/agent/goose.go
  • pkg/agent/registry_test.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/config/merger.go
  • pkg/config/merger_test.go
  • pkg/instances/manager.go
  • pkg/instances/manager_test.go
  • skills/working-with-config-system/SKILL.md
  • skills/working-with-instances-manager/SKILL.md
✅ Files skipped from review due to trivial changes (4)
  • go.mod
  • AGENTS.md
  • pkg/agent/claude_test.go
  • README.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • pkg/agent/registry_test.go
  • pkg/instances/manager.go
  • pkg/config/config.go
  • skills/working-with-instances-manager/SKILL.md
  • pkg/config/merger_test.go
  • pkg/agent/claude.go
  • pkg/instances/manager_test.go

Comment thread skills/working-with-config-system/SKILL.md
Two bugs in the MCP merge logic identified in PR openkaiden#239 review:

- Cross-type name collision: mergeMCP merged commands and servers
  independently, so a base server and an override command with the
  same name both survived. When agents flatten both lists into a
  single mcpServers map the lower-precedence entry would silently
  overwrite the higher-precedence one. Fix filters the losing type
  after each independent merge using the override name sets.
- Shallow copy: mergeMCPCommands, mergeMCPServers, and copyMCP copied
  McpCommand/McpServer by value, leaving Args, Env, and Headers
  pointer fields aliasing the originals. Added deepCopyMcpCommand and
  deepCopyMcpServer helpers used at all three call sites.

Also clarifies in the config-system skill doc that MCP server names
must be unique across both commands and servers lists.

Co-Authored-By: Claude Code (claude-sonnet-4-6) <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
skills/working-with-config-system/SKILL.md (1)

149-153: Avoid normalizing inline auth tokens in repo-scoped examples.

workspace.json is presented here as shared project config, so an Authorization: Bearer ... example makes it easy for users to copy real credentials into source control. A clearly non-secret placeholder, or a note to put authenticated MCP entries in user-level config, would be safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/working-with-config-system/SKILL.md` around lines 149 - 153, The
example in SKILL.md shows a repo-scoped workspace.json entry with an inline
Authorization header containing a realistic bearer token; replace the inline
secret with a non-sensitive placeholder (e.g., "Bearer <TOKEN>") or move the
authenticated MCP entry into user-level config and add a short note telling
readers not to commit real credentials; update the example for the "remote-api"
object and add the brief guidance near the workspace.json snippet to avoid
encouraging committing secrets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/config/merger.go`:
- Around line 367-388: copyMCP currently returns a non-nil McpConfiguration even
when input has empty Commands/Servers; update copyMCP (which calls
deepCopyMcpCommand and deepCopyMcpServer) to normalize empty MCP configs to nil
by treating empty slices as absent: after copying, if both result.Commands and
result.Servers are nil or point to zero-length slices, return nil (or set them
to nil) so callers (e.g., mergedConfig.Mcp checks and SetMCPServers) behave the
same as mergeMCP for empty inputs.

In `@skills/working-with-config-system/SKILL.md`:
- Around line 81-83: The documentation fails to state that merging is name-based
across different types (commands vs servers) so a higher-precedence entry of one
type can remove a lower-precedence entry of the other type if they share the
same name; update the SKILL.md text that describes manager.readAgentSettings and
the merge behavior to explicitly say that pkg/config/merger.go performs
name-based merging across both command and server lists (i.e., entries with the
same name, regardless of type, are overridden by higher-precedence entries), and
mirror this clarification where the doc mentions MCP/server merging and the
runtime steps (including references to SetMCPServers, SetModel, SkipOnboarding)
so readers understand cross-type overrides.

---

Nitpick comments:
In `@skills/working-with-config-system/SKILL.md`:
- Around line 149-153: The example in SKILL.md shows a repo-scoped
workspace.json entry with an inline Authorization header containing a realistic
bearer token; replace the inline secret with a non-sensitive placeholder (e.g.,
"Bearer <TOKEN>") or move the authenticated MCP entry into user-level config and
add a short note telling readers not to commit real credentials; update the
example for the "remote-api" object and add the brief guidance near the
workspace.json snippet to avoid encouraging committing secrets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3262b202-929a-4815-b459-5493cd73785a

📥 Commits

Reviewing files that changed from the base of the PR and between eb3f3ee and 0b4c128.

📒 Files selected for processing (3)
  • pkg/config/merger.go
  • pkg/config/merger_test.go
  • skills/working-with-config-system/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/config/merger_test.go

Comment thread pkg/config/merger.go
Comment on lines +81 to +83
MCP servers from all configuration levels are merged before being passed to the agent, with higher-precedence levels overriding lower ones by server `name`.

**Implementation:** `manager.readAgentSettings(storageDir, agentName)` in `pkg/instances/manager.go` walks this directory and returns a `map[string][]byte` (relative forward-slash path → content). If the agent is registered in the agent registry, the manager calls the agent's `SkipOnboarding()` method to modify the settings. If a model ID is provided, the manager then calls the agent's `SetModel()` method. If the merged config contains MCP servers, the manager calls the agent's `SetMCPServers()` method. The final map is passed to the runtime via `runtime.CreateParams.AgentSettings`. The Podman runtime writes the files into the build context and adds a `COPY --chown=agent:agent agent-settings/. /home/agent/` instruction to the Containerfile. The model is also stored directly in the `instance` struct and persisted in `instances.json` via `InstanceData.Model`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document cross-type overrides explicitly.

These sections still read like commands and servers merge independently by type. In pkg/config/merger.go, Lines 193-258 also remove a lower-precedence command when a higher-precedence server reuses the same name, and vice versa, so the docs should describe this as name-based merging across both lists.

Also applies to: 345-347

🧰 Tools
🪛 LanguageTool

[style] ~83-~83: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... calls the agent's SetModel() method. If the merged config contains MCP servers,...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/working-with-config-system/SKILL.md` around lines 81 - 83, The
documentation fails to state that merging is name-based across different types
(commands vs servers) so a higher-precedence entry of one type can remove a
lower-precedence entry of the other type if they share the same name; update the
SKILL.md text that describes manager.readAgentSettings and the merge behavior to
explicitly say that pkg/config/merger.go performs name-based merging across both
command and server lists (i.e., entries with the same name, regardless of type,
are overridden by higher-precedence entries), and mirror this clarification
where the doc mentions MCP/server merging and the runtime steps (including
references to SetMCPServers, SetModel, SkipOnboarding) so readers understand
cross-type overrides.

Signed-off-by: Philippe Martin <phmartin@redhat.com>
@feloy feloy merged commit 54ec3a9 into openkaiden:main Apr 10, 2026
8 checks passed
MarsKubeX added a commit to MarsKubeX/kortex-cli that referenced this pull request Apr 13, 2026
The Agent interface now requires SetMCPServers after openkaiden#239 was merged.
Add a no-op implementation (like goose/cursor) since OpenCode does not
yet support MCP configuration through agent settings files.

Closes openkaiden#213

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>
MarsKubeX added a commit that referenced this pull request Apr 13, 2026
* feat(agent): add OpenCode agent with Podman runtime support

Add OpenCode as a supported AI agent with default Podman runtime
configuration, automatic onboarding (SkipOnboarding, SetModel), and
agent registration. The installer places the binary in ~/.opencode/bin/
which is outside the container's ENV PATH, so the default config
symlinks it into ~/.local/bin/.

Closes #213

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>

* feat(agent): add no-op SetMCPServers to OpenCode agent

The Agent interface now requires SetMCPServers after #239 was merged.
Add a no-op implementation (like goose/cursor) since OpenCode does not
yet support MCP configuration through agent settings files.

Closes #213

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>

* docs: add OpenCode agent to project documentation

Update README, AGENTS.md, CLI help examples, and skill guides to
include OpenCode as a supported agent alongside Claude, Goose, and
Cursor.

Closes #213

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>

---------

Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

configure mcp servers

2 participants