Display info about the client (that performed the last tool call) in dashboard

Had to hack around a bit to make it happen but worth the added transparency for users
This commit is contained in:
Michael Panchenko committed 2026-01-14 22:18:20 +07:00
1 parent 5897585ef2
commit 2eca7af593
4 files changed
+36 -3

No files matched your search

+3
View File
@@ -56,6 +56,7 @@ class ResponseConfigOverview(BaseModel):
jetbrains_mode: bool
languages: list[str]
encoding: str | None
current_client: str | None
class ResponseAvailableLanguages(BaseModel):
@@ -370,6 +371,7 @@ class SerenaDashboardAPI:
def _get_config_overview(self) -> ResponseConfigOverview:
from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode
from serena.tools.tools_base import Tool
# Get active project info
project = self._agent.get_active_project()
@@ -491,6 +493,7 @@ class SerenaDashboardAPI:
jetbrains_mode=self._agent.serena_config.language_backend == LanguageBackend.JETBRAINS,
languages=languages,
encoding=encoding,
current_client=Tool.get_last_tool_call_client_str(),
)
def _shutdown(self) -> None:
+3 -1
View File
@@ -239,7 +239,9 @@ class SerenaMCPFactory:
parameters=parameters,
fn_metadata=func_arg_metadata,
is_async=is_async,
context_kwarg=None,
# keep the value in sync with the kwarg name in Tool.apply_ex. The mcp sdk uses reflection to infer this
# when the tool is constructed via from_function (which is a bit crazy IMO, but well...)
context_kwarg="mcp_ctx",
annotations=annotations,
title=tool_title,
)
@@ -619,6 +619,10 @@ class Dashboard {
html += '<div class="config-label">File Encoding:</div>';
html += '<div class="config-value">' + (config.encoding || 'N/A') + '</div>';
// Current Client info
html += '<div class="config-label">Current Client:</div>';
html += '<div class="config-value">' + (config.current_client || 'None') + '</div>';
html += '</div>';
// Active tools - collapsible
+26 -2
View File
@@ -4,8 +4,10 @@ from abc import ABC
from collections.abc import Iterable
from dataclasses import dataclass
from types import TracebackType
from typing import TYPE_CHECKING, Any, Protocol, Self, TypeVar
from typing import TYPE_CHECKING, Any, Protocol, Self, TypeVar, cast
from mcp import Implementation
from mcp.server.fastmcp import Context
from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
from sensai.util import logging
from sensai.util.string import dict_string
@@ -115,6 +117,17 @@ class Tool(Component):
# (which is use by the LLM, so a good description is important)
# and to validate the tool call arguments.
_last_tool_call_client_str: str | None = None
"""We can only get the client info from within a tool call. Each tool call will update this variable."""
@classmethod
def set_last_tool_call_client_str(cls, client_str: str | None) -> None:
cls._last_tool_call_client_str = client_str
@classmethod
def get_last_tool_call_client_str(cls) -> str | None:
return cls._last_tool_call_client_str
@classmethod
def get_name_from_cls(cls) -> str:
name = cls.__name__
@@ -219,10 +232,21 @@ class Tool(Component):
def is_active(self) -> bool:
return self.agent.tool_is_active(self.__class__)
def apply_ex(self, log_call: bool = True, catch_exceptions: bool = True, **kwargs) -> str: # type: ignore
def apply_ex(self, log_call: bool = True, catch_exceptions: bool = True, mcp_ctx: Context | None = None, **kwargs) -> str: # type: ignore
"""
Applies the tool with logging and exception handling, using the given keyword arguments
"""
if mcp_ctx is not None:
try:
client_params = mcp_ctx.session.client_params
if client_params is not None:
client_info = cast(Implementation, client_params.clientInfo)
client_str = client_info.title if client_info.title else client_info.name + " " + client_info.version
if client_str != self.get_last_tool_call_client_str():
log.debug(f"Updating client info: {client_info}")
self.set_last_tool_call_client_str(client_str)
except BaseException as e:
log.info(f"Failed to get client info: {e}.")
def task() -> str:
apply_fn = self.get_apply_fn()