Files
wehub-resource-sync 49b9bb6724
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:10:27 +08:00

73 lines
2.7 KiB
Python

"""The same notebook against the low-level Server: an explicit bus + ListenHandler."""
from typing import Any
import mcp_types as types
from mcp.server.context import ServerRequestContext
from mcp.server.lowlevel import Server
from mcp.server.subscriptions import (
InMemorySubscriptionBus,
ListenHandler,
ResourceUpdated,
ToolsListChanged,
)
from stories._hosting import run_server_from_args
EDIT_NOTE_SCHEMA: dict[str, Any] = {
"type": "object",
"properties": {"name": {"type": "string"}, "text": {"type": "string"}},
"required": ["name", "text"],
}
EMPTY_SCHEMA: dict[str, Any] = {"type": "object", "properties": {}}
SEARCH_SCHEMA: dict[str, Any] = {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
}
def build_server() -> Server[Any]:
# The bus lives wherever your handlers can reach it; the lifespan is the
# natural home in a bigger app. The closure is enough here.
bus = InMemorySubscriptionBus()
notes = {"todo": "buy milk", "journal": "day one"}
search_enabled = False
async def list_tools(
ctx: ServerRequestContext[Any], params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:
tools = [
types.Tool(name="edit_note", description="Replace a note's text.", input_schema=EDIT_NOTE_SCHEMA),
types.Tool(name="enable_search", description="Register the search tool.", input_schema=EMPTY_SCHEMA),
]
if search_enabled:
tools.append(types.Tool(name="search", description="Find notes.", input_schema=SEARCH_SCHEMA))
return types.ListToolsResult(tools=tools)
async def call_tool(ctx: ServerRequestContext[Any], params: types.CallToolRequestParams) -> types.CallToolResult:
nonlocal search_enabled
args = params.arguments or {}
if params.name == "edit_note":
notes[args["name"]] = args["text"]
await bus.publish(ResourceUpdated(uri=f"note://{args['name']}"))
return types.CallToolResult(content=[types.TextContent(text="saved")])
if params.name == "enable_search":
search_enabled = True
await bus.publish(ToolsListChanged())
return types.CallToolResult(content=[types.TextContent(text="search is live")])
assert params.name == "search" and search_enabled
matches = [name for name, text in notes.items() if args["query"] in text]
return types.CallToolResult(content=[types.TextContent(text=", ".join(matches))])
return Server(
"subscriptions-example",
on_list_tools=list_tools,
on_call_tool=call_tool,
on_subscriptions_listen=ListenHandler(bus),
)
if __name__ == "__main__":
run_server_from_args(build_server)