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.
Files changed (42) hide show
  1. iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/METADATA +931 -0
  2. iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/RECORD +42 -0
  3. iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/WHEEL +4 -0
  4. iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/licenses/LICENSE +21 -0
  6. mci/__init__.py +10 -0
  7. mci/assets/example_toolset.mci.json +37 -0
  8. mci/assets/example_toolset.mci.yaml +23 -0
  9. mci/assets/gitignore +1 -0
  10. mci/assets/mci.json +29 -0
  11. mci/assets/mci.yaml +19 -0
  12. mci/cli/__init__.py +8 -0
  13. mci/cli/add.py +108 -0
  14. mci/cli/envs.py +257 -0
  15. mci/cli/formatters/__init__.py +12 -0
  16. mci/cli/formatters/env_formatter.py +83 -0
  17. mci/cli/formatters/json_formatter.py +93 -0
  18. mci/cli/formatters/table_formatter.py +138 -0
  19. mci/cli/formatters/yaml_formatter.py +93 -0
  20. mci/cli/install.py +147 -0
  21. mci/cli/list.py +153 -0
  22. mci/cli/run.py +125 -0
  23. mci/cli/validate.py +113 -0
  24. mci/core/__init__.py +8 -0
  25. mci/core/config.py +144 -0
  26. mci/core/dynamic_server.py +187 -0
  27. mci/core/file_finder.py +105 -0
  28. mci/core/mci_client.py +196 -0
  29. mci/core/mcp_server.py +240 -0
  30. mci/core/schema_editor.py +284 -0
  31. mci/core/tool_converter.py +119 -0
  32. mci/core/tool_manager.py +118 -0
  33. mci/core/validator.py +162 -0
  34. mci/mci.py +39 -0
  35. mci/py.typed +0 -0
  36. mci/utils/__init__.py +8 -0
  37. mci/utils/dotenv.py +170 -0
  38. mci/utils/env_scanner.py +84 -0
  39. mci/utils/error_formatter.py +165 -0
  40. mci/utils/error_handler.py +174 -0
  41. mci/utils/timestamp.py +50 -0
  42. mci/utils/validation.py +92 -0
@@ -0,0 +1,42 @@
1
+ mci/__init__.py,sha256=nuemO6CcSJn9t-0Ah57JVcbIkzSFEZPjYWTfHC3nbkI,204
2
+ mci/mci.py,sha256=Xw3ibISXDPtLaz3RGMk1WqPODbByyijRXceXOXWUNIw,909
3
+ mci/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mci/assets/example_toolset.mci.json,sha256=oNxS_QxfScOJWjBr3JmNeNGoScquDPzZ8zBVh-qr7qo,770
5
+ mci/assets/example_toolset.mci.yaml,sha256=WS8pIf4t5VIEbp0aWLeGw0S31KwgGHBY7RuaYAluLbw,541
6
+ mci/assets/gitignore,sha256=l6yi4sEzheFyEHHK6oHzm9nP2DIZjrwXfMVOIGsjxG0,5
7
+ mci/assets/mci.json,sha256=GgFiQJ3EtL7NVtMVwoleFKU-H71WCEEYXY283s4WIYk,600
8
+ mci/assets/mci.yaml,sha256=B95JmmzjUgYOSW2s6AFWMkQo9jc6dX7LGhZ33QRkSgk,420
9
+ mci/cli/__init__.py,sha256=N6bvmpH2Wrn5HXELYEQ7H7QDmAknL0ih_WCUfcWDvCs,190
10
+ mci/cli/add.py,sha256=6FeuGQTLZs23A8_ZReJryk7LAPey-L8z4ZVEKPQVbIQ,3217
11
+ mci/cli/envs.py,sha256=WTgkszkdg5-B5JMZ7gDA4TgU-H7z7Ov6RpH2d2pyQyg,8860
12
+ mci/cli/install.py,sha256=NDQnNvgzPJlSQnQ_RLnVy19dIonEYbJEvZoxYidw08o,4359
13
+ mci/cli/list.py,sha256=o7hyb9iNZ9fKO7vKLY4HhpaVcC_n8xuwSO6WHVOw3O4,5018
14
+ mci/cli/run.py,sha256=0a1Vj413_XzCgtX0Mky1-K6uIQgaYC2ZMv1BIsJjJQk,4101
15
+ mci/cli/validate.py,sha256=qit0lpz0YkGAArPUCrQA1_vOtPQhlrll3IvCDXq1hcM,3409
16
+ mci/cli/formatters/__init__.py,sha256=mijJRgrkNYkoLHztNUM3ezskwmhOZtHCGogFjxW7fq4,435
17
+ mci/cli/formatters/env_formatter.py,sha256=zuL76kTzV7Kph58QvTlQNpAlxYhOmn-tfg70z9E5n9Y,2674
18
+ mci/cli/formatters/json_formatter.py,sha256=Sopuw7eYP-0eakUlat_5o1F82AkbUAKgzA_qwOOvNjE,2919
19
+ mci/cli/formatters/table_formatter.py,sha256=kV73T5phFlLe468X4PAfpTiFvzKMZq6iKri1ZIJ70Gc,4541
20
+ mci/cli/formatters/yaml_formatter.py,sha256=0_G2jE2zIXnun7mlU9cQzUUsztqZMhUVkqt22tiCohw,2952
21
+ mci/core/__init__.py,sha256=nuCREJL3nCOQi5IQKVyL1swl_xrz7QPdUOO6Ju_K2Vc,187
22
+ mci/core/config.py,sha256=3oiJz9bjfympXZsDwisZZrKLY1hb_1rb7ERGil3YOYM,5869
23
+ mci/core/dynamic_server.py,sha256=5nnTO8LadRJdjCA4zRJ0qZIHBXFTY7TSX3JLZByECXU,6977
24
+ mci/core/file_finder.py,sha256=nB0TVV5kSqYzVp04KCE_q0n4xabudowFWNQkvQ7H0pg,3226
25
+ mci/core/mci_client.py,sha256=k8lTBcEbw830TcQEEWI3PiD7-WgugOY6y6ZxZnZU8kE,6797
26
+ mci/core/mcp_server.py,sha256=csIkKv9bMaecAR2hu-7p_Jht3QHZs6xPIqiSVTI5YR4,8453
27
+ mci/core/schema_editor.py,sha256=uYrHgcdz3vLagjNjKxPuVt1Rt615NLYgMQCo86xhZM4,10065
28
+ mci/core/tool_converter.py,sha256=xRluMABCB9whdy_Y0ncug9FpbumnpkxC7_9_n2KN2S4,4491
29
+ mci/core/tool_manager.py,sha256=eAggVfC6sAxwYhAfkNbK-FnJBpySRvOEx5GGMLLyZF4,4438
30
+ mci/core/validator.py,sha256=7b5mt2uP0ko2nUWGHStB4sKNicTRrwJEMPcx0CBEYmo,5746
31
+ mci/utils/__init__.py,sha256=b_nmqu4_AAAq3D5p9njpkXj8y9oU_-MyLMFBYhQmAII,179
32
+ mci/utils/dotenv.py,sha256=XBUhGcQtHQ8hJFoM9NlY0bxTMDUqqkI7zdzCYe73s4w,6181
33
+ mci/utils/env_scanner.py,sha256=7W00ov1DuWaxD6j6M56pXb0SqQdYaA_EeCSAzyVt77Q,2599
34
+ mci/utils/error_formatter.py,sha256=TOs6CT3SAcBWUycjn1er6Tzp6kJkAB_5u_vYfZ5VRJw,5093
35
+ mci/utils/error_handler.py,sha256=ybv_JpHDTqW8Zmm4Vv2MyQGzJ20T1hMbe7D5UwM4nCw,5899
36
+ mci/utils/timestamp.py,sha256=AxRpVv9cTQByduDOJYEYtRZl2XGdrAtCZtdvb2oQXP0,1341
37
+ mci/utils/validation.py,sha256=qKI3tn8oZzDRVP0qrMhPnMJSYjEi2XTj_1b28Ly0WG8,1806
38
+ iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/METADATA,sha256=u1tAv0pcLi-YrUK4VMP0DZShVRtTqTvLscjOPIShJC8,25157
39
+ iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
40
+ iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/entry_points.txt,sha256=5d6RSblyFS_5gbD524eS-tJEUzQQ8C2mUugZVnkTKbU,34
41
+ iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/licenses/LICENSE,sha256=tMFywLM9365292nkpvJgJTcleQn_ABNSELN5YMV2dzo,1069
42
+ iflow_mcp_modelcontextinterface_mcix-1.1.1.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mcix = mci:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 maestroerror
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
mci/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """
2
+ MCI CLI Package
3
+
4
+ This package provides a command-line interface for managing Model Context Interface (MCI)
5
+ schemas and dynamically running MCP servers.
6
+ """
7
+
8
+ from .mci import main
9
+
10
+ __all__ = ("main",)
@@ -0,0 +1,37 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "metadata": {
4
+ "name": "Example Toolset",
5
+ "description": "Example MCI toolset with CLI tool"
6
+ },
7
+ "tools": [
8
+ {
9
+ "name": "list_files",
10
+ "description": "List files in a directory",
11
+ "inputSchema": {
12
+ "type": "object",
13
+ "properties": {
14
+ "directory": {
15
+ "type": "string",
16
+ "description": "Directory to list files from"
17
+ }
18
+ },
19
+ "required": [
20
+ "directory"
21
+ ]
22
+ },
23
+ "execution": {
24
+ "type": "cli",
25
+ "command": "ls",
26
+ "args": [
27
+ "-la",
28
+ "{{props.directory}}"
29
+ ]
30
+ },
31
+ "enableAnyPaths": false,
32
+ "directoryAllowList": [
33
+ "{{env.PROJECT_ROOT}}"
34
+ ]
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,23 @@
1
+ schemaVersion: '1.0'
2
+ metadata:
3
+ name: Example Toolset
4
+ description: Example MCI toolset with CLI tool
5
+ tools:
6
+ - name: list_files
7
+ description: List files in a directory
8
+ inputSchema:
9
+ type: object
10
+ properties:
11
+ directory:
12
+ type: string
13
+ description: Directory to list files from
14
+ required: [directory]
15
+ execution:
16
+ type: cli
17
+ command: ls
18
+ args:
19
+ - '-la'
20
+ - '{{props.directory}}'
21
+ enableAnyPaths: false
22
+ directoryAllowList:
23
+ - '{{env.PROJECT_ROOT}}'
mci/assets/gitignore ADDED
@@ -0,0 +1 @@
1
+ mcp/
mci/assets/mci.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "metadata": {
4
+ "name": "Example Project",
5
+ "description": "Example MCI configuration"
6
+ },
7
+ "tools": [
8
+ {
9
+ "name": "echo_test",
10
+ "description": "Simple echo test tool",
11
+ "inputSchema": {
12
+ "type": "object",
13
+ "properties": {
14
+ "message": {
15
+ "type": "string",
16
+ "description": "Message to echo"
17
+ }
18
+ },
19
+ "required": ["message"]
20
+ },
21
+ "execution": {
22
+ "type": "text",
23
+ "text": "Echo: {{props.message}}"
24
+ }
25
+ }
26
+ ],
27
+ "toolsets": [],
28
+ "mcp_servers": {}
29
+ }
mci/assets/mci.yaml ADDED
@@ -0,0 +1,19 @@
1
+ schemaVersion: '1.0'
2
+ metadata:
3
+ name: Example Project
4
+ description: Example MCI configuration
5
+ tools:
6
+ - name: echo_test
7
+ description: Simple echo test tool
8
+ inputSchema:
9
+ type: object
10
+ properties:
11
+ message:
12
+ type: string
13
+ description: Message to echo
14
+ required: [message]
15
+ execution:
16
+ type: text
17
+ text: 'Echo: {{props.message}}'
18
+ toolsets: []
19
+ mcp_servers: {}
mci/cli/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ CLI command modules for the MCI CLI Tool.
3
+
4
+ This package contains all command implementations for the MCI CLI,
5
+ including install, list, validate, add, and run commands.
6
+ """
7
+
8
+ __all__ = ()
mci/cli/add.py ADDED
@@ -0,0 +1,108 @@
1
+ """
2
+ add.py - CLI command to add toolset references to MCI schemas
3
+
4
+ This module provides the `mcix add` command which adds toolset references
5
+ to MCI schema files with optional filtering, while preserving the original
6
+ file format (JSON or YAML).
7
+ """
8
+
9
+ import sys
10
+
11
+ import click
12
+ from rich.console import Console
13
+
14
+ from mci.core.file_finder import MCIFileFinder
15
+ from mci.core.schema_editor import SchemaEditor, parse_add_filter
16
+
17
+
18
+ @click.command()
19
+ @click.argument("toolset_name", type=str)
20
+ @click.option(
21
+ "--filter",
22
+ "-f",
23
+ default=None,
24
+ help='Filter specification (e.g., "only:Tool1,Tool2" or "tags:api,database")',
25
+ )
26
+ @click.option(
27
+ "--path",
28
+ "-p",
29
+ default=None,
30
+ help="Path to MCI schema file (default: auto-discover mci.json/mci.yaml)",
31
+ )
32
+ def add(toolset_name: str, filter: str | None, path: str | None) -> None:
33
+ """
34
+ Add a toolset reference to an MCI schema file.
35
+
36
+ This command adds a toolset to the schema's toolsets array. If the toolset
37
+ already exists, it will be updated with the new filter (if provided).
38
+ The original file format (JSON or YAML) is preserved.
39
+
40
+ TOOLSET_NAME is the name of the toolset to add.
41
+
42
+ Examples:
43
+
44
+ \b
45
+ # Add a toolset without filter
46
+ mcix add weather-tools
47
+
48
+ \b
49
+ # Add a toolset with "only" filter
50
+ mcix add analytics --filter=only:Tool1,Tool2
51
+
52
+ \b
53
+ # Add a toolset with "tags" filter
54
+ mcix add api-tools --filter=tags:api,database
55
+
56
+ \b
57
+ # Add to a custom file
58
+ mcix add weather-tools --path=custom.mci.json
59
+ """
60
+ console = Console()
61
+
62
+ # Find the schema file
63
+ if path is None:
64
+ file_finder = MCIFileFinder()
65
+ schema_file = file_finder.find_mci_file()
66
+ if schema_file is None:
67
+ console.print("[red]❌ No MCI schema file found in current directory[/red]\n")
68
+ console.print("[yellow]💡 Run 'mcix install' to create a default mci.json file[/yellow]")
69
+ sys.exit(1)
70
+ else:
71
+ schema_file = path
72
+
73
+ # Parse filter specification if provided
74
+ filter_type = None
75
+ filter_value = None
76
+ if filter:
77
+ try:
78
+ filter_type, filter_value = parse_add_filter(filter)
79
+ except ValueError as e:
80
+ console.print(f"[red]❌ Invalid filter specification: {e}[/red]\n")
81
+ sys.exit(1)
82
+
83
+ # Load, modify, and save the schema
84
+ editor = SchemaEditor()
85
+ try:
86
+ editor.load_schema(schema_file)
87
+ editor.add_toolset(toolset_name, filter_type, filter_value)
88
+ editor.save_schema()
89
+
90
+ # Display success message
91
+ console.print(f"[green]✅ Added toolset '{toolset_name}' to {schema_file}[/green]")
92
+ if filter_type and filter_value:
93
+ console.print(f"[dim] Filter: {filter_type}:{filter_value}[/dim]")
94
+
95
+ except FileNotFoundError as e:
96
+ console.print(f"[red]❌ File not found: {e}[/red]\n")
97
+ sys.exit(1)
98
+ except ValueError as e:
99
+ console.print(f"[red]❌ Error: {e}[/red]\n")
100
+ sys.exit(1)
101
+ except Exception as e:
102
+ console.print(f"[red]❌ Unexpected error: {e}[/red]\n")
103
+ sys.exit(1)
104
+
105
+
106
+ # Allow running as script
107
+ if __name__ == "__main__":
108
+ add()
mci/cli/envs.py ADDED
@@ -0,0 +1,257 @@
1
+ """
2
+ envs.py - Environment variables command for displaying required env vars
3
+
4
+ This module implements the `envs` command for the MCI CLI, which scans
5
+ the MCI configuration and displays all environment variables referenced
6
+ in templates across tools, toolsets, and MCP servers.
7
+ """
8
+
9
+ import json
10
+ from pathlib import Path
11
+
12
+ import click
13
+ import yaml
14
+ from mcipy import MCIClientError
15
+ from rich.console import Console
16
+ from rich.table import Table
17
+
18
+ from mci.cli.formatters.env_formatter import EnvFormatter
19
+ from mci.core.file_finder import MCIFileFinder
20
+ from mci.utils.env_scanner import EnvScanner
21
+ from mci.utils.error_handler import ErrorHandler
22
+
23
+
24
+ def extract_env_vars_from_schema(schema_path: str) -> dict[str, list[str]]:
25
+ """
26
+ Extract all environment variables from a schema file and its referenced files.
27
+
28
+ This function loads the main schema file and scans it along with all
29
+ referenced toolsets and MCP servers to find environment variable references.
30
+
31
+ Args:
32
+ schema_path: Path to the main MCI schema file
33
+
34
+ Returns:
35
+ Dictionary mapping variable names to list of locations where used
36
+ (e.g., {"API_KEY": ["main", "weather-toolset"]})
37
+
38
+ Raises:
39
+ FileNotFoundError: If schema file or referenced files are not found
40
+ ValueError: If schema file is invalid
41
+ """
42
+ env_vars: dict[str, set[str]] = {}
43
+ schema_dir = Path(schema_path).parent.resolve()
44
+
45
+ # Load main schema
46
+ with open(schema_path) as f:
47
+ if schema_path.endswith((".yaml", ".yml")):
48
+ main_schema = yaml.safe_load(f)
49
+ else:
50
+ main_schema = json.load(f)
51
+
52
+ # Scan main schema (excluding mcp_servers which we'll scan separately)
53
+ main_schema_without_mcp = {k: v for k, v in main_schema.items() if k != "mcp_servers"}
54
+ main_env_vars = EnvScanner.scan_dict(main_schema_without_mcp)
55
+ for var in main_env_vars:
56
+ if var not in env_vars:
57
+ env_vars[var] = set()
58
+ env_vars[var].add("main")
59
+
60
+ # Get library directory for toolsets
61
+ library_dir = main_schema.get("libraryDir", "mci")
62
+ lib_path = schema_dir / library_dir
63
+
64
+ # Scan registered toolsets
65
+ toolsets = main_schema.get("toolsets", [])
66
+ for toolset_ref in toolsets:
67
+ toolset_name = toolset_ref if isinstance(toolset_ref, str) else toolset_ref.get("name")
68
+ if not toolset_name:
69
+ continue
70
+
71
+ # Try to find toolset file
72
+ toolset_file = _find_toolset_file(lib_path, toolset_name)
73
+ if toolset_file:
74
+ try:
75
+ with open(toolset_file) as f:
76
+ if toolset_file.suffix in {".yaml", ".yml"}:
77
+ toolset_schema = yaml.safe_load(f)
78
+ else:
79
+ toolset_schema = json.load(f)
80
+
81
+ # Scan toolset
82
+ toolset_env_vars = EnvScanner.scan_dict(toolset_schema)
83
+ for var in toolset_env_vars:
84
+ if var not in env_vars:
85
+ env_vars[var] = set()
86
+ env_vars[var].add(toolset_name)
87
+ except Exception as e:
88
+ # Warn but continue if toolset file is invalid
89
+ console = Console()
90
+ console.print(
91
+ f"[yellow]⚠[/yellow] Warning: Could not load toolset '{toolset_name}': {e}",
92
+ style="yellow",
93
+ )
94
+
95
+ # Scan MCP servers
96
+ mcp_servers = main_schema.get("mcp_servers", {})
97
+ if isinstance(mcp_servers, dict):
98
+ for server_name, server_config in mcp_servers.items():
99
+ if isinstance(server_config, dict):
100
+ server_env_vars = EnvScanner.scan_dict(server_config)
101
+ for var in server_env_vars:
102
+ if var not in env_vars:
103
+ env_vars[var] = set()
104
+ env_vars[var].add(f"mcp:{server_name}")
105
+
106
+ # Convert sets to sorted lists
107
+ result = {var: sorted(locations) for var, locations in env_vars.items()}
108
+ return result
109
+
110
+
111
+ def _find_toolset_file(lib_path: Path, toolset_name: str) -> Path | None:
112
+ """
113
+ Find a toolset file in the library directory.
114
+
115
+ Args:
116
+ lib_path: Path to the library directory
117
+ toolset_name: Name of the toolset
118
+
119
+ Returns:
120
+ Path to the toolset file if found, None otherwise
121
+ """
122
+ if not lib_path.exists():
123
+ return None
124
+
125
+ # Try various file patterns
126
+ patterns = [
127
+ f"{toolset_name}.mci.json",
128
+ f"{toolset_name}.mci.yaml",
129
+ f"{toolset_name}.mci.yml",
130
+ f"{toolset_name}.json",
131
+ f"{toolset_name}.yaml",
132
+ f"{toolset_name}.yml",
133
+ ]
134
+
135
+ for pattern in patterns:
136
+ file_path = lib_path / pattern
137
+ if file_path.exists() and file_path.is_file():
138
+ return file_path
139
+
140
+ # Check if it's a directory with a schema file
141
+ dir_path = lib_path / toolset_name
142
+ if dir_path.exists() and dir_path.is_dir():
143
+ for pattern in patterns:
144
+ file_path = dir_path / pattern
145
+ if file_path.exists() and file_path.is_file():
146
+ return file_path
147
+
148
+ return None
149
+
150
+
151
+ @click.command()
152
+ @click.option(
153
+ "--file",
154
+ "-f",
155
+ type=click.Path(exists=True),
156
+ default=None,
157
+ help="Path to MCI schema file (defaults to mci.json or mci.yaml in current directory)",
158
+ )
159
+ @click.option(
160
+ "--format",
161
+ type=click.Choice(["table", "env"], case_sensitive=False),
162
+ default="table",
163
+ help="Output format (default: table)",
164
+ )
165
+ def envs_command(file: str | None, format: str):
166
+ """
167
+ List all environment variables used in the MCI configuration.
168
+
169
+ Scans the main schema file, referenced toolsets, and MCP servers to find
170
+ all environment variable references in templates ({{env.VARIABLE}}).
171
+
172
+ Outputs a list of all required environment variables with their locations,
173
+ helping you understand what credentials and configuration are needed.
174
+
175
+ Examples:
176
+
177
+ # Show environment variables in table format
178
+ mci envs
179
+
180
+ # Generate .env.example.mci file
181
+ mci envs --format=env
182
+
183
+ # Scan specific schema file
184
+ mci envs --file=custom.mci.json
185
+ """
186
+ console = Console()
187
+
188
+ try:
189
+ # Find MCI file
190
+ if file is None:
191
+ finder = MCIFileFinder()
192
+ file = finder.find_mci_file()
193
+ if file is None:
194
+ console.print(
195
+ "[red]✗[/red] No MCI schema file found. "
196
+ "Run 'mcix install' to create one or specify --file.",
197
+ style="red",
198
+ )
199
+ raise click.Abort()
200
+
201
+ # Extract environment variables
202
+ try:
203
+ env_vars = extract_env_vars_from_schema(file)
204
+ except FileNotFoundError as e:
205
+ console.print(f"[red]✗[/red] File not found: {e}", style="red")
206
+ raise click.Abort() from e
207
+ except (json.JSONDecodeError, yaml.YAMLError) as e:
208
+ console.print(f"[red]✗[/red] Invalid schema file: {e}", style="red")
209
+ raise click.Abort() from e
210
+ except Exception as e:
211
+ console.print(ErrorHandler.format_generic_error(e))
212
+ raise click.Abort() from e
213
+
214
+ # Check if any variables were found
215
+ if not env_vars:
216
+ if format == "table":
217
+ console.print("[yellow]ℹ[/yellow] No environment variables found in schema.")
218
+ else:
219
+ console.print(
220
+ "[yellow]ℹ[/yellow] No environment variables found. Skipping file generation."
221
+ )
222
+ return
223
+
224
+ # Format and display output
225
+ if format == "table":
226
+ # Display table to console
227
+ table = Table(
228
+ title=f"🔐 Environment Variables ({len(env_vars)})",
229
+ show_header=True,
230
+ header_style="bold cyan",
231
+ )
232
+
233
+ table.add_column("Variable", style="green", no_wrap=True)
234
+ table.add_column("Used In", style="blue")
235
+
236
+ # Sort by variable name
237
+ for var_name in sorted(env_vars.keys()):
238
+ locations = env_vars[var_name]
239
+ locations_str = ", ".join(locations)
240
+ table.add_row(var_name, locations_str)
241
+
242
+ console.print(table)
243
+
244
+ elif format == "env":
245
+ # Write to .env.example.mci file
246
+ filename = EnvFormatter.format_to_file(env_vars)
247
+ console.print(f"[green]✓[/green] Environment variables exported to: {filename}")
248
+ console.print("[dim]Copy this file to .env.mci and fill in your values[/dim]")
249
+
250
+ except click.Abort:
251
+ raise
252
+ except MCIClientError as e:
253
+ console.print(ErrorHandler.format_mci_client_error(e))
254
+ raise click.Abort() from e
255
+ except Exception as e:
256
+ console.print(ErrorHandler.format_generic_error(e))
257
+ raise click.Abort() from e
@@ -0,0 +1,12 @@
1
+ """
2
+ formatters package - Output formatters for CLI commands
3
+
4
+ This package provides formatters for displaying tool information
5
+ in various formats including Rich tables, JSON, and YAML.
6
+ """
7
+
8
+ from mci.cli.formatters.json_formatter import JSONFormatter
9
+ from mci.cli.formatters.table_formatter import TableFormatter
10
+ from mci.cli.formatters.yaml_formatter import YAMLFormatter
11
+
12
+ __all__ = ["TableFormatter", "JSONFormatter", "YAMLFormatter"]
@@ -0,0 +1,83 @@
1
+ """
2
+ env_formatter.py - Environment variable file formatter
3
+
4
+ This module provides formatting for environment variable output in .env file format.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+
10
+ class EnvFormatter:
11
+ """
12
+ Formats environment variable information as .env files.
13
+
14
+ This formatter generates .env.example.mci files containing all detected
15
+ environment variables with empty values, suitable for use as templates.
16
+ """
17
+
18
+ @staticmethod
19
+ def format_to_string(env_vars: dict[str, list[str]]) -> str:
20
+ """
21
+ Format environment variables as a string in .env format.
22
+
23
+ Args:
24
+ env_vars: Dictionary mapping variable names to list of locations where used
25
+
26
+ Returns:
27
+ Formatted string in .env format
28
+
29
+ Example:
30
+ >>> env_vars = {"API_KEY": ["main"], "DB_URL": ["database-toolset"]}
31
+ >>> print(EnvFormatter.format_to_string(env_vars))
32
+ # .env.example.mci
33
+ ...
34
+ """
35
+ # Sort variables alphabetically
36
+ sorted_vars = sorted(env_vars.keys())
37
+
38
+ # Generate content
39
+ lines: list[str] = []
40
+ lines.append("# .env.example.mci")
41
+ lines.append("# Environment variables used in MCI configuration")
42
+ lines.append("#")
43
+ lines.append("# Copy this file to .env.mci and fill in your values")
44
+ lines.append("")
45
+
46
+ for var_name in sorted_vars:
47
+ locations = env_vars[var_name]
48
+ # Add comment showing where the variable is used
49
+ location_str = ", ".join(sorted(locations))
50
+ lines.append(f"# Used in: {location_str}")
51
+ lines.append(f"{var_name}=")
52
+ lines.append("")
53
+
54
+ return "\n".join(lines)
55
+
56
+ @staticmethod
57
+ def format_to_file(env_vars: dict[str, list[str]], output_path: str | None = None) -> str:
58
+ """
59
+ Format environment variables and write to .env.example.mci file.
60
+
61
+ Args:
62
+ env_vars: Dictionary mapping variable names to list of locations where used
63
+ output_path: Optional custom output path (default: .env.example.mci)
64
+
65
+ Returns:
66
+ Path to the generated file
67
+
68
+ Example:
69
+ >>> env_vars = {
70
+ ... "API_KEY": ["main", "weather-toolset"],
71
+ ... "DB_URL": ["database-toolset"]
72
+ ... }
73
+ >>> path = EnvFormatter.format_to_file(env_vars)
74
+ >>> print(path)
75
+ .env.example.mci
76
+ """
77
+ if output_path is None:
78
+ output_path = ".env.example.mci"
79
+
80
+ content = EnvFormatter.format_to_string(env_vars)
81
+ Path(output_path).write_text(content)
82
+
83
+ return output_path