Skip to content

Use Depends() for database connection and RequestContext injection into tool handlers #326

@dtehan-td

Description

@dtehan-td

Summary

Replace the multi-step wrapper chain (make_tool_wrapper, create_mcp_tool, execute_db_tool, asyncio.to_thread) with FastMCP v3 Depends() so database connections and request context are injected directly into handler signatures as hidden parameters.

Background

Every tool currently goes through ~130 lines of plumbing: make_tool_wrapper() strips internal params from the signature, create_mcp_tool() builds an async wrapper with asyncio.to_thread(), execute_db_tool() fetches the connection, and _fetch_request_context() reaches into FastMCP state for the RequestContext. FastMCP v3's Depends() (borrowed from FastAPI) handles all of this natively — parameters annotated with Depends() are automatically hidden from the MCP schema.

Proposed Approach

from fastmcp.dependencies import Depends

def get_db_connection(ctx: Context = CurrentContext()) -> Connection:
    tdconn = ctx.lifespan_context["tdconn"]
    return tdconn.engine.connect()

def get_request_context() -> RequestContext | None:
    ctx = get_context()
    return ctx.get_state_sync("request_context")

@mcp.tool
def base_readQuery(
    sql: str,
    conn: Connection = Depends(get_db_connection),
    rc: RequestContext | None = Depends(get_request_context),
) -> str:
    """Run a read-only SQL query."""
    ...

Files to Change

  • app.py:323-450
  • tools/utils/factory.py (can be deleted)
  • All tools/*/handle_*.py handler files (signature change)

Value

  • Eliminates factory.py entirely — create_mcp_tool, _fetch_request_context, and asyncio.to_thread wrapper are no longer needed.
  • Eliminates make_tool_wrapper() in app.py.
  • handle_* functions can be registered directly with @mcp.tool — no wrapping step.
  • Sync handlers still run in a thread pool automatically (FastMCP default for sync tools).

Dependencies

Requires lifespan management (#9) to be in place first. Benefits from docstring Args: sections (#6) being completed beforehand. A phased approach — adopt Depends() for new tools first and migrate existing handlers incrementally — is recommended to reduce risk.

Metadata

Metadata

Assignees

No one assigned
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions