sparkmedic-mcp
Analyzes Spark applications, stages, and SQL queries by reading Spark UI metrics and event logs, providing performance diagnostics and recommended fixes for open-source Spark and Spark History Server.
Diagnoses Databricks clusters and job runs, leveraging the Databricks SDK, driver proxy, and event log delivery to identify performance issues and suggest optimizations.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sparkmedic-mcpAnalyze the slowest stage of my Databricks job"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
sparkmedic
Your Spark job is sick. sparkmedic tells you why, and how to fix it.
sparkmedic reads the same data you see in the Spark UI (stages, task metrics, SQL plans, executors)
through the documented Spark monitoring REST API
or from Spark event logs. It then runs a set of diagnosis rules and gives you a ranked report:
what is slow (stage, query, share of total task time),
why (the evidence from the metrics and the physical plan),
what to do (code changes, Spark configs, cluster/table changes), each linked to the official Apache Spark or Databricks documentation.
It works on open-source Spark, Spark History Server and Databricks, and ships as a CLI, a Python API (usable inside notebooks) and an MCP server so AI assistants such as Claude can run the diagnosis for you.
$ sparkmedic analyze databricks://0923-123456-abcdefgh
1. [CRITICAL] Compute-bound Stage 26 (involves 95% of total task time)
- I/O rate 0.37 MB/s per core: not I/O bound. CPU 97% of task time, GC 1%.
- The plan for this stage calls variant_get() 776 times on column 'periodic_json'.
Fix: cast the VARIANT to a STRUCT once, then select the fields. Then try Photon.
2. [LOW] Spill to disk in Stage 26: only 3% of the data moved, a minor cost here.
...
5. [LOW] The same input is read 2 times (ExistingRDD, e.g. a foreachBatch micro-batch)
Fix: persist the micro-batch once, run every action on it, then unpersist.Install
pip install sparkmedic # core: zero dependencies (Python 3.10+)
pip install "sparkmedic[databricks]" # + remote Databricks clusters (Databricks SDK)
pip install "sparkmedic[mcp]" # + MCP server
pip install "sparkmedic[all]" # everything, incl. zstd event logsFrom source:
git clone https://github.com/brianzavareh/sparkmedic.git
cd sparkmedic
pip install -e ".[all,dev]"
pytestRelated MCP server: Sprinklr MCP Server
Quick start
sparkmedic analyze <TARGET> works the same for every data source:
Where your app is | TARGET |
Running app, driver UI |
|
Spark History Server |
|
Databricks cluster (from your laptop) |
|
Databricks job run (any task cluster) |
|
Inside a Databricks notebook |
|
Event log files / folder / zip |
|
Databricks delivered logs |
|
Saved snapshot |
|
sparkmedic apps http://localhost:18080 # list applications
sparkmedic analyze http://localhost:18080 --app-id app-2026... # analyze one
sparkmedic analyze ./spark-events -o reports/ --format both # write Markdown + JSON + snapshot
sparkmedic analyze databricks://0923-123456-abcdefgh --focus-stage 26
sparkmedic collect http://localhost:4040 -o snap.json # save now, analyze later / elsewhere
sparkmedic rules # list all rulesBefore collecting, analyze shows which application and scope it will analyze and asks you to confirm
(-y skips the prompt; non-interactive runs proceed automatically). Useful flags:
--focus-stage ID/--focus-sql ID(repeatable): report only on the stage/query you care about--min-severity medium: hide low-severity findings--min-stage-share 0.02: also analyze smaller stages (default: stages using at least 5% of task time)--disable SD403: turn a rule off--fail-on high: exit with code 2 when a finding of that severity exists (CI / scheduled checks)
Inside a Databricks notebook
%pip install sparkmedic
from sparkmedic import diagnose
result = diagnose("notebook") # current Spark app on this cluster, no token needed
result.display() # renders the report
result.save("/Volumes/main/default/perf_reports/")See examples/databricks_notebook.py.
Python API
from sparkmedic import collect, analyze, diagnose
from sparkmedic.analysis.engine import AnalysisConfig, Thresholds
snap = collect("http://localhost:18080", app_id="app-20260922-0001")
diag = analyze(snap, AnalysisConfig(thresholds=Thresholds(min_stage_share=0.02)))
print(diag.to_markdown())
data = diag.to_dict() # stable JSON schema (schema_version 1.0)Authentication (no hard-coded credentials)
Secrets are never accepted as command-line arguments or tool parameters, never logged, and never written to reports or snapshots (Spark conf values that look like secrets are redacted).
Target | How credentials are provided |
Spark UI / History Server | none, or |
Databricks remote | Databricks unified authentication through the official SDK: OAuth (U2M |
Databricks notebook | the notebook's own short-lived context token, in memory only (only when the driver-local API is not reachable). |
TLS certificates are verified by default (--ca-bundle for private CAs). Credentials are dropped if a
server redirects to a different host.
How Databricks access works
notebook: reads the driver's local Spark UI API (sc.uiWebUrl). If the SparkContext is not available (standard/shared access mode), it falls back to the workspace driver proxy.databricks://<cluster-id>on a running cluster: calls the Spark REST API through the workspace driver proxyhttps://<workspace>/driver-proxy-api/o/<org-id>/<cluster-id>/40001/api/v1/, authenticated with the Databricks SDK. The org id is detected automatically. Note: this proxy route is widely used but only community-documented.Terminated clusters / job clusters: falls back to the Spark event logs written by compute log delivery (Volumes or DBFS), downloads them with the SDK and replays them locally. Turn on log delivery for job clusters you want to diagnose after they finish. S3 destinations: copy the folder locally and pass the path.
Serverless compute does not expose the Spark UI API; use the query profile or system.query.history there.
Using it from Claude and other AI assistants (MCP)
sparkmedic-mcp is a stdio Model Context Protocol server with read-only tools:
Tool | What it does |
| apps at a target |
| resolves the app and scope; the assistant confirms this with you before continuing |
| collects + analyzes; returns the JSON report, Markdown and a |
| full stage metrics, quantiles and slowest tasks |
| physical plan and per-operator metrics |
| rule catalog |
Claude Code:
claude mcp add sparkmedic -- sparkmedic-mcpClaude Desktop / Cursor / other clients (mcpServers config):
{
"mcpServers": {
"sparkmedic": {
"command": "sparkmedic-mcp",
"env": { "DATABRICKS_CONFIG_PROFILE": "DEFAULT" }
}
}
}Then ask: "Why is the job on databricks://0923-123456-abcdefgh slow?". The server works with MCP Python SDK 1.x and 2.x.
What it checks
The stage rules follow the Databricks Spark UI diagnosis guide: find the stages that use the most task time, check them for skew and spill, decide whether they are I/O bound, and otherwise look for the compute cost in the physical plan.
Rule | Detects | Main evidence |
SD101 | Data skew / straggler tasks | max task duration > 1.5x the 75th percentile; per-task bytes/records |
SD102 | Spill to disk | memory/disk spill, per-task spill, bytes per partition |
SD103 | GC pressure | JVM GC time / task time |
SD104 | Compute-bound stage (little I/O) | I/O MB/s per core, CPU share, rows/s; plan scan for repeated JSON/VARIANT extraction, Python UDFs, regex, very wide rows |
SD105 | I/O-bound stage | largest of input/output/shuffle per core-second |
SD106 | Low parallelism / huge partitions | tasks vs cores, bytes per task |
SD107 | Tiny tasks / scheduling overhead | median task time, scheduler delay |
SD108 | Shuffle fetch wait | fetch wait / task time |
SD201 | Python UDFs | BatchEvalPython / ArrowEvalPython / pandas operators |
SD202 | Repeated JSON/VARIANT parsing | many |
SD203 | Nested-loop / cartesian joins | BroadcastNestedLoopJoin, CartesianProduct |
SD204 | Shuffle join that could broadcast | sort-merge join input sizes |
SD205 | Row explosion | output rows >> input rows (explode, many-to-many joins) |
SD206 | Small files (read and write) | files read/written, average size < 8 MB |
SD207 | Same data read repeatedly | same table / |
SD301 | Driver-side gaps / idle cluster | time with no running jobs, core utilization |
SD302 | Task/stage failures | failure reasons grouped (OOM, fetch failed, lost nodes, exceptions) |
SD303 | Executors lost | remove reasons (OOM, spot, excluding autoscaling) |
SD304 | Many small jobs | job count and median duration |
SD305 | Large results to driver | result size per stage |
SD306 | Heap almost full | peak JVM heap vs |
SD401-405 | Configuration | AQE disabled, fixed shuffle partitions vs data size, Photon off (Databricks), broadcast disabled, low CPU efficiency |
All thresholds live in sparkmedic.analysis.engine.Thresholds and can be tuned.
Report format
See a full example: examples/sample_report.md (and the matching
sample_report.json), built from a real-world pattern: a Databricks
foreachBatch MERGE where 776 variant_get calls per row dominated the runtime.
Markdown: summary, ranked findings (evidence, why it is slow, what to do with code/config snippets, references), longest stages and queries, data collection notes, and a "Confirm before acting" checklist.
JSON (
schema_version: "1.0"): the same content for automation, plus ascoreper finding.Snapshot: the normalized, redacted data behind the report. Share it or re-analyze it later (
sparkmedic analyze snapshot.json).
Try it locally
pip install pyspark "sparkmedic[zstd]"
python examples/demo_slow_job.py --keep-alive 600 # a job with deliberate problems, UI on :4040
sparkmedic analyze http://localhost:4040
sparkmedic analyze ./spark-events # same result from the event logProject layout
src/sparkmedic/
collectors/ rest.py (Spark REST API), eventlog.py (event log replay), databricks.py, targets.py
analysis/ engine.py (rules, findings, thresholds), context.py (derived metrics),
patterns.py (plan scanning), knowledge.py (doc links), rules/*.py
report/ markdown.py, json_report.py
cli.py, mcp_server.py, api.py, model.py (normalized snapshot), http.py, auth.py
tests/ unit + integration tests (fake Spark UI, fake Databricks workspace, recorded fixtures)
examples/ demo_slow_job.py, databricks_notebook.py, sample_report.md/jsonLimitations
The live UI only keeps the most recent stages/tasks (
spark.ui.retained*); use event logs or the History Server for very long applications.Event logs compressed with Spark's
lz4/snappy/lzfframing are not readable here; usespark.eventLog.compression.codec=zstd(the Spark 4 default) or analyze through a History Server.Plan-to-stage mapping uses the stage ids recorded in SQL metrics; when unavailable, the whole query plan is scanned instead (reported as such).
Recommendations are heuristics. Validate changes on a sample and compare outputs before rolling them out.
Security
Read-only GET requests only; see SECURITY.md for reporting vulnerabilities.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only AI coding tools for change verification, release readiness, capacity, and guidance.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Related MCP Servers
- AlicenseCqualityDmaintenanceA read-only MCP server that enables users to query Databricks SQL, browse metadata, and monitor Delta Lake tables. It also supports tracking Databricks Jobs, DLT Pipelines, and cluster metrics through natural language interfaces.254MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants read-only access to Sprinklr data via MCP, allowing querying reports, searching cases, and calling Sprinklr API endpoints.4 npmISC
- AlicenseAqualityCmaintenanceEnables read-only querying of Azure Log Analytics and Azure Resource Graph through MCP, supporting KQL queries, workspace discovery, and resource inventory exploration with Azure RBAC authentication.52MIT
- FlicenseNot gradedqualityBmaintenanceEnables read-only monitoring and root-cause analysis of Azure Data Factory resources through MCP, allowing users to inspect factories, pipelines, and pipeline runs and diagnose failures via natural language.-