Leonidus MCP — Model Context Protocol reference
Leonidus exposes a remote Model Context Protocol server at /api/mcp + an npm-shipped stdio binary
@pisteyo/penta-mcp. 31 tools — scanner control, finding triage, deploy-gate verdicts, fix
reconciliation, compliance + service auto-detection, architecture documentation (per-repo docs with scorecard letter grades, drift checks, and a fleet-wide portfolio), the 754-skill CybersecuritySkills library, and — new in v2.4.0 — the
Client → Project → Repository hierarchy with client-scoped posture and per-client compliance reports.
Both transports share identical tool schemas — the same 31 calls work over HTTP for hosted clients (Claude.ai, Cursor remote MCP) and over stdio for local Claude Code / Claude Desktop. Every tool call round-trips through Leonidus's regular REST endpoints under the same Bearer key, so audit logs, rate limits, scopes, and project-key isolation all apply consistently.
Authentication
Every MCP tool call requires a Bearer API key in the standard Authorization: Bearer pk_live_...
header (HTTP transport) or the PENTA_API_KEY env var (stdio transport). Mint a key at:
/settings/api-keys— workspace-wide (for CI/CD, multi-repo automation)/repositories/<id>/keys— project-scoped (recommended — force-filtered to one repo, blast radius limited if leaked)
Project-scoped keys auto-bind every MCP tool call to the bound repository — even if Claude asks
penta_get_findings({fullName: "other/repo"}), Leonidus returns 403 and the key sees no other-repo data.
Client scope. Every key also inherits its creator's live client scope (see
Clients & isolation). A key minted by a contributor who belongs to client A sees only
client A's projects, repos, scans, findings and posture — penta_hierarchy({clientId: "<B>"}) returns
404. Nothing is baked into the key: change the person's memberships and the key follows on its next request.
Format. pk_live_<22-char-base64url>. Plaintext shown ONCE on mint —
Leonidus stores only sha256(key). Revoke instantly at the same UI.
Clients, projects & isolation v2.4.0
Leonidus organizes what you scan into a Client → Project → Repository hierarchy. A client is a customer or account inside your workspace (a tenant is the whole workspace — clients live inside it). Each project belongs to exactly one client and each repository to one project; compliance frameworks attach at client and/or project level and a project's effective frameworks are the union of both. Projects and repos not yet under a client sit in an unassigned bucket that stays visible workspace-wide.
Isolation semantics. Clients are a permission boundary inside a workspace. Contributor and viewer
users who hold at least one client membership are hard-scoped to those clients; users with zero memberships stay
workspace-wide (back-compat — nothing changes until you create memberships); superadmin, admin and cso always see
everything. API keys inherit their creator's live client scope — revoking a membership de-scopes existing
keys on the next request, no re-mint needed. Assigning a repository team lead or contributor auto-creates that
person's client membership, and every scan snapshots its clientId at creation so history stays
attributed even if a repo is moved later. Scoped callers get 404, never 403, for other clients'
resources (observe-empty) — a scoped key cannot even confirm another customer exists.
Contributor keys & token budgets. Contributors mint their own MCP keys under
Settings → API Keys (no admin needed). Each contributor key carries a scan-token budget:
every scan it starts is billed the LLM tokens that scan actually burns — a tiny scan costs a few thousand tokens, a
large one tens of thousands — so a key is metered by real work, not a flat run count. When a key reaches its budget,
further scans return HTTP 429 until a superadmin raises the limit or lifts the cap. Admin/superadmin
keys are unlimited by default. Live usage (used / limit / ≈ scans left) shows on the API Keys page.
| Tool | What it returns | Underlying REST |
|---|---|---|
penta_hierarchy | The tree (view: "map"), the people view — who belongs to which client and leads/contributes to which repos ("people"), the flat client list ("clients"), or one client's detail (clientId). | GET /api/v1/hierarchy/map · /hierarchy/people · /clients · /clients/<id> |
penta_client_posture | Compliance posture for exactly one client or project, over its effective frameworks. | GET /api/v1/compliance/posture?clientId=… or ?projectId=… |
penta_client_report | Submits a per-client / per-project compliance report, polls up to ~60 s, returns { id, status, exportUrl }. | POST /api/v1/reports/hierarchy → GET …/hierarchy/<id> → …/export?format=html |
penta_get_findings, penta_compliance_status, penta_compliance_detection, penta_service_detection | Accept optional clientId / projectId to narrow further. They can never widen a scoped key's view. | Pass-through query params on /findings and /compliance/posture |
Managing the hierarchy (creating clients, moving projects, attaching repos, assigning team leads and contributors,
adding members) is admin-tier work done in the UI or via the REST endpoints under /api/v1/clients,
/api/v1/projects/<id> and /api/v1/repositories/<id>/people — see the
REST reference. The MCP surface is deliberately read/report-only.
Transport options
Remote HTTP (recommended for hosted Claude clients)
Single POST endpoint at https://app.leonidus.ai/api/mcp. JSON-RPC 2.0 body. Works with
Claude.ai web, Cursor remote MCP, Continue, and any client that accepts a remote MCP URL + Bearer header.
curl -X POST https://app.leonidus.ai/api/mcp \
-H "Authorization: Bearer $PENTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Local stdio (recommended for Claude Code / Claude Desktop)
Distributed as @pisteyo/penta-mcp on npm. npx runs it on demand — no global install
required. The stdio binary is a thin transport — it imports the same shared tool definitions used by the
remote endpoint and proxies every call back to app.leonidus.ai under your Bearer key.
npx -y @pisteyo/penta-mcp init # one-time: writes config + Skill
# OR add directly to claude_desktop_config.json — see Install
Install
Add to ~/.claude/config.json (or run npx @pisteyo/penta-mcp init from any project root):
{
"mcpServers": {
"penta": {
"command": "npx",
"args": ["-y", "@pisteyo/penta-mcp"],
"env": {
"PENTA_API_KEY": "pk_live_...",
"PENTA_BASE_URL": "https://app.leonidus.ai"
}
}
}
}
Then in any project: claude → "Leonidus review — find what needs fixing." The bundled Skill (penta-review.md) teaches Claude when to call which tool and how to lay out findings in a local Leonidus/<scan>/<severity>/ folder.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"penta": {
"command": "npx",
"args": ["-y", "@pisteyo/penta-mcp"],
"env": {
"PENTA_API_KEY": "pk_live_...",
"PENTA_BASE_URL": "https://app.leonidus.ai"
}
}
}
}
Restart Claude Desktop. Tools appear as penta_* in the tool list.
Cursor settings → MCP servers → Add remote server. URL: https://app.leonidus.ai/api/mcp. Header: Authorization: Bearer $PENTA_API_KEY.
Alternatively use the local stdio path same as Claude Desktop (Cursor reads ~/.cursor/mcp.json).
The MCP endpoint is a plain JSON-RPC POST — works without any client install. Useful for CI scripts, smoke tests, or quick exploration:
# List the 31 tools
curl -s -X POST https://app.leonidus.ai/api/mcp \
-H "Authorization: Bearer $PENTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
# Trigger a scan
curl -s -X POST https://app.leonidus.ai/api/mcp \
-H "Authorization: Bearer $PENTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"penta_scan_repo","arguments":{"fullName":"owner/repo"}}}'
JSON-RPC 2.0 envelope
Leonidus's MCP server implements JSON-RPC 2.0. Every request
body is a JSON object with jsonrpc: "2.0", a client-chosen id, a method,
and method-specific params. Responses echo the id and carry either
result or error.
// Request envelope
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "penta_get_findings", "arguments": { "fullName": "owner/repo", "latest": true } }
}
// Response envelope (success)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{ "type": "text", "text": "{\"findings\": [...], \"total\": 12}" }
],
"isError": false
}
}
// Response envelope (error — JSON-RPC style)
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32603, "message": "Tool call failed", "data": { "status": 403, "error": "Forbidden" } }
}
RPC methods
| Method | Description | Params |
|---|---|---|
initialize | Handshake; returns server name + protocol version + capabilities. | { protocolVersion, clientInfo, capabilities } |
tools/list | Enumerate the 31 tools with full schemas. | — |
tools/call | Invoke a tool. Returns { content: [{ type: "text", text }] } where text is the JSON-stringified Leonidus REST response. | { name, arguments } |
ping | Liveness probe. Returns {}. | — |
The 31 tools
Click JSON-RPC request on any tool to see a copy-pasteable example. All examples assume the
Authorization: Bearer $PENTA_API_KEY header.
penta_scan_repo
Trigger a fresh server-side scan of a linked repository. Leonidus clones via stored PAT, runs Semgrep + Trivy + CodeQL, and persists findings. Returns scanId for polling.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo (e.g. 'acme/api'). For project-scoped keys this is auto-filled."
},
"branch": {
"type": "string",
"description": "Branch to scan. Defaults to the repo's default branch."
},
"commitSha": {
"type": "string",
"description": "Optional commit SHA to scan (informational; the scanner uses --depth 1 of the branch tip)."
},
"projectId": {
"type": "string",
"description": "Optional project UUID to file the scan under. Defaults to the repository's project; a repo with no project is filed under the workspace's “Unassigned scans” project (and assigned to it)."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (resolved within your scope; add `client` to disambiguate). The scan rolls up to that project's client."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_scan_repo",
"arguments": {
"fullName": "owner/repo",
"branch": "main"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"id\": \"a096f305-d48b-465f-9491-044da0dc3ce1\",\n \"status\": \"queued\",\n \"inputType\": \"repo\",\n \"repository\": \"owner/repo\",\n \"branch\": \"main\"\n}"
}
]
}
}
penta_scan_paste
Submit one or more file snippets for an ad-hoc scan when the repo isn't linked in Leonidus yet. Returns scanId for polling. Use penta_scan_repo when possible.
Input schema
{
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"content": {
"type": "string"
},
"language": {
"type": "string"
}
},
"required": [
"path",
"content"
]
},
"minItems": 1
},
"projectId": {
"type": "string",
"description": "Optional project UUID to file the scan under so it joins that project's history. Omitted → the workspace's “Unassigned scans” project."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": [
"files"
]
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_scan_paste",
"arguments": {
"files": [
{
"path": "src/auth.js",
"content": "const q = `SELECT * FROM users WHERE id=${id}`;",
"language": "javascript"
}
]
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"id\": \"b1c2d3e4-...\",\n \"status\": \"completed\",\n \"findingsCount\": {\n \"high\": 1,\n \"medium\": 0,\n \"low\": 0,\n \"info\": 0,\n \"critical\": 0\n }\n}"
}
]
}
}
penta_get_findings
List findings — by scanRunId, or by repo fullName + latest=true to pull the most recent completed scan. Returns minimal fields for each finding (severity, title, fingerprint, filePath, lineStart, plainWhy, plainFix). Optional clientId / projectId narrow the list to one client or project of the Client → Project → Repository hierarchy; a client-scoped key is ALREADY limited server-side to its creator's clients, so these only narrow further — never widen.
Input schema
{
"type": "object",
"properties": {
"scanRunId": {
"type": "string"
},
"fullName": {
"type": "string"
},
"latest": {
"type": "boolean"
},
"severity": {
"type": "array",
"items": {
"type": "string",
"enum": [
"critical",
"high",
"medium",
"low",
"info"
]
}
},
"status": {
"type": "string",
"enum": [
"open",
"in_progress",
"fixed",
"accepted_risk",
"dismissed",
"all"
]
},
"limit": {
"type": "number",
"minimum": 1,
"maximum": 500
},
"clientId": {
"type": "string",
"description": "Optional: only findings from repositories under this client. Out-of-scope ids yield 404 for client-scoped keys."
},
"projectId": {
"type": "string",
"description": "Optional: only findings from repositories under this project."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_get_findings",
"arguments": {
"fullName": "owner/repo",
"latest": true,
"severity": [
"critical",
"high"
],
"limit": 50
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"total\": 12,\n \"findings\": [\n {\n \"id\": \"f1...\",\n \"severity\": \"high\",\n \"title\": \"Tainted SQL string\",\n \"fingerprint\": \"F-A3F2C1B45E20\",\n \"filePath\": \"src/auth.js\",\n \"lineStart\": 23,\n \"plainWhy\": \"User input flows into a raw SQL string without parameterization.\",\n \"plainFix\": \"Use Prisma's parameterized query API.\",\n \"relatedSkillSlugs\": [\n \"implementing-parameterized-queries\"\n ]\n }\n ]\n}"
}
]
}
}
penta_get_finding_detail
Return the full content of a single finding incl. plainWhy, plainFix, remediation, remediationCode, ruleId, engine, filePath, lineStart/lineEnd, compliance mapping. Used to author the Leonidus/<scan>/1.ToBeWork/<severity>/F-XXX.md markdown locally.
Input schema
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Finding id (UUID)"
}
},
"required": [
"id"
]
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_get_finding_detail",
"arguments": {
"id": "a3f2c1b4-5e20-4a9b-9c1d-2b3c4d5e6f7a"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"id\": \"a3f2c1b4...\",\n \"severity\": \"high\",\n \"title\": \"Tainted SQL string\",\n \"ruleId\": \"javascript.lang.security.audit.sqli.tainted-sql-string\",\n \"description\": \"...\",\n \"plainWhy\": \"...\",\n \"plainFix\": \"...\",\n \"remediation\": \"...\",\n \"remediationCode\": \"prisma.user.findUnique({ where: { email } })\",\n \"relatedSkills\": [\n {\n \"slug\": \"implementing-parameterized-queries\",\n \"title\": \"Implementing parameterized queries\",\n \"subdomain\": \"web-app-security\",\n \"frameworks\": {\n \"mitreAttack\": [\n \"T1190\"\n ],\n \"nistCsf\": [\n \"PR.PS-01\"\n ]\n }\n }\n ]\n}"
}
]
}
}
penta_gate_check
Pre-push verdict: allow | warn | block. Triggers a fresh scan if needed and waits up to maxWaitSec for completion. Returns blockReasons[], new-vs-fixed diff, and the cached verdict.
Input schema
{
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"repo",
"url"
]
},
"fullName": {
"type": "string"
},
"branch": {
"type": "string"
},
"commitSha": {
"type": "string"
},
"url": {
"type": "string"
},
"depth": {
"type": "string",
"enum": [
"quick",
"deep"
]
}
},
"required": [
"type"
]
},
"policy": {
"type": "object",
"properties": {
"minSeverity": {
"type": "string",
"enum": [
"critical",
"high",
"medium",
"low",
"info",
"none"
]
},
"maxRiskScore": {
"type": "number"
},
"requireScanTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"gateOnNewOnly": {
"type": "boolean"
},
"failOpen": {
"type": "boolean"
}
}
},
"wait": {
"type": "boolean"
},
"maxWaitSec": {
"type": "number",
"minimum": 1,
"maximum": 600
}
},
"required": [
"input"
]
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_gate_check",
"arguments": {
"input": {
"type": "repo",
"fullName": "owner/repo",
"commitSha": "a3f2c1b"
},
"policy": {
"minSeverity": "high",
"gateOnNewOnly": true
},
"wait": true,
"maxWaitSec": 300
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"scanId\": \"a096f305...\",\n \"verdict\": \"block\",\n \"findingsCount\": {\n \"critical\": 0,\n \"high\": 3,\n \"medium\": 5,\n \"low\": 12,\n \"info\": 0\n },\n \"blockReasons\": [\n \"3 findings at or above 'high' exceed gate threshold\"\n ],\n \"newFindingsVsPrior\": 3,\n \"fixedFindingsVsPrior\": 0\n}"
}
]
}
}
penta_report_completed
Engineer-triggered batch update: tells Leonidus which findings have been moved to Leonidus/<scan>/2.WorkedCompleted/. Marks them status=fixed with optional commit-sha. Use when you want Leonidus-side state updated NOW (otherwise the next periodic scan reconciles automatically).
Input schema
{
"type": "object",
"properties": {
"scanId": {
"type": "string",
"description": "ScanRun id whose findings are being reconciled."
},
"completed": {
"type": "array",
"items": {
"type": "string"
},
"description": "Array of finding fingerprints (e.g. 'F-9a3b1c') that the engineer has fixed."
},
"commit": {
"type": "string",
"description": "Optional resolving commit SHA."
}
},
"required": [
"scanId",
"completed"
]
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_report_completed",
"arguments": {
"scanId": "a096f305-d48b-465f-9491-044da0dc3ce1",
"completed": [
"F-A3F2C1B45E20",
"F-7C2A8E9D1F45"
],
"commit": "b4d5e6f"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"scanId\": \"a096f305...\",\n \"updated\": 2,\n \"notFound\": []\n}"
}
]
}
}
penta_compliance_status
Compliance posture — workspace-wide by default, or narrowed with clientId / projectId to one client or project (penta_client_posture is the scope-first variant). Useful for messaging like 'this fix moves you from 67% → 74% on SOC2 CC7.1'. A client-scoped key is already limited server-side to its creator's clients; naming an out-of-scope client/project returns 404.
Input schema
{
"type": "object",
"properties": {
"frameworkSlug": {
"type": "string"
},
"clientId": {
"type": "string",
"description": "Optional: posture for one client only."
},
"projectId": {
"type": "string",
"description": "Optional: posture for one project only (its effective frameworks = client ∪ project)."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_compliance_status",
"arguments": {
"frameworkSlug": "soc2"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"framework\": \"soc2\",\n \"coverage\": {\n \"pass\": 47,\n \"fail\": 12,\n \"na\": 8,\n \"total\": 67\n },\n \"percentage\": 70.1\n}"
}
]
}
}
penta_hierarchy
Browse the Client → Project → Repository hierarchy. A Client is a customer/account INSIDE the workspace (a tenant is the whole workspace) and is a permission boundary: contributor/viewer users with ≥1 client membership are hard-scoped to those clients (other clients' resources return 404, never 403), users with zero memberships stay workspace-wide (back-compat), superadmin/admin/cso always see everything, and API keys inherit their creator's LIVE client scope — revoking a membership de-scopes the key on its next request. Each project belongs to exactly one client and each repository to one project; compliance frameworks attach at client and/or project level (effective = union); making someone a repo team lead or contributor auto-creates their client membership; scans snapshot clientId at creation so history stays attributed. view='map' → whole tree incl. the unassigned bucket ({ clients:[{ id,name,slug,status,frameworkSlugs, projects:[{ id,name,slug,status,frameworkSlugs,effectiveFrameworkSlugs, repos:[{ id,fullName,teamLead,contributors,lastScan }] }] }], unassigned:{ projects, repos } }); view='people' → every user with their clients, team-lead repos and contributor repos ('who can see what'); view='clients' → flat client list; pass clientId → that one client's detail. Results are already filtered to what this key may see.
Input schema
{
"type": "object",
"properties": {
"view": {
"type": "string",
"enum": [
"map",
"people",
"clients"
],
"description": "Which projection to return. Default 'map'. Ignored when clientId is given."
},
"clientId": {
"type": "string",
"description": "Return one client's detail (GET /api/v1/clients/<id>) instead of a list."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_hierarchy",
"arguments": {
"view": "map"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"clients\": [\n {\n \"id\": \"c1a2b3c4-...\",\n \"name\": \"Acme Corp\",\n \"slug\": \"acme-corp\",\n \"status\": \"active\",\n \"frameworkSlugs\": [\n \"soc2\"\n ],\n \"projects\": [\n {\n \"id\": \"p1...\",\n \"name\": \"Payments\",\n \"slug\": \"payments\",\n \"status\": \"active\",\n \"frameworkSlugs\": [\n \"pci-dss\"\n ],\n \"effectiveFrameworkSlugs\": [\n \"soc2\",\n \"pci-dss\"\n ],\n \"repos\": [\n {\n \"id\": \"r1...\",\n \"fullName\": \"acme/payments-api\",\n \"teamLead\": {\n \"id\": \"u1...\",\n \"name\": \"Alice Ng\",\n \"email\": \"alice@acme.com\"\n },\n \"contributors\": [\n {\n \"id\": \"u2...\",\n \"name\": \"Bob Ortiz\",\n \"email\": \"bob@acme.com\"\n }\n ],\n \"lastScan\": {\n \"id\": \"a096f305-...\",\n \"status\": \"completed\",\n \"createdAt\": \"2026-08-30T10:12:00Z\",\n \"riskScore\": 22\n }\n }\n ]\n }\n ]\n }\n ],\n \"unassigned\": {\n \"projects\": [],\n \"repos\": [\n {\n \"id\": \"r9...\",\n \"fullName\": \"acme/legacy-tools\",\n \"teamLead\": null,\n \"contributors\": [],\n \"lastScan\": null\n }\n ]\n }\n}"
}
]
}
}
penta_client_posture
Compliance posture for ONE client or ONE project (pass exactly one of clientId / projectId). Uses only scans + evidence attributed to that scope and the scope's effective frameworks (client frameworks ∪ project frameworks). Client-scoped keys can only ask about clients they belong to — anything else is 404. Optional frameworkSlug narrows to a single framework.
Input schema
{
"type": "object",
"properties": {
"clientId": {
"type": "string",
"description": "Client UUID (mutually exclusive with projectId)."
},
"projectId": {
"type": "string",
"description": "Project UUID (mutually exclusive with clientId)."
},
"frameworkSlug": {
"type": "string",
"description": "Optional single framework, e.g. 'soc2'."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_client_posture",
"arguments": {
"clientId": "c1a2b3c4-..."
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"scope\": {\n \"clientId\": \"c1a2b3c4-...\",\n \"name\": \"Acme Corp\",\n \"frameworkSlugs\": [\n \"soc2\"\n ]\n },\n \"posture\": [\n {\n \"slug\": \"soc2\",\n \"name\": \"SOC 2\",\n \"totalControls\": 67,\n \"passing\": 47,\n \"failing\": 12,\n \"pending\": 8,\n \"passingPercent\": 70,\n \"failingPercent\": 18,\n \"pendingPercent\": 12\n }\n ]\n}"
}
]
}
}
penta_client_report
Generate a customer-ready compliance report for ONE client or ONE project (exactly one of clientId / projectId): repository inventory with team leads, latest scan per repo, open findings by severity, and per-framework posture for the scope's effective frameworks — nothing from other clients. Submits the report, polls up to ~60 s for status 'completed', and returns { id, status, exportUrl } where exportUrl serves self-contained HTML (fetch it with the same Bearer key). If generation is still running after the wait, status is returned as-is with timedOut=true — poll GET /api/v1/reports/hierarchy/<id> yourself. Scoped keys can only report on their own clients (404 otherwise). Rate limit: reports.generate 20/min.
Input schema
{
"type": "object",
"properties": {
"clientId": {
"type": "string",
"description": "Client UUID (mutually exclusive with projectId)."
},
"projectId": {
"type": "string",
"description": "Project UUID (mutually exclusive with clientId)."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_client_report",
"arguments": {
"projectId": "p1..."
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"id\": \"7f3e...\",\n \"status\": \"completed\",\n \"exportUrl\": \"/api/v1/reports/hierarchy/7f3e.../export?format=html\"\n}"
}
]
}
}
penta_compliance_detection
Auto-detect which compliance frameworks apply to this codebase based on the most recent scan. Returns a ranked list with applicability tiers (required / recommended / optional), confidence, and the signals that triggered each match. Useful at the start of a project to scope your audit work, or after a major refactor to spot newly-applicable regimes. When scanId is omitted the latest scan is resolved via /api/v1/findings — optional fullName / clientId / projectId narrow that lookup (client-scoped keys are already server-side limited).
Input schema
{
"type": "object",
"properties": {
"scanId": {
"type": "string",
"description": "ScanRun id to analyze. If omitted, the latest completed scan for the bound repository is used."
},
"fullName": {
"type": "string",
"description": "Optional owner/repo to resolve the latest scan for when scanId is omitted."
},
"clientId": {
"type": "string",
"description": "Optional: resolve the latest scan within this client only."
},
"projectId": {
"type": "string",
"description": "Optional: resolve the latest scan within this project only."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_compliance_detection",
"arguments": {
"scanId": "a096f305-d48b-465f-9491-044da0dc3ce1"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"scanRunId\": \"a096f305...\",\n \"detectedFrameworks\": [\n {\n \"slug\": \"soc2\",\n \"name\": \"SOC 2\",\n \"applicability\": \"required\",\n \"confidence\": 92,\n \"rationale\": \"Workspace serves SaaS customers; auth + audit-log patterns detected.\"\n },\n {\n \"slug\": \"hipaa\",\n \"name\": \"HIPAA\",\n \"applicability\": \"recommended\",\n \"confidence\": 78,\n \"rationale\": \"Health-data identifiers detected in test fixtures.\"\n }\n ],\n \"summary\": {\n \"totalSignals\": 14,\n \"requiredCount\": 2,\n \"recommendedCount\": 3,\n \"optionalCount\": 5\n }\n}"
}
]
}
}
penta_service_detection
Inventory every external service and dependency the codebase uses — cloud providers (AWS/Azure/GCP), SaaS APIs (Stripe, Twilio, etc.), databases, queues, auth providers, observability tools, AI/ML SDKs, frameworks, languages, CI/CD, IaC. Returns a grouped service list with vendor, confidence, and evidence per match. Great as a starting-point architecture map or for vendor-risk inventory. When scanId is omitted the latest scan is resolved via /api/v1/findings — optional fullName / clientId / projectId narrow that lookup (client-scoped keys are already server-side limited).
Input schema
{
"type": "object",
"properties": {
"scanId": {
"type": "string",
"description": "ScanRun id to analyze. If omitted, the latest completed scan for the bound repository is used."
},
"fullName": {
"type": "string",
"description": "Optional owner/repo to resolve the latest scan for when scanId is omitted."
},
"clientId": {
"type": "string",
"description": "Optional: resolve the latest scan within this client only."
},
"projectId": {
"type": "string",
"description": "Optional: resolve the latest scan within this project only."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_service_detection",
"arguments": {
"scanId": "a096f305-d48b-465f-9491-044da0dc3ce1"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"scanRunId\": \"a096f305...\",\n \"services\": [\n {\n \"identifier\": \"aws-s3\",\n \"name\": \"AWS S3\",\n \"vendor\": \"Amazon Web Services\",\n \"category\": \"cloud-aws\",\n \"thirdParty\": true,\n \"confidence\": 95\n },\n {\n \"identifier\": \"stripe\",\n \"name\": \"Stripe\",\n \"vendor\": \"Stripe\",\n \"category\": \"payments\",\n \"thirdParty\": true,\n \"confidence\": 100\n }\n ],\n \"summary\": {\n \"totalServices\": 12,\n \"thirdPartyCount\": 9,\n \"internalCount\": 3,\n \"byCategory\": {\n \"cloud-aws\": 4,\n \"payments\": 1,\n \"observability\": 2\n }\n }\n}"
}
]
}
}
penta_architecture_status
Architecture document status for a repository: versions, the latest doc's per-tab statuses, scans completed since it was generated (drift hint), and recent change-log entries. Available to any key with scans:view. Use before generating.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo (e.g. 'acme/api')."
}
},
"required": [
"fullName"
]
}
penta_architecture_generate
Start a NEW architecture document version for a repository (requires a key whose creator holds the Architect designation). Builds the code model and registers one pending tab per configured tab (the workspace's tab registry — superadmins can customize it), then returns { docId, tabs }. Generate each tab with penta_architecture_generate_tab — the doc flips to ready when the last tab completes. For many repositories at once, prefer penta_architecture_portfolio + the background queue.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo."
}
},
"required": [
"fullName"
]
}
penta_architecture_generate_tab
Generate ONE tab of an architecture document (architect keys only). Call once per tabKey returned by penta_architecture_generate; each call is a single AI pass (~20-60s). Idempotent for already-ready tabs.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo."
},
"docId": {
"type": "string",
"description": "Document id from penta_architecture_generate."
},
"tabKey": {
"type": "string",
"description": "Tab key, e.g. 'summary', 'reqpath', 'security'."
}
},
"required": [
"fullName",
"docId",
"tabKey"
]
}
penta_architecture_get_tab
Fetch one tab's content (typed render blocks + source file citations) from a repository's architecture document. Defaults to the latest document; pass docId for an older version. Available to any key with scans:view.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo."
},
"tabKey": {
"type": "string",
"description": "Tab key, e.g. 'summary', 'integrations', 'journeys'."
},
"docId": {
"type": "string",
"description": "Optional specific document version id."
}
},
"required": [
"fullName",
"tabKey"
]
}
penta_architecture_drift
Run an architecture drift check (architect keys only): re-fingerprints the repo's code and diffs it against the latest document. Logs findings to the permanent change log; returns drifted true/false with the entries. Link it to a scan by passing scanRunId.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo."
},
"scanRunId": {
"type": "string",
"description": "Optional scan to link the log entries to."
}
},
"required": [
"fullName"
]
}
penta_architecture_portfolio
Fleet-wide architecture portfolio: one row per repository with its latest document (version/status/progress/queue position), scorecard letter grades (Overall + per-dimension, parsed from each doc's Scorecard tab), staleness, plus workspace queue depth/ETA and the latest executive portfolio report's metadata. THE tool for reviewing every repository at once. Available to any key with scans:view.
Input schema
{
"type": "object",
"properties": {},
"required": []
}
penta_get_skill
Fetch the full content of one CybersecuritySkill (Apache-2.0 library, 754 procedures). Returns the procedure body markdown, the 'When to Use' + 'Prerequisites' sections, framework mappings (MITRE ATT&CK / ATLAS / D3FEND / NIST CSF / NIST AI RMF), tags, and the upstream source URL. Use this when a `penta_get_finding_detail` response includes a `relatedSkills[]` slug you want to expand, or when you want to look up authoritative procedure for a specific CWE / technique / SaaS service.
Input schema
{
"type": "object",
"properties": {
"slug": {
"type": "string",
"description": "Skill slug, e.g. 'performing-jwt-none-algorithm-attack' or 'implementing-secrets-management-with-vault'."
}
},
"required": [
"slug"
]
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_get_skill",
"arguments": {
"slug": "performing-jwt-none-algorithm-attack"
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"slug\": \"performing-jwt-none-algorithm-attack\",\n \"title\": \"Performing JWT 'none' algorithm attack\",\n \"subdomain\": \"api-security\",\n \"body\": \"## Procedure\\n\\n1. Inspect the JWT...\\n2. Set the `alg` header to `none`...\\n... (full markdown body)\",\n \"frameworks\": {\n \"mitreAttack\": [\n \"T1556\"\n ],\n \"nistCsf\": [\n \"PR.AA-01\"\n ],\n \"d3fend\": [\n \"D3-MFA\"\n ]\n },\n \"license\": \"Apache-2.0\",\n \"sourceUrl\": \"https://github.com/...\"\n}"
}
]
}
}
penta_remediation_prompt
Return a ready-to-run REMEDIATION PROMPT. Pass `scanId` for one scan, OR `projectId` for the WHOLE project: the project prompt combines code + database + infrastructure findings, applies the live-database overrides (findings the live DB proves already fixed are excluded), and splits the work into sections (fix in code, run SQL/migration, fix infra). Both carry the hard guardrail: change ONLY the security issue — no functionality, UI, behavior, files, APIs, or dependencies (the app is QA-approved). Optional `notes` are woven into the top and saved; `fresh=true` rebuilds.
Input schema
{
"type": "object",
"properties": {
"scanId": {
"type": "string",
"description": "ScanRun id (UUID) to build a per-scan remediation prompt for."
},
"projectId": {
"type": "string",
"description": "Project id (UUID) to build the COMBINED project-level prompt (code + DB + infra, live-DB aware). Takes precedence over scanId."
},
"notes": {
"type": "string",
"description": "Optional extra instructions added to the top of the prompt and persisted."
},
"fresh": {
"type": "boolean",
"description": "Rebuild from the latest findings + template, ignoring any saved edit. Default false."
}
},
"required": []
}
JSON-RPC request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "penta_remediation_prompt",
"arguments": {
"scanId": "a096f305-d48b-465f-9491-044da0dc3ce1",
"notes": "Only touch the auth module; open a PR against release."
}
}
}
JSON-RPC response (abridged)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"prompt\": \"You are an expert application-security engineer… ⛔ HARD CONSTRAINTS … [grouped findings + CSV] …\",\n \"edited\": false,\n \"notes\": \"Only touch the auth module; open a PR against release.\",\n \"groupCount\": 7,\n \"findingsCount\": 42,\n \"severitySummary\": \"12 critical, 18 high, 12 medium\"\n}"
}
]
}
}
penta_project_inventory
Return the project's ARTIFACT INVENTORY plus its combined finding set. The inventory lists every repository (at its scanned commit), database connection (per environment) and attached scan, with what was scanned, the checks that ran, open findings after live-DB overrides, and a PASS / FAIL / NOT_SCANNED / INCOMPLETE verdict per artifact — i.e. exactly what was scanned and what passed. The combined set is the reconciled findings across code + DB + infra with live-DB overrides applied.
Input schema
{
"type": "object",
"properties": {
"projectId": {
"type": "string",
"description": "Project id (UUID)."
}
},
"required": [
"projectId"
]
}
penta_scan_project
Scan the WHOLE project in one action: every repository with an SCM connection and every database connection with stored credentials. Returns the batchId and the queued scan ids. Poll penta_project_inventory afterwards to see the combined result and per-artifact pass/fail.
Input schema
{
"type": "object",
"properties": {
"projectId": {
"type": "string",
"description": "Project id (UUID)."
}
},
"required": [
"projectId"
]
}
penta_list_repos
List the repositories this key can scan (linked to the workspace) and the connected GitHub accounts. Call this first to discover targets: if the repo you want is already linked, scan it with penta_scan_repo; if it is visible but not linked, link it with penta_link_repo; if there is no GitHub connection at all, upload the code with penta_scan_upload instead. Returns each linked repo's fullName, default branch and project.
Input schema
{
"type": "object",
"properties": {
"search": {
"type": "string",
"description": "Optional case-insensitive substring to filter repo fullNames."
}
},
"required": []
}
penta_link_repo
Link a GitHub repository to the workspace so it can be scanned by penta_scan_repo. Needs a GitHub account connected in Leonidus (Settings → Repositories) and a key with the `repos:add` scope. Pass the `fullName` (owner/repo); `connectionId` is optional when exactly one GitHub account is connected. If nothing is connected, or the key lacks the scope, you'll get a clear message — fall back to penta_scan_upload to scan the code without linking.
Input schema
{
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "GitHub owner/repo, e.g. acme/api."
},
"connectionId": {
"type": "string",
"description": "Which connected GitHub account to link through (optional when only one is connected)."
},
"projectId": {
"type": "string",
"description": "Optional project (UUID) to file the repo under."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": [
"fullName"
]
}
penta_scan_upload
Upload a code archive and scan it in one call — for code that isn't a linked GitHub repo. `contentBase64` is a base64-encoded .zip / .tar.gz of the source (exclude node_modules / .git; up to ~12 MB decoded). File it under a project so results roll up. Returns the scanId; poll penta_scan_status, then penta_get_findings and penta_remediation_prompt. For a linked GitHub repo prefer penta_scan_repo; for larger code, link the repo or use the browser upload.
Input schema
{
"type": "object",
"properties": {
"fileName": {
"type": "string",
"description": "Archive name ending in .zip / .tar.gz / .tgz."
},
"contentBase64": {
"type": "string",
"description": "Base64-encoded archive bytes."
},
"projectId": {
"type": "string",
"description": "Project id (UUID) to file under; rolls up to the client."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": [
"fileName",
"contentBase64"
]
}
penta_scan_database
Run a LIVE database posture scan (Supabase / Postgres) — RLS, grants, SECURITY DEFINER functions, storage buckets, plaintext secrets, and an anon-key attacker's-eye probe. Detect-and-report only; never writes to the DB. Pass `dbConnectionId` to reuse a saved connection, OR credentials to save + scan (at least one of dbUrl / mgmtToken / anonKey). File it under a project with `projectId` (or `project` name + optional `client` name) so its findings roll up to that project → client and combine with the repo scan. Returns the scanId; poll penta_scan_status.
Input schema
{
"type": "object",
"properties": {
"dbConnectionId": {
"type": "string",
"description": "Reuse a saved connection by id (skip credentials)."
},
"name": {
"type": "string",
"description": "Connection name when saving a new one (re-using the same name updates it)."
},
"kind": {
"type": "string",
"enum": [
"supabase",
"postgres"
],
"description": "Default supabase."
},
"environment": {
"type": "string",
"enum": [
"dev",
"staging",
"production"
],
"description": "Which deployment this points at. Default production."
},
"projectRef": {
"type": "string",
"description": "Supabase project ref, host, or full URL (used for the anon REST probe)."
},
"dbUrl": {
"type": "string",
"description": "Read-only Postgres connection string (best — direct catalog)."
},
"mgmtToken": {
"type": "string",
"description": "Supabase management token (sbp_…) for catalog access over HTTPS."
},
"anonKey": {
"type": "string",
"description": "Public anon key — the unauthenticated attacker's-eye probe."
},
"serviceRoleKey": {
"type": "string",
"description": "service_role key (owner-view row counts)."
},
"projectId": {
"type": "string",
"description": "Project id (UUID) to file the scan under; it rolls up to the project's client. Get ids from penta_hierarchy."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (resolved within your scope; add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": []
}
penta_scan_infrastructure
Run a cloud INFRASTRUCTURE posture scan (CSPM). Providers: `azure` (resource group, Reader service principal), `vercel` (API token), `aws` (read-only access key), `gcp` (Viewer service-account JSON). Read-only — reads configuration and reports misconfigurations, never changes anything. Pass `cloudConnectionId` to reuse a saved connection, OR the provider + its credentials to save + scan. File it under a project with `projectId` (or `project`/`client` names) so it combines with the code + DB scans. Returns the scanId; poll penta_scan_status.
Input schema
{
"type": "object",
"properties": {
"cloudConnectionId": {
"type": "string",
"description": "Reuse a saved connection by id (skip credentials)."
},
"provider": {
"type": "string",
"enum": [
"azure",
"vercel",
"aws",
"gcp"
],
"description": "Cloud provider. Default azure."
},
"name": {
"type": "string",
"description": "Connection name when saving a new one."
},
"environment": {
"type": "string",
"enum": [
"dev",
"staging",
"production"
],
"description": "Default production."
},
"tenantId": {
"type": "string",
"description": "Azure: tenant id."
},
"subscriptionId": {
"type": "string",
"description": "Azure: subscription id."
},
"resourceGroup": {
"type": "string",
"description": "Azure: the single resource group to scan."
},
"appId": {
"type": "string",
"description": "Azure: service-principal application (client) id."
},
"appSecret": {
"type": "string",
"description": "Azure: service-principal client secret."
},
"vercelToken": {
"type": "string",
"description": "Vercel: API token (read-only is fine)."
},
"vercelTeamId": {
"type": "string",
"description": "Vercel: team id (omit for a personal account)."
},
"awsAccessKeyId": {
"type": "string",
"description": "AWS: access key id for a read-only IAM user (SecurityAudit)."
},
"awsSecretAccessKey": {
"type": "string",
"description": "AWS: secret access key."
},
"awsRegion": {
"type": "string",
"description": "AWS: region (default us-east-1)."
},
"gcpServiceAccountKey": {
"type": "string",
"description": "GCP: service-account JSON key (Viewer / Security Reviewer)."
},
"gcpProjectId": {
"type": "string",
"description": "GCP: project id."
},
"projectId": {
"type": "string",
"description": "Project id (UUID) to file under; rolls up to the client."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
}
},
"required": []
}
penta_scan_status
Read one scan's outcome: status, timing, severity counts, risk level, the coverage record (did a database scan actually introspect the live catalog?), and — for an incomplete database scan — the reason it could not connect. Use after any scan tool to confirm it really ran.
Input schema
{
"type": "object",
"properties": {
"scanId": {
"type": "string",
"description": "ScanRun id (UUID)."
}
},
"required": [
"scanId"
]
}
penta_submit_feedback
Submit engineer feedback on a project's findings so Leonidus learns which were fixed / not-fixed / false-positive and carries that into the NEXT remediation prompt. `raw` is the free-text report (the same format penta_remediation_prompt emits a REPORT-BACK template for). The full text is stored on the project for later, each line is parsed and matched to a finding by fingerprint, and a summary of matched/unmatched is returned. No finding is auto-changed. File under the project the findings belong to.
Input schema
{
"type": "object",
"properties": {
"projectId": {
"type": "string",
"description": "Project id (UUID) the feedback is about."
},
"project": {
"type": "string",
"description": "Project NAME instead of an id (add `client` to disambiguate)."
},
"client": {
"type": "string",
"description": "Client NAME to disambiguate a project name."
},
"raw": {
"type": "string",
"description": "The free-text engineer feedback / report-back to parse and store."
}
},
"required": [
"raw"
]
}
penta_github_coverage
GitHub Security overview: each connected GitHub org's tier and what its licence unlocks (Secret Protection / Code Security / Enterprise / Copilot), open GitHub alerts by source (Dependabot, code scanning, secret scanning, advisories, audit log) and severity, and the action coverage — how many alerts already have an action (prompt sent, in progress, accepted risk…). Needs a workspace key minted with the ghsec:view scope by someone with the GitHub Security role (Settings → API Keys → scopes).
Input schema
{
"type": "object",
"properties": {
"connectionId": {
"type": "string",
"description": "Optional: one GitHub connection (UUID)."
}
},
"required": []
}
penta_github_alerts
List open GitHub security alerts with their Leonidus action state, or — with `prompt: true` and a `repo` — get the ready-to-paste remediation prompt for that repo's open GitHub alerts (the same prompt Leonidus sends the repo owner). Filter by repo (owner/name), source (dependabot | code-scanning | secret-scanning | repo-advisory | audit-log | bypass-request | copilot), severity or action. Without ghsec:view, pass `repo`: the key's creator must own that repo (team lead or contributor) or have been sent its prompt, and a key bound to one repository always reads its own repo.
Input schema
{
"type": "object",
"properties": {
"repo": {
"type": "string",
"description": "owner/name of the repository."
},
"source": {
"type": "string",
"description": "Alert source filter."
},
"severity": {
"type": "string",
"description": "critical | high | medium | low | info"
},
"action": {
"type": "string",
"description": "untriaged | prompt_sent | acknowledged | in_progress | fixed | accepted_risk | false_positive"
},
"prompt": {
"type": "boolean",
"description": "Return the remediation prompt for `repo` instead of the list."
},
"pageSize": {
"type": "number",
"description": "Max alerts to return (default 50, max 200)."
}
},
"required": []
}
Error codes
JSON-RPC errors return a code + message; Leonidus also fills data with the underlying HTTP status + REST error body so clients can surface a useful message.
| Code | Meaning | Cause |
|---|---|---|
-32700 | Parse error | Body wasn't valid JSON. |
-32600 | Invalid request | Missing jsonrpc / method / wrong shape. |
-32601 | Method not found | Unknown method or tool name. |
-32602 | Invalid params | Tool arguments don't match the input schema. |
-32603 | Internal / tool failure | Underlying REST call returned non-2xx. data.status + data.error have the details. |
-32000 | Auth required | Missing / invalid / revoked Bearer key. Mint a new one at /settings/api-keys. |
-32001 | Rate-limited | Exceeded mcp.tool bucket (120/min). Retry after the seconds in data.retryAfter. |
Rate limits
The MCP endpoint shares Leonidus's standard rate-limit buckets:
mcp.tool— 120 calls/min per API keyscans.create— 30 calls/min (applies topenta_scan_repo+penta_scan_paste)gate.create— 60 calls/min (applies topenta_gate_check)arch.generate— 6 calls/hour per workspace (applies topenta_architecture_generate)arch.ai— 40 calls/min per workspace (applies topenta_architecture_generate_tabandpenta_architecture_drift)reports.generate— 20 calls/min (applies topenta_client_report)
On 429 the response includes RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and Retry-After headers. Honor them.
Recommended Claude Code workflow
The bundled penta-review.md Skill teaches Claude when to call
which tool. The everyday loop:
- Developer says "Leonidus review — find what needs fixing" in
claude. - Skill: check for a recent scan via
penta_get_findings({fullName, latest: true}). - If none, trigger
penta_scan_repo+ poll until complete. - Write each finding to
Leonidus/<MMDDYY-HHMM>/1.ToBeWork/<severity>/F-XXX.md. - Iterate highest-severity-first:
penta_get_finding_detail→ open file → propose diff → engineer accepts → tests pass → engineer moves the F-XXX.md file to2.WorkedCompleted/. - Optional batch sync:
penta_report_completedtells Leonidus which fingerprints are fixed now (otherwise the next scheduled scan reconciles). - Pre-push check:
penta_gate_checkreturnsallow/warn/blockwith reasons. - Claude emits
PENTA_RESULT: SHIP_OK(orSHIP_WITH_WARNINGS <reason>).git push.
See also
REST API reference — every underlying endpoint the MCP tools call, with full request/response schemas, error codes, and code samples in curl / fetch / requests.
OpenAPI 3.1 spec — machine-readable spec; import into Postman, Insomnia,
Stoplight, or any OpenAPI-aware tool. scalar mock /api/openapi.json
for a local mock server.
Help center — articles on per-project keys, the gate API, compliance frameworks, the CybersecuritySkills library, and the client hierarchy (Clients, Projects & Repositories, Client-level access isolation, Setting up your first client).