MCP server for AI coding agents#

MCP (Model Context Protocol) is a standard way for an AI coding agent – Claude Code, or any other MCP-capable client – to call tools exposed by a local server. scope-profiler-mcp is such a server: it exposes profiling, benchmarking and comparison as structured tools, so an agent can inspect a run’s performance and check whether a code change helped, as part of its own workflow, without you copy-pasting tables into the chat.

It is a thin adapter, not a second implementation: every tool calls straight into the same Python API Plotting with the CLI, CLI reference and the HDF5 output & post-processing from Python page describe — read_h5, the region_rows/ diff_rows used by inspect/diff, ProfileManager.run_script, and the plotting_scripts functions behind plot. Nothing about profiling, HDF5 parsing, or comparing runs is reimplemented for MCP.

Claude Code
    |  MCP (stdio)
    v
scope-profiler-mcp
    |  the same Python API the CLI uses
    v
scope-profiler
    |
    v
inspect / benchmark / compare -> structured JSON

This page covers using the MCP server as it stands today: an agent can read profiling data and judge whether a change helped. It does not modify your code, run an optimization search, or make commits – an agent using these tools still has to decide what to try next and make the edit itself, the same as it would from a git diff and your instructions.

Installing#

pip install "scope-profiler[mcp]"

This is a separate extra from the base install: pip install scope-profiler alone does not pull in the mcp package, so normal profiling and the scope-profiler CLI are unaffected either way. plot_profile (see below) additionally needs the pproc extra, exactly as scope-profiler plot does:

pip install "scope-profiler[mcp,pproc]"

Starting the server#

scope-profiler-mcp

or equivalently:

python -m scope_profiler.mcp_server

This speaks MCP over stdio and is meant to be launched by an MCP client (Claude Code, another agent, or mcp dev/an MCP inspector for manual testing) – not run directly in a terminal you intend to type into.

Configuring Claude Code#

Add it as an MCP server, either via the CLI:

claude mcp add scope-profiler -- scope-profiler-mcp

or by adding it directly to your project’s .mcp.json (or Claude Code’s global MCP config):

{
  "mcpServers": {
    "scope-profiler": {
      "command": "scope-profiler-mcp"
    }
  }
}

If scope-profiler-mcp is not on PATH (e.g. installed into a virtualenv Claude Code does not activate), point command at the interpreter instead:

{
  "mcpServers": {
    "scope-profiler": {
      "command": "/path/to/venv/bin/python",
      "args": ["-m", "scope_profiler.mcp_server"]
    }
  }
}

Once configured, restart Claude Code (or run /mcp to check connection status) and the four tools below become available for it to call.

Available tools#

inspect_profile#

Structured version of scope-profiler inspect: runtime, rank count, region statistics (sorted, capped at top_n – raise it or pass 0 for no limit), metadata grouped the same way (run info, hardware, MPI/OpenMP, Slurm), and a LIKWID hardware-counter summary when the run recorded one. Large, rarely-useful metadata (raw environment variables, unrecognized fields) is collapsed to a count by default; full_metadata=True returns everything.

compare_profiles#

Structured version of scope-profiler diff, plus the whole-run comparison diff does not compute: given a baseline and a candidate file, it returns overall.faster (bool), overall.speedup, overall.relative_change_pct and overall.absolute_diff_seconds, alongside the per-region deltas and a regressions/improvements split by threshold_pct. The arithmetic is done here, in Python – an agent should never need to subtract two numbers from two separate inspect_profile calls to answer “did this get faster?”.

run_profile#

Runs a script under the profiler (equivalent to scope-profiler run) and returns an inspect_profile-style summary of the result. The script runs in its own subprocess, with a timeout, so it cannot hang or crash the MCP server; a non-zero exit, a timeout, or a missing script all come back as a clear tool error rather than a partial result. script_args are passed through as a plain argument list (no shell involved), matching how ProfileManager.run_script already works.

plot_profile (optional)#

Renders one figure (gantt, flame_chart, flame_graph, durations, timeseries, speedup) via the same functions scope-profiler plot uses, and returns the path it wrote. Most agent workflows should prefer the numbers from inspect_profile/compare_profiles; reach for this only when a human is going to look at the image afterwards (e.g. attaching it to a PR). Requires the pproc extra.

Example: an agent judging a code change#

A typical loop, in words, for “is this function faster after my change?”:

  1. The agent calls run_profile on a benchmark script before making any change, and keeps the returned file_path (or passes output_path itself to control where it lands) as the baseline.

  2. It edits the code.

  3. It calls run_profile again on the same script for the candidate run.

  4. It calls compare_profiles with the two file paths and reads overall.faster and overall.speedup directly, plus regressions/ improvements to see which regions moved.

  5. It reports the result (and, optionally, calls plot_profile to attach a Gantt or duration chart) – and decides, itself, whether to keep the change, try something else, or ask you.

Step 5 is where this stops: the agent reasons about the structured numbers you get from steps 1-4, the same as it would reason about a git diff, but scope-profiler does not close the loop by editing code or committing on its own.

Testing without a live client#

The server’s tools are plain Python functions under scope_profiler.mcp_server.tools, importable and testable without the mcp package at all:

from scope_profiler.mcp_server.tools import inspect_profile

print(inspect_profile("profiling_data.h5", top_n=5))

scope_profiler.mcp_server.server.create_server() builds the full MCP server object (tool registration, JSON schemas) without starting a transport, useful for scripting against it directly:

import asyncio
from scope_profiler.mcp_server.server import create_server

server = create_server()
print(asyncio.run(server.list_tools()))