Scan, Fix, Verify Is Only as Good as the Evidence: Wiring a DAST MCP Server Into Your Coding Agent

A DAST MCP server exposes dynamic application security testing to AI coding agents through the Model Context Protocol: the agent can discover an application's API from source code, launch an authenticated scan against the running app, and read the runtime results back as structured data it can act on. The agent drafts the fix. A human approves the merge.

In Autonomous Penetration Testing: A CISO Reality Check, we argued that this division of labor is the durable one: autonomous penetration testing has an evidence problem, not an autonomy problem. This post is the practical half. Here is how you actually wire it.

Key takeaways

  • Your coding agent can already write and "fix" code confidently. What it lacks is ground truth about the running application. A DAST MCP server supplies it.
  • NightVision's API discovery runs as deterministic, local source analysis. No code is sent to an LLM, and the same code produces the same spec every run.
  • Because the API spec is derived from the same source tree the agent has open, an injection or XSS finding comes back pointing at the handler that declared the route, so the agent starts at the right file instead of guessing.
  • The loop closes with a re-scan against the live app and a human approving the pull request. Determinism on the finding, agents on the fix, humans on the merge.

The hole in the scan-fix-verify loop

Every AI coding workflow converges on the same shape: scan the code, let the agent fix what was found, verify the fix. The step nobody scrutinizes is the first one and the last one. If the scan is static analysis alone, the agent is fixing findings that may not be reachable in the running app. If the verification is the agent re-reading its own diff, the agent is grading its own homework.

Dynamic testing closes both gaps, because it exercises the application the way an attacker would: over the network, against the live app, through the real authentication flow. The problem has always been that DAST results lived in a dashboard a human had to read. The Model Context Protocol changes that. MCP is the open standard that lets AI applications call external tools and read external data, and it is how your agent gets DAST results as something it can parse, reason about, and act on instead of a PDF someone forwards.

Step 1: Wire the server

Prerequisites: the NightVision CLI 0.5.0 or later, installed, authenticated, and on your PATH; Node.js 22 or later; and a NightVision account (the trial is self-serve). The MCP server drives the CLI, so the CLI is what does the work.

Clone and build the MCP server, then register it with your agent:

git clone https://github.com/nvsecurity/nightvision-mcp.git
cd nightvision-mcp && npm install && npm run build
{
  "mcpServers": {
    "nightvision": {
      "command": "node",
      "args": ["/path/to/nightvision-mcp/build/index.js"],
      "env": {
        "NIGHTVISION_CLI_PATH": "/path/to/nightvision"
      }
    }
  }
}

That block works in any MCP-compatible client, including Claude Code, Claude Desktop, and Cursor. NIGHTVISION_CLI_PATH is optional when the client can already find nightvision on your PATH, but set it for desktop clients launched outside a login shell: that is the usual reason a freshly wired server comes up unable to see the CLI. From here on, everything below is a tool call the agent makes on its own inside the conversation.

Step 2: Discover the API from source

Ask the agent to run API discovery against the repo it already has open. NightVision analyzes the source directly and emits an OpenAPI specification of the routes it finds, including the endpoints nothing links to: the deprecated admin route, the internal-only handler, the endpoint a crawler alone would never reach.

Two properties of this step matter for everything that follows. It is deterministic: the same code produces the same spec on every run, so you can diff it, gate on it, and trust it in CI. And it is local static analysis, not an LLM pass: your source never leaves the machine to be summarized by a model.

Source discovery reads C#, Go, Java, JavaScript and TypeScript, PHP, Python, and Ruby, and how much it extracts depends on the framework: it is strongest where routes are declared through a router or annotations (Spring, ASP.NET Core, Django and DRF, Flask, FastAPI, Express, NestJS, Laravel, Rails, Gin). Hand-rolled routing gives it less to read. If discovery comes back empty, that is worth knowing before you scan rather than after: run the scan anyway against the URL, and you get the same runtime findings without the source line attached to them.

The spec is also the join key for the whole workflow. Every operation in it exists because a specific handler in a specific file declared that route. The agent holding the source tree knows exactly which endpoint belongs to which controller.

Step 3: Scan the running application

Steps 2 and 3 are really one call. The MCP server exposes a guided harness that runs the whole sequence in order: preflight the app, run API discovery against the source, create or update the target with the freshly derived spec, and start the scan. Point it at the app's source directory and the URL it is serving on, and it returns a scan ID.

Ask for the pieces separately and you can get a scan that quietly runs against last month's spec, because nothing forced discovery to re-run first. Routing through the one call is what keeps the spec and the scan in sync, and it is the difference between a scan that tests your current routes and one that tests the routes you used to have.

The URL can be a local build, a staging deployment, or a CI environment. Localhost is the interesting case: the scan reaches a private app over a relay the CLI opens, so an app on localhost:9000 that is not exposed to the internet is testable from the same conversation where you are writing it. Scan status is a tool call too, so the agent polls, waits, and moves on when the scan completes, no dashboard required.

Authentication is the part worth getting right. For anything with a real login, expiring sessions, OAuth, or MFA, hand the scan a Playwright login script rather than a captured cookie or bearer token, and the scanner replays the actual login flow at scan time instead of running with a credential that went stale an hour in. A scan that silently failed to log in still reports success; it just tests your login page over and over.

Step 4: Read the evidence back

When the scan finishes, the agent can pull the whole record through MCP, not just the headline.

The check results are the first piece: every test that ran, passed or failed, with severity and a link to the public definition of the check that produced it. Ask for failures and you get the short list; ask for everything and you get the full ledger. In a scan of our deliberately vulnerable Java Spring demo app, that ledger held close to 6,900 passing checks against 19 failures, which is a far more useful answer to "what did you actually test" than a number of findings.

The second piece is the tested surface: the complete list of paths and methods the scanner exercised, with the response codes it saw. The same scan came back with 144 distinct paths, anchored on the route set the spec derived from source. This is the fastest way to catch a scan that passed for the wrong reason. Fifteen paths on an app you know has two hundred endpoints means the spec was stale or the login failed, and no amount of green changes that.

The third piece is per-finding detail: the full HTTP request and response behind a given result, with secrets redacted, so the agent reads the exact payload that triggered it. And the whole scan exports to SARIF, which drops the findings into GitHub code scanning and hands the agent back the source-linked ones with their file and line already resolved.

A failing check is not a model's opinion about the code. It is a specific request that produced a specific response from your running application, from a check definition you can open and read.

Step 5: Fix at the source, verify at runtime

Now the join pays off, and it is worth being precise about how far it carries.

An injection or XSS finding arrives with an endpoint, the vulnerable parameter, and the payload that proved it, plus a source location. That location is the entry point, the handler the request came through, which is not always where the bug lives. It is a starting line, not an X on a map: the agent opens that handler, follows the named parameter into the service or repository that consumes it, and fixes the sink. That is still a categorically better starting position than a severity label and a URL, and it is exactly the kind of tracing an agent is good at.

Other findings do not work that way at all. A missing security header or a weak authentication method has no controller to fix; it belongs in the app's security configuration, and the line such a finding reports, if it reports one, points at wherever the response happened to be observed. NightVision does not hand the agent a patch. It hands it a located, reproducible fact and lets the agent do the code work.

Then the loop closes the only way that counts: the agent re-scans the running app and confirms the finding no longer reproduces. For a bug you never want back, the agent can author a custom check for it and assign it to the target through the same MCP server, a permanent regression test at the scanning layer.

The last step belongs to a person. The agent opens the pull request with the runtime evidence attached: what failed, what changed, what the re-scan proved. A human reviews and merges.

Who does what

LayerResponsibility
NightVisionDeterministic evidence: source-derived API spec, authenticated runtime scan, reproducible check results with request and response, exportable as SARIF
Coding agentCode context: traces a finding from its entry point to the sink, drafts the fix, re-runs the verification
HumanJudgment: reviews the evidence, approves the merge

Notice what is deliberately absent: nothing in this workflow asks the agent to decide what is vulnerable. The evidence layer is deterministic precisely so the agent's creativity is spent on the fix, where it belongs. This is what we mean by agent-ready, and why it is not the same thing as autonomous.

Related readingFor the argument behind this workflow, read Autonomous Penetration Testing: A CISO Reality Check. For a framework-level look at where the spec in step 2 comes from, see ASP.NET Core API Security Testing: Start From the Source, Not the Swagger File. For where DAST fits in a broader program, see our guide to API and application security testing.

Frequently asked questions

What is a DAST MCP server?

An MCP server that exposes dynamic application security testing capabilities (API discovery, scan orchestration, and scan results) as tools an AI agent can call through the Model Context Protocol, so runtime security evidence becomes structured data inside the agent's context.

Which coding agents does this work with?

Any MCP-compatible client, including Claude Code, Claude Desktop, and Cursor. The configuration is the same JSON block in each. Clients launched outside a login shell may also need NIGHTVISION_CLI_PATH set so the server can find the NightVision CLI.

Does API discovery send my source code to an LLM?

No. NightVision's API discovery is deterministic, local static analysis. It reads your code on your machine and emits an OpenAPI spec. The same code produces the same spec every run.

How do runtime findings map back to source?

The API spec is derived from the source tree your agent already has open, so an endpoint in a finding corresponds to a route a specific handler declared. Request-level findings such as injection and cross-site scripting carry that location, plus the vulnerable parameter and the payload that proved it. Treat it as the entry point rather than the fix site: the agent follows the parameter from there into the code that consumes it. Configuration-level findings such as missing headers or weak authentication have no handler to point at and are fixed in the app's security configuration.

Is this autonomous penetration testing?

No, and that is deliberate. The scan is deterministic, the agent drafts fixes it can verify against the running app, and a human approves every merge. We wrote up why we think that division of labor is the durable one.

Does this replace a penetration test?

No. It gives your team and your agents continuous runtime evidence between tests, and it makes the findings a pentest does produce easier to reproduce and fix. For where DAST fits in a broader program, see our application security testing guide.

Put runtime evidence inside your agent.

If your developers live in Claude Code or Cursor, the fastest way to see this loop run is on your own application.