fix(mcp): strip URL credentials from MCP tool names and require an ASCII first letter

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Qr6KDhPSJyMX48rMMUqk4D
This commit is contained in:
Claude
2026-10-10 22:46:04 +00:00
parent 59bf502407
commit be60c1443f
2 changed files with 16 additions and 9 deletions
+4 -3
View File
@@ -527,11 +527,12 @@ class MCPToolResolver:
def _extract_server_name(server_url: str) -> str:
"""Extract clean server name from URL for tool prefixing."""
parsed = urlparse(server_url)
domain = parsed.netloc.replace(".", "_")
# Drop any "user:pass@" so credentials never end up in tool names.
domain = parsed.netloc.rpartition("@")[2].replace(".", "_")
path = parsed.path.replace("/", "_").strip("_")
name = f"{domain}_{path}" if path else domain
# Provider function names must start with a letter; IP hosts don't.
if name and not name[0].isalpha():
# Provider function names must start with an ASCII letter; IP hosts don't.
if name and not (name[0].isascii() and name[0].isalpha()):
name = f"mcp_{name}"
return name
@@ -4,9 +4,9 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from crewai.agent.core import Agent
from crewai.llms.providers.utils.common import safe_tool_conversion
from crewai.mcp.config import MCPServerHTTP
from crewai.mcp.tool_resolver import MCPToolResolver
from crewai.llms.providers.utils.common import safe_tool_conversion
@pytest.fixture
@@ -99,6 +99,7 @@ class TestResolveNativeRuntimeError:
with pytest.raises(ConnectionError, match="Failed to get native MCP tools"):
resolver._resolve_native(http_config)
class TestResolveNativeToolNames:
@staticmethod
def _resolve_tool_names(mock_client_class, resolver, url):
@@ -114,19 +115,16 @@ class TestResolveNativeToolNames:
tools, _ = resolver._resolve_native(MCPServerHTTP(url=url))
return [tool.name for tool in tools]
@pytest.mark.parametrize(
"provider", ["OpenAI", "Anthropic", "Gemini", "Bedrock", "Azure"]
)
@patch("crewai.mcp.tool_resolver.MCPClient")
def test_ip_addressed_server_tool_name_passes_provider_validation(
self, mock_client_class, resolver, provider
self, mock_client_class, resolver
):
names = self._resolve_tool_names(
mock_client_class, resolver, "http://127.0.0.1:8800/mcp"
)
name, _, _ = safe_tool_conversion(
{"name": names[0], "description": "Add numbers"}, provider
{"name": names[0], "description": "Add numbers"}, "OpenAI"
)
assert name == "mcp_127_0_0_1_8800_mcp_add"
@@ -139,3 +137,11 @@ class TestResolveNativeToolNames:
)
assert names == ["localhost:8800_mcp_add"]
@patch("crewai.mcp.tool_resolver.MCPClient")
def test_url_credentials_are_not_in_tool_name(self, mock_client_class, resolver):
names = self._resolve_tool_names(
mock_client_class, resolver, "http://user:[email protected]:8800/mcp"
)
assert names == ["mcp_10_0_0_5:8800_mcp_add"]