Added RFC 6570 complaint form style query expansion as optional param…
Add RFC 6570 support for optional parameters as form-style query expansions
Motivation and Context
Adds optional parameter support with RFC 6570 compliance as form-style query expansions. This change addresses issue #378, .
Breaking Changes
None
Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update
Checklist
- [x] I have read the MCP Documentation
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
@ihrpr thanks for the feedback
during the fn call with params result = self.fn(**params)
can i use call_fn_with_arg_validation https://github.com/modelcontextprotocol/python-sdk/blob/f2f4dbdcbd30fd00ced777fd1f59d00624362c97/src/mcp/server/fastmcp/utilities/func_metadata.py#L45 for type validation and conversion and should i fallback to default values for optional parameters if validation fails for any one of them
@ihrpr the pydantic validate_call already takes care of the compatible type conversion based on annontated parameters for query parameters and raises validation error for incompatible types added a decorator which check if the validation error is raised by optional parameters and ignores them so they fall back to default values to keep it compliant
added relevant tests covering these cases
for the cases in query parsing the empty values and url encoded parameters are handled by parse_qs added relevant tests covering these
Don't we want something more FastAPI-ish? Like Annotated[type[T], Query()]? 🤔
Don't we want something more FastAPI-ish? Like
Annotated[type[T], Query()]? 🤔
Something like this?
from typing import Annotated
@mcp.resource("search://{category}/{id}")
def search(
category: Annotated[str, Path()],
id: Annotated[int, Path()],
format: Annotated[str, Query()] = "json",
limit: Annotated[int, Query()] = 10
):
...
Note: there's also https://github.com/modelcontextprotocol/python-sdk/pull/1439 which is touching the same area so we'd need some coordination cc: @beaterblank
Don't we want something more FastAPI-ish? Like
Annotated[type[T], Query()]? 🤔Something like this?
from typing import Annotated @mcp.resource("search://{category}/{id}") def search( category: Annotated[str, Path()], id: Annotated[int, Path()], format: Annotated[str, Query()] = "json", limit: Annotated[int, Query()] = 10 ): ...Note: there's also #1439 which is touching the same area so we'd need some coordination cc: @beaterblank
For category and id, you wouldn't need it, but you could... Also, I think Query/Path would have the default parameter, so you don't need to add default in the parameters.
Don't we want something more FastAPI-ish? Like
Annotated[type[T], Query()]? 🤔Something like this?
from typing import Annotated @mcp.resource("search://{category}/{id}") def search( category: Annotated[str, Path()], id: Annotated[int, Path()], format: Annotated[str, Query()] = "json", limit: Annotated[int, Query()] = 10 ): ...Note: there's also #1439 which is touching the same area so we'd need some coordination cc: @beaterblank
For
categoryandid, you wouldn't need it, but you could... Also, I thinkQuery/Pathwould have thedefaultparameter, so you don't need to add default in the parameters.
OK thanks, so more like this?
from typing import Annotated
@mcp.resource("search://{category}/{id}")
def search(
category: str,
id: int,
format: Annotated[str, Query(default="json")],
limit: Annotated[int, Query(default=10)],
):
...
Don't we want something more FastAPI-ish? Like
Annotated[type[T], Query()]? 🤔Something like this?
from typing import Annotated @mcp.resource("search://{category}/{id}") def search( category: Annotated[str, Path()], id: Annotated[int, Path()], format: Annotated[str, Query()] = "json", limit: Annotated[int, Query()] = 10 ): ...Note: there's also #1439, which is touching the same area, so we'd need some coordination cc: @beaterblank
Yes, using Annotated sounds like a good approach, but we should ideally support both styles.
~Also, a small nitpick, the classes should be named PathParameter and QueryParameter, since I spent a few minutes confused :)~ or maybe its best stick with how fast api does it.
I also noticed an issue in my PR: I’m currently inferring types only from the URI, but this should be done based on the function signature value's types instead. can reuse the code from this PR for that.
Additionally, since both features modify the same function in different ways, we should refactor the logic into smaller, more focused functions. That will make the codebase cleaner and easier to extend in the future.
At this point, I could either merge this PR into mine and resolve the conflicts properly, or do it the other way around.
merging this into #1439
Can close this PR merged into #1439 .
Consolidating discussion on https://github.com/modelcontextprotocol/python-sdk/pull/1439