60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
211 lines
6.7 KiB
Python
211 lines
6.7 KiB
Python
"""Tests for PromptsAsTools transform."""
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from fastmcp import FastMCP
|
|
from fastmcp.client import Client
|
|
from fastmcp.server.transforms import PromptsAsTools
|
|
|
|
|
|
class TestPromptsAsToolsBasic:
|
|
"""Test basic PromptsAsTools functionality."""
|
|
|
|
async def test_adds_list_prompts_tool(self):
|
|
"""Transform adds list_prompts tool."""
|
|
mcp = FastMCP("Test")
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
tools = await client.list_tools()
|
|
tool_names = {t.name for t in tools}
|
|
assert "list_prompts" in tool_names
|
|
|
|
async def test_adds_get_prompt_tool(self):
|
|
"""Transform adds get_prompt tool."""
|
|
mcp = FastMCP("Test")
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
tools = await client.list_tools()
|
|
tool_names = {t.name for t in tools}
|
|
assert "get_prompt" in tool_names
|
|
|
|
async def test_preserves_existing_tools(self):
|
|
"""Transform preserves existing tools."""
|
|
mcp = FastMCP("Test")
|
|
|
|
@mcp.tool
|
|
def my_tool() -> str:
|
|
return "result"
|
|
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
tools = await client.list_tools()
|
|
tool_names = {t.name for t in tools}
|
|
assert "my_tool" in tool_names
|
|
assert "list_prompts" in tool_names
|
|
assert "get_prompt" in tool_names
|
|
|
|
|
|
class TestListPromptsTool:
|
|
"""Test the list_prompts tool."""
|
|
|
|
async def test_lists_prompts(self):
|
|
"""list_prompts returns prompt metadata."""
|
|
mcp = FastMCP("Test")
|
|
|
|
@mcp.prompt
|
|
def analyze_code() -> str:
|
|
"""Analyze code for issues."""
|
|
return "Analyze this code"
|
|
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
result = await client.call_tool("list_prompts", {})
|
|
prompts = json.loads(result.data)
|
|
|
|
assert len(prompts) == 1
|
|
assert prompts[0]["name"] == "analyze_code"
|
|
assert prompts[0]["description"] == "Analyze code for issues."
|
|
|
|
async def test_lists_prompt_with_arguments(self):
|
|
"""list_prompts includes argument metadata."""
|
|
mcp = FastMCP("Test")
|
|
|
|
@mcp.prompt
|
|
def analyze_code(code: str, language: str = "python") -> str:
|
|
"""Analyze code for issues."""
|
|
return f"Analyze this {language} code:\n{code}"
|
|
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
result = await client.call_tool("list_prompts", {})
|
|
prompts = json.loads(result.data)
|
|
|
|
assert len(prompts) == 1
|
|
args = prompts[0]["arguments"]
|
|
assert len(args) == 2
|
|
|
|
# Check required arg
|
|
code_arg = next(a for a in args if a["name"] == "code")
|
|
assert code_arg["required"] is True
|
|
|
|
# Check optional arg
|
|
lang_arg = next(a for a in args if a["name"] == "language")
|
|
assert lang_arg["required"] is False
|
|
|
|
async def test_empty_when_no_prompts(self):
|
|
"""list_prompts returns empty list when no prompts exist."""
|
|
mcp = FastMCP("Test")
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
result = await client.call_tool("list_prompts", {})
|
|
assert json.loads(result.data) == []
|
|
|
|
|
|
class TestGetPromptTool:
|
|
"""Test the get_prompt tool."""
|
|
|
|
async def test_gets_prompt_without_arguments(self):
|
|
"""get_prompt gets a prompt with no arguments."""
|
|
mcp = FastMCP("Test")
|
|
|
|
@mcp.prompt
|
|
def simple_prompt() -> str:
|
|
"""A simple prompt."""
|
|
return "Hello, world!"
|
|
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
result = await client.call_tool("get_prompt", {"name": "simple_prompt"})
|
|
response = json.loads(result.data)
|
|
|
|
assert "messages" in response
|
|
assert len(response["messages"]) == 1
|
|
assert response["messages"][0]["role"] == "user"
|
|
assert "Hello, world!" in response["messages"][0]["content"]
|
|
|
|
async def test_gets_prompt_with_arguments(self):
|
|
"""get_prompt gets a prompt with arguments."""
|
|
mcp = FastMCP("Test")
|
|
|
|
@mcp.prompt
|
|
def analyze_code(code: str, language: str = "python") -> str:
|
|
"""Analyze code."""
|
|
return f"Analyze this {language} code:\n{code}"
|
|
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
result = await client.call_tool(
|
|
"get_prompt",
|
|
{
|
|
"name": "analyze_code",
|
|
"arguments": {"code": "x = 1", "language": "python"},
|
|
},
|
|
)
|
|
response = json.loads(result.data)
|
|
|
|
assert "messages" in response
|
|
content = response["messages"][0]["content"]
|
|
assert "python" in content
|
|
assert "x = 1" in content
|
|
|
|
async def test_error_on_unknown_prompt(self):
|
|
"""get_prompt raises error for unknown prompt name."""
|
|
from fastmcp.exceptions import ToolError
|
|
|
|
mcp = FastMCP("Test")
|
|
mcp.add_transform(PromptsAsTools(mcp))
|
|
|
|
async with Client(mcp) as client:
|
|
with pytest.raises(ToolError, match="Unknown prompt"):
|
|
await client.call_tool("get_prompt", {"name": "unknown_prompt"})
|
|
|
|
|
|
class TestPromptsAsToolsWithNamespace:
|
|
"""Test PromptsAsTools combined with other transforms."""
|
|
|
|
async def test_works_with_namespace_on_provider(self):
|
|
"""PromptsAsTools works when provider has Namespace transform."""
|
|
from fastmcp.server.providers import FastMCPProvider
|
|
from fastmcp.server.transforms import Namespace
|
|
|
|
sub = FastMCP("Sub")
|
|
|
|
@sub.prompt
|
|
def my_prompt() -> str:
|
|
"""A prompt."""
|
|
return "Hello"
|
|
|
|
main = FastMCP("Main")
|
|
provider = FastMCPProvider(sub)
|
|
provider.add_transform(Namespace("sub"))
|
|
main.add_provider(provider)
|
|
main.add_transform(PromptsAsTools(main))
|
|
|
|
async with Client(main) as client:
|
|
result = await client.call_tool("list_prompts", {})
|
|
prompts = json.loads(result.data)
|
|
|
|
# Prompt should have namespaced name
|
|
assert len(prompts) == 1
|
|
assert prompts[0]["name"] == "sub_my_prompt"
|
|
|
|
|
|
class TestPromptsAsToolsRepr:
|
|
"""Test PromptsAsTools repr."""
|
|
|
|
def test_repr(self):
|
|
"""Transform has useful repr."""
|
|
mcp = FastMCP("Test")
|
|
transform = PromptsAsTools(mcp)
|
|
assert "PromptsAsTools" in repr(transform)
|