Skip to content

feat: propagate docstring parameter descriptions to JSON Schema (#226) - #3350

Closed
zsxh1990 wants to merge 1 commit into
modelcontextprotocol:mainfrom
zsxh1990:fix/docstring-jsonschema
Closed

feat: propagate docstring parameter descriptions to JSON Schema (#226)#3350
zsxh1990 wants to merge 1 commit into
modelcontextprotocol:mainfrom
zsxh1990:fix/docstring-jsonschema

Conversation

@zsxh1990

Copy link
Copy Markdown

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

  1. pyproject.toml: Added griffe>=1.0.0 to runtime dependencies
  2. func_metadata.py:
    • Added _parse_docstring_params(func) — parses docstrings via griffe and returns {param_name: description}
    • Added _has_field_description(annotation) — checks if an Annotated[...] field already has a Field(description=...) to avoid overriding explicit descriptions
    • Modified func_metadata() loop to add Field(description=...) to each parameter's field kwargs when a docstring description is available
  3. 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_descriptions
  • test_numpy_style_docstring_descriptions
  • test_sphinx_style_docstring_descriptions
  • test_no_docstring
  • test_docstring_no_args_section
  • test_docstring_partial_args
  • test_docstring_with_skip_names
  • test_field_description_preserved_over_docstring

🤖 Generated with Claude Code

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 addresses issue modelcontextprotocol#226: when a tool function has a docstring with an
Args/Parameters section, the descriptions are now automatically added to
the JSON Schema output, giving LLMs richer context about each parameter.

Key design decisions:
- Tries all three docstring styles (Google, NumPy, Sphinx) and picks the
  one that yields the most parameter descriptions
- Annotated Field descriptions take precedence over docstring descriptions
- Gracefully degrades: no docstring or unrecognized format → no descriptions
- Suppresses griffe's noisy type-annotation warnings (we only need descriptions)

Signed-off-by: zsxh1990 <zsxh1990@gmail.com>
Signed-off-by: zsxh1990 <445655361@qq.com>
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the contribution. This repository only keeps pull requests open when they're linked to an issue that a maintainer has assigned to the author — CONTRIBUTING.md explains why and how we work. This PR has been closed for now because you aren't currently assigned to #226.

If a maintainer would like this change as a PR from you, they'll assign you to #226 and this PR will reopen automatically — there's nothing more you need to do. (If you opened the issue, this PR already shows up on its timeline.)

There's no need to open a new PR — this one will be reopened. While it's closed, please push any updates as new commits rather than force-pushing, since GitHub can't reopen a PR whose branch has been rewritten.

Maintainers: reopening this PR, removing the missing-issue-link label, or adding bypass-issue-check bypasses the check.

@github-actions github-actions Bot closed this Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improving how function docstring gets converted to tool's jsonschema for FastMCP

1 participant