scope_profiler.mcp_server.tools
Pure, MCP-framework-free implementations of the tools exposed over MCP.
Kept separate from :mod:scope_profiler.mcp_server.server so these functions
can be unit-tested (and reused) without importing the mcp package at all,
and so the MCP layer stays a thin adapter over the existing
scope_profiler Python API rather than a place where new profiling,
HDF5-parsing, summary or comparison logic gets written. Every function here
delegates the actual computation to :mod:scope_profiler.summary,
:mod:scope_profiler.diff, :mod:scope_profiler.inspection,
:mod:scope_profiler.results or :mod:scope_profiler.plotting_scripts.
Every public function returns plain, JSON-serializable dict/list
data (or raises :class:ToolError) - never terminal-formatted text - so an
agent gets structured values it can act on directly instead of having to
parse a table.
Classes
| Name | Description |
|---|---|
ToolError | A user-facing error from an MCP tool (bad input, missing file, ...). |
Functions
| Name | Description |
|---|---|
compare_benchmarks | Compare benchmark manifests and return a keep/reject decision. |
compare_profiles | Machine-readable performance diff between two profiling files. |
inspect_profile | Structured summary of one profiling file: metadata + region statistics. |
plot_profile | Render one figure from one or more profiling files and return its path. |
run_benchmark | Run a declarative repeated benchmark and correctness gate. |
run_profile | Profile a script with scope-profiler run and return a structured summary. |
ToolErrorclass#
class ToolError(Exception)Bases: Exception
A user-facing error from an MCP tool (bad input, missing file, …).
compare_benchmarksfunction#
def compare_benchmarks(baseline_path: str, candidate_path: str) -> dictCompare benchmark manifests and return a keep/reject decision.
compare_profilesfunction#
def compare_profiles(baseline_path: str, candidate_path: str, include: list[str] | None = None, exclude: list[str] | None = None, ranks: list[int] | None = None, metric: str = 'total', threshold_pct: float = 5.0, top_n: int | None = _DEFAULT_TOP_N) -> dictMachine-readable performance diff between two profiling files.
Reuses :func:scope_profiler.diff.diff_rows for the per-region deltas,
then computes the overall (whole-run) comparison the same way, so an
agent can answer “did this change make it faster?” directly from
overall without inferring anything from formatted text.
Regions whose percent change exceeds threshold_pct are additionally
surfaced in regressions/improvements (candidate slower/faster
than baseline respectively), sorted by magnitude.
inspect_profilefunction#
def inspect_profile(file_path: str, include: list[str] | None = None, exclude: list[str] | None = None, ranks: list[int] | None = None, sort: str = 'total', top_n: int | None = _DEFAULT_TOP_N, full_metadata: bool = False) -> dictStructured summary of one profiling file: metadata + region statistics.
Reuses :func:scope_profiler.summary.region_rows and
:func:scope_profiler.inspection.collect_file_metadata - the same data
scope-profiler inspect prints - without any text formatting.
plot_profilefunction#
def plot_profile(file_paths: list[str] | str, plot_type: str = 'gantt', output_dir: str | None = None, include: list[str] | None = None, exclude: list[str] | None = None, ranks: list[int] | None = None, backend: str = 'matplotlib', x_field: str | None = None) -> dictRender one figure from one or more profiling files and return its path.
A thin wrapper over the existing plotting functions in
:mod:scope_profiler.plotting_scripts (the same code scope-profiler plot uses) - it does not draw anything itself. An agent normally
wants :func:inspect_profile/:func:compare_profiles instead; this
tool exists for the cases where a human will actually look at the
figure afterwards.
run_benchmarkfunction#
def run_benchmark(config_path: str, label: str = 'candidate') -> dictRun a declarative repeated benchmark and correctness gate.
run_profilefunction#
def run_profile(script_path: str, script_args: list[str] | None = None, only_user_code: bool = True, buffer_limit: int = 1024, output_path: str | None = None, timeout_seconds: float = 300.0, top_n: int | None = _DEFAULT_TOP_N) -> dictProfile a script with scope-profiler run and return a structured summary.
Runs the script in a separate subprocess (python -m scope_profiler run ..., the same entry point scope-profiler run uses) rather than
in-process, so arbitrary user code never executes inside the long-lived
MCP server: no shell=True, no string-built command line - the
script path and its arguments are passed as a plain argv list, exactly
as :meth:ProfileManager.run_script does internally. A timeout bounds
runaway scripts.
Results are read back from the HDF5 file the run produced via
:func:scope_profiler.h5reader.read_h5 and summarized with
:func:inspect_profile - the agent gets structured numbers, not
parsed subprocess stdout (stdout is only included, truncated, as a
debugging aid).