@@ -186,10 +186,10 @@ def _wrap_call_tool(
186186 async def handler (req : Any ) -> Any :
187187 name = req .params .name
188188 arguments = dict (req .params .arguments or {})
189- strip , model_ours = (
189+ strip , model_ours , input_schema = (
190190 await _standalone_ownership (data , high_level , name , req .params .meta )
191191 if strip_injected
192- else (set (), data .tool_model_parameter_injected .get (name ))
192+ else (set (), data .tool_model_parameter_injected .get (name ), None )
193193 )
194194 client_name , client_version = _client_info (server )
195195 protocol_version = _protocol_version (server )
@@ -212,6 +212,7 @@ async def handler(req: Any) -> Any:
212212 client_version = client_version ,
213213 protocol_version = protocol_version ,
214214 extra = {"session_id" : mcp_session_id , "ctx" : _request_context (server )},
215+ input_schema = input_schema ,
215216 )
216217
217218 if lifecycle .is_missing_capability and (
@@ -472,7 +473,7 @@ async def handler(req: Any) -> Any:
472473
473474 # Zero advertised tools is treated as an errored tools/list before the
474475 # virtual missing-capability tool is appended.
475- names , empty = collect_listed_tools (data , tools , lifecycle . session_id )
476+ names , empty = collect_listed_tools (data , tools )
476477 injection = resolve_virtual_tool_injection (
477478 data ,
478479 tools ,
@@ -565,10 +566,11 @@ def _tool_lookup_not_found_errors() -> Tuple[type, ...]:
565566
566567async def _standalone_ownership (
567568 data : MCPAnalyticsData , high_level : Any , name : str , meta : Any
568- ) -> Tuple [set , Optional [bool ]]:
569+ ) -> Tuple [set , Optional [bool ], Optional [ Dict [ str , Any ]] ]:
569570 """Ownership of the injected arguments on jlowin's standalone FastMCP: the
570571 keys to strip before it validates the call, and whether ``llm_model`` is
571- ours (``None`` when nothing can say).
572+ ours (``None`` when nothing can say). The third item is the trusted schema
573+ for input-name analytics, or ``None`` when middleware can change dispatch.
572574
573575 Only keys injected under the current options are candidates. ``context``
574576 and ``conversation_id`` are stripped unless the registered schema (or,
@@ -580,17 +582,41 @@ async def _standalone_ownership(
580582 stays and is still read (posthog-js ADR-0011).
581583 """
582584 try :
583- declared , model_injectable = await _registry_view (high_level , name , meta )
585+ declared , model_injectable , input_schema = await _registry_view (
586+ high_level , name , meta
587+ )
584588 model_ours = data .tool_model_parameter_injected .get (name , model_injectable )
585589 if _dispatch_can_differ (high_level ):
586590 model_ours = False
591+ input_schema = None
587592 except Exception : # noqa: BLE001 - ownership inference must never prevent dispatch
588- declared , model_ours = None , None
593+ declared , model_ours , input_schema = None , None , None
589594 candidates = _injected_keys (data )
590595 strip = {k for k in candidates - {"llm_model" } if k not in (declared or set ())}
591596 if "llm_model" in candidates and model_ours :
592597 strip .add ("llm_model" )
593- return strip , model_ours
598+ return strip , model_ours , input_schema
599+
600+
601+ def _tool_schema_view (
602+ high_level : Any , tool : Any
603+ ) -> Tuple [Optional [set ], Optional [bool ], Optional [Dict [str , Any ]]]:
604+ if tool is None :
605+ return None , None , None
606+ schema = getattr (tool , "parameters" , None )
607+ if isinstance (schema , dict ):
608+ declared , injectable = _schema_view (
609+ schema , dereferenced = _server_dereferences (high_level )
610+ )
611+ return declared , injectable , schema
612+ fn = getattr (tool , "fn" , None )
613+ if fn is None :
614+ return set (), True , None
615+ try :
616+ declared = {k for k in _INJECTED_KEYS if k in inspect .signature (fn ).parameters }
617+ except Exception : # noqa: BLE001 - introspection is best-effort
618+ return set (), True , None
619+ return declared , "llm_model" not in declared , None
594620
595621
596622def _injected_keys (data : MCPAnalyticsData ) -> set :
@@ -609,35 +635,24 @@ def _injected_keys(data: MCPAnalyticsData) -> set:
609635
610636async def _registry_view (
611637 high_level : Any , name : str , meta : Any
612- ) -> Tuple [Optional [set ], Optional [bool ]]:
638+ ) -> Tuple [Optional [set ], Optional [bool ], Optional [ Dict [ str , Any ]] ]:
613639 """What the registered tool says about the injected keys: which of
614640 ``_INJECTED_KEYS`` it declares itself, and whether a listing would have
615641 injected ``llm_model`` into its schema (the same test the listing applies,
616642 on the schema as the client would see it). Read from the schema (a ``Tool``
617643 subclass may have no function) else the signature. The registry is read
618644 directly, never through middleware, so a cold instance answers without a
619645 listing and rate limiters are not charged. ``(None, None)`` when the
620- registry has no such tool or cannot be read."""
646+ registry has no such tool or cannot be read. The third item is the tool's
647+ input schema when the registry supplies one."""
621648 try :
622649 tool = await _registered_tool (high_level , name , meta )
623650 except Exception as error : # noqa: BLE001 - introspection is best-effort
624651 if not isinstance (error , _tool_lookup_not_found_errors ()):
625652 warn_ownership_lookup_failed (name , error )
626- return set (_INJECTED_KEYS ), None
627- return None , None
628- if tool is None :
629- return None , None
630- schema = getattr (tool , "parameters" , None )
631- if isinstance (schema , dict ):
632- return _schema_view (schema , dereferenced = _server_dereferences (high_level ))
633- fn = getattr (tool , "fn" , None )
634- if fn is None :
635- return set (), True
636- try :
637- declared = {k for k in _INJECTED_KEYS if k in inspect .signature (fn ).parameters }
638- except Exception : # noqa: BLE001 - introspection is best-effort
639- return set (), True
640- return declared , "llm_model" not in declared
653+ return set (_INJECTED_KEYS ), None , None
654+ return None , None , None
655+ return _tool_schema_view (high_level , tool )
641656
642657
643658async def _registered_tool (high_level : Any , name : str , meta : Any ) -> Any :
0 commit comments