agent-audit 0.1.0__tar.gz

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 (36) hide show
  1. agent_audit-0.1.0/PKG-INFO +219 -0
  2. agent_audit-0.1.0/README.md +181 -0
  3. agent_audit-0.1.0/agent_audit/__init__.py +3 -0
  4. agent_audit-0.1.0/agent_audit/__main__.py +13 -0
  5. agent_audit-0.1.0/agent_audit/cli/__init__.py +1 -0
  6. agent_audit-0.1.0/agent_audit/cli/commands/__init__.py +1 -0
  7. agent_audit-0.1.0/agent_audit/cli/commands/init.py +44 -0
  8. agent_audit-0.1.0/agent_audit/cli/commands/inspect.py +236 -0
  9. agent_audit-0.1.0/agent_audit/cli/commands/scan.py +329 -0
  10. agent_audit-0.1.0/agent_audit/cli/formatters/__init__.py +1 -0
  11. agent_audit-0.1.0/agent_audit/cli/formatters/json.py +138 -0
  12. agent_audit-0.1.0/agent_audit/cli/formatters/sarif.py +155 -0
  13. agent_audit-0.1.0/agent_audit/cli/formatters/terminal.py +221 -0
  14. agent_audit-0.1.0/agent_audit/cli/main.py +34 -0
  15. agent_audit-0.1.0/agent_audit/config/__init__.py +1 -0
  16. agent_audit-0.1.0/agent_audit/config/ignore.py +477 -0
  17. agent_audit-0.1.0/agent_audit/core_utils/__init__.py +1 -0
  18. agent_audit-0.1.0/agent_audit/models/__init__.py +18 -0
  19. agent_audit-0.1.0/agent_audit/models/finding.py +159 -0
  20. agent_audit-0.1.0/agent_audit/models/risk.py +77 -0
  21. agent_audit-0.1.0/agent_audit/models/tool.py +182 -0
  22. agent_audit-0.1.0/agent_audit/rules/__init__.py +6 -0
  23. agent_audit-0.1.0/agent_audit/rules/engine.py +503 -0
  24. agent_audit-0.1.0/agent_audit/rules/loader.py +160 -0
  25. agent_audit-0.1.0/agent_audit/scanners/__init__.py +5 -0
  26. agent_audit-0.1.0/agent_audit/scanners/base.py +32 -0
  27. agent_audit-0.1.0/agent_audit/scanners/config_scanner.py +390 -0
  28. agent_audit-0.1.0/agent_audit/scanners/mcp_config_scanner.py +321 -0
  29. agent_audit-0.1.0/agent_audit/scanners/mcp_inspector.py +421 -0
  30. agent_audit-0.1.0/agent_audit/scanners/python_scanner.py +544 -0
  31. agent_audit-0.1.0/agent_audit/scanners/secret_scanner.py +521 -0
  32. agent_audit-0.1.0/agent_audit/utils/__init__.py +21 -0
  33. agent_audit-0.1.0/agent_audit/utils/compat.py +98 -0
  34. agent_audit-0.1.0/agent_audit/utils/mcp_client.py +343 -0
  35. agent_audit-0.1.0/agent_audit/version.py +3 -0
  36. agent_audit-0.1.0/pyproject.toml +56 -0
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.1
2
+ Name: agent-audit
3
+ Version: 0.1.0
4
+ Summary: Security scanner for AI agents and MCP configurations - Based on OWASP Agentic Top 10
5
+ Home-page: https://github.com/HeadyZhang/agent-audit
6
+ License: MIT
7
+ Keywords: ai,agent,security,mcp,audit,owasp,vulnerability,scanner
8
+ Author: Agent Security Team
9
+ Author-email: security@example.com
10
+ Requires-Python: >=3.9,<4.0
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Security
24
+ Classifier: Topic :: Software Development :: Quality Assurance
25
+ Classifier: Topic :: Software Development :: Testing
26
+ Requires-Dist: aiofiles (>=23.0,<24.0)
27
+ Requires-Dist: aiohttp (>=3.9,<4.0)
28
+ Requires-Dist: click (>=8.1.0,<9.0.0)
29
+ Requires-Dist: pydantic (>=2.0,<3.0)
30
+ Requires-Dist: pyyaml (>=6.0,<7.0)
31
+ Requires-Dist: rich (>=13.0.0,<14.0.0)
32
+ Project-URL: Bug Tracker, https://github.com/HeadyZhang/agent-audit/issues
33
+ Project-URL: Changelog, https://github.com/HeadyZhang/agent-audit/releases
34
+ Project-URL: Documentation, https://github.com/HeadyZhang/agent-audit#readme
35
+ Project-URL: Repository, https://github.com/HeadyZhang/agent-audit
36
+ Description-Content-Type: text/markdown
37
+
38
+ # Agent Audit
39
+
40
+ [![PyPI version](https://badge.fury.io/py/agent-audit.svg)](https://badge.fury.io/py/agent-audit)
41
+ [![Python](https://img.shields.io/pypi/pyversions/agent-audit.svg)](https://pypi.org/project/agent-audit/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+ [![CI](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml/badge.svg)](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml)
44
+ [![codecov](https://codecov.io/gh/HeadyZhang/agent-audit/branch/master/graph/badge.svg)](https://codecov.io/gh/HeadyZhang/agent-audit)
45
+
46
+ > 🛡️ Security scanner for AI agents and MCP configurations. Detects vulnerabilities based on the **OWASP Agentic Top 10**.
47
+
48
+ <p align="center">
49
+ <img src="docs/demo.gif" alt="Agent Audit Demo" width="800">
50
+ </p>
51
+
52
+ ## ✨ Features
53
+
54
+ - **🔍 Python AST Scanning** - Detects dangerous patterns like `shell=True`, `eval()`, and tainted input flows
55
+ - **⚙️ MCP Configuration Scanning** - Validates MCP server configurations for security issues
56
+ - **🔐 Secret Detection** - Finds hardcoded credentials (AWS keys, API tokens, private keys)
57
+ - **🌐 Runtime MCP Inspection** - Probes MCP servers without executing tools ("Agent Nmap")
58
+ - **📊 Multiple Output Formats** - Terminal, JSON, SARIF (for GitHub Code Scanning), Markdown
59
+
60
+ ## 🚀 Quick Start
61
+
62
+ ### Installation
63
+
64
+ ```bash
65
+ pip install agent-audit
66
+ ```
67
+
68
+ ### Basic Usage
69
+
70
+ ```bash
71
+ # Scan current directory
72
+ agent-audit scan .
73
+
74
+ # Scan with JSON output
75
+ agent-audit scan ./my-agent --format json
76
+
77
+ # Scan with SARIF output for GitHub Code Scanning
78
+ agent-audit scan . --format sarif --output results.sarif
79
+
80
+ # Fail CI on critical findings only
81
+ agent-audit scan . --fail-on critical
82
+
83
+ # Inspect an MCP server at runtime
84
+ agent-audit inspect stdio -- npx -y @modelcontextprotocol/server-filesystem /tmp
85
+ ```
86
+
87
+ ## 🔗 GitHub Action
88
+
89
+ Add Agent Audit to your CI/CD pipeline with just a few lines:
90
+
91
+ ```yaml
92
+ name: Security Scan
93
+ on: [push, pull_request]
94
+
95
+ jobs:
96
+ agent-audit:
97
+ runs-on: ubuntu-latest
98
+ steps:
99
+ - uses: actions/checkout@v4
100
+
101
+ - name: Run Agent Audit
102
+ uses: HeadyZhang/agent-audit@v1
103
+ with:
104
+ path: '.'
105
+ fail-on: 'high'
106
+ upload-sarif: 'true'
107
+ ```
108
+
109
+ ### Action Inputs
110
+
111
+ | Input | Description | Default |
112
+ |-------|-------------|---------|
113
+ | `path` | Path to scan | `.` |
114
+ | `format` | Output format: `terminal`, `json`, `sarif`, `markdown` | `sarif` |
115
+ | `severity` | Minimum severity to report: `info`, `low`, `medium`, `high`, `critical` | `low` |
116
+ | `fail-on` | Exit with error if findings at this severity | `high` |
117
+ | `baseline` | Path to baseline file for incremental scanning | - |
118
+ | `upload-sarif` | Upload SARIF to GitHub Security tab | `true` |
119
+
120
+ ## 🎯 Detected Issues
121
+
122
+ | Rule ID | Title | Severity |
123
+ |---------|-------|----------|
124
+ | AGENT-001 | Command Injection via Unsanitized Input | 🔴 Critical |
125
+ | AGENT-002 | Excessive Agent Permissions | 🟡 Medium |
126
+ | AGENT-003 | Potential Data Exfiltration Chain | 🟠 High |
127
+ | AGENT-004 | Hardcoded Credentials | 🔴 Critical |
128
+ | AGENT-005 | Unverified MCP Server | 🟠 High |
129
+
130
+ ## ⚙️ Configuration
131
+
132
+ Create a `.agent-audit.yaml` file to customize scanning:
133
+
134
+ ```yaml
135
+ # Allowed network hosts (reduces AGENT-003 confidence)
136
+ allowed_hosts:
137
+ - "*.internal.company.com"
138
+ - "api.openai.com"
139
+
140
+ # Ignore rules
141
+ ignore:
142
+ - rule_id: AGENT-003
143
+ paths:
144
+ - "auth/**"
145
+ reason: "Auth module legitimately communicates externally"
146
+
147
+ # Scan settings
148
+ scan:
149
+ exclude:
150
+ - "tests/**"
151
+ - "venv/**"
152
+ min_severity: low
153
+ fail_on: high
154
+ ```
155
+
156
+ ## 📈 Baseline Scanning
157
+
158
+ Track new findings incrementally:
159
+
160
+ ```bash
161
+ # Save current findings as baseline
162
+ agent-audit scan . --save-baseline baseline.json
163
+
164
+ # Only report new findings
165
+ agent-audit scan . --baseline baseline.json
166
+ ```
167
+
168
+ ## 📖 CLI Reference
169
+
170
+ ```
171
+ Usage: agent-audit [OPTIONS] COMMAND [ARGS]...
172
+
173
+ Commands:
174
+ scan Scan agent code and configurations
175
+ inspect Inspect an MCP server at runtime
176
+ init Initialize configuration file
177
+
178
+ Options:
179
+ --version Show version
180
+ -v Enable verbose output
181
+ -q Only show errors
182
+ --help Show this message
183
+ ```
184
+
185
+ ## 🛠️ Development
186
+
187
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
188
+
189
+ ```bash
190
+ # Clone the repository
191
+ git clone https://github.com/HeadyZhang/agent-audit
192
+ cd agent-security-suite
193
+
194
+ # Install dependencies
195
+ cd packages/core && poetry install
196
+ cd ../audit && poetry install
197
+
198
+ # Run tests
199
+ poetry run pytest tests/ -v
200
+
201
+ # Run the scanner
202
+ poetry run agent-audit scan .
203
+ ```
204
+
205
+ ## 📄 License
206
+
207
+ MIT License - see [LICENSE](LICENSE) for details.
208
+
209
+ ## 🙏 Acknowledgments
210
+
211
+ - Based on the [OWASP Agentic Security Top 10](https://owasp.org/www-project-agentic-security/)
212
+ - Inspired by the need for better AI agent security tooling
213
+
214
+ ---
215
+
216
+ <p align="center">
217
+ Made with ❤️ for the AI agent security community
218
+ </p>
219
+
@@ -0,0 +1,181 @@
1
+ # Agent Audit
2
+
3
+ [![PyPI version](https://badge.fury.io/py/agent-audit.svg)](https://badge.fury.io/py/agent-audit)
4
+ [![Python](https://img.shields.io/pypi/pyversions/agent-audit.svg)](https://pypi.org/project/agent-audit/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![CI](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml/badge.svg)](https://github.com/HeadyZhang/agent-audit/actions/workflows/ci.yml)
7
+ [![codecov](https://codecov.io/gh/HeadyZhang/agent-audit/branch/master/graph/badge.svg)](https://codecov.io/gh/HeadyZhang/agent-audit)
8
+
9
+ > 🛡️ Security scanner for AI agents and MCP configurations. Detects vulnerabilities based on the **OWASP Agentic Top 10**.
10
+
11
+ <p align="center">
12
+ <img src="docs/demo.gif" alt="Agent Audit Demo" width="800">
13
+ </p>
14
+
15
+ ## ✨ Features
16
+
17
+ - **🔍 Python AST Scanning** - Detects dangerous patterns like `shell=True`, `eval()`, and tainted input flows
18
+ - **⚙️ MCP Configuration Scanning** - Validates MCP server configurations for security issues
19
+ - **🔐 Secret Detection** - Finds hardcoded credentials (AWS keys, API tokens, private keys)
20
+ - **🌐 Runtime MCP Inspection** - Probes MCP servers without executing tools ("Agent Nmap")
21
+ - **📊 Multiple Output Formats** - Terminal, JSON, SARIF (for GitHub Code Scanning), Markdown
22
+
23
+ ## 🚀 Quick Start
24
+
25
+ ### Installation
26
+
27
+ ```bash
28
+ pip install agent-audit
29
+ ```
30
+
31
+ ### Basic Usage
32
+
33
+ ```bash
34
+ # Scan current directory
35
+ agent-audit scan .
36
+
37
+ # Scan with JSON output
38
+ agent-audit scan ./my-agent --format json
39
+
40
+ # Scan with SARIF output for GitHub Code Scanning
41
+ agent-audit scan . --format sarif --output results.sarif
42
+
43
+ # Fail CI on critical findings only
44
+ agent-audit scan . --fail-on critical
45
+
46
+ # Inspect an MCP server at runtime
47
+ agent-audit inspect stdio -- npx -y @modelcontextprotocol/server-filesystem /tmp
48
+ ```
49
+
50
+ ## 🔗 GitHub Action
51
+
52
+ Add Agent Audit to your CI/CD pipeline with just a few lines:
53
+
54
+ ```yaml
55
+ name: Security Scan
56
+ on: [push, pull_request]
57
+
58
+ jobs:
59
+ agent-audit:
60
+ runs-on: ubuntu-latest
61
+ steps:
62
+ - uses: actions/checkout@v4
63
+
64
+ - name: Run Agent Audit
65
+ uses: HeadyZhang/agent-audit@v1
66
+ with:
67
+ path: '.'
68
+ fail-on: 'high'
69
+ upload-sarif: 'true'
70
+ ```
71
+
72
+ ### Action Inputs
73
+
74
+ | Input | Description | Default |
75
+ |-------|-------------|---------|
76
+ | `path` | Path to scan | `.` |
77
+ | `format` | Output format: `terminal`, `json`, `sarif`, `markdown` | `sarif` |
78
+ | `severity` | Minimum severity to report: `info`, `low`, `medium`, `high`, `critical` | `low` |
79
+ | `fail-on` | Exit with error if findings at this severity | `high` |
80
+ | `baseline` | Path to baseline file for incremental scanning | - |
81
+ | `upload-sarif` | Upload SARIF to GitHub Security tab | `true` |
82
+
83
+ ## 🎯 Detected Issues
84
+
85
+ | Rule ID | Title | Severity |
86
+ |---------|-------|----------|
87
+ | AGENT-001 | Command Injection via Unsanitized Input | 🔴 Critical |
88
+ | AGENT-002 | Excessive Agent Permissions | 🟡 Medium |
89
+ | AGENT-003 | Potential Data Exfiltration Chain | 🟠 High |
90
+ | AGENT-004 | Hardcoded Credentials | 🔴 Critical |
91
+ | AGENT-005 | Unverified MCP Server | 🟠 High |
92
+
93
+ ## ⚙️ Configuration
94
+
95
+ Create a `.agent-audit.yaml` file to customize scanning:
96
+
97
+ ```yaml
98
+ # Allowed network hosts (reduces AGENT-003 confidence)
99
+ allowed_hosts:
100
+ - "*.internal.company.com"
101
+ - "api.openai.com"
102
+
103
+ # Ignore rules
104
+ ignore:
105
+ - rule_id: AGENT-003
106
+ paths:
107
+ - "auth/**"
108
+ reason: "Auth module legitimately communicates externally"
109
+
110
+ # Scan settings
111
+ scan:
112
+ exclude:
113
+ - "tests/**"
114
+ - "venv/**"
115
+ min_severity: low
116
+ fail_on: high
117
+ ```
118
+
119
+ ## 📈 Baseline Scanning
120
+
121
+ Track new findings incrementally:
122
+
123
+ ```bash
124
+ # Save current findings as baseline
125
+ agent-audit scan . --save-baseline baseline.json
126
+
127
+ # Only report new findings
128
+ agent-audit scan . --baseline baseline.json
129
+ ```
130
+
131
+ ## 📖 CLI Reference
132
+
133
+ ```
134
+ Usage: agent-audit [OPTIONS] COMMAND [ARGS]...
135
+
136
+ Commands:
137
+ scan Scan agent code and configurations
138
+ inspect Inspect an MCP server at runtime
139
+ init Initialize configuration file
140
+
141
+ Options:
142
+ --version Show version
143
+ -v Enable verbose output
144
+ -q Only show errors
145
+ --help Show this message
146
+ ```
147
+
148
+ ## 🛠️ Development
149
+
150
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
151
+
152
+ ```bash
153
+ # Clone the repository
154
+ git clone https://github.com/HeadyZhang/agent-audit
155
+ cd agent-security-suite
156
+
157
+ # Install dependencies
158
+ cd packages/core && poetry install
159
+ cd ../audit && poetry install
160
+
161
+ # Run tests
162
+ poetry run pytest tests/ -v
163
+
164
+ # Run the scanner
165
+ poetry run agent-audit scan .
166
+ ```
167
+
168
+ ## 📄 License
169
+
170
+ MIT License - see [LICENSE](LICENSE) for details.
171
+
172
+ ## 🙏 Acknowledgments
173
+
174
+ - Based on the [OWASP Agentic Security Top 10](https://owasp.org/www-project-agentic-security/)
175
+ - Inspired by the need for better AI agent security tooling
176
+
177
+ ---
178
+
179
+ <p align="center">
180
+ Made with ❤️ for the AI agent security community
181
+ </p>
@@ -0,0 +1,3 @@
1
+ """Agent Audit - Security scanner for AI agents and MCP configurations."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,13 @@
1
+ """Entry point for running agent-audit as a module."""
2
+
3
+ import sys
4
+
5
+ # Windows event loop policy fix - must be set before any asyncio imports
6
+ if sys.platform == "win32":
7
+ import asyncio
8
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
9
+
10
+ from agent_audit.cli.main import cli
11
+
12
+ if __name__ == "__main__":
13
+ cli()
@@ -0,0 +1 @@
1
+ """CLI module for agent-audit."""
@@ -0,0 +1 @@
1
+ """CLI commands for agent-audit."""
@@ -0,0 +1,44 @@
1
+ """Init command for creating configuration files."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import click
7
+ from rich.console import Console
8
+
9
+ from agent_audit.config.ignore import create_default_config
10
+
11
+ console = Console()
12
+
13
+
14
+ @click.command()
15
+ @click.option('--force', '-f', is_flag=True, help='Overwrite existing config')
16
+ def init(force: bool):
17
+ """
18
+ Initialize agent-audit configuration.
19
+
20
+ Creates a .agent-audit.yaml file in the current directory with
21
+ default settings and example ignore rules.
22
+
23
+ Examples:
24
+
25
+ agent-audit init
26
+
27
+ agent-audit init --force
28
+ """
29
+ config_path = Path('.agent-audit.yaml')
30
+
31
+ if config_path.exists() and not force:
32
+ console.print(f"[yellow]Configuration file already exists: {config_path}[/yellow]")
33
+ console.print("Use --force to overwrite")
34
+ sys.exit(1)
35
+
36
+ config_content = create_default_config()
37
+ config_path.write_text(config_content, encoding="utf-8")
38
+
39
+ console.print(f"[green]Created configuration file: {config_path}[/green]")
40
+ console.print()
41
+ console.print("Edit this file to:")
42
+ console.print(" - Add allowed hosts for network destinations")
43
+ console.print(" - Configure ignore rules for false positives")
44
+ console.print(" - Set scan exclusion patterns")
@@ -0,0 +1,236 @@
1
+ """Inspect command for probing MCP servers."""
2
+
3
+ import asyncio
4
+ import sys
5
+ from typing import Optional
6
+
7
+ import click
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich.panel import Panel
11
+
12
+ from agent_audit.scanners.mcp_inspector import MCPInspector, MCPInspectionResult
13
+ from agent_audit.utils.mcp_client import TransportType
14
+
15
+ console = Console()
16
+
17
+
18
+ def render_inspection_result(result: MCPInspectionResult, output_format: str = "terminal"):
19
+ """Render inspection result to the console."""
20
+ if output_format == "json":
21
+ _render_json(result)
22
+ else:
23
+ _render_terminal(result)
24
+
25
+
26
+ def _render_terminal(result: MCPInspectionResult):
27
+ """Render result as Rich terminal output."""
28
+ # Status indicator
29
+ if result.connected:
30
+ status = "[green]✓ Connected[/green]"
31
+ border_style = "blue" if result.risk_score < 5 else "red"
32
+ else:
33
+ status = "[red]✗ Failed[/red]"
34
+ border_style = "red"
35
+
36
+ # Header panel
37
+ header_text = (
38
+ f"[bold]MCP Server Inspection[/bold]\n"
39
+ f"Server: {result.server_name}"
40
+ )
41
+ if result.server_version:
42
+ header_text += f" v{result.server_version}"
43
+ header_text += "\n"
44
+ header_text += f"Status: {status} | Response: {result.response_time_ms:.0f}ms\n"
45
+ header_text += f"Risk Score: {result.risk_score:.1f}/10"
46
+
47
+ console.print(Panel.fit(header_text, border_style=border_style))
48
+
49
+ if not result.connected:
50
+ console.print(f"[red]Error: {result.connection_error}[/red]")
51
+ return
52
+
53
+ # Capabilities
54
+ if result.capabilities_declared:
55
+ caps = ", ".join(result.capabilities_declared) or "none"
56
+ console.print(f"\n[dim]Capabilities:[/dim] {caps}")
57
+
58
+ # Tools table
59
+ console.print(f"\n[bold]Tools ({result.tool_count})[/bold]")
60
+
61
+ if result.tools:
62
+ tool_table = Table(show_header=True, header_style="bold cyan")
63
+ tool_table.add_column("Tool", style="cyan")
64
+ tool_table.add_column("Permissions", style="yellow")
65
+ tool_table.add_column("Risk", justify="center")
66
+ tool_table.add_column("Validation")
67
+
68
+ risk_emoji = {
69
+ 1: "🟢", # SAFE
70
+ 2: "🟢", # LOW
71
+ 3: "🟡", # MEDIUM
72
+ 4: "🟠", # HIGH
73
+ 5: "🔴", # CRITICAL
74
+ }
75
+
76
+ for tool in result.tools:
77
+ perms = ", ".join(p.name for p in tool.permissions) or "none"
78
+ risk_value = tool.risk_level.value if hasattr(tool.risk_level, 'value') else 1
79
+ risk = risk_emoji.get(risk_value, "⚪")
80
+ validation = "✅" if tool.has_input_validation else "❌"
81
+
82
+ # Truncate description if needed
83
+ tool_name = tool.name
84
+ if len(tool_name) > 30:
85
+ tool_name = tool_name[:27] + "..."
86
+
87
+ tool_table.add_row(tool_name, perms, risk, validation)
88
+
89
+ console.print(tool_table)
90
+ else:
91
+ console.print("[dim]No tools exposed[/dim]")
92
+
93
+ # Resources
94
+ if result.resources:
95
+ console.print(f"\n[bold]Resources ({result.resource_count})[/bold]")
96
+ for res in result.resources[:10]: # Limit display
97
+ uri = res.get('uri', 'unknown')
98
+ console.print(f" 📄 {uri}")
99
+ if result.resource_count > 10:
100
+ console.print(f" [dim]... and {result.resource_count - 10} more[/dim]")
101
+
102
+ # Prompts
103
+ if result.prompts:
104
+ console.print(f"\n[bold]Prompts ({result.prompt_count})[/bold]")
105
+ for prompt in result.prompts[:10]:
106
+ name = prompt.get('name', 'unknown')
107
+ console.print(f" 💬 {name}")
108
+ if result.prompt_count > 10:
109
+ console.print(f" [dim]... and {result.prompt_count - 10} more[/dim]")
110
+
111
+ # Security findings
112
+ if result.findings:
113
+ console.print(f"\n[bold red]Security Findings ({len(result.findings)})[/bold red]")
114
+
115
+ severity_colors = {
116
+ "critical": "red",
117
+ "high": "red",
118
+ "medium": "yellow",
119
+ "low": "blue"
120
+ }
121
+
122
+ for finding in result.findings:
123
+ severity = finding.get("severity", "medium")
124
+ color = severity_colors.get(severity, "white")
125
+ desc = finding.get("description", "Unknown issue")
126
+ tool_name = finding.get("tool", "")
127
+
128
+ if tool_name:
129
+ console.print(f" [{color}]⚠ {severity.upper()}[/{color}]: {desc} (tool: {tool_name})")
130
+ else:
131
+ console.print(f" [{color}]⚠ {severity.upper()}[/{color}]: {desc}")
132
+
133
+
134
+ def _render_json(result: MCPInspectionResult):
135
+ """Render result as JSON."""
136
+ import json
137
+
138
+ output = {
139
+ "server_name": result.server_name,
140
+ "server_version": result.server_version,
141
+ "transport": result.transport.value,
142
+ "connected": result.connected,
143
+ "connection_error": result.connection_error,
144
+ "response_time_ms": result.response_time_ms,
145
+ "risk_score": result.risk_score,
146
+ "capabilities": result.capabilities_declared,
147
+ "tool_count": result.tool_count,
148
+ "tools": [t.to_dict() for t in result.tools],
149
+ "resource_count": result.resource_count,
150
+ "resources": result.resources,
151
+ "prompt_count": result.prompt_count,
152
+ "prompts": result.prompts,
153
+ "findings": result.findings,
154
+ }
155
+
156
+ console.print_json(json.dumps(output, indent=2))
157
+
158
+
159
+ async def run_inspect_async(
160
+ target: str,
161
+ transport: Optional[str],
162
+ timeout: int,
163
+ output_format: str
164
+ ) -> int:
165
+ """Run the inspection asynchronously."""
166
+ inspector = MCPInspector(timeout=timeout)
167
+
168
+ # Determine transport type
169
+ transport_type: Optional[TransportType] = None
170
+ if transport:
171
+ transport_type = TransportType(transport)
172
+
173
+ result = await inspector.inspect(target, transport_type)
174
+ render_inspection_result(result, output_format)
175
+
176
+ # Return exit code based on risk
177
+ if not result.connected:
178
+ return 2 # Connection failure
179
+ elif result.risk_score >= 7.0:
180
+ return 1 # High risk
181
+ return 0
182
+
183
+
184
+ def run_inspect(
185
+ target: str,
186
+ transport: Optional[str],
187
+ timeout: int,
188
+ output_format: str
189
+ ) -> int:
190
+ """Run the inspection."""
191
+ return asyncio.run(run_inspect_async(target, transport, timeout, output_format))
192
+
193
+
194
+ @click.command()
195
+ @click.argument('transport_type', type=click.Choice(['stdio', 'sse']), required=True)
196
+ @click.argument('target', nargs=-1, required=True)
197
+ @click.option('--timeout', '-t', default=30, help='Connection timeout in seconds')
198
+ @click.option('--format', '-f', 'output_format',
199
+ type=click.Choice(['terminal', 'json']), default='terminal',
200
+ help='Output format')
201
+ def inspect(transport_type: str, target: tuple, timeout: int, output_format: str):
202
+ """
203
+ Inspect a running MCP server and analyze its tools.
204
+
205
+ TRANSPORT_TYPE is either 'stdio' or 'sse'.
206
+
207
+ For stdio, TARGET is the command to run the server:
208
+
209
+ agent-audit inspect stdio -- python my_mcp_server.py
210
+
211
+ For sse, TARGET is the URL:
212
+
213
+ agent-audit inspect sse https://example.com/sse
214
+
215
+ The inspector connects to the server, retrieves its tool definitions,
216
+ and analyzes them for security risks WITHOUT executing any tools.
217
+ """
218
+ # Join target parts back together
219
+ target_str = ' '.join(target)
220
+
221
+ # Handle the "--" separator for stdio
222
+ if target_str.startswith('-- '):
223
+ target_str = target_str[3:]
224
+
225
+ if not target_str:
226
+ console.print("[red]Error: No target specified[/red]")
227
+ sys.exit(1)
228
+
229
+ exit_code = run_inspect(
230
+ target=target_str,
231
+ transport=transport_type,
232
+ timeout=timeout,
233
+ output_format=output_format
234
+ )
235
+
236
+ sys.exit(exit_code)