Arachne docs Home Open app

MCP server

Tools reference

The eight Arachne MCP tools: input schemas, the scope and role each needs, which are metered, and a worked JSON-RPC request and response for every one.

All tools are called with tools/call. Arguments are checked before anything runs: types, sizes, required fields, and no unknown keys (additionalProperties: false). Ids are UUIDs. The examples use obviously fake ids; replace them with yours. Every successful result is a JSON document in result.content[0].text, shown below already decoded.

ToolToken scopeRole must allowCost
advisories_latestmcp:advisories.readfindings:readfree
advisories_searchmcp:advisories.readfindings:readfree
advisory_explainmcp:advisories.readfindings:readfree
repos_affectedmcp:repos.readprojects:readfree
propose_versionmcp:repos.readprojects:readfree
job_statusmcp:repos.readjobs:readfree
fix_submitmcp:fix.submitjobs:submitreserves 1 use
pr_openmcp:pr.openpr:open1 PR = 1 use

With a personal access token the token scopes are derived from your role, so the "role must allow" column is the one that matters: owners and admins get all eight, members everything except pr_open, viewers the six read tools.

advisories_latest#

Newest advisories in Arachne's database, ordered by (modified, id). Without a cursor you get the newest limit; pass the returned next_cursor back as since_cursor to poll for anything newer.

ArgumentType
since_cursorstringoptional; from a previous next_cursor
ecosystemstringoptional; e.g. PyPI, npm, Maven, crates.io, Go
limitinteger 1–100optional; default 20
{"jsonrpc":"2.0","id":10,"method":"tools/call",
 "params":{"name":"advisories_latest","arguments":{"ecosystem":"PyPI","limit":2}}}
{"items":[
  {"id":"GHSA-xxxx-xxxx-xxxx","aliases":["CVE-2026-00000"],"summary":"Example summary","severity":"HIGH",
   "modified":"2026-11-13T09:12:00Z","ecosystems":["PyPI"],"packages":["example-pkg"],"kev":false,"epss":0.0012},
  {"id":"GHSA-yyyy-yyyy-yyyy","aliases":[],"summary":"Another example","severity":"MODERATE",
   "modified":"2026-11-14T07:40:00Z","ecosystems":["PyPI"],"packages":["other-pkg"],"kev":false,"epss":0.0}],
 "next_cursor":"WyIyMDI2LTExLTE0VDA3OjQwOjAwWiIsIkdIU0EteXl5eS15eXl5LXl5eXkiXQ"}

Look an advisory up by id or alias (CVE, GHSA or OSV id), or list a package's advisories by ecosystem + package. With a version, it says whether that version is affected, drops advisories that do not affect it, and lists the fixed versions.

ArgumentType
querystringan advisory id or alias; or give ecosystem + package instead
ecosystem, packagestringtogether
versionstringoptional; evaluates affected
limit, cursorinteger 1–100, stringoptional paging
{"jsonrpc":"2.0","id":11,"method":"tools/call",
 "params":{"name":"advisories_search","arguments":{"ecosystem":"PyPI","package":"requests","version":"2.30.0"}}}
{"items":[
  {"id":"GHSA-j8r2-6x86-q33q","aliases":["CVE-2023-32681"],
   "summary":"Unintended leak of Proxy-Authorization header in requests","severity":"MODERATE",
   "modified":"…","ecosystems":["PyPI"],"packages":["requests"],"kev":false,"epss":0.06,
   "affected":true,"fixed_versions":["2.31.0"],"fix_state":"fixed"}],
 "next_cursor":null}

fix_state is fixed when at least one fixed version is known, otherwise no_fix_known. affected is null when no version was given. Values such as severity, modified and epss above are illustrative.

advisory_explain#

One advisory in full: impact line (severity, KEV, EPSS), affected ranges per package, fix guidance, references (up to 20), details (up to 4000 characters), source and licence. With project_id it adds that project's recorded findings for the advisory. The advisory part is cached and shared; your project findings are fetched per call and never cached.

ArgumentType
advisory_idstringrequired; id or alias
project_idstring (UUID)optional
{"jsonrpc":"2.0","id":12,"method":"tools/call",
 "params":{"name":"advisory_explain","arguments":{"advisory_id":"GHSA-j8r2-6x86-q33q",
   "project_id":"11111111-2222-4333-8444-555555555555"}}}
{"advisory_id":"GHSA-j8r2-6x86-q33q","aliases":["CVE-2023-32681"],
 "summary":"Unintended leak of Proxy-Authorization header in requests","details":"…","severity":"MODERATE",
 "affected":[{"ecosystem":"PyPI","package":"requests",
   "ranges":[[{"introduced":"2.3.0"},{"fixed":"2.31.0"}]],"fixed_versions":["2.31.0"]}],
 "fix_guidance":["PyPI/requests: upgrade to 2.31.0 or later"],
 "references":["https://github.com/psf/requests/security/advisories/GHSA-j8r2-6x86-q33q", "…"],
 "source":"…","license":"…",
 "impact":"Severity MODERATE. Not in CISA KEV. EPSS 0.060.","kev":false,"epss":0.06,
 "generated_by":"template","cached":true,
 "project":{"project_id":"11111111-2222-4333-8444-555555555555",
   "findings":[{"package":"requests","version":"2.30.0","fix_state":"fixed","fixed_versions":["2.31.0"]}],"note":null}}

If the project has no recorded finding, findings is empty and note suggests running repos_affected on it first.

repos_affected#

Scans your organisation's connected projects now (clone, read manifests, match) and records the findings. It scans a few projects per call: up to limit (1–5, default 5) per page, or exactly one with project_id. One project failing does not hide the others; it appears in errors.

ArgumentType
project_idstring (UUID)optional; scan only this project
kev_onlybooleanoptional; only findings in CISA KEV
limitinteger 1–5optional; projects per page
cursorstringoptional; the previous next_cursor
{"jsonrpc":"2.0","id":13,"method":"tools/call","params":{"name":"repos_affected","arguments":{}}}
{"items":[
  {"project_id":"11111111-2222-4333-8444-555555555555","repo":"aadhi0612/arachne-demo",
   "package":"requests","version":"2.30.0","direct":true,"advisory":"GHSA-j8r2-6x86-q33q",
   "summary":"Unintended leak of Proxy-Authorization header in requests","fix_state":"fixed",
   "fixed_versions":["2.31.0"],"kev":false,"epss":0.06,"source":"…","license":"…"}],
 "errors":[{"project_id":"66666666-7777-4888-9999-000000000000","repo":"example-org/private-repo","error":"scan_failed"}],
 "projects_scanned":2,"next_cursor":null}

Note

scan_failed usually means the repository could not be cloned (private, renamed, or deleted). See Troubleshooting.

propose_version#

The deterministic resolver's answer for one package in one project: the lowest version that fixes every known advisory, is old enough to pass the cooldown (7 days by default), and is not a major bump unless approved, with every rejected candidate and the reason. It does not change anything.

ArgumentType
project_idstring (UUID)required
packagestringrequired; as declared in the project
{"jsonrpc":"2.0","id":14,"method":"tools/call",
 "params":{"name":"propose_version","arguments":{"project_id":"11111111-2222-4333-8444-555555555555","package":"requests"}}}
{"package":"requests","ecosystem":"PyPI","current":"2.30.0","status":"fix_found","chosen":"2.31.0",
 "major_bump":false,"advisories":["GHSA-j8r2-6x86-q33q"],"rejected":[],"reason":"…","notes":[]}

status is one of fix_found, no_fix_known, no_safe_candidate, not_vulnerable.

job_status#

Where a fix job is and what it found.

ArgumentType
job_idstring (UUID)required
{"jsonrpc":"2.0","id":15,"method":"tools/call",
 "params":{"name":"job_status","arguments":{"job_id":"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"}}}
{"job_id":"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee","project_id":"11111111-2222-4333-8444-555555555555",
 "status":"done","outcome":"verified","confidence":"high: deterministic dependency-only change",
 "versions":{"requests":"2.31.0"},"error":null,
 "gates":[{"name":"clean_room_reinstall","status":"pass"},{"name":"build","status":"pass"},
          {"name":"independent_rescan","status":"pass"},{"name":"test_delta","status":"pass"},
          {"name":"policy","status":"pass"}],
 "approvals":{"open_pr":"pending"},"needs_approval_for":[]}

While running, status is queued or running and outcome is null. approvals maps each approval kind (open_pr, major_bump) to pending, approved or rejected.

fix_submit metered#

Queues a sandboxed fix job for a project and reserves one use of your monthly allowance. The use is released again unless a PR is eventually opened. The idempotency key makes retries safe: the same key returns the same job (created: false) and never reserves twice.

ArgumentType
project_idstring (UUID)required
idempotency_keystring, 8–128 charsrequired; one per intended fix, e.g. fix-requests-2026-11-14
{"jsonrpc":"2.0","id":16,"method":"tools/call",
 "params":{"name":"fix_submit","arguments":{"project_id":"11111111-2222-4333-8444-555555555555",
   "idempotency_key":"fix-requests-2026-11-14"}}}
{"job_id":"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee","created":true,
 "usage":{"allowance":3,"consumed":0,"reserved":1,"remaining":2,"resets_at":"2026-12-01T00:00:00+00:00","plan":"hatchling"}}

When the allowance is used up, the call returns a tool error instead:

{"jsonrpc":"2.0","id":16,"result":{"isError":true,"content":[{"type":"text",
 "text":"{\"error\": \"quota_exhausted\", \"resets_at\": \"2026-12-01T00:00:00+00:00\", \"options\": [\"upgrade\", \"request_more\", \"wait\"]}"}]}}

pr_open 1 PR = 1 use#

Opens the pull request for a job that is verified and whose open_pr approval a person approved in the web app. Pushes a remediation/… branch with exactly the reviewed files and opens the PR with the evidence bundle as its body. This is when the reserved use is consumed. Calling it again for the same job returns the existing PR (created: false) and counts nothing.

ArgumentType
job_idstring (UUID)required
{"jsonrpc":"2.0","id":17,"method":"tools/call",
 "params":{"name":"pr_open","arguments":{"job_id":"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"}}}
{"url":"https://github.com/aadhi0612/arachne-demo/pull/1","number":1,"created":true,
 "usage":{"allowance":3,"consumed":1,"reserved":0,"remaining":2,"resets_at":"2026-12-01T00:00:00+00:00","plan":"hatchling"}}

Without an approval it refuses:

{"error":"not_approved","detail":"open_pr requires an approved human decision"}

Errors#

Tool-level problems come back as a normal JSON-RPC result with isError: true and a JSON body {"error": "<code>", …} in content[0].text, so your model can read and explain them:

CodeMeaning
invalid_argumentMissing required field, unknown field, wrong type or size, bad cursor. detail says which.
not_foundNo such project, job, advisory or package, including anything that belongs to another organisation (Arachne never says whether it exists elsewhere).
quota_exhausted / payments_not_enabledAllowance used up; carries resets_at and options.
not_approvedpr_open on a job that is not verified or not approved.
rate_limitedPer-person or action limit; carries retry_after_seconds.
org_frozen, not_a_member, account_requiredThe organisation is frozen, you are not a member, or the action needs a person account.
busyStorage contention; retry after retry_after_seconds.
internal_errorSomething failed on Arachne's side; the call was logged.

Protocol-level problems are JSON-RPC errors instead: -32700 parse error, -32600 invalid request (also for batches and oversized bodies), -32601 unknown method, -32602 unknown tool or bad params, -32001 insufficient scope (HTTP 403), -32029 rate limited (HTTP 429).