chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user