refactor: split RequestContext between server and client (#1987)
This commit is contained in:
parent
050aeb6456
commit
b1f7eec3cd
35 changed files with 210 additions and 212 deletions
2
.github/actions/conformance/client.py
vendored
2
.github/actions/conformance/client.py
vendored
|
|
@ -187,7 +187,7 @@ async def run_sse_retry(server_url: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
async def default_elicitation_callback(
|
async def default_elicitation_callback(
|
||||||
context: RequestContext[ClientSession, Any], # noqa: ARG001
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
) -> types.ElicitResult | types.ErrorData:
|
) -> types.ElicitResult | types.ErrorData:
|
||||||
"""Accept elicitation and apply defaults from the schema (SEP-1034)."""
|
"""Accept elicitation and apply defaults from the schema (SEP-1034)."""
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,6 @@ from contextlib import asynccontextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from mcp.server.mcpserver import Context, MCPServer
|
from mcp.server.mcpserver import Context, MCPServer
|
||||||
from mcp.server.session import ServerSession
|
|
||||||
|
|
||||||
|
|
||||||
# Mock database class for example
|
# Mock database class for example
|
||||||
|
|
@ -275,7 +274,7 @@ mcp = MCPServer("My App", lifespan=app_lifespan)
|
||||||
|
|
||||||
# Access type-safe lifespan context in tools
|
# Access type-safe lifespan context in tools
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def query_db(ctx: Context[ServerSession, AppContext]) -> str:
|
def query_db(ctx: Context[AppContext]) -> str:
|
||||||
"""Tool that uses initialized resources."""
|
"""Tool that uses initialized resources."""
|
||||||
db = ctx.request_context.lifespan_context.db
|
db = ctx.request_context.lifespan_context.db
|
||||||
return db.query()
|
return db.query()
|
||||||
|
|
@ -2135,7 +2134,7 @@ server_params = StdioServerParameters(
|
||||||
|
|
||||||
# Optional: create a sampling callback
|
# Optional: create a sampling callback
|
||||||
async def handle_sampling_message(
|
async def handle_sampling_message(
|
||||||
context: RequestContext[ClientSession, None], params: types.CreateMessageRequestParams
|
context: RequestContext[ClientSession], params: types.CreateMessageRequestParams
|
||||||
) -> types.CreateMessageResult:
|
) -> types.CreateMessageResult:
|
||||||
print(f"Sampling request: {params.messages}")
|
print(f"Sampling request: {params.messages}")
|
||||||
return types.CreateMessageResult(
|
return types.CreateMessageResult(
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,50 @@ async def handle_tool(name: str, arguments: dict) -> list[TextContent]:
|
||||||
await ctx.session.send_progress_notification(ctx.meta["progress_token"], 0.5, 100)
|
await ctx.session.send_progress_notification(ctx.meta["progress_token"], 0.5, 100)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `RequestContext` and `ProgressContext` type parameters simplified
|
||||||
|
|
||||||
|
The `RequestContext` class has been split to separate shared fields from server-specific fields. The shared `RequestContext` now only takes 1 type parameter (the session type) instead of 3.
|
||||||
|
|
||||||
|
**`RequestContext` changes:**
|
||||||
|
|
||||||
|
- Type parameters reduced from `RequestContext[SessionT, LifespanContextT, RequestT]` to `RequestContext[SessionT]`
|
||||||
|
- Server-specific fields (`lifespan_context`, `experimental`, `request`, `close_sse_stream`, `close_standalone_sse_stream`) moved to new `ServerRequestContext` class in `mcp.server.context`
|
||||||
|
|
||||||
|
**`ProgressContext` changes:**
|
||||||
|
|
||||||
|
- Type parameters reduced from `ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]` to `ProgressContext[SessionT]`
|
||||||
|
|
||||||
|
**Before (v1):**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mcp.shared.context import RequestContext, LifespanContextT, RequestT
|
||||||
|
from mcp.shared.progress import ProgressContext
|
||||||
|
|
||||||
|
# RequestContext with 3 type parameters
|
||||||
|
ctx: RequestContext[ClientSession, LifespanContextT, RequestT]
|
||||||
|
|
||||||
|
# ProgressContext with 5 type parameters
|
||||||
|
progress_ctx: ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (v2):**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mcp.shared.context import RequestContext
|
||||||
|
from mcp.shared.progress import ProgressContext
|
||||||
|
|
||||||
|
# RequestContext with 1 type parameter
|
||||||
|
ctx: RequestContext[ClientSession]
|
||||||
|
|
||||||
|
# ProgressContext with 1 type parameter
|
||||||
|
progress_ctx: ProgressContext[ClientSession]
|
||||||
|
|
||||||
|
# For server-specific context with lifespan and request types
|
||||||
|
from mcp.server.context import ServerRequestContext, LifespanContextT, RequestT
|
||||||
|
|
||||||
|
server_ctx: ServerRequestContext[LifespanContextT, RequestT]
|
||||||
|
```
|
||||||
|
|
||||||
### Resource URI type changed from `AnyUrl` to `str`
|
### Resource URI type changed from `AnyUrl` to `str`
|
||||||
|
|
||||||
The `uri` field on resource-related types now uses `str` instead of Pydantic's `AnyUrl`. This aligns with the [MCP specification schema](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.ts) which defines URIs as plain strings (`uri: string`) without strict URL validation. This change allows relative paths like `users/me` that were previously rejected.
|
The `uri` field on resource-related types now uses `str` instead of Pydantic's `AnyUrl`. This aligns with the [MCP specification schema](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.ts) which defines URIs as plain strings (`uri: string`) without strict URL validation. This change allows relative paths like `users/me` that were previously rejected.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ This example demonstrates the spec-compliant polling pattern:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from mcp import ClientSession
|
from mcp import ClientSession
|
||||||
|
|
@ -24,7 +23,7 @@ from mcp.types import (
|
||||||
|
|
||||||
|
|
||||||
async def elicitation_callback(
|
async def elicitation_callback(
|
||||||
context: RequestContext[ClientSession, Any],
|
context: RequestContext[ClientSession],
|
||||||
params: ElicitRequestParams,
|
params: ElicitRequestParams,
|
||||||
) -> ElicitResult:
|
) -> ElicitResult:
|
||||||
"""Handle elicitation requests from the server."""
|
"""Handle elicitation requests from the server."""
|
||||||
|
|
@ -39,7 +38,7 @@ async def elicitation_callback(
|
||||||
|
|
||||||
|
|
||||||
async def sampling_callback(
|
async def sampling_callback(
|
||||||
context: RequestContext[ClientSession, Any],
|
context: RequestContext[ClientSession],
|
||||||
params: CreateMessageRequestParams,
|
params: CreateMessageRequestParams,
|
||||||
) -> CreateMessageResult:
|
) -> CreateMessageResult:
|
||||||
"""Handle sampling requests from the server."""
|
"""Handle sampling requests from the server."""
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ server_params = StdioServerParameters(
|
||||||
|
|
||||||
# Optional: create a sampling callback
|
# Optional: create a sampling callback
|
||||||
async def handle_sampling_message(
|
async def handle_sampling_message(
|
||||||
context: RequestContext[ClientSession, None], params: types.CreateMessageRequestParams
|
context: RequestContext[ClientSession], params: types.CreateMessageRequestParams
|
||||||
) -> types.CreateMessageResult:
|
) -> types.CreateMessageResult:
|
||||||
print(f"Sampling request: {params.messages}")
|
print(f"Sampling request: {params.messages}")
|
||||||
return types.CreateMessageResult(
|
return types.CreateMessageResult(
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ from mcp.types import URL_ELICITATION_REQUIRED
|
||||||
|
|
||||||
|
|
||||||
async def handle_elicitation(
|
async def handle_elicitation(
|
||||||
context: RequestContext[ClientSession, Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
) -> types.ElicitResult | types.ErrorData:
|
) -> types.ElicitResult | types.ErrorData:
|
||||||
"""Handle elicitation requests from the server.
|
"""Handle elicitation requests from the server.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from contextlib import asynccontextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from mcp.server.mcpserver import Context, MCPServer
|
from mcp.server.mcpserver import Context, MCPServer
|
||||||
from mcp.server.session import ServerSession
|
|
||||||
|
|
||||||
|
|
||||||
# Mock database class for example
|
# Mock database class for example
|
||||||
|
|
@ -51,7 +50,7 @@ mcp = MCPServer("My App", lifespan=app_lifespan)
|
||||||
|
|
||||||
# Access type-safe lifespan context in tools
|
# Access type-safe lifespan context in tools
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def query_db(ctx: Context[ServerSession, AppContext]) -> str:
|
def query_db(ctx: Context[AppContext]) -> str:
|
||||||
"""Tool that uses initialized resources."""
|
"""Tool that uses initialized resources."""
|
||||||
db = ctx.request_context.lifespan_context.db
|
db = ctx.request_context.lifespan_context.db
|
||||||
return db.query()
|
return db.query()
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,10 @@ Use cases:
|
||||||
- Server polls client's task status via tasks/get, tasks/result, etc.
|
- Server polls client's task status via tasks/get, tasks/result, etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING, Any, Protocol
|
from typing import TYPE_CHECKING, Protocol
|
||||||
|
|
||||||
from pydantic import TypeAdapter
|
from pydantic import TypeAdapter
|
||||||
|
|
||||||
|
|
@ -32,7 +34,7 @@ class GetTaskHandlerFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.GetTaskRequestParams,
|
params: types.GetTaskRequestParams,
|
||||||
) -> types.GetTaskResult | types.ErrorData: ... # pragma: no branch
|
) -> types.GetTaskResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
@ -45,7 +47,7 @@ class GetTaskResultHandlerFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.GetTaskPayloadRequestParams,
|
params: types.GetTaskPayloadRequestParams,
|
||||||
) -> types.GetTaskPayloadResult | types.ErrorData: ... # pragma: no branch
|
) -> types.GetTaskPayloadResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
@ -58,7 +60,7 @@ class ListTasksHandlerFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.PaginatedRequestParams | None,
|
params: types.PaginatedRequestParams | None,
|
||||||
) -> types.ListTasksResult | types.ErrorData: ... # pragma: no branch
|
) -> types.ListTasksResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
@ -71,7 +73,7 @@ class CancelTaskHandlerFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CancelTaskRequestParams,
|
params: types.CancelTaskRequestParams,
|
||||||
) -> types.CancelTaskResult | types.ErrorData: ... # pragma: no branch
|
) -> types.CancelTaskResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
@ -88,7 +90,7 @@ class TaskAugmentedSamplingFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CreateMessageRequestParams,
|
params: types.CreateMessageRequestParams,
|
||||||
task_metadata: types.TaskMetadata,
|
task_metadata: types.TaskMetadata,
|
||||||
) -> types.CreateTaskResult | types.ErrorData: ... # pragma: no branch
|
) -> types.CreateTaskResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
@ -106,14 +108,14 @@ class TaskAugmentedElicitationFnT(Protocol):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
task_metadata: types.TaskMetadata,
|
task_metadata: types.TaskMetadata,
|
||||||
) -> types.CreateTaskResult | types.ErrorData: ... # pragma: no branch
|
) -> types.CreateTaskResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
||||||
async def default_get_task_handler(
|
async def default_get_task_handler(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.GetTaskRequestParams,
|
params: types.GetTaskRequestParams,
|
||||||
) -> types.GetTaskResult | types.ErrorData:
|
) -> types.GetTaskResult | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
|
|
@ -123,7 +125,7 @@ async def default_get_task_handler(
|
||||||
|
|
||||||
|
|
||||||
async def default_get_task_result_handler(
|
async def default_get_task_result_handler(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.GetTaskPayloadRequestParams,
|
params: types.GetTaskPayloadRequestParams,
|
||||||
) -> types.GetTaskPayloadResult | types.ErrorData:
|
) -> types.GetTaskPayloadResult | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
|
|
@ -133,7 +135,7 @@ async def default_get_task_result_handler(
|
||||||
|
|
||||||
|
|
||||||
async def default_list_tasks_handler(
|
async def default_list_tasks_handler(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.PaginatedRequestParams | None,
|
params: types.PaginatedRequestParams | None,
|
||||||
) -> types.ListTasksResult | types.ErrorData:
|
) -> types.ListTasksResult | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
|
|
@ -143,7 +145,7 @@ async def default_list_tasks_handler(
|
||||||
|
|
||||||
|
|
||||||
async def default_cancel_task_handler(
|
async def default_cancel_task_handler(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CancelTaskRequestParams,
|
params: types.CancelTaskRequestParams,
|
||||||
) -> types.CancelTaskResult | types.ErrorData:
|
) -> types.CancelTaskResult | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
|
|
@ -153,7 +155,7 @@ async def default_cancel_task_handler(
|
||||||
|
|
||||||
|
|
||||||
async def default_task_augmented_sampling(
|
async def default_task_augmented_sampling(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CreateMessageRequestParams,
|
params: types.CreateMessageRequestParams,
|
||||||
task_metadata: types.TaskMetadata,
|
task_metadata: types.TaskMetadata,
|
||||||
) -> types.CreateTaskResult | types.ErrorData:
|
) -> types.CreateTaskResult | types.ErrorData:
|
||||||
|
|
@ -164,7 +166,7 @@ async def default_task_augmented_sampling(
|
||||||
|
|
||||||
|
|
||||||
async def default_task_augmented_elicitation(
|
async def default_task_augmented_elicitation(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
task_metadata: types.TaskMetadata,
|
task_metadata: types.TaskMetadata,
|
||||||
) -> types.CreateTaskResult | types.ErrorData:
|
) -> types.CreateTaskResult | types.ErrorData:
|
||||||
|
|
@ -248,7 +250,7 @@ class ExperimentalTaskHandlers:
|
||||||
|
|
||||||
async def handle_request(
|
async def handle_request(
|
||||||
self,
|
self,
|
||||||
ctx: RequestContext["ClientSession", Any],
|
ctx: RequestContext[ClientSession],
|
||||||
responder: RequestResponder[types.ServerRequest, types.ClientResult],
|
responder: RequestResponder[types.ServerRequest, types.ClientResult],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle a task-related request from the server.
|
"""Handle a task-related request from the server.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Protocol
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
|
@ -22,7 +24,7 @@ logger = logging.getLogger("client")
|
||||||
class SamplingFnT(Protocol):
|
class SamplingFnT(Protocol):
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CreateMessageRequestParams,
|
params: types.CreateMessageRequestParams,
|
||||||
) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData: ... # pragma: no branch
|
) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
@ -30,22 +32,19 @@ class SamplingFnT(Protocol):
|
||||||
class ElicitationFnT(Protocol):
|
class ElicitationFnT(Protocol):
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
) -> types.ElicitResult | types.ErrorData: ... # pragma: no branch
|
) -> types.ElicitResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
||||||
class ListRootsFnT(Protocol):
|
class ListRootsFnT(Protocol):
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self, context: RequestContext["ClientSession", Any]
|
self, context: RequestContext[ClientSession]
|
||||||
) -> types.ListRootsResult | types.ErrorData: ... # pragma: no branch
|
) -> types.ListRootsResult | types.ErrorData: ... # pragma: no branch
|
||||||
|
|
||||||
|
|
||||||
class LoggingFnT(Protocol):
|
class LoggingFnT(Protocol):
|
||||||
async def __call__(
|
async def __call__(self, params: types.LoggingMessageNotificationParams) -> None: ... # pragma: no branch
|
||||||
self,
|
|
||||||
params: types.LoggingMessageNotificationParams,
|
|
||||||
) -> None: ... # pragma: no branch
|
|
||||||
|
|
||||||
|
|
||||||
class MessageHandlerFnT(Protocol):
|
class MessageHandlerFnT(Protocol):
|
||||||
|
|
@ -62,7 +61,7 @@ async def _default_message_handler(
|
||||||
|
|
||||||
|
|
||||||
async def _default_sampling_callback(
|
async def _default_sampling_callback(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.CreateMessageRequestParams,
|
params: types.CreateMessageRequestParams,
|
||||||
) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData:
|
) -> types.CreateMessageResult | types.CreateMessageResultWithTools | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
|
|
@ -72,7 +71,7 @@ async def _default_sampling_callback(
|
||||||
|
|
||||||
|
|
||||||
async def _default_elicitation_callback(
|
async def _default_elicitation_callback(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
params: types.ElicitRequestParams,
|
params: types.ElicitRequestParams,
|
||||||
) -> types.ElicitResult | types.ErrorData:
|
) -> types.ElicitResult | types.ErrorData:
|
||||||
return types.ErrorData( # pragma: no cover
|
return types.ErrorData( # pragma: no cover
|
||||||
|
|
@ -82,7 +81,7 @@ async def _default_elicitation_callback(
|
||||||
|
|
||||||
|
|
||||||
async def _default_list_roots_callback(
|
async def _default_list_roots_callback(
|
||||||
context: RequestContext["ClientSession", Any],
|
context: RequestContext[ClientSession],
|
||||||
) -> types.ListRootsResult | types.ErrorData:
|
) -> types.ListRootsResult | types.ErrorData:
|
||||||
return types.ErrorData(
|
return types.ErrorData(
|
||||||
code=types.INVALID_REQUEST,
|
code=types.INVALID_REQUEST,
|
||||||
|
|
@ -153,10 +152,7 @@ class ClientSession(
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
elicitation = (
|
elicitation = (
|
||||||
types.ElicitationCapability(
|
types.ElicitationCapability(form=types.FormElicitationCapability(), url=types.UrlElicitationCapability())
|
||||||
form=types.FormElicitationCapability(),
|
|
||||||
url=types.UrlElicitationCapability(),
|
|
||||||
)
|
|
||||||
if self._elicitation_callback is not _default_elicitation_callback
|
if self._elicitation_callback is not _default_elicitation_callback
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
@ -414,12 +410,7 @@ class ClientSession(
|
||||||
await self.send_notification(types.RootsListChangedNotification())
|
await self.send_notification(types.RootsListChangedNotification())
|
||||||
|
|
||||||
async def _received_request(self, responder: RequestResponder[types.ServerRequest, types.ClientResult]) -> None:
|
async def _received_request(self, responder: RequestResponder[types.ServerRequest, types.ClientResult]) -> None:
|
||||||
ctx = RequestContext[ClientSession, Any](
|
ctx = RequestContext[ClientSession](request_id=responder.request_id, meta=responder.request_meta, session=self)
|
||||||
request_id=responder.request_id,
|
|
||||||
meta=responder.request_meta,
|
|
||||||
session=self,
|
|
||||||
lifespan_context=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delegate to experimental task handler if applicable
|
# Delegate to experimental task handler if applicable
|
||||||
if self._task_handlers.handles_request(responder.request):
|
if self._task_handlers.handles_request(responder.request):
|
||||||
|
|
|
||||||
23
src/mcp/server/context.py
Normal file
23
src/mcp/server/context.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Generic
|
||||||
|
|
||||||
|
from typing_extensions import TypeVar
|
||||||
|
|
||||||
|
from mcp.server.experimental.request_context import Experimental
|
||||||
|
from mcp.server.session import ServerSession
|
||||||
|
from mcp.shared.context import RequestContext
|
||||||
|
from mcp.shared.message import CloseSSEStreamCallback
|
||||||
|
|
||||||
|
LifespanContextT = TypeVar("LifespanContextT")
|
||||||
|
RequestT = TypeVar("RequestT", default=Any)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class ServerRequestContext(RequestContext[ServerSession], Generic[LifespanContextT, RequestT]):
|
||||||
|
lifespan_context: LifespanContextT
|
||||||
|
experimental: Experimental
|
||||||
|
request: RequestT | None = None
|
||||||
|
close_sse_stream: CloseSSEStreamCallback | None = None
|
||||||
|
close_standalone_sse_stream: CloseSSEStreamCallback | None = None
|
||||||
|
|
@ -57,10 +57,7 @@ class Experimental:
|
||||||
return self._client_capabilities.tasks is not None
|
return self._client_capabilities.tasks is not None
|
||||||
|
|
||||||
def validate_task_mode(
|
def validate_task_mode(
|
||||||
self,
|
self, tool_task_mode: TaskExecutionMode | None, *, raise_error: bool = True
|
||||||
tool_task_mode: TaskExecutionMode | None,
|
|
||||||
*,
|
|
||||||
raise_error: bool = True,
|
|
||||||
) -> ErrorData | None:
|
) -> ErrorData | None:
|
||||||
"""Validate that the request is compatible with the tool's task execution mode.
|
"""Validate that the request is compatible with the tool's task execution mode.
|
||||||
|
|
||||||
|
|
@ -95,12 +92,7 @@ class Experimental:
|
||||||
|
|
||||||
return error
|
return error
|
||||||
|
|
||||||
def validate_for_tool(
|
def validate_for_tool(self, tool: Tool, *, raise_error: bool = True) -> ErrorData | None:
|
||||||
self,
|
|
||||||
tool: Tool,
|
|
||||||
*,
|
|
||||||
raise_error: bool = True,
|
|
||||||
) -> ErrorData | None:
|
|
||||||
"""Validate that the request is compatible with the given tool.
|
"""Validate that the request is compatible with the given tool.
|
||||||
|
|
||||||
Convenience wrapper around validate_task_mode that extracts the mode from a Tool.
|
Convenience wrapper around validate_task_mode that extracts the mode from a Tool.
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend, RequireAut
|
||||||
from mcp.server.auth.provider import OAuthAuthorizationServerProvider, TokenVerifier
|
from mcp.server.auth.provider import OAuthAuthorizationServerProvider, TokenVerifier
|
||||||
from mcp.server.auth.routes import build_resource_metadata_url, create_auth_routes, create_protected_resource_routes
|
from mcp.server.auth.routes import build_resource_metadata_url, create_auth_routes, create_protected_resource_routes
|
||||||
from mcp.server.auth.settings import AuthSettings
|
from mcp.server.auth.settings import AuthSettings
|
||||||
|
from mcp.server.context import ServerRequestContext
|
||||||
from mcp.server.experimental.request_context import Experimental
|
from mcp.server.experimental.request_context import Experimental
|
||||||
from mcp.server.lowlevel.experimental import ExperimentalHandlers
|
from mcp.server.lowlevel.experimental import ExperimentalHandlers
|
||||||
from mcp.server.lowlevel.func_inspection import create_call_wrapper
|
from mcp.server.lowlevel.func_inspection import create_call_wrapper
|
||||||
|
|
@ -100,7 +101,6 @@ from mcp.server.session import ServerSession
|
||||||
from mcp.server.streamable_http import EventStore
|
from mcp.server.streamable_http import EventStore
|
||||||
from mcp.server.streamable_http_manager import StreamableHTTPASGIApp, StreamableHTTPSessionManager
|
from mcp.server.streamable_http_manager import StreamableHTTPASGIApp, StreamableHTTPSessionManager
|
||||||
from mcp.server.transport_security import TransportSecuritySettings
|
from mcp.server.transport_security import TransportSecuritySettings
|
||||||
from mcp.shared.context import RequestContext
|
|
||||||
from mcp.shared.exceptions import MCPError, UrlElicitationRequiredError
|
from mcp.shared.exceptions import MCPError, UrlElicitationRequiredError
|
||||||
from mcp.shared.message import ServerMessageMetadata, SessionMessage
|
from mcp.shared.message import ServerMessageMetadata, SessionMessage
|
||||||
from mcp.shared.session import RequestResponder
|
from mcp.shared.session import RequestResponder
|
||||||
|
|
@ -117,16 +117,11 @@ UnstructuredContent: TypeAlias = Iterable[types.ContentBlock]
|
||||||
CombinationContent: TypeAlias = tuple[UnstructuredContent, StructuredContent]
|
CombinationContent: TypeAlias = tuple[UnstructuredContent, StructuredContent]
|
||||||
|
|
||||||
# This will be properly typed in each Server instance's context
|
# This will be properly typed in each Server instance's context
|
||||||
request_ctx: contextvars.ContextVar[RequestContext[ServerSession, Any, Any]] = contextvars.ContextVar("request_ctx")
|
request_ctx: contextvars.ContextVar[ServerRequestContext[Any, Any]] = contextvars.ContextVar("request_ctx")
|
||||||
|
|
||||||
|
|
||||||
class NotificationOptions:
|
class NotificationOptions:
|
||||||
def __init__(
|
def __init__(self, prompts_changed: bool = False, resources_changed: bool = False, tools_changed: bool = False):
|
||||||
self,
|
|
||||||
prompts_changed: bool = False,
|
|
||||||
resources_changed: bool = False,
|
|
||||||
tools_changed: bool = False,
|
|
||||||
):
|
|
||||||
self.prompts_changed = prompts_changed
|
self.prompts_changed = prompts_changed
|
||||||
self.resources_changed = resources_changed
|
self.resources_changed = resources_changed
|
||||||
self.tools_changed = tools_changed
|
self.tools_changed = tools_changed
|
||||||
|
|
@ -253,9 +248,7 @@ class Server(Generic[LifespanResultT, RequestT]):
|
||||||
return capabilities
|
return capabilities
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def request_context(
|
def request_context(self) -> ServerRequestContext[LifespanResultT, RequestT]:
|
||||||
self,
|
|
||||||
) -> RequestContext[ServerSession, LifespanResultT, RequestT]:
|
|
||||||
"""If called outside of a request context, this will raise a LookupError."""
|
"""If called outside of a request context, this will raise a LookupError."""
|
||||||
return request_ctx.get()
|
return request_ctx.get()
|
||||||
|
|
||||||
|
|
@ -762,12 +755,12 @@ class Server(Generic[LifespanResultT, RequestT]):
|
||||||
if hasattr(req, "params") and req.params is not None:
|
if hasattr(req, "params") and req.params is not None:
|
||||||
task_metadata = getattr(req.params, "task", None)
|
task_metadata = getattr(req.params, "task", None)
|
||||||
token = request_ctx.set(
|
token = request_ctx.set(
|
||||||
RequestContext(
|
ServerRequestContext(
|
||||||
message.request_id,
|
request_id=message.request_id,
|
||||||
message.request_meta,
|
meta=message.request_meta,
|
||||||
session,
|
session=session,
|
||||||
lifespan_context,
|
lifespan_context=lifespan_context,
|
||||||
Experimental(
|
experimental=Experimental(
|
||||||
task_metadata=task_metadata,
|
task_metadata=task_metadata,
|
||||||
_client_capabilities=client_capabilities,
|
_client_capabilities=client_capabilities,
|
||||||
_session=session,
|
_session=session,
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,8 @@ from mcp.server.mcpserver.utilities.func_metadata import func_metadata
|
||||||
from mcp.types import ContentBlock, Icon, TextContent
|
from mcp.types import ContentBlock, Icon, TextContent
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseModel):
|
class Message(BaseModel):
|
||||||
|
|
@ -137,7 +136,7 @@ class Prompt(BaseModel):
|
||||||
async def render(
|
async def render(
|
||||||
self,
|
self,
|
||||||
arguments: dict[str, Any] | None = None,
|
arguments: dict[str, Any] | None = None,
|
||||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
context: Context[LifespanContextT, RequestT] | None = None,
|
||||||
) -> list[Message]:
|
) -> list[Message]:
|
||||||
"""Render the prompt with arguments."""
|
"""Render the prompt with arguments."""
|
||||||
# Validate required arguments
|
# Validate required arguments
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ from mcp.server.mcpserver.prompts.base import Message, Prompt
|
||||||
from mcp.server.mcpserver.utilities.logging import get_logger
|
from mcp.server.mcpserver.utilities.logging import get_logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -50,7 +49,7 @@ class PromptManager:
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
arguments: dict[str, Any] | None = None,
|
arguments: dict[str, Any] | None = None,
|
||||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
context: Context[LifespanContextT, RequestT] | None = None,
|
||||||
) -> list[Message]:
|
) -> list[Message]:
|
||||||
"""Render a prompt by name with arguments."""
|
"""Render a prompt by name with arguments."""
|
||||||
prompt = self.get_prompt(name)
|
prompt = self.get_prompt(name)
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,8 @@ from mcp.server.mcpserver.utilities.logging import get_logger
|
||||||
from mcp.types import Annotations, Icon
|
from mcp.types import Annotations, Icon
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -82,7 +81,7 @@ class ResourceManager:
|
||||||
return template
|
return template
|
||||||
|
|
||||||
async def get_resource(
|
async def get_resource(
|
||||||
self, uri: AnyUrl | str, context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None
|
self, uri: AnyUrl | str, context: Context[LifespanContextT, RequestT] | None = None
|
||||||
) -> Resource:
|
) -> Resource:
|
||||||
"""Get resource by URI, checking concrete resources first, then templates."""
|
"""Get resource by URI, checking concrete resources first, then templates."""
|
||||||
uri_str = str(uri)
|
uri_str = str(uri)
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ from mcp.server.mcpserver.utilities.func_metadata import func_metadata
|
||||||
from mcp.types import Annotations, Icon
|
from mcp.types import Annotations, Icon
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceTemplate(BaseModel):
|
class ResourceTemplate(BaseModel):
|
||||||
|
|
@ -100,7 +99,7 @@ class ResourceTemplate(BaseModel):
|
||||||
self,
|
self,
|
||||||
uri: str,
|
uri: str,
|
||||||
params: dict[str, Any],
|
params: dict[str, Any],
|
||||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
context: Context[LifespanContextT, RequestT] | None = None,
|
||||||
) -> Resource:
|
) -> Resource:
|
||||||
"""Create a resource from the template with the given parameters."""
|
"""Create a resource from the template with the given parameters."""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ from mcp.server.auth.middleware.auth_context import AuthContextMiddleware
|
||||||
from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend, RequireAuthMiddleware
|
from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend, RequireAuthMiddleware
|
||||||
from mcp.server.auth.provider import OAuthAuthorizationServerProvider, ProviderTokenVerifier, TokenVerifier
|
from mcp.server.auth.provider import OAuthAuthorizationServerProvider, ProviderTokenVerifier, TokenVerifier
|
||||||
from mcp.server.auth.settings import AuthSettings
|
from mcp.server.auth.settings import AuthSettings
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT, ServerRequestContext
|
||||||
from mcp.server.elicitation import ElicitationResult, ElicitSchemaModelT, UrlElicitationResult, elicit_with_validation
|
from mcp.server.elicitation import ElicitationResult, ElicitSchemaModelT, UrlElicitationResult, elicit_with_validation
|
||||||
from mcp.server.elicitation import elicit_url as _elicit_url
|
from mcp.server.elicitation import elicit_url as _elicit_url
|
||||||
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
||||||
|
|
@ -36,13 +37,11 @@ from mcp.server.mcpserver.resources import FunctionResource, Resource, ResourceM
|
||||||
from mcp.server.mcpserver.tools import Tool, ToolManager
|
from mcp.server.mcpserver.tools import Tool, ToolManager
|
||||||
from mcp.server.mcpserver.utilities.context_injection import find_context_parameter
|
from mcp.server.mcpserver.utilities.context_injection import find_context_parameter
|
||||||
from mcp.server.mcpserver.utilities.logging import configure_logging, get_logger
|
from mcp.server.mcpserver.utilities.logging import configure_logging, get_logger
|
||||||
from mcp.server.session import ServerSession, ServerSessionT
|
|
||||||
from mcp.server.sse import SseServerTransport
|
from mcp.server.sse import SseServerTransport
|
||||||
from mcp.server.stdio import stdio_server
|
from mcp.server.stdio import stdio_server
|
||||||
from mcp.server.streamable_http import EventStore
|
from mcp.server.streamable_http import EventStore
|
||||||
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
||||||
from mcp.server.transport_security import TransportSecuritySettings
|
from mcp.server.transport_security import TransportSecuritySettings
|
||||||
from mcp.shared.context import LifespanContextT, RequestContext, RequestT
|
|
||||||
from mcp.types import Annotations, ContentBlock, GetPromptResult, Icon, ToolAnnotations
|
from mcp.types import Annotations, ContentBlock, GetPromptResult, Icon, ToolAnnotations
|
||||||
from mcp.types import Prompt as MCPPrompt
|
from mcp.types import Prompt as MCPPrompt
|
||||||
from mcp.types import PromptArgument as MCPPromptArgument
|
from mcp.types import PromptArgument as MCPPromptArgument
|
||||||
|
|
@ -294,7 +293,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
for info in tools
|
for info in tools
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_context(self) -> Context[ServerSession, LifespanResultT, Request]:
|
def get_context(self) -> Context[LifespanResultT, Request]:
|
||||||
"""Returns a Context object. Note that the context will only be valid
|
"""Returns a Context object. Note that the context will only be valid
|
||||||
during a request; outside a request, most methods will error.
|
during a request; outside a request, most methods will error.
|
||||||
"""
|
"""
|
||||||
|
|
@ -972,7 +971,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||||
raise ValueError(str(e))
|
raise ValueError(str(e))
|
||||||
|
|
||||||
|
|
||||||
class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]):
|
class Context(BaseModel, Generic[LifespanContextT, RequestT]):
|
||||||
"""Context object providing access to MCP capabilities.
|
"""Context object providing access to MCP capabilities.
|
||||||
|
|
||||||
This provides a cleaner interface to MCP's RequestContext functionality.
|
This provides a cleaner interface to MCP's RequestContext functionality.
|
||||||
|
|
@ -1006,14 +1005,15 @@ class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]):
|
||||||
The context is optional - tools that don't need it can omit the parameter.
|
The context is optional - tools that don't need it can omit the parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_request_context: RequestContext[ServerSessionT, LifespanContextT, RequestT] | None
|
_request_context: ServerRequestContext[LifespanContextT, RequestT] | None
|
||||||
_mcp_server: MCPServer | None
|
_mcp_server: MCPServer | None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
request_context: (RequestContext[ServerSessionT, LifespanContextT, RequestT] | None) = None,
|
request_context: ServerRequestContext[LifespanContextT, RequestT] | None = None,
|
||||||
mcp_server: MCPServer | None = None,
|
mcp_server: MCPServer | None = None,
|
||||||
|
# TODO(Marcelo): We should drop this kwargs parameter.
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
@ -1028,9 +1028,7 @@ class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]):
|
||||||
return self._mcp_server # pragma: no cover
|
return self._mcp_server # pragma: no cover
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def request_context(
|
def request_context(self) -> ServerRequestContext[LifespanContextT, RequestT]:
|
||||||
self,
|
|
||||||
) -> RequestContext[ServerSessionT, LifespanContextT, RequestT]:
|
|
||||||
"""Access to the underlying request context."""
|
"""Access to the underlying request context."""
|
||||||
if self._request_context is None: # pragma: no cover
|
if self._request_context is None: # pragma: no cover
|
||||||
raise ValueError("Context is not available outside of a request")
|
raise ValueError("Context is not available outside of a request")
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ from mcp.shared.tool_name_validation import validate_and_warn_tool_name
|
||||||
from mcp.types import Icon, ToolAnnotations
|
from mcp.types import Icon, ToolAnnotations
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
|
|
||||||
|
|
||||||
class Tool(BaseModel):
|
class Tool(BaseModel):
|
||||||
|
|
@ -93,7 +92,7 @@ class Tool(BaseModel):
|
||||||
async def run(
|
async def run(
|
||||||
self,
|
self,
|
||||||
arguments: dict[str, Any],
|
arguments: dict[str, Any],
|
||||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
context: Context[LifespanContextT, RequestT] | None = None,
|
||||||
convert_result: bool = False,
|
convert_result: bool = False,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Run the tool with arguments."""
|
"""Run the tool with arguments."""
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,11 @@ from typing import TYPE_CHECKING, Any
|
||||||
from mcp.server.mcpserver.exceptions import ToolError
|
from mcp.server.mcpserver.exceptions import ToolError
|
||||||
from mcp.server.mcpserver.tools.base import Tool
|
from mcp.server.mcpserver.tools.base import Tool
|
||||||
from mcp.server.mcpserver.utilities.logging import get_logger
|
from mcp.server.mcpserver.utilities.logging import get_logger
|
||||||
from mcp.shared.context import LifespanContextT, RequestT
|
|
||||||
from mcp.types import Icon, ToolAnnotations
|
from mcp.types import Icon, ToolAnnotations
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from mcp.server.context import LifespanContextT, RequestT
|
||||||
from mcp.server.mcpserver.server import Context
|
from mcp.server.mcpserver.server import Context
|
||||||
from mcp.server.session import ServerSessionT
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
@ -82,7 +81,7 @@ class ToolManager:
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
arguments: dict[str, Any],
|
arguments: dict[str, Any],
|
||||||
context: Context[ServerSessionT, LifespanContextT, RequestT] | None = None,
|
context: Context[LifespanContextT, RequestT] | None = None,
|
||||||
convert_result: bool = False,
|
convert_result: bool = False,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Call a tool by name with arguments."""
|
"""Call a tool by name with arguments."""
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,20 @@
|
||||||
"""Request context for MCP handlers."""
|
"""Request context for MCP handlers."""
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from typing import Any, Generic
|
from typing import Any, Generic
|
||||||
|
|
||||||
from typing_extensions import TypeVar
|
from typing_extensions import TypeVar
|
||||||
|
|
||||||
from mcp.shared.message import CloseSSEStreamCallback
|
|
||||||
from mcp.shared.session import BaseSession
|
from mcp.shared.session import BaseSession
|
||||||
from mcp.types import RequestId, RequestParamsMeta
|
from mcp.types import RequestId, RequestParamsMeta
|
||||||
|
|
||||||
SessionT = TypeVar("SessionT", bound=BaseSession[Any, Any, Any, Any, Any])
|
SessionT = TypeVar("SessionT", bound=BaseSession[Any, Any, Any, Any, Any])
|
||||||
LifespanContextT = TypeVar("LifespanContextT")
|
|
||||||
RequestT = TypeVar("RequestT", default=Any)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass(kw_only=True)
|
||||||
class RequestContext(Generic[SessionT, LifespanContextT, RequestT]):
|
class RequestContext(Generic[SessionT]):
|
||||||
|
"""Common context for handling incoming requests."""
|
||||||
|
|
||||||
request_id: RequestId
|
request_id: RequestId
|
||||||
meta: RequestParamsMeta | None
|
meta: RequestParamsMeta | None
|
||||||
session: SessionT
|
session: SessionT
|
||||||
lifespan_context: LifespanContextT
|
|
||||||
# NOTE: This is typed as Any to avoid circular imports. The actual type is
|
|
||||||
# mcp.server.experimental.request_context.Experimental, but importing it here
|
|
||||||
# triggers mcp.server.__init__ -> mcpserver -> tools -> back to this module.
|
|
||||||
# The Server sets this to an Experimental instance at runtime.
|
|
||||||
experimental: Any = field(default=None)
|
|
||||||
request: RequestT | None = None
|
|
||||||
close_sse_stream: CloseSSEStreamCallback | None = None
|
|
||||||
close_standalone_sse_stream: CloseSSEStreamCallback | None = None
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue