Skip to content

feat: add Kiro client manager#328

Open
fyodoriv wants to merge 1 commit intopathintegral-institute:mainfrom
fyodoriv:feat/kiro-adapter
Open

feat: add Kiro client manager#328
fyodoriv wants to merge 1 commit intopathintegral-institute:mainfrom
fyodoriv:feat/kiro-adapter

Conversation

@fyodoriv
Copy link
Copy Markdown

@fyodoriv fyodoriv commented Apr 28, 2026

Kiro is a coding agent IDE. Its MCP configuration lives at ~/.kiro/settings/mcp.json and uses the standard mcpServers top-level key — same JSON shape as Cursor, Cline, Claude Code, and Windsurf.

This PR adds a KiroManager extending JSONClientManager so mcpm install <name> and mcpm client edit kiro --add-server <name> work end-to-end against Kiro's config file. Net change: ~70 LOC of source + ~80 LOC of tests across 3 files.

Why

Kiro is on agentbrew's agent matrix (45+ AI coding agents tracked there) and one of the 9 strict-intersection clients in agentbrew's MCP delegation evaluation. Adding it here closes one of the 3 carve-outs (kiro, amp, opencode) called out in that doc — opencode landed via #327, this PR addresses kiro, and a sibling PR will address amp.

Source-code references

  • Adds: src/mcpm/clients/managers/kiro.py (new file)
  • Modifies: src/mcpm/clients/managers/__init__.py (re-export KiroManager)
  • Modifies: src/mcpm/clients/client_registry.py (register "kiro": KiroManager)
  • Adds: tests/test_clients/test_kiro.py (new file, mirrors tests/test_clients/test_qwen_cli.py shape)

Diff

src/mcpm/clients/managers/kiro.py (new)

"""
Kiro IDE integration utilities for MCP
"""

import logging
import os
import shutil
from typing import Any, Dict

from mcpm.clients.base import JSONClientManager

logger = logging.getLogger(__name__)


class KiroManager(JSONClientManager):
    """Manages Kiro IDE MCP server configurations"""

    # Client information
    client_key = "kiro"
    display_name = "Kiro"
    download_url = "https://kiro.dev"

    def __init__(self, config_path_override: str | None = None):
        """Initialize the Kiro client manager

        Args:
            config_path_override: Optional path to override the default config file location
        """
        super().__init__(config_path_override=config_path_override)

        if config_path_override:
            self.config_path = config_path_override
        else:
            # Kiro stores its MCP settings in ~/.kiro/settings/mcp.json
            # across macOS, Linux, and Windows (per Kiro's docs at
            # https://kiro.dev/docs/mcp).
            self.config_path = os.path.expanduser("~/.kiro/settings/mcp.json")

    def _get_empty_config(self) -> Dict[str, Any]:
        """Get empty config structure for Kiro"""
        return {self.configure_key_name: {}}

    def is_client_installed(self) -> bool:
        """Check if Kiro is installed
        Returns:
            bool: True if kiro command is available, False otherwise
        """
        # shutil.which() handles Windows PATHEXT automatically (.cmd, .bat, .exe, etc.)
        return shutil.which("kiro") is not None

    def get_client_info(self) -> Dict[str, str]:
        """Get information about this client

        Returns:
            Dict: Information about the client including display name, download URL, and config path
        """
        return {
            "name": self.display_name,
            "download_url": self.download_url,
            "config_file": self.config_path,
            "description": "Kiro coding agent IDE",
        }

src/mcpm/clients/managers/__init__.py (diff)

 from mcpm.clients.managers.gemini_cli import GeminiCliManager
 from mcpm.clients.managers.goose import GooseClientManager
+from mcpm.clients.managers.kiro import KiroManager
 from mcpm.clients.managers.qwen_cli import QwenCliManager
 from mcpm.clients.managers.trae import TraeManager

 __all__ = [
     "ClaudeCodeManager",
     "ClaudeDesktopManager",
     "CursorManager",
     "WindsurfManager",
     "ClineManager",
     "ContinueManager",
     "FiveireManager",
     "GooseClientManager",
+    "KiroManager",
     "QwenCliManager",
     "TraeManager",
     "VSCodeManager",
     "GeminiCliManager",
     "CodexCliManager",
 ]

src/mcpm/clients/client_registry.py (diff)

 from mcpm.clients.managers.gemini_cli import GeminiCliManager
 from mcpm.clients.managers.goose import GooseClientManager
+from mcpm.clients.managers.kiro import KiroManager
 from mcpm.clients.managers.qwen_cli import QwenCliManager
 from mcpm.clients.managers.trae import TraeManager
@@ -49,6 +50,7 @@
         "vscode": VSCodeManager,
         "gemini-cli": GeminiCliManager,
         "codex-cli": CodexCliManager,
+        "kiro": KiroManager,
         "qwen-cli": QwenCliManager,
     }

tests/test_clients/test_kiro.py (new)

"""Tests for the Kiro client manager."""

import json
import os
import tempfile
from unittest.mock import patch

import pytest

from mcpm.clients.managers.kiro import KiroManager


@pytest.fixture
def temp_json_config():
    with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f:
        json.dump({"mcpServers": {}}, f)
        temp_path = f.name

    yield temp_path
    os.unlink(temp_path)


@pytest.fixture
def kiro_manager(temp_json_config):
    return KiroManager(config_path_override=temp_json_config)


def test_default_config_path():
    """Test that the default config path is ~/.kiro/settings/mcp.json"""
    with patch.dict(os.environ, {"HOME": "/home/user"}, clear=False):
        manager = KiroManager()
        assert manager.config_path.endswith(".kiro/settings/mcp.json")


def test_config_path_override(temp_json_config):
    """Test that config_path_override takes precedence over the default path"""
    manager = KiroManager(config_path_override=temp_json_config)
    assert manager.config_path == temp_json_config


def test_get_empty_config(kiro_manager):
    """Test that empty config returns the standard mcpServers shape"""
    empty = kiro_manager._get_empty_config()
    assert empty == {"mcpServers": {}}


def test_uses_standard_mcpServers_key():
    """Test that Kiro uses the standard 'mcpServers' key (no override)"""
    assert KiroManager.configure_key_name == "mcpServers"


def test_get_client_info(kiro_manager):
    info = kiro_manager.get_client_info()
    assert info["name"] == "Kiro"
    assert info["download_url"] == "https://kiro.dev"
    assert "kiro" in info["description"].lower()
    assert "config_file" in info


def test_is_client_installed_when_kiro_on_path(kiro_manager):
    """Test that is_client_installed returns True when kiro binary is on PATH"""
    with patch("mcpm.clients.managers.kiro.shutil.which", return_value="/usr/local/bin/kiro"):
        assert kiro_manager.is_client_installed() is True


def test_is_client_installed_when_kiro_missing(kiro_manager):
    """Test that is_client_installed returns False when kiro binary is missing"""
    with patch("mcpm.clients.managers.kiro.shutil.which", return_value=None):
        assert kiro_manager.is_client_installed() is False


def test_add_and_list_server(kiro_manager):
    """Test the add_server / list_servers / get_server lifecycle"""
    success = kiro_manager.add_server(
        {
            "name": "test-server",
            "type": "stdio",
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-test"],
        },
        "test-server",
    )
    assert success

    servers = kiro_manager.list_servers()
    assert "test-server" in servers

    server = kiro_manager.get_server("test-server")
    assert server is not None
    assert server.name == "test-server"


def test_remove_server(kiro_manager):
    """Test the remove_server lifecycle"""
    kiro_manager.add_server(
        {"name": "test-server", "type": "stdio", "command": "npx"},
        "test-server",
    )
    assert kiro_manager.remove_server("test-server") is True
    assert kiro_manager.get_server("test-server") is None


def test_load_config_returns_empty_when_file_missing():
    """Test that _load_config returns empty config shape when file doesn't exist"""
    with tempfile.TemporaryDirectory() as tmpdir:
        missing_path = os.path.join(tmpdir, "nonexistent.json")
        manager = KiroManager(config_path_override=missing_path)
        config = manager._load_config()
        assert config == {"mcpServers": {}}

Why it matters

Once this lands, agentbrew (and any other downstream tool that delegates to mcpm) can call mcpm install <server> --client kiro and mcpm client edit kiro --add-server <server> end-to-end. Today agentbrew keeps a native carve-out for kiro because mcpm doesn't have an adapter — that ~80 LOC carve-out becomes deletable on merge.

The harm of not landing this is bounded (kiro can still be configured manually), but the fix is mechanical, the test surface is small, and it parallels the open opencode adapter PR (#327).

Summary by CodeRabbit

  • New Features

    • Added support for the Kiro coding agent IDE as a supported MCP client within the application.
    • The system now detects and manages Kiro client configurations automatically alongside existing clients.
    • Kiro configuration files are identified and handled through the standard client management system.
  • Tests

    • Added comprehensive test suite for Kiro client functionality including configuration management and server operations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This pull request introduces support for a new Kiro client manager to the MCPM library. It adds a KiroManager class that manages MCP configuration for the Kiro IDE, registers it within the existing client registry system, and includes comprehensive test coverage for the new manager.

Changes

Cohort / File(s) Summary
Client Registry Integration
src/mcpm/clients/client_registry.py, src/mcpm/clients/managers/__init__.py
Added KiroManager import and registration in the client registry mapping and public exports, enabling the manager to be discovered and accessed through the existing client infrastructure.
Kiro Manager Implementation
src/mcpm/clients/managers/kiro.py
New KiroManager class that extends JSONClientManager to handle MCP configuration for Kiro. Includes config path resolution (~/.kiro/settings/mcp.json), installation detection via shutil.which, and client metadata (name, download URL, description).
Test Suite
tests/test_clients/test_kiro.py
Comprehensive test coverage for KiroManager including config path handling, empty config generation, installation detection mocking, server management operations (add/list/remove), and client info validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop, hop, hooray! A Kiro arrives,
New managers help the registry thrive,
Config paths whisper ~/.kiro/settings/mcp,
Tests scurry through logic—not a single misstep!
Another fine client joins the warren's embrace! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Kiro client manager' directly and accurately summarizes the main change—introducing a new KiroManager client manager for the Kiro IDE to the mcpm system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add Kiro IDE client manager for MCP integration

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Adds Kiro IDE client manager for MCP server configuration
• Extends JSONClientManager with Kiro-specific config path handling
• Registers KiroManager in client registry for CLI integration
• Includes comprehensive test suite covering manager lifecycle
Diagram
flowchart LR
  A["KiroManager<br/>extends JSONClientManager"] -- "config at" --> B["~/.kiro/settings/mcp.json"]
  C["ClientRegistry"] -- "registers" --> A
  D["mcpm install"] -- "uses" --> A
  E["mcpm client edit"] -- "uses" --> A
  A -- "manages" --> F["mcpServers config"]
Loading

Grey Divider

File Changes

1. src/mcpm/clients/managers/kiro.py ✨ Enhancement +62/-0

New Kiro IDE client manager implementation

• New KiroManager class extending JSONClientManager
• Implements config path resolution to ~/.kiro/settings/mcp.json
• Provides installation detection via shutil.which for kiro binary
• Implements client info retrieval with name, URL, and description

src/mcpm/clients/managers/kiro.py


2. src/mcpm/clients/managers/__init__.py ✨ Enhancement +2/-0

Export KiroManager from managers package

• Imports KiroManager from kiro module
• Exports KiroManager in __all__ list for public API

src/mcpm/clients/managers/init.py


3. src/mcpm/clients/client_registry.py ✨ Enhancement +2/-0

Register Kiro in client registry

• Imports KiroManager from managers.kiro module
• Registers "kiro" key mapping to KiroManager in CLIENT_MANAGERS dict

src/mcpm/clients/client_registry.py


View more (1)
4. tests/test_clients/test_kiro.py 🧪 Tests +109/-0

Complete test suite for KiroManager

• Comprehensive test suite with 11 test cases covering KiroManager
• Tests default and override config paths, empty config structure
• Tests client installation detection with mocked shutil.which
• Tests server lifecycle operations (add, list, get, remove)
• Tests missing config file handling

tests/test_clients/test_kiro.py


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 28, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0)

Grey Divider


Remediation recommended

1. False-negative install detection 🐞 Bug ☼ Reliability
Description
KiroManager.is_client_installed() requires a kiro executable on PATH, even though all manager
operations are purely config-file based; this can cause
ClientRegistry.detect_installed_clients()/get_recommended_client() to incorrectly treat an
installed/configured Kiro as not installed. As a result, Kiro may never be recommended/detected
despite being usable for config edits.
Code

src/mcpm/clients/managers/kiro.py[R43-49]

+    def is_client_installed(self) -> bool:
+        """Check if Kiro is installed
+        Returns:
+            bool: True if kiro command is available, False otherwise
+        """
+        # shutil.which() handles Windows PATHEXT automatically (.cmd, .bat, .exe, etc.)
+        return shutil.which("kiro") is not None
Evidence
KiroManager overrides installation detection to shutil.which('kiro'), while the registry uses
is_client_installed() results to detect installed clients and choose a recommended client. The
JSONClientManager functionality used by Kiro (add/list/get/remove) only reads/writes the config file
and does not invoke any client binary, and comparable IDE managers (e.g., Cursor) do not require a
binary check for installation detection.

src/mcpm/clients/managers/kiro.py[43-49]
src/mcpm/clients/client_registry.py[89-97]
src/mcpm/clients/client_registry.py[125-139]
src/mcpm/clients/base.py[263-283]
src/mcpm/clients/base.py[371-379]
src/mcpm/clients/managers/cursor.py[14-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`KiroManager.is_client_installed()` currently returns `True` only if a `kiro` executable is discoverable via `PATH`. However, all Kiro manager operations in this repo are config-file based (read/write `mcp.json`) and do not require a binary. This can cause false negatives in `ClientRegistry.detect_installed_clients()` and `get_recommended_client()`.

### Issue Context
Other GUI/file-based IDE managers (e.g. Cursor) rely on the default `JSONClientManager.is_client_installed()` behavior (config-dir existence) rather than a binary on PATH.

### Fix Focus Areas
- src/mcpm/clients/managers/kiro.py[43-49]

### Proposed fix
Update `is_client_installed()` to detect Kiro based on the config location (e.g., `os.path.isdir(os.path.dirname(self.config_path))` or `os.path.exists(self.config_path)`), optionally OR-ing with `shutil.which('kiro')` if you want to keep the PATH check as an additional signal.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

@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

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

Inline comments:
In `@tests/test_clients/test_kiro.py`:
- Around line 29-34: The test test_default_config_path hardcodes forward slashes
and may fail on Windows; update it to normalize both the manager.config_path and
the expected string before asserting (e.g., use os.path.normpath or pathlib.Path
to build/normalize the expected ".kiro/settings/mcp.json" path) so the assertion
compares platform-normalized paths when calling KiroManager() and checking
manager.config_path.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa4f3015-59a0-4949-a0c2-cd6fded65a44

📥 Commits

Reviewing files that changed from the base of the PR and between bdd5ed9 and 2004dcf.

📒 Files selected for processing (4)
  • src/mcpm/clients/client_registry.py
  • src/mcpm/clients/managers/__init__.py
  • src/mcpm/clients/managers/kiro.py
  • tests/test_clients/test_kiro.py

Comment on lines +29 to +34
def test_default_config_path():
"""Test that the default config path is ~/.kiro/settings/mcp.json"""
with patch.dict(os.environ, {"HOME": "/home/user"}, clear=False):
manager = KiroManager()
assert manager.config_path.endswith(".kiro/settings/mcp.json")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Normalize the default-path assertion for cross-platform test stability.

Line 33 hardcodes / separators, which can fail on Windows even when the path is correct. Normalize both sides before asserting.

✅ Proposed test fix
 def test_default_config_path():
     """Test that the default config path is ~/.kiro/settings/mcp.json"""
     with patch.dict(os.environ, {"HOME": "/home/user"}, clear=False):
         manager = KiroManager()
-        assert manager.config_path.endswith(".kiro/settings/mcp.json")
+        assert os.path.normpath(manager.config_path).endswith(
+            os.path.normpath(".kiro/settings/mcp.json")
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_clients/test_kiro.py` around lines 29 - 34, The test
test_default_config_path hardcodes forward slashes and may fail on Windows;
update it to normalize both the manager.config_path and the expected string
before asserting (e.g., use os.path.normpath or pathlib.Path to build/normalize
the expected ".kiro/settings/mcp.json" path) so the assertion compares
platform-normalized paths when calling KiroManager() and checking
manager.config_path.

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.

1 participant