Install
$ agentstack add mcp-trustabl-trustabl ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Trustabl is a static analyzer for agent reliability. It parses an agent-SDK repository (Claude Agent SDK, OpenAI Agents SDK, Google ADK, MCP, LangChain / LangGraph, CrewAI, AutoGen / AG2, Pydantic AI, and the Vercel AI SDK), models the tools, agents, subagents, skills, slash commands, and plugin manifests it declares, and checks them against a catalog of reliability and safety rules. It reports the weaknesses it finds — each with an explanation, a suggested fix, and a confidence score — as a human-readable summary, JSON, or SARIF 2.1.0, plus a per-surface reliability score and a CI-friendly exit code. It ships as a single Go binary with no hosted service: it runs as a CLI, or as a local stdio MCP server (trustabl mcp) that exposes the same scan to MCP clients without opening a network port.
The rest of this document explains what Trustabl reasons about and how the scan works, then covers building and running it. For the full implementation reference see [ARCHITECTURE.md](ARCHITECTURE.md); for the at-a-glance SDK coverage matrix see [COVERAGE.md](COVERAGE.md).
What it analyzes — the five-scope model
Trustabl does not treat a repository as one undifferentiated blob. Every rule is classified into exactly one of five scopes, and each scope receives a different typed input:
tool— fires once per tool definition. Input: aToolDef(a
@function_tool / @tool / @claude_tool function, a Claude TS tool(name, description, schema, handler) factory call, a FunctionTool(fn) ADK wrapper, an @server.tool MCP registration, or a bare shell-invoking function) plus its parsed file. Catches a missing docstring, an HTTP call with no timeout, untyped parameters, or an unnormalized path flowing into open(). (Hosted tools like WebSearchTool() are agent-scope edge data, captured as HostedToolDef, not ToolDef.)
agent— fires once per agent declaration. Input: anAgentDef—
a Python Agent(...) / SandboxAgent(...) / AgentDefinition(...) call, a Claude TS typed-const AgentDefinition, a Claude TS sub-agent inline in options.agents, or the Claude TS query(...) main-thread agent (QueryMainAgent) — with every constructor kwarg captured and its edges to tools, handoffs, and guardrails resolved. Catches an agent with shell tools and no input_guardrails, tool_use_behavior="stop_on_first_tool" paired with filesystem-touching tools, or a main-thread agent with unrestricted allowedTools.
subagent— fires once per Claude Code subagent markdown
declaration. Discovery is hybrid: canonical .claude/agents/*.md (any path depth, monorepo-safe) PLUS a frontmatter-shape fallback over all markdown files (gated on name + tools/model) that catches flat-collection repos which ship subagents under categories/*.md, plugins//agents/*.md, or similar layouts. Input: a SubagentDef parsed from frontmatter — name, description, tools[] (verbatim) + ToolGrants[] (parsed permission grammar), disallowedTools, model, permissionMode (incl. bypassPermissions), mcpServers, skills, isolation, hasHooks. Catches a subagent granted the built-in Bash tool despite a read-only description (CSDK-110). Subagent presence alone contributes claude_agent_sdk to SDKsDetected, so the Claude pack loads and CSDK-110 fires on pure-markdown subagent collections.
skill— fires once per Claude Code skill (SKILL.md, any path
depth). Input: a SkillDef parsed from frontmatter — name, description, allowed-tools → ToolGrants[], disable-model-invocation — plus body facts (dynamic-context exec commands, external URLs, prompt-injection markers) and a bundled-file inventory. Catches a skill that auto-approves unrestricted Bash (CSKILL-001), runs a dynamic-context command that performs network egress or reads secrets before the model sees it (CSKILL-003), or is model-invocable while granting side-effecting tools (CSKILL-050). Skills are markdown, so skill rules carry no language:; the claude_skill pack loads whenever a SKILL.md is present.
repo— fires once per scan against the whole inventory. Catches
project-wide gaps such as the OpenAI Agents SDK being present with no custom trace processor configured.
The agent is the unit of analysis, not the repo
A repo can declare zero, one, or many agents, across one or more SDKs. Two agents in the same repo can be in completely different security postures — one wired with input/output guardrails, the other not. Agent-scoped findings therefore attribute to a specific agent at its constructor call site; flattening them to a single repo-level verdict would lose that attribution and be wrong. Discovery builds a small per-repo graph (tools, agents, subagents, and the edges between them) so agent-scope and subagent-scope rules can query it.
Rules are scoped to one SDK and one language
A Claude-SDK rule and an OpenAI-Agents-SDK rule that detect the same conceptual problem (a missing timeout, say) are two separate rules with SDK-specific explanation and fix text — there is no cross-SDK casting. When a repo declares agents from multiple SDKs side by side, each agent is checked only against the rules for the SDK that declared it. The same holds across languages: a language: python rule will not fire on a TypeScript agent.
How it reasons — the scanning pipeline
trustabl scans in four steps. Each step's output is the typed input to the next, with no shared state between runs — and the inventory the early steps build is what makes policy selection data-driven rather than statically configured.
The binary ships with no embedded rules. Before the pipeline runs, Trustabl resolves its detection rules from a separate git repository (trustabl-rules) — fetching the latest, caching the clone locally, and falling back to the cache when the network is unreachable. This decouples rule updates from binary releases: rules can be added or changed without rebuilding the scanner. The resolved rules commit is recorded in the result and folded into the ScanID, so a scan is honest about which rules produced it. If no rules can be fetched and none are cached, the scan exits 2 and tells you to run trustabl rules pull — Trustabl never runs rule-less.
flowchart LR
target[("Agent repo(local path or GitHub URL)")]
recon["Reconfiles · SDK deps"]
inv["InventoryPython + TS AST:tools · agents ·subagents · MCP servers"]
pol["Policy selectionload rules perdetected SDK ·META findings"]
ana["Analysistool · agent · subagent ·repo detectors"]
score["Scoringper-surface score ·overall readiness"]
out[("ScanResultfindings · scores(human / JSON / SARIF)")]
target --> recon --> inv --> pol --> ana --> score --> out
- Recon — walk the repo and answer "what's in here" cheaply, without
parsing any source language: languages present (by extension), SDK dependencies declared in manifests (pyproject.toml / requirements.txt / Pipfile / poetry.lock / package.json for the claude-agent-sdk / @anthropic-ai/claude-agent-sdk / openai-agents / @openai/agents / google-adk / @google/adk needles), the file inventory, and discovered agent components (MCP configs, hook scripts, CLAUDE.md and AGENTS.md guidance docs, .claude/agents/*.md subagents at any depth, SKILL.md skills, slash commands at both .claude/commands/*.md and /commands/*.md, .claude-plugin/{plugin,marketplace}.json manifests, sandbox policies). No tree-sitter parses happen here — this step decides whether the expensive AST work is even worth attempting.
- Inventory — for each language Recon cleared, do the AST work and
extract a typed inventory: ToolDefs with their config and body facts, AgentDefs with all kwargs captured, SubagentDefs / SkillDefs / SlashCommandDefs / PluginManifests parsed from markdown and JSON frontmatter, MCPServerDefs, guardrails, sessions, and the resolved edges between agents and the tools/guardrails they reference. Detectors read fields off these structs — they never re-parse raw source.
- Policy selection — load only the rule packs for SDKs actually
observed in code. An SDK seen in code with no shipped pack emits a META-001 info finding ("Trustabl does not currently audit this SDK") — silence on an unknown SDK is wrong. A dep declared but never used in code emits a different info finding flagging the drift.
- Analysis — run the selected scope-aware detectors against the
inventory. Findings carry the scope they fired at and attribute to the right location: tool file/line, agent call site, subagent markdown file, or the manifest.
Three properties fall out of this staging, by design:
- Performance. A repo with no Python skips Python AST work; a repo
with only Claude TS code skips Python AST work AND OpenAI policy loading.
- Honest coverage. An "unaudited SDK" info finding is louder than a
zero-findings clean bill of health on an SDK Trustabl doesn't know. A META-004 finding further distinguishes "audited and clean" from "could not audit — discovery extracted nothing a rule targets."
- Determinism is a contract. Same inputs → same
ScanID, and the
report is byte-stable across runs (findings sorted by (RuleID, FilePath, Line), inventory slices sorted deterministically). CI consumers can diff scans without spurious churn.
See [ARCHITECTURE.md § 2](ARCHITECTURE.md#2-pipeline) for the full diagram with typed inputs at each step.
What's wired today
Tool/agent AST discovery is wired for:
- Python — Claude Agent SDK (decorators), OpenAI Agents SDK, Google
ADK, LangChain / LangGraph, CrewAI, AutoGen / AG2, and Pydantic AI. Discovery extracts tool definitions, agent constructors, hosted tools, MCP servers, guardrails, sessions. The bare Agent(...) constructor shared by OpenAI / ADK / CrewAI / Pydantic AI is import-gated per SDK so the classes never cross-match, and the shared @tool decorator is routed to the owning SDK by its import binding.
- TypeScript — Claude Agent SDK (the
tool()factory, the
query() main-thread QueryMainAgent, inline-in-query() sub-agents, typed-const AgentDefinitions, createSdkMcpServer and the four options.mcpServers config literals), OpenAI Agents SDK (the tool({...}) factory, new Agent({...}) and Agent.create({...}), 9 hosted-tool factories, MCP server classes across 3 transports plus the MCPServers wrapper, 4 defineX guardrail factories, and the MemorySession / OpenAIConversationsSession / OpenAIResponsesCompactionSession session classes — gated on imports from @openai/agents, @openai/agents-core, or @openai/agents-openai), and Google ADK (the new FunctionTool({...}) constructor, 5 agent constructors — new LlmAgent({...}) / SequentialAgent / ParallelAgent / LoopAgent / RoutedAgent — 13 hosted-tool classes, and subAgents edges — gated on imports from @google/adk), LangChain / LangGraph (the tool(fn, {...}) factory, DynamicStructuredTool / DynamicTool, and createReactAgent / createAgent / new AgentExecutor — gated on the @langchain/* / langchain / langgraph ecosystem), and the Vercel AI SDK (the tool({...}) / dynamicTool({...}) single-object factory, the call-based generateText / streamText / generateObject / streamObject agents and the class ToolLoopAgent / Experimental_Agent, with tools walked as an object/record, plus the .tools.*() hosted tools — gated on the bare ai import). Handles .ts / .tsx / .mts / .cts plus JavaScript .js / .jsx / .mjs / .cjs with the tree-sitter-typescript and tree-sitter-tsx grammars (JavaScript routes to the tsx grammar — a JS superset — and is audited by the same language: typescript rule packs). TypeScript rule packs ship for the Claude Agent SDK (CSDK-010/011/012/013/014/016 tool rules; CSDK-120/130/131 agent rules), OpenAI Agents SDK (OAI-016/017/019/022/024 tool rules; OAI-105 agent rule), Google ADK (ADK-013/015/016 tool rules; ADK-109 agent rule), MCP (MCP-011/012/013/014 tool rules), LangChain (LC-010/011/012/013/014 tool rules; LC-111 agent rule), and the Vercel AI SDK (VAI-001..008 tool/agent rules; VAI-012 repo rule). A TS repo for any of these no longer produces a blanket META-004; see COVERAGE.md for the full matrix.
JavaScript (.js / .jsx / .mjs / .cjs) is AST-parsed through the shared TypeScript-family pipeline: its tools and agents are discovered, tagged javascript, and audited by the language: typescript rule packs (both ES import and CommonJS require() bindings are recognized). Go has tree-sitter-go discovery for MCP tools (mark3labs/mcp-go and the official modelcontextprotocol/go-sdk), audited by the language: go rules in the MCP pack. C# has tree-sitter-c-sharp discovery for the official ModelContextProtocol SDK's [McpServerTool] methods, audited by the language: csharp rules. PHP has tree-sitter-php discovery for #[McpTool]-attributed methods (official mcp/sdk and community php-mcp/server), audited by the language: php rules. Rust has tree-sitter-rust discovery for the official rmcp crate's #[tool]-attributed methods (descriptions read from the description = "..." arg or the /// doc comment), audited by the language: rust rules; other Go, .NET, PHP, and Rust SDKs are recognized as files by Recon but not yet AST-parsed. The rule schema's language: field gates per-language rule sets.
Scope boundaries
- LLM enrichment is a separate post-scan step (
trustabl enrich). Rule-based
detection (trustabl scan) makes no network call — there is no LLM involved in the scan itself. trustabl enrich reads the scan output and calls the configured LLM provider (Anthropic, OpenAI, or Google Gemini) with BYOK (key stored via trustabl llm key set at ~/.config/trustabl/keys.json, mode 0600). Each call carries a request timeout, and --apply rewrites a file only when its current contents still match what the model reviewed (writing a .trustabl.bak backup first) — a stale scan is skipped, never mis-applied.
- Confidence scores are heuristic, not LLM-judged, and not yet
calibrated against a labelled real-agent corpus — treat findings as signal to investigate.
- The CLI is the surface. No web app, API server, or hosted service:
pipe --format json or --format sarif into your own automation. On GitHub Actions, trustabl/trustabl-action wraps the scan and uploads SARIF to the Security tab for you; for any other CI, --format sarif --output produces a SARIF 2.1.0 report that feeds github/codeql-action/upload-sarif or any SARIF-aware step.
What it produces
Trustabl is a detect-and-report tool: it does not write or modify any files in the scanned repo. Each run produces a ScanResult containing:
- Findings — one per rule hit, each with
severity,confidence,
an explanation, a suggested_fix, and the location it fired at (tool file/line, agent call site, subagent file, or the manifest).
- Per-surface readiness scores (one per discovered tool, agent, subagent,
or the repo as a whole) and an overall score (a breadth-aware, badness-weighted mean — weak surfaces pull it down harder, but a single poor surface does not zero it; the score is a triage signal, not the CI gate).
- The discovered inventory — tools, agents, hosted tools, MCP
servers, subagents, skills, slash commands, plugin manifests, and Claude settings — surfaced at the top level for CI consumers.
The summary's tool surface, broken out
The human format honestl
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: trustabl
- Source: trustabl/trustabl
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.