Skip to content
Projects

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

NameDescription
ToolErrorA user-facing error from an MCP tool (bad input, missing file, ...).

Functions

NameDescription
compare_benchmarksCompare benchmark manifests and return a keep/reject decision.
compare_profilesMachine-readable performance diff between two profiling files.
inspect_profileStructured summary of one profiling file: metadata + region statistics.
plot_profileRender one figure from one or more profiling files and return its path.
run_benchmarkRun a declarative repeated benchmark and correctness gate.
run_profileProfile 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) -> dict

Compare 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) -> dict

Machine-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) -> dict

Structured 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) -> dict

Render 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') -> dict

Run 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) -> dict

Profile 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).