What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
To see what tools an MCP server advertises, send the MCP tools/list discovery request and record each returned definition. The Python script below uses only the standard library and supports stdio transport: it launches a server command, initializes an MCP session, follows tool-list pagination, and writes a JSON inventory. It describes the interface the server advertises; it does not test tool calls or establish that a tool is safe.
What an MCP tool profile tells you
MCP separates discovery from execution. tools/list returns the tools and their metadata; tools/call invokes a named tool. A profile made from discovery is a snapshot of the advertised surface, not a behavioral test. A declared input schema does not prove that the implementation accepts every declared input, and descriptions or annotations are server-provided metadata rather than verified guarantees.
As an Amazon Associate I earn from qualifying purchases.
A tool definition has a name and input schema. Depending on what the server supplies, it can also include a title, description, output schema, and annotations. The specification allows the list to be paginated, so an inventory should record whether it followed continuation cursors until no further page remained. See the MCP Tools specification and MCP schema reference.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →List MCP tools without installing the SDK
This minimal profiler uses Python 3 and standard-library modules only. It implements MCP over stdio, not Streamable HTTP or SSE. Pass the server’s normal launch command after --; for example, if a server is launched with python server.py, run the profiler as python mcp_profile.py -- python server.py. The command must start an MCP stdio server that reads newline-delimited JSON-RPC messages from stdin and writes protocol messages to stdout. Keep diagnostic output on stderr so it does not corrupt the protocol stream.
#1 Best Overall
Save the following as mcp_profile.py. The default protocol version is 2025-06-18; override it with --protocol-version if your server requires a different version. The profile records the negotiated version returned during initialization when available.
#!/usr/bin/env python3
import argparse
import datetime as dt
import json
import subprocess
import sys
def read_message(proc):
while True:
line = proc.stdout.readline()
if not line:
raise RuntimeError("Server closed stdout before replying")
try:
message = json.loads(line)
except json.JSONDecodeError:
raise RuntimeError("Expected newline-delimited JSON on server stdout")
if isinstance(message, dict) and "id" in message:
return message
def request(proc, message_id, method, params=None):
message = {"jsonrpc": "2.0", "id": message_id, "method": method}
if params is not None:
message["params"] = params
proc.stdin.write(json.dumps(message, separators=(",", ":")) + "n")
proc.stdin.flush()
while True:
reply = read_message(proc)
if reply.get("id") == message_id:
if "error" in reply:
raise RuntimeError(f"{method} failed: {reply['error']}")
return reply.get("result", {})
def main():
parser = argparse.ArgumentParser(
description="Profile tools advertised by an MCP stdio server"
)
parser.add_argument(
"--protocol-version", default="2025-06-18",
help="MCP protocol version to request during initialization"
)
parser.add_argument("command", nargs=argparse.REMAINDER,
help="server launch command, preceded by --")
args = parser.parse_args()
command = args.command[1:] if args.command[:1] == ["--"] else args.command
if not command:
parser.error("provide a server launch command after --")
proc = subprocess.Popen(command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, text=True, bufsize=1)
try:
init = request(proc, 1, "initialize", {
"protocolVersion": args.protocol_version,
"capabilities": {},
"clientInfo": {"name": "stdlib-mcp-profiler", "version": "1.0"}
})
proc.stdin.write(json.dumps({
"jsonrpc": "2.0", "method": "notifications/initialized"
}, separators=(",", ":")) + "n")
proc.stdin.flush()
tools = []
pages = 0
cursor = None
while True:
params = {"cursor": cursor} if cursor is not None else {}
result = request(proc, pages + 2, "tools/list", params)
pages += 1
tools.extend(result.get("tools", []))
cursor = result.get("nextCursor")
if cursor is None:
break
profile = {
"profiled_at": dt.datetime.now(dt.timezone.utc).isoformat(),
"transport": "stdio",
"launch_command": command,
"requested_protocol_version": args.protocol_version,
"negotiated_protocol_version": init.get("protocolVersion"),
"pages_received": pages,
"continuation_cursor_present": cursor is not None,
"pagination_complete": cursor is None,
"tool_count": len(tools),
"tools": tools
}
print(json.dumps(profile, indent=2, ensure_ascii=False))
finally:
proc.terminate()
proc.wait(timeout=5)
if __name__ == "__main__":
main()
Read and retain the inventory
Run the script with the launch command appropriate to your server. Its JSON output preserves each returned tool object, including the raw input schema and any output schema or other metadata supplied by the server. That makes the original declarations available for inspection even when a compact summary would omit schema constraints.
Rank #2
For a quick human-readable view, each input schema’s properties describes declared fields, and its required array identifies required fields. A field’s type is useful but not the whole contract: constraints such as enumerated values, bounds, nested properties, or string formats can matter to callers. Keep the raw schema rather than reducing it to field names and types alone.
Recommended Free Tools
The profile records the launch command, stdio transport, timestamp, requested and negotiated protocol versions, number of pages received, and whether pagination completed. It does not capture endpoint or authorization details for other transports; this script does not support those transports.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Compare profiles without mistaking them for compatibility tests
To make a before-and-after comparison meaningful, profile the same server version under the same credentials or authorization scope, transport, and protocol version. Record those conditions alongside the output. The specification permits the tool set to vary with authorization, so a difference between inventories may reflect access context rather than a deployment change.
Useful comparison points include tool count and names, changed required inputs, changed schemas, and whether both inventories completed pagination. A renamed tool or altered required field is a signal to investigate, not by itself proof of a breaking change: the MCP protocol does not promise that comparing these declarations establishes compatibility.
Rank #4
When to use an SDK instead
This script is intentionally narrow: it launches a stdio server and collects declarations. MCP also defines Streamable HTTP and SSE transports, which require their own transport handling; this code does not speak them. The official MCP Python SDK documentation lists those transports, and its client guide shows a client API whose list_tools() result exposes tool metadata. Use an SDK client when you need broader transport support or a fuller integration rather than this lightweight inventory.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




