Skip to content

Releases: alefra93/MCPSonar

MCPSonar v0.9.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 09:45
9709bef

Added

  • mcpsonar demo --scenario NAME: deterministic demos told step by step in the
    terminal: healthy, poisoning, rug-pull, latency-regression, ci-gate, or all.
    • 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
      risks view, scan with a lockfile and SARIF.
    • Self-checking: each scenario asserts MCPSonar's reaction; exit 1 if one does not
      behave, so --scenario all --auto is an end-to-end smoke test (run by the test suite).
    • Pacing: Enter between steps on a terminal; --auto for 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.
  • mcpsonar demo --servers: only the three demo MCP servers, with their URLs and
    one-line curl commands 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_URL adds it to each message.
  • 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.
  • 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.
  • 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.
  • 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), and mcpsonar trust-report (renders the instance's JSON with the same code).
  • 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 an on status. CheckEvent gains previous_status and
    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).
  • 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

Choose a tag to compare

@github-actions github-actions released this 22 Oct 07:03
5d12fac

Fixed

  • Dashboard stuck on "Loading…" after a latency shift. Since 0.8.0 a latency_shift
    carries since; /api/status failed (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 ongoing no 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

Choose a tag to compare

@github-actions github-actions released this 21 Oct 22:20
78ba109

Observability

  • Where the time goes. Every check now records:
    • timings_ms: handshake, discovery (every tools/list page), probes and total
      (connection set-up included);
    • tool_latency_ms: the latency of each probed tool call.
  • Unchanged meaning: latency_ms is 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 history shows 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 of structuredContent, order of magnitude of the
    size) and reports a response_drift anomaly once when it changes.
    • Response content is never stored.
    • Opt-in drift: hash (with drift_ignore) also catches value changes on deterministic
      probes; drift: off disables 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.
  • doctor checks the remote setup: whether MCPSONAR_TOKEN is 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/status reports the version only to an
    authenticated caller, and the validity of a bearer token sent with it.
  • Checked packages. A CI package job 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 through uvx. 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 env values 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 named MCPSONAR_SECRET_* or listed in MCPSONAR_SECRET_ENV; files inside
    /run/secrets or MCPSONAR_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 check down with 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>.bak is 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 .db file 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_spike is notified,
    the following ones carry ongoing: true (kept in the history, not notified), and the
    first normal check reports a new latency_recovered anomaly. Generic webhooks with
    on_anomaly now fire once per episode plus the recovery, instead of on every slow check.
    anomaly.degrade keeps 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 (with since, 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 reports samples and provisional.
  • 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 report for 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
    like scan.
  • mcpsonar scan --sarif FILE and a sarif input on the GitHub Action. Upload the file with
    github/codeql-action/upload-sarif and 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 partialFingerprints so alerts follow the same issue across runs.
    • Checked against the official SARIF 2.1.0 schema.
  • risks --json also 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-injection ignores CLI flags and parameters (--ignore, override=true).
    • sensitive-path: private keys, .env files 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.
    • exfiltration needs data (conversation, files, keys, …) heading to the destination, so
      "Send a POST request to https://api…" no longer matches.
    • coercive-instruction needs "always / first / also": "You must provide a city name" and
      API ordering notes no longer match.
    • hidden-unicode ignores the zero-width joiner inside emoji sequences.
    • fake-system-tag also catches [SYSTEM] / [IMPORTANT].
    • encoded-blob ignores long URLs.
    • long-description threshold raised from 1,500 to 4,000 characters (real descriptions
      reach ~3,300).

Pin by SHA: alefra93/MCPSonar@78ba109a07a4eb7bae0d20fc8f0484bf3bf5622a

MCPSonar v0.7.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 13:01
a40ab64

Added

  • CLI commands tools, risks, config validate|show|export, doctor and version
    (plus --version). They work without a running instance, against a URL, --stdio or 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-env keeps credentials out of
      shell history. edit changes only the given options and keeps credentials and probe
      arguments. remove asks for confirmation on a terminal and needs --yes otherwise.
  • GET /api/servers/{id}/tool-changes includes the lint findings of 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/discover for 2026-07-28 servers, falling back to initialize for
    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 in examples/popular-servers.yaml already speak it.
  • New validator for 2026-07-28. spec_version reports 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.
  • httpx is replaced by httpx2, 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 scan and the new commands share one inspection
    service. As a result scan now has an overall time budget too.
  • One-shot commands log only warnings; run and demo are unchanged.
  • scan also accepts --format json.

Fixed

  • --stdio with 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

Choose a tag to compare

@github-actions github-actions released this 21 Oct 07:12
5cd37ea

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/import on an --no-auth instance (including mcpsonar demo and 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-api is given.
  • Host allowlist (--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; Secure cookie 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 and scan output. 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; optional MCPSONAR_METRICS_TOKEN for /metrics; http(s)-only URLs.
  • The container runs as an unprivileged user with no capabilities and has a HEALTHCHECK.
  • pyjwt>=2.15 and certifi>=2026.10.19 (transitive dependencies with known advisories, GHSA-6q48-6c7w-9gw2 for certifi); CI runs pip-audit.

Fixed

  • tools/list pagination 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
    unless allow_destructive: true; new per-probe timeout_seconds.
  • Duplicate tool names are reported; reordering a schema's required list 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/export is masked by default (?include_secrets=true for 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

Choose a tag to compare

@github-actions github-actions released this 14 Oct 23:06
742f3ce

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