-
Notifications
You must be signed in to change notification settings - Fork 117
feat: add Kiro client manager #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| """ | ||
| 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", | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| """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 | ||
| from mcpm.core.schema import STDIOServerConfig | ||
|
|
||
|
|
||
| @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""" | ||
| server_config = STDIOServerConfig( | ||
| name="test-server", | ||
| command="npx", | ||
| args=["-y", "@modelcontextprotocol/server-test"], | ||
| ) | ||
| success = kiro_manager.add_server(server_config) | ||
| 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""" | ||
| server_config = STDIOServerConfig( | ||
| name="test-server", | ||
| command="npx", | ||
| args=[], | ||
| ) | ||
| kiro_manager.add_server(server_config) | ||
| 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": {}} | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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