ab24c34d89
- pre_parse_json leaves a string alone when json.loads refuses it with something other than JSONDecodeError (over-long integer, deep nesting), so validation rejects it as a bad argument instead of it surfacing as a crash with a traceback per request. - convert_result skips output-schema validation for a returned CallToolResult(is_error=True); an error result has no structured content to check, and the author's message now reaches the client as written. - read_resource checks that Resource.read() returned str or bytes, so a mistyped custom resource is logged as a crash and answered with -32603 rather than "Invalid request parameters" with no log record. - Docs and examples that still said "raise any exception and the model reads it" now say ToolError; deprecated.md lists the deprecated FuncMetadata helper; docstrings spell out the MCPError carve-out and the nested-crash __cause__. - Two tests tightened: the prompt argument-validation test proves the body never ran, and the invalid-types check asserts on validate_arguments.
36 lines
913 B
Python
36 lines
913 B
Python
from mcp.server import MCPServer
|
|
from mcp.server.mcpserver.exceptions import ToolError
|
|
|
|
mcp = MCPServer("Bookshop")
|
|
|
|
CATALOG = {
|
|
"Dune": "Frank Herbert",
|
|
"Neuromancer": "William Gibson",
|
|
"The Left Hand of Darkness": "Ursula K. Le Guin",
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def search_books(query: str) -> list[str]:
|
|
"""Search the catalog by title or author."""
|
|
needle = query.lower()
|
|
return [title for title, author in CATALOG.items() if needle in title.lower() or needle in author.lower()]
|
|
|
|
|
|
@mcp.tool()
|
|
def get_author(title: str) -> str:
|
|
"""Look up the author of a book in the catalog."""
|
|
if title not in CATALOG:
|
|
raise ToolError(f"No book titled {title!r} in the catalog.")
|
|
return CATALOG[title]
|
|
|
|
|
|
@mcp.resource("catalog://titles")
|
|
def titles() -> str:
|
|
"""Every title in the catalog, one per line."""
|
|
return "\n".join(sorted(CATALOG))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
mcp.run()
|