AI agents & MCP

Your AI assistant can ask RepoGates about a repository before it clones, downloads or installs from it. This page is how to wire that up — and exactly what it does and does not buy you. ← All documentation

Read the boundary before relying on it. RepoGates gates browser downloads. It does not see git clone, package managers or curl — outside Claude Code with the RepoGates plugin, whose hook refuses a clone or install that names a blocked repository on the command line, before it runs. The MCP server changes the direction of the question — instead of RepoGates observing the agent, the agent asks RepoGates. An agent that never calls these tools is not stopped, exactly as curl is not stopped. What the server provides is a safe path that costs one cheap call, a hard-gate verb an organisation can require in its agent policy, a preflight verb for the exact command line, and a way to verify the connector is actually wired up. Outside the plugin's hook it is not enforcement, and anyone describing it as blocking AI agents is overclaiming.

What connecting actually buys you

Every row below is something the browser extension alone cannot do. The limits are in the second table because a list of benefits that omits them is the overclaiming this page just warned you about.

What you getWhy it matters
Cover for the fetches the extension cannot see The extension gates browser downloads. An agent running git clone, npm install or curl is invisible to it. Connecting inverts the direction: the agent asks before it fetches.
A verb that fails closed assert_allowed raises on BLOCK, on an active campaign, and when the repository cannot be assessed at all. A body without a real verdict is never treated as a pass.
Your agent and your browser cannot disagree One implementation of the 22 checks answers both. There is no second engine to drift.
Judge the maintainer, not just the repository check_owner reports account age, what the owner publishes, and their worst-scoring repository — the signal that catches a fresh account with one plausible-looking project.
Assess a URL before anything fetches it check_download_url resolves release assets and raw links, and refuses an opaque CDN URL rather than inventing a repository name for it.
Re-checking is free Cached lookups are uncounted; only a genuinely new assessment touches the hourly cap.
Not tied to a browser Any MCP client on any platform, including CI and headless agents where no extension exists.
Enforcement where a hook exists Inside Claude Code, the RepoGates plugin runs a hook before every Bash tool call and refuses a clone or install that names a blocked repository — the one place the answer is not advisory.

And what it does not buy you. Three limits, stated plainly because each one is a thing somebody could reasonably assume:

It is not enforcement. An agent that never calls these tools is not stopped. This is the same boundary as above, restated because it survives connecting the server. The one exception is the Claude Code plugin's hook, which sees Bash tool calls in that client and nothing else — see 1b.

It can start a deep scan only with your explicit yes, each time. On a REVIEW the agent may offer to scan the files; deep_scan refuses unless it is called with confirm=true, which the tool's own description tells it to do only after you agreed and were told the cost (one scan credit). A BLOCK is never scanned — it stays blocked — and a clean PASS is not scanned by an agent. Agents may start five scans a day per account; the rest you start from your account page.

It does not apply your extension's allowlist. That list lives in the browser and is applied there. A repository you allowlisted for downloads can still come back BLOCK through an MCP tool, and assert_allowed will raise on it.

1. Connect your platform

One endpoint serves every client — Streamable HTTP, stateless:

https://mcp.repogates.com/mcp

The extension generates these same lines for you (Options → AI agents), including for self-hosted server URLs, so the product and this page cannot drift apart.

You sign in once, and the agent never holds a secret of yours. The server requires OAuth 2.1, so the setup lines below are the whole configuration — there is no API key to paste and nothing to keep out of version control. On its first call your client discovers the authorisation server, opens a browser, and asks you to approve the connection; after that it holds a token it refreshes on its own. You can revoke it from your account page, which takes effect on the agent's next call.

The MCP server and the Claude Code plugin are part of Personal, Premium and Team, which is what the pricing page has always said. Signed out, a tool call answers 401 mcp_sign_in_required; on a free account it answers 402 mcp_requires_plan. Neither is a verdict — no tool reports "allowed" because it could not ask.

The plugin's hook uses a personal API token instead of OAuth: created on your account page, shown once, saved to ~/.config/repogates/token from a terminal. A token reads verdicts and nothing else — it cannot open the account page, change a plan or start a scan — and you revoke it on the same page.

PlatformSetup
Claude Code claude mcp add --transport http repogates https://mcp.repogates.com/mcp
Claude.ai / Claude Desktop Settings → Connectors → Add custom connector → https://mcp.repogates.com/mcp
ChatGPT Settings → Connectors → Add → https://mcp.repogates.com/mcp (remote HTTPS only — a localhost URL will not work here)
Microsoft 365 Copilot Copilot Studio → Add tool → Model Context Protocol → https://mcp.repogates.com/mcp
GitHub Copilot (VS Code) .vscode/mcp.json:
{
  "servers": {
    "repogates": {
      "type": "http",
      "url": "https://mcp.repogates.com/mcp"
    }
  }
}
Gemini CLI gemini mcp add --transport http repogates https://mcp.repogates.com/mcp
Grok grok mcp add repogates https://mcp.repogates.com/mcp
Cursor · Windsurf · Cline mcp.json:
{
  "mcpServers": {
    "repogates": {
      "url": "https://mcp.repogates.com/mcp"
    }
  }
}
DeepSeek No first-party MCP client — use any MCP-capable harness (Cline, Continue) pointed at a DeepSeek model, and add RepoGates there.

What it costs you. Assessing a repository RepoGates has not seen recently is capped at 60 per hour per account — that is one account's share of the GitHub budget the whole product draws on, and exceeding it answers 429 mcp_rate_limited rather than degrading everyone else's verdicts. Re-checking a repository already assessed is served from cache, costs nothing and is not counted, so an agent that asks about the same dependency repeatedly never approaches the cap.

1b. The Claude Code plugin — the one hook that refuses

Everything above is advisory: the agent asks, and what it does with the answer is up to it. Claude Code has a hook mechanism, so there the answer can be enforced. The RepoGates plugin runs a PreToolUse hook before every Bash tool call, sends the command line to POST /v1/preflight — parsed on the server, never executed, with URL credentials and NAME=value assignments redacted first — and acts on the decision. It also adds the MCP server and the vetting skill.

/plugin marketplace add https://repogates.com/plugin/marketplace.json
/plugin install repogates@repogates

The plugin is published from this site and nowhere else. That marketplace file names one plugin, the zip it comes from and the zip's sha256, so what installs is exactly what we built; /plugin update picks up a new version when the file changes. Prefer a folder you can read first? Download repogates-claude-plugin.zip, unzip it, and add the folder as a local marketplace: /plugin marketplace add ./repogates-claude-plugin.

Then create a personal API token on your account page and save it from a terminal, not through an agent:

umask 077; mkdir -p ~/.config/repogates; pbpaste > ~/.config/repogates/token

Run /repogates:setup to check it: it exercises the hook against a permanently blocked canary repository without ever printing the token. On a REVIEW, /repogates:scan owner/repo scans the files before you decide — one scan credit, and the command asks first.

RepoGates saysThe hook does
An active malware campaign, or a finding your policy profile blocks — hidden Unicode in agent rules, a nested bare repository, any CRIT the profile does not downgrade Refuses the command, with the reason and a report link.
A finding your policy warns on — every REVIEW, and the CRITs the shipped profiles downgrade (a devcontainer, an install script); could not be assessed; the repository is a variable or a subshell; hourly cap reached; not answered in time Asks you, with the reason and what decided it. In a headless run an ask cannot be answered, so the command does not run.
PASS Adds a one-line note. Claude Code's own permission prompt still applies to the rest of the line — a PASS is never a permission.
Nothing — the line names no GitHub repository Says nothing, and no request leaves the machine.
No answer — no token, an unknown or revoked token (401), no paid plan (402), rate limited (429), the service unreachable or answering 5xx, a missing tool, an interrupted hook Asks you, naming the cause and the fix. No answer is not permission.

Lines it recognises: git clone in every URL form, git submodule add, git remote add, gh repo clone, gh release download -R, npm/pnpm/yarn/bun installs with github:, o/r or git+ specs, npx degit, pip/uv/poetry with git+ specs, go get, cargo --git, and any github.com or githubusercontent.com URL whatever the verb — curl …/raw/o/r/… | sh included — also inside bash -c, eval, sudo, chains and heredocs. Registry packages too: npm install express, pip install flask, npx, yarn add, uv add — RepoGates asks npm or PyPI which source repository the package declares and assesses that one. A package that declares none is allowed, with a note; a registry that does not answer is asked about. Direct dependencies named on the command line only; transitive dependencies and lockfiles are not resolved. The repository is assessed, not the artifact: the tarball or wheel the registry serves for the version you install is not fetched or compared with it.

What it does not cover. Anything that is not a Bash tool call: Read/Edit/Write, MCP git tools, a human at a terminal, another agent. A command whose repository is not literally on the line (git clone $REPO, git submodule update, a manifest such as package.json) — those are asked about, not allowed. The dependency tree: a named package is resolved to its own repository, the packages it pulls in are not. Sessions with hooks disabled. A hook killed at its 15-second timeout is treated by Claude Code as "proceed", which is why the hook's own network budget is 12 seconds at most. Windows is best effort. Verified against Claude Code 2.1.251 on 2026-09-07: a refusal holds in headless runs and inside subagents, and an "ask" that cannot be answered stops the command; on 2026-09-09, with the plugin installed from the public marketplace, the same refusal held under --dangerously-skip-permissions.

1c. Claude.ai and Cowork — the skill

Claude.ai and Cowork have no hook, so nothing there is enforced. What they can have is the same behaviour the plugin carries, as a skill: repogates-vetting tells Claude to check RepoGates before any clone, download or install, to stop on a refusal and never route around it, to tell you what fired on a warning, and to say so plainly when the connector is missing. It is one file, packaged as a zip and served from this site — not fetched from GitHub:

Download repogates-vetting-skill.zip

  1. Attach the connector. Claude.ai: Settings → Connectors → add a custom connector with https://mcp.repogates.com/mcp. It signs you in with OAuth the first time a tool runs; a paid RepoGates plan is required.
  2. Upload the skill. Settings → Capabilities → Skills → upload the zip above. Re-upload it when this page says the skill changed (the download is always the current one).
  3. Ask for a clone or an install. Claude calls repogates_status to prove the connector is there, then preflight on the exact line — git clone …, npm install express, pip install flask — and acts on the answer: on allow it proceeds under Claude's own rules; on ask it tells you what fired and waits; on a refusal it stops, quotes the reason, and does not fetch the repository another way. On a REVIEW it may offer a deep_scan of the files — one scan credit, only after your yes to that call.
  4. Each tool call asks you first. The "Allow once / Always allow" card is Claude's own permission prompt for a connector tool; it is not RepoGates, and it is one more place a human is in the loop.

What this is not: enforcement. An agent that ignores the skill is not stopped, exactly as curl is not stopped — the skill says so itself, and the response to every call says enforced: false. Inside Claude Code the same lines meet the plugin's hook, which refuses a denied line before the Bash tool runs it — there is no prompt to argue with.

1d. What it looks like — five real runs

Five runs in Claude.ai on 2026-09-09, connector attached, skill uploaded, engine 1.2.0. The sidebar is cropped; nothing else is edited. Each shows the moment the answer came back and what Claude did with it.

Claude reporting a git clone preflight: decision ask, two HIGH provenance findings, two checks not run with the reason for each, nothing cloned
git clone, a REVIEW repository. git clone https://github.com/shukyb26-maker/repogates-agentbaiting-demo came back REVIEW 70 — C1 owner account age and C2 repository age, both HIGH — so the decision is ask. Claude names what fired, names the two checks that did not run and why (no Scorecard result; C22 needs the browser), clones nothing, and waits for you.
Claude showing the preflight tool error BLOCKED for a repository listed in an active malware campaign, and installing nothing
npm install of a campaign-listed repository. npm install github:repogates-canary/always-blocked — the permanent canary on the campaign list. preflight does not return a decision; it raises: "BLOCKED — repogates-canary/always-blocked is listed in an active malware campaign (canary, confirmed). Do not run this command, and do not fetch the same repository another way." Claude installs nothing, creates nothing, and looks for no other route.
Claude installing express with --ignore-scripts after preflight resolved the package to expressjs/express and answered allow
npm install of a registry package. npm install express names no repository, so RepoGates asks npm which one the package declares — expressjs/express — and assesses that: PASS 100, allow. Claude installs it with --ignore-scripts and says the true thing about the result: one repository was vetted, the 67 packages it pulled in were not. Direct dependencies named on the command line only; transitive dependencies and lockfiles are not resolved.
Claude reporting what fired for pip install flask and asking how to proceed, with a deep scan offered as one option
pip install of a registry package that warns. pip install flask resolves through PyPI to pallets/flask: REVIEW 85 — C10 devcontainer lifecycle command (HIGH) and C9 a source archive beside its wheel (MED) — and your policy warns on both, so the decision is ask. Claude explains what fired, installs nothing, and hands the choice back to you; "run deep_scan first (costs 1 credit)" is the agent offering the scan, not starting it.
Claude listing a deep scan result verbatim: PASS, decision allow, reused false, reuse window 900 seconds, credits left, and the note
A deep scan, with your yes. After the REVIEW above you approve one credit and Claude calls deep_scan with confirm=true. The files are read in the sandbox by four engines; the answer says it was a fresh scan (reused: false), that a repeat within 900 s is free, how many credits remain, and — in the current wording — that "the metadata verdict is unchanged: the repository was REVIEW on provenance, which is why a scan was allowed at all". A clean scan does not erase the provenance findings; Claude is told to report both.

2. Verify it is actually connected

"The connector was never added" is how a security control silently stops existing, so verification runs in both directions:

  • From the agent: ask it to call repogates_status. If the tool does not exist, RepoGates is not connected — add the setup line above. If it returns connected: false, the server is up but its verdict service is not; treat every repository as unassessed.
  • From your browser: a plain GET to https://mcp.repogates.com/clients answers without any MCP client: {"ok": true, "server": "repogates-mcp/1.0", "clients_seen": […], "verdict_service": "ok", "engine_version": …}. clients_seen lists the platforms that have actually contacted the server — which is the only thing RepoGates can observe. An empty list means nothing has called yet, not that your connector is missing.
  • From the extension: Options → AI agents → Test connection runs the same check and prints the result.
  • From the plugin: /repogates:setup checks the token file and runs the hook against a permanently blocked canary repository; a refusal naming the canary means the hook, the token and the plan all work.

3. The tools

Nine tools, deliberately without a shell, a filesystem or a write verb — the one that spends anything, deep_scan, spends a credit only with your per-call yes. Every tool raises an error rather than returning a reassuring empty result when RepoGates cannot answer — "cannot answer" must never be rendered as "safe".

ToolOne line
check_repo(owner, repo) Verdict, 0–100 trust score, how many of the 22 checks passed, the findings that fired, and the four dimension verdicts — open, clone, install, agent — each with your profile's action; and what changed since the previous assessment on record (the timeline).
check_owner(login) Assesses a GitHub account — age, what it publishes, and its worst-scoring repositories.
check_download_url(url) Resolves a GitHub URL — repo page, Download-ZIP link, release asset, raw file — to its repository and assesses it. Refuses opaque CDN links rather than guessing.
assert_allowed(owner, repo) Hard gate: succeeds quietly or raises. See below.
preflight(line) Judges the exact clone or install command line before it runs — parsed on the server, never executed. Raises on a blocked repository; otherwise returns allow or ask with the repositories it found, each with its findings, the checks that did not run, with why, and its four dimension verdicts; the reason ends with the one the verb is about (clone: REVIEW, install: PASS). A registry package on the line (npm install express, pip install flask) is resolved to the source repository it declares. Advisory unless the Claude Code plugin's hook is installed.
deep_scan(owner, repo, confirm) Scans the repository's files — malware, secrets, dependencies, behaviour, in an isolated sandbox, about a minute — before a clone or install. Spends one scan credit; does nothing without confirm=true, which the agent may pass only after you said yes. Returns reused: true, and spends nothing, when a scan of the same repository finished within the reuse window, and reuse_window_ends_in_s says how long a repeat stays free. Raises on a scan verdict of BLOCK.
deep_scan_status(scan_id) The result of a scan that was still running.
active_campaigns() Supply-chain campaigns and newly exploited CVEs active right now.
repogates_status() Proves the connector works and restates the scope limit.

The server also exposes a resource, repogates://checks — the roster of all 22 checks, so a model can cite why a check exists rather than guess — and a prompt, vet_before_clone, that tells an agent to gate a named repository before touching it. Findings are capped per call so a hostile repository cannot flood an agent's context.

4. assert_allowed — the hard gate

Prose is negotiable to a language model; a tool error is not. That is the whole design of assert_allowed: called before a clone, download or install, it returns a short success string when the repository is acceptable — and raises a tool error when it is not, so the agent's step fails instead of receiving text it can talk itself past.

OutcomeWhen
ALLOWED Verdict PASS — returns the score and verdict.
PROCEED WITH CAUTION Verdict REVIEW — returns, telling the agent to surface what fired to the user before continuing.
NOT ASSESSED The repository could not be fully examined — returned as explicitly unchecked, never as approved.
Raises: BLOCKED A finding your policy profile blocks (the same profiles.json the extension applies — a devcontainer command on the host is CRIT and a warning, hidden Unicode is a block), a listing in an active malware campaign, a response with no real verdict, or the verdict service being unreachable. It fails closed — no answer is not permission.

This is the verb an organisation can mandate. A line for your agent standards document:

Before cloning, downloading, installing from, or recommending a GitHub
repository, call RepoGates assert_allowed(owner, repo). If it raises,
stop and report the reason — do not fetch the repository by another
route, and do not retry hoping for a different answer.

The repogates-vetting agent skill packages exactly this behaviour for platforms that support skills, including refusing to continue silently when the connector is missing.

5. Organisation policy scoping

Not currently available. The organisation tier was withdrawn in August 2026, so no enrolment tokens are issued. The server still honours the header if you hold one, and the mechanism is described here because it is real API behaviour — but there is nothing to obtain today, and every request therefore gets default policy.

With a token, an agent's assessments use that OU's thresholds, allowlist and campaign settings, so an agent and a browser on the same laptop reach the same verdict:

Authorization: Bearer rgt1.<ou-token>

Without a header the server answers with default policy. Requests are rate-limited per client address; a 429 carries retry_after.

6. AgentBaiting — C22

C22 is the one check that cannot run on any backend, because the signal only exists in the browser at navigation time: did an AI surface lead this person to this repository? Attackers seed AI-recommendable fakes precisely because an assistant's recommendation reads as a trust signal. It is not one.

How the extension sees it, and everything it sends:

  • It watches navigation on an enumerated list of surfaces only — AI chats (claude.ai, chatgpt.com, chat.openai.com, gemini.google.com, copilot.microsoft.com, perplexity.ai) and MCP directories (lobehub.com, glama.ai, mcp.so, mcpmarket.com, smithery.ai). It holds no permission to see any other site, and deliberately requests neither tabs nor webNavigation.
  • Arriving at a GitHub repository from one of those surfaces — same tab, a new tab it opened, or via referrer — records that provenance for 30 minutes.
  • If a download of that repository then starts, the verdict request carries prov=agent and the surface's hostname only — never the page URL, never the conversation.
  • Policy then escalates: by default a warn-grade verdict becomes a block when the repository was agent-surfaced (warn_to_block; strict profiles use always_block), and the decision page says plainly that an AI surface led you there.

The check has its own page — C22, AI-agent provenance — with the scoring, the false positives and the limits; the technique it answers is described at AgentBaiting.

C22 and the MCP server are the two halves of the agent problem: C22 covers the human who follows an agent's recommendation into a browser download; the MCP tools cover the agent that fetches code itself — when it asks. Neither half claims the other.

7. The GitHub-page badge, board and feed

On github.com the extension can draw three things — all display, none of them enforcement, each with its own toggle (Options → GitHub pages):

  • Trust badge — the 0–100 score beside the repository name, computed from the same 22 checks the gate enforces. The same badge appears on owner profiles for the account's repositories.
  • Quick-stats board — under the page header: stars, owner age, contributors, licence, Scorecard, release downloads, and the findings behind the score, with honest "—" for anything unknown (unknown is never rendered as zero).
  • Live security feed — a banner of active supply-chain campaigns and newly exploited CVEs, refreshed every 30 minutes; the same data the active_campaigns tool returns.

The board is fed by the verdict the background already fetched to pre-warm the gate, so it costs no extra GitHub API calls — and if GitHub changes its page markup, a server-side kill-switch hides the overlay rather than rendering it broken. Enforcement is unaffected either way.

← All documentation · Personal user guide · Personal user guide