feat: propagate docstring parameter descriptions to JSON Schema (#226)
Closed
ZS
2mo ago+242+242+012345678901234567890123456789-1-1-0123456789
Summary
Parse Python docstrings (Google, NumPy, and Sphinx styles) using griffe to extract parameter descriptions, then include them in the generated JSON Schema via Field(description=...).
This closes #226.
Motivation
When an MCP tool function has a docstring with an Args/Parameters section, the descriptions are currently ignored during JSON Schema generation. This means LLMs only see parameter names and types, missing the rich context that docstrings provide.
Implementation
Changes
pyproject.toml: Addedgriffe>=1.0.0to runtime dependenciesfunc_metadata.py:- Added
_parse_docstring_params(func)— parses docstrings via griffe and returns{param_name: description} - Added
_has_field_description(annotation)— checks if anAnnotated[...]field already has aField(description=...)to avoid overriding explicit descriptions - Modified
func_metadata()loop to addField(description=...)to each parameter's field kwargs when a docstring description is available
- Added
test_func_metadata.py: Added 8 new tests covering all three docstring styles, edge cases, and precedence rules
Design Decisions
- Multi-style support: Tries Google, NumPy, and Sphinx parsers; returns the result from whichever yields the most parameter descriptions
- Explicit > inferred:
Annotated[str, Field(description="...")]takes precedence over docstring descriptions - Graceful degradation: No docstring, no Args section, or unrecognized format → no descriptions added (no errors)
- Warning suppression: griffe emits noisy "No type or annotation for parameter" warnings since we're parsing descriptions not types — these are silenced via the griffe logger
Before / After
def search(query: str, limit: int = 10) -> list[dict]:
"""Search the database.
Args:
query: The search query string.
limit: Maximum number of results to return.
"""
Before — schema has no descriptions:
{"query": {"title": "Query", "type": "string"}, "limit": {"default": 10, "title": "Limit", "type": "integer"}}
After — schema includes descriptions:
{"query": {"title": "Query", "type": "string", "description": "The search query string."}, "limit": {"default": 10, "title": "Limit", "type": "integer", "description": "Maximum number of results to return."}}
Testing
All 63 existing tests pass unchanged. 8 new tests added:
test_google_style_docstring_descriptionstest_numpy_style_docstring_descriptionstest_sphinx_style_docstring_descriptionstest_no_docstringtest_docstring_no_args_sectiontest_docstring_partial_argstest_docstring_with_skip_namestest_field_description_preserved_over_docstring
🤖 Generated with Claude Code