This guide covers the technical aspects of developing the MCI CLI tool, including architecture, internal components, and contribution workflows.
- Setting Up Development Environment
- Project Architecture
- Core Components
- Development Workflows
- Testing
- Documentation
This project uses uv to manage Python and dependencies.
-
Install uv:
curl -LsSf https://astral.sh/uv/install.sh | sh -
Fork and clone the repository:
-
Install dependencies:
make install # or uv sync --all-extras
src/mci/
├── __init__.py # Package initialization, exports main CLI entry point
├── mci.py # Main CLI entry point with Click group
├── cli/ # CLI command modules
│ ├── __init__.py
│ ├── install.py # mcix install command
│ ├── list.py # mcix list command
│ ├── validate.py # mcix validate command
│ ├── add.py # mcix add command
│ ├── run.py # mcix run command
│ └── formatters/ # Output formatters (JSON, YAML, table)
├── core/ # Core business logic
│ ├── __init__.py
│ ├── config.py # Configuration loading and management
│ ├── file_finder.py # MCI file discovery utilities
│ ├── mci_client.py # MCIClient wrapper for CLI integration
│ ├── mcp_server.py # MCP server creation infrastructure
│ ├── dynamic_server.py # Dynamic MCP server from MCI schemas
│ ├── tool_converter.py # MCI to MCP tool conversion
│ ├── tool_manager.py # Tool filtering and management
│ ├── schema_editor.py # Schema editing utilities
│ └── validator.py # Schema validation
└── utils/ # Utility functions
├── __init__.py
├── error_handler.py # Error formatting for CLI
├── error_formatter.py # Rich-based error display
├── validation.py # File and path validation
└── timestamp.py # Timestamp utilities
The main entry point is the main() function in src/mci/mci.py, which is exported from the package root. This function is a Click command group that serves as the foundation for all CLI commands.
The MCIFileFinder class provides methods to locate MCI configuration files:
from mci.core.file_finder import MCIFileFinder
finder = MCIFileFinder()
config_file = finder.find_mci_file("./my_project")File Priority: When both mci.json and mci.yaml exist, JSON format is prioritized.
Supported Formats: mci.json, mci.yaml, mci.yml
The MCIConfig class uses MCIClient from mci-py to load and validate MCI configuration files:
from mci.core.config import MCIConfig
config = MCIConfig()
client = config.load("mci.json")
tools = client.tools()Environment Variables: Support for environment variable substitution in MCI schemas:
env_vars = {
"API_KEY": "your-api-key",
"BASE_URL": "https://api.example.com"
}
client = config.load("mci.json", env_vars)All tool loading, filtering, and schema operations are delegated to MCIClient from mci-py, ensuring consistency with the upstream adapter.
The MCIClientWrapper class provides a CLI-friendly interface to MCIClient:
from mci.core.mci_client import MCIClientWrapper
wrapper = MCIClientWrapper("mci.json")
tools = wrapper.get_tools()
# Filter tools
api_tools = wrapper.filter_tags(["api"])
safe_tools = wrapper.filter_except(["deprecated_tool"])
specific_tools = wrapper.filter_only(["tool1", "tool2"])The ToolManager class parses CLI filter specifications and applies filters:
from mci.core.tool_manager import ToolManager
api_tools = ToolManager.apply_filter_spec(wrapper, "tags:api,database")
non_deprecated = ToolManager.apply_filter_spec(wrapper, "without-tags:deprecated")
selected_tools = ToolManager.apply_filter_spec(wrapper, "only:tool1,tool2")Supported Filter Types:
only:tool1,tool2,...- Include only specified tools by nameexcept:tool1,tool2,...- Exclude specified tools by nametags:tag1,tag2,...- Include tools with any of these tags (OR logic)without-tags:tag1,tag2,...- Exclude tools with any of these tagstoolsets:toolset1,toolset2,...- Include tools from specified toolsets
The MCI CLI provides infrastructure for creating MCP servers that dynamically serve tools from MCI schemas.
- MCIToolConverter: Converts MCI tool definitions to MCP tool format
- MCPServerBuilder: Creates and configures MCP servers with MCI tools
- ServerInstance: Manages server lifecycle and delegates execution to MCIClient
- DynamicMCPServer: High-level interface for creating and running servers
from mci.core.dynamic_server import DynamicMCPServer
# Create and run server
server = DynamicMCPServer("mci.json", filter_spec="tags:api")
await server.create_from_mci_schema()
await server.start_stdio()MCI Schema → MCIClient → MCPServerBuilder → MCP Server → STDIO
↓ ↓ ↓
Tools loaded Converted Tool execution
from schema to MCP format via MCIClient
Formats MCIClientError exceptions for CLI display:
from mci.utils.error_handler import ErrorHandler
from mcipy import MCIClientError
try:
wrapper = MCIClientWrapper("nonexistent.mci.json")
except MCIClientError as e:
formatted = ErrorHandler.format_mci_client_error(e)
print(formatted)Provides Rich-based formatting for validation errors and warnings:
from mci.utils.error_formatter import ErrorFormatter, ValidationError
formatter = ErrorFormatter()
errors = [ValidationError(message="Missing required field: name")]
formatter.format_validation_errors(errors)The Makefile provides shortcuts to uv commands for developer convenience.
# Install all dependencies
make install
# Run sync, lint, and test
make
# Build wheel
make build
# Run linters
make lint
# Run tests
make test
# Run tests with coverage
make coverage
# Clean build artifacts
make clean
# Upgrade dependencies
make upgrade# All tests
uv run pytest
# Specific test file with output
uv run pytest -s tests/test_file.py
# With coverage
uv run pytest --cov=src/mci --cov-report=term-missing
# Single test
uv run pytest tests/test_file.py::test_function_name# Run all linters
make lint
# Individual linters
uv run ruff check --fix src/
uv run ruff format src/
uv run basedpyright --stats src/# Add dependency
uv add package_name
# Add dev dependency
uv add --dev package_name
# Update to latest compatible versions
uv sync --upgrade
# Update specific package
uv lock --upgrade-package package_name# Install as editable tool
uv tool install --editable .
# Now you can use `mci` command globally with your dev changes
mci --helptests/
├── test_*.py # Feature tests
├── unit/ # Unit tests
│ ├── cli/ # CLI command tests
│ ├── core/ # Core logic tests
│ └── utils/ # Utility tests
└── testsManual/ # Manual integration tests
- Target: 90%+ test coverage across all modules
- Current: 90% (Stage 10 achievement)
Test individual functions in isolation:
def test_is_valid_path_with_valid_path():
"""Test is_valid_path with a valid path."""
assert is_valid_path("./mci.json") is TrueTest complete features end-to-end:
def test_list_command_basic(runner):
"""Test basic list command."""
result = runner.invoke(cli, ["list"])
assert result.exit_code == 0
assert "Available Tools" in result.outputLarge integration tests run individually:
uv run python testsManual/test_mcp_server.pyEvery file should have a docstring explaining:
- Purpose of the file
- What functionality it provides
- How it fits into the project
Example:
"""
mci_client.py - MCIClient wrapper for CLI integration
This module provides a CLI-friendly wrapper around MCIClient from mci-py,
handling tool loading, filtering, and error formatting for command-line usage.
"""Every function and class should have a docstring with:
- What it does
- Why it exists
- Important implementation details
- Parameters and return values
Example:
def apply_filter_spec(wrapper: MCIClientWrapper, filter_spec: str) -> list:
"""
Apply a filter specification to tools from MCIClient wrapper.
Parses filter specifications like "tags:api,database" and applies
the appropriate filtering method from MCIClient. Supports all MCI
filter types: only, except, tags, without-tags, toolsets.
Args:
wrapper: MCIClientWrapper instance with loaded tools
filter_spec: Filter specification string
Returns:
List of filtered tools
Raises:
ValueError: If filter specification is invalid
"""- README.md - User-focused quickstart and usage guide
- development.md - This file - technical development guide
- PLAN.md - Full implementation plan and roadmap
- PRD.md - Product requirements and specifications
- installation.md - Detailed installation instructions
- publishing.md - PyPI publishing guide
- mcp-server-docs.md - Advanced MCP server features
For VSCode, Cursor, or similar editors:
See .cursor/rules for agent rules. Generate aggregated rules:
make agent-rulesThis creates CLAUDE.md and AGENTS.md from the cursor rules.
- Fork and clone the repository
- Create a branch for your feature/fix
- Make changes following the code style
- Write tests for your changes
- Run linters and tests:
make lint make test make coverage - Commit with clear messages
- Push to your fork
- Open a pull request
- Use modern Python 3.11+ features
- Type annotations for all functions
- Docstrings for all public APIs
- Follow existing code patterns
- Keep comments concise and explanatory
Use clear, descriptive commit messages:
Add support for YAML output in list command
- Implement YAMLFormatter class
- Add --format yaml option
- Update tests for new format
- Document in README
This file was built with simple-modern-uv.