# Your AI Agent Has Push Access to Every Repo

> The GitHub MCP server exposes 83 tools — including file deletion, repo creation, and PR merges. Here's how to enforce policies before your agent ships something it shouldn't.

Published: Mon Mar 16

Canonical: https://policylayer.com/blog/secure-github-mcp-server

Your coding agent just merged a pull request to main, deleted three files it thought were unused, and created a new repository called `temp-debug-workspace`. You didn't ask it to do any of that. But you gave it access to the GitHub MCP server, and the GitHub MCP server said yes to everything.

<!--truncate-->

## What the GitHub MCP server exposes

The [official GitHub MCP server](https://github.com/github/github-mcp-server) registers **83 tools**. Most people set it up for reading code and managing issues. What they don't realise is they've also handed their agent the keys to:

- **`delete_file`** — permanently remove files from a repository
- **`merge_pull_request`** — merge PRs without human review
- **`push_files`** — commit directly to any branch, including main
- **`create_repository`** — spin up new repos under your organisation
- **`fork_repository`** — fork repositories on your behalf
- **`create_or_update_file`** — overwrite any file in any repo
- **`actions_run_trigger`** — trigger CI/CD workflows

There's no permission model inside MCP. The protocol forwards every `tools/call` from agent to server without restriction. If the GitHub token has write access, the agent has write access. And if the agent hallucinates a plan that involves tidying up old files or "fixing" a broken workflow, nothing stands in its way.

This is the same class of problem we covered in [what happens when an AI agent goes rogue](/blog/ai-agent-goes-rogue) — except here the blast radius is your entire GitHub organisation.

## Block the dangerous tools, rate limit the rest

[PolicyLayer](/mcp-gateway) sits between your agent and the GitHub MCP server as a proxy. Every tool call passes through a YAML policy before reaching GitHub. The policy is deterministic — no LLM judgment, no prompt-based guardrails, just hard rules evaluated at the transport layer.

Here's the destructive tool policy from our GitHub starter config. File deletion is blocked outright:

```yaml
version: "1"
description: "Policy for github/github-mcp-server"
default: "allow"

tools:
  # Block file deletion entirely
  delete_file:
    rules:
      - name: "block-delete"
        action: deny
        on_deny: "File deletion blocked by policy"
```

When the agent tries to call `delete_file`, it receives the `on_deny` message instead. The request never reaches GitHub.

For write operations you want to permit but not leave unchecked, rate limits keep things sane:

```yaml
tools:
  # Cap write operations
  create_or_update_file:
    rules:
      - name: "rate-limit-writes"
        rate_limit: "30/hour"
        on_deny: "Rate limit: max 30 write operations per hour"

  push_files:
    rules:
      - name: "rate-limit-writes"
        rate_limit: "30/hour"
        on_deny: "Rate limit: max 30 write operations per hour"

  # Tighter limit on repo creation
  create_repository:
    rules:
      - name: "rate-limit-repo-creation"
        rate_limit: "5/hour"
        on_deny: "Rate limit: max 5 repository creations per hour"

  # Comments can loop fast — cap them
  add_issue_comment:
    rules:
      - name: "rate-limit-comments"
        rate_limit: "20/hour"
        on_deny: "Rate limit: max 20 comments per hour"

  # Workflow triggers are expensive
  actions_run_trigger:
    rules:
      - name: "rate-limit-workflows"
        rate_limit: "10/hour"
        on_deny: "Rate limit: max 10 workflow triggers per hour"
```

Rate limits use [stateful counters](/blog/rate-limiting-mcp-tool-calls) that reset at the top of each window (hour, minute, or day). If the agent burns through 30 file writes in a loop, it's cut off until the next hour — and it knows why, because the denial message tells it.

Finally, a global backstop catches anything you haven't explicitly configured:

```yaml
tools:
  "*":
    rules:
      - name: "global-rate-limit"
        rate_limit: "120/minute"
        on_deny: "Global rate limit: max 120 calls per minute"
```

This caps the agent at 120 total tool calls per minute across all 83 tools. Even if you forget to add a rule for a specific tool, the wildcard catches runaway loops before they cause real damage.

## Getting started

Route your GitHub MCP server through PolicyLayer — point your MCP client at the gateway URL with a per-person grant token, and the policy above runs on every call before it reaches GitHub:

```json
{
  "mcpServers": {
    "github": {
      "url": "https://proxy.policylayer.com/mcp/<server-uuid>/",
      "headers": { "Authorization": "Bearer <grant-token>" }
    }
  }
}
```

You define and adjust the policy in the [PolicyLayer dashboard](https://app.policylayer.com) — no local proxy to install or run.

Every tool call now passes through the policy. Denied calls return the `on_deny` message to the agent. Allowed calls forward to GitHub as normal. The agent doesn't know PolicyLayer is there — it just sees an MCP server that sometimes says no.

You can start with our pre-built GitHub policy and adjust from there. Block what's dangerous, rate limit what's useful, and leave read-only tools open.

[Full GitHub policy →](/policies/github)
