@@ -881,11 +881,14 @@ def _resolve_tool_refs(
881881 doctree : nodes .document ,
882882 fromdocname : str ,
883883) -> None :
884- """Resolve ``{tool}``, ``{toolref}``, and ``{toolicon}`` placeholders.
884+ """Resolve ``{tool}``, ``{toolref}``, and ``{toolicon* }`` placeholders.
885885
886886 ``{tool}`` renders as ``code`` + safety badge (text + icon).
887887 ``{toolref}`` renders as ``code`` only (no badge).
888- ``{toolicon}`` renders as ``code`` + icon-only badge (square, no text).
888+ ``{toolicon}``/``{tooliconl}`` — icon-only badge left of code.
889+ ``{tooliconr}`` — icon-only badge right of code.
890+ ``{tooliconil}`` — icon-only badge inside code, left of text.
891+ ``{tooliconir}`` — icon-only badge inside code, right of text.
889892
890893 Runs at ``doctree-resolved`` — after all labels are registered and
891894 standard ``{ref}`` resolution is done.
@@ -897,7 +900,7 @@ def _resolve_tool_refs(
897900 for node in list (doctree .findall (_tool_ref_placeholder )):
898901 target = node .get ("reftarget" , "" )
899902 show_badge = node .get ("show_badge" , True )
900- icon_only = node .get ("icon_only " , False )
903+ icon_pos = node .get ("icon_pos " , "" )
901904 label_info = domain .labels .get (target )
902905 if label_info is None :
903906 node .replace_self (nodes .literal ("" , target .replace ("-" , "_" )))
@@ -916,24 +919,44 @@ def _resolve_tool_refs(
916919 newnode ["classes" ].append ("reference" )
917920 newnode ["classes" ].append ("internal" )
918921
919- if icon_only :
920- # Icon badge goes BEFORE the <code> element, outside it
922+ if icon_pos :
921923 tool_info = tool_data .get (tool_name )
924+ badge = None
922925 if tool_info :
923926 badge = _safety_badge (tool_info .safety )
924927 badge ["classes" ].append ("icon-only" )
928+ if icon_pos .startswith ("inline" ):
929+ badge ["classes" ].append ("icon-only-inline" )
925930 badge .children .clear ()
926931 badge += nodes .Text ("" )
927- newnode += badge
928- newnode += nodes .literal ("" , tool_name )
932+
933+ if icon_pos == "left" :
934+ if badge :
935+ newnode += badge
936+ newnode += nodes .literal ("" , tool_name )
937+ elif icon_pos == "right" :
938+ newnode += nodes .literal ("" , tool_name )
939+ if badge :
940+ newnode += badge
941+ elif icon_pos == "inline-left" :
942+ code_node = nodes .literal ("" , "" )
943+ if badge :
944+ code_node += badge
945+ code_node += nodes .Text (tool_name )
946+ newnode += code_node
947+ elif icon_pos == "inline-right" :
948+ code_node = nodes .literal ("" , "" )
949+ code_node += nodes .Text (tool_name )
950+ if badge :
951+ code_node += badge
952+ newnode += code_node
929953 else :
930954 newnode += nodes .literal ("" , tool_name )
931-
932- if not icon_only and show_badge :
933- tool_info = tool_data .get (tool_name )
934- if tool_info :
935- newnode += nodes .Text (" " )
936- newnode += _safety_badge (tool_info .safety )
955+ if show_badge :
956+ tool_info = tool_data .get (tool_name )
957+ if tool_info :
958+ newnode += nodes .Text (" " )
959+ newnode += _safety_badge (tool_info .safety )
937960
938961 node .replace_self (newnode )
939962
@@ -975,25 +998,34 @@ def _toolref_role(
975998 return [node ], []
976999
9771000
978- def _toolicon_role (
979- name : str ,
980- rawtext : str ,
981- text : str ,
982- lineno : int ,
983- inliner : object ,
984- options : dict [str , object ] | None = None ,
985- content : list [str ] | None = None ,
986- ) -> tuple [list [nodes .Node ], list [nodes .system_message ]]:
987- """Inline role ``:toolicon:`capture-pane``` → code + square icon badge.
1001+ def _make_toolicon_role (
1002+ icon_pos : str ,
1003+ ) -> t .Callable [..., tuple [list [nodes .Node ], list [nodes .system_message ]]]:
1004+ """Create an icon-only tool reference role for a given position."""
1005+
1006+ def role_fn (
1007+ name : str ,
1008+ rawtext : str ,
1009+ text : str ,
1010+ lineno : int ,
1011+ inliner : object ,
1012+ options : dict [str , object ] | None = None ,
1013+ content : list [str ] | None = None ,
1014+ ) -> tuple [list [nodes .Node ], list [nodes .system_message ]]:
1015+ target = text .strip ().replace ("_" , "-" )
1016+ node = _tool_ref_placeholder (
1017+ rawtext , reftarget = target , show_badge = False , icon_pos = icon_pos ,
1018+ )
1019+ return [node ], []
9881020
989- Like ``{tool}`` but the badge is icon-only (no text label), square,
990- and compact — designed for inline prose where the full badge is too wide.
991- """
992- target = text . strip (). replace ( "_" , "- " )
993- node = _tool_ref_placeholder (
994- rawtext , reftarget = target , show_badge = False , icon_only = True ,
995- )
996- return [ node ], []
1021+ return role_fn
1022+
1023+
1024+ _toolicon_role = _make_toolicon_role ( "left " )
1025+ _tooliconl_role = _make_toolicon_role ( "left" )
1026+ _tooliconr_role = _make_toolicon_role ( "right" )
1027+ _tooliconil_role = _make_toolicon_role ( "inline-left" )
1028+ _tooliconir_role = _make_toolicon_role ( "inline-right" )
9971029
9981030
9991031def _badge_role (
@@ -1022,6 +1054,10 @@ def setup(app: Sphinx) -> ExtensionMetadata:
10221054 app .add_role ("tool" , _tool_role )
10231055 app .add_role ("toolref" , _toolref_role )
10241056 app .add_role ("toolicon" , _toolicon_role )
1057+ app .add_role ("tooliconl" , _tooliconl_role )
1058+ app .add_role ("tooliconr" , _tooliconr_role )
1059+ app .add_role ("tooliconil" , _tooliconil_role )
1060+ app .add_role ("tooliconir" , _tooliconir_role )
10251061 app .add_role ("badge" , _badge_role )
10261062 app .add_directive ("fastmcp-tool" , FastMCPToolDirective )
10271063 app .add_directive ("fastmcp-tool-input" , FastMCPToolInputDirective )
0 commit comments