mcpscore 0.3.0__tar.gz → 0.4.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 (38) hide show
  1. {mcpscore-0.3.0 → mcpscore-0.4.0}/PKG-INFO +56 -19
  2. {mcpscore-0.3.0 → mcpscore-0.4.0}/README.md +44 -12
  3. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/__init__.py +2 -2
  4. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/cli.py +10 -9
  5. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/enums.py +11 -4
  6. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/mcp_auditor.py +47 -7
  7. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/mcp_client.py +116 -32
  8. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/__init__.py +8 -2
  9. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/base.py +3 -1
  10. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/protocol_version.py +8 -7
  11. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/registry.py +2 -2
  12. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/security.py +3 -2
  13. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/tools.py +36 -32
  14. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/transport.py +21 -18
  15. mcpscore-0.4.0/pyproject.toml +274 -0
  16. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_auditor.py +161 -135
  17. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_cli.py +70 -63
  18. mcpscore-0.4.0/tests/test_mcp_client_handshake.py +182 -0
  19. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_mcp_client_http.py +0 -11
  20. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_mcp_client_session.py +0 -15
  21. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_mcp_client_sse.py +0 -11
  22. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_mcp_client_stdio.py +0 -8
  23. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_protocol_version_rules.py +13 -0
  24. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_security_rules.py +7 -5
  25. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_tools_rules.py +41 -30
  26. mcpscore-0.4.0/tests/test_transport_rules.py +66 -0
  27. mcpscore-0.3.0/pyproject.toml +0 -118
  28. mcpscore-0.3.0/tests/test_transport_rules.py +0 -64
  29. {mcpscore-0.3.0 → mcpscore-0.4.0}/.gitignore +0 -0
  30. {mcpscore-0.3.0 → mcpscore-0.4.0}/LICENSE +0 -0
  31. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/py.typed +0 -0
  32. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/capabilities.py +0 -0
  33. {mcpscore-0.3.0 → mcpscore-0.4.0}/mcpscore/rules/server_info.py +0 -0
  34. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/__init__.py +0 -0
  35. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/conftest.py +0 -0
  36. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_capabilities_rules.py +0 -0
  37. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_registry.py +0 -0
  38. {mcpscore-0.3.0 → mcpscore-0.4.0}/tests/test_server_info_rules.py +0 -0
@@ -1,14 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpscore
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: CLI tool to analyze your MCP server and get a comprehensive report on its quality
5
- Project-URL: Homepage, https://mcp-box.dev
5
+ Project-URL: Homepage, https://mcpaudit.dev
6
6
  Project-URL: Repository, https://github.com/mcp-box/mcpscore
7
7
  Project-URL: Issues, https://github.com/mcp-box/mcpscore/issues
8
+ Project-URL: Documentation, https://github.com/mcp-box/mcpscore#readme
9
+ Project-URL: Changelog, https://github.com/mcp-box/mcpscore/blob/main/CHANGELOG.md
8
10
  Author: Alex Akimov
9
11
  License-Expression: MIT
10
12
  License-File: LICENSE
11
- Keywords: ai,audit,cli,developer-tools,llm,mcp,mcp-server,model-context-protocol,quality
13
+ Keywords: ai,audit,cli,compliance,developer-tools,llm,mcp,mcp-audit,mcp-server,model-context-protocol,protocol,quality,validation
12
14
  Classifier: Development Status :: 4 - Beta
13
15
  Classifier: Environment :: Console
14
16
  Classifier: Intended Audience :: Developers
@@ -16,29 +18,61 @@ Classifier: License :: OSI Approved :: MIT License
16
18
  Classifier: Operating System :: OS Independent
17
19
  Classifier: Programming Language :: Python
18
20
  Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
19
24
  Classifier: Programming Language :: Python :: 3.13
20
25
  Classifier: Topic :: Software Development
21
26
  Classifier: Topic :: Software Development :: Libraries
22
27
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
28
  Classifier: Topic :: Software Development :: Quality Assurance
24
29
  Classifier: Typing :: Typed
25
- Requires-Python: >=3.13
26
- Requires-Dist: httpx-sse>=0.4.0
27
- Requires-Dist: httpx>=0.28.0
28
- Requires-Dist: mcp>=1.23.0
30
+ Requires-Python: >=3.11
31
+ Requires-Dist: httpx-sse>=0.4.3
32
+ Requires-Dist: httpx>=0.28.1
33
+ Requires-Dist: mcp>=1.27.2
29
34
  Description-Content-Type: text/markdown
30
35
 
31
36
  # MCPScore
32
37
 
38
+ [![CI](https://github.com/mcp-box/mcpscore/actions/workflows/ci.yml/badge.svg)](https://github.com/mcp-box/mcpscore/actions/workflows/ci.yml)
39
+ [![Coverage](https://codecov.io/gh/mcp-box/mcpscore/graph/badge.svg)](https://codecov.io/gh/mcp-box/mcpscore)
40
+ [![PyPI](https://img.shields.io/pypi/v/mcpscore.svg)](https://pypi.org/project/mcpscore/)
41
+ [![Python](https://img.shields.io/pypi/pyversions/mcpscore.svg)](https://pypi.org/project/mcpscore/)
42
+ [![License](https://img.shields.io/github/license/mcp-box/mcpscore.svg)](LICENSE)
43
+
33
44
  A command-line tool for auditing MCP (Model Context Protocol) servers. MCPScore connects to your server, runs a comprehensive set of validation rules against it, and produces a severity-based report showing what's compliant and what needs attention.
34
45
 
46
+ ## Why MCPScore?
47
+
48
+ MCP servers that violate the spec fail silently in the worst place: inside someone else's AI agent. A missing tool description, an outdated protocol version, or an unencrypted endpoint won't crash your server — it will just make agents pick the wrong tool, drop your server from their registry, or leak traffic. MCPScore catches these issues in seconds, before your users do.
49
+
50
+ ```bash
51
+ pip install mcpscore
52
+ mcpscore https://your-server.example/mcp
53
+ ```
54
+
55
+ ## How scoring works
56
+
57
+ Every rule has a severity, and each passing rule contributes its weight to the score:
58
+
59
+ | Severity | Points | Meaning |
60
+ |----------|--------|---------|
61
+ | CRITICAL | 5 | Spec violations that break interoperability (protocol version, server name, TLS) |
62
+ | HIGH | 3 | Strong spec expectations (server version, valid tool schemas) |
63
+ | MEDIUM | 2 | Recommendations that improve agent UX (titles, descriptions, error hygiene) |
64
+ | LOW | 1 | Nice-to-haves (capability extras, transport recommendations) |
65
+
66
+ The final score is reported as `earned/maximum` — higher means better MCP compliance.
67
+
35
68
  ## Features
36
69
 
37
70
  - **Multiple transports**: STDIO (local servers), Streamable HTTP, and SSE (remote servers)
38
71
  - **Auto-detection**: Picks the right transport automatically — tries Streamable HTTP first, falls back to SSE for URLs
72
+ - **Real handshake verification**: A connection only counts once the server completes the MCP `initialize` handshake — pointing it at a non-MCP endpoint fails cleanly
39
73
  - **Multi-language**: Audits both Python (`.py`) and Node.js (`.js`) MCP servers via STDIO
40
74
  - **Severity-based reporting**: Rules categorized as CRITICAL, HIGH, MEDIUM, or LOW
41
- - **Comprehensive validation**: Protocol compliance, server metadata, capabilities, security, and transport
75
+ - **Library-friendly**: Fully typed (`py.typed`); use `MCPClient` + `MCPAuditor` programmatically
42
76
 
43
77
  ## What it audits
44
78
 
@@ -54,16 +88,19 @@ A command-line tool for auditing MCP (Model Context Protocol) servers. MCPScore
54
88
 
55
89
  - **Capabilities**: Tools, resources, prompts, logging, and subscription support
56
90
 
91
+ - **Tools**: Names (presence, uniqueness, format), titles, descriptions, and JSON Schema validity of input/output schemas
92
+
57
93
  - **Security**:
58
- - ✅ HTTPS/TLS usage verification
94
+ - ✅ HTTPS/TLS usage with the actually negotiated TLS version
59
95
  - ✅ Valid certificate checks
96
+ - ✅ Error responses checked for data leaks
60
97
 
61
98
  - **Transport**:
62
- - ✅ SSE transport support detection
99
+ - ✅ Streamable HTTP usage (the current MCP standard; SSE-only servers get migration advice)
63
100
 
64
101
  ## Requirements
65
102
 
66
- - Python 3.13+
103
+ - Python 3.11+
67
104
  - Node.js on `PATH` if auditing a Node.js MCP server
68
105
  - A Python interpreter on `PATH` if auditing a Python MCP server
69
106
 
@@ -101,9 +138,9 @@ Welcome to MCPScore!
101
138
  Connected to the MCP server: /path/to/server.py
102
139
  Transport: stdio
103
140
  Starting the audit...
104
- ✅ Protocol version '2025-06-18' is one of the allowed versions
105
- ✅ Protocol version '2025-06-18' is not deprecated
106
- ✅ Protocol version '2025-06-18' is the latest version
141
+ ✅ Protocol version '2025-11-25' is one of the allowed versions
142
+ ✅ Protocol version '2025-11-25' is not deprecated
143
+ ✅ Protocol version '2025-11-25' is the latest version
107
144
  ✅ Server name is present: 'weather'
108
145
  ✅ Server version is present: '1.17.0'
109
146
  ❌ Server title is not present in server info
@@ -124,23 +161,23 @@ Starting the audit...
124
161
  Audit finished. Final score: 55/71
125
162
  ```
126
163
 
127
- ### Understanding the score
128
-
129
- Each passing rule contributes points equal to its severity weight: **CRITICAL = 5, HIGH = 3, MEDIUM = 2, LOW = 1**. Higher scores indicate better compliance with MCP standards.
130
-
131
164
  ## Troubleshooting
132
165
 
133
166
  **Connection fails**
134
167
 
135
168
  - Check the path or URL is correct and reachable
136
169
  - For local servers, make sure Python or Node.js is on `PATH`
137
- - Verify the server actually implements the MCP protocol
170
+ - "Not a valid MCP server (handshake failed)" means the endpoint responded but did not complete the MCP `initialize` handshake — verify the URL points at an actual MCP endpoint (often `/mcp`)
138
171
 
139
172
  **Protocol version errors**
140
173
 
141
174
  - Confirm your server uses a currently supported MCP protocol version
142
175
  - If your server uses a newer version that MCPScore doesn't yet recognize, please [open an issue](https://github.com/mcp-box/mcpscore/issues)
143
176
 
177
+ ## Contributing
178
+
179
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and how to add audit rules. Security reports: [SECURITY.md](SECURITY.md). Release history: [CHANGELOG.md](CHANGELOG.md).
180
+
144
181
  ## Feedback
145
182
 
146
183
  Bug reports, feature requests, and general feedback are welcome at <https://github.com/mcp-box/mcpscore/issues>.
@@ -1,14 +1,43 @@
1
1
  # MCPScore
2
2
 
3
+ [![CI](https://github.com/mcp-box/mcpscore/actions/workflows/ci.yml/badge.svg)](https://github.com/mcp-box/mcpscore/actions/workflows/ci.yml)
4
+ [![Coverage](https://codecov.io/gh/mcp-box/mcpscore/graph/badge.svg)](https://codecov.io/gh/mcp-box/mcpscore)
5
+ [![PyPI](https://img.shields.io/pypi/v/mcpscore.svg)](https://pypi.org/project/mcpscore/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/mcpscore.svg)](https://pypi.org/project/mcpscore/)
7
+ [![License](https://img.shields.io/github/license/mcp-box/mcpscore.svg)](LICENSE)
8
+
3
9
  A command-line tool for auditing MCP (Model Context Protocol) servers. MCPScore connects to your server, runs a comprehensive set of validation rules against it, and produces a severity-based report showing what's compliant and what needs attention.
4
10
 
11
+ ## Why MCPScore?
12
+
13
+ MCP servers that violate the spec fail silently in the worst place: inside someone else's AI agent. A missing tool description, an outdated protocol version, or an unencrypted endpoint won't crash your server — it will just make agents pick the wrong tool, drop your server from their registry, or leak traffic. MCPScore catches these issues in seconds, before your users do.
14
+
15
+ ```bash
16
+ pip install mcpscore
17
+ mcpscore https://your-server.example/mcp
18
+ ```
19
+
20
+ ## How scoring works
21
+
22
+ Every rule has a severity, and each passing rule contributes its weight to the score:
23
+
24
+ | Severity | Points | Meaning |
25
+ |----------|--------|---------|
26
+ | CRITICAL | 5 | Spec violations that break interoperability (protocol version, server name, TLS) |
27
+ | HIGH | 3 | Strong spec expectations (server version, valid tool schemas) |
28
+ | MEDIUM | 2 | Recommendations that improve agent UX (titles, descriptions, error hygiene) |
29
+ | LOW | 1 | Nice-to-haves (capability extras, transport recommendations) |
30
+
31
+ The final score is reported as `earned/maximum` — higher means better MCP compliance.
32
+
5
33
  ## Features
6
34
 
7
35
  - **Multiple transports**: STDIO (local servers), Streamable HTTP, and SSE (remote servers)
8
36
  - **Auto-detection**: Picks the right transport automatically — tries Streamable HTTP first, falls back to SSE for URLs
37
+ - **Real handshake verification**: A connection only counts once the server completes the MCP `initialize` handshake — pointing it at a non-MCP endpoint fails cleanly
9
38
  - **Multi-language**: Audits both Python (`.py`) and Node.js (`.js`) MCP servers via STDIO
10
39
  - **Severity-based reporting**: Rules categorized as CRITICAL, HIGH, MEDIUM, or LOW
11
- - **Comprehensive validation**: Protocol compliance, server metadata, capabilities, security, and transport
40
+ - **Library-friendly**: Fully typed (`py.typed`); use `MCPClient` + `MCPAuditor` programmatically
12
41
 
13
42
  ## What it audits
14
43
 
@@ -24,16 +53,19 @@ A command-line tool for auditing MCP (Model Context Protocol) servers. MCPScore
24
53
 
25
54
  - **Capabilities**: Tools, resources, prompts, logging, and subscription support
26
55
 
56
+ - **Tools**: Names (presence, uniqueness, format), titles, descriptions, and JSON Schema validity of input/output schemas
57
+
27
58
  - **Security**:
28
- - ✅ HTTPS/TLS usage verification
59
+ - ✅ HTTPS/TLS usage with the actually negotiated TLS version
29
60
  - ✅ Valid certificate checks
61
+ - ✅ Error responses checked for data leaks
30
62
 
31
63
  - **Transport**:
32
- - ✅ SSE transport support detection
64
+ - ✅ Streamable HTTP usage (the current MCP standard; SSE-only servers get migration advice)
33
65
 
34
66
  ## Requirements
35
67
 
36
- - Python 3.13+
68
+ - Python 3.11+
37
69
  - Node.js on `PATH` if auditing a Node.js MCP server
38
70
  - A Python interpreter on `PATH` if auditing a Python MCP server
39
71
 
@@ -71,9 +103,9 @@ Welcome to MCPScore!
71
103
  Connected to the MCP server: /path/to/server.py
72
104
  Transport: stdio
73
105
  Starting the audit...
74
- ✅ Protocol version '2025-06-18' is one of the allowed versions
75
- ✅ Protocol version '2025-06-18' is not deprecated
76
- ✅ Protocol version '2025-06-18' is the latest version
106
+ ✅ Protocol version '2025-11-25' is one of the allowed versions
107
+ ✅ Protocol version '2025-11-25' is not deprecated
108
+ ✅ Protocol version '2025-11-25' is the latest version
77
109
  ✅ Server name is present: 'weather'
78
110
  ✅ Server version is present: '1.17.0'
79
111
  ❌ Server title is not present in server info
@@ -94,23 +126,23 @@ Starting the audit...
94
126
  Audit finished. Final score: 55/71
95
127
  ```
96
128
 
97
- ### Understanding the score
98
-
99
- Each passing rule contributes points equal to its severity weight: **CRITICAL = 5, HIGH = 3, MEDIUM = 2, LOW = 1**. Higher scores indicate better compliance with MCP standards.
100
-
101
129
  ## Troubleshooting
102
130
 
103
131
  **Connection fails**
104
132
 
105
133
  - Check the path or URL is correct and reachable
106
134
  - For local servers, make sure Python or Node.js is on `PATH`
107
- - Verify the server actually implements the MCP protocol
135
+ - "Not a valid MCP server (handshake failed)" means the endpoint responded but did not complete the MCP `initialize` handshake — verify the URL points at an actual MCP endpoint (often `/mcp`)
108
136
 
109
137
  **Protocol version errors**
110
138
 
111
139
  - Confirm your server uses a currently supported MCP protocol version
112
140
  - If your server uses a newer version that MCPScore doesn't yet recognize, please [open an issue](https://github.com/mcp-box/mcpscore/issues)
113
141
 
142
+ ## Contributing
143
+
144
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and how to add audit rules. Security reports: [SECURITY.md](SECURITY.md). Release history: [CHANGELOG.md](CHANGELOG.md).
145
+
114
146
  ## Feedback
115
147
 
116
148
  Bug reports, feature requests, and general feedback are welcome at <https://github.com/mcp-box/mcpscore/issues>.
@@ -1,10 +1,10 @@
1
- """MCPDoctor - A comprehensive auditing tool for MCP (Model Context Protocol) servers.
1
+ """MCPScore - A comprehensive auditing tool for MCP (Model Context Protocol) servers.
2
2
 
3
3
  This package provides tools for auditing MCP servers to ensure compliance with
4
4
  protocol standards and best practices. It includes:
5
5
 
6
+ - MCPAuditor: For orchestrating the audit process
6
7
  - MCPClient: For connecting to and communicating with MCP servers
7
- - MCPDoctor: For orchestrating the audit process
8
8
  - Rule system: Extensible framework for implementing audit checks
9
9
  - Enums: Protocol versions and transport types
10
10
 
@@ -32,22 +32,23 @@ async def async_main() -> None:
32
32
 
33
33
  target: str = sys.argv[1]
34
34
  client: MCPClient = MCPClient()
35
- doctor: MCPAuditor = MCPAuditor()
35
+ auditor: MCPAuditor = MCPAuditor()
36
36
 
37
37
  success, transport = await client.detect_and_connect(target)
38
38
 
39
- if success:
40
- logger.info("Connected to the MCP server: %s", target)
41
- logger.info("Transport: %s", transport)
42
- else:
39
+ if not success:
43
40
  logger.error("Error connecting to the MCP server: %s", target)
44
41
  sys.exit(2)
45
42
 
46
- logger.info("Starting the audit...")
47
- final_score, max_score = await doctor.audit(client)
48
- logger.info("Audit finished. Final score: %s/%s", final_score, max_score)
43
+ logger.info("Connected to the MCP server: %s", target)
44
+ logger.info("Transport: %s", transport)
49
45
 
50
- await client.cleanup()
46
+ try:
47
+ logger.info("Starting the audit...")
48
+ final_score, max_score = await auditor.audit(client)
49
+ logger.info("Audit finished. Final score: %s/%s", final_score, max_score)
50
+ finally:
51
+ await client.cleanup()
51
52
 
52
53
 
53
54
  def main() -> None:
@@ -1,6 +1,6 @@
1
1
  """Enumerations and constants for MCP (Model Context Protocol) auditing.
2
2
 
3
- This module defines the core enumerations used throughout the MCPDoctor system:
3
+ This module defines the core enumerations used throughout the MCPScore system:
4
4
 
5
5
  - MCPTransportType: Supported transport methods for MCP communication
6
6
  - MCPProtocolVersion: Supported versions of the MCP protocol
@@ -38,7 +38,14 @@ class MCPProtocolVersion(StrEnum):
38
38
  """MCP protocol version from March 26, 2025."""
39
39
 
40
40
  v2025_06_18 = "2025-06-18"
41
- """Latest MCP protocol version (June 18, 2025)."""
41
+ """MCP protocol version from June 18, 2025."""
42
42
 
43
- Latest = v2025_06_18
44
- """Alias for the latest protocol version."""
43
+ v2025_11_25 = "2025-11-25"
44
+ """Latest MCP protocol version (November 25, 2025)."""
45
+
46
+ Latest = v2025_11_25
47
+ """Alias for the latest protocol version.
48
+
49
+ This is an enum alias, not a distinct member: it does not appear in
50
+ `list(MCPProtocolVersion)` or in iteration-derived version lists.
51
+ """
@@ -1,5 +1,8 @@
1
+ import asyncio
1
2
  import logging
3
+ import ssl
2
4
  from typing import TYPE_CHECKING
5
+ from urllib.parse import urlparse
3
6
 
4
7
  if TYPE_CHECKING:
5
8
  from mcp.types import InitializeResult, Prompt, Resource, Tool
@@ -9,6 +12,9 @@ from .rules import AuditData, BaseRule, RuleResult, RuleSeverity, create_all_rul
9
12
 
10
13
  logger = logging.getLogger(__name__)
11
14
 
15
+ TLS_PROBE_TIMEOUT_S = 10
16
+ """Timeout for probing the negotiated TLS version of an HTTPS server."""
17
+
12
18
 
13
19
  class MCPAuditor:
14
20
  """Orchestrates the MCP server audit process.
@@ -19,14 +25,14 @@ class MCPAuditor:
19
25
  - Tracks audit results and scoring
20
26
  - Provides audit summary and reporting
21
27
 
22
- The doctor uses a rule-based system where each rule checks specific
28
+ The auditor uses a rule-based system where each rule checks specific
23
29
  aspects of MCP compliance and contributes to an overall audit score.
24
30
  """
25
31
 
26
32
  def __init__(self) -> None:
27
- """Initialize a new MCPDoctor instance.
33
+ """Initialize a new MCPAuditor instance.
28
34
 
29
- Sets up the doctor with:
35
+ Sets up the auditor with:
30
36
  - Empty audit data container
31
37
  - All registered audit rules
32
38
  - Zero initial score
@@ -68,11 +74,11 @@ class MCPAuditor:
68
74
  await self._collect_resources()
69
75
  if self.audit_data.capabilities.prompts is not None:
70
76
  await self._collect_prompts()
71
- await self._run_all_rules()
77
+ self._run_all_rules()
72
78
 
73
79
  return self.score, self.max_score
74
80
 
75
- async def _run_all_rules(self) -> None:
81
+ def _run_all_rules(self) -> None:
76
82
  """Execute all registered audit rules and update the audit score.
77
83
 
78
84
  Iterates through all rules, executes each one, logs the results,
@@ -110,14 +116,48 @@ class MCPAuditor:
110
116
 
111
117
  # For HTTPS connections, check TLS
112
118
  if self.mcp_client.url and self.mcp_client.url.startswith("https://"):
113
- # If we successfully connected via HTTPS, assume TLS is verified
119
+ # If we successfully connected via HTTPS, TLS is verified
114
120
  # (httpx would have failed the connection if cert validation failed)
115
121
  self.audit_data.tls_verified = True
116
- self.audit_data.tls_version = "TLSv1.3" # Modern default, could be probed more precisely
122
+ self.audit_data.tls_version = await self._probe_tls_version(self.mcp_client.url)
117
123
  elif self.mcp_client.url and self.mcp_client.url.startswith("http://"):
118
124
  self.audit_data.tls_verified = False
119
125
  self.audit_data.tls_version = None
120
126
 
127
+ @staticmethod
128
+ async def _probe_tls_version(url: str) -> str | None:
129
+ """Probe the TLS version negotiated with an HTTPS server.
130
+
131
+ Opens a short-lived TLS connection to the server and reads the
132
+ negotiated protocol version (e.g. "TLSv1.3") from the SSL object.
133
+
134
+ Returns:
135
+ The negotiated TLS version string, or None if it could not be
136
+ determined (the TLS rules treat an unknown version leniently).
137
+
138
+ """
139
+ parsed = urlparse(url)
140
+ host = parsed.hostname
141
+ if host is None:
142
+ return None
143
+ port = parsed.port or 443
144
+
145
+ writer = None
146
+ try:
147
+ context = ssl.create_default_context()
148
+ _reader, writer = await asyncio.wait_for(
149
+ asyncio.open_connection(host, port, ssl=context),
150
+ timeout=TLS_PROBE_TIMEOUT_S,
151
+ )
152
+ ssl_object = writer.get_extra_info("ssl_object")
153
+ return ssl_object.version() if ssl_object is not None else None
154
+ except Exception as e: # noqa: BLE001 — probe failure must not abort the audit
155
+ logger.info("Could not probe TLS version for %s: %s", url, e)
156
+ return None
157
+ finally:
158
+ if writer is not None:
159
+ writer.close()
160
+
121
161
  async def _collect_init_result(self) -> None:
122
162
  """Collect initialization data from the MCP server.
123
163