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
mci/mci.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ mci.py - Main entry point for the MCI CLI Tool
3
+
4
+ This module provides the main CLI interface for managing MCI (Model Context Interface)
5
+ schemas and dynamically running MCP servers using defined MCI toolsets.
6
+ """
7
+
8
+ import click
9
+
10
+ from mci.cli.add import add
11
+ from mci.cli.envs import envs_command
12
+ from mci.cli.install import install
13
+ from mci.cli.list import list_command
14
+ from mci.cli.run import run
15
+ from mci.cli.validate import validate
16
+
17
+
18
+ @click.group()
19
+ @click.version_option()
20
+ def main():
21
+ """
22
+ MCI CLI - Manage Model Context Interface schemas and run MCP servers.
23
+
24
+ Use 'mci COMMAND --help' for more information on a specific command.
25
+ """
26
+ pass
27
+
28
+
29
+ # Register commands
30
+ main.add_command(add)
31
+ main.add_command(envs_command, name="envs")
32
+ main.add_command(install)
33
+ main.add_command(list_command, name="list")
34
+ main.add_command(run)
35
+ main.add_command(validate)
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()
mci/py.typed ADDED
File without changes
mci/utils/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Utility functions for the MCI CLI Tool.
3
+
4
+ This package contains utility functions for error handling, validation,
5
+ formatting, and other helper functionality.
6
+ """
7
+
8
+ __all__ = ()
mci/utils/dotenv.py ADDED
@@ -0,0 +1,170 @@
1
+ """
2
+ dotenv.py - Environment variable file parsing utilities
3
+
4
+ This module provides functionality to parse .env files using the python-dotenv library
5
+ and merge environment variables from multiple sources. It supports:
6
+ - Standard .env format via python-dotenv
7
+ - Comments starting with #
8
+ - Blank lines
9
+ - Export keyword (which is ignored)
10
+ - Quoted values
11
+
12
+ The module is used to automatically load .env files from the project root
13
+ and ./mci directory when initializing MCI configurations. It supports both
14
+ .env and .env.mci files, with .env.mci taking precedence for MCI-specific
15
+ configuration.
16
+ """
17
+
18
+ import os
19
+ from pathlib import Path
20
+
21
+ from dotenv import dotenv_values
22
+
23
+
24
+ def parse_dotenv_file(file_path: str | Path) -> dict[str, str]:
25
+ """
26
+ Parse a .env file and return a dictionary of environment variables.
27
+
28
+ This function uses python-dotenv to parse .env files, which supports:
29
+ - KEY=VALUE format
30
+ - Lines starting with # are comments (ignored)
31
+ - Blank lines are ignored
32
+ - Export keyword is ignored (e.g., "export KEY=VALUE" is treated as "KEY=VALUE")
33
+ - Values can be quoted with single or double quotes
34
+ - Variable expansion and interpolation (if needed)
35
+
36
+ Args:
37
+ file_path: Path to the .env file to parse
38
+
39
+ Returns:
40
+ Dictionary of environment variable key-value pairs (excluding None values)
41
+
42
+ Example:
43
+ >>> env_vars = parse_dotenv_file(".env")
44
+ >>> print(env_vars.get("API_KEY"))
45
+ 'my-secret-key'
46
+ """
47
+ file_path = Path(file_path)
48
+
49
+ if not file_path.exists():
50
+ return {}
51
+
52
+ try:
53
+ # Use dotenv_values to parse the file
54
+ # This returns a dict with all variables, including None for empty values
55
+ env_dict = dotenv_values(file_path)
56
+ # Filter out None values and convert to strings
57
+ return {k: str(v) for k, v in env_dict.items() if v is not None}
58
+ except (OSError, UnicodeDecodeError):
59
+ # If we can't read the file, return empty dict (silent failure)
60
+ # This maintains the "no error if .env is missing" requirement
61
+ return {}
62
+
63
+
64
+ def find_and_merge_dotenv_files(project_root: str | Path | None = None) -> dict[str, str]:
65
+ """
66
+ Find and merge .env files from project root and ./mci directory.
67
+
68
+ This function looks for .env files with the following priority order:
69
+ 1. Check for .env.mci files first (MCI-specific configs have priority)
70
+ a. {project_root}/mci/.env.mci (MCI library MCI-specific)
71
+ b. {project_root}/.env.mci (project MCI-specific - highest priority)
72
+ 2. If no .env.mci files exist, check for .env files
73
+ a. {project_root}/mci/.env (MCI library defaults)
74
+ b. {project_root}/.env (project-level configs)
75
+
76
+ Variables from files loaded later override those from earlier files.
77
+
78
+ Args:
79
+ project_root: Path to the project root directory. If None, uses current directory.
80
+
81
+ Returns:
82
+ Dictionary of merged environment variables
83
+
84
+ Example:
85
+ >>> env_vars = find_and_merge_dotenv_files()
86
+ >>> # Variables from root .env.mci override all others
87
+ """
88
+ if project_root is None:
89
+ project_root = Path.cwd()
90
+ else:
91
+ project_root = Path(project_root)
92
+
93
+ merged_env: dict[str, str] = {}
94
+
95
+ # Check for .env.mci files first (MCI-specific configs)
96
+ mci_env_mci_path = project_root / "mci" / ".env.mci"
97
+ root_env_mci_path = project_root / ".env.mci"
98
+
99
+ has_env_mci = mci_env_mci_path.exists() or root_env_mci_path.exists()
100
+
101
+ if has_env_mci:
102
+ # Priority order for .env.mci files (lowest to highest):
103
+ # 1. ./mci/.env.mci (library MCI-specific)
104
+ if mci_env_mci_path.exists():
105
+ mci_env_mci_vars = parse_dotenv_file(mci_env_mci_path)
106
+ merged_env.update(mci_env_mci_vars)
107
+
108
+ # 2. .env.mci (project root MCI-specific - highest priority)
109
+ if root_env_mci_path.exists():
110
+ root_env_mci_vars = parse_dotenv_file(root_env_mci_path)
111
+ merged_env.update(root_env_mci_vars)
112
+ else:
113
+ # No .env.mci files found, check for .env files
114
+ # Priority order for .env files (lowest to highest):
115
+ # 1. ./mci/.env (library defaults)
116
+ mci_env_path = project_root / "mci" / ".env"
117
+ if mci_env_path.exists():
118
+ mci_env_vars = parse_dotenv_file(mci_env_path)
119
+ merged_env.update(mci_env_vars)
120
+
121
+ # 2. .env (project root - higher priority)
122
+ root_env_path = project_root / ".env"
123
+ if root_env_path.exists():
124
+ root_env_vars = parse_dotenv_file(root_env_path)
125
+ merged_env.update(root_env_vars)
126
+
127
+ return merged_env
128
+
129
+
130
+ def get_env_with_dotenv(
131
+ project_root: str | Path | None = None, additional_env: dict[str, str] | None = None
132
+ ) -> dict[str, str]:
133
+ """
134
+ Get complete environment variables including system, .env files, and additional vars.
135
+
136
+ The precedence order (lowest to highest):
137
+ - If .env.mci files exist:
138
+ 1. ./mci/.env.mci (library MCI-specific)
139
+ 2. {project_root}/.env.mci (project MCI-specific)
140
+ - If no .env.mci files exist:
141
+ 1. ./mci/.env (library defaults)
142
+ 2. {project_root}/.env (project-level)
143
+ - Then:
144
+ 3. System environment variables (os.environ)
145
+ 4. Additional environment variables passed as argument (highest)
146
+
147
+ Args:
148
+ project_root: Path to the project root directory. If None, uses current directory.
149
+ additional_env: Additional environment variables to merge (highest priority)
150
+
151
+ Returns:
152
+ Dictionary of merged environment variables with proper precedence
153
+
154
+ Example:
155
+ >>> # Get all env vars with .env files loaded
156
+ >>> env_vars = get_env_with_dotenv()
157
+ >>> # Add custom vars that override everything
158
+ >>> env_vars = get_env_with_dotenv(additional_env={"API_KEY": "override"})
159
+ """
160
+ # Start with .env files (lowest priority)
161
+ merged_env = find_and_merge_dotenv_files(project_root)
162
+
163
+ # Merge with system environment variables (higher priority)
164
+ merged_env.update(os.environ)
165
+
166
+ # Merge with additional environment variables (highest priority)
167
+ if additional_env:
168
+ merged_env.update(additional_env)
169
+
170
+ return merged_env
@@ -0,0 +1,84 @@
1
+ """
2
+ env_scanner.py - Environment variable scanning utilities
3
+
4
+ This module provides utilities to scan MCI schemas and extract all
5
+ referenced environment variables from template placeholders.
6
+ """
7
+
8
+ import re
9
+ from typing import Any
10
+
11
+
12
+ class EnvScanner:
13
+ """
14
+ Scans MCI schemas to find all environment variable references.
15
+
16
+ Environment variables can appear in templates as {{env.VARIABLE_NAME}}.
17
+ This scanner recursively searches through dictionaries, lists, and strings
18
+ to find all such references.
19
+ """
20
+
21
+ # Regex pattern to match {{env.VARIABLE_NAME}}
22
+ ENV_PATTERN: re.Pattern[str] = re.compile(r"\{\{env\.([A-Za-z_][A-Za-z0-9_]*)\}\}")
23
+
24
+ @staticmethod
25
+ def scan_value(value: Any) -> set[str]:
26
+ """
27
+ Scan a single value for environment variable references.
28
+
29
+ Args:
30
+ value: Value to scan (can be str, dict, list, or other types)
31
+
32
+ Returns:
33
+ Set of environment variable names found
34
+
35
+ Example:
36
+ >>> EnvScanner.scan_value("{{env.API_KEY}}")
37
+ {'API_KEY'}
38
+ >>> EnvScanner.scan_value("Hello {{env.USER}}, your key is {{env.API_KEY}}")
39
+ {'USER', 'API_KEY'}
40
+ """
41
+ env_vars: set[str] = set()
42
+
43
+ if isinstance(value, str):
44
+ # Extract all env variable names from the string
45
+ matches = EnvScanner.ENV_PATTERN.findall(value)
46
+ env_vars.update(matches)
47
+
48
+ elif isinstance(value, dict):
49
+ # Recursively scan all values in the dictionary
50
+ for v in value.values():
51
+ env_vars.update(EnvScanner.scan_value(v))
52
+
53
+ elif isinstance(value, list):
54
+ # Recursively scan all items in the list
55
+ for item in value:
56
+ env_vars.update(EnvScanner.scan_value(item))
57
+
58
+ return env_vars
59
+
60
+ @staticmethod
61
+ def scan_dict(data: dict[str, Any]) -> set[str]:
62
+ """
63
+ Scan a dictionary for environment variable references.
64
+
65
+ This is a convenience wrapper around scan_value() for dictionaries.
66
+
67
+ Args:
68
+ data: Dictionary to scan
69
+
70
+ Returns:
71
+ Set of environment variable names found
72
+
73
+ Example:
74
+ >>> schema = {
75
+ ... "execution": {
76
+ ... "type": "http",
77
+ ... "url": "{{env.BASE_URL}}/api",
78
+ ... "headers": {"Authorization": "Bearer {{env.API_KEY}}"}
79
+ ... }
80
+ ... }
81
+ >>> EnvScanner.scan_dict(schema)
82
+ {'BASE_URL', 'API_KEY'}
83
+ """
84
+ return EnvScanner.scan_value(data)
@@ -0,0 +1,165 @@
1
+ """
2
+ error_formatter.py - Format validation errors and warnings for CLI display
3
+
4
+ This module provides utilities for formatting validation errors and warnings
5
+ in a user-friendly way with color-coded output using Rich library.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+
10
+ from rich.console import Console
11
+ from rich.panel import Panel
12
+ from rich.text import Text
13
+
14
+
15
+ @dataclass
16
+ class ValidationError:
17
+ """Represents a validation error with message and optional location."""
18
+
19
+ message: str
20
+ location: str | None = None
21
+
22
+
23
+ @dataclass
24
+ class ValidationWarning:
25
+ """Represents a validation warning with message and optional suggestion."""
26
+
27
+ message: str
28
+ suggestion: str | None = None
29
+
30
+
31
+ class ErrorFormatter:
32
+ """
33
+ Formats validation errors and warnings for CLI display.
34
+
35
+ This class provides methods to format validation results using the Rich library
36
+ for beautiful, color-coded terminal output.
37
+ """
38
+
39
+ def __init__(self, console: Console | None = None):
40
+ """
41
+ Initialize the ErrorFormatter.
42
+
43
+ Args:
44
+ console: Rich Console instance. Defaults to a new Console if not provided.
45
+ """
46
+ self.console: Console = console if console is not None else Console()
47
+
48
+ def format_validation_errors(self, errors: list[ValidationError]) -> None:
49
+ """
50
+ Display validation errors with color-coded output.
51
+
52
+ Args:
53
+ errors: List of ValidationError objects to display
54
+
55
+ Example:
56
+ >>> formatter = ErrorFormatter()
57
+ >>> errors = [ValidationError(message="Missing required field: name")]
58
+ >>> formatter.format_validation_errors(errors)
59
+ """
60
+ if not errors:
61
+ return
62
+
63
+ self.console.print()
64
+ error_text = Text()
65
+ error_text.append("❌ Validation Errors\n\n", style="bold red")
66
+
67
+ for i, error in enumerate(errors, 1):
68
+ error_text.append(f"{i}. ", style="red")
69
+ if error.location:
70
+ error_text.append(f"[{error.location}] ", style="yellow")
71
+ error_text.append(f"{error.message}\n", style="red")
72
+
73
+ panel = Panel(
74
+ error_text,
75
+ title="[bold red]Schema Validation Failed",
76
+ border_style="red",
77
+ expand=False,
78
+ )
79
+ self.console.print(panel)
80
+
81
+ def format_validation_warnings(self, warnings: list[ValidationWarning]) -> None:
82
+ """
83
+ Display validation warnings with color-coded output.
84
+
85
+ Args:
86
+ warnings: List of ValidationWarning objects to display
87
+
88
+ Example:
89
+ >>> formatter = ErrorFormatter()
90
+ >>> warnings = [ValidationWarning(
91
+ ... message="Toolset file not found: weather.mci.json",
92
+ ... suggestion="Create the file or update your schema"
93
+ ... )]
94
+ >>> formatter.format_validation_warnings(warnings)
95
+ """
96
+ if not warnings:
97
+ return
98
+
99
+ self.console.print()
100
+ warning_text = Text()
101
+ warning_text.append("⚠️ Validation Warnings\n\n", style="bold yellow")
102
+
103
+ for i, warning in enumerate(warnings, 1):
104
+ warning_text.append(f"{i}. ", style="yellow")
105
+ warning_text.append(f"{warning.message}\n", style="yellow")
106
+ if warning.suggestion:
107
+ warning_text.append(f" 💡 {warning.suggestion}\n", style="cyan dim")
108
+
109
+ panel = Panel(
110
+ warning_text,
111
+ title="[bold yellow]Warnings",
112
+ border_style="yellow",
113
+ expand=False,
114
+ )
115
+ self.console.print(panel)
116
+
117
+ def format_validation_success(self, file_path: str) -> None:
118
+ """
119
+ Display validation success message.
120
+
121
+ Args:
122
+ file_path: Path to the validated file
123
+
124
+ Example:
125
+ >>> formatter = ErrorFormatter()
126
+ >>> formatter.format_validation_success("mci.json")
127
+ """
128
+ self.console.print()
129
+ success_text = Text()
130
+ success_text.append("✅ Schema is valid!\n\n", style="bold green")
131
+ success_text.append(f"File: {file_path}", style="green")
132
+
133
+ panel = Panel(
134
+ success_text,
135
+ title="[bold green]Validation Successful",
136
+ border_style="green",
137
+ expand=False,
138
+ )
139
+ self.console.print(panel)
140
+ self.console.print()
141
+
142
+ def format_mci_error(self, error_message: str) -> None:
143
+ """
144
+ Display an MCIClient error message.
145
+
146
+ Args:
147
+ error_message: Error message from MCIClient
148
+
149
+ Example:
150
+ >>> formatter = ErrorFormatter()
151
+ >>> formatter.format_mci_error("Failed to load schema: Invalid JSON")
152
+ """
153
+ self.console.print()
154
+ error_text = Text()
155
+ error_text.append("❌ MCI Error\n\n", style="bold red")
156
+ error_text.append(error_message, style="red")
157
+
158
+ panel = Panel(
159
+ error_text,
160
+ title="[bold red]Error",
161
+ border_style="red",
162
+ expand=False,
163
+ )
164
+ self.console.print(panel)
165
+ self.console.print()
@@ -0,0 +1,174 @@
1
+ """
2
+ error_handler.py - Error handling utilities for CLI
3
+
4
+ This module provides utilities for formatting errors from mci-py
5
+ in a CLI-friendly way, with helpful messages and suggestions.
6
+ """
7
+
8
+ from mcipy import MCIClientError
9
+
10
+
11
+ class ErrorHandler:
12
+ """
13
+ Handles and formats errors for CLI display.
14
+
15
+ This class provides methods to format exceptions from mci-py
16
+ into user-friendly error messages suitable for terminal output.
17
+ """
18
+
19
+ @staticmethod
20
+ def format_mci_client_error(error: MCIClientError) -> str:
21
+ """
22
+ Format MCIClientError for CLI display.
23
+
24
+ Converts technical error messages from MCIClient into
25
+ user-friendly messages with helpful suggestions.
26
+
27
+ Args:
28
+ error: MCIClientError exception from mci-py
29
+
30
+ Returns:
31
+ Formatted error message string
32
+
33
+ Example:
34
+ >>> from mcipy import MCIClientError
35
+ >>> try:
36
+ ... # Some MCIClient operation
37
+ ... pass
38
+ ... except MCIClientError as e:
39
+ ... msg = ErrorHandler.format_mci_client_error(e)
40
+ ... print(msg)
41
+ """
42
+ error_str = str(error)
43
+
44
+ # Check for common error patterns and provide helpful messages
45
+ if "No such file or directory" in error_str:
46
+ return (
47
+ f"❌ Schema file not found\n\n"
48
+ f"Error: {error_str}\n\n"
49
+ f"💡 Suggestions:\n"
50
+ f" • Check that the file path is correct\n"
51
+ f" • Run 'mcix install' to create a default mci.json file\n"
52
+ f" • Use --file option to specify a different schema file"
53
+ )
54
+
55
+ if "Unsupported file extension" in error_str:
56
+ return (
57
+ f"❌ Unsupported file format\n\n"
58
+ f"Error: {error_str}\n\n"
59
+ f"💡 Supported formats:\n"
60
+ f" • .json (JSON format)\n"
61
+ f" • .yaml or .yml (YAML format)"
62
+ )
63
+
64
+ if "Failed to load schema" in error_str:
65
+ # Try to extract the specific parse error
66
+ return (
67
+ f"❌ Failed to load schema\n\n"
68
+ f"Error: {error_str}\n\n"
69
+ f"💡 Suggestions:\n"
70
+ f" • Check that the file contains valid JSON or YAML\n"
71
+ f" • Run 'mcix validate' to see detailed validation errors\n"
72
+ f" • Check for syntax errors like missing commas or brackets"
73
+ )
74
+
75
+ if "Tool not found" in error_str:
76
+ return (
77
+ f"❌ Tool not found\n\n"
78
+ f"Error: {error_str}\n\n"
79
+ f"💡 Suggestions:\n"
80
+ f" • Run 'mcix list' to see all available tools\n"
81
+ f" • Check the tool name for typos\n"
82
+ f" • Ensure the tool is defined in your schema"
83
+ )
84
+
85
+ if "Template variable not found" in error_str:
86
+ return (
87
+ f"❌ Missing template variable\n\n"
88
+ f"Error: {error_str}\n\n"
89
+ f"💡 Suggestions:\n"
90
+ f" • Set required environment variables before running the tool\n"
91
+ f" • Check your schema for {{{{env.VARIABLE}}}} placeholders\n"
92
+ f" • Use --env option to provide environment variables"
93
+ )
94
+
95
+ if "validation" in error_str.lower() or "invalid" in error_str.lower():
96
+ return (
97
+ f"❌ Schema validation error\n\n"
98
+ f"Error: {error_str}\n\n"
99
+ f"💡 Suggestions:\n"
100
+ f" • Run 'mcix validate' for detailed validation errors\n"
101
+ f" • Check that all required fields are present\n"
102
+ f" • Verify that field types match the schema specification"
103
+ )
104
+
105
+ # Default formatting for other errors
106
+ return f"❌ Error\n\n{error_str}"
107
+
108
+ @staticmethod
109
+ def format_generic_error(error: Exception) -> str:
110
+ """
111
+ Format a generic exception for CLI display.
112
+
113
+ Args:
114
+ error: Any Python exception
115
+
116
+ Returns:
117
+ Formatted error message string
118
+
119
+ Example:
120
+ >>> try:
121
+ ... # Some operation
122
+ ... pass
123
+ ... except Exception as e:
124
+ ... msg = ErrorHandler.format_generic_error(e)
125
+ ... print(msg)
126
+ """
127
+ error_str = str(error)
128
+ error_type = type(error).__name__
129
+
130
+ return f"❌ {error_type}\n\n{error_str}"
131
+
132
+ @staticmethod
133
+ def format_file_not_found_error(file_path: str) -> str:
134
+ """
135
+ Format a file not found error with helpful suggestions.
136
+
137
+ Args:
138
+ file_path: Path to the file that was not found
139
+
140
+ Returns:
141
+ Formatted error message string
142
+
143
+ Example:
144
+ >>> msg = ErrorHandler.format_file_not_found_error("mci.json")
145
+ >>> print(msg)
146
+ """
147
+ return (
148
+ f"❌ File not found: {file_path}\n\n"
149
+ f"💡 Suggestions:\n"
150
+ f" • Run 'mcix install' to create a default mci.json file\n"
151
+ f" • Check that you're in the correct directory\n"
152
+ f" • Use --file option to specify a different schema file"
153
+ )
154
+
155
+ @staticmethod
156
+ def format_validation_error(message: str) -> str:
157
+ """
158
+ Format a validation error message.
159
+
160
+ Args:
161
+ message: Validation error message
162
+
163
+ Returns:
164
+ Formatted error message string
165
+
166
+ Example:
167
+ >>> msg = ErrorHandler.format_validation_error("Missing required field: name")
168
+ >>> print(msg)
169
+ """
170
+ return (
171
+ f"❌ Validation Error\n\n"
172
+ f"{message}\n\n"
173
+ f"💡 Run 'mcix validate' for detailed validation information"
174
+ )
mci/utils/timestamp.py ADDED
@@ -0,0 +1,50 @@
1
+ """
2
+ timestamp.py - Timestamp utilities for file output
3
+
4
+ This module provides utilities for generating timestamped filenames
5
+ and ISO 8601 timestamps for use in CLI output files.
6
+ """
7
+
8
+ from datetime import UTC, datetime
9
+
10
+
11
+ def generate_timestamp_filename(format: str, prefix: str = "tools") -> str:
12
+ """
13
+ Generate a timestamped filename for output files.
14
+
15
+ Creates a filename in the format: {prefix}_YYYYMMDD_HHMMSS.{format}
16
+ Uses UTC time for consistency.
17
+
18
+ Args:
19
+ format: File extension (e.g., "json", "yaml")
20
+ prefix: Filename prefix (default: "tools")
21
+
22
+ Returns:
23
+ Timestamped filename string (e.g., "tools_20241029_143022.json")
24
+
25
+ Example:
26
+ >>> filename = generate_timestamp_filename("json")
27
+ >>> print(filename)
28
+ tools_20241029_143022.json
29
+ """
30
+ now = datetime.now(UTC)
31
+ timestamp = now.strftime("%Y%m%d_%H%M%S")
32
+ return f"{prefix}_{timestamp}.{format}"
33
+
34
+
35
+ def get_iso_timestamp() -> str:
36
+ """
37
+ Get current timestamp in ISO 8601 format.
38
+
39
+ Returns UTC timestamp in ISO 8601 format for use in metadata.
40
+
41
+ Returns:
42
+ ISO 8601 formatted timestamp string
43
+
44
+ Example:
45
+ >>> timestamp = get_iso_timestamp()
46
+ >>> print(timestamp)
47
+ 2024-10-29T14:30:22Z
48
+ """
49
+ now = datetime.now(UTC)
50
+ return now.strftime("%Y-%m-%dT%H:%M:%SZ")