mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
Fix typos (#4498)
* Fix typos * Document Cachable* -> Cacheable* rename as v4 breaking change Adds the response-cache model rename to the change register, per maintainer decision to skip compatibility aliases in favor of clear documentation. * Skip invalid Before import in doc test; use inline codespell ignore The Cachable* -> Cacheable* breaking-change entry showed the old, now-invalid import for contrast, which the doc-example test picked up as a real import and flagged as a regression. Comment out the deliberately-broken "Before" line (matching the McpError entry just above it) so only the working "After" import is exercised. Also swap the blanket codespell ignore-words-list entry for a narrower inline `codespell:ignore` directive on the one line that needs it, so codespell keeps flagging "cachable" everywhere else. --------- Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
This commit is contained in:
parent
a57f1c8b20
commit
81fada4922
10 changed files with 61 additions and 43 deletions
|
|
@ -635,7 +635,7 @@ FastMCP 3.2 is the Apps release: your tools can now return interactive UIs — c
|
|||
* Add tag to docs by [@jlowin](https://github.com/jlowin) in [#3382](https://github.com/PrefectHQ/fastmcp/pull/3382)
|
||||
* Add settings and environment variables reference by [@jlowin](https://github.com/jlowin) in [#3384](https://github.com/PrefectHQ/fastmcp/pull/3384)
|
||||
* Add contributing guidelines and update issue/PR templates by [@jlowin](https://github.com/jlowin) in [#3485](https://github.com/PrefectHQ/fastmcp/pull/3485)
|
||||
* [Documentation] Move stateless_http transport kwarg to http_app as FastMCP constructo… by [@mhallo](https://github.com/mhallo) in [#3510](https://github.com/PrefectHQ/fastmcp/pull/3510)
|
||||
* [Documentation] Move stateless_http transport kwarg to http_app as FastMCP constructor… by [@mhallo](https://github.com/mhallo) in [#3510](https://github.com/PrefectHQ/fastmcp/pull/3510)
|
||||
* Update security policy by [@jlowin](https://github.com/jlowin) in [#3521](https://github.com/PrefectHQ/fastmcp/pull/3521)
|
||||
* Add release instructions to CLAUDE.md by [@jlowin](https://github.com/jlowin) in [#3583](https://github.com/PrefectHQ/fastmcp/pull/3583)
|
||||
* fix(docs): correct misleading stateless_http header by [@jlowin](https://github.com/jlowin) in [#3622](https://github.com/PrefectHQ/fastmcp/pull/3622)
|
||||
|
|
@ -1967,7 +1967,7 @@ Thank you to our new contributors and everyone who tested preview builds. Your f
|
|||
* Add configurable redirect URI validation for OAuth providers by [@jlowin](https://github.com/jlowin) in [#1582](https://github.com/PrefectHQ/fastmcp/pull/1582)
|
||||
* Remove invalid-argument-type ignore and fix type errors by [@jlowin](https://github.com/jlowin) in [#1588](https://github.com/PrefectHQ/fastmcp/pull/1588)
|
||||
* Remove generate-schema from public CLI by [@jlowin](https://github.com/jlowin) in [#1591](https://github.com/PrefectHQ/fastmcp/pull/1591)
|
||||
* Skip flaky windows test / mulit-client garbage collection by [@jlowin](https://github.com/jlowin) in [#1592](https://github.com/PrefectHQ/fastmcp/pull/1592)
|
||||
* Skip flaky windows test / multi-client garbage collection by [@jlowin](https://github.com/jlowin) in [#1592](https://github.com/PrefectHQ/fastmcp/pull/1592)
|
||||
* Add setting to disable logging configuration by [@isra17](https://github.com/isra17) in [#1575](https://github.com/PrefectHQ/fastmcp/pull/1575)
|
||||
* Improve debug logging for nested Servers / Clients by [@strawgate](https://github.com/strawgate) in [#1604](https://github.com/PrefectHQ/fastmcp/pull/1604)
|
||||
* Add GitHub pull request template by [@strawgate](https://github.com/strawgate) in [#1581](https://github.com/PrefectHQ/fastmcp/pull/1581)
|
||||
|
|
|
|||
|
|
@ -164,6 +164,22 @@ Resource-not-found responses from the core `resources/read` handler previously u
|
|||
|
||||
*Verify:* `fastmcp_slim/fastmcp/exceptions.py` (`to_mcp_error`); `fastmcp_slim/fastmcp/server/mixins/mcp_operations.py`; `tests/test_exceptions.py`.
|
||||
|
||||
### `Cachable*` response-cache models renamed to `Cacheable*` — Breaking (rename) <!-- codespell:ignore -->
|
||||
|
||||
The response-caching middleware's Pydantic wrapper models — used to serialize cached tool, resource, and prompt results for `ResponseCachingMiddleware` — carried a spelling typo. `CachableToolResult`, `CachableResourceContent`, `CachableResourceResult`, `CachableMessage`, and `CachablePromptResult` are renamed to `CacheableToolResult`, `CacheableResourceContent`, `CacheableResourceResult`, `CacheableMessage`, and `CacheablePromptResult`. None of these classes are re-exported from `fastmcp` or any package `__init__.py`, so the realistic blast radius is limited to code that imported the old names directly from `fastmcp.server.middleware.caching`:
|
||||
|
||||
```python
|
||||
# Before (now raises ImportError):
|
||||
# from fastmcp.server.middleware.caching import CachableToolResult
|
||||
|
||||
# After
|
||||
from fastmcp.server.middleware.caching import CacheableToolResult
|
||||
```
|
||||
|
||||
There is deliberately no compatibility alias for the old spelling.
|
||||
|
||||
*Verify:* `fastmcp_slim/fastmcp/server/middleware/caching.py`.
|
||||
|
||||
## Client
|
||||
|
||||
The `fastmcp.Client` public API is preserved exactly. The client stays a wrapper around `mcp.ClientSession` in legacy/handshake mode; the first-class `mcp.client.Client` is deliberately not adopted in this PR.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
|
@ -31,7 +31,7 @@ The middleware automatically maps MCP methods to Permit.io resources and actions
|
|||
> **Note:**
|
||||
> Don't forget to assign the relevant role (e.g., Admin, User) to the user authenticating to your MCP server (such as the user in the JWT) in the Permit.io Directory. Without the correct role assignment, users will not have access to the resources and actions you've configured in your policies.
|
||||
>
|
||||
> 
|
||||
> 
|
||||
>
|
||||
> *Example: In Permit.io Directory, both 'client' and 'admin' users are assigned the 'Admin' role, granting them the permissions defined in your policy mapping.*
|
||||
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ Thank you to our new contributors and everyone who tested preview builds. Your f
|
|||
* Add configurable redirect URI validation for OAuth providers by [@jlowin](https://github.com/jlowin) in [#1582](https://github.com/PrefectHQ/fastmcp/pull/1582)
|
||||
* Remove invalid-argument-type ignore and fix type errors by [@jlowin](https://github.com/jlowin) in [#1588](https://github.com/PrefectHQ/fastmcp/pull/1588)
|
||||
* Remove generate-schema from public CLI by [@jlowin](https://github.com/jlowin) in [#1591](https://github.com/PrefectHQ/fastmcp/pull/1591)
|
||||
* Skip flaky windows test / mulit-client garbage collection by [@jlowin](https://github.com/jlowin) in [#1592](https://github.com/PrefectHQ/fastmcp/pull/1592)
|
||||
* Skip flaky windows test / multi-client garbage collection by [@jlowin](https://github.com/jlowin) in [#1592](https://github.com/PrefectHQ/fastmcp/pull/1592)
|
||||
* Add setting to disable logging configuration by [@isra17](https://github.com/isra17) in [#1575](https://github.com/PrefectHQ/fastmcp/pull/1575)
|
||||
* Improve debug logging for nested Servers / Clients by [@strawgate](https://github.com/strawgate) in [#1604](https://github.com/PrefectHQ/fastmcp/pull/1604)
|
||||
* Add GitHub pull request template by [@strawgate](https://github.com/strawgate) in [#1581](https://github.com/PrefectHQ/fastmcp/pull/1581)
|
||||
|
|
@ -2339,4 +2339,4 @@ This release is highlighted by the ability to handle complex JSON objects as MCP
|
|||
The very first release of FastMCP! 🎉
|
||||
|
||||
**Full Changelog**: [Initial commits](https://github.com/PrefectHQ/fastmcp/commits/v0.1.0)
|
||||
</Update>
|
||||
</Update>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
|
@ -31,7 +31,7 @@ The middleware automatically maps MCP methods to Permit.io resources and actions
|
|||
> **Note:**
|
||||
> Don't forget to assign the relevant role (e.g., Admin, User) to the user authenticating to your MCP server (such as the user in the JWT) in the Permit.io Directory. Without the correct role assignment, users will not have access to the resources and actions you've configured in your policies.
|
||||
>
|
||||
> 
|
||||
> 
|
||||
>
|
||||
> *Example: In Permit.io Directory, both 'client' and 'admin' users are assigned the 'Admin' role, granting them the permissions defined in your policy mapping.*
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ def _to_base_model(value: FastMCPBaseModel, model_type: type[BaseModelT]) -> Bas
|
|||
return model_type.model_validate(field_values)
|
||||
|
||||
|
||||
class CachableResourceContent(FastMCPBaseModel):
|
||||
class CacheableResourceContent(FastMCPBaseModel):
|
||||
"""A wrapper for ResourceContent that can be cached."""
|
||||
|
||||
content: str | bytes
|
||||
|
|
@ -57,10 +57,10 @@ class CachableResourceContent(FastMCPBaseModel):
|
|||
meta: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class CachableResourceResult(FastMCPBaseModel):
|
||||
class CacheableResourceResult(FastMCPBaseModel):
|
||||
"""A wrapper for ResourceResult that can be cached."""
|
||||
|
||||
contents: list[CachableResourceContent]
|
||||
contents: list[CacheableResourceContent]
|
||||
meta: dict[str, Any] | None = None
|
||||
|
||||
def get_size(self) -> int:
|
||||
|
|
@ -70,7 +70,7 @@ class CachableResourceResult(FastMCPBaseModel):
|
|||
def wrap(cls, value: ResourceResult) -> Self:
|
||||
return cls(
|
||||
contents=[
|
||||
CachableResourceContent(
|
||||
CacheableResourceContent(
|
||||
content=item.content, mime_type=item.mime_type, meta=item.meta
|
||||
)
|
||||
for item in value.contents
|
||||
|
|
@ -90,7 +90,7 @@ class CachableResourceResult(FastMCPBaseModel):
|
|||
)
|
||||
|
||||
|
||||
class CachableToolResult(FastMCPBaseModel):
|
||||
class CacheableToolResult(FastMCPBaseModel):
|
||||
content: list[mcp_types.ContentBlock]
|
||||
structured_content: dict[str, Any] | None
|
||||
meta: dict[str, Any] | None
|
||||
|
|
@ -114,7 +114,7 @@ class CachableToolResult(FastMCPBaseModel):
|
|||
)
|
||||
|
||||
|
||||
class CachableMessage(FastMCPBaseModel):
|
||||
class CacheableMessage(FastMCPBaseModel):
|
||||
"""A wrapper for Message that can be cached."""
|
||||
|
||||
role: str
|
||||
|
|
@ -126,10 +126,10 @@ class CachableMessage(FastMCPBaseModel):
|
|||
)
|
||||
|
||||
|
||||
class CachablePromptResult(FastMCPBaseModel):
|
||||
class CacheablePromptResult(FastMCPBaseModel):
|
||||
"""A wrapper for PromptResult that can be cached."""
|
||||
|
||||
messages: list[CachableMessage]
|
||||
messages: list[CacheableMessage]
|
||||
description: str | None = None
|
||||
meta: dict[str, Any] | None = None
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ class CachablePromptResult(FastMCPBaseModel):
|
|||
def wrap(cls, value: PromptResult) -> Self:
|
||||
return cls(
|
||||
messages=[
|
||||
CachableMessage(role=m.role, content=m.content) for m in value.messages
|
||||
CacheableMessage(role=m.role, content=m.content) for m in value.messages
|
||||
],
|
||||
description=value.description,
|
||||
meta=value.meta,
|
||||
|
|
@ -287,23 +287,25 @@ class ResponseCachingMiddleware(Middleware):
|
|||
default_collection="prompts/list",
|
||||
)
|
||||
|
||||
self._read_resource_cache: PydanticAdapter[CachableResourceResult] = (
|
||||
self._read_resource_cache: PydanticAdapter[CacheableResourceResult] = (
|
||||
PydanticAdapter(
|
||||
key_value=self._stats,
|
||||
pydantic_model=CachableResourceResult,
|
||||
pydantic_model=CacheableResourceResult,
|
||||
default_collection="resources/read",
|
||||
)
|
||||
)
|
||||
|
||||
self._get_prompt_cache: PydanticAdapter[CachablePromptResult] = PydanticAdapter(
|
||||
key_value=self._stats,
|
||||
pydantic_model=CachablePromptResult,
|
||||
default_collection="prompts/get",
|
||||
self._get_prompt_cache: PydanticAdapter[CacheablePromptResult] = (
|
||||
PydanticAdapter(
|
||||
key_value=self._stats,
|
||||
pydantic_model=CacheablePromptResult,
|
||||
default_collection="prompts/get",
|
||||
)
|
||||
)
|
||||
|
||||
self._call_tool_cache: PydanticAdapter[CachableToolResult] = PydanticAdapter(
|
||||
self._call_tool_cache: PydanticAdapter[CacheableToolResult] = PydanticAdapter(
|
||||
key_value=self._stats,
|
||||
pydantic_model=CachableToolResult,
|
||||
pydantic_model=CacheableToolResult,
|
||||
default_collection="tools/call",
|
||||
)
|
||||
|
||||
|
|
@ -326,15 +328,15 @@ class ResponseCachingMiddleware(Middleware):
|
|||
tools: Sequence[Tool] = await call_next(context)
|
||||
|
||||
# Turn any subclass of Tool into a Tool
|
||||
cachable_tools = [_to_base_model(tool, Tool) for tool in tools]
|
||||
cacheable_tools = [_to_base_model(tool, Tool) for tool in tools]
|
||||
|
||||
await self._list_tools_cache.put(
|
||||
key=cache_key,
|
||||
value=cachable_tools,
|
||||
value=cacheable_tools,
|
||||
ttl=self._list_tools_settings.get("ttl", FIVE_MINUTES_IN_SECONDS),
|
||||
)
|
||||
|
||||
return cachable_tools
|
||||
return cacheable_tools
|
||||
|
||||
@override
|
||||
async def on_list_resources(
|
||||
|
|
@ -355,17 +357,17 @@ class ResponseCachingMiddleware(Middleware):
|
|||
resources: Sequence[Resource] = await call_next(context)
|
||||
|
||||
# Turn any subclass of Resource into a Resource
|
||||
cachable_resources = [
|
||||
cacheable_resources = [
|
||||
_to_base_model(resource, Resource) for resource in resources
|
||||
]
|
||||
|
||||
await self._list_resources_cache.put(
|
||||
key=cache_key,
|
||||
value=cachable_resources,
|
||||
value=cacheable_resources,
|
||||
ttl=self._list_resources_settings.get("ttl", FIVE_MINUTES_IN_SECONDS),
|
||||
)
|
||||
|
||||
return cachable_resources
|
||||
return cacheable_resources
|
||||
|
||||
@override
|
||||
async def on_list_prompts(
|
||||
|
|
@ -386,15 +388,15 @@ class ResponseCachingMiddleware(Middleware):
|
|||
prompts: Sequence[Prompt] = await call_next(context)
|
||||
|
||||
# Turn any subclass of Prompt into a Prompt
|
||||
cachable_prompts = [_to_base_model(prompt, Prompt) for prompt in prompts]
|
||||
cacheable_prompts = [_to_base_model(prompt, Prompt) for prompt in prompts]
|
||||
|
||||
await self._list_prompts_cache.put(
|
||||
key=cache_key,
|
||||
value=cachable_prompts,
|
||||
value=cacheable_prompts,
|
||||
ttl=self._list_prompts_settings.get("ttl", FIVE_MINUTES_IN_SECONDS),
|
||||
)
|
||||
|
||||
return cachable_prompts
|
||||
return cacheable_prompts
|
||||
|
||||
@override
|
||||
async def on_call_tool(
|
||||
|
|
@ -419,17 +421,17 @@ class ResponseCachingMiddleware(Middleware):
|
|||
return cached_value.unwrap()
|
||||
|
||||
tool_result: ToolResult = await call_next(context)
|
||||
cachable_tool_result: CachableToolResult = CachableToolResult.wrap(
|
||||
cacheable_tool_result: CacheableToolResult = CacheableToolResult.wrap(
|
||||
value=tool_result
|
||||
)
|
||||
|
||||
await self._call_tool_cache.put(
|
||||
key=cache_key,
|
||||
value=cachable_tool_result,
|
||||
value=cacheable_tool_result,
|
||||
ttl=self._call_tool_settings.get("ttl", ONE_HOUR_IN_SECONDS),
|
||||
)
|
||||
|
||||
return cachable_tool_result.unwrap()
|
||||
return cacheable_tool_result.unwrap()
|
||||
|
||||
@override
|
||||
async def on_read_resource(
|
||||
|
|
@ -445,13 +447,13 @@ class ResponseCachingMiddleware(Middleware):
|
|||
cache_key: str = _make_read_resource_cache_key(
|
||||
msg=context.message, auth_key=_get_auth_partition_key()
|
||||
)
|
||||
cached_value: CachableResourceResult | None
|
||||
cached_value: CacheableResourceResult | None
|
||||
|
||||
if cached_value := await self._read_resource_cache.get(key=cache_key):
|
||||
return cached_value.unwrap()
|
||||
|
||||
value: ResourceResult = await call_next(context)
|
||||
cached_value = CachableResourceResult.wrap(value)
|
||||
cached_value = CacheableResourceResult.wrap(value)
|
||||
|
||||
await self._read_resource_cache.put(
|
||||
key=cache_key,
|
||||
|
|
@ -480,7 +482,7 @@ class ResponseCachingMiddleware(Middleware):
|
|||
return cached_value.unwrap()
|
||||
|
||||
value: PromptResult = await call_next(context)
|
||||
cached_value = CachablePromptResult.wrap(value)
|
||||
cached_value = CacheablePromptResult.wrap(value)
|
||||
|
||||
await self._get_prompt_cache.put(
|
||||
key=cache_key,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from fastmcp.prompts.function_prompt import FunctionPrompt
|
|||
from fastmcp.resources.base import Resource
|
||||
from fastmcp.server.middleware.caching import (
|
||||
ANONYMOUS_AUTH_KEY,
|
||||
CachableToolResult,
|
||||
CacheableToolResult,
|
||||
CallToolSettings,
|
||||
ResponseCachingMiddleware,
|
||||
ResponseCachingStatistics,
|
||||
|
|
@ -571,7 +571,7 @@ class TestResponseCachingMiddlewareIntegration:
|
|||
)
|
||||
|
||||
|
||||
class TestCachableToolResult:
|
||||
class TestCacheableToolResult:
|
||||
def test_wrap_and_unwrap(self):
|
||||
tool_result = ToolResult(
|
||||
"unstructured content",
|
||||
|
|
@ -579,7 +579,7 @@ class TestCachableToolResult:
|
|||
meta={"meta": "data"},
|
||||
)
|
||||
|
||||
cached_tool_result = CachableToolResult.wrap(tool_result).unwrap()
|
||||
cached_tool_result = CacheableToolResult.wrap(tool_result).unwrap()
|
||||
|
||||
assert cached_tool_result.content == tool_result.content
|
||||
assert cached_tool_result.structured_content == tool_result.structured_content
|
||||
|
|
@ -588,7 +588,7 @@ class TestCachableToolResult:
|
|||
def test_wrap_and_unwrap_preserves_is_error(self):
|
||||
tool_result = ToolResult("boom", is_error=True)
|
||||
|
||||
cached_tool_result = CachableToolResult.wrap(tool_result).unwrap()
|
||||
cached_tool_result = CacheableToolResult.wrap(tool_result).unwrap()
|
||||
|
||||
assert cached_tool_result.is_error is True
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class TestHeadlessOAuthCallbackHandler:
|
|||
|
||||
The OAuth callback handler in HeadlessOAuth parses the redirect Location
|
||||
header. parse_qs without keep_blank_values=True silently drops keys whose
|
||||
value is empty (e.g. `?state=`), which mis-models real OAuth callbacks
|
||||
value is empty (e.g. `?state=`), which misrepresents real OAuth callbacks
|
||||
where an empty `state` is distinct from a missing one.
|
||||
"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue