Refactor provider execution: components own their execution (#2663)

* Add test_custom_subclass_tasks.py

* Refactor provider execution: delegate to middleware via wrapper components

- Remove execution methods (call_tool, read_resource, etc.) from Provider base
- Add FastMCPProvider* wrapper classes that delegate to child server middleware
- Move task routing to Tool._run() using contextvars (_task_metadata, _tool_call_key)
- Add convert_to_tool_result(result, output_schema) utility for Docket results
- Add convert_to_prompt_result() utility for prompt task results
- Pass namespaced key via add_to_docket(name=) for mounted tool lookup

* Standardize add_to_docket() with fn_key/task_key parameters

All components now use explicit fn_key (function lookup) and task_key
(result storage) parameters instead of relying on implicit key handling.
This fixes mounted component task execution where the MCP-visible key
differs from the Docket-registered function name.

* Add middleware chain tests for three-level mount hierarchy

Tests verify middleware runs at parent, child, and grandchild levels
for tools, resources, prompts, and resource templates.

* WIP: Provider refactor - unified submit_to_docket, template _read() in progress

Work in progress on refactoring execution to use component _read()/_run()/_render() methods.
Template background tasks not yet working - needs fix for Docket key lookup.

* Fix conversion functions to take full component for attribute access

Pass Tool/Prompt/Resource/Template to conversion functions instead of
individual attributes, ensuring access to serializer, output_schema,
mime_type, etc. Also fixes mixed-content output schema validation.

* Refactor: unified convert_result() methods and check_background_task helper

- Add convert_result() instance methods to all component types (Tool, Prompt, Resource, ResourceTemplate)
- Extract duplicated task routing logic into check_background_task() helper
- Fix type annotations on FastMCPProviderResource.read() and FastMCPProviderPrompt.render()
- Update protocol.py to use component.convert_result() uniformly

* Update tests to use namespace= instead of deprecated prefix= parameter
This commit is contained in:
Jeremiah Lowin 2025-12-21 15:03:24 -05:00 committed by GitHub
commit 19fdac7b02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1857 additions and 958 deletions

View file

@ -14,6 +14,7 @@ from __future__ import annotations
import asyncio
import json
from collections.abc import Sequence
from pathlib import Path
from typing import Any
@ -21,7 +22,6 @@ import aiosqlite
from rich import print
from fastmcp import Client, FastMCP
from fastmcp.server.context import Context
from fastmcp.server.providers import Provider
from fastmcp.tools.tool import Tool, ToolResult
@ -72,16 +72,17 @@ class SQLiteToolProvider(Provider):
"""
def __init__(self, db_path: str):
super().__init__()
self.db_path = db_path
async def list_tools(self, context: Context) -> list[Tool]:
async def list_tools(self) -> Sequence[Tool]:
async with aiosqlite.connect(self.db_path) as db:
db.row_factory = aiosqlite.Row
async with db.execute("SELECT * FROM tools WHERE enabled = 1") as cursor:
rows = await cursor.fetchall()
return [self._make_tool(row) for row in rows]
async def get_tool(self, context: Context, name: str) -> Tool | None:
async def get_tool(self, name: str) -> Tool | None:
async with aiosqlite.connect(self.db_path) as db:
db.row_factory = aiosqlite.Row
async with db.execute(