UseCase MCP API
create_use_case_mcp_server
from pydantic_resolve.use_case import create_use_case_mcp_server
mcp = create_use_case_mcp_server(
apps: list[UseCaseAppConfig],
name: str = "Pydantic-Resolve UseCase API",
) -> "FastMCP"
Creates an MCP server that exposes UseCaseService methods to AI agents via progressive disclosure.
| Parameter | Type | Description |
|---|---|---|
apps |
list[UseCaseAppConfig] |
Application configurations |
name |
str |
MCP server name (default: "Pydantic-Resolve UseCase API") |
Returns a configured FastMCP server instance.
mcp = create_use_case_mcp_server(
apps=[UseCaseAppConfig(name="project", services=[TaskService])]
)
mcp.run(transport="streamable-http", port=8080)
UseCaseAppConfig
from pydantic_resolve.use_case import UseCaseAppConfig
UseCaseAppConfig(
name: str,
services: list[type[UseCaseService]],
description: str | None = None,
enable_mutation: bool = True,
context_extractor: Callable | None = None,
)
| Parameter | Type | Description |
|---|---|---|
name |
str |
Application name (required) |
services |
list[type[UseCaseService]] |
List of UseCaseService subclasses (required) |
description |
str \| None |
Application description for AI agents |
enable_mutation |
bool |
Whether mutation methods are visible in MCP (default: True) |
context_extractor |
Callable \| None |
Callback to extract request-scoped context |
context_extractor
Optional callback that extracts request-scoped context (e.g. user identity from Authorization header) from the MCP HTTP request. The extracted dict is merged into method kwargs for parameters annotated with FromContext.
Signature: (Context) -> dict | Awaitable[dict], supports both sync and async.
from fastmcp.server.context import Context
from fastmcp.server.dependencies import get_http_headers
def extract_user_context(ctx: Context) -> dict:
headers = get_http_headers(include={"authorization"})
auth = headers.get("authorization", "")
if auth.startswith("Bearer "):
token = auth[7:]
return {"user_id": int(token)}
return {}
apps = [
UseCaseAppConfig(
name="project",
services=[TaskService],
context_extractor=extract_user_context,
),
]
Data flow:
HTTP Request (Authorization: Bearer <token>)
→ FastMCP Context
→ context_extractor(ctx) → {"user_id": 1}
→ call_use_case merges context into kwargs
→ TaskService.get_my_tasks(user_id=1)
Important: get_http_headers() excludes authorization, content-type, and other sensitive headers by default. You must pass include={"authorization"} to receive the Authorization header. When MCP runs via stdio transport (no HTTP request), get_http_headers() returns an empty dict.
UseCaseService
from pydantic_resolve.use_case import UseCaseService
from pydantic_resolve import query, mutation
class MyService(UseCaseService):
"""Service description (used by AI agents)."""
@query
async def my_method(cls, param1: int) -> MyDTO:
"""Method description (used by AI agents)."""
...
Base class for business service definitions. The BusinessMeta metaclass automatically discovers methods decorated with @query or @mutation and stores them for introspection.
Conventions:
- Methods must be decorated with
@queryor@mutation(frompydantic_resolve) - Methods must be
async - Private methods (prefixed with
_) andget_tag_nameare excluded from discovery - Docstrings on the class and methods become descriptions visible to AI agents
- Return type annotations are used for SDL type generation
get_tag_name
Returns the class name by default. Override to customize the OpenAPI tag name when using with FastAPI:
class TaskService(UseCaseService):
@classmethod
def get_tag_name(cls):
return "Tasks"
# Usage in FastAPI
@app.get("/tasks", tags=[TaskService.get_tag_name()])
FromContext
from typing import Annotated
from pydantic_resolve.use_case import FromContext
user_id: Annotated[int, FromContext()]
Marker annotation for method parameters that should receive values from context_extractor rather than from the MCP tool's params JSON. This keeps the method signature identical for both FastAPI (parameter passed directly) and MCP (injected from context).
class TaskService(UseCaseService):
@query
async def get_my_tasks(
cls,
user_id: Annotated[int, FromContext()],
) -> list[TaskSummary]:
...
- If the context key is present, it is injected into the method call
- If the context key is missing and the parameter has no default, an error is returned
- If the context key is missing and the parameter has a default, the default is used
Progressive Disclosure Tools
The MCP server registers these tools automatically:
| Tool | Layer | Description |
|---|---|---|
list_apps |
0 | Discover available applications |
list_services |
1 | List services in an app |
describe_service |
2 | Get method signatures, parameter schemas, and DTO type definitions |
call_use_case |
3 | Execute a specific method |
call_use_case
| Parameter | Type | Description |
|---|---|---|
app_name |
str |
Application name (from list_apps) |
service_name |
str |
Service name (from list_services) |
method_name |
str |
Method name (from describe_service) |
params |
str |
JSON string with method parameters (default: "{}") |
The params string is parsed as JSON and passed as keyword arguments to the method. Parameters annotated with FromContext are injected from the context_extractor result, not from params.