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.
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 get | Why 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.
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.
| Platform | Setup |
|---|---|
| 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:
|
| 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:
|
| 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.
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 says | The 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.
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
https://mcp.repogates.com/mcp.
It signs you in with OAuth the first time a tool runs; a paid
RepoGates plan is required.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.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.
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.
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.
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.
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.
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.
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."The connector was never added" is how a security control silently stops existing, so verification runs in both directions:
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.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./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.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".
| Tool | One 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.
assert_allowed — the hard gateProse 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.
| Outcome | When |
|---|---|
| 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.
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.
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:
tabs nor
webNavigation.prov=agent and the surface's hostname
only — never the page URL, never the conversation.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.
On github.com the extension can draw three things — all display, none of them enforcement, each with its own toggle (Options → GitHub pages):
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