mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-09-01 12:03:19 +02:00
Addresses #541: - Server now auto-deserializes JSON string args (list, dict, BaseModel) for prompts. This simplifies server-side prompt logic by reducing boilerplate `json.loads()` calls. - Docs updated to clarify `list_resource_templates` usage for templatized resources. - Docs updated to require client-side `json.dumps()` for complex `get_prompt` arguments, resolving the original Pydantic error. - Adds a new example (`examples/dynamic_story_prompt/`) demonstrating the server-side deserialization benefit and correct client-side serialization. Closes #541. --- Notes for Reviewers: - **Server-Side Auto-Deserialization:** This change introduces a "magic" `json.loads()` in `Prompt.render`. This is an intentional DX improvement. It only triggers for `str` inputs targeting `list`, `dict`, or `BaseModel` type hints. If `json.loads()` fails (e.g., malformed JSON), the original string is passed to Pydantic's `validate_call`, ensuring robust error handling. This avoids boilerplate in user prompt functions. - **Client `get_prompt()` Return Value:** The `examples/dynamic_story_prompt/story_client.py` parses the result of `client.get_prompt()` by iterating and looking for a `('messages', ...)` tuple. This reflects the observed behavior of the current `client.get_prompt()`. This commit does *not* change `client.get_prompt()`'s return behavior; the example merely adapts to it. A separate discussion might be warranted for potentially simplifying `client.get_prompt()`'s return signature in the future.
69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
from pydantic import BaseModel, Field
|
|
|
|
from fastmcp import FastMCP
|
|
|
|
mcp = FastMCP(name="DynamicStoryServer")
|
|
|
|
|
|
class Character(BaseModel):
|
|
name: str = Field(..., description="The character's name.")
|
|
archetype: str = Field(
|
|
default="Mysterious Stranger",
|
|
description="The character's archetype (e.g., Brave Knight, Wily Rogue).",
|
|
)
|
|
quirky_trait: str = Field(
|
|
..., description="A unique or unusual trait of the character."
|
|
)
|
|
|
|
|
|
@mcp.prompt()
|
|
def generate_dynamic_story_prompt(
|
|
character_details: Character,
|
|
mysterious_objects: list[str],
|
|
world_laws: dict[str, str],
|
|
) -> str:
|
|
"""Generates a creative story prompt based on character, objects, and world laws."""
|
|
|
|
# Directly use the deserialized Python objects:
|
|
prompt = f"Our protagonist, {character_details.name}, a self-proclaimed '{character_details.archetype}', "
|
|
prompt += f"known for their {character_details.quirky_trait}, stumbles upon a peculiar collection: "
|
|
|
|
if mysterious_objects:
|
|
if len(mysterious_objects) == 1:
|
|
prompt += f"a single {mysterious_objects[0]}. "
|
|
else:
|
|
objects_str = (
|
|
", ".join(mysterious_objects[:-1]) + f", and a {mysterious_objects[-1]}"
|
|
)
|
|
prompt += f"{objects_str}. "
|
|
else:
|
|
prompt += "nothing but dust bunnies. "
|
|
|
|
prompt += (
|
|
"\n\nSuddenly, the world shifts. The very laws of reality seem to be in flux:"
|
|
)
|
|
|
|
if world_laws:
|
|
for law, description in world_laws.items():
|
|
prompt += f"\n- {law.capitalize()}: {description}."
|
|
else:
|
|
prompt += "\n- Everything is disconcertingly normal."
|
|
|
|
prompt += "\n\nWhat happens next?"
|
|
|
|
return prompt
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Starting Dynamic Story Server...")
|
|
# To run this server, you would typically call mcp.run()
|
|
# For example, if you have uvicorn and want to run it as an ASGI app (if FastMCP supports it directly or via an adapter):
|
|
# import uvicorn
|
|
# uvicorn.run(mcp.asgi_app, host="0.0.0.0", port=8000)
|
|
# Or, if it runs via its own stdio mechanism, just mcp.run()
|
|
try:
|
|
mcp.run() # Assuming this is the standard way to run a FastMCP stdio server
|
|
except KeyboardInterrupt:
|
|
print("Server shutting down.")
|
|
except Exception as e:
|
|
print(f"Server failed to start or run: {e}")
|