mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-27 15:51:33 +02:00
Remove custom names
This commit is contained in:
parent
4c3bf80652
commit
702412e28b
5 changed files with 29 additions and 341 deletions
|
|
@ -1,231 +0,0 @@
|
|||
"""Tests for OpenAPI component naming in FastMCP."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from fastmcp.server.openapi import FastMCPOpenAPI, MCPType
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def simple_openapi_spec():
|
||||
"""A simple OpenAPI spec with some routes for testing."""
|
||||
return {
|
||||
"openapi": "3.0.0",
|
||||
"info": {"title": "Test API", "version": "1.0.0"},
|
||||
"paths": {
|
||||
"/users": {
|
||||
"get": {
|
||||
"summary": "Get all users",
|
||||
"responses": {"200": {"description": "OK"}},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create a user",
|
||||
"responses": {"201": {"description": "Created"}},
|
||||
},
|
||||
},
|
||||
"/users/{id}": {
|
||||
"get": {
|
||||
"summary": "Get a user",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": True,
|
||||
"schema": {"type": "string"},
|
||||
}
|
||||
],
|
||||
"responses": {"200": {"description": "OK"}},
|
||||
},
|
||||
"put": {
|
||||
"summary": "Update a user",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": True,
|
||||
"schema": {"type": "string"},
|
||||
}
|
||||
],
|
||||
"responses": {"200": {"description": "OK"}},
|
||||
},
|
||||
},
|
||||
"/users/{id}/orders": {
|
||||
"get": {
|
||||
"summary": "Get user orders",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": True,
|
||||
"schema": {"type": "string"},
|
||||
}
|
||||
],
|
||||
"responses": {"200": {"description": "OK"}},
|
||||
}
|
||||
},
|
||||
"/products": {
|
||||
"get": {
|
||||
"operationId": "listProducts",
|
||||
"summary": "Get all products",
|
||||
"responses": {"200": {"description": "OK"}},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestOpenAPIComponentNaming:
|
||||
"""Tests for OpenAPI component naming functionality."""
|
||||
|
||||
@patch("fastmcp.server.openapi._combine_schemas")
|
||||
def test_default_naming(self, mock_combine, simple_openapi_spec):
|
||||
"""Test the default component naming behavior."""
|
||||
# Mock the HTTP client
|
||||
mock_client = MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
# Mock the combine schemas function to return empty dict
|
||||
mock_combine.return_value = {}
|
||||
|
||||
# Create a server with the default naming
|
||||
# Instead of mocking the creation methods, we'll just override them to
|
||||
# add the names to _used_names without actually creating components
|
||||
class TestServer(FastMCPOpenAPI):
|
||||
def _create_openapi_tool(self, route, name):
|
||||
_tool_name = self._get_unique_name(name, "tools")
|
||||
# Don't actually create the tool, just record that the name was used
|
||||
|
||||
def _create_openapi_resource(self, route, name):
|
||||
_resource_name = self._get_unique_name(name, "resources")
|
||||
# Don't actually create the resource, just record that the name was used
|
||||
|
||||
def _create_openapi_template(self, route, name):
|
||||
_template_name = self._get_unique_name(name, "templates")
|
||||
# Don't actually create the template, just record that the name was used
|
||||
|
||||
# Create the server with our test subclass
|
||||
server = TestServer(
|
||||
openapi_spec=simple_openapi_spec,
|
||||
client=mock_client,
|
||||
)
|
||||
|
||||
# Check that the correct names were generated
|
||||
expected_names = {
|
||||
"tools": {"post_users", "put_users"},
|
||||
"resources": {
|
||||
"users",
|
||||
"listProducts",
|
||||
}, # GET /users, GET /products (from operationId)
|
||||
"templates": {
|
||||
"users_id",
|
||||
"users_id_orders",
|
||||
}, # GET /users/{id}, GET /users/{id}/orders
|
||||
}
|
||||
|
||||
# The "tools" set in the server might contain more than our expected names
|
||||
# because all HTTP methods could be converted to tools - we just check for inclusion
|
||||
assert expected_names["tools"].issubset(server._used_names["tools"])
|
||||
assert expected_names["resources"].issubset(server._used_names["resources"])
|
||||
assert expected_names["templates"].issubset(server._used_names["templates"])
|
||||
|
||||
# Check that the operationId is preferred for naming
|
||||
assert "listProducts" in server._used_names["resources"]
|
||||
|
||||
@patch("fastmcp.server.openapi._combine_schemas")
|
||||
def test_custom_naming(self, mock_combine, simple_openapi_spec):
|
||||
"""Test custom component naming function."""
|
||||
# Mock the HTTP client
|
||||
mock_client = MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
# Mock the combine schemas function to return empty dict
|
||||
mock_combine.return_value = {}
|
||||
|
||||
# Create a custom naming function
|
||||
def custom_namer(route, mcp_type, default_name):
|
||||
# Always prefix with component type
|
||||
if mcp_type == MCPType.TOOL:
|
||||
prefix = "tool"
|
||||
elif mcp_type == MCPType.RESOURCE:
|
||||
prefix = "res"
|
||||
elif mcp_type == MCPType.RESOURCE_TEMPLATE:
|
||||
prefix = "tmpl"
|
||||
else:
|
||||
prefix = "other"
|
||||
|
||||
# Use operationId if available
|
||||
if route.operation_id:
|
||||
return f"{prefix}_{route.operation_id}"
|
||||
|
||||
# Otherwise use the path
|
||||
path_name = route.path.replace("/", "_").replace("{", "").replace("}", "")
|
||||
return f"{prefix}{path_name}"
|
||||
|
||||
# Create a custom testing server subclass
|
||||
class TestServer(FastMCPOpenAPI):
|
||||
def _create_openapi_tool(self, route, name):
|
||||
_tool_name = self._get_unique_name(name, "tools")
|
||||
# Don't actually create the tool, just record that the name was used
|
||||
|
||||
def _create_openapi_resource(self, route, name):
|
||||
_resource_name = self._get_unique_name(name, "resources")
|
||||
# Don't actually create the resource, just record that the name was used
|
||||
|
||||
def _create_openapi_template(self, route, name):
|
||||
_template_name = self._get_unique_name(name, "templates")
|
||||
# Don't actually create the template, just record that the name was used
|
||||
|
||||
# Create a server with the custom naming
|
||||
server = TestServer(
|
||||
openapi_spec=simple_openapi_spec,
|
||||
client=mock_client,
|
||||
component_namer=custom_namer,
|
||||
)
|
||||
|
||||
# Check some of the generated names
|
||||
assert "tool_users" in server._used_names["tools"]
|
||||
assert "res_users" in server._used_names["resources"]
|
||||
assert "tmpl_users_id" in server._used_names["templates"]
|
||||
assert "res_listProducts" in server._used_names["resources"]
|
||||
|
||||
@patch("fastmcp.server.openapi._combine_schemas")
|
||||
def test_collision_handling(self, mock_combine, simple_openapi_spec):
|
||||
"""Test how name collisions are handled by appending numbers."""
|
||||
# Mock the HTTP client
|
||||
mock_client = MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
# Mock the combine schemas function to return empty dict
|
||||
mock_combine.return_value = {}
|
||||
|
||||
# Create a custom naming function that always returns the same name
|
||||
def collision_namer(route, mcp_type, default_name):
|
||||
return "same_name"
|
||||
|
||||
# Create a custom testing server subclass
|
||||
class TestServer(FastMCPOpenAPI):
|
||||
def _create_openapi_tool(self, route, name):
|
||||
_tool_name = self._get_unique_name(name, "tools")
|
||||
# Don't actually create the tool, just record that the name was used
|
||||
|
||||
def _create_openapi_resource(self, route, name):
|
||||
_resource_name = self._get_unique_name(name, "resources")
|
||||
# Don't actually create the resource, just record that the name was used
|
||||
|
||||
def _create_openapi_template(self, route, name):
|
||||
_template_name = self._get_unique_name(name, "templates")
|
||||
# Don't actually create the template, just record that the name was used
|
||||
|
||||
# Create a server with the collision namer
|
||||
server = TestServer(
|
||||
openapi_spec=simple_openapi_spec,
|
||||
client=mock_client,
|
||||
component_namer=collision_namer,
|
||||
)
|
||||
|
||||
# Check that names were renamed with numbers
|
||||
assert "same_name" in server._used_names["tools"]
|
||||
assert "same_name_2" in server._used_names["tools"]
|
||||
assert "same_name" in server._used_names["resources"]
|
||||
assert "same_name_2" in server._used_names["resources"]
|
||||
assert "same_name" in server._used_names["templates"]
|
||||
assert "same_name_2" in server._used_names["templates"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue