Skip to content

Commit 87efa1d

Browse files
committed
feat(docs[_ext]): Add {toolref} role — code-linked tool refs without badge
why: {ref} renders as plain text (Sphinx hardcodes nodes.inline in build_reference_node). {tool} renders as code+badge. Needed a middle ground: code-formatted linked tool names without badges for dense contexts like tables and inline prose. what: - Add {toolref} role: resolves same labels as {tool}, renders as <code> inside <a>, no safety badge - Reuse _tool_ref_placeholder with show_badge=False - Update prompting.md tables and heuristics to use {toolref}
1 parent a31a376 commit 87efa1d

2 files changed

Lines changed: 45 additions & 16 deletions

File tree

docs/_ext/fastmcp_autodoc.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -869,15 +869,22 @@ def _add_section_badges(
869869

870870

871871
class _tool_ref_placeholder(nodes.General, nodes.Inline, nodes.Element):
872-
"""Placeholder node for ``{tool}`` role, resolved at doctree-resolved."""
872+
"""Placeholder node for ``{tool}`` and ``{toolref}`` roles.
873+
874+
Resolved at ``doctree-resolved`` by ``_resolve_tool_refs``.
875+
The ``show_badge`` attribute controls whether the safety badge is appended.
876+
"""
873877

874878

875879
def _resolve_tool_refs(
876880
app: Sphinx,
877881
doctree: nodes.document,
878882
fromdocname: str,
879883
) -> None:
880-
"""Resolve ``{tool}`` placeholders into links with safety badges.
884+
"""Resolve ``{tool}`` and ``{toolref}`` placeholders into links.
885+
886+
``{tool}`` renders as ``code`` + safety badge.
887+
``{toolref}`` renders as ``code`` only (no badge).
881888
882889
Runs at ``doctree-resolved`` — after all labels are registered and
883890
standard ``{ref}`` resolution is done.
@@ -888,6 +895,7 @@ def _resolve_tool_refs(
888895

889896
for node in list(doctree.findall(_tool_ref_placeholder)):
890897
target = node.get("reftarget", "")
898+
show_badge = node.get("show_badge", True)
891899
label_info = domain.labels.get(target)
892900
if label_info is None:
893901
node.replace_self(nodes.literal("", target.replace("-", "_")))
@@ -908,10 +916,11 @@ def _resolve_tool_refs(
908916

909917
newnode += nodes.literal("", tool_name)
910918

911-
tool_info = tool_data.get(tool_name)
912-
if tool_info:
913-
newnode += nodes.Text(" ")
914-
newnode += _safety_badge(tool_info.safety)
919+
if show_badge:
920+
tool_info = tool_data.get(tool_name)
921+
if tool_info:
922+
newnode += nodes.Text(" ")
923+
newnode += _safety_badge(tool_info.safety)
915924

916925
node.replace_self(newnode)
917926

@@ -930,7 +939,26 @@ def _tool_role(
930939
Creates a placeholder node resolved later by ``_resolve_tool_refs``.
931940
"""
932941
target = text.strip().replace("_", "-")
933-
node = _tool_ref_placeholder(rawtext, reftarget=target)
942+
node = _tool_ref_placeholder(rawtext, reftarget=target, show_badge=True)
943+
return [node], []
944+
945+
946+
def _toolref_role(
947+
name: str,
948+
rawtext: str,
949+
text: str,
950+
lineno: int,
951+
inliner: object,
952+
options: dict[str, object] | None = None,
953+
content: list[str] | None = None,
954+
) -> tuple[list[nodes.Node], list[nodes.system_message]]:
955+
"""Inline role ``:toolref:`capture-pane``` → code-linked tool name, no badge.
956+
957+
Like ``{tool}`` but without the safety badge. Use in dense contexts
958+
(tables, inline prose) where badges would be too heavy.
959+
"""
960+
target = text.strip().replace("_", "-")
961+
node = _tool_ref_placeholder(rawtext, reftarget=target, show_badge=False)
934962
return [node], []
935963

936964

@@ -958,6 +986,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
958986
app.connect("doctree-resolved", _add_section_badges)
959987
app.connect("doctree-resolved", _resolve_tool_refs)
960988
app.add_role("tool", _tool_role)
989+
app.add_role("toolref", _toolref_role)
961990
app.add_role("badge", _badge_role)
962991
app.add_directive("fastmcp-tool", FastMCPToolDirective)
963992
app.add_directive("fastmcp-tool-input", FastMCPToolInputDirective)

docs/topics/prompting.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ These natural-language prompts reliably trigger the right tool sequences:
3434

3535
| Prompt | Agent interprets as |
3636
|--------|-------------------|
37-
| "Run `pytest` in my build pane and show results" | {tool}`send-keys` → {tool}`wait-for-text` → {tool}`capture-pane` |
38-
| "Start the dev server and wait until it's ready" | {tool}`send-keys` → {tool}`wait-for-text` (for "listening on") |
39-
| "Check if any pane has errors" | {tool}`search-panes` with pattern "error" |
40-
| "Set up a workspace with editor, server, and tests" | {tool}`create-session` → {tool}`split-window` (x2) → {tool}`set-pane-title` (x3) |
41-
| "What's running in my tmux sessions?" | {tool}`list-sessions` → {tool}`list-panes` → {tool}`capture-pane` |
42-
| "Kill the old workspace session" | {tool}`kill-session` (after confirming target) |
37+
| "Run `pytest` in my build pane and show results" | {toolref}`send-keys` → {toolref}`wait-for-text` → {toolref}`capture-pane` |
38+
| "Start the dev server and wait until it's ready" | {toolref}`send-keys` → {toolref}`wait-for-text` (for "listening on") |
39+
| "Check if any pane has errors" | {toolref}`search-panes` with pattern "error" |
40+
| "Set up a workspace with editor, server, and tests" | {toolref}`create-session` → {toolref}`split-window` (x2) → {toolref}`set-pane-title` (x3) |
41+
| "What's running in my tmux sessions?" | {toolref}`list-sessions` → {toolref}`list-panes` → {toolref}`capture-pane` |
42+
| "Kill the old workspace session" | {toolref}`kill-session` (after confirming target) |
4343

4444
## Anti-patterns to avoid
4545

@@ -85,8 +85,8 @@ that depend on it.
8585

8686
When an agent is unsure which tool to use, these rules help:
8787

88-
1. **Discovery first**: Call {tool}`list-sessions` or {tool}`list-panes` before acting on specific targets
88+
1. **Discovery first**: Call {toolref}`list-sessions` or {toolref}`list-panes` before acting on specific targets
8989
2. **Prefer IDs**: Once you have a `pane_id`, use it for all subsequent calls — it never changes during the pane's lifetime
90-
3. **Wait, don't poll**: Use {tool}`wait-for-text` instead of repeatedly calling {tool}`capture-pane` in a loop
91-
4. **Content vs. metadata**: If looking for text *in* a terminal, use {tool}`search-panes`. If looking for pane *properties* (name, PID, path), use {tool}`list-panes` or {tool}`get-pane-info`
90+
3. **Wait, don't poll**: Use {toolref}`wait-for-text` instead of repeatedly calling {toolref}`capture-pane` in a loop
91+
4. **Content vs. metadata**: If looking for text *in* a terminal, use {toolref}`search-panes`. If looking for pane *properties* (name, PID, path), use {toolref}`list-panes` or {toolref}`get-pane-info`
9292
5. **Destructive tools are opt-in**: Never kill sessions, windows, or panes unless the user explicitly asks

0 commit comments

Comments
 (0)