Skip to content

list_mcp_tools

Python · package toolnexus · SPEC §2 · python/src/toolnexus/mcp_source.py

async def list_mcp_tools(
input: str | dict[str, Any],
*,
cancel: asyncio.Event | None = None,
) -> McpInventory

Connects to every enabled server, lists its tool definitions, and disconnects before returning — no Tool.execute, no toolkit, nothing left running. The returned tool defs use each server’s original, unprefixed names, and are the unfiltered set (ignoring any per-server tools allowlist in config) — this is the call that exists to author and validate those allowlists, so it needs to show you everything a server has, not the narrowed view.

  • Building a config UI or a mcp.json validator: show what a server would expose, without committing to running it.
  • Deciding a per-server tools allowlist — you need the full, unfiltered tool list to write it against.
  • A one-shot inventory (a CLI --list-tools flag, a CI check) where holding connections open past the listing would be pure waste.

1. Disabled servers report status, and nothing else

Section titled “1. Disabled servers report status, and nothing else”
import asyncio
from toolnexus import list_mcp_tools, parse_mcp_config
config = parse_mcp_config("examples/mcp.json")
for cfg in config.values():
cfg["enabled"] = False
async def main():
inventory = await list_mcp_tools(config)
assert inventory.tools == {}
assert inventory.status == {"everything": "disabled", "example-remote": "disabled"}
print("ok:", inventory.status)
asyncio.run(main())

inventory.tools only gets a key for a server that actually listed successfully — a failed connect shows up in status, not as an empty list.

import asyncio
from toolnexus import list_mcp_tools
config = {
"broken": {
"type": "local",
"command": ["/no/such/binary-toolnexus-docs-example"],
},
}
async def main():
inventory = await list_mcp_tools(config)
assert inventory.status["broken"] == "failed"
# No key at all for a server that never listed anything — not `[]`.
assert "broken" not in inventory.tools
print("ok:", inventory.status, "| tools:", inventory.tools)
asyncio.run(main())

3. Parse first, decide what to inventory, run it, confirm nothing lingers

Section titled “3. Parse first, decide what to inventory, run it, confirm nothing lingers”

The realistic shape: use parse_mcp_config to see what a config declares, then call list_mcp_tools on the part you actually want inventoried. Here every server is disabled, so the call has nothing to connect to — proving the “disconnect before returning” contract trivially, since there is nothing left to disconnect.

import asyncio
from toolnexus import list_mcp_tools, parse_mcp_config
config = parse_mcp_config("examples/mcp.json")
assert sorted(config.keys()) == ["everything", "example-remote"]
for cfg in config.values():
cfg["enabled"] = False
async def main():
inventory = await list_mcp_tools(config, cancel=asyncio.Event())
assert inventory.tools == {}
assert set(inventory.status) == {"everything", "example-remote"}
assert all(v == "disabled" for v in inventory.status.values())
print("ok:", sorted(inventory.status))
asyncio.run(main())
Parameter Type What it does
input str | dict Same accepted shapes as load_mcp — see parse_mcp_config.
cancel asyncio.Event | None Same whole-call abort semantics as load_mcp_with_context.
Member Type What it is
tools dict[str, list[ToolInfo]] Server name → its listed, unfiltered tool defs (original names). Only present for a server that listed successfully.
status dict[str, McpStatus] Per-server "connected", "disabled", or "failed" — same vocabulary as load_mcp.

ToolInfo carries name, description, input_schema — no execute.

  • load_mcp — Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.
  • load_mcp_with_context — The ctx-aware load: bound connection time and cancel a slow or hung server without leaking a child process.
  • parse_mcp_config — Parse and validate config without connecting — the fast fail for a malformed or misspelled server block.
  • elicitation_to_request — Map an MCP server’s elicitation request onto the §10 suspension contract, and map the answer back.