AgentStack
MCP verified Apache-2.0 Self-run

Octopus Inspect

mcp-octoryn-octopus-inspect · by octoryn

Catch governance holes before production does. A static governance linter for AI agent workspaces.

No reviews yet
0 installs
5 views
0.0% view→install

Install

$ agentstack add mcp-octoryn-octopus-inspect

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • 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.

Are you the author of Octopus Inspect? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

English | [简体中文](README.zh-CN.md)

Inspect

[](https://github.com/octoryn/octopus-inspect/actions/workflows/ci.yml) [](https://github.com/octoryn/octopus-inspect/releases/latest) [](LICENSE) [](package.json) [](https://github.com/octoryn/octopus-evidence)

> Catch governance holes before production does. A static governance linter for > AI agent workspaces — and a rule host: an engine that walks a workspace, > runs governance rules over it, and produces deterministic findings.

> Part of Octopus Core — the open infrastructure stack for governed AI. One job per repo, along the agent lifecycle: Scout · Observe · Experience · Blackboard · Workstate · Runtime · Replay — with Inspect governing every stage. The whole stack rides one root primitive: Evidence — the canonical, tamper-evident atom, the root category the rest of Octopus is built on. > > This repo — Inspect · Govern (every stage): Governance lint for AI workspaces.

Workspace → Rules → Findings → Report (pretty / json / sarif)

Point Inspect at a directory of AI-agent configuration — prompts, agent and workflow definitions, MCP manifests, decision records — and it flags the governance holes that don't show up until something ships: a committed secret, an untrusted variable spliced into a prompt, an auto-executing action with no approval gate, a wildcard tool grant, a decision recorded with no evidence, a workflow that can never complete, a dependency pinned to a mutable tag. Findings come back sorted and deterministic, ready for a human, a JSON pipeline, or CI code scanning.

Boundaries

Inspect is static and self-contained. It reads the tree on disk and reasons about the shape of what it finds. It does not execute your workspace, import a runtime, call a network, reproduce another system's policy semantics, or modify any file. Every built-in rule is pure: same bytes on disk → same findings, in the same order.

That last boundary is deliberate. A check that needs to understand a specific runtime's semantics — what a given policy engine actually permits, what a blackboard counts as a valid handoff — does not belong in the built-in rules; baking it in would fork the definition of "safe". Such checks arrive as plugins contributed by the runtime that owns those semantics, so there is a single source of truth for what safe means. The built-ins only flag the shape that most often hides a hole.

It is built on the first-party octopus-evidence primitive — its only runtime dependency, and the same Evidence atom the rest of the stack rides. That single dependency is what lets --format evidence turn each finding into a tamper-evident, independently-verifiable unit: your linter's findings are court-admissible without trusting the linter. The repo is otherwise self-contained and takes on no third-party dependencies.

Install & build

npm install
npm run typecheck   # tsc --noEmit
npm test            # node --test
npm run build       # emit dist/
npm run example     # run the bundled demo workspace

Requires Node ≥ 22. Inspect scans JSON / JSONC / text / Markdown files; .mcp.json and .prompt are recognized. YAML is a documented future extension and is not parsed yet (see [Boundaries](#boundaries) and [docs/DESIGN.md](docs/DESIGN.md)).

CLI

octopus-inspect [path]                 # inspect a directory or file (default ".")
octopus-inspect . --format sarif       # emit SARIF for CI code scanning
octopus-inspect . --format evidence    # emit tamper-evident Evidence per finding
octopus-inspect . --threshold warning  # fail the build on warnings too

| Option | Meaning | | ---------------- | ------------------------------------------------------------- | | --format | Output format: pretty \| json \| sarif \| evidence (default pretty) | | --config | Config file to use (default .octoinspect.json at the root) | | --threshold | Severity that fails the run: error \| warning \| info (default error) | | --no-color | Disable ANSI color in pretty output | | --version | Print version and exit | | --help | Show help |

Exit codes: 0 clean · 1 findings at or above the threshold · 2 configuration error (bad config, an unloadable plugin, a colliding rule id, or a missing path). Pretty output is colorized only on a TTY; --no-color forces it off.

GitHub code scanning (primary path)

SARIF is the standard static-analysis exchange format, and it is Inspect's primary distribution path: emit SARIF in CI and GitHub renders every finding inline on the PR, in the Security tab, and as a check. Add a step to your workflow:

# .github/workflows/inspect.yml
name: inspect
on: [push, pull_request]

jobs:
  governance:
    runs-on: ubuntu-latest
    permissions:
      security-events: write   # required to upload SARIF
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
      - run: npx octopus-inspect . --format sarif > inspect.sarif
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: inspect.sarif

The SARIF run carries each rule's id, title, description, and default level, so findings land already labeled and grouped. To also fail the job on findings, run a second octopus-inspect . (pretty or json) with your chosen --threshold; the upload step above never fails the build on its own.

GitHub Action (drop-in)

A composite action wraps the SARIF path above so any repo can adopt it in two steps: run Inspect, then upload the SARIF. The action runs the published npm package via npx — no build, no checkout of this repo. The SARIF upload is kept out of the action so permissions stay in the caller's workflow (the security-events: write grant belongs to you, not to a third-party action).

# .github/workflows/inspect.yml
name: inspect
on: [push, pull_request]

permissions:
  security-events: write # required to upload SARIF
  contents: read

jobs:
  governance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - id: inspect
        uses: octoryn/octopus-inspect@v0.3.2
        with:
          path: .
          fail-on-findings: "false" # report without failing the build
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: ${{ steps.inspect.outputs.sarif-file }}

Findings then render inline on the PR, in the Security tab, and as a check. Set fail-on-findings: "true" (the default) to also fail the step when Inspect exits nonzero — combine that with args: "--threshold warning" to choose what severity blocks the build.

Inputs

| Input | Default | Meaning | | ------------------ | ----------------------- | ------- | | path | . | Workspace directory or file to scan. | | args | "" | Extra CLI arguments passed through to octopus-inspect (e.g. --threshold warning). | | version | 0.3.2 | npm version/spec of octopus-inspect to run (octopus-inspect@). | | sarif-file | octopus-inspect.sarif | Path the SARIF report is written to. | | fail-on-findings | true | When true, a nonzero Inspect exit (findings at/above the threshold, or a config error) fails the step; when false, the step always succeeds so the SARIF can still be uploaded. |

Outputs

| Output | Meaning | | ------------ | ------- | | sarif-file | Path to the SARIF report the action produced — feed it to upload-sarif. |

The CLI writes SARIF to stdout; the action redirects that to sarif-file and captures the exit code so fail-on-findings can gate the step independently of the upload.

Programmatic API

Everything the CLI does is available as a library:

import { inspect, shouldFail, formatSarif } from "octopus-inspect";

const report = await inspect("./workspace");

for (const f of report.findings) {
  console.log(`${f.severity}\t${f.file}:${f.line ?? 0}\t${f.ruleId}\t${f.message}`);
}

console.log(report.summary);              // { error, warning, info }
console.log(shouldFail(report, "error")); // would CI fail?

const sarif = formatSarif(report);        // SARIF 2.1.0 string

inspect(root, options?) returns a deterministic InspectReport ({ root, findings, fileCount, ruleCount, summary }); pass { config, rules } to override either. formatPretty and formatJson are the other two reporters. The full built-in rule set is exported as builtinRules.

Built-in rules

Seven static rules ship in the box. Each has a stable id and a default severity you can override or disable in config.

| Id | Default | Kind | What it flags | | --------------------------- | --------- | ---------- | ------------- | | secret-in-source | error | text | Hardcoded credentials — cloud keys, provider API keys, private-key blocks, generic secret = "…" assignments — committed to the tree. Placeholders (your-…, ${…}, process.env) stay quiet; the matched secret is redacted in the finding. | | prompt-injection-sink | warning | text/json | A prompt or instruction template that splices an untrusted variable ({{ user_input }}, ${userMessage}, %(query)s) straight into the instructions, where it could smuggle in adversarial instructions. | | unsafe-autonomy | error | structural | A config object that marks itself auto-executing (autonomy, autoApprove, requireApproval: false, …) next to a side-effect marker (execute, command, tool, …) while declaring no approval or policy field. | | overbroad-permission | warning | structural | A wildcard (*, all, *:*) in an allowlist, tool list, or scope grant — every capability allowed instead of a least-privilege set. | | missing-evidence | warning | structural | A record stating a decision / claim / conclusion that carries no evidence / source / rationale / citation field. Shape only — it does not judge whether the evidence is good, only that some is present. | | circular-workflow | error | structural | A dependency cycle among workflow/agent steps (via next / then / dependsOn / requires / needs edges) parsed from JSON. A cyclic graph can never complete. | | unpinned-agent-dependency | info | structural | An MCP/agent launch arg (npx pkg@latest) or manifest dependency pinned to a mutable tag (latest, *, next, …), where the executed code can change without review. |

"Structural" rules read the parsed shape of JSON/JSONC objects; they never reason about a runtime's semantics — that boundary is what keeps "safe" defined in one place. secret-in-source and prompt-injection-sink also scan plain text and prompt files.

OWASP Agentic Top 10 mapping

Every built-in rule is tagged with the category it addresses in the OWASP Top 10 for Agentic Applications (2026) — surfaced in SARIF (properties.tags, e.g. OWASP-ASI-2026:ASI01) so findings slot into the taxonomy security teams already use.

| Rule | OWASP Agentic 2026 | |---|---| | secret-in-source | ASI03 · Identity and Privilege Abuse | | prompt-injection-sink | ASI01 · Agent Goal Hijack | | unsafe-autonomy | ASI02 · Tool Misuse · ASI09 · Human-Agent Trust | | overbroad-permission | ASI03 · Identity and Privilege Abuse | | missing-evidence | ASI09 · Human-Agent Trust Exploitation | | circular-workflow | ASI08 · Cascading Failures | | unpinned-agent-dependency | ASI04 · Agentic Supply Chain Vulnerabilities |

Inspect's static rules cover the subset of the Top 10 visible in committed workspace config; runtime-only risks are out of a linter's scope by design. The catalog is exported (OWASP_AGENTIC_2026, owaspLabel) for use in plugins.

Configuration

Inspect looks for .octoinspect.json (JSONC — comments allowed) at the root, or takes an explicit --config . A malformed or absent config is not an error; the run proceeds with defaults.

{
  // Extra ignore globs, added to the built-in defaults (node_modules, .git, dist, …).
  "ignore": ["fixtures/**", "**/*.generated.json"],

  // Per-rule overrides: "off" disables a rule; a severity re-grades it.
  "rules": {
    "unpinned-agent-dependency": "off",     // don't care in this repo
    "prompt-injection-sink": "error",       // treat as blocking here
    "missing-evidence": "info"              // downgrade to advisory
  },

  // Plugin module specifiers, resolved from the workspace root.
  "plugins": ["./inspect-plugins/runtime-policy.js"],

  // Skip files larger than this many bytes (default 1 MiB).
  "maxFileBytes": 524288
}

A rule set to "off" is not run at all. A severity override (or a plugin rule's own default) wins over the built-in default; that effective severity is what the threshold and the exit code compare against. See [.octoinspect.json](.octoinspect.json) for a fully commented example.

Plugins

Inspect is a rule host. When a check needs a specific runtime's semantics, that runtime ships the rule as a plugin and Inspect hosts it — so the definition of "safe" is never forked into the built-ins. A plugin is just a module that exports a { name, rules } object (as the default export, a named plugin, or the module shape itself):

// inspect-plugins/runtime-policy.ts
import { definePlugin, type Rule, type Workspace } from "octopus-inspect";

const noProdWrites: Rule = {
  id: "runtime/no-prod-writes",
  title: "Agent may write to production without a policy",
  description: "Rejects a manifest that grants prod write scope with no attached policy.",
  severity: "error",
  check(workspace: Workspace) {
    // Read the shape of the workspace; return raw findings.
    return [];
  },
};

export default definePlugin({ name: "runtime-policy", rules: [noProdWrites] });

Reference it from config and its rules run alongside the built-ins:

{ "plugins": ["./inspect-plugins/runtime-policy.js"] }

Relative specifiers resolve against the workspace root; bare specifiers resolve as normal node modules. Duplicate rule ids are rejected (a plugin can never silently shadow another rule), and a plugin that fails to load is reported as a configuration error rather than aborting the run. The parseJsonc, walkJson, isJsonObject, and findKeyLine helpers are exported to make structural plugin rules easy to author.

A complete, working reference plugin lives at [inspect-plugins/runtime-policy.ts](inspect-plugins/runtime-policy.ts): a semantic rule that derives its verdict from octopus-runtime's real autonomy model rather than re-encoding it. See [docs/PLUGINS.md](docs/PLUGINS.md) for the walkthrough — this is the pattern to copy for a semantic check a static linter can't do alone.

Design

The authoritative architecture and contracts live in [docs/DESIGN.md](docs/DESIGN.md) — the rule-host philosophy, the Workspace → Rules → Findings → Report pipeline, the determinism guarantees, and the config/plugin model. Read it before making ch

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.