mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-09-02 04:23:20 +02:00
Remove path parameter descriptions from format_simple_description since parameters are already visible in the tool's inputSchema with their descriptions. This prevents hidden parameters (via transforms) from still appearing in descriptions and avoids redundant information. Fixes #3130 Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
249 lines
9.1 KiB
Python
249 lines
9.1 KiB
Python
"""Tests for hidden path parameters in OpenAPI tool descriptions."""
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from fastmcp import FastMCP
|
|
from fastmcp.client import Client
|
|
from fastmcp.server.transforms import ToolTransform
|
|
from fastmcp.tools.tool_transform import ArgTransformConfig, ToolTransformConfig
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hidden_path_parameter_not_in_description():
|
|
"""Test that hidden path parameters are not included in tool descriptions.
|
|
|
|
This test replicates the bug described in issue #3130 where path parameters
|
|
that are hidden via ToolTransform still appear in the tool description.
|
|
"""
|
|
openapi_spec = {
|
|
"openapi": "3.1.0",
|
|
"info": {"title": "Test API", "version": "0.1.0"},
|
|
"paths": {
|
|
"/hello/{not_for_agent}": {
|
|
"get": {
|
|
"operationId": "hello",
|
|
"summary": "Say hello",
|
|
"parameters": [
|
|
{
|
|
"name": "not_for_agent",
|
|
"in": "path",
|
|
"required": True,
|
|
"description": "Secret setting",
|
|
"schema": {
|
|
"type": "string",
|
|
"title": "Not For Agent"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"message": {"type": "string"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async with httpx.AsyncClient(base_url="http://localhost:8000") as http_client:
|
|
mcp = FastMCP.from_openapi(
|
|
openapi_spec=openapi_spec,
|
|
client=http_client,
|
|
)
|
|
|
|
# Hide not_for_agent parameter and set default
|
|
mcp.add_transform(
|
|
ToolTransform({
|
|
"hello": ToolTransformConfig(
|
|
arguments={
|
|
"not_for_agent": ArgTransformConfig(
|
|
hide=True,
|
|
default="secret",
|
|
)
|
|
}
|
|
)
|
|
})
|
|
)
|
|
|
|
async with Client(mcp) as mcp_client:
|
|
tools = await mcp_client.list_tools()
|
|
assert len(tools) == 1
|
|
|
|
hello_tool = tools[0]
|
|
assert hello_tool.name == "hello"
|
|
|
|
# The parameter should not be in the schema
|
|
assert "not_for_agent" not in hello_tool.inputSchema.get("properties", {})
|
|
|
|
# The BUG: parameter description should not appear in the tool description
|
|
assert "Secret setting" not in hello_tool.description
|
|
assert "not_for_agent" not in hello_tool.description
|
|
|
|
# The description should only contain the summary
|
|
assert "Say hello" in hello_tool.description
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_visible_path_parameter_in_schema():
|
|
"""Test that visible path parameters are included in the inputSchema.
|
|
|
|
Path parameters are not included in the description since they're already
|
|
visible in the inputSchema with their descriptions.
|
|
"""
|
|
openapi_spec = {
|
|
"openapi": "3.1.0",
|
|
"info": {"title": "Test API", "version": "0.1.0"},
|
|
"paths": {
|
|
"/users/{user_id}": {
|
|
"get": {
|
|
"operationId": "get_user",
|
|
"summary": "Get user by ID",
|
|
"parameters": [
|
|
{
|
|
"name": "user_id",
|
|
"in": "path",
|
|
"required": True,
|
|
"description": "The user's unique identifier",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {"type": "string"},
|
|
"name": {"type": "string"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async with httpx.AsyncClient(base_url="http://localhost:8000") as http_client:
|
|
mcp = FastMCP.from_openapi(
|
|
openapi_spec=openapi_spec,
|
|
client=http_client,
|
|
)
|
|
|
|
async with Client(mcp) as mcp_client:
|
|
tools = await mcp_client.list_tools()
|
|
assert len(tools) == 1
|
|
|
|
user_tool = tools[0]
|
|
assert user_tool.name == "get_user"
|
|
|
|
# The parameter should be in the schema with its description
|
|
assert "user_id" in user_tool.inputSchema.get("properties", {})
|
|
assert user_tool.inputSchema["properties"]["user_id"]["description"] == "The user's unique identifier"
|
|
|
|
# The description should only contain the summary, not parameter details
|
|
assert user_tool.description == "Get user by ID"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mixed_path_parameters_in_schema():
|
|
"""Test that when some path params are hidden, only visible ones appear in schema."""
|
|
openapi_spec = {
|
|
"openapi": "3.1.0",
|
|
"info": {"title": "Test API", "version": "0.1.0"},
|
|
"paths": {
|
|
"/api/{api_version}/users/{user_id}": {
|
|
"get": {
|
|
"operationId": "get_versioned_user",
|
|
"summary": "Get user from specific API version",
|
|
"parameters": [
|
|
{
|
|
"name": "api_version",
|
|
"in": "path",
|
|
"required": True,
|
|
"description": "API version to use",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"name": "user_id",
|
|
"in": "path",
|
|
"required": True,
|
|
"description": "The user's unique identifier",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {"type": "string"},
|
|
"name": {"type": "string"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async with httpx.AsyncClient(base_url="http://localhost:8000") as http_client:
|
|
mcp = FastMCP.from_openapi(
|
|
openapi_spec=openapi_spec,
|
|
client=http_client,
|
|
)
|
|
|
|
# Hide api_version but not user_id
|
|
mcp.add_transform(
|
|
ToolTransform({
|
|
"get_versioned_user": ToolTransformConfig(
|
|
arguments={
|
|
"api_version": ArgTransformConfig(
|
|
hide=True,
|
|
default="v1",
|
|
)
|
|
}
|
|
)
|
|
})
|
|
)
|
|
|
|
async with Client(mcp) as mcp_client:
|
|
tools = await mcp_client.list_tools()
|
|
assert len(tools) == 1
|
|
|
|
tool = tools[0]
|
|
assert tool.name == "get_versioned_user"
|
|
|
|
# Only user_id should be in the schema
|
|
assert "user_id" in tool.inputSchema.get("properties", {})
|
|
assert "api_version" not in tool.inputSchema.get("properties", {})
|
|
|
|
# The description should only contain the summary, not parameter details
|
|
assert tool.description == "Get user from specific API version"
|