iam-policy-validator 1.14.7__py3-none-any.whl → 1.15.0__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.
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.0.dist-info}/METADATA +16 -11
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.0.dist-info}/RECORD +41 -28
- iam_policy_validator-1.15.0.dist-info/entry_points.txt +4 -0
- iam_validator/__version__.py +1 -1
- iam_validator/checks/__init__.py +2 -0
- iam_validator/checks/action_validation.py +91 -27
- iam_validator/checks/not_action_not_resource.py +163 -0
- iam_validator/checks/resource_validation.py +132 -81
- iam_validator/checks/wildcard_resource.py +136 -6
- iam_validator/commands/__init__.py +3 -0
- iam_validator/commands/cache.py +66 -24
- iam_validator/commands/completion.py +94 -15
- iam_validator/commands/mcp.py +210 -0
- iam_validator/commands/query.py +489 -65
- iam_validator/core/aws_service/__init__.py +5 -1
- iam_validator/core/aws_service/cache.py +20 -0
- iam_validator/core/aws_service/fetcher.py +180 -11
- iam_validator/core/aws_service/storage.py +14 -6
- iam_validator/core/aws_service/validators.py +32 -41
- iam_validator/core/check_registry.py +100 -35
- iam_validator/core/config/aws_global_conditions.py +13 -0
- iam_validator/core/config/check_documentation.py +104 -51
- iam_validator/core/config/config_loader.py +39 -3
- iam_validator/core/config/defaults.py +6 -0
- iam_validator/core/constants.py +11 -4
- iam_validator/core/models.py +39 -14
- iam_validator/mcp/__init__.py +162 -0
- iam_validator/mcp/models.py +118 -0
- iam_validator/mcp/server.py +2928 -0
- iam_validator/mcp/session_config.py +319 -0
- iam_validator/mcp/templates/__init__.py +79 -0
- iam_validator/mcp/templates/builtin.py +856 -0
- iam_validator/mcp/tools/__init__.py +72 -0
- iam_validator/mcp/tools/generation.py +888 -0
- iam_validator/mcp/tools/org_config_tools.py +263 -0
- iam_validator/mcp/tools/query.py +395 -0
- iam_validator/mcp/tools/validation.py +376 -0
- iam_validator/sdk/__init__.py +2 -0
- iam_validator/sdk/policy_utils.py +31 -5
- iam_policy_validator-1.14.7.dist-info/entry_points.txt +0 -2
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.0.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,6 +6,8 @@ The generated scripts provide intelligent autocompletion for:
|
|
|
6
6
|
- Command options (--service, --access-level, etc.)
|
|
7
7
|
- Cached AWS service names (for --service flag)
|
|
8
8
|
|
|
9
|
+
Both `iam-validator` and `iam-policy-validator` commands are supported.
|
|
10
|
+
|
|
9
11
|
Usage:
|
|
10
12
|
# Bash completion
|
|
11
13
|
iam-validator completion bash > ~/.bash_completion.d/iam-validator
|
|
@@ -135,7 +137,7 @@ _iam_validator_completion() {{
|
|
|
135
137
|
prev="${{COMP_WORDS[COMP_CWORD-1]}}"
|
|
136
138
|
|
|
137
139
|
# Main commands
|
|
138
|
-
local commands="validate post-to-pr analyze cache sync-services query completion"
|
|
140
|
+
local commands="validate post-to-pr analyze cache sync-services query completion mcp"
|
|
139
141
|
|
|
140
142
|
# Get the command (first non-option argument)
|
|
141
143
|
local cmd=""
|
|
@@ -186,7 +188,7 @@ _iam_validator_completion() {{
|
|
|
186
188
|
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
187
189
|
return 0
|
|
188
190
|
;;
|
|
189
|
-
--resource-type|--condition|--name|--batch-size)
|
|
191
|
+
--resource-type|--condition|--name|--batch-size|--host|--port)
|
|
190
192
|
# Allow any input
|
|
191
193
|
return 0
|
|
192
194
|
;;
|
|
@@ -194,8 +196,23 @@ _iam_validator_completion() {{
|
|
|
194
196
|
COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
|
|
195
197
|
return 0
|
|
196
198
|
;;
|
|
199
|
+
--transport)
|
|
200
|
+
COMPREPLY=( $(compgen -W "stdio sse" -- "$cur") )
|
|
201
|
+
return 0
|
|
202
|
+
;;
|
|
203
|
+
--config)
|
|
204
|
+
# File completion for config
|
|
205
|
+
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
206
|
+
return 0
|
|
207
|
+
;;
|
|
197
208
|
esac
|
|
198
209
|
|
|
210
|
+
# Handle cache list --format specifically
|
|
211
|
+
if [[ "$cmd" == "cache" && "$prev" == "--format" ]]; then
|
|
212
|
+
COMPREPLY=( $(compgen -W "table columns simple" -- "$cur") )
|
|
213
|
+
return 0
|
|
214
|
+
fi
|
|
215
|
+
|
|
199
216
|
# Command-specific completions
|
|
200
217
|
case "$cmd" in
|
|
201
218
|
query)
|
|
@@ -215,10 +232,12 @@ _iam_validator_completion() {{
|
|
|
215
232
|
fi
|
|
216
233
|
|
|
217
234
|
# Complete options for query subcommands
|
|
235
|
+
# Note: --service is optional if --name includes service prefix (e.g., s3:GetObject)
|
|
236
|
+
# Note: --name accepts multiple values and supports wildcards
|
|
218
237
|
local opts=""
|
|
219
238
|
case "$query_subcmd" in
|
|
220
239
|
action)
|
|
221
|
-
opts="--service --name --access-level --resource-type --condition --output"
|
|
240
|
+
opts="--service --name --access-level --resource-type --condition --output --show-condition-keys --show-resource-types --show-access-level"
|
|
222
241
|
;;
|
|
223
242
|
arn)
|
|
224
243
|
opts="--service --name --list-arn-types --output"
|
|
@@ -276,7 +295,16 @@ _iam_validator_completion() {{
|
|
|
276
295
|
COMPREPLY=( $(compgen -W "info list clear refresh prefetch location" -- "$cur") )
|
|
277
296
|
return 0
|
|
278
297
|
fi
|
|
279
|
-
|
|
298
|
+
|
|
299
|
+
# Cache subcommand-specific options
|
|
300
|
+
case "$cache_subcmd" in
|
|
301
|
+
list)
|
|
302
|
+
COMPREPLY=( $(compgen -W "--config --format" -- "$cur") )
|
|
303
|
+
;;
|
|
304
|
+
info|clear|refresh|prefetch|location)
|
|
305
|
+
COMPREPLY=( $(compgen -W "--config" -- "$cur") )
|
|
306
|
+
;;
|
|
307
|
+
esac
|
|
280
308
|
return 0
|
|
281
309
|
;;
|
|
282
310
|
sync-services)
|
|
@@ -284,12 +312,18 @@ _iam_validator_completion() {{
|
|
|
284
312
|
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
285
313
|
return 0
|
|
286
314
|
;;
|
|
315
|
+
mcp)
|
|
316
|
+
opts="--transport --host --port --verbose -v --config --instructions --instructions-file"
|
|
317
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
318
|
+
return 0
|
|
319
|
+
;;
|
|
287
320
|
esac
|
|
288
321
|
|
|
289
322
|
return 0
|
|
290
323
|
}}
|
|
291
324
|
|
|
292
325
|
complete -F _iam_validator_completion iam-validator
|
|
326
|
+
complete -F _iam_validator_completion iam-policy-validator
|
|
293
327
|
'''
|
|
294
328
|
|
|
295
329
|
def _generate_zsh_completion(self) -> str:
|
|
@@ -302,8 +336,8 @@ complete -F _iam_validator_completion iam-validator
|
|
|
302
336
|
# For zsh, we need to format as: 'service1' 'service2' ...
|
|
303
337
|
services_list = " ".join(f"'{svc}'" for svc in cached_services) if cached_services else ""
|
|
304
338
|
|
|
305
|
-
return f"""#compdef iam-validator
|
|
306
|
-
# Zsh completion for iam-validator
|
|
339
|
+
return f"""#compdef iam-validator iam-policy-validator
|
|
340
|
+
# Zsh completion for iam-validator and iam-policy-validator
|
|
307
341
|
# Generated by: iam-validator completion zsh
|
|
308
342
|
|
|
309
343
|
_iam_validator() {{
|
|
@@ -332,24 +366,27 @@ _iam_validator() {{
|
|
|
332
366
|
case $words[1] in
|
|
333
367
|
action)
|
|
334
368
|
_arguments \\
|
|
335
|
-
'--service[AWS service name]:service:($aws_services)' \\
|
|
336
|
-
'
|
|
369
|
+
'--service[AWS service (optional if --name has prefix)]:service:($aws_services)' \\
|
|
370
|
+
'*--name[Action name(s) - supports multiple values and wildcards]:action name:' \\
|
|
337
371
|
'--access-level[Filter by access level]:access level:(read write list tagging permissions-management)' \\
|
|
338
372
|
'--resource-type[Filter by resource type]:resource type:' \\
|
|
339
373
|
'--condition[Filter by condition key]:condition key:' \\
|
|
340
|
-
'--output[Output format]:format:(json yaml text)'
|
|
374
|
+
'--output[Output format]:format:(json yaml text)' \\
|
|
375
|
+
'--show-condition-keys[Show only condition keys for each action]' \\
|
|
376
|
+
'--show-resource-types[Show only resource types for each action]' \\
|
|
377
|
+
'--show-access-level[Show only access level for each action]'
|
|
341
378
|
;;
|
|
342
379
|
arn)
|
|
343
380
|
_arguments \\
|
|
344
|
-
'--service[AWS service name]:service:($aws_services)' \\
|
|
345
|
-
'--name[ARN
|
|
381
|
+
'--service[AWS service (optional if --name has prefix)]:service:($aws_services)' \\
|
|
382
|
+
'--name[ARN type (e.g., bucket or s3:bucket)]:arn type:' \\
|
|
346
383
|
'--list-arn-types[List all ARN types]' \\
|
|
347
384
|
'--output[Output format]:format:(json yaml text)'
|
|
348
385
|
;;
|
|
349
386
|
condition)
|
|
350
387
|
_arguments \\
|
|
351
|
-
'--service[AWS service name]:service:($aws_services)' \\
|
|
352
|
-
'--name[Condition key
|
|
388
|
+
'--service[AWS service (optional if --name has prefix)]:service:($aws_services)' \\
|
|
389
|
+
'--name[Condition key (e.g., prefix or s3:prefix)]:condition key:' \\
|
|
353
390
|
'--output[Output format]:format:(json yaml text)'
|
|
354
391
|
;;
|
|
355
392
|
esac
|
|
@@ -414,8 +451,26 @@ _iam_validator() {{
|
|
|
414
451
|
'(--verbose -v)'{{--verbose,-v}}'[Enable verbose logging]'
|
|
415
452
|
;;
|
|
416
453
|
cache)
|
|
417
|
-
|
|
418
|
-
|
|
454
|
+
local cache_state
|
|
455
|
+
_arguments -C \\
|
|
456
|
+
'1: :_iam_validator_cache_subcommands' \\
|
|
457
|
+
'*::arg:->cache_args' && return 0
|
|
458
|
+
|
|
459
|
+
case $state in
|
|
460
|
+
cache_args)
|
|
461
|
+
case $words[1] in
|
|
462
|
+
info|clear|refresh|prefetch|location)
|
|
463
|
+
_arguments \\
|
|
464
|
+
'--config[Configuration file]:file:_files'
|
|
465
|
+
;;
|
|
466
|
+
list)
|
|
467
|
+
_arguments \\
|
|
468
|
+
'--config[Configuration file]:file:_files' \\
|
|
469
|
+
'--format[Output format]:format:(table columns simple)'
|
|
470
|
+
;;
|
|
471
|
+
esac
|
|
472
|
+
;;
|
|
473
|
+
esac
|
|
419
474
|
;;
|
|
420
475
|
sync-services)
|
|
421
476
|
_arguments \\
|
|
@@ -426,6 +481,16 @@ _iam_validator() {{
|
|
|
426
481
|
_arguments \\
|
|
427
482
|
'1: :(bash zsh)'
|
|
428
483
|
;;
|
|
484
|
+
mcp)
|
|
485
|
+
_arguments \\
|
|
486
|
+
'--transport[Transport protocol]:transport:(stdio sse)' \\
|
|
487
|
+
'--host[Host for SSE transport]:host:' \\
|
|
488
|
+
'--port[Port for SSE transport]:port:' \\
|
|
489
|
+
'(--verbose -v)'{{--verbose,-v}}'[Enable verbose logging]' \\
|
|
490
|
+
'--config[Path to configuration YAML file]:file:_files' \\
|
|
491
|
+
'--instructions[Custom instructions for policy generation]:text:' \\
|
|
492
|
+
'--instructions-file[Path to file containing custom instructions]:file:_files'
|
|
493
|
+
;;
|
|
429
494
|
esac
|
|
430
495
|
;;
|
|
431
496
|
esac
|
|
@@ -441,6 +506,7 @@ _iam_validator_commands() {{
|
|
|
441
506
|
'sync-services:Sync/download all AWS service definitions for offline use'
|
|
442
507
|
'query:Query AWS service definitions (actions, ARNs, condition keys)'
|
|
443
508
|
'completion:Generate shell completion scripts (bash or zsh)'
|
|
509
|
+
'mcp:Start MCP server for AI assistant integration'
|
|
444
510
|
)
|
|
445
511
|
_describe 'command' commands
|
|
446
512
|
}}
|
|
@@ -455,6 +521,19 @@ _iam_validator_query_subcommands() {{
|
|
|
455
521
|
_describe 'query subcommand' subcommands
|
|
456
522
|
}}
|
|
457
523
|
|
|
524
|
+
_iam_validator_cache_subcommands() {{
|
|
525
|
+
local -a subcommands
|
|
526
|
+
subcommands=(
|
|
527
|
+
'info:Show cache information and statistics'
|
|
528
|
+
'list:List all cached AWS services'
|
|
529
|
+
'clear:Clear all cached AWS service definitions'
|
|
530
|
+
'refresh:Refresh all cached services with fresh data'
|
|
531
|
+
'prefetch:Pre-fetch common AWS services (without clearing)'
|
|
532
|
+
'location:Show cache directory location'
|
|
533
|
+
)
|
|
534
|
+
_describe 'cache subcommand' subcommands
|
|
535
|
+
}}
|
|
536
|
+
|
|
458
537
|
_iam_validator "$@"
|
|
459
538
|
"""
|
|
460
539
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""MCP command for IAM Policy Validator."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from iam_validator.commands.base import Command
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MCPCommand(Command):
|
|
10
|
+
"""Command to start MCP server for AI assistant integration."""
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def name(self) -> str:
|
|
14
|
+
return "mcp"
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def help(self) -> str:
|
|
18
|
+
return "Start MCP server for AI assistant integration"
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def epilog(self) -> str:
|
|
22
|
+
return """
|
|
23
|
+
Examples:
|
|
24
|
+
# Start MCP server with stdio transport (for Claude Desktop)
|
|
25
|
+
iam-validator mcp
|
|
26
|
+
|
|
27
|
+
# Start with SSE transport on custom host/port
|
|
28
|
+
iam-validator mcp --transport sse --host 127.0.0.1 --port 8000
|
|
29
|
+
|
|
30
|
+
# Start with config preloaded
|
|
31
|
+
iam-validator mcp --config ./config.yaml
|
|
32
|
+
|
|
33
|
+
Claude Desktop Configuration:
|
|
34
|
+
Add to your claude_desktop_config.json:
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"iam-validator": {
|
|
38
|
+
"command": "iam-validator",
|
|
39
|
+
"args": ["mcp"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
With configuration:
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"iam-validator": {
|
|
48
|
+
"command": "iam-validator",
|
|
49
|
+
"args": ["mcp", "--config", "/path/to/config.yaml"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Config File (YAML) - same format as CLI validator:
|
|
55
|
+
settings:
|
|
56
|
+
fail_on_severity: [error, critical, high]
|
|
57
|
+
wildcard_resource:
|
|
58
|
+
severity: critical
|
|
59
|
+
sensitive_action:
|
|
60
|
+
enabled: true
|
|
61
|
+
severity: high
|
|
62
|
+
|
|
63
|
+
Features:
|
|
64
|
+
- Policy generation from natural language descriptions
|
|
65
|
+
- Template-based policy generation (15 built-in templates)
|
|
66
|
+
- Policy validation with 19 security checks
|
|
67
|
+
- AWS service queries (actions, resources, condition keys)
|
|
68
|
+
- Security enforcement (auto-adds required conditions)
|
|
69
|
+
- Session-wide configuration management
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
73
|
+
"""Add MCP command arguments."""
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--transport",
|
|
76
|
+
choices=["stdio", "sse"],
|
|
77
|
+
default="stdio",
|
|
78
|
+
help="Transport protocol (default: stdio for Claude Desktop, sse for HTTP/SSE)",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--host",
|
|
83
|
+
default="127.0.0.1",
|
|
84
|
+
help="Host for SSE transport (default: 127.0.0.1)",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--port",
|
|
89
|
+
type=int,
|
|
90
|
+
default=8000,
|
|
91
|
+
help="Port for SSE transport (default: 8000)",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--verbose",
|
|
96
|
+
"-v",
|
|
97
|
+
action="store_true",
|
|
98
|
+
help="Enable verbose logging",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"--config",
|
|
103
|
+
type=str,
|
|
104
|
+
metavar="FILE",
|
|
105
|
+
help="Path to configuration YAML file to load at startup",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
async def execute(self, args: argparse.Namespace) -> int:
|
|
109
|
+
"""Execute the MCP server command.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
args: Parsed command-line arguments
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Exit code (0 for success, non-zero for failure)
|
|
116
|
+
"""
|
|
117
|
+
# Check if fastmcp is installed
|
|
118
|
+
try:
|
|
119
|
+
import fastmcp # noqa: F401
|
|
120
|
+
except ImportError:
|
|
121
|
+
logging.error(
|
|
122
|
+
"FastMCP is not installed. Install with: uv sync --extra mcp or pip install 'iam-validator[mcp]'"
|
|
123
|
+
)
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
# Import and create the MCP server
|
|
127
|
+
try:
|
|
128
|
+
from iam_validator.mcp import create_server
|
|
129
|
+
except ImportError as e:
|
|
130
|
+
logging.error(f"Failed to import MCP server: {e}")
|
|
131
|
+
logging.error(
|
|
132
|
+
"Make sure the MCP module is properly installed with: uv sync --extra mcp"
|
|
133
|
+
)
|
|
134
|
+
return 1
|
|
135
|
+
|
|
136
|
+
# Set up logging level
|
|
137
|
+
if args.verbose:
|
|
138
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
139
|
+
else:
|
|
140
|
+
logging.basicConfig(level=logging.INFO)
|
|
141
|
+
|
|
142
|
+
# Load config if provided
|
|
143
|
+
if args.config:
|
|
144
|
+
try:
|
|
145
|
+
from pathlib import Path
|
|
146
|
+
|
|
147
|
+
from iam_validator.mcp.session_config import SessionConfigManager
|
|
148
|
+
|
|
149
|
+
config_path = Path(args.config)
|
|
150
|
+
if not config_path.exists():
|
|
151
|
+
logging.error(f"Config file not found: {args.config}")
|
|
152
|
+
return 1
|
|
153
|
+
|
|
154
|
+
config, warnings = SessionConfigManager.load_from_file(str(config_path))
|
|
155
|
+
|
|
156
|
+
for warning in warnings:
|
|
157
|
+
logging.warning(f"Config warning: {warning}")
|
|
158
|
+
|
|
159
|
+
logging.info(f"Loaded config from: {args.config}")
|
|
160
|
+
|
|
161
|
+
# Log config summary
|
|
162
|
+
if config.settings:
|
|
163
|
+
fail_on = config.settings.get("fail_on_severity", [])
|
|
164
|
+
if fail_on:
|
|
165
|
+
logging.info(f" Fail on severity: {fail_on}")
|
|
166
|
+
|
|
167
|
+
if config.checks_config:
|
|
168
|
+
disabled_checks = [
|
|
169
|
+
k for k, v in config.checks_config.items() if not v.get("enabled", True)
|
|
170
|
+
]
|
|
171
|
+
if disabled_checks:
|
|
172
|
+
logging.info(f" Disabled checks: {disabled_checks}")
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logging.error(f"Failed to load config: {e}")
|
|
176
|
+
if args.verbose:
|
|
177
|
+
import traceback
|
|
178
|
+
|
|
179
|
+
traceback.print_exc()
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
# Create and run the server
|
|
184
|
+
server = create_server()
|
|
185
|
+
|
|
186
|
+
if args.transport == "stdio":
|
|
187
|
+
logging.info("Starting MCP server with stdio transport...")
|
|
188
|
+
logging.info("Waiting for client connection...")
|
|
189
|
+
# Run with stdio transport using async method
|
|
190
|
+
await server.run_stdio_async()
|
|
191
|
+
else:
|
|
192
|
+
# SSE transport
|
|
193
|
+
logging.info(
|
|
194
|
+
f"Starting MCP server with SSE transport on {args.host}:{args.port}..."
|
|
195
|
+
)
|
|
196
|
+
# Run with SSE transport using async method
|
|
197
|
+
await server.run_http_async(host=args.host, port=args.port)
|
|
198
|
+
|
|
199
|
+
return 0
|
|
200
|
+
|
|
201
|
+
except KeyboardInterrupt:
|
|
202
|
+
logging.info("\nMCP server stopped by user")
|
|
203
|
+
return 0
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logging.error(f"Failed to start MCP server: {e}")
|
|
206
|
+
if args.verbose:
|
|
207
|
+
import traceback
|
|
208
|
+
|
|
209
|
+
traceback.print_exc()
|
|
210
|
+
return 1
|