Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6bc24d7
fastmcp-autodoc(feat[prompts,resources]): four new card directives
tony Apr 17, 2026
0ea93cb
feat(sphinx-autodoc-fastmcp): prompt/resource anchors, IDs, badges
tony Apr 17, 2026
aa1ceb3
feat(sphinx-autodoc-fastmcp): sibling adoption for prompts/resources
tony Apr 17, 2026
eb42115
fix(sphinx-ux-autodoc-layout): hide card-shell permalink (hover to show)
tony Apr 17, 2026
75cb028
mypy(fix[fastmcp]): add ignore_missing_imports override for fastmcp.*
tony Apr 17, 2026
1df2fff
sphinx-autodoc-fastmcp(fix[collector]): drop stale type: ignore comment
tony Apr 17, 2026
a85d419
sphinx-autodoc-fastmcp(fix[collector]): replace _first_paragraph with…
tony Apr 17, 2026
a47dbc1
sphinx-autodoc-fastmcp(fix[collector]): add doctests to pure helpers
tony Apr 17, 2026
bc8183c
fix(_numpy_docstring): strip Raises roles, filter Notes invisibles
tony Apr 19, 2026
8ca05a2
sphinx-autodoc-fastmcp(fix[badges,labels]): redesign + incremental la…
tony Apr 19, 2026
83ba28a
sphinx-gp-theme(feat[spa]): dispatch gp-sphinx:navigated CustomEvent …
tony Apr 19, 2026
ebbde09
sphinx-autodoc-fastmcp(fix[directives]): typed std-domain + cleanup
tony Apr 19, 2026
8fb342c
sphinx-autodoc-typehints-gp(fix[numpy]): tilde + brackets + scope rubric
tony Apr 19, 2026
47a0600
sphinx-autodoc-fastmcp(fix[collector]): always invoke register-all hook
tony Apr 19, 2026
16aa15e
sphinx-autodoc-fastmcp(fix[collector]): simplify _strip_schema_note t…
tony Apr 19, 2026
0e902ec
sphinx-autodoc-fastmcp(fix[collector]): narrow fastmcp ImportError
tony Apr 19, 2026
6cd5118
docs(CHANGES): bug-fix bullets for PR #21 review fixups
tony Apr 19, 2026
8834a5f
test(fastmcp): integration scenario for the four new directives
tony Apr 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ $ uv add gp-sphinx --prerelease allow

### Features

- `sphinx-gp-theme`: dispatch `gp-sphinx:navigated` CustomEvent on
`document` after every SPA-nav DOM swap. Third-party widgets that bind to
swapped DOM can listen for this event to re-initialise after navigation
without a full page reload. Payload: `event.detail.url` is the new URL.
- `sphinx-autodoc-fastmcp`: autodoc MCP prompts and resources end-to-end.
Four new directives —
``{fastmcp-prompt}``, ``{fastmcp-prompt-input}``,
``{fastmcp-resource}``, ``{fastmcp-resource-template}`` — render the
same card-style layout used for tools, with dedicated type badges
(``prompt``, ``resource``, ``resource-template``) plus a MIME pill
next to resources. A new ``fastmcp_server_module`` config value
(``"pkg.server:mcp"``) points the collector at a live
``FastMCP`` instance; the collector reads
``local_provider._components`` directly and strips FastMCP's
auto-appended ``"Provide as a JSON string matching the following
schema: {...}"`` hint from prompt argument descriptions so docs
read naturally. If the configured server hasn't run its registration
hook yet, the collector calls ``register_all()`` / ``_register_all()``
to populate the components.
- `sphinx-autodoc-fastmcp`: new Sphinx extension for FastMCP tool docs (card-style
`desc` layouts, safety badges, MyST directives, cross-reference roles)
- `sphinx-ux-badges`: shared badge node (`BadgeNode`), builder API
Expand Down Expand Up @@ -65,6 +84,32 @@ $ uv add gp-sphinx --prerelease allow

### Bug fixes

- `sphinx-autodoc-fastmcp`: section labels for prompts and resources now
use the typed `env.domains.standard_domain` accessor and route through the
same direct-write pattern as `_transforms.register_tool_labels`. Resource
and resource-template card labels carry the actual component name (e.g.
``my_resource``) rather than a slugified round-trip, so ``{ref}`` lookups
resolve against the human-readable identifier (#21)
- `sphinx-autodoc-fastmcp`: the FastMCP register-all hook now fires whenever
the resolved server exposes ``local_provider``, regardless of whether
``_components`` is already populated. Servers that register some components
at module-import time (decorators) and others via an explicit
``register_all()`` previously dropped the deferred ones from autodoc (#21)
- `sphinx-autodoc-fastmcp`: narrow the ``except Exception`` around the optional
``fastmcp.*`` imports to ``except ImportError`` so unrelated runtime errors
during fastmcp import propagate instead of producing silently empty docs
(matches Sphinx's own ``sphinx.util.images`` pattern) (#21)
- `sphinx-autodoc-typehints-gp`: ``:exc:`~mod.Foo``` in ``Raises`` sections
now renders as ``Foo`` (last-component shortener), matching Sphinx's
``PyXRefRole.process_link`` convention (#21)
- `sphinx-autodoc-typehints-gp`: ``Raises`` type fields split on commas only
at bracket depth 0, keeping parameterised generics like ``Dict[str, X]``
intact (#21)
- `sphinx-autodoc-typehints-gp`: empty ``Notes`` sections drop their rubric
(intentional after ``.. todo::`` filtering); empty ``Examples``,
``References``, and other generic stub sections regain their rubric for
legitimate stub usage (scoped via a new ``suppress_empty`` flag on
``_fmt_generic``) (#21)
- `sphinx-gp-theme`: SPA nav now scrolls to the target anchor on
cross-page fragment links. `document.querySelector(hash)` mis-parses
any hash containing `.` (e.g. Python autodoc IDs like
Expand Down
67 changes: 65 additions & 2 deletions docs/packages/sphinx-autodoc-fastmcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ fastmcp_tool_modules = [
fastmcp_area_map = {
"fastmcp_tools": "api/tools",
}
fastmcp_collector_mode = "introspect"
fastmcp_collector_mode = "register"

# Optional: point at a live FastMCP server instance to autodoc its prompts,
# resources, and resource templates. Format is "module.path:attr_name".
# Both an instance and a zero-arg factory callable are accepted.
fastmcp_server_module = "my_project.server:mcp"
```

`sphinx_autodoc_fastmcp` automatically registers `sphinx_ux_badges`,
Expand All @@ -49,12 +54,29 @@ You do not need to add them separately to your `extensions` list.
|---------|---------|-------------|
| `fastmcp_tool_modules` | `[]` | Python module paths that expose tool callables |
| `fastmcp_area_map` | `{}` | Maps module stem to area path for ToC labels |
| `fastmcp_collector_mode` | `"introspect"` | `"introspect"` or `"register"` — how tools are discovered |
| `fastmcp_collector_mode` | `"register"` | `"register"` or `"introspect"` — how tools are discovered |
| `fastmcp_server_module` | `""` | `"module.path:attr"` — live FastMCP instance for prompt/resource autodoc |
| `fastmcp_model_module` | `None` | Module containing Pydantic model classes |
| `fastmcp_model_classes` | `set()` | Set of model class names to cross-reference |
| `fastmcp_section_badge_map` | `{}` | Maps section names to safety badge labels |
| `fastmcp_section_badge_pages` | `set()` | Pages where section safety badges are injected |

### `fastmcp_server_module`

Pointing the collector at a live FastMCP instance enables autodoc of
**prompts**, **resources**, and **resource templates** — see the four new
directives below. The collector accepts either:

* A live instance: `"my_project.server:mcp"` (where `mcp = FastMCP(...)`).
* A zero-argument factory: `"my_project.server:make_server"` returning a
`FastMCP` instance.

If the resolved object is not a `FastMCP` (no `local_provider` attribute),
collection is skipped and a warning is logged. The collector also invokes
the server's `register_all` / `_register_all` hook (if exported) to
ensure components registered lazily appear in the docs; FastMCP's default
`on_duplicate="error"` policy is suppressed for this call.

## Working usage examples

Render one tool card:
Expand Down Expand Up @@ -88,6 +110,47 @@ Use {tool}`list_sessions` for a linked badge, or {toolref}`delete_session`
for a plain inline reference.
````

### Prompts and resources

After setting `fastmcp_server_module`, four MyST directives become available
for documenting MCP prompts and resources:

````myst
```{fastmcp-prompt} my_prompt
```

```{fastmcp-prompt-input} my_prompt
```

```{fastmcp-resource} my_resource
```

```{fastmcp-resource-template} my_resource_template
```
````

Resources and resource templates accept either the friendly component name
(`my_resource`) or the literal URI (`mem://my_resource`). When two
distinct resources share a name, autodoc keeps the first registration and
emits a warning — disambiguate by URI.

### `:ref:` cross-reference IDs

Section IDs follow `fastmcp-{kind}-{name}` (canonical):

```text
{ref}`fastmcp-tool-list-sessions`
{ref}`fastmcp-prompt-greet`
{ref}`fastmcp-resource-status`
{ref}`fastmcp-resource-template-events-by-day`
```

Tool sections additionally register the bare slug as a back-compat alias
(e.g. `{ref}`list-sessions`` continues to resolve), preserving links
shipped before the kind-prefix introduction. Prompts, resources, and
resource templates use the canonical ID only — no bare alias is created
for them.

## Live demos

Use {tool}`list_sessions` for a linked badge, or {toolref}`delete_session`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@

from sphinx.application import Sphinx

from sphinx_autodoc_fastmcp._collector import collect_tools
from sphinx_autodoc_fastmcp._collector import (
collect_prompts_and_resources,
collect_tools,
)
from sphinx_autodoc_fastmcp._directives import (
FastMCPPromptDirective,
FastMCPPromptInputDirective,
FastMCPResourceDirective,
FastMCPResourceTemplateDirective,
FastMCPToolDirective,
FastMCPToolInputDirective,
FastMCPToolSummaryDirective,
Expand Down Expand Up @@ -77,6 +84,7 @@ def setup(app: Sphinx) -> dict[str, t.Any]:
app.add_config_value("fastmcp_section_badge_map", {}, "env")
app.add_config_value("fastmcp_section_badge_pages", (), "env")
app.add_config_value("fastmcp_collector_mode", "register", "env")
app.add_config_value("fastmcp_server_module", "", "env")

_static_dir = str(pathlib.Path(__file__).parent / "_static")

Expand All @@ -88,6 +96,7 @@ def _add_static_path(app: Sphinx) -> None:
app.add_css_file("css/sphinx_autodoc_fastmcp.css")

app.connect("builder-inited", collect_tools)
app.connect("builder-inited", collect_prompts_and_resources)
app.connect("doctree-read", register_tool_labels)
app.connect("doctree-read", collect_tool_section_content)
app.connect("doctree-resolved", add_section_badges)
Expand All @@ -105,6 +114,10 @@ def _add_static_path(app: Sphinx) -> None:
app.add_directive("fastmcp-tool", FastMCPToolDirective)
app.add_directive("fastmcp-tool-input", FastMCPToolInputDirective)
app.add_directive("fastmcp-tool-summary", FastMCPToolSummaryDirective)
app.add_directive("fastmcp-prompt", FastMCPPromptDirective)
app.add_directive("fastmcp-prompt-input", FastMCPPromptInputDirective)
app.add_directive("fastmcp-resource", FastMCPResourceDirective)
app.add_directive("fastmcp-resource-template", FastMCPResourceTemplateDirective)

return {
"version": _EXTENSION_VERSION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,94 @@ def build_toolbar(safety: str) -> nodes.inline:
True
"""
return _sab_build_toolbar(build_tool_badge_group(safety))


_TYPE_TOOLTIP_PROMPT = "MCP prompt recipe"
_TYPE_TOOLTIP_RESOURCE = "MCP resource (fixed URI)"
_TYPE_TOOLTIP_RESOURCE_TEMPLATE = "MCP resource template (parameterised URI)"


def build_prompt_badge_group(tags: t.Sequence[str] = ()) -> nodes.inline:
"""Badge group: ``prompt`` type + optional tag pills.

Examples
--------
>>> g = build_prompt_badge_group(())
>>> "gp-sphinx-badge-group" in g["classes"]
True
"""
specs = [
BadgeSpec(
"prompt",
tooltip=_TYPE_TOOLTIP_PROMPT,
classes=(
SAB.DENSE,
SAB.NO_UNDERLINE,
SAB.BADGE_TYPE,
_CSS.TYPE_PROMPT,
),
),
]
for tag in tags:
specs.append(
BadgeSpec(
tag,
tooltip=f"Tag: {tag}",
classes=(SAB.DENSE, SAB.NO_UNDERLINE, _CSS.BADGE_TAG),
),
)
return build_badge_group_from_specs(specs)


def build_resource_badge_group(
mime_type: str,
tags: t.Sequence[str] = (),
*,
kind: t.Literal["resource", "resource-template"] = "resource",
) -> nodes.inline:
"""Badge group for a resource or resource template.

Emits a type badge (``resource`` or ``resource-template``), an optional
MIME pill, and optional tag pills.

Examples
--------
>>> g = build_resource_badge_group("application/json")
>>> "gp-sphinx-badge-group" in g["classes"]
True
"""
if kind == "resource-template":
type_spec = BadgeSpec(
"resource-template",
tooltip=_TYPE_TOOLTIP_RESOURCE_TEMPLATE,
classes=(
SAB.DENSE,
SAB.NO_UNDERLINE,
SAB.BADGE_TYPE,
_CSS.TYPE_RESOURCE_TEMPLATE,
),
)
else:
type_spec = BadgeSpec(
"resource",
tooltip=_TYPE_TOOLTIP_RESOURCE,
classes=(SAB.DENSE, SAB.NO_UNDERLINE, SAB.BADGE_TYPE, _CSS.TYPE_RESOURCE),
)
specs = [type_spec]
if mime_type:
specs.append(
BadgeSpec(
mime_type,
tooltip=f"MIME type: {mime_type}",
classes=(SAB.DENSE, SAB.NO_UNDERLINE, _CSS.BADGE_MIME),
),
)
for tag in tags:
specs.append(
BadgeSpec(
tag,
tooltip=f"Tag: {tag}",
classes=(SAB.DENSE, SAB.NO_UNDERLINE, _CSS.BADGE_TAG),
),
)
return build_badge_group_from_specs(specs)
Loading