Skip to content

Commit 5c1c291

Browse files
committed
feat(docs[_ext]): register tool sections as Sphinx labels for site-wide {ref} links
Add doctree-read hook to fastmcp_autodoc that registers all section nodes with StandardDomain (labels + anonlabels), mirroring the pattern used by sphinx.ext.autosectionlabel. note_explicit_target() only registers with docutils — the Sphinx {ref} role looks up targets in StandardDomain, which was never populated. Now any section ID created by the fastmcp-tool directive (or any other heading) is a valid cross-reference target from any page. docs/index.md "What you can do" tool names are now inline code links using MyST [`tool_name`](#label) syntax — renders as <a><code> elements that resolve through Sphinx's label system across the dirhtml builder.
1 parent 59856c6 commit 5c1c291

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

docs/_ext/fastmcp_autodoc.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from sphinx.util.docutils import SphinxDirective
3535

3636
if t.TYPE_CHECKING:
37+
from sphinx.domains.std import StandardDomain
3738
from sphinx.util.typing import ExtensionMetadata
3839

3940
# ---------------------------------------------------------------------------
@@ -705,9 +706,30 @@ def run(self) -> list[nodes.Node]:
705706
# ---------------------------------------------------------------------------
706707

707708

709+
def _register_tool_labels(app: Sphinx, doctree: nodes.document) -> None:
710+
"""Register tool sections with StandardDomain for site-wide {ref} links.
711+
712+
``note_explicit_target()`` only registers with docutils, not with Sphinx's
713+
StandardDomain. This hook mirrors the pattern used by
714+
``sphinx.ext.autosectionlabel`` so that ``{ref}`list-sessions``` works
715+
from any page.
716+
"""
717+
domain = t.cast("StandardDomain", app.env.get_domain("std"))
718+
docname = app.env.docname
719+
for section in doctree.findall(nodes.section):
720+
if not section["ids"]:
721+
continue
722+
section_id = section["ids"][0]
723+
if section.children and isinstance(section[0], nodes.title):
724+
title = section[0].astext()
725+
domain.anonlabels[section_id] = (docname, section_id)
726+
domain.labels[section_id] = (docname, section_id, title)
727+
728+
708729
def setup(app: Sphinx) -> ExtensionMetadata:
709730
"""Register the fastmcp_autodoc extension."""
710731
app.connect("builder-inited", _collect_tools)
732+
app.connect("doctree-read", _register_tool_labels)
711733
app.add_directive("fastmcp-tool", FastMCPToolDirective)
712734
app.add_directive("fastmcp-tool-input", FastMCPToolInputDirective)
713735
app.add_directive("fastmcp-toolsummary", FastMCPToolSummaryDirective)

docs/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@ Config blocks for Claude Desktop, Claude Code, Cursor, and others.
5353

5454
Read tmux state without changing anything.
5555

56-
`list_sessions` · `capture_pane` · `get_pane_info` · `search_panes` · `wait_for_text`
56+
[`list_sessions`](#list-sessions) · [`capture_pane`](#capture-pane) · [`get_pane_info`](#get-pane-info) · [`search_panes`](#search-panes) · [`wait_for_text`](#wait-for-text)
5757

5858
### Act (mutating)
5959

6060
Create or modify tmux objects.
6161

62-
`create_session` · `send_keys` · `create_window` · `split_window` · `resize_pane` · `set_option`
62+
[`create_session`](#create-session) · [`send_keys`](#send-keys) · [`create_window`](#create-window) · [`split_window`](#split-window) · [`resize_pane`](#resize-pane) · [`set_option`](#set-option)
6363

6464
### Destroy (destructive)
6565

6666
Tear down tmux objects. Not reversible.
6767

68-
`kill_session` · `kill_window` · `kill_pane` · `kill_server`
68+
[`kill_session`](#kill-session) · [`kill_window`](#kill-window) · [`kill_pane`](#kill-pane) · [`kill_server`](#kill-server)
6969

7070
[Browse all tools →](tools/index)
7171

0 commit comments

Comments
 (0)