Module fastmcp_extensions.utils
FastMCP Extensions utilities for testing and server description.
This module contains utilities that are designed to be called as scripts or used programmatically for testing and describing MCP servers.
Submodules
- test_tool: MCP tool testing utilities (stdio and HTTP transports)
- describe_server: MCP server description and measurement utilities
Sub-modules
fastmcp_extensions.utils.describe_server-
MCP server description and measurement utilities …
fastmcp_extensions.utils.test_tool-
MCP tool testing utilities …
Functions
async def call_mcp_tool(app: FastMCP, tool_name: str, args: dict[str, Any]) ‑> object-
Expand source code
async def call_mcp_tool(app: FastMCP, tool_name: str, args: dict[str, Any]) -> object: """Call an MCP tool using the FastMCP client.""" async with Client(app) as client: return await client.call_tool(tool_name, args)Call an MCP tool using the FastMCP client.
def find_free_port() ‑> int-
Expand source code
def find_free_port() -> int: """Find an available port on localhost.""" with socket.socket() as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1]Find an available port on localhost.
async def get_tool_details(app: FastMCP) ‑> list[dict[str, Any]]-
Expand source code
async def get_tool_details(app: FastMCP) -> list[dict[str, Any]]: """Get detailed information about each tool. Args: app: The FastMCP app instance Returns: List of dictionaries with tool details including name, description length, and schema length for each tool. """ async with Client(app) as client: tools = await client.list_tools() details = [] for tool in tools: name_len = len(tool.name) desc_len = len(tool.description) if tool.description else 0 schema_len = len(str(tool.inputSchema)) if tool.inputSchema else 0 details.append( { "name": tool.name, "name_length": name_len, "description_length": desc_len, "schema_length": schema_len, "total_length": name_len + desc_len + schema_len, } ) return detailsGet detailed information about each tool.
Args
app- The FastMCP app instance
Returns
List of dictionaries with tool details including name, description length, and schema length for each tool.
async def list_mcp_tools(app: FastMCP) ‑> list[Any]-
Expand source code
async def list_mcp_tools(app: FastMCP) -> list[Any]: """List all available MCP tools.""" async with Client(app) as client: return await client.list_tools()List all available MCP tools.
async def measure_tool_list(app: FastMCP) ‑> tuple[int, int]-
Expand source code
async def measure_tool_list(app: FastMCP) -> tuple[int, int]: """Measure the tool list size from the MCP server. This function connects to the MCP server and measures the character count of the tool list, including tool names, descriptions, and input schemas. Args: app: The FastMCP app instance Returns: Tuple of (tool_count, total_character_count) """ async with Client(app) as client: tools = await client.list_tools() tool_count = len(tools) total_chars = 0 for tool in tools: total_chars += len(tool.name) if tool.description: total_chars += len(tool.description) if tool.inputSchema: total_chars += len(str(tool.inputSchema)) return tool_count, total_charsMeasure the tool list size from the MCP server.
This function connects to the MCP server and measures the character count of the tool list, including tool names, descriptions, and input schemas.
Args
app- The FastMCP app instance
Returns
Tuple of (tool_count, total_character_count)
async def measure_tool_list_detailed(app: FastMCP, server_name: str | None = None) ‑> ToolListMeasurement-
Expand source code
async def measure_tool_list_detailed( app: FastMCP, server_name: str | None = None, ) -> ToolListMeasurement: """Measure the tool list size with detailed results. Args: app: The FastMCP app instance server_name: Optional name of the server for reporting Returns: ToolListMeasurement with detailed results """ tool_count, total_chars = await measure_tool_list(app) return ToolListMeasurement( tool_count=tool_count, total_characters=total_chars, average_chars_per_tool=total_chars // tool_count if tool_count > 0 else 0, server_name=server_name, )Measure the tool list size with detailed results.
Args
app- The FastMCP app instance
server_name- Optional name of the server for reporting
Returns
ToolListMeasurement with detailed results
async def run_http_tool_test(app: FastMCP,
port: int | None = None,
tool_name: str | None = None,
args: dict[str, Any] | None = None) ‑> int-
Expand source code
async def run_http_tool_test( app: FastMCP, port: int | None = None, tool_name: str | None = None, args: dict[str, Any] | None = None, ) -> int: """Run a tool test over HTTP transport using the app directly.""" import uvicorn if port is None: port = find_free_port() url = f"http://127.0.0.1:{port}/mcp" os.environ["MCP_HTTP_PORT"] = str(port) print(f"Starting HTTP server on port {port}...", file=sys.stderr) server_error: Exception | None = None def run_server() -> None: nonlocal server_error try: uvicorn.run( app.http_app(), host="127.0.0.1", port=port, log_level="error", ) except Exception as e: server_error = e server_thread = threading.Thread(target=run_server, daemon=True) server_thread.start() try: if not await wait_for_server(url): if server_error: print(f"Server error: {server_error}", file=sys.stderr) print(f"Server failed to start on port {port}", file=sys.stderr) return 1 async with Client(url) as client: tools = await client.list_tools() print(f"HTTP transport OK - {len(tools)} tools available") if tool_name: print(f"Calling tool: {tool_name}", file=sys.stderr) result = await client.call_tool(tool_name, args or {}) if hasattr(result, "text"): print(result.text) else: print(str(result)) return 0 finally: passRun a tool test over HTTP transport using the app directly.
def run_measurement(app: FastMCP, server_name: str | None = None) ‑> None-
Expand source code
def run_measurement(app: FastMCP, server_name: str | None = None) -> None: """Run tool list measurement and print results. This is a convenience function for CLI measurement scripts. Args: app: The FastMCP app instance server_name: Optional name of the server for reporting """ measurement = asyncio.run(measure_tool_list_detailed(app, server_name)) print(str(measurement))Run tool list measurement and print results.
This is a convenience function for CLI measurement scripts.
Args
app- The FastMCP app instance
server_name- Optional name of the server for reporting
def run_tool_test(app: FastMCP, tool_name: str, json_args: str) ‑> None-
Expand source code
def run_tool_test( app: FastMCP, tool_name: str, json_args: str, ) -> None: """Run a tool test with JSON arguments and print the result.""" args: dict[str, Any] = json.loads(json_args) result = asyncio.run(call_mcp_tool(app, tool_name, args)) if hasattr(result, "text"): print(result.text) else: print(str(result))Run a tool test with JSON arguments and print the result.
async def wait_for_server(url: str, timeout: float = 10.0) ‑> bool-
Expand source code
async def wait_for_server(url: str, timeout: float = SERVER_STARTUP_TIMEOUT) -> bool: """Wait for the MCP server to be ready by attempting to list tools.""" deadline = asyncio.get_event_loop().time() + timeout while asyncio.get_event_loop().time() < deadline: try: async with Client(url) as client: await client.list_tools() return True except Exception: await asyncio.sleep(POLL_INTERVAL) return FalseWait for the MCP server to be ready by attempting to list tools.
Classes
class ToolListMeasurement (tool_count: int,
total_characters: int,
average_chars_per_tool: int,
server_name: str | None = None)-
Expand source code
@dataclass class ToolListMeasurement: """Measurement results for an MCP tool list.""" tool_count: int total_characters: int average_chars_per_tool: int server_name: str | None = None def __str__(self) -> str: """Return a human-readable string representation.""" lines = [] if self.server_name: lines.append(f"MCP Server: {self.server_name}") lines.append(f"Tool count: {self.tool_count}") lines.append(f"Total characters: {self.total_characters:,}") lines.append(f"Average chars per tool: {self.average_chars_per_tool:,}") return "\n".join(lines)Measurement results for an MCP tool list.
Instance variables
var average_chars_per_tool : intvar server_name : str | Nonevar tool_count : intvar total_characters : int