Repository navigation
Releases: alefra93/MCPSonar
Releases · alefra93/MCPSonar
Release list
MCPSonar v0.9.0
Added
mcpsonar demo --scenario NAME: deterministic demos told step by step in the
terminal:healthy,poisoning,rug-pull,latency-regression,ci-gate, orall.- Real engine: the local demo servers are steered before each step (latency, clean or
poisoned tools) and the real engine runs on them: the monitor's check pipeline, the
risksview,scanwith a lockfile and SARIF. - Self-checking: each scenario asserts MCPSonar's reaction; exit 1 if one does not
behave, so--scenario all --autois an end-to-end smoke test (run by the test suite). - Pacing: Enter between steps on a terminal;
--autofor no pauses;--json. --dashboard: the dashboard follows every step live (checks go through the
scheduler, like "Check now").- Guides:
docs/demo/: overview, scenarios, speaker guide, troubleshooting.
- Real engine: the local demo servers are steered before each step (latency, clean or
mcpsonar demo --servers: only the three demo MCP servers, with their URLs and
one-linecurlcommands to rug-pull or slow them down. The control endpoint also accepts
a query string (/__demo?tools=poisoned), so the same line works in every shell.- Onboarding docs:
docs/quickstart.md: install, scan and pin, monitor, rug pull, review, CI gate and
doctor in about ten minutes, all local, every command run while writing it;docs/faq.md;docs/troubleshooting.md, including a verified procedure for a forgotten admin
password.
scan --no-color, like every other command.- Slack and Discord notifications (
type: slack/discord): one message per change,
not per check.- What is sent: down or degraded, back up with how long it was out, and new findings.
Episodes from 0.8 mean a slow period is one message. - Hostile text: server-controlled text is stripped of invisible characters and
escaped: no<!channel>or@everyone, no disguised links. URLs must be https and
are masked. - Link to the instance:
MCPSONAR_PUBLIC_URLadds it to each message.
- What is sent: down or degraded, back up with how long it was out, and new findings.
- Email notifications (
type: email): the same rules, through your SMTP server.- TLS: required (STARTTLS that must succeed, or implicit TLS on 465). Plain SMTP only
for a relay on localhost. - Outbound policy: checked before connecting; the connection goes only to an allowed
address, and the certificate is verified against the host name. - Password: masked in every output and restored on edit. It can be a secret
reference. - Tested against a scripted SMTP server with a certificate generated for the test:
STARTTLS, implicit TLS, a server without STARTTLS, an untrusted certificate and a
blocked destination.
- TLS: required (STARTTLS that must succeed, or implicit TLS on 465). Plain SMTP only
- Dashboard:
- Incidents in History: each run of non-up checks with its start, worst status,
duration (or "ongoing") and first reason. It comes from the same function as
mcpsonar history, now shared with the insights API ("incidents"). - Filters: down, degraded, to review, findings, latency, auth required, paused. Only
the filters that match something are shown, and the choice is remembered in the
browser. - When the API can't be reached: a banner says so, keeps the last data, and retries.
A 401 goes to the login page. Before, the page stayed on "Loading…". - Phones: no more horizontal scrolling at 375 px (the top bar, tiles and actions
wrap). - Accessibility:
- every text color reaches WCAG AA (4.5:1) in both themes; the light theme's green,
amber, teal and faint text were below it; - per-server buttons have accessible names ("History · calculator");
- the heartbeat strip has a text equivalent ("Last 40 checks: 37 up, 3 down");
- the tool count is a real button.
- every text color reaches WCAG AA (4.5:1) in both themes; the light theme's green,
- Incidents in History: each run of non-up checks with its start, worst status,
- Accounts and roles:
viewer,operator,admin; admins manage accounts
(/api/users). The first account and every account from earlier versions is an admin;
the last admin can be neither demoted nor deleted. See
Accounts and roles. - Your account: change your password (your other sessions end), list your sessions
(with their address and browser) and end any of them, keep your API tokens private. An
admin resetting a password also ends that person's sessions and tokens. - Audit log (
/api/audit, admins): logins, accounts, tokens, server changes, imports,
exports with secrets and every approval of tool changes. No credentials. It is
append-only in the database itself (triggers refuse UPDATE and DELETE). - Dashboard for teams:
- Per role: each role sees only the controls it can use. Operators see stdio as
"admins only"; viewers get read tokens only. The server still enforces every rule. - Account: change your password, see your sessions (browser, address) and sign them
out. - Users (admins): add accounts, change roles, reset passwords, delete accounts.
- Audit (admins): the log, newest first, with what each approval covered.
- Per role: each role sees only the controls it can use. Operators see stdio as
- Trust Report, for one server, several or the whole instance, over N days (default 30):
- Contents: availability (share of checks answered, incidents, time not up, the
longest incident, latency p50/p95); the pinned tool definitions with SHA-256
fingerprints; every tool change in the window with its review status and who
approved it, when (audit log); the lint findings on the current tools, labeled as
heuristics. - Format: one self-contained page (no script, no external resource), styled for
printing to PDF. It gives no score and no verdict: it records observations. - Where:
GET /api/trust-report(html or json, any role), the dashboard (top bar,
History), andmcpsonar trust-report(renders the instance's JSON with the same code).
- Contents: availability (share of checks answered, incidents, time not up, the
- Schema version 4: user roles, session ids, the audit log. It is migrated
automatically, with a backup. - Recovery for the generic webhook:
on_recovery(default on) sends one POST when a
server is back up from anonstatus.CheckEventgainsprevious_statusand
previous_status_since(live events only).
Changed
- Security: stricter API tokens.
- Lower of scope and role: a token now acts with the lower of its scope and its
owner's current role, and never as an admin. - Owner gone: a token whose owner no longer exists stops working.
- No longer possible with a write token: adding or changing a stdio server's process,
and exporting with secrets. These are admin actions and need a dashboard session. - Visibility: tokens are listed and revoked by their owner (admins see all).
- Lower of scope and role: a token now acts with the lower of its scope and its
- The way back after a latency shift is found sooner. After a reported shift, the next
shift is searched relative to the new level. Before, a short regression followed by a fix
could hide the return to normal, because the strongest split of the window was the
already-reported first change. - Quieter new databases: creating a new database no longer logs each migration step at
INFO; upgrading an existing one still does.
Pin by SHA: alefra93/MCPSonar@9709bef420c877303500ebf55c0c9f9eacdf421f
MCPSonar v0.8.1
Fixed
- Dashboard stuck on "Loading…" after a latency shift. Since 0.8.0 a
latency_shift
carriessince;/api/statusfailed (500) as soon as any server's last anomaly was a
shift, and the whole dashboard stopped loading. Upgrade if you run 0.8.0. - Heartbeat strip of new servers looked empty: the padding slots shared a CSS class with
the empty-state box, so servers with fewer than 40 checks showed no bars. - The "Anomalies" tile counts each episode once: checks marked
ongoingno longer add
to it (they still show in the strip, with "(ongoing)" in the tooltip). - History is ordered deterministically when two checks share a timestamp (coarse clocks
on Windows): insertion order wins. A test depending on it failed intermittently on the
Windows CI runner.
Pin by SHA: alefra93/MCPSonar@5d12fac3813c6fe888912a4d0e880fd39984b818
MCPSonar v0.8.0
Observability
- Where the time goes. Every check now records:
timings_ms:handshake,discovery(everytools/listpage),probesandtotal
(connection set-up included);tool_latency_ms: the latency of each probed tool call.
- Unchanged meaning:
latency_msis still the handshake, so baselines, anomaly detection
and dashboards behave as before. The new fields are additive in the API, webhooks and the
database, through an automatic migration. - Prometheus: new
mcpsonar_check_duration_ms,mcpsonar_phase_latency_ms{phase}and
mcpsonar_tool_latency_ms{tool}. No existing metric was renamed. - CLI:
mcpsonar historyshows the median of each phase and of each probed tool. - Response drift. For each probed tool, MCPSonar remembers the shape of the answer
(error or not, content types, structure ofstructuredContent, order of magnitude of the
size) and reports aresponse_driftanomaly once when it changes.- Response content is never stored.
- Opt-in
drift: hash(withdrift_ignore) also catches value changes on deterministic
probes;drift: offdisables it per probe. - The status is not affected. See Response drift.
Packaging and developer experience
mcpsonar completion bash|zsh|fish|powershell, generated from the CLI's own parser,
so it always matches the installed version; no new dependency.doctorchecks the remote setup: whetherMCPSONAR_TOKENis accepted (scope,
expiry), the instance version against the CLI's, and it refuses to send the token over
plain HTTP to a non-local instance./api/auth/statusreports the version only to an
authenticated caller, and the validity of a bearer token sent with it.- Checked packages. A CI
packagejob builds the wheel and sdist, checks their metadata
and contents (scripts/check_package.py,twine check --strict), installs the wheel into
a clean environment, runs it, and runs it throughuvx. Releases attach both files. - Metadata: Documentation, Changelog and Issues URLs; README links are absolute, so they
work outside GitHub too; the sdist no longer ships the screenshots (7.3 MB to 241 KB).
Secret references
- Header and stdio
envvalues accept${env:NAME}and${file:PATH}, resolved at every
check and never stored, exported or shown resolved.${file:...}is re-read on every
check, so a rotated secret needs no restart. - Allowlisted reads, because configurations also come from the API and shared files:
variables namedMCPSONAR_SECRET_*or listed inMCPSONAR_SECRET_ENV; files inside
/run/secretsorMCPSONAR_SECRETS_DIR(after resolving symlinks and..). - Errors: a malformed or forbidden reference is refused by the API (400) and by config
loading; an unresolvable one makes the checkdownwith the reason, never a value. - Display: references are shown as-is in the dashboard, the API and exports, so an
export with references is complete and safe to share. - See Secret references.
Storage
- Versioned schema. The database records its schema version (
PRAGMA user_version)
and is migrated forward on start by numbered steps, each in its own transaction.
Databases from v0.5, v0.6 and v0.7 are upgraded automatically; tests restore databases
created by each released tag and check that the migrated schema equals a new one. - Backup before migrating:
mcpsonar.db.schema<N>.bakis written next to the database. - No silent downgrade: a database written by a newer mcpsonar is refused (exit 2).
- WAL mode with a separate read-only connection: reads no longer wait for writes.
Back up with SQLite's online backup, not by copying the.dbfile alone (see
Backups). - Atomic writes. Each write runs in one transaction, one writer at a time. Before, a
multi-statement write (replacing a tool snapshot) could be committed halfway by another
task sharing the connection.
Anomaly detection
- Episodes. A run of slow checks is one episode: the first
latency_spikeis notified,
the following ones carryongoing: true(kept in the history, not notified), and the
first normal check reports a newlatency_recoveredanomaly. Generic webhooks with
on_anomalynow fire once per episode plus the recovery, instead of on every slow check.
anomaly.degradekeeps the server degraded for the whole episode. - Baseline poisoning. Slow checks are left out of the baseline while they are a
minority, so repeated bursts no longer inflate it and hide milder spikes. - Sustained regression vs. burst. A latency shift now needs most of the new segment
to sit at the new level: a burst shorter than 8 checks was reported as a shift before.
Once reported, the new level becomes the baseline (withsince, when it started). - Cold start. From the 8th check a provisional baseline flags blatant spikes only
(≥ 2× and 1.5× the sensitivity); the insights API reportssamplesandprovisional. - Zero MAD. When over half the latencies are identical, the mean absolute deviation
stands in for the MAD. - A reproducible scenario corpus (
tests/corpus/anomaly_scenarios.py) pins all of this.
Reports and SARIF
mcpsonar reportfor one server or every server in--config, in Markdown (for an
issue or PR comment), JSON or SARIF 2.1.0. It includes findings with each rule's
documentation, lockfile drift (--lock, read-only) and unreachable servers. Exit 0/1/2
likescan.mcpsonar scan --sarif FILEand asarifinput on the GitHub Action. Upload the file with
github/codeql-action/upload-sarifand findings become code scanning alerts, pointing
at the tool's line in the lockfile.- What the SARIF carries:
- every rule, with its help text,
security-severity,precision(the rule's confidence)
and OWASP MCP Top 10 tags; - lockfile drift as the rule
tool-definition-changed; - stable
partialFingerprintsso alerts follow the same issue across runs. - Checked against the official SARIF 2.1.0 schema.
- every rule, with its help text,
risks --jsonalso carries each finding's confidence and OWASP categories (same function
as the reports).
Detection quality
- New adversarial corpus (
tests/corpus/detection.yaml), run in CI: positive and negative
cases for every lint rule, plus documented known limitations run as strict expected
failures. - Every rule is documented in docs/detection-rules.md with its
severity, confidence (precision), OWASP MCP Top 10 mapping, examples and what to
review. The registry (lint.RULES) is the single source of each rule's severity. - Tuned against the corpus and against the tools of the 24 watched public servers: 8 false
positives on 42 live tools became 0, and the corpus has 0 misses and 0 false positives
(3 documented limitations).prompt-injectionignores CLI flags and parameters (--ignore,override=true).sensitive-path: private keys,.envfiles and MCP client configs count only when the
text asks to read or send them. "Add this server to your mcp.json" is setup, not an
attack.exfiltrationneeds data (conversation, files, keys, …) heading to the destination, so
"Send a POST request to https://api…" no longer matches.coercive-instructionneeds "always / first / also": "You must provide a city name" and
API ordering notes no longer match.hidden-unicodeignores the zero-width joiner inside emoji sequences.fake-system-tagalso catches[SYSTEM]/[IMPORTANT].encoded-blobignores long URLs.long-descriptionthreshold raised from 1,500 to 4,000 characters (real descriptions
reach ~3,300).
Pin by SHA: alefra93/MCPSonar@78ba109a07a4eb7bae0d20fc8f0484bf3bf5622a
MCPSonar v0.7.0
Added
- CLI commands
tools,risks,config validate|show|export,doctorandversion
(plus--version). They work without a running instance, against a URL,--stdioor a
server name from a YAML file. Every command supports--format json,NO_COLOR/
--no-color, results on stdout and diagnostics on stderr, and stable exit codes (0/1/2).
See docs/cli.md. - A rule registry (
lint.RULES): every finding's rule ID now has a title, an explanation
and a remediation. - API tokens for the CLI and automation: created and revoked in the dashboard (new API
tokens dialog). They are scoped read (GET only) or write, can expire, are stored
hashed and shown once, and can't manage tokens. They are sent as
Authorization: Bearer mcps_…. - Remote-mode CLI commands that work against a running instance (
MCPSONAR_URL,
MCPSONAR_TOKEN), through the same API as the dashboard and never through its database:status;servers list|show;check NAME|--all, run by the instance;diff NAME: changes marked pending, SUSPICIOUS or APPROVED; never approves anything;history NAME --last 24h: uptime, latency percentiles, baseline, shift, incidents;servers add|edit|pause|resume|remove.--header-from-envkeeps credentials out of
shell history.editchanges only the given options and keeps credentials and probe
arguments.removeasks for confirmation on a terminal and needs--yesotherwise.
GET /api/servers/{id}/tool-changesincludes the lintfindingsof each new definition.
Protocol
- MCP Python SDK 2.x (
mcp>=2.3,<3) and spec revision 2026-07-28. One client speaks both
protocol eras:server/discoverfor 2026-07-28 servers, falling back toinitializefor
2024-11-05 … 2025-11-25 servers. Servers that only speak 2026-07-28 used to be reported
down. 5 of the 24 public servers inexamples/popular-servers.yamlalready speak it. - New validator for 2026-07-28.
spec_versionreports the negotiated revision. - Unchanged:
- Tool fingerprints: identical to mcp 1.x, with golden values tested and 42/42 tools
checked on live servers. Stored baselines and lockfiles are re-normalized, so annotation
keys 2.x no longer represents don't count as changes. - 401/403/429 handling: auth required, credentials rejected, rate limited +
Retry-After. - Latency continuity: HTTP times only the RPC that succeeded; stdio includes process
startup, as before. A 401/403/429 on the discover probe ends negotiation at once, so
protected servers cost one request per check, as before.
- Tool fingerprints: identical to mcp 1.x, with golden values tested and 42/42 tools
httpxis replaced byhttpx2, the stack mcp 2.x uses, so the outbound policy covers one HTTP
stack. On Windows and macOS, TLS uses the operating-system trust store, so corporate CAs work
without extra configuration.- "down" details now include the HTTP status when there was one (e.g.
(HTTP 503)).
Changed
- The dashboard's Tools view,
mcpsonar scanand the new commands share one inspection
service. As a resultscannow has an overall time budget too. - One-shot commands log only warnings;
runanddemoare unchanged. scanalso accepts--format json.
Fixed
--stdiowith quoted paths on Windows (e.g."C:\Program Files\nodejs\npx.cmd"): the
quotes were kept as part of the path, so the command could not be found.
Pin by SHA: alefra93/MCPSonar@a40ab6486e2cd1cbb10ce17cbb0c2f6a1fbb2919
MCPSonar v0.6.0
Security hardening release. Details, evidence and tests for each item:
docs/engineering-audit.md. Upgrade notes:
docs/configuration.md.
Security
- Critical: blocked a drive-by remote command execution. A page on any website could POST
YAML to/api/importon an--no-authinstance (includingmcpsonar demoand the watch
compose) and make MCPSonar launch an arbitrary stdio command. State-changing requests from
other sites or origins are now refused, and with--no-auth, stdio servers can't be added
over the API unless--allow-stdio-apiis given. Hostallowlist (--allowed-host; loopback-only by default with--no-auth) against
DNS rebinding.- First-admin setup requires a one-time token printed in the log (or
MCPSONAR_SETUP_TOKEN); concurrent setups can no longer create two admins. - Server-side sessions, revoked on logout and stored as hashes;
Securecookie over HTTPS
(--cookie-secure). - Login throttling; PBKDF2 raised to 600k iterations and moved off the event loop (old hashes
are upgraded at the next login). - Credentials (headers,
env, URL keys and userinfo, webhook and push URLs) are masked in API
responses, the dashboard and exports, and scrubbed from check details, webhook and Kuma
messages, logs andscanoutput. Editing a server keeps its stored credentials. - Outbound policy enforced at connect time: cloud metadata and link-local addresses blocked;
MCPSONAR_BLOCK_PRIVATE_NETWORKS,MCPSONAR_OUTBOUND_ALLOW/_DENY. Configured headers are
no longer forwarded on cross-origin redirects; at most 5 redirects. - Security headers (no framing, CSP, nosniff, no-referrer); 1 MiB body limit; bounded history
queries; optionalMCPSONAR_METRICS_TOKENfor/metrics; http(s)-only URLs. - The container runs as an unprivileged user with no capabilities and has a HEALTHCHECK.
pyjwt>=2.15andcertifi>=2026.10.19(transitive dependencies with known advisories, GHSA-6q48-6c7w-9gw2 for certifi); CI runspip-audit.
Fixed
tools/listpagination is followed. Only the first page used to be checked, pinned and
linted.- A stdio server that never answers can no longer hang its check loop: session read timeouts
plus an overall check budget. - Probes never call a tool the server doesn't list, and skip tools annotated destructive
unlessallow_destructive: true; new per-probetimeout_seconds. - Duplicate tool names are reported; reordering a schema's
requiredlist is no longer a
"tool change". - Concurrent checks of the same server (Check now + loop) could record tool changes twice.
- Editing a server in the dashboard dropped its probes' arguments.
Changed
GET /api/exportis masked by default (?include_secrets=truefor a full export); import
refuses a masked export.- Everyone signs in once more after upgrading (sessions moved server-side).
- CI runs on Linux, Windows and macOS and smoke-tests the Docker image; Dependabot enabled.
Pin by SHA: alefra93/MCPSonar@5cd37eac7267e36556a3a6081d9c2847ba25fabd
MCPSonar v0.5.0
First tagged release.
Added
- Rug-pull detection: every server's tool definitions are pinned on first sight; added,
removed or changed tools are recorded and (by default) keep the server degraded until a
human reviews the diff in the dashboard. - Tool-poisoning lint: hidden Unicode, instruction overrides, fake
<IMPORTANT>blocks,
"don't tell the user", credential paths, exfiltration, tool shadowing, suspicious
parameters. - Anomaly detection with a learned per-server baseline: latency spikes (robust z-score),
level shifts (change point) and flapping. mcpsonar scan: one-shot scan with a tool lockfile for CI, plus a GitHub Action.mcpsonar demo: dashboard against four scripted demo servers.examples/popular-servers.yaml+docker-compose.watch.yml: watch 24 hosted MCP servers.- Validators for MCP spec 2024-11-05, 2025-03-26 and 2025-06-18 (besides 2025-11-25).
Changed
- OAuth-protected servers (401/403 to an anonymous client) are up · auth required
instead of down; rejected credentials are down; 429 is degraded (rate limited).
Fixed
- Latency was inflated when several servers were checked at once (a TLS context was rebuilt
on every check). - Pinned
mcp>=1.28,<2: the 2.x SDK is a breaking rewrite and broke every check on a fresh
install. - Source files contained literal zero-width and bidi-override characters (in the lint's own
regex and two test fixtures). They are now written as Unicode escape sequences, and CI
rejects any invisible character in tracked files. - ruff pinned to an exact version so CI and local linting agree.
- Pausing, editing or deleting a server while one of its checks was in flight could hang
forever: the MCP SDK can turn a cancellation into an ordinary error, which was recorded as
"down" and the check loop kept running. Cancellation is now always propagated.
Pin by SHA: alefra93/MCPSonar@742f3ceab3a658f6943a7c358e12318ddedd9bfa