Rename FastMCP to MCPServer (#1951)
This commit is contained in:
parent
4a2d83a0cb
commit
65c614e48e
3 changed files with 713 additions and 693 deletions
0
tests/server/mcpserver/prompts/__init__.py
Normal file
0
tests/server/mcpserver/prompts/__init__.py
Normal file
183
tests/server/mcpserver/prompts/test_base.py
Normal file
183
tests/server/mcpserver/prompts/test_base.py
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from mcp.server.mcpserver.prompts.base import AssistantMessage, Message, Prompt, TextContent, UserMessage
|
||||
from mcp.types import EmbeddedResource, TextResourceContents
|
||||
|
||||
|
||||
class TestRenderPrompt:
|
||||
@pytest.mark.anyio
|
||||
async def test_basic_fn(self):
|
||||
def fn() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [UserMessage(content=TextContent(type="text", text="Hello, world!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_async_fn(self):
|
||||
async def fn() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [UserMessage(content=TextContent(type="text", text="Hello, world!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_with_args(self):
|
||||
async def fn(name: str, age: int = 30) -> str:
|
||||
return f"Hello, {name}! You're {age} years old."
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render(arguments={"name": "World"}) == [
|
||||
UserMessage(content=TextContent(type="text", text="Hello, World! You're 30 years old."))
|
||||
]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_with_invalid_kwargs(self):
|
||||
async def fn(name: str, age: int = 30) -> str: # pragma: no cover
|
||||
return f"Hello, {name}! You're {age} years old."
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
with pytest.raises(ValueError):
|
||||
await prompt.render(arguments={"age": 40})
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_message(self):
|
||||
async def fn() -> UserMessage:
|
||||
return UserMessage(content="Hello, world!")
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [UserMessage(content=TextContent(type="text", text="Hello, world!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_assistant_message(self):
|
||||
async def fn() -> AssistantMessage:
|
||||
return AssistantMessage(content=TextContent(type="text", text="Hello, world!"))
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [AssistantMessage(content=TextContent(type="text", text="Hello, world!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_multiple_messages(self):
|
||||
expected: list[Message] = [
|
||||
UserMessage("Hello, world!"),
|
||||
AssistantMessage("How can I help you today?"),
|
||||
UserMessage("I'm looking for a restaurant in the center of town."),
|
||||
]
|
||||
|
||||
async def fn() -> list[Message]:
|
||||
return expected
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == expected
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_list_of_strings(self):
|
||||
expected = [
|
||||
"Hello, world!",
|
||||
"I'm looking for a restaurant in the center of town.",
|
||||
]
|
||||
|
||||
async def fn() -> list[str]:
|
||||
return expected
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [UserMessage(t) for t in expected]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_resource_content(self):
|
||||
"""Test returning a message with resource content."""
|
||||
|
||||
async def fn() -> UserMessage:
|
||||
return UserMessage(
|
||||
content=EmbeddedResource(
|
||||
type="resource",
|
||||
resource=TextResourceContents(
|
||||
uri="file://file.txt",
|
||||
text="File contents",
|
||||
mime_type="text/plain",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [
|
||||
UserMessage(
|
||||
content=EmbeddedResource(
|
||||
type="resource",
|
||||
resource=TextResourceContents(
|
||||
uri="file://file.txt",
|
||||
text="File contents",
|
||||
mime_type="text/plain",
|
||||
),
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_mixed_content(self):
|
||||
"""Test returning messages with mixed content types."""
|
||||
|
||||
async def fn() -> list[Message]:
|
||||
return [
|
||||
UserMessage(content="Please analyze this file:"),
|
||||
UserMessage(
|
||||
content=EmbeddedResource(
|
||||
type="resource",
|
||||
resource=TextResourceContents(
|
||||
uri="file://file.txt",
|
||||
text="File contents",
|
||||
mime_type="text/plain",
|
||||
),
|
||||
)
|
||||
),
|
||||
AssistantMessage(content="I'll help analyze that file."),
|
||||
]
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [
|
||||
UserMessage(content=TextContent(type="text", text="Please analyze this file:")),
|
||||
UserMessage(
|
||||
content=EmbeddedResource(
|
||||
type="resource",
|
||||
resource=TextResourceContents(
|
||||
uri="file://file.txt",
|
||||
text="File contents",
|
||||
mime_type="text/plain",
|
||||
),
|
||||
)
|
||||
),
|
||||
AssistantMessage(content=TextContent(type="text", text="I'll help analyze that file.")),
|
||||
]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_fn_returns_dict_with_resource(self):
|
||||
"""Test returning a dict with resource content."""
|
||||
|
||||
async def fn() -> dict[str, Any]:
|
||||
return {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
"uri": "file://file.txt",
|
||||
"text": "File contents",
|
||||
"mimeType": "text/plain",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
prompt = Prompt.from_function(fn)
|
||||
assert await prompt.render() == [
|
||||
UserMessage(
|
||||
content=EmbeddedResource(
|
||||
type="resource",
|
||||
resource=TextResourceContents(
|
||||
uri="file://file.txt",
|
||||
text="File contents",
|
||||
mime_type="text/plain",
|
||||
),
|
||||
)
|
||||
)
|
||||
]
|
||||
108
tests/server/mcpserver/prompts/test_manager.py
Normal file
108
tests/server/mcpserver/prompts/test_manager.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import pytest
|
||||
|
||||
from mcp.server.mcpserver.prompts.base import Prompt, TextContent, UserMessage
|
||||
from mcp.server.mcpserver.prompts.manager import PromptManager
|
||||
|
||||
|
||||
class TestPromptManager:
|
||||
def test_add_prompt(self):
|
||||
"""Test adding a prompt to the manager."""
|
||||
|
||||
def fn() -> str: # pragma: no cover
|
||||
return "Hello, world!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt = Prompt.from_function(fn)
|
||||
added = manager.add_prompt(prompt)
|
||||
assert added == prompt
|
||||
assert manager.get_prompt("fn") == prompt
|
||||
|
||||
def test_add_duplicate_prompt(self, caplog: pytest.LogCaptureFixture):
|
||||
"""Test adding the same prompt twice."""
|
||||
|
||||
def fn() -> str: # pragma: no cover
|
||||
return "Hello, world!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt = Prompt.from_function(fn)
|
||||
first = manager.add_prompt(prompt)
|
||||
second = manager.add_prompt(prompt)
|
||||
assert first == second
|
||||
assert "Prompt already exists" in caplog.text
|
||||
|
||||
def test_disable_warn_on_duplicate_prompts(self, caplog: pytest.LogCaptureFixture):
|
||||
"""Test disabling warning on duplicate prompts."""
|
||||
|
||||
def fn() -> str: # pragma: no cover
|
||||
return "Hello, world!"
|
||||
|
||||
manager = PromptManager(warn_on_duplicate_prompts=False)
|
||||
prompt = Prompt.from_function(fn)
|
||||
first = manager.add_prompt(prompt)
|
||||
second = manager.add_prompt(prompt)
|
||||
assert first == second
|
||||
assert "Prompt already exists" not in caplog.text
|
||||
|
||||
def test_list_prompts(self):
|
||||
"""Test listing all prompts."""
|
||||
|
||||
def fn1() -> str: # pragma: no cover
|
||||
return "Hello, world!"
|
||||
|
||||
def fn2() -> str: # pragma: no cover
|
||||
return "Goodbye, world!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt1 = Prompt.from_function(fn1)
|
||||
prompt2 = Prompt.from_function(fn2)
|
||||
manager.add_prompt(prompt1)
|
||||
manager.add_prompt(prompt2)
|
||||
prompts = manager.list_prompts()
|
||||
assert len(prompts) == 2
|
||||
assert prompts == [prompt1, prompt2]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_render_prompt(self):
|
||||
"""Test rendering a prompt."""
|
||||
|
||||
def fn() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt = Prompt.from_function(fn)
|
||||
manager.add_prompt(prompt)
|
||||
messages = await manager.render_prompt("fn")
|
||||
assert messages == [UserMessage(content=TextContent(type="text", text="Hello, world!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_render_prompt_with_args(self):
|
||||
"""Test rendering a prompt with arguments."""
|
||||
|
||||
def fn(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt = Prompt.from_function(fn)
|
||||
manager.add_prompt(prompt)
|
||||
messages = await manager.render_prompt("fn", arguments={"name": "World"})
|
||||
assert messages == [UserMessage(content=TextContent(type="text", text="Hello, World!"))]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_render_unknown_prompt(self):
|
||||
"""Test rendering a non-existent prompt."""
|
||||
manager = PromptManager()
|
||||
with pytest.raises(ValueError, match="Unknown prompt: unknown"):
|
||||
await manager.render_prompt("unknown")
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_render_prompt_with_missing_args(self):
|
||||
"""Test rendering a prompt with missing required arguments."""
|
||||
|
||||
def fn(name: str) -> str: # pragma: no cover
|
||||
return f"Hello, {name}!"
|
||||
|
||||
manager = PromptManager()
|
||||
prompt = Prompt.from_function(fn)
|
||||
manager.add_prompt(prompt)
|
||||
with pytest.raises(ValueError, match="Missing required arguments"):
|
||||
await manager.render_prompt("fn")
|
||||
Loading…
Reference in a new issue