mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-27 15:51:33 +02:00
Merge branch 'main' into custom-routes
This commit is contained in:
commit
9a906f05cf
13 changed files with 129 additions and 19 deletions
|
|
@ -210,6 +210,23 @@ class Client:
|
|||
result = await self.session.send_ping()
|
||||
return isinstance(result, mcp.types.EmptyResult)
|
||||
|
||||
async def cancel(
|
||||
self,
|
||||
request_id: str | int,
|
||||
reason: str | None = None,
|
||||
) -> None:
|
||||
"""Send a cancellation notification for an in-progress request."""
|
||||
notification = mcp.types.ClientNotification(
|
||||
mcp.types.CancelledNotification(
|
||||
method="notifications/cancelled",
|
||||
params=mcp.types.CancelledNotificationParams(
|
||||
requestId=request_id,
|
||||
reason=reason,
|
||||
),
|
||||
)
|
||||
)
|
||||
await self.session.send_notification(notification)
|
||||
|
||||
async def progress(
|
||||
self,
|
||||
progress_token: str | int,
|
||||
|
|
|
|||
|
|
@ -535,8 +535,12 @@ class MCPConfigTransport(ClientTransport):
|
|||
config = MCPConfig.from_dict(config)
|
||||
self.config = config
|
||||
|
||||
# if there are no servers, raise an error
|
||||
if len(self.config.mcpServers) == 0:
|
||||
raise ValueError("No MCP servers defined in the config")
|
||||
|
||||
# if there's exactly one server, create a client for that server
|
||||
if len(self.config.mcpServers) == 1:
|
||||
elif len(self.config.mcpServers) == 1:
|
||||
self.transport = list(self.config.mcpServers.values())[0].to_transport()
|
||||
|
||||
# otherwise create a composite client
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from mcp.shared.context import RequestContext
|
|||
from mcp.types import (
|
||||
CreateMessageResult,
|
||||
ImageContent,
|
||||
ModelHint,
|
||||
ModelPreferences,
|
||||
Root,
|
||||
SamplingMessage,
|
||||
TextContent,
|
||||
|
|
@ -200,6 +202,7 @@ class Context:
|
|||
system_prompt: str | None = None,
|
||||
temperature: float | None = None,
|
||||
max_tokens: int | None = None,
|
||||
model_preferences: ModelPreferences | str | list[str] | None = None,
|
||||
) -> TextContent | ImageContent:
|
||||
"""
|
||||
Send a sampling request to the client and await the response.
|
||||
|
|
@ -231,6 +234,7 @@ class Context:
|
|||
system_prompt=system_prompt,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
model_preferences=self._parse_model_preferences(model_preferences),
|
||||
)
|
||||
|
||||
return result.content
|
||||
|
|
@ -248,3 +252,45 @@ class Context:
|
|||
)
|
||||
|
||||
return fastmcp.server.dependencies.get_http_request()
|
||||
|
||||
def _parse_model_preferences(
|
||||
self, model_preferences: ModelPreferences | str | list[str] | None
|
||||
) -> ModelPreferences | None:
|
||||
"""
|
||||
Validates and converts user input for model_preferences into a ModelPreferences object.
|
||||
|
||||
Args:
|
||||
model_preferences (ModelPreferences | str | list[str] | None):
|
||||
The model preferences to use. Accepts:
|
||||
- ModelPreferences (returns as-is)
|
||||
- str (single model hint)
|
||||
- list[str] (multiple model hints)
|
||||
- None (no preferences)
|
||||
|
||||
Returns:
|
||||
ModelPreferences | None: The parsed ModelPreferences object, or None if not provided.
|
||||
|
||||
Raises:
|
||||
ValueError: If the input is not a supported type or contains invalid values.
|
||||
"""
|
||||
if model_preferences is None:
|
||||
return None
|
||||
elif isinstance(model_preferences, ModelPreferences):
|
||||
return model_preferences
|
||||
elif isinstance(model_preferences, str):
|
||||
# Single model hint
|
||||
return ModelPreferences(hints=[ModelHint(name=model_preferences)])
|
||||
elif isinstance(model_preferences, list):
|
||||
# List of model hints (strings)
|
||||
if not all(isinstance(h, str) for h in model_preferences):
|
||||
raise ValueError(
|
||||
"All elements of model_preferences list must be"
|
||||
" strings (model name hints)."
|
||||
)
|
||||
return ModelPreferences(
|
||||
hints=[ModelHint(name=h) for h in model_preferences]
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"model_preferences must be one of: ModelPreferences, str, list[str], or None."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -998,7 +998,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
from fastmcp.server.proxy import FastMCPProxy
|
||||
|
||||
if tool_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Tools are now prefixed using 'prefix_toolname' format.",
|
||||
|
|
@ -1007,7 +1007,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
if resource_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The resource_separator parameter is deprecated and ignored. "
|
||||
"Resource prefixes are now added using the protocol://prefix/path format.",
|
||||
|
|
@ -1016,7 +1016,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
if prompt_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Prompts are now prefixed using 'prefix_promptname' format.",
|
||||
|
|
@ -1083,7 +1083,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
prompt_separator: Deprecated. Separator for prompt names.
|
||||
"""
|
||||
if tool_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Tools are now prefixed using 'prefix_toolname' format.",
|
||||
|
|
@ -1092,7 +1092,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
if resource_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The resource_separator parameter is deprecated and ignored. "
|
||||
"Resource prefixes are now added using the protocol://prefix/path format.",
|
||||
|
|
@ -1101,7 +1101,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
if prompt_separator is not None:
|
||||
# Deprecated since 2.3.6
|
||||
# Deprecated since 2.4.0
|
||||
warnings.warn(
|
||||
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
||||
"Prompts are now prefixed using 'prefix_promptname' format.",
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ def infer_transport_type_from_url(
|
|||
return "streamable-http"
|
||||
|
||||
|
||||
class LocalMCPServer(BaseModel):
|
||||
class StdioMCPServer(BaseModel):
|
||||
command: str
|
||||
args: list[str] = Field(default_factory=list)
|
||||
env: dict[str, Any] = Field(default_factory=dict)
|
||||
cwd: str | None = None
|
||||
transport: Literal["stdio"] = "stdio"
|
||||
|
||||
def to_transport(self) -> StdioTransport:
|
||||
from fastmcp.client.transports import StdioTransport
|
||||
|
|
@ -51,8 +52,8 @@ class LocalMCPServer(BaseModel):
|
|||
|
||||
class RemoteMCPServer(BaseModel):
|
||||
url: str
|
||||
transport: Literal["streamable-http", "sse", "http"] | None = None
|
||||
headers: dict[str, str] = Field(default_factory=dict)
|
||||
transport: Literal["streamable-http", "sse", "http"] | None = None
|
||||
|
||||
def to_transport(self) -> StreamableHttpTransport | SSETransport:
|
||||
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
||||
|
|
@ -69,7 +70,7 @@ class RemoteMCPServer(BaseModel):
|
|||
|
||||
|
||||
class MCPConfig(BaseModel):
|
||||
mcpServers: dict[str, LocalMCPServer | RemoteMCPServer]
|
||||
mcpServers: dict[str, StdioMCPServer | RemoteMCPServer]
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, config: dict[str, Any]) -> MCPConfig:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue