Introduce ResourceResult as canonical resource return type#2734
Conversation
Introduces ResourceResult as the canonical wrapper for resource reads, matching the ToolResult/PromptResult pattern. ResourceResult normalizes various return types (str, bytes, dict, list) and provides result-level meta alongside content-level meta via ResourceContent. Key changes: - Add ResourceResult with __init__ normalization and to_mcp_result() - Add convert_result() to Resource and ResourceTemplate classes - Update middleware signatures to use ResourceResult - Update FastMCPProvider to return full ResourceResult - Comprehensive unit tests for ResourceContent and ResourceResult - Integration tests for meta propagation to MCP clients
ResourceResult now only accepts str | bytes | list[ResourceContent]. Dict and list values are no longer auto-converted; use ResourceContent for serialization or json.dumps() for explicit JSON strings.
|
Warning Rate limit exceeded@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 20 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughThis PR introduces a new Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/servers/resources.mdx (2)
406-410: Inconsistent example with new typing requirements.This annotations example shows
def get_config() -> dict:which contradicts the new strict typing introduced in this PR. Resource functions must now returnstr | bytes | ResourceResult, notdict.🔎 Proposed fix
@mcp.resource( "data://config", annotations={ "readOnlyHint": True, "idempotentHint": True } ) -def get_config() -> dict: +def get_config() -> str: """Get application configuration.""" - return {"version": "1.0", "debug": False} + return json.dumps({"version": "1.0", "debug": False})
440-471: Resource template examples show inconsistent return types.The template examples show
-> dictreturn types, which is inconsistent with the new strict typing. While resource templates may have different handling, the documentation should be consistent with the enforced types to avoid confusion.🔎 Proposed fix for get_weather
@mcp.resource("weather://{city}/current") -def get_weather(city: str) -> dict: +def get_weather(city: str) -> str: """Provides weather information for a specific city.""" - return { + return json.dumps({ "city": city.capitalize(), "temperature": 22, "condition": "Sunny", "unit": "celsius" - } + })🔎 Proposed fix for get_repo_info
def get_repo_info(owner: str, repo: str) -> dict: +def get_repo_info(owner: str, repo: str) -> str: """Retrieves information about a GitHub repository.""" - return { + return json.dumps({ "owner": owner, "name": repo, "full_name": f"{owner}/{repo}", "stars": 120, "forks": 48 - } + })
🧹 Nitpick comments (4)
src/fastmcp/server/providers/fastmcp_provider.py (1)
22-23: Provider resource/template wrappers correctly propagate ResourceResultThe provider now consistently treats resource reads as
ResourceResult | mcp.types.CreateTaskResult:
FastMCPProviderResource._readandFastMCPProviderResourceResourceTemplate._readsimply delegate to the nested server’sread_resource, preservingResourceResult | CreateTaskResultwithout re-wrapping.FastMCPProviderResourceTemplate.read(Docket background path) correctly expects a concreteResourceResultand defensively raises if a nested server ever returnsCreateTaskResultthere, which would violate the contract.This aligns the provider layer with the new
ResourceResult-centric API and task routing model.Also applies to: 143-156, 272-275, 287-304, 308-325
src/fastmcp/server/server.py (1)
1783-1804: Update @resource decorator docs to match new ResourceResult behaviorThe
FastMCP.resourcedecorator docstring still says “other types will be converted to JSON”. WithResourceResultnow only acceptingstr | bytes | list[ResourceContent], unwrappeddict/listreturns will raiseTypeErrorviaResource.convert_result(ResourceResult(raw_value)).Consider updating this docstring to clarify that structured data must be:
- explicitly serialized (e.g.,
json.dumps()), or- wrapped via
ResourceContent/ResourceResult.This will help users align their resource return types with the stricter runtime contract.
src/fastmcp/resources/resource.py (1)
37-93: ResourceContent / ResourceResult pipeline cleanly enforces the new typing contract
ResourceContent.__init__now takesAnyand centralizes auto-serialization:
str/bytespass through with sensible default MIME types.- All other types (dict/list/BaseModel/etc.) are JSON-serialized via
pydantic_core.to_json(...).decode()and default toapplication/json.ResourceResultis now strictly constructed fromstr | bytes | list[ResourceContent], with an explicitTypeErrorif a list contains non‑ResourceContentitems or if the top-level type is unsupported. This is exactly where dict/bare-list inputs are rejected.Resource.convert_resultand_readensure that:
- subclasses can keep
read()simple (str | bytes | ResourceResult),- all public and MCP-facing paths see a normalized
ResourceResult.This matches the PR’s goal of pushing implicit dict/list handling into
ResourceContentand makingResourceResultstrongly typed.Also applies to: 122-212, 281-289, 293-307, 309-333
src/fastmcp/resources/template.py (1)
24-25: Templates now match ResourceResult-centered behavior of resources
ResourceTemplate.readandFunctionResourceTemplate.readboth advertisestr | bytes | ResourceResult, with normalization centralized inconvert_result, mirroring theResourceclass.- Both
_readimplementations (ResourceTemplateandFunctionResourceTemplate) now returnResourceResult | CreateTaskResultand consistently applyconvert_resultto whatever theirreadreturns (or to Docket task results).FunctionResourceTemplate.create_resourceuses an asyncresource_read_fnthat simply callsself.read(arguments=params)and returns itsstr | bytes | ResourceResultoutput, leaving normalization to downstream callers.This keeps templates and concrete resources aligned on the stricter
ResourceResultcontract.Also applies to: 158-162, 164-179, 180-211, 297-320, 325-329, 340-369
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (21)
tests/client/test_client.pyis excluded by none and included by nonetests/client/test_sse.pyis excluded by none and included by nonetests/client/test_streamable_http.pyis excluded by none and included by nonetests/deprecated/test_import_server.pyis excluded by none and included by nonetests/resources/test_file_resources.pyis excluded by none and included by nonetests/resources/test_function_resources.pyis excluded by none and included by nonetests/resources/test_resource_template.pyis excluded by none and included by nonetests/resources/test_resources.pyis excluded by none and included by nonetests/server/http/test_http_dependencies.pyis excluded by none and included by nonetests/server/middleware/test_caching.pyis excluded by none and included by nonetests/server/middleware/test_middleware.pyis excluded by none and included by nonetests/server/middleware/test_tool_injection.pyis excluded by none and included by nonetests/server/providers/test_fastmcp_provider.pyis excluded by none and included by nonetests/server/providers/test_local_provider_resources.pyis excluded by none and included by nonetests/server/providers/test_local_provider_tools.pyis excluded by none and included by nonetests/server/proxy/test_proxy_server.pyis excluded by none and included by nonetests/server/tasks/test_task_mount.pyis excluded by none and included by nonetests/server/tasks/test_task_return_types.pyis excluded by none and included by nonetests/server/test_file_server.pyis excluded by none and included by nonetests/server/test_mount.pyis excluded by none and included by nonev3-notes/resource-internal-types.mdis excluded by none and included by none
📒 Files selected for processing (15)
docs/servers/resources.mdxsrc/fastmcp/resources/__init__.pysrc/fastmcp/resources/resource.pysrc/fastmcp/resources/template.pysrc/fastmcp/resources/types.pysrc/fastmcp/server/context.pysrc/fastmcp/server/low_level.pysrc/fastmcp/server/middleware/caching.pysrc/fastmcp/server/middleware/middleware.pysrc/fastmcp/server/middleware/tool_injection.pysrc/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/server/providers/openapi/components.pysrc/fastmcp/server/providers/proxy.pysrc/fastmcp/server/server.pysrc/fastmcp/server/tasks/requests.py
🧰 Additional context used
📓 Path-based instructions (4)
src/fastmcp/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/fastmcp/**/*.py: Python ≥ 3.10 with full type annotations required
Prioritize readable, understandable code - clarity over cleverness. Avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code implementation
Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types
Never use bare except - be specific with exception types
Files:
src/fastmcp/server/middleware/middleware.pysrc/fastmcp/server/providers/openapi/components.pysrc/fastmcp/server/tasks/requests.pysrc/fastmcp/server/low_level.pysrc/fastmcp/server/middleware/tool_injection.pysrc/fastmcp/resources/types.pysrc/fastmcp/server/providers/proxy.pysrc/fastmcp/resources/resource.pysrc/fastmcp/server/middleware/caching.pysrc/fastmcp/server/context.pysrc/fastmcp/server/server.pysrc/fastmcp/server/providers/fastmcp_provider.pysrc/fastmcp/resources/template.pysrc/fastmcp/resources/__init__.py
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...
Files:
docs/servers/resources.mdx
docs/**/*.{md,mdx,json}
📄 CodeRabbit inference engine (AGENTS.md)
Documentation uses Mintlify framework. Files must be in docs.json to be included. Never modify docs/python-sdk/** (auto-generated)
Files:
docs/servers/resources.mdx
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
docs/**/*.{md,mdx}: Code examples in documentation must explain before showing code and make blocks fully runnable (include imports)
Documentation structure: Headers form navigation guide with logical H2/H3 hierarchy. Content should be user-focused with sections motivating features (why) before mechanics (how). Use prose over code comments for important information
Never use 'This isn't...' or 'not just...' constructions in writing - state what something IS directly. Avoid defensive writing patterns
Files:
docs/servers/resources.mdx
🧠 Learnings (4)
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required
Applied to files:
src/fastmcp/server/middleware/middleware.pysrc/fastmcp/server/providers/openapi/components.pysrc/fastmcp/server/low_level.pysrc/fastmcp/resources/types.pysrc/fastmcp/server/providers/proxy.pysrc/fastmcp/resources/resource.pysrc/fastmcp/server/middleware/caching.pysrc/fastmcp/server/context.pysrc/fastmcp/server/server.pysrc/fastmcp/server/providers/fastmcp_provider.py
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Never use bare except - be specific with exception types
Applied to files:
src/fastmcp/resources/types.py
📚 Learning: 2025-11-26T21:51:44.174Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Note that Resources and Resource Templates are distinct objects but both handled by ResourceManager, requiring coordinated updates when changes affect either object type
Applied to files:
src/fastmcp/resources/template.py
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types
Applied to files:
src/fastmcp/resources/__init__.py
🧬 Code graph analysis (14)
src/fastmcp/server/middleware/middleware.py (1)
src/fastmcp/resources/resource.py (2)
Resource(214-388)ResourceResult(122-211)
src/fastmcp/server/providers/openapi/components.py (3)
src/fastmcp/resources/resource.py (5)
Resource(214-388)ResourceContent(37-119)ResourceResult(122-211)read(281-291)read(453-467)src/fastmcp/resources/template.py (3)
ResourceTemplate(97-289)read(158-162)read(340-369)src/fastmcp/resources/types.py (5)
read(26-30)read(38-42)read(83-94)read(106-115)read(160-172)
src/fastmcp/server/tasks/requests.py (2)
src/fastmcp/resources/resource.py (3)
convert_result(293-307)to_mcp_result(198-211)Resource(214-388)src/fastmcp/resources/template.py (1)
convert_result(164-178)
src/fastmcp/server/low_level.py (1)
src/fastmcp/client/tasks.py (4)
result(202-207)result(336-393)result(427-458)result(497-551)
src/fastmcp/server/middleware/tool_injection.py (1)
src/fastmcp/resources/resource.py (1)
ResourceResult(122-211)
src/fastmcp/resources/types.py (1)
src/fastmcp/resources/resource.py (5)
Resource(214-388)ResourceContent(37-119)ResourceResult(122-211)read(281-291)read(453-467)
src/fastmcp/server/providers/proxy.py (1)
src/fastmcp/resources/resource.py (4)
ResourceContent(37-119)ResourceResult(122-211)read(281-291)read(453-467)
src/fastmcp/resources/resource.py (4)
src/fastmcp/resources/types.py (5)
read(26-30)read(38-42)read(83-94)read(106-115)read(160-172)src/fastmcp/server/providers/fastmcp_provider.py (1)
read(308-324)src/fastmcp/server/providers/openapi/components.py (1)
read(193-272)src/fastmcp/server/providers/proxy.py (1)
read(208-242)
src/fastmcp/server/middleware/caching.py (2)
src/fastmcp/resources/resource.py (3)
Resource(214-388)ResourceContent(37-119)ResourceResult(122-211)src/fastmcp/server/middleware/middleware.py (1)
CallNext(42-43)
src/fastmcp/server/context.py (2)
src/fastmcp/server/server.py (2)
resource(1783-1865)read_resource(1195-1287)src/fastmcp/resources/resource.py (1)
ResourceResult(122-211)
src/fastmcp/server/server.py (2)
src/fastmcp/resources/resource.py (3)
Resource(214-388)ResourceResult(122-211)to_mcp_result(198-211)src/fastmcp/utilities/components.py (1)
make_key(80-91)
src/fastmcp/server/providers/fastmcp_provider.py (3)
src/fastmcp/server/server.py (2)
resource(1783-1865)read_resource(1195-1287)src/fastmcp/resources/resource.py (5)
Resource(214-388)ResourceResult(122-211)_read(309-332)read(281-291)read(453-467)src/fastmcp/resources/types.py (5)
read(26-30)read(38-42)read(83-94)read(106-115)read(160-172)
src/fastmcp/resources/template.py (1)
src/fastmcp/resources/resource.py (6)
Resource(214-388)ResourceResult(122-211)read(281-291)read(453-467)convert_result(293-307)_read(309-332)
src/fastmcp/resources/__init__.py (1)
src/fastmcp/resources/resource.py (3)
Resource(214-388)ResourceContent(37-119)ResourceResult(122-211)
🪛 Ruff (0.14.10)
src/fastmcp/resources/resource.py
189-192: Avoid specifying long messages outside the exception class
(TRY003)
194-196: Avoid specifying long messages outside the exception class
(TRY003)
src/fastmcp/server/server.py
1249-1249: Consider moving this statement to an else block
(TRY300)
1273-1273: Consider moving this statement to an else block
(TRY300)
src/fastmcp/server/providers/fastmcp_provider.py
322-322: Prefer TypeError exception for invalid type
(TRY004)
322-322: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (21)
docs/servers/resources.mdx (2)
42-50: LGTM!Good update demonstrating the explicit
json.dumps()serialization pattern for returning structured data as JSON strings. This aligns with the stricter typing requirements and will help users understand the expected approach.
137-196: LGTM!The ResourceResult documentation is clear and comprehensive:
- Explains the three accepted types (
str | bytes | list[ResourceContent])- Shows both simple and advanced usage patterns
- Documents both
ResourceResultandResourceContentparameters with appropriate typesThe note about serializing structured data with
json.dumps()on lines 143-145 effectively communicates the rationale for this design choice.src/fastmcp/server/middleware/tool_injection.py (2)
12-12: LGTM!Import correctly updated to use
ResourceResultfromfastmcp.resources.resource.
98-103: LGTM!The return type annotation is correctly updated to
ResourceResult, and the implementation properly delegates tocontext.read_resource()which returnsResourceResult. The type flow is consistent.src/fastmcp/server/context.py (2)
41-41: LGTM!Import correctly updated to
ResourceResult.
313-327: LGTM!The
read_resourcemethod is correctly updated:
- Return type annotation changed to
ResourceResult- Docstring accurately reflects the new return type
- Implementation properly handles the
CreateTaskResultcheck before returningThis aligns with the broader PR goal of consistent
ResourceResulttyping throughout the codebase.src/fastmcp/server/tasks/requests.py (1)
325-335: LGTM!The resource result handling is now consistent with the
ResourceResultpattern:
- Uses
component.convert_result(raw_value)to normalize toResourceResult- Calls
to_mcp_result()with the appropriate URI (uri_templatefor templates,str(component.uri)for resources)- Attaches related-task metadata consistently with Tool and Prompt branches
The pattern mirrors the implementation in
src/fastmcp/resources/resource.pylines 197-210 for theto_mcp_resultconversion.src/fastmcp/resources/__init__.py (1)
1-22: LGTM!
ResourceResultis appropriately exported as part of the public API:
- It's a core type defining the module's purpose (canonical result type for resource reads)
- The
__all__list maintains alphabetical ordering for consistency- This aligns with the coding guideline to be intentional about re-exports—core types should be exported while specialized features can remain in submodules.
Based on learnings, this export is appropriate since
ResourceResultis a fundamental type for the resources module.src/fastmcp/server/middleware/middleware.py (2)
21-21: LGTM!Import correctly updated to include both
Resource(used inon_list_resources) andResourceResult(used inon_read_resource).
163-168: LGTM!The
on_read_resourcemiddleware hook signature is correctly updated:
CallNextreturn type changed fromSequence[ResourceContent]toResourceResult- Method return type changed to
ResourceResultThis provides a cleaner API for middleware implementers—they now work with a single
ResourceResultobject rather than a sequence, which better represents the semantic of reading a resource.src/fastmcp/server/providers/openapi/components.py (2)
14-19: LGTM!Import block correctly updated to include
ResourceContentandResourceResultfromfastmcp.resources. This follows the cleaner import pattern from the package's public API.
193-257: LGTM!The
read()method is correctly updated to returnResourceResult:
- Return type annotation is properly set to
ResourceResult- All three content branches wrap content appropriately:
- JSON:
json.dumps(result)for explicit serialization- Text/XML:
response.text(str)- Binary:
response.content(bytes)- Each branch correctly constructs
ResourceResult(contents=[ResourceContent(...)])- MIME type handling is preserved
This aligns with the patterns established in
src/fastmcp/resources/types.py.src/fastmcp/resources/types.py (5)
15-15: LGTM!Import correctly updated to include
ResourceResultalongsideResourceandResourceContent.
26-42: LGTM!
TextResource.read()andBinaryResource.read()are correctly updated:
- Return type annotations set to
ResourceResult- Content properly wrapped in
ResourceContentwithin a single-elementcontentslist- MIME types correctly passed through from the resource's
mime_typeattribute
82-94: LGTM!
FileResource.read()is correctly updated:
- Return type annotation set to
ResourceResult- Both binary and text content paths properly construct
ResourceResult- Exception handling uses
except Exception(not bareexcept) and wraps errors inResourceError, complying with coding guidelines
105-115: LGTM!
HttpResource.read()is correctly updated with consistentResourceResultconstruction pattern.
159-172: LGTM!
DirectoryResource.read()is correctly updated:
- Returns
ResourceResultwith JSON content representing the file listing- Exception handling follows the established pattern with
ResourceErrorwrappingsrc/fastmcp/server/low_level.py (1)
213-220: Updated read_resource decorator typing is consistent with new MCP result flowThe decorator’s type hints and handler now correctly treat the wrapped callable as returning
mcp.types.ReadResourceResult | mcp.types.CreateTaskResultand simply wrap that inServerResult. This matches_read_resource_mcpand keeps MCP conversion centralized at the server layer. No changes needed.Also applies to: 231-239, 241-245
src/fastmcp/server/middleware/caching.py (1)
21-22: ResourceResult-aware caching is symmetric and respects stricter typing
CachableResourceResult.wrap/unwrapcorrectly round-tripResourceResultby cloning eachResourceContentand reconstructing withcontents: list[ResourceContent], which is compatible with the newResourceResultconstructor._read_resource_cacheandon_read_resourcenow operate purely onResourceResult, and both cached and fresh values are returned viaunwrap(), preserving the “no-op clone” semantics of this middleware.Looks consistent with the new
ResourceResultcontract.Also applies to: 37-75, 227-233, 395-422
src/fastmcp/server/server.py (1)
69-70: Core read_resource / _read_resource_mcp flow is consistent with ResourceResult contract
FastMCP.read_resourcenow consistently returnsResourceResult | mcp.types.CreateTaskResultfrom both middleware and non-middleware paths, relying onResource._read/ResourceTemplate._readto normalize viaResource.convert_result/ResourceTemplate.convert_result._read_resource_mcpcorrectly:
- Pulls SEP‑1686 task metadata from
self._mcp_server.request_context.- Sets
_task_metadataand_docket_fn_key(Resource.make_key(str(uri)))so downstream resource/task routing sees the right key.- Calls
read_resource(str(uri))and converts aResourceResulttomcp.types.ReadResourceResultviato_mcp_result(uri), while passing throughCreateTaskResultunchanged.This cleanly separates internal
ResourceResultusage from the external MCPReadResourceResulttype.Also applies to: 1195-1214, 1219-1239, 1241-1250, 1263-1274, 1539-1554, 1557-1579
src/fastmcp/server/providers/proxy.py (1)
39-40: Proxy resources correctly normalize remote MCP contents into ResourceResult
ProxyResource.readnow returns aResourceResult, translating the firstTextResourceContents/BlobResourceContentsfrom the remote server into a singleResourceContentinsidecontents=[...].ProxyTemplate.create_resourceeagerly reads once from the backend, builds acached_content: ResourceResult, and injects it into the returnedProxyResource, so template-instantiated proxies have a stable snapshot.- All
ResourceResultconstructions usecontents=[ResourceContent(...)], which is compatible with the stricterResourceResultconstructor.No functional issues spotted with the new
ResourceResult-centric behavior.Also applies to: 157-170, 208-240, 314-347
Move task metadata extraction and MCP conversion from LowLevelServer.get_prompt() decorator into FastMCP._get_prompt_mcp(), matching the resource handler architecture from PR #2734.
Introduces
ResourceResultandResourceContentas the canonical types for resource returns, following up on the work in #2611.The MCP SDK requires resources to return a list of content items, which is awkward DX for most resources that return a single value.
ResourceResultwraps this pattern so users can return a single coherent object:ResourceContentauto-serializes dict/list/BaseModel to JSON, so structured data works when wrapped explicitly.ResourceResultacceptsstr | bytes | list[ResourceContent]with strict typing - no implicit conversion of raw dicts or lists.This moves validation from runtime (when a client reads a resource) to development time (when your type checker runs).