-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathtool_name_validation.py
More file actions
129 lines (95 loc) · 4.41 KB
/
tool_name_validation.py
File metadata and controls
129 lines (95 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""Tool name validation utilities according to SEP-986.
Tool names SHOULD be between 1 and 128 characters in length (inclusive).
Tool names are case-sensitive.
Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z),
digits (0-9), underscore (_), dash (-), and dot (.).
Tool names SHOULD NOT contain spaces, commas, or other special characters.
See: https://modelcontextprotocol.io/specification/draft/server/tools#tool-names
"""
from __future__ import annotations
import logging
import re
from dataclasses import dataclass, field
logger = logging.getLogger(__name__)
# Regular expression for valid tool names according to SEP-986 specification
TOOL_NAME_REGEX = re.compile(r"^[A-Za-z0-9._-]{1,128}$")
# SEP reference URL for warning messages
SEP_986_URL = "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986"
@dataclass
class ToolNameValidationResult:
"""Result of tool name validation.
Attributes:
is_valid: Whether the tool name conforms to SEP-986 requirements.
warnings: List of warning messages for non-conforming aspects.
"""
is_valid: bool
warnings: list[str] = field(default_factory=lambda: [])
def validate_tool_name(name: str) -> ToolNameValidationResult:
"""Validate a tool name according to the SEP-986 specification.
Args:
name: The tool name to validate.
Returns:
ToolNameValidationResult containing validation status and any warnings.
"""
warnings: list[str] = []
# Check for empty name
if not name:
return ToolNameValidationResult(
is_valid=False,
warnings=["Tool name cannot be empty"],
)
# Check length
if len(name) > 128:
return ToolNameValidationResult(
is_valid=False,
warnings=[f"Tool name exceeds maximum length of 128 characters (current: {len(name)})"],
)
# Check for problematic patterns (warnings, not validation failures)
if " " in name:
warnings.append("Tool name contains spaces, which may cause parsing issues")
if "," in name:
warnings.append("Tool name contains commas, which may cause parsing issues")
# Check for potentially confusing leading/trailing characters
if name.startswith("-") or name.endswith("-"):
warnings.append("Tool name starts or ends with a dash, which may cause parsing issues in some contexts")
if name.startswith(".") or name.endswith("."):
warnings.append("Tool name starts or ends with a dot, which may cause parsing issues in some contexts")
# Check for invalid characters
if not TOOL_NAME_REGEX.match(name):
# Find all invalid characters (unique, preserving order)
invalid_chars: list[str] = []
seen: set[str] = set()
for char in name:
if not re.match(r"[A-Za-z0-9._-]", char) and char not in seen:
invalid_chars.append(char)
seen.add(char)
warnings.append(f"Tool name contains invalid characters: {', '.join(repr(c) for c in invalid_chars)}")
warnings.append("Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)")
return ToolNameValidationResult(is_valid=False, warnings=warnings)
return ToolNameValidationResult(is_valid=True, warnings=warnings)
def issue_tool_name_warning(name: str, warnings: list[str]) -> None:
"""Log warnings for non-conforming tool names.
Args:
name: The tool name that triggered the warnings.
warnings: List of warning messages to log.
"""
if not warnings:
return
logger.warning(f'Tool name validation warning for "{name}":')
for warning in warnings:
logger.warning(f" - {warning}")
logger.warning("Tool registration will proceed, but this may cause compatibility issues.")
logger.warning("Consider updating the tool name to conform to the MCP tool naming standard.")
logger.warning(f"See SEP-986 ({SEP_986_URL}) for more details.")
def validate_and_warn_tool_name(name: str) -> bool:
"""Validate a tool name and issue warnings for non-conforming names.
This is the primary entry point for tool name validation. It validates
the name and logs any warnings via the logging module.
Args:
name: The tool name to validate.
Returns:
True if the name is valid, False otherwise.
"""
result = validate_tool_name(name)
issue_tool_name_warning(name, result.warnings)
return result.is_valid