Capabilities
A 85-threat matrix plus content rules, across everything the agent touches: what's typed, what it reads, what it passes to a tool, and what it says back.
Enforcement the agent can’t switch off.
Where an agent runs unattended, MoorAI’s checks sit in root-owned managed settings or in your own code, where neither the agent nor the repository it works on can turn them off. Either way the check is continuous, not a point-in-time assessment: each tool call is checked before it runs, and on a laptop, where the developer can switch MoorAI off, an enrolled device tells the console when coverage lapses.
/etc/claude-code/managed-settings.json as root and runs the agent as an unprivileged user. Claude Code ranks managed settings above user, project and local ones, and only managed settings can disable managed hooks. A settings file that tries to repoint MoorAI’s console or supply its trust keys is ignored and reported to the console as tampering, by name only.claude -p run. An Agent SDK service and a GitHub Actions run have not been watched end to end.@moorai/agent-sdk hands the Claude Agent SDK the same hooks, inside your service’s process, so the model has no way to skip them. Claude Code keeps hooks the Agent SDK registers in process running even when managed settings disable every other hook. A call that would wait for sign-off is denied, because no one is there to approve it.moorai-serve, gives an OpenAI Agents SDK, LangGraph, CrewAI or custom loop the hook’s allow or deny for each tool call, from your code before the tool runs. A local gateway sits in front of remote MCP servers and, on an enforcing device, refuses an explicitly denied call instead of forwarding it.bypassPermissions. That is detected, not prevented: an enrolled device sends a daily, content-free posture heartbeat, and the console flags an agent in use with no MoorAI hook traffic, a weakened setting, and a hook removed or gone stale.FAIL_CLOSED alert, and the console shows every device’s effective mode, flagged when it fails open against a closed policy.Built into every install.
Seven core controls, each on-device and content-free.
browser-ext/ in the open-source repo, not in an extension store — same content-free contract as the endpoint agent.moorai scan at a skill, agent, or MCP config before you install it and get a verdict (clean, caution, review, or do-not-install) from the same on-device engine that enforces at runtime. Reverse shells, credential reads, poisoned MCP descriptors and injected instructions are caught in the artifact, not after it’s running. The verdict is the engine’s own enforcement decision, not an invented score.traceId and spanId, the model proxy and the MCP stdio guard send a traceparent on, a sub-agent’s records sit under the call that spawned it, and the console’s trace view draws the session’s span tree across the hook, the model proxy, the MCP gateway and guard. On Claude Code, a per-session context tree records one content-free leaf for each prompt, file read, web result, MCP result, command output and sub-agent hand-off, and every record carries the tree’s root at the moment of decision. An injection alert names the leaf that triggered it, and moorai-context-proof --verify <file> --all finds every session on the device that took in a given file, with an RFC 6962 inclusion proof. With the policy set to send leaves, the console’s context tab re-verifies each record’s root, a sub-agent’s against its own tree.Five jobs for local-agent security, and how far MoorAI goes on each.
Each job gets a status: covered or partial.
Scroll sideways →
| Use case | Status | How MoorAI handles it |
|---|---|---|
| Supply chainMCP servers, skills, packages | Partial | Every MCP server your agents call is a vendor nobody assessed, so MoorAI treats each one as a third party. moorai scan checks a skill or agent artifact on the device before you install it. MCP servers need approval to join an allow-list, and a server that changes after approval gets flagged. Rules also cover poisoned CLAUDE.md and .cursorrules files and typosquatted package names. A skill or instruction file that hides its directive from the person installing it is labelled by technique: padding, HTML comments, concealment wording, text addressed to a reviewer, or an order to write itself into the agent’s memory. Each MCP server gets a 0–100 reputation score the first time it is seen and again when its version changes, and an org can refuse servers below a minimum score. Model files can be pinned by hash: only a file whose SHA-256 an admin approved loads through an agent’s llama-server, vllm serve, ollama run or Python load without an alert, a hold or a block, by policy.
The same check runs free at SkillTriage, which also publishes a catalogue of scanned MCP servers and skills.
|
| Unintended damagethe agent runs something destructive | Partial | Destructive commands are coached, alerted on or blocked, depending on policy. They include commands that remove a machine’s way back: deleting shadow copies, disabling boot recovery or Windows Server Backup, and deleting Time Machine, APFS, ZFS, Timeshift, ESXi or cloud snapshots, backups and recovery points (ATT&CK T1490). The alert and block levels put a human in the loop, and a critical hit can kill the agent process instead of just denying the one call. In the model proxy, function calls are classed by name as money, bulk data, identity, destructive or delegation actions, each set to allow, alert, ask or block. |
| Indirect prompt injectioninstructions hidden in data the agent reads | Partial | Named rules for indirect and second-order injection and for invisible or obfuscated text run on the web pages, files, command output and MCP results the agent reads. An injection no rule recognises is covered by where the file came from: reading a downloaded file or a messaging-app attachment taints the session by its origin, with no content analysis, and for 30 minutes uploads, memory-file writes, MCP or skill installs, running an outside file, git push and sending messages notify, ask or block at the admin’s level. With the quarantined reader turned on, such a file reaches the agent only as a structured summary written by a separate model with no tools. When the agent writes an instruction into its own memory or instruction files (Claude Code auto-memory, CLAUDE.md, Codex AGENTS.md, Cursor, Windsurf, Cline and Copilot rules, skills) that would send data out, hide something from you, skip a check or override its rules in future sessions, MoorAI raises memory poisoning (#22), through the agent’s file tools or a shell echo >>, tee or heredoc, and runs the same check when those files load at session start. Any write into those files, by a Write or Edit, an MCP filesystem server’s write tool, or a shell copy, move, download, patch or in-place edit, also raises a content-free alert with no policy at all, which an admin can raise to ask or block. Content headed into a knowledge base or vector index raises knowledge-base poisoning (#21) when a passage addresses the AI that will retrieve it, tells it to ignore the other sources, forces an answer or carries hidden instructions; this is poisoning at retrieval time with an instruction in it, not poisoning of a model’s training data. A false fact that gives no instruction is caught two ways: a price, policy, payment or authority claim from an author the app marks untrusted or unknown is reported, held or blocked by policy, and two passages retrieved into one request that give the same product different prices, or the same payee different account numbers, raise conflicting facts (#21). A false fact from a trusted or unlabelled author, with nothing retrieved beside it to contradict it, is not detected. Ambiguous cases can go to a local model through Ollama.
|
| Blast radiuswhat the agent can reach | Partial | You declare an entitlement envelope for each agent (the tools, paths and MCP servers it may use), and MoorAI alerts or blocks when the agent drifts outside it. It also flags excessive agency, and the lethal trifecta: private data, untrusted input and an outbound channel in the same agent. On Claude Code, a risky call aimed at a host, path or service the user’s own request never named is reported as outside the stated task. Task-bound tool calls go further for high-impact calls: the target of an MCP write, a payment, a role grant or any function whose name carries a write verb must be named in the request, and for a task an admin lists, only the listed tools and argument values pass. |
| Adoption governancewhich agents, apps and accounts are in use | ✓ Covered | Discovery lists the agents, AI desktop apps, browser extensions and MCP servers on each device, and marks agents as managed or unmanaged. Accounts are sorted into personal and corporate by email domain. On enrolled devices a daily check reports local model servers reachable without MoorAI and unknown programs calling AI provider APIs. Results export as an AIBOM, and fleet policy is set in the management console. |
What MoorAI does, row by row.
Each row is a yes, or the scope it applies to.
claude -p, codex exec, an agent CLI install, a cron or launchd run)Reports by default; ask or block by policyPrompts, files and MCP tool calls,
reviewed before the AI sees them.
Before anything an employee types reaches an AI tool, MoorAI checks it on the computer, against the rules you set. The same local engine also checks the files an agent reads into its context, the arguments it passes to MCP tools, and the answers it sends back. Anything sensitive gets flagged or stopped, and the console never receives what your people write unless an administrator turns on a capture tier.
.env full of live keys is caught before that content lands in the model's context. Enforcement on Claude Code runs through its PreToolUse hook and is validated end to end. Codex, Copilot CLI, Gemini CLI and Cursor get the same deny through their own pre-tool hooks; those adapters are tested against each vendor's documented payloads and not yet against the live agents.mcp__* tool call for secrets and PII before the call runs, and blocks per policy. The MCP surface is where agents quietly hand data to third-party servers — this is the check on that path.claude -p CLI guard masks flagged secret spans before they're printed, so a key the agent echoes out of a config file doesn't land in a terminal log or a pasted transcript.CLAUDE.md, AGENTS.md, GEMINI.md, and the Copilot, Cursor, Windsurf and Cline rules) when a substantial share of one shows up in what the agent writes, in a shell upload, a WebFetch or MCP arguments, or is uploaded by path, as in curl -d "$(cat CLAUDE.md)". Each file is fingerprinted on the device as keyed hashes of 7-word shingles; no text is stored. Editing the rules file itself and quoting a line or two stay silent. Report-only (threat #52). A paraphrase or translation is not matched.CLAUDE.md, AGENTS.md, a rules folder or the agent’s memory steers every later session. MoorAI watches those files with no policy at all: a write raises a content-free alert, with no prompt, and an admin can make it ask or block. It reads the direct writes and the indirect ones: cp, mv, install, rsync, curl -o, wget, dd, git checkout and git restore, patch, tee, sed -i, heredocs and an MCP filesystem server’s write, edit and move tools. A command it cannot parse is held, never passed as clean. A write whose target is known only at run time (a variable, a glob, a download named by the server, an abbreviated or unknown option, a work tree or config set from the environment) raises its own alert, write-target-unknown, or is held for a decision where protected paths are declared. Part of threat #84, weakening oversight.moorai-serve offers the same check at /v1/file-scan. Hash mode sends only the file’s SHA-256; upload mode is an explicit opt-in. A malicious file blocks the request or alerts, by policy; an unknown one follows its own setting: allow, ask or block. What is scanned is what is sent: a file field is read under every key case Ollama’s parser accepts (Images, IMAGES), and in enforce mode a request that repeats a JSON key or gives one field two case variants is refused rather than guessed at. Scanning is off until an admin turns it on, and keys come only from the server’s admin binding, key file or environment.Zone.Identifier for the Internet or an untrusted zone, or sits in the download folder. When the agent reads one (a Read, cat, textutil or pandoc, an MCP filesystem read), unpacks an outside archive or runs an outside file, the session is tainted for 30 minutes, with no content analysis. In that window six actions take the admin’s level, each set on its own: an upload or other outbound transfer, a write to a memory or instruction file, adding an MCP server or installing a skill or plugin, running an outside file, git push, and sending a message. Notify is the default; ask and block are policy settings. Removing an origin mark (xattr -d com.apple.quarantine, Unblock-File) raises an alert every time. Alerts name the source kind, the app and a keyed host hash, never a path or a host (threat #3). On Linux, the download folder is the signal.fileQuarantine. Then a file from outside reaches the agent only as a structured summary: a separate model with no tools, local unless the admin allows otherwise, reads it and returns the document type, a short summary, key facts and any text aimed at an AI, framed as untrusted data. A Read is pointed at the summary; other reads are refused with the summary in the reason. Every detector runs over the summary, and one that carries an injection is withheld. Under "ask" the person can still approve the raw file. The reader runs under its own OS account on Linux; on macOS the admin pins a same-user reader by executable path and code-signing Team ID. MoorAI checks which process accepted the connection before it sends anything. Each read raises one content-free alert with its outcome (threat #3). Origin taint stays on, since a summary is still model-written text.modelPins) and picks report, ask or block (modelPinMode), in the signed policy or the root-owned machine config, never a user file. An agent’s llama-server -m and the other llama.cpp tools, vllm serve, ollama run and ollama create, and Python loads of a local path in the command’s own code (torch.load, pickle, joblib, np.load, load_model, from_pretrained) are checked before they run; an Ollama blob is checked against its own hash, never its name. The download scan and the store scan mark every unpinned file not-approved, and a pin never overrides a static finding. A file too large to hash in time is asked or denied by mode, never allowed silently. Alerts carry the load kind, format, a size class and the first 12 hex of the hash, never a path or a model name (threat #76). A pin proves which file runs; task-bound tool calls check what it then does.intentArgs names), the call’s target must be named in the request. Under “cut the apple”, cut(target: "apple") passes and cut(target: "hand") is object-not-in-task; it reports by default, and policy can ask or block. Task allowlists: when a session’s task matches an entry, by keywords or a task id the service sets, only the tools and argument values it lists pass (exact values, globs, numbers or ranges), an unlisted argument is denied, and anything else is held at the entry’s deny or ask. An entry that cannot be read denies what it would have covered and raises an alert. Runs in the model proxy, moorai-serve, the Agent SDK and the endpoint hook on MCP calls. The task itself is never stored, only keyed hashes of its words, and alerts carry no argument (threat #64). It matches words, not meaning..env, cloud credentials) on the device as keyed one-way hashes and blocks an outbound command or tool call that carries one, verbatim or encoded: base64 or base64url, padded or not and alone or inside a larger blob, hex, percent-encoded, JSON-escaped, reversed or wrapped across lines. A secret the agent reads during the session, a provider token in a file, a command’s output or an MCP result, or a KEY=value line of a credential file, joins the check for the session-risk window (30 minutes by default). When MoorAI cannot be sure it saw every secret, the call is an unverified match that secretEgress.unverified decides: ask by default, or block or report; on a host with no one to approve, ask becomes deny. Only the hash prefix, the encoding and the source kind leave the device. Encryption, compression and custom encodings are not covered.Invoke-WebRequest sending a body, -T or a non-GET method to a public object-storage endpoint (Amazon S3, Azure Blob, Google Cloud Storage, Alibaba OSS, Cloudflare R2, Tencent COS, Huawei OBS, DigitalOcean Spaces, Backblaze B2 or Wasabi), matched by the provider’s own endpoint naming rather than the bucket name, or to a pre-signed URL on any host (X-Amz-Signature, OSSAccessKeyId, an Azure SAS sig=, X-Goog-Signature). Uploading to your own bucket is ordinary work, so it reports by default; a policy can ask or block (threatPolicy["86"]), and that setting applies to every bucket, your own included. A download, signed or not, is not an upload. Uploads through scp or rsync, or through the providers’ own CLIs (aws s3 cp, gsutil cp, az storage blob upload, rclone), are outside this rule (threat #86; ATT&CK T1567.002, partial).claude -p, codex exec, gemini -p, aider --message, cursor-agent -p, opencode run, goose run, also through npx or uvx), installs an agent CLI (a global npm install, pip, pipx, uv, brew, winget, or a vendor install script that is run), or schedules one through cron, launchd, systemd, at or a Windows scheduled task. It reads the command the way the shell runs it, so a name in a commit message, a README or a fetched page is not a launch, and --help and --version never are. The agent’s own sub-agent feature is tracked separately. It reports by default; a policy can ask or block (threatPolicy["88"]).node_modules files) it alerts on 148 and blocks none; 45 of 87 attacks alert, measured in-sample. It adds one hook process per Bash, MCP or sub-agent call, about 82 ms at p50. PowerShell output is scanned the same way as Bash output, and the other agents’ adapters forward only web results.[MOORAI:secret:…], derived from a keyed hash so no part of the value survives, re-scans the rewritten call and lets it proceed. It rewrites Claude Code tool inputs (a command, file content, MCP arguments, a sub-agent prompt) and tool results, and never auto-approves a call. Where it cannot rewrite — a Read, the other agents’ adapters, a device with no account — the policy’s fallback applies. Masking is built from Claude Code’s hooks reference and has not yet been observed in a live session.moorai-doctor checks, read-only, whether MoorAI is actually protecting a machine: its hook registration in Claude Code, Codex, Cursor, Gemini and Copilot, Claude Code managed settings that can switch hooks off, enrollment, which policy is enforced and whether its signature verifies, and a live self-test of the real hook. moorai-explain answers “why did MoorAI flag this?” for any string: each finding, the policy action, the final decision and the safer alternative, locally. Step by step in the quick start.scanBeforeEmbed(chunks) and guardEmbed(embedFn) in the Agent SDK for an app that runs its own ingestion, POST /v1/index-scan on the local sidecar for any framework, and the MCP proxy and gateway, which scan the document arguments of vector-store write tools before the server sees them. The write tools were read from each server’s source: Chroma’s add and update documents, Qdrant’s qdrant-store, Pinecone’s upsert-records, Weaviate’s insert and upsert, mem0 and OpenMemory’s add and update memory, Milvus’s insert, the OpenSearch memory tools, Redis’s set_vector_in_hash, LanceDB’s ingest_docs, and the MCP memory server’s create_entities, create_relations and add_observations. Run against the real Chroma and Qdrant MCP servers under a blocking policy, a poisoned document was refused and never stored. It reports by default and blocks when policy says so. An HTML comment carrying a system: directive is the kind of delayed hijack this catches. An app that embeds without calling MoorAI, or an in-process vector library with no MCP or API hook, is not covered.components[] shape for GRC, audits, and third-party risk reviews.One view of the fleet’s AI use.
The console’s At a glance view counts the AI agents, MCP servers, models and devices in use, with what’s new in the period. Four use-case cards show the share of the fleet affected, the top detections with share, count and MITRE ATLAS id, and incidents against the previous period, over the last 7, 30 or 90 days.
Mapped to the frameworks auditors ask about.
MoorAI maps its threat model and content-free telemetry to these frameworks, and rolls it into a board-ready readiness report and an on-device AIBOM for the EU AI Act. The answer to “how do you govern AI use?” comes with evidence drawn from redacted signals, never from what anyone typed.
An AI-readiness report
your board can read.
MoorAI rolls its content-free telemetry into a single board-level report: a composite trust score, shadow-AI exposure, endpoint posture, framework control-mapping, and a data-lineage-by-agent trail. All of it is built from redacted signals, never prompt content.
See the full layout — composite score, framework-mapping summary, and a data-lineage-by-agent table — rendered with example data.
Discover the shadow agents already on your dev laptops.
You almost certainly have unmanaged Claude Code, Cursor, and Copilot installs running on developer machines, and no visibility into what they read, send, or exfiltrate. MoorAI inventories every AI agent, app, account, MCP server, and browser extension on each device from redacted, content-free signals, then lets you govern them: which agents and MCP servers are approved, and what each is allowed to do.
Review it
on the device.
Coach, alert, or block — before the prompt reaches the agent.