Refactor resource behavior and add meta support#2598
Conversation
Add Resource._read() private method that always returns ResourceContent, maintaining backwards compatibility for custom resources returning str/bytes from read(). Includes deprecation warning when str/bytes is returned.
WalkthroughA new ResourceContent model was added to encapsulate resource content, mime_type, and runtime meta. Resource.read implementations were standardized to return ResourceContent (with a new internal Resource._read helper and deprecation of plain str/bytes). The resources package now exports ResourceContent and ResourceManager. Server, proxy, middleware, caching, tooling, OpenAPI, task converters, and documentation were updated to produce, consume, cache, and serialize ResourceContent instead of raw text/bytes or ReadResourceContents. Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (1)src/**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📓 Common learnings🧬 Code graph analysis (1)src/fastmcp/server/proxy.py (3)
🪛 Ruff (0.14.8)src/fastmcp/server/proxy.py200-202: Avoid specifying long messages outside the exception class (TRY003) 392-392: Avoid specifying long messages outside the exception class (TRY003) 457-459: 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). (3)
🔇 Additional comments (3)
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: 4
🧹 Nitpick comments (5)
src/fastmcp/server/context.py (1)
38-38: Type update forContext.read_resource()is aligned with the new canonical content type.Optional: consider rewording the Line 287 comment to clarify why the context path returns a list (protocol shape) vs internal single-item canonicalization, if that distinction matters to readers.
Also applies to: 278-289
src/fastmcp/server/openapi/components.py (1)
190-272: Potential MIME type mismatch for text/XML responses.When the response
content-typeistext/*orapplication/xml, the code usesself.mime_type(which defaults to"application/json"per line 168) rather than the actual response content-type. This could result in an XML response being labeled as JSON.Consider using the actual content-type from the response header for non-JSON text responses:
if "application/json" in content_type: result = response.json() return ResourceContent( content=json.dumps(result), mime_type="application/json" ) elif any(ct in content_type for ct in ["text/", "application/xml"]): - return ResourceContent(content=response.text, mime_type=self.mime_type) + # Use actual content-type or fallback to configured mime_type + actual_mime = content_type.split(";")[0].strip() or self.mime_type + return ResourceContent(content=response.text, mime_type=actual_mime) else: return ResourceContent( content=response.content, mime_type=self.mime_type )src/fastmcp/server/server.py (1)
1706-1711: Mutatingcontent.mime_typein-place could cause side effects.The code directly mutates
content.mime_typeon theResourceContentobject returned fromread_resource(). If that same object is cached or reused elsewhere, this mutation could cause unexpected behavior.Consider creating a new
ResourceContentinstance instead of mutating:content = await self._resource_manager.read_resource(uri_str) # read_resource() always returns ResourceContent now # Use mime_type from ResourceContent if set, otherwise from resource if content.mime_type is None: - content.mime_type = resource.mime_type + content = ResourceContent( + content=content.content, + mime_type=resource.mime_type, + meta=content.meta, + ) return [content]Alternatively, use Pydantic's
model_copy:content = content.model_copy(update={"mime_type": resource.mime_type})src/fastmcp/server/tasks/converters.py (1)
151-156: Unusedserverparameter.The
serverparameter is not used in this function (also flagged by Ruff ARG001). If kept for API consistency withconvert_tool_resultandconvert_prompt_result, consider prefixing with underscore to indicate intentional non-use.async def convert_resource_result( - server: FastMCP, + server: FastMCP, # noqa: ARG001 - kept for API consistency raw_value: str | bytes | ResourceContent, uri: str, client_task_id: str, ) -> dict[str, Any]:Or alternatively:
async def convert_resource_result( - server: FastMCP, + _server: FastMCP, raw_value: str | bytes | ResourceContent, uri: str, client_task_id: str, ) -> dict[str, Any]:src/fastmcp/resources/resource.py (1)
229-248: Review stacklevel for deprecation warning.The
stacklevel=2points to the caller of_read()(likelyResourceManager), not to the user'sread()implementation that actually returns the deprecated type. Consider whether this provides useful information to the user trying to locate the deprecated code.A higher stacklevel (e.g., 3 or 4) might better point to user code, but this depends on the call chain depth. Alternatively, the warning message already includes the class name and URI which helps identify the source.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (6)
tests/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_manager.pyis excluded by none and included by nonetests/resources/test_resource_template.pyis excluded by none and included by nonetests/server/middleware/test_tool_injection.pyis excluded by none and included by nonetests/server/test_server.pyis excluded by none and included by none
📒 Files selected for processing (14)
docs/servers/resources.mdx(1 hunks)src/fastmcp/__init__.py(2 hunks)src/fastmcp/resources/__init__.py(2 hunks)src/fastmcp/resources/resource.py(4 hunks)src/fastmcp/resources/resource_manager.py(3 hunks)src/fastmcp/resources/types.py(5 hunks)src/fastmcp/server/context.py(2 hunks)src/fastmcp/server/middleware/caching.py(5 hunks)src/fastmcp/server/middleware/middleware.py(2 hunks)src/fastmcp/server/middleware/tool_injection.py(2 hunks)src/fastmcp/server/openapi/components.py(3 hunks)src/fastmcp/server/proxy.py(4 hunks)src/fastmcp/server/server.py(8 hunks)src/fastmcp/server/tasks/converters.py(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bareexcept- be specific with exception types
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code organization and style
Files:
src/fastmcp/server/context.pysrc/fastmcp/server/middleware/tool_injection.pysrc/fastmcp/server/server.pysrc/fastmcp/server/tasks/converters.pysrc/fastmcp/resources/resource_manager.pysrc/fastmcp/__init__.pysrc/fastmcp/server/middleware/middleware.pysrc/fastmcp/server/middleware/caching.pysrc/fastmcp/server/proxy.pysrc/fastmcp/resources/types.pysrc/fastmcp/server/openapi/components.pysrc/fastmcp/resources/resource.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
🧠 Learnings (1)
📚 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/__init__.py
🧬 Code graph analysis (11)
src/fastmcp/server/context.py (3)
src/fastmcp/resources/resource.py (1)
ResourceContent(35-129)src/fastmcp/server/middleware/tool_injection.py (1)
read_resource(98-103)src/fastmcp/server/proxy.py (1)
read_resource(188-213)
src/fastmcp/server/middleware/tool_injection.py (1)
src/fastmcp/resources/resource.py (1)
ResourceContent(35-129)
src/fastmcp/server/tasks/converters.py (1)
src/fastmcp/resources/resource.py (2)
ResourceContent(35-129)from_value(64-103)
src/fastmcp/resources/resource_manager.py (1)
src/fastmcp/resources/resource.py (3)
Resource(132-282)ResourceContent(35-129)_read(229-248)
src/fastmcp/__init__.py (1)
src/fastmcp/resources/resource.py (1)
ResourceContent(35-129)
src/fastmcp/server/middleware/middleware.py (1)
src/fastmcp/resources/resource.py (2)
Resource(132-282)ResourceContent(35-129)
src/fastmcp/server/proxy.py (3)
src/fastmcp/resources/resource.py (3)
ResourceContent(35-129)read(215-227)read(353-372)src/fastmcp/resources/resource_manager.py (1)
read_resource(289-347)src/fastmcp/resources/types.py (5)
read(26-28)read(36-38)read(79-88)read(100-105)read(150-160)
src/fastmcp/resources/types.py (2)
src/fastmcp/resources/resource.py (4)
Resource(132-282)ResourceContent(35-129)read(215-227)read(353-372)src/fastmcp/server/openapi/components.py (1)
read(190-290)
src/fastmcp/server/openapi/components.py (1)
src/fastmcp/resources/resource.py (4)
Resource(132-282)ResourceContent(35-129)read(215-227)read(353-372)
src/fastmcp/resources/resource.py (2)
src/fastmcp/server/dependencies.py (1)
without_injected_parameters(85-139)src/fastmcp/server/openapi/components.py (1)
read(190-290)
src/fastmcp/resources/__init__.py (2)
src/fastmcp/resources/resource.py (3)
FunctionResource(285-372)Resource(132-282)ResourceContent(35-129)src/fastmcp/resources/resource_manager.py (1)
ResourceManager(25-347)
🪛 LanguageTool
docs/servers/resources.mdx
[style] ~167-~167: To form a complete sentence, be sure to include a subject.
Context: ...ntent** - The actual resource content. Can be str(text content) orbytes` (bin...
(MISSING_IT_THERE)
🪛 Ruff (0.14.8)
src/fastmcp/server/tasks/converters.py
152-152: Unused function argument: server
(ARG001)
⏰ 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). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (24)
src/fastmcp/__init__.py (1)
22-23: Public API export forResourceContentlooks correct.Also applies to: 32-38
src/fastmcp/server/middleware/middleware.py (1)
21-22:on_read_resourcenow cleanly carriesResourceContentthrough the middleware pipeline.Also applies to: 163-168
src/fastmcp/server/middleware/tool_injection.py (1)
12-13: Import and typing changes are correct and consistent. TheResourceContentreturn type annotation is properly typed and follows Python 3.10+ requirements with full type annotations. Pydantic v2.11.x bytes fields (without explicitser_json_bytesconfiguration) serialize as UTF-8 strings by default, which is consistent with both the JSON schema generation and runtime serialization viapydantic_core.to_jsonable_python(). The code is readable, follows existing patterns, and requires no changes.src/fastmcp/server/openapi/components.py (1)
12-12: LGTM!The import correctly adds
ResourceContentalongside existingResourceandResourceTemplateimports from thefastmcp.resourcespackage.src/fastmcp/resources/resource_manager.py (2)
14-14: LGTM!Import correctly brings in
ResourceContentalongsideResourcefrom the resource module.
289-328: LGTM! Correct implementation of ResourceContent as canonical type.The changes properly implement the new pattern:
- Return type updated to
ResourceContent- Docstring accurately describes the new behavior
- Internal calls use
resource._read()which wraps legacystr/bytesreturns with deprecation warnings- Both concrete resources and template-created resources follow the same path
This ensures uniform handling throughout the read pipeline.
src/fastmcp/server/server.py (4)
65-65: LGTM!Import correctly adds
ResourceContentto the existing imports fromfastmcp.resources.resource.
738-757: LGTM!The conversion from
ResourceContentto MCP contents usingto_mcp_resource_contents(uri)is correctly applied in both the task metadata path and the normal response path.
1627-1638: LGTM!Return type correctly updated to
list[ResourceContent]and the explicit cast ensures type safety.
1646-1673: LGTM!Return types for
_read_resource_middlewareand_read_resourceare consistently updated tolist[ResourceContent].src/fastmcp/resources/__init__.py (1)
1-23: LGTM! Public API correctly updated.The changes properly expose
ResourceContentandResourceManageras part of the public API:
- Imports are consolidated and clean
__all__list maintains alphabetical ordering- This enables users to import
ResourceContentdirectly fromfastmcp.resourcessrc/fastmcp/resources/types.py (6)
15-15: LGTM!Import correctly adds
ResourceContentalongsideResourcefrom the resource module.
26-28: LGTM!
TextResource.read()correctly returnsResourceContentwrapping the text content with the configured mime_type.
36-38: LGTM!
BinaryResource.read()correctly returnsResourceContentwrapping the binary data with the configured mime_type.
79-86: LGTM!
FileResource.read()correctly handles both binary and text content paths, wrapping the result inResourceContentwith the appropriate mime_type.
100-105: LGTM!
HttpResource.read()correctly returnsResourceContentwrapping the HTTP response text with the configured mime_type.
150-158: LGTM!
DirectoryResource.read()correctly returnsResourceContentwrapping the JSON directory listing with the configured mime_type (defaults toapplication/json).src/fastmcp/server/tasks/converters.py (2)
14-15: LGTM on import changes.The
ResourceContentimport aligns with the PR's goal of making it the canonical internal type for resource reads.
175-203: LGTM on the ResourceContent normalization and content building logic.The implementation correctly:
- Normalizes input to
ResourceContentviafrom_value- Extracts and propagates content, MIME type, and metadata
- Applies correct default MIME types matching
ResourceContentbehavior- Preserves both content metadata (
_meta) and related-task metadata separatelysrc/fastmcp/resources/resource.py (3)
35-103: Well-designed ResourceContent model with comprehensive factory method.The
from_valueclass method correctly handles the conversion hierarchy:
ResourceContent→ pass-throughstr→ text content withtext/plaindefaultbytes→ binary content withapplication/octet-streamdefault- Other types → JSON serialization with
application/jsondefaultThis provides a clean upgrade path from legacy
str/bytesreturns.
105-129: LGTM on MCP conversion method.The
to_mcp_resource_contentsmethod correctly produces the appropriate MCP type based on content type, with proper base64 encoding for binary content and consistent MIME type defaults.
215-227: Good backward-compatible return type with clear deprecation guidance.The
str | bytes | ResourceContentunion return type allows existing subclasses to continue working while the docstring clearly communicates the deprecation path.src/fastmcp/server/middleware/caching.py (2)
37-65: LGTM on CachableReadResourceContents updates.The wrapper class correctly:
- Adds the
metafield to matchResourceContent- Preserves all fields through
wrap()/unwrap()round-trips- Uses correct type annotations (
Sequence[ResourceContent],list[ResourceContent])
388-416: LGTM on on_read_resource type updates.The method signature and internal handling are correctly updated to use
ResourceContentthroughout the caching flow. Thewrap()/unwrap()calls properly convert betweenResourceContentandCachableReadResourceContentsfor cache storage.
- Remove ResourceContent from root exports (import from fastmcp.resources) - Fix FunctionResource.read() return type to str | bytes | ResourceContent - Decode base64 blobs in proxy when receiving from remote servers - Preserve meta in ProxyResource cached content
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)
src/fastmcp/server/proxy.py (2)
198-214: Guard against empty result list before accessingresult[0].If the remote server returns an empty content list, accessing
result[0]will raiseIndexError. Add a guard to handle this edge case.async with client: result = await client.read_resource(uri) + if not result: + raise ResourceError(f"Remote server returned empty content for {uri}") if isinstance(result[0], TextResourceContents): return ResourceContent(This same pattern should be applied to the other locations that access
result[0]}:
ProxyResource.read()at line 387ProxyTemplate.create_resource()at line 448
447-476: Add empty result guard increate_resource.Same issue as other locations -
result[0]accessed without checking if result is non-empty.async with self._client: result = await self._client.read_resource(parameterized_uri) + if not result: + raise ResourceError(f"Remote server returned empty content for {parameterized_uri}") if isinstance(result[0], TextResourceContents):
♻️ Duplicate comments (3)
docs/servers/resources.mdx (2)
174-186: Add error handling to the binary content example.Per the MDX documentation guidelines, code examples should include realistic error handling. The file read operation should handle
FileNotFoundError.# Binary content with metadata @mcp.resource("images://logo") def get_logo() -> ResourceContent: """Returns a logo image with caching metadata.""" - with open("logo.png", "rb") as f: - image_data = f.read() + try: + with open("logo.png", "rb") as f: + image_data = f.read() + except FileNotFoundError: + raise ResourceError("Logo file not found") return ResourceContent( content=image_data, mime_type="image/png", meta={"cache-control": "max-age=3600"} )You'll also need to add the import at the top of the example:
from fastmcp.exceptions import ResourceErrorBased on learnings, MDX documentation should always include realistic error handling in code examples.
168-168: Fix sentence fragment for grammar consistency.The description is a sentence fragment. Adding "It" makes it a complete sentence.
-**`content`** - The actual resource content. Can be `str` (text content) or `bytes` (binary content). This is the data that will be returned to the client. +**`content`** - The actual resource content. It can be `str` (text content) or `bytes` (binary content). This is the data that will be returned to the client.src/fastmcp/resources/resource.py (1)
368-373: Use_read()for recursive Resource reads to ensure consistent return type.When
resultis aResource, callingresult.read()may returnstr | bytes | ResourceContent, but this value is returned directly without normalization. Usingresult._read()would ensure the recursive read always returnsResourceContent, maintaining internal consistency.# If user returned another Resource, read it recursively if isinstance(result, Resource): - return await result.read() + return await result._read()
🧹 Nitpick comments (1)
src/fastmcp/server/tasks/converters.py (1)
152-157: Remove or prefix unusedserverparameter.The
serverparameter is declared but never used in the function body. Either remove it if not needed for the API contract, or prefix with underscore to indicate it's intentionally unused.async def convert_resource_result( - server: FastMCP, + _server: FastMCP, raw_value: str | bytes | ResourceContent, uri: str, client_task_id: str, ) -> dict[str, Any]:Alternatively, if this parameter is kept for API consistency with other converters but truly unused, add a comment explaining why.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/server/test_server.pyis excluded by none and included by none
📒 Files selected for processing (4)
docs/servers/resources.mdx(1 hunks)src/fastmcp/resources/resource.py(4 hunks)src/fastmcp/server/proxy.py(8 hunks)src/fastmcp/server/tasks/converters.py(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bareexcept- be specific with exception types
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code organization and style
Files:
src/fastmcp/resources/resource.pysrc/fastmcp/server/tasks/converters.pysrc/fastmcp/server/proxy.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
🧠 Learnings (5)
📚 Learning: 2025-11-26T21:52:08.947Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-11-26T21:52:08.947Z
Learning: Applies to docs/**/*.mdx : Always include realistic error handling in code examples in MDX documentation
Applied to files:
docs/servers/resources.mdx
📚 Learning: 2025-11-26T21:52:08.947Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-11-26T21:52:08.947Z
Learning: Applies to docs/**/*.mdx : Show both success and error response examples with realistic data in MDX API documentation
Applied to files:
docs/servers/resources.mdx
📚 Learning: 2025-11-26T21:52:08.947Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-11-26T21:52:08.947Z
Learning: Applies to docs/**/*.mdx : Show proper error handling and edge case management in MDX code examples
Applied to files:
docs/servers/resources.mdx
📚 Learning: 2025-11-26T21:52:08.947Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-11-26T21:52:08.947Z
Learning: Applies to docs/**/*.mdx : Explain all HTTP status codes and error handling in MDX API documentation
Applied to files:
docs/servers/resources.mdx
📚 Learning: 2025-11-26T21:52:08.947Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-11-26T21:52:08.947Z
Learning: Applies to docs/**/*.mdx : Add appropriate warnings for destructive or security-sensitive actions in MDX documentation
Applied to files:
docs/servers/resources.mdx
🧬 Code graph analysis (3)
src/fastmcp/resources/resource.py (3)
src/fastmcp/server/dependencies.py (1)
without_injected_parameters(85-139)src/fastmcp/utilities/types.py (1)
get_fn_name(34-35)src/fastmcp/resources/types.py (5)
read(26-28)read(36-38)read(79-88)read(100-105)read(150-160)
src/fastmcp/server/tasks/converters.py (1)
src/fastmcp/resources/resource.py (2)
ResourceContent(35-130)from_value(65-104)
src/fastmcp/server/proxy.py (4)
src/fastmcp/resources/resource.py (3)
ResourceContent(35-130)read(216-228)read(354-373)src/fastmcp/resources/resource_manager.py (1)
read_resource(289-347)src/fastmcp/server/middleware/tool_injection.py (1)
read_resource(98-103)src/fastmcp/resources/types.py (5)
read(26-28)read(36-38)read(79-88)read(100-105)read(150-160)
🪛 LanguageTool
docs/servers/resources.mdx
[style] ~168-~168: To form a complete sentence, be sure to include a subject.
Context: ...ntent** - The actual resource content. Can be str(text content) orbytes` (bin...
(MISSING_IT_THERE)
🪛 Ruff (0.14.8)
src/fastmcp/server/tasks/converters.py
153-153: Unused function argument: server
(ARG001)
⏰ 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). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (6)
src/fastmcp/server/tasks/converters.py (1)
176-183: LGTM on ResourceContent normalization.The conversion logic correctly uses
ResourceContent.from_value()to normalize input and properly extractscontent,mime_type, andmetafields for downstream processing.src/fastmcp/resources/resource.py (3)
35-104: Well-designed ResourceContent class with comprehensive type handling.The
from_valuefactory method correctly handles all expected input types:
- Pass-through for existing
ResourceContent- String content with
text/plaindefault- Binary content with
application/octet-streamdefault- JSON serialization for other types using
pydantic_core.to_jsonThe fallback to
strinpydantic_core.to_jsonensures graceful handling of non-serializable types.
106-130: LGTM on MCP content conversion.The
to_mcp_resource_contentsmethod correctly:
- Handles string/URI type coercion
- Uses appropriate MCP types (
TextResourceContentsvsBlobResourceContents)- Applies proper base64 encoding for binary content
- Propagates metadata via
_meta
230-249: Clean deprecation path with backward compatibility.The
_read()internal API correctly wraps legacystr/bytesreturns inResourceContentwhile emitting a deprecation warning (when enabled). The stacklevel=2 ensures the warning points to the caller.src/fastmcp/server/proxy.py (2)
345-356: Good refactor:_cached_contentpreserves full ResourceContent including metadata.The change from
_value: str | bytes | Noneto_cached_content: ResourceContent | Noneproperly preserves MIME type and metadata through the caching layer, addressing the previous concern about lost runtime meta.
380-400: LGTM on ProxyResource.read() with proper base64 decoding.The implementation correctly:
- Returns cached content when available
- Decodes base64 blob content to bytes
- Preserves MIME type and metadata in ResourceContent
However, add an empty result guard as noted above:
async with self._client: result = await self._client.read_resource(self.uri) + if not result: + raise ResourceError(f"Remote server returned empty content for {self.uri}") if isinstance(result[0], TextResourceContents):
IMPORTANT: this was merged and reverted. See #2609 for new PR
Resources can now include metadata via
ResourceContent.meta, giving resource authors a way to pass structured information alongside content.Internally, this represents a significant refactor of resource result handling, with
ResourceContentnow the canonical type for all resource reads. Custom resources returningstrorbytesfromread()continue to work with a deprecation warning when enabled.