feat: add MCP client debug tools
Add get_client_capabilities and list_client_roots tools to inspect MCP client information. These help debug MCP connections by exposing: - Client name/version (e.g., "claude-code" v2.1.15) - Protocol version and supported capabilities - Root directories advertised by the client (typically CWD) Both tools are always registered (part of mode control, not runtime).
This commit is contained in:
parent
3811099623
commit
652c4796a9
@ -1,9 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from fastmcp import FastMCP
|
from fastmcp import Context, FastMCP
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from gnuradio_mcp.middlewares.docker import DockerMiddleware
|
from gnuradio_mcp.middlewares.docker import DockerMiddleware
|
||||||
@ -22,6 +22,25 @@ class RuntimeModeStatus(BaseModel):
|
|||||||
oot_available: bool
|
oot_available: bool
|
||||||
|
|
||||||
|
|
||||||
|
class ClientCapabilities(BaseModel):
|
||||||
|
"""MCP client capability information from initialize handshake."""
|
||||||
|
|
||||||
|
client_name: str | None = None
|
||||||
|
client_version: str | None = None
|
||||||
|
protocol_version: str | None = None
|
||||||
|
capabilities: dict[str, Any] = {}
|
||||||
|
roots_supported: bool = False
|
||||||
|
sampling_supported: bool = False
|
||||||
|
experimental: dict[str, Any] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class ClientRoot(BaseModel):
|
||||||
|
"""A root directory advertised by the MCP client."""
|
||||||
|
|
||||||
|
uri: str
|
||||||
|
name: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class McpRuntimeProvider:
|
class McpRuntimeProvider:
|
||||||
"""Registers runtime control tools with FastMCP.
|
"""Registers runtime control tools with FastMCP.
|
||||||
|
|
||||||
@ -130,8 +149,73 @@ class McpRuntimeProvider:
|
|||||||
oot_available=self._provider._has_oot,
|
oot_available=self._provider._has_oot,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Debug tools for MCP client inspection
|
||||||
|
@self._mcp.tool
|
||||||
|
async def get_client_capabilities(ctx: Context) -> ClientCapabilities:
|
||||||
|
"""Get the connected MCP client's capabilities.
|
||||||
|
|
||||||
|
Returns information about the client including:
|
||||||
|
- Client name and version (e.g., "claude-code" v2.1.15)
|
||||||
|
- MCP protocol version
|
||||||
|
- Supported capabilities (roots, sampling, etc.)
|
||||||
|
- Experimental features
|
||||||
|
|
||||||
|
Useful for debugging MCP connections and understanding
|
||||||
|
what features the client supports.
|
||||||
|
"""
|
||||||
|
session = ctx.session
|
||||||
|
client_params = session.client_params if session else None
|
||||||
|
|
||||||
|
if client_params is None:
|
||||||
|
return ClientCapabilities()
|
||||||
|
|
||||||
|
client_info = getattr(client_params, "clientInfo", None)
|
||||||
|
caps = getattr(client_params, "capabilities", None)
|
||||||
|
|
||||||
|
result = ClientCapabilities(
|
||||||
|
client_name=client_info.name if client_info else None,
|
||||||
|
client_version=client_info.version if client_info else None,
|
||||||
|
protocol_version=getattr(client_params, "protocolVersion", None),
|
||||||
|
)
|
||||||
|
|
||||||
|
if caps:
|
||||||
|
if hasattr(caps, "roots") and caps.roots is not None:
|
||||||
|
result.roots_supported = True
|
||||||
|
result.capabilities["roots"] = {
|
||||||
|
"listChanged": getattr(caps.roots, "listChanged", None)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasattr(caps, "sampling") and caps.sampling is not None:
|
||||||
|
result.sampling_supported = True
|
||||||
|
result.capabilities["sampling"] = {}
|
||||||
|
|
||||||
|
if hasattr(caps, "experimental"):
|
||||||
|
result.experimental = caps.experimental or {}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@self._mcp.tool
|
||||||
|
async def list_client_roots(ctx: Context) -> list[ClientRoot]:
|
||||||
|
"""List the root directories advertised by the MCP client.
|
||||||
|
|
||||||
|
Roots represent project directories or workspaces the client
|
||||||
|
wants the server to be aware of. Typically includes the
|
||||||
|
current working directory.
|
||||||
|
|
||||||
|
Returns empty list if roots capability is not supported.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
roots = await ctx.list_roots()
|
||||||
|
return [
|
||||||
|
ClientRoot(uri=str(root.uri), name=root.name)
|
||||||
|
for root in roots
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("Failed to list client roots: %s", e)
|
||||||
|
return []
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Registered 3 mode control tools (runtime mode disabled by default)"
|
"Registered 5 mode control tools (runtime mode disabled by default)"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _register_runtime_tools(self):
|
def _register_runtime_tools(self):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user