iflow-mcp_modelcontextinterface-mcix 1.1.1.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/METADATA +931 -0
- iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/RECORD +42 -0
- iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/WHEEL +4 -0
- iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/entry_points.txt +2 -0
- iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/licenses/LICENSE +21 -0
- mci/__init__.py +10 -0
- mci/assets/example_toolset.mci.json +37 -0
- mci/assets/example_toolset.mci.yaml +23 -0
- mci/assets/gitignore +1 -0
- mci/assets/mci.json +29 -0
- mci/assets/mci.yaml +19 -0
- mci/cli/__init__.py +8 -0
- mci/cli/add.py +108 -0
- mci/cli/envs.py +257 -0
- mci/cli/formatters/__init__.py +12 -0
- mci/cli/formatters/env_formatter.py +83 -0
- mci/cli/formatters/json_formatter.py +93 -0
- mci/cli/formatters/table_formatter.py +138 -0
- mci/cli/formatters/yaml_formatter.py +93 -0
- mci/cli/install.py +147 -0
- mci/cli/list.py +153 -0
- mci/cli/run.py +125 -0
- mci/cli/validate.py +113 -0
- mci/core/__init__.py +8 -0
- mci/core/config.py +144 -0
- mci/core/dynamic_server.py +187 -0
- mci/core/file_finder.py +105 -0
- mci/core/mci_client.py +196 -0
- mci/core/mcp_server.py +240 -0
- mci/core/schema_editor.py +284 -0
- mci/core/tool_converter.py +119 -0
- mci/core/tool_manager.py +118 -0
- mci/core/validator.py +162 -0
- mci/mci.py +39 -0
- mci/py.typed +0 -0
- mci/utils/__init__.py +8 -0
- mci/utils/dotenv.py +170 -0
- mci/utils/env_scanner.py +84 -0
- mci/utils/error_formatter.py +165 -0
- mci/utils/error_handler.py +174 -0
- mci/utils/timestamp.py +50 -0
- mci/utils/validation.py +92 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
json_formatter.py - JSON formatter for file output
|
|
3
|
+
|
|
4
|
+
This module provides formatting for outputting tools to JSON files
|
|
5
|
+
with metadata including timestamp, source file, filters, and tool count.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from mcipy.models import Tool
|
|
12
|
+
|
|
13
|
+
from mci.utils.timestamp import generate_timestamp_filename, get_iso_timestamp
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JSONFormatter:
|
|
17
|
+
"""
|
|
18
|
+
Formats tool information as JSON files with metadata.
|
|
19
|
+
|
|
20
|
+
This class provides methods to format tool lists into JSON files
|
|
21
|
+
with timestamp, source file, filters applied, and total count.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def format_to_file(
|
|
26
|
+
tools: list[Tool],
|
|
27
|
+
mci_file: str,
|
|
28
|
+
filters_applied: list[str] | None = None,
|
|
29
|
+
verbose: bool = False,
|
|
30
|
+
) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Format tools to a JSON file and return the filename.
|
|
33
|
+
|
|
34
|
+
Creates a timestamped JSON file with tool data and metadata.
|
|
35
|
+
Filename format: tools_YYYYMMDD_HHMMSS.json
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
tools: List of Tool objects to format
|
|
39
|
+
mci_file: Path to the source MCI file
|
|
40
|
+
filters_applied: Optional list of filter specifications that were applied
|
|
41
|
+
verbose: Whether to include verbose tool metadata
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The filename of the created JSON file
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> formatter = JSONFormatter()
|
|
48
|
+
>>> filename = formatter.format_to_file(tools, "mci.json", ["tags:api"])
|
|
49
|
+
>>> print(filename)
|
|
50
|
+
tools_20241029_143022.json
|
|
51
|
+
"""
|
|
52
|
+
# Generate timestamped filename
|
|
53
|
+
filename = generate_timestamp_filename("json")
|
|
54
|
+
|
|
55
|
+
# Build output data structure
|
|
56
|
+
output_data: dict[str, Any] = {
|
|
57
|
+
"timestamp": get_iso_timestamp(),
|
|
58
|
+
"mci_file": mci_file,
|
|
59
|
+
"filters_applied": filters_applied or [],
|
|
60
|
+
"total": len(tools),
|
|
61
|
+
"tools": [],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Format each tool
|
|
65
|
+
for tool in tools:
|
|
66
|
+
tool_data: dict[str, str | list[str] | dict[str, Any] | bool] = {
|
|
67
|
+
"name": tool.name,
|
|
68
|
+
"source": tool.toolset_source or "main",
|
|
69
|
+
"description": tool.description or "",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Add verbose fields if requested
|
|
73
|
+
if verbose:
|
|
74
|
+
tool_data["tags"] = tool.tags
|
|
75
|
+
tool_data["execution_type"] = (
|
|
76
|
+
tool.execution.type.value
|
|
77
|
+
if hasattr(tool.execution.type, "value")
|
|
78
|
+
else str(tool.execution.type)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if tool.inputSchema:
|
|
82
|
+
tool_data["inputSchema"] = tool.inputSchema
|
|
83
|
+
|
|
84
|
+
if tool.disabled:
|
|
85
|
+
tool_data["disabled"] = tool.disabled
|
|
86
|
+
|
|
87
|
+
output_data["tools"].append(tool_data)
|
|
88
|
+
|
|
89
|
+
# Write to file
|
|
90
|
+
with open(filename, "w") as f:
|
|
91
|
+
json.dump(output_data, f, indent=2)
|
|
92
|
+
|
|
93
|
+
return filename
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
table_formatter.py - Rich table formatter for CLI output
|
|
3
|
+
|
|
4
|
+
This module provides formatting for displaying tools in Rich terminal tables
|
|
5
|
+
with support for both basic and verbose output modes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from mcipy.models import Tool
|
|
9
|
+
from rich.markup import escape
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TableFormatter:
|
|
14
|
+
"""
|
|
15
|
+
Formats tool information as Rich terminal tables.
|
|
16
|
+
|
|
17
|
+
This class provides methods to format tool lists into beautiful
|
|
18
|
+
terminal tables using the Rich library, with support for both
|
|
19
|
+
basic and verbose output modes.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def format(tools: list[Tool], verbose: bool = False) -> Table | list[str]:
|
|
24
|
+
"""
|
|
25
|
+
Format tools as a Rich table.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
tools: List of Tool objects to format
|
|
29
|
+
verbose: Whether to show verbose output with additional metadata
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Rich Table object (basic mode) or list of Rich markup strings (verbose mode)
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> formatter = TableFormatter()
|
|
36
|
+
>>> output = formatter.format(tools, verbose=False)
|
|
37
|
+
>>> print(output)
|
|
38
|
+
"""
|
|
39
|
+
if verbose:
|
|
40
|
+
return TableFormatter.format_verbose(tools)
|
|
41
|
+
else:
|
|
42
|
+
return TableFormatter.format_basic(tools)
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def format_basic(tools: list[Tool]) -> Table:
|
|
46
|
+
"""
|
|
47
|
+
Format tools in basic table mode.
|
|
48
|
+
|
|
49
|
+
Displays a table with columns: Name, Source, Description
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tools: List of Tool objects to format
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Rich Table object ready for rendering
|
|
56
|
+
"""
|
|
57
|
+
# Create table
|
|
58
|
+
table = Table(
|
|
59
|
+
title=f"🧩 Available Tools ({len(tools)})",
|
|
60
|
+
show_header=True,
|
|
61
|
+
header_style="bold cyan",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
table.add_column("Name", style="green", no_wrap=True)
|
|
65
|
+
table.add_column("Source", style="blue")
|
|
66
|
+
table.add_column("Description", style="white")
|
|
67
|
+
|
|
68
|
+
# Add rows
|
|
69
|
+
for tool in tools:
|
|
70
|
+
name = tool.name
|
|
71
|
+
source = tool.toolset_source or "main"
|
|
72
|
+
description = tool.description or ""
|
|
73
|
+
|
|
74
|
+
table.add_row(name, source, description)
|
|
75
|
+
|
|
76
|
+
return table
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def format_verbose(tools: list[Tool]) -> list[str]:
|
|
80
|
+
"""
|
|
81
|
+
Format tools in verbose mode with detailed information.
|
|
82
|
+
|
|
83
|
+
Shows detailed information for each tool including tags,
|
|
84
|
+
parameters, execution type, and other metadata.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
tools: List of Tool objects to format
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of formatted output lines with Rich markup
|
|
91
|
+
"""
|
|
92
|
+
output_lines: list[str] = []
|
|
93
|
+
|
|
94
|
+
output_lines.append(f"🧩 Available Tools ({len(tools)}):\n")
|
|
95
|
+
|
|
96
|
+
for tool in tools:
|
|
97
|
+
# Tool header
|
|
98
|
+
source = tool.toolset_source or "main"
|
|
99
|
+
output_lines.append(f"[bold green]{tool.name}[/bold green] [blue]({source})[/blue]")
|
|
100
|
+
|
|
101
|
+
# Description
|
|
102
|
+
if tool.description:
|
|
103
|
+
output_lines.append(f"├── Description: {tool.description}")
|
|
104
|
+
|
|
105
|
+
# Tags
|
|
106
|
+
if tool.tags:
|
|
107
|
+
tags_str = ", ".join(tool.tags)
|
|
108
|
+
# Escape the brackets and content to prevent Rich markup interpretation
|
|
109
|
+
output_lines.append(f"├── Tags: {escape(f'[{tags_str}]')}")
|
|
110
|
+
|
|
111
|
+
# Execution type
|
|
112
|
+
execution_type = (
|
|
113
|
+
tool.execution.type.value
|
|
114
|
+
if hasattr(tool.execution.type, "value")
|
|
115
|
+
else str(tool.execution.type)
|
|
116
|
+
)
|
|
117
|
+
output_lines.append(f"├── Execution: {execution_type}")
|
|
118
|
+
|
|
119
|
+
# Parameters from inputSchema
|
|
120
|
+
if tool.inputSchema and "properties" in tool.inputSchema:
|
|
121
|
+
params = tool.inputSchema["properties"]
|
|
122
|
+
required = tool.inputSchema.get("required", [])
|
|
123
|
+
|
|
124
|
+
param_strs = []
|
|
125
|
+
for param_name, param_def in params.items():
|
|
126
|
+
param_type = param_def.get("type", "any")
|
|
127
|
+
is_required = param_name in required
|
|
128
|
+
req_indicator = "" if is_required else " (optional)"
|
|
129
|
+
param_strs.append(f"{param_name} ({param_type}){req_indicator}")
|
|
130
|
+
|
|
131
|
+
if param_strs:
|
|
132
|
+
output_lines.append(f"└── Parameters: {', '.join(param_strs)}")
|
|
133
|
+
else:
|
|
134
|
+
output_lines.append("└── Parameters: none")
|
|
135
|
+
|
|
136
|
+
output_lines.append("") # Empty line between tools
|
|
137
|
+
|
|
138
|
+
return output_lines
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
yaml_formatter.py - YAML formatter for file output
|
|
3
|
+
|
|
4
|
+
This module provides formatting for outputting tools to YAML files
|
|
5
|
+
with metadata including timestamp, source file, filters, and tool count.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
from mcipy.models import Tool
|
|
12
|
+
|
|
13
|
+
from mci.utils.timestamp import generate_timestamp_filename, get_iso_timestamp
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class YAMLFormatter:
|
|
17
|
+
"""
|
|
18
|
+
Formats tool information as YAML files with metadata.
|
|
19
|
+
|
|
20
|
+
This class provides methods to format tool lists into YAML files
|
|
21
|
+
with timestamp, source file, filters applied, and total count.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def format_to_file(
|
|
26
|
+
tools: list[Tool],
|
|
27
|
+
mci_file: str,
|
|
28
|
+
filters_applied: list[str] | None = None,
|
|
29
|
+
verbose: bool = False,
|
|
30
|
+
) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Format tools to a YAML file and return the filename.
|
|
33
|
+
|
|
34
|
+
Creates a timestamped YAML file with tool data and metadata.
|
|
35
|
+
Filename format: tools_YYYYMMDD_HHMMSS.yaml
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
tools: List of Tool objects to format
|
|
39
|
+
mci_file: Path to the source MCI file
|
|
40
|
+
filters_applied: Optional list of filter specifications that were applied
|
|
41
|
+
verbose: Whether to include verbose tool metadata
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The filename of the created YAML file
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> formatter = YAMLFormatter()
|
|
48
|
+
>>> filename = formatter.format_to_file(tools, "mci.json", ["tags:api"])
|
|
49
|
+
>>> print(filename)
|
|
50
|
+
tools_20241029_143022.yaml
|
|
51
|
+
"""
|
|
52
|
+
# Generate timestamped filename
|
|
53
|
+
filename = generate_timestamp_filename("yaml")
|
|
54
|
+
|
|
55
|
+
# Build output data structure
|
|
56
|
+
output_data: dict[str, Any] = {
|
|
57
|
+
"timestamp": get_iso_timestamp(),
|
|
58
|
+
"mci_file": mci_file,
|
|
59
|
+
"filters_applied": filters_applied or [],
|
|
60
|
+
"total": len(tools),
|
|
61
|
+
"tools": [],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Format each tool
|
|
65
|
+
for tool in tools:
|
|
66
|
+
tool_data: dict[str, str | list[str] | dict[str, Any] | bool] = {
|
|
67
|
+
"name": tool.name,
|
|
68
|
+
"source": tool.toolset_source or "main",
|
|
69
|
+
"description": tool.description or "",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Add verbose fields if requested
|
|
73
|
+
if verbose:
|
|
74
|
+
tool_data["tags"] = tool.tags
|
|
75
|
+
tool_data["execution_type"] = (
|
|
76
|
+
tool.execution.type.value
|
|
77
|
+
if hasattr(tool.execution.type, "value")
|
|
78
|
+
else str(tool.execution.type)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if tool.inputSchema:
|
|
82
|
+
tool_data["inputSchema"] = tool.inputSchema
|
|
83
|
+
|
|
84
|
+
if tool.disabled:
|
|
85
|
+
tool_data["disabled"] = tool.disabled
|
|
86
|
+
|
|
87
|
+
output_data["tools"].append(tool_data)
|
|
88
|
+
|
|
89
|
+
# Write to file
|
|
90
|
+
with open(filename, "w") as f:
|
|
91
|
+
yaml.dump(output_data, f, default_flow_style=False, sort_keys=False)
|
|
92
|
+
|
|
93
|
+
return filename
|
mci/cli/install.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
install.py - CLI command for initializing MCI project structure
|
|
3
|
+
|
|
4
|
+
This module provides the 'install' command which creates a new MCI project
|
|
5
|
+
by copying template files and setting up the directory structure.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from importlib.resources import files
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def copy_asset(resource_name: str, dest_path: Path, overwrite: bool = False) -> bool:
|
|
15
|
+
"""
|
|
16
|
+
Copy an asset file from the package to a destination path.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
resource_name: Name of the asset file in src/mci/assets/
|
|
20
|
+
dest_path: Destination path to copy the file to
|
|
21
|
+
overwrite: Whether to overwrite existing files (default: False)
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
True if file was copied, False if it already existed and overwrite=False
|
|
25
|
+
"""
|
|
26
|
+
if dest_path.exists() and not overwrite:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
# Get the asset file content using importlib.resources
|
|
30
|
+
asset_files = files("mci.assets")
|
|
31
|
+
asset_file = asset_files.joinpath(resource_name)
|
|
32
|
+
|
|
33
|
+
# Read the asset content and write to destination
|
|
34
|
+
content = asset_file.read_text()
|
|
35
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
dest_path.write_text(content)
|
|
37
|
+
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_mci_file(format: str = "json") -> None:
|
|
42
|
+
"""
|
|
43
|
+
Create the main MCI configuration file (mci.json or mci.yaml).
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
format: File format, either "json" or "yaml"
|
|
47
|
+
"""
|
|
48
|
+
if format == "yaml":
|
|
49
|
+
filename = "mci.yaml"
|
|
50
|
+
asset_name = "mci.yaml"
|
|
51
|
+
else:
|
|
52
|
+
filename = "mci.json"
|
|
53
|
+
asset_name = "mci.json"
|
|
54
|
+
|
|
55
|
+
dest_path = Path.cwd() / filename
|
|
56
|
+
|
|
57
|
+
if copy_asset(asset_name, dest_path):
|
|
58
|
+
click.echo(f"✓ Created {filename}")
|
|
59
|
+
else:
|
|
60
|
+
click.echo(f"⚠ {filename} already exists, skipping")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def create_mci_directory() -> None:
|
|
64
|
+
"""
|
|
65
|
+
Create the ./mci/ directory and update .gitignore.
|
|
66
|
+
"""
|
|
67
|
+
mci_dir = Path.cwd() / "mci"
|
|
68
|
+
mci_dir.mkdir(exist_ok=True)
|
|
69
|
+
click.echo("✓ Created ./mci/ directory")
|
|
70
|
+
|
|
71
|
+
# Create or update .gitignore
|
|
72
|
+
gitignore_path = mci_dir / ".gitignore"
|
|
73
|
+
|
|
74
|
+
if gitignore_path.exists():
|
|
75
|
+
# Check if 'mcp/' already exists in .gitignore
|
|
76
|
+
content = gitignore_path.read_text()
|
|
77
|
+
if "mcp/" not in content:
|
|
78
|
+
# Append mcp/ entry
|
|
79
|
+
with gitignore_path.open("a") as f:
|
|
80
|
+
if not content.endswith("\n"):
|
|
81
|
+
f.write("\n")
|
|
82
|
+
f.write("mcp/\n")
|
|
83
|
+
click.echo("✓ Updated ./mci/.gitignore with mcp/ entry")
|
|
84
|
+
else:
|
|
85
|
+
click.echo("⚠ ./mci/.gitignore already contains mcp/ entry, skipping")
|
|
86
|
+
else:
|
|
87
|
+
# Create new .gitignore from template
|
|
88
|
+
copy_asset("gitignore", gitignore_path, overwrite=True)
|
|
89
|
+
click.echo("✓ Created ./mci/.gitignore")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_example_toolset(format: str = "json") -> None:
|
|
93
|
+
"""
|
|
94
|
+
Create the example toolset file in ./mci/ directory.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
format: File format, either "json" or "yaml"
|
|
98
|
+
"""
|
|
99
|
+
if format == "yaml":
|
|
100
|
+
filename = "example_toolset.mci.yaml"
|
|
101
|
+
asset_name = "example_toolset.mci.yaml"
|
|
102
|
+
else:
|
|
103
|
+
filename = "example_toolset.mci.json"
|
|
104
|
+
asset_name = "example_toolset.mci.json"
|
|
105
|
+
|
|
106
|
+
dest_path = Path.cwd() / "mci" / filename
|
|
107
|
+
|
|
108
|
+
if copy_asset(asset_name, dest_path):
|
|
109
|
+
click.echo(f"✓ Created ./mci/{filename}")
|
|
110
|
+
else:
|
|
111
|
+
click.echo(f"⚠ ./mci/{filename} already exists, skipping")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@click.command()
|
|
115
|
+
@click.option(
|
|
116
|
+
"--yaml",
|
|
117
|
+
is_flag=True,
|
|
118
|
+
help="Create YAML configuration file instead of JSON",
|
|
119
|
+
)
|
|
120
|
+
def install(yaml: bool):
|
|
121
|
+
"""
|
|
122
|
+
Initialize an MCI project structure.
|
|
123
|
+
|
|
124
|
+
Creates mci.json (or mci.yaml with --yaml flag), ./mci/ directory,
|
|
125
|
+
example toolset file, and .gitignore configuration.
|
|
126
|
+
"""
|
|
127
|
+
click.echo("Initializing MCI project...")
|
|
128
|
+
click.echo()
|
|
129
|
+
|
|
130
|
+
format = "yaml" if yaml else "json"
|
|
131
|
+
|
|
132
|
+
# Create main configuration file
|
|
133
|
+
create_mci_file(format=format)
|
|
134
|
+
|
|
135
|
+
# Create ./mci directory and .gitignore
|
|
136
|
+
create_mci_directory()
|
|
137
|
+
|
|
138
|
+
# Create example toolset
|
|
139
|
+
create_example_toolset(format=format)
|
|
140
|
+
|
|
141
|
+
click.echo()
|
|
142
|
+
click.echo("✓ MCI project initialized successfully!")
|
|
143
|
+
click.echo()
|
|
144
|
+
click.echo("Next steps:")
|
|
145
|
+
click.echo(" 1. Review the generated configuration files")
|
|
146
|
+
click.echo(" 2. Run 'mcix list' to see available tools")
|
|
147
|
+
click.echo(" 3. Run 'mcix validate' to check your configuration")
|
mci/cli/list.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
list.py - List command for displaying available tools
|
|
3
|
+
|
|
4
|
+
This module implements the `list` command for the MCI CLI, which displays
|
|
5
|
+
all available tools in the current MCI configuration with support for
|
|
6
|
+
filtering, multiple output formats, and verbose mode.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from mcipy import MCIClientError
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from mci.cli.formatters import JSONFormatter, TableFormatter, YAMLFormatter
|
|
14
|
+
from mci.core.file_finder import MCIFileFinder
|
|
15
|
+
from mci.core.mci_client import MCIClientWrapper
|
|
16
|
+
from mci.core.tool_manager import ToolManager
|
|
17
|
+
from mci.utils.error_handler import ErrorHandler
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command()
|
|
21
|
+
@click.option(
|
|
22
|
+
"--file",
|
|
23
|
+
"-f",
|
|
24
|
+
type=click.Path(exists=True),
|
|
25
|
+
default=None,
|
|
26
|
+
help="Path to MCI schema file (defaults to mci.json or mci.yaml in current directory)",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--filter",
|
|
30
|
+
type=str,
|
|
31
|
+
default=None,
|
|
32
|
+
help="Filter tools (format: type:value1,value2 - e.g., tags:api,database)",
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--format",
|
|
36
|
+
type=click.Choice(["table", "json", "yaml"], case_sensitive=False),
|
|
37
|
+
default="table",
|
|
38
|
+
help="Output format (default: table)",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--verbose",
|
|
42
|
+
"-v",
|
|
43
|
+
is_flag=True,
|
|
44
|
+
help="Show detailed tool information including tags, parameters, etc.",
|
|
45
|
+
)
|
|
46
|
+
def list_command(file: str | None, filter: str | None, format: str, verbose: bool):
|
|
47
|
+
"""
|
|
48
|
+
List available tools from the MCI configuration.
|
|
49
|
+
|
|
50
|
+
Displays all tools defined in the MCI schema file, with support for
|
|
51
|
+
filtering, multiple output formats (table, JSON, YAML), and verbose mode.
|
|
52
|
+
|
|
53
|
+
The list command uses the same tool loading and filtering logic as the
|
|
54
|
+
run command, ensuring consistency between what is listed and what will
|
|
55
|
+
actually be available when running the MCP server.
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
|
|
59
|
+
# List all tools in table format
|
|
60
|
+
mcix list
|
|
61
|
+
|
|
62
|
+
# List tools from specific file
|
|
63
|
+
mcix list --file=./custom.mci.json
|
|
64
|
+
|
|
65
|
+
# List tools with filter
|
|
66
|
+
mcix list --filter=tags:api,database
|
|
67
|
+
|
|
68
|
+
# List tools with verbose output
|
|
69
|
+
mcix list --verbose
|
|
70
|
+
|
|
71
|
+
# Export tools to JSON file
|
|
72
|
+
mcix list --format=json
|
|
73
|
+
|
|
74
|
+
# Export tools to YAML file with verbose info
|
|
75
|
+
mcix list --format=yaml --verbose
|
|
76
|
+
"""
|
|
77
|
+
console = Console()
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
# Find MCI file
|
|
81
|
+
if file is None:
|
|
82
|
+
finder = MCIFileFinder()
|
|
83
|
+
file = finder.find_mci_file()
|
|
84
|
+
if file is None:
|
|
85
|
+
console.print(
|
|
86
|
+
"[red]✗[/red] No MCI schema file found. "
|
|
87
|
+
"Run 'mcix install' to create one or specify --file.",
|
|
88
|
+
style="red",
|
|
89
|
+
)
|
|
90
|
+
raise click.Abort()
|
|
91
|
+
|
|
92
|
+
# Load schema using MCIClientWrapper
|
|
93
|
+
try:
|
|
94
|
+
client = MCIClientWrapper(file)
|
|
95
|
+
except MCIClientError as e:
|
|
96
|
+
console.print(ErrorHandler.format_mci_client_error(e))
|
|
97
|
+
raise click.Abort() from e
|
|
98
|
+
except Exception as e:
|
|
99
|
+
console.print(ErrorHandler.format_generic_error(e))
|
|
100
|
+
raise click.Abort() from e
|
|
101
|
+
|
|
102
|
+
# Get tools (with or without filter)
|
|
103
|
+
if filter:
|
|
104
|
+
try:
|
|
105
|
+
tools = ToolManager.apply_filter_spec(client, filter)
|
|
106
|
+
except ValueError as e:
|
|
107
|
+
console.print(f"[red]✗[/red] Invalid filter: {e}", style="red")
|
|
108
|
+
raise click.Abort() from e
|
|
109
|
+
else:
|
|
110
|
+
tools = client.get_tools()
|
|
111
|
+
|
|
112
|
+
# Format and display output
|
|
113
|
+
if format == "table":
|
|
114
|
+
# Display table to console
|
|
115
|
+
output = TableFormatter.format(tools, verbose=verbose)
|
|
116
|
+
if isinstance(output, list):
|
|
117
|
+
# Verbose mode returns list of Rich markup strings
|
|
118
|
+
for line in output:
|
|
119
|
+
console.print(line)
|
|
120
|
+
else:
|
|
121
|
+
# Basic mode returns a Table object
|
|
122
|
+
console.print(output)
|
|
123
|
+
|
|
124
|
+
elif format == "json":
|
|
125
|
+
# Write to JSON file
|
|
126
|
+
filters_applied = [filter] if filter else []
|
|
127
|
+
filename = JSONFormatter.format_to_file(
|
|
128
|
+
tools=tools,
|
|
129
|
+
mci_file=file,
|
|
130
|
+
filters_applied=filters_applied,
|
|
131
|
+
verbose=verbose,
|
|
132
|
+
)
|
|
133
|
+
console.print(f"[green]✓[/green] Tools exported to: {filename}")
|
|
134
|
+
|
|
135
|
+
elif format == "yaml":
|
|
136
|
+
# Write to YAML file
|
|
137
|
+
filters_applied = [filter] if filter else []
|
|
138
|
+
filename = YAMLFormatter.format_to_file(
|
|
139
|
+
tools=tools,
|
|
140
|
+
mci_file=file,
|
|
141
|
+
filters_applied=filters_applied,
|
|
142
|
+
verbose=verbose,
|
|
143
|
+
)
|
|
144
|
+
console.print(f"[green]✓[/green] Tools exported to: {filename}")
|
|
145
|
+
|
|
146
|
+
except click.Abort:
|
|
147
|
+
raise
|
|
148
|
+
except MCIClientError as e:
|
|
149
|
+
console.print(ErrorHandler.format_mci_client_error(e))
|
|
150
|
+
raise click.Abort() from e
|
|
151
|
+
except Exception as e:
|
|
152
|
+
console.print(ErrorHandler.format_generic_error(e))
|
|
153
|
+
raise click.Abort() from e
|