Compare commits

...

4 commits

Author SHA1 Message Date
claude[bot]
42d851964f Remove unnecessary None checks for ctx.fastmcp
The fastmcp property raises RuntimeError if None, so it can never return
None. Removed unnecessary checks from list_resources(), list_prompts(),
get_prompt(), and read_resource().

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: William Easton <strawgate@users.noreply.github.com>
2025-10-24 23:24:46 +00:00
William Easton
a83824c04d
Merge branch 'main' into claude/issue-2245-20251024-2225 2025-10-24 18:16:34 -05:00
Jeremiah Lowin
7de74c05cb Update docs 2025-10-24 19:16:25 -04:00
claude[bot]
63add18970 Add list_resources, list_prompts, and get_prompt methods to Context
- Add Context.list_resources() to list all available resources
- Add Context.list_prompts() to list all available prompts
- Add Context.get_prompt() to get a specific prompt with arguments
- Update ToolInjectionMiddleware to use new Context methods instead of creating temporary Client instances
- Remove unused Client and FastMCPTransport imports from tool_injection.py

This improves API consistency by allowing middleware/tools to use Context methods directly without needing to create temporary Client instances.

Fixes #2245

Co-authored-by: William Easton <strawgate@users.noreply.github.com>
2025-10-24 22:34:45 +00:00
4 changed files with 67 additions and 19 deletions

View file

@ -14,7 +14,8 @@ The `Context` object provides a clean interface to access MCP features within yo
- **Logging**: Send debug, info, warning, and error messages back to the client
- **Progress Reporting**: Update the client on the progress of long-running operations
- **Resource Access**: Read data from resources registered with the server
- **Resource Access**: List and read data from resources registered with the server
- **Prompt Access**: List and retrieve prompts registered with the server
- **LLM Sampling**: Request the client's LLM to generate text based on provided messages
- **User Elicitation**: Request structured input from users during tool execution
- **State Management**: Store and share data between middleware and the handler within a single request
@ -177,16 +178,40 @@ See [Progress Reporting](/servers/progress) for detailed patterns and examples.
### Resource Access
Read data from resources registered with your FastMCP server, allowing access to files, configuration, or dynamic content.
List and read data from resources registered with your FastMCP server, allowing access to files, configuration, or dynamic content.
```python
# List available resources
resources = await ctx.list_resources()
# Read a specific resource
content_list = await ctx.read_resource("resource://config")
content = content_list[0].content
```
**Method signature:**
**Method signatures:**
- **`ctx.list_resources() -> list[MCPResource]`**: <VersionBadge version="2.13.0" /> Returns list of all available resources
- **`ctx.read_resource(uri: str | AnyUrl) -> list[ReadResourceContents]`**: Returns a list of resource content parts
### Prompt Access
<VersionBadge version="2.13.0" />
List and retrieve prompts registered with your FastMCP server, allowing tools and middleware to discover and use available prompts programmatically.
```python
# List available prompts
prompts = await ctx.list_prompts()
# Get a specific prompt with arguments
result = await ctx.get_prompt("analyze_data", {"dataset": "users"})
messages = result.messages
```
**Method signatures:**
- **`ctx.list_prompts() -> list[MCPPrompt]`**: Returns list of all available prompts
- **`ctx.get_prompt(name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult`**: Get a specific prompt with optional arguments
### State Management
<VersionBadge version="2.11.0" />

View file

@ -22,6 +22,7 @@ from mcp.types import (
AudioContent,
ClientCapabilities,
CreateMessageResult,
GetPromptResult,
ImageContent,
IncludeContext,
ModelHint,
@ -32,6 +33,8 @@ from mcp.types import (
TextContent,
)
from mcp.types import CreateMessageRequestParams as SamplingParams
from mcp.types import Prompt as MCPPrompt
from mcp.types import Resource as MCPResource
from pydantic.networks import AnyUrl
from starlette.requests import Request
from typing_extensions import TypeVar
@ -215,6 +218,36 @@ class Context:
related_request_id=self.request_id,
)
async def list_resources(self) -> list[MCPResource]:
"""List all available resources from the server.
Returns:
List of Resource objects available on the server
"""
return await self.fastmcp._list_resources_mcp()
async def list_prompts(self) -> list[MCPPrompt]:
"""List all available prompts from the server.
Returns:
List of Prompt objects available on the server
"""
return await self.fastmcp._list_prompts_mcp()
async def get_prompt(
self, name: str, arguments: dict[str, Any] | None = None
) -> GetPromptResult:
"""Get a prompt by name with optional arguments.
Args:
name: The name of the prompt to get
arguments: Optional arguments to pass to the prompt
Returns:
The prompt result
"""
return await self.fastmcp._get_prompt_mcp(name, arguments)
async def read_resource(self, uri: str | AnyUrl) -> list[ReadResourceContents]:
"""Read a resource by URI.
@ -224,8 +257,6 @@ class Context:
Returns:
The resource content as either text or bytes
"""
if self.fastmcp is None:
raise ValueError("Context is not available outside of a request")
return await self.fastmcp._read_resource_mcp(uri)
async def log(

View file

@ -10,8 +10,6 @@ from mcp.types import Prompt
from pydantic import AnyUrl
from typing_extensions import override
from fastmcp.client.client import Client
from fastmcp.client.transports import FastMCPTransport
from fastmcp.server.context import Context
from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
from fastmcp.tools.tool import Tool, ToolResult
@ -55,9 +53,7 @@ class ToolInjectionMiddleware(Middleware):
async def list_prompts(context: Context) -> list[Prompt]:
"""List prompts available on the server."""
async with Client[FastMCPTransport](context.fastmcp) as client:
return await client.list_prompts()
return await context.list_prompts()
list_prompts_tool = Tool.from_function(
@ -73,9 +69,7 @@ async def get_prompt(
] = None,
) -> mcp.types.GetPromptResult:
"""Render a prompt available on the server."""
async with Client[FastMCPTransport](context.fastmcp) as client:
return await client.get_prompt(name=name, arguments=arguments)
return await context.get_prompt(name=name, arguments=arguments)
get_prompt_tool = Tool.from_function(
@ -93,9 +87,7 @@ class PromptToolMiddleware(ToolInjectionMiddleware):
async def list_resources(context: Context) -> list[mcp.types.Resource]:
"""List resources available on the server."""
async with Client[FastMCPTransport](context.fastmcp) as client:
return await client.list_resources()
return await context.list_resources()
list_resources_tool = Tool.from_function(

6
uv.lock generated
View file

@ -1,5 +1,5 @@
version = 1
revision = 2
revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.11'",
@ -566,7 +566,6 @@ dependencies = [
{ name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] },
{ name = "pydantic", extra = ["email"] },
{ name = "pyperclip" },
{ name = "pytest-asyncio" },
{ name = "python-dotenv" },
{ name = "rich" },
{ name = "websockets" },
@ -591,6 +590,7 @@ dev = [
{ name = "pyinstrument" },
{ name = "pyperclip" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "pytest-env" },
{ name = "pytest-flakefinder" },
@ -617,7 +617,6 @@ requires-dist = [
{ name = "py-key-value-aio", extras = ["disk", "keyring", "memory"], specifier = ">=0.2.6,<0.3.0" },
{ name = "pydantic", extras = ["email"], specifier = ">=2.11.7" },
{ name = "pyperclip", specifier = ">=1.9.0" },
{ name = "pytest-asyncio", specifier = ">=1.2.0" },
{ name = "python-dotenv", specifier = ">=1.1.0" },
{ name = "rich", specifier = ">=13.9.4" },
{ name = "websockets", specifier = ">=15.0.1" },
@ -637,6 +636,7 @@ dev = [
{ name = "pyinstrument", specifier = ">=5.0.2" },
{ name = "pyperclip", specifier = ">=1.9.0" },
{ name = "pytest", specifier = ">=8.3.3" },
{ name = "pytest-asyncio", specifier = ">=1.2.0" },
{ name = "pytest-cov", specifier = ">=6.1.1" },
{ name = "pytest-env", specifier = ">=1.1.5" },
{ name = "pytest-flakefinder" },