Scanning src/ · 6 tools · 3 flagged

A SECURITY LINTER FOR WEBMCP

See what your buttons really do before an agent presses them

Your site is starting to hand actions to AI agents. agentfrisk X-rays your source, finds every WebMCP tool you expose, works out what each one actually runs, and flags the dangerous ones before they ship.

$ npx agentfrisk scan

NEW TO WEBMCP?

What is this, exactly?

A one-minute version for anyone who has not met WebMCP yet.

  1. 01

    Sites now hand tools to AI agents

    WebMCP is a new browser standard. A website registers tools, such as search_products or refund_order, on document.modelContext, and an AI agent in the browser can call them for the user.

  2. 02

    Some of those tools are dangerous

    A tool that deletes an account, moves money or returns a password hash can be called by an agent that misread the page or was steered by injected text. Nothing stops it unless the tool asks the user first.

  3. 03

    agentfrisk checks them before you ship

    Point it at your project. It finds every tool, works out what each one really does, and flags the risky ones with a fix. It runs in your terminal and in CI, and it never executes your code.

Agents will press every button your site hands them.

agentfrisk checks the buttons first.

rules, each with a fix you can paste

ways to define a tool, all understood: imperative, library, forms

lines of your code executed. It is pure static analysis

command to run. Exit code 1 fails the build

Three passes of the scanner. No code runs.

Static analysis on the TypeScript compiler API. It reads, it never executes.

mc.registerTool({ … })
defineTools({ add_todo })
<form toolname="book">

01 · DISCOVER

Find every tool

registerTool on document or navigator, aliases included. nextjs-webmcp and webmcp-react definitions. Forms with toolname in JSX and plain HTML.

02 · CLASSIFY

Judge it by what it does

DB deletes, raw SQL, POST and DELETE requests, Stripe refunds. Followed one call deep across imports. The handler body beats the name.

03 · CHECK

Stamp it, with a fix

Seven focused rules. Readable report, JSON, or SARIF for GitHub code scanning. Exit code 1 when something crosses your line.

GET STARTED

Start in three steps

Node.js 20.19 or later. Nothing to configure for a first run.

  1. STEP 1

    Run it in your project

    $ npx agentfrisk scan

    No install needed. It scans the current folder, skipping node_modules, dist and build output.

  2. STEP 2

    Read the report

    delete_account · destructiveERROR

    Every tool is listed with what it does. Each finding names the rule, explains the risk and gives a fix. Exit code 1 means an error was found.

  3. STEP 3

    Fix it, then guard CI

    $ npx agentfrisk scan . --sarif

    Apply the fix or suppress a finding on purpose. Then upload SARIF in CI so new risky tools show up on pull requests.

THE SCAN SHEET · 7 CHECKS

What the X-ray catches

AGENTFRISK · SCAN REPORTv0.1.0
$ npx agentfrisk scan
src/tools.ts
get_order_status · readCLEARED
delete_account · destructiveERROR
destructive-no-confirm · runs without asking the user
find_user · readWARNING
sensitive-output · returns passwordHash
search_notes · writeWARNING
description-mismatch · fetch POST request
src/legacy.js
list_products · readINFO
deprecated-api · navigator.modelContext only
5 tools · 1 error · 2 warnings · 1 infoexit 1

The scan report

Grouped by file. Every tool, its effect, every finding, and the fix. Same data as JSON or SARIF.

scan [dir]
Report, then exit 1 on findings
list [dir]
Just the tools and their effects
--json
Machine-readable output
--sarif
SARIF 2.1.0 for code scanning
--fail-on
error, warning or info
// agentfrisk-ignore
Suppress a rule on the next tool

CI

An X-ray on every pull request

Upload SARIF and each dangerous tool lands as an annotation on the exact line. The job fails on errors; add --fail-on warning to be stricter.


            
1 check failed Add account tools
21 required: ["userId"],
22+ mc.registerTool({
23+ name: "delete_account",
erroragentfrisk / destructive-no-confirm

Destructive tool "delete_account" runs without asking the user to confirm. Fix: set annotations: { consequentialHint: true }.

24+ execute: ({ userId }) => deleteAccount(userId),
25+ });