Schedule icon
WorkingDirectory icon
Script icon
Process icon
If icon
Parallel icon
Create icon
SlackIncomingWebhook icon

Security-Test Your MCP Server with a Regression Harness

Scheduled security harness for MCP servers: tool-definition poisoning and rug-pull checks, sandboxed traversal and injection probes, tool-result injection scanning, scorecard gate, GitHub issues, and Slack alerts.

Categories
AI

MCP servers are becoming the hands of production AI agents, and their tool definitions are an attack surface. A tool description can be poisoned with instructions for the agent, a definition can change silently after approval (the classic rug pull), a tool can quietly accumulate permissions, and the server itself can accept traversal paths, injected commands, or oversized payloads while its tool results smuggle instructions back to the agent. This blueprint regression-tests an MCP server the way CI tests code: on a schedule and on every change, with a scored gate.

How it works

  1. weekly_mcp_security (io.kestra.plugin.core.trigger.Schedule) runs the harness every Wednesday 08:00 UTC. For an on-demand run, for example right after a server release, execute the flow from the UI or the API.
  2. load_server_config (io.kestra.plugin.scripts.python.Script) resolves the server configuration (inline JSON, URL, or the bundled demo server), validates the tool definitions, and hashes each one. The configuration also carries baseline_tools, the pinned snapshot of the definitions as they were approved.
  3. static_checks (Python) screens every description for poisoning patterns, compares each definition hash against the pinned baseline to catch silent changes, and flags over-broad permission sets.
  4. dynamic_probes (Python) writes a bundled stub MCP server (JSON-RPC over stdio) and a minimal client into the working directory, launches the target server, and fires the probe battery: path traversal (plain and URL-encoded), command injection, SQL injection, an oversized write payload, and a cloud metadata request. A probe passes only when the server rejects the call or answers without the dangerous content.
  5. result_injection_checks (Python) calls the content-bearing tools with benign requests and scans every tool result for instruction-like content planted for the agent: fake system turns, override phrases, and covert forwarding instructions.
  6. build_scorecard (Python) aggregates all three batteries into scorecard.json and scorecard.md: per-category pass rates, the overall security score, the gate verdict, and the trend against the previous run's scorecard.
  7. write_trend_record (Python) appends the run to trend.jsonl, keeping the posture auditable over time.
  8. triage (io.kestra.plugin.core.flow.If) branches on the gate: escalate (io.kestra.plugin.core.flow.Parallel) opens the GitHub issue and pages Slack together, while a passing run goes to record_passing_run.

What you get

  • Static coverage of the MCP-specific definition attacks: description poisoning, rug-pull drift against a pinned baseline, and permission creep.
  • Dynamic proof, not just linting: the server is actually launched and probed in a sandbox, and the bundled stub makes the whole flow runnable with no external services.
  • Tool-result injection scanning, the indirect prompt injection path most test suites miss.
  • A scored gate with a trend, so a server that gets worse between releases fails loudly instead of drifting.

Who it's for

  • Platform and AI security teams shipping MCP servers to production agents.
  • Server authors who want a pre-deployment security check they can run in CI or on a schedule.
  • Governance teams who need evidence that tool definitions have not changed since approval.

Why orchestrate this with Kestra

A test script can fire probes, but it cannot schedule the battery, keep the configuration, findings, and scorecard in one auditable place, branch on the gate, and escalate in parallel. Kestra's schedule trigger makes the cadence declarative, the working directory keeps every stage reading the same artifacts, and every execution is a timestamped record of the server's security posture. Retries and secret masking come free.

Prerequisites

  • A server configuration JSON (current tools plus the pinned baseline_tools snapshot), or use the bundled demo server to see the harness work end to end.
  • To probe a real server: a command that starts it with JSON-RPC over stdio. With server_command empty, the bundled stub is used; set stub_mode to vulnerable to watch the harness catch a bad server.
  • A GitHub token with issue-write scope on the tracking repository.
  • A Slack incoming webhook URL for the security channel.

Secrets

  • GITHUB_TOKEN: token used to open the tracking issue (required).
  • SLACK_WEBHOOK_URL: incoming webhook for the security-channel alert (required).

Quick start

  1. Add the two secrets above to your Kestra namespace.
  2. Import this blueprint and run it as-is: the bundled demo server carries a poisoned tool description and a silently changed definition, so the static battery fails the gate and you see the full escalation path.
  3. Point server_config_url and server_command at your own server, pin its approved definitions as baseline_tools, and leave the weekly schedule enabled.

How to extend

  • Grow the probe battery with resource-endpoint probes (resources/read traversal) and prompt-template checks as your servers expose them.
  • Add a second stub persona per server vendor to catch behaviour differences.
  • Write the scorecard to a warehouse table for a security posture dashboard.
  • Gate deployments: fail the release pipeline when the score drops below threshold or the trend verdict is regressed.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.