Topic: mcp server security github

MCP server security on GitHub — Action and PR gate

If you maintain a Model Context Protocol server on GitHub, or your team allows MCP servers into a fleet's agent stack, the right place to enforce security is the pull request — not the install. Here's the GitHub Action, the workflow YAML, and what it catches that traditional code scanning doesn't.

TL;DR

SkillAudit ships a GitHub Action (skillaudit/skillaudit-gate@v1, available on the Pro and Team plans) that runs the same six-axis scan you'd run from the website, posts the report card as a check on the PR, and fails the workflow if the grade falls below a configurable minimum. For author repos: catch SSRF, prompt-injection, and credential-echo before they merge. For consumer repos: fail PRs that add a new MCP server below a B grade. It composes with GitHub's native code scanning rather than competing with it — code scanning handles the conventional SAST surface; SkillAudit handles the MCP-specific surface. The 101-server corpus shows why the gap matters: 50% SSRF, 38% credential findings, 19% A-grade.

Why a PR-time gate, not an install-time check

An install-time scan is the right defense if you're a solo developer evaluating one server before claude plugin install. It's the wrong defense once a team is involved, for two reasons:

The same logic that put dependency review and code scanning into PR checks puts MCP-server review there too. An install-time scan catches the case where someone adds foo-mcp to their personal config; a PR gate catches the case where they add it to the team's. Both layers are useful; the PR gate is the higher-leverage one if you're building team policy.

The Action — what it does

  1. Detects MCP additions. Diffs the PR for new entries in common MCP config locations (.mcp.json, mcp.config.json, ~/.claude/plugins.json, custom paths via input). For author repos, uses the repo itself.
  2. Resolves each addition to a canonical source. A GitHub URL, npm package name, or local path. The Action follows package-redirect chains the same way the website scanner does.
  3. Runs the six-axis scan. Same engine as the public scanner, same v0.3 methodology, same calibration set. The Action runs the static layer locally and the LLM-assist layer via the SkillAudit API (a Pro-tier API key is required for the LLM-assist axis).
  4. Posts the result as a check. The check status is "passing" if the resulting grade meets the minimum, "failing" otherwise. The check page links to the full report card at /audits/<owner>-<repo>/.
  5. Fails the workflow if any addition is below the minimum. Configurable per-repo via the min-grade input. Default is B. Below the min = workflow fails = PR can't merge if branch protection is enabled.

Workflow YAML

Drop this into .github/workflows/skillaudit.yml in the repo where you want the gate enforced:

name: SkillAudit gate

on:
  pull_request:
    paths:
      - '.mcp.json'
      - 'mcp.config.json'
      - 'src/**/*.ts'
      - 'src/**/*.py'

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read
      checks: write
    steps:
      - uses: actions/checkout@v4

      - name: Run SkillAudit gate
        uses: skillaudit/skillaudit-gate@v1
        with:
          # Minimum grade to allow. A | B | C | D | F.
          # Below this = workflow fails.
          min-grade: B

          # Where to look for MCP server additions.
          # Default: .mcp.json, mcp.config.json
          mcp-config-paths: |
            .mcp.json
            mcp.config.json

          # API key for the LLM-assist axis. Free tier runs static-only.
          api-key: ${{ secrets.SKILLAUDIT_API_KEY }}

          # Optional: scan this repo as itself (use for author repos).
          scan-self: false

The action posts a check on the PR. The check page shows the grade per added server, the per-axis breakdown, and a deep link to the public report card. If the grade meets the minimum, the workflow passes; otherwise it fails and the merge is blocked (assuming branch protection is on).

What's caught at PR-time, and how it composes with code scanning

GitHub-native code scanning (CodeQL, third-party SARIF uploads from Semgrep, Snyk Code, etc.) is the right tool for conventional SAST findings — XSS, SQLi, OWASP web patterns, dependency CVEs through SCA. None of that goes away. SkillAudit's gate adds an orthogonal axis: does this MCP server's tool-handler code expose a class of bug that conventional SAST doesn't model.

Concretely, here's the split for a representative MCP-adding PR:

The two layers compose. A typical Pro-plan team enables both: code scanning for the framework and the dependency tree, SkillAudit for the MCP surface. The GitHub code scanning alternative page covers the comparison in side-by-side detail.

Configuring the gate for different repo shapes

Repo shapeRecommended min-gradeRecommended scan-selfWhy
Author repo (you ship one MCP server)AtrueThe marketplace listing review uses the same axes; aim for a green badge before publishing.
Team agent config (you consume MCP servers)BfalseB is install-with-confidence; C is install-with-caution. Don't auto-merge a C without a human read.
Internal-only MCP server (private repo)BtruePrivate MCP code is still tool-handler code. Same threat surface, same axes apply.
OSS MCP framework (FastMCP, mcp-use, etc.)AtrueFramework code is run by every downstream user; the ceiling has to be higher.
Experimental fork during a security auditF (i.e. don't gate)trueYou want the report on every PR but you're consciously below grade while you remediate. Set min-grade to F to keep the report posting without failing the build.

Author flow: getting your repo's grade up before listing

If you're publishing a Claude skill or MCP server to a public marketplace and want the badge before submission, the gate becomes a self-improvement loop. Set scan-self: true, min-grade: A, push a branch, and read the failing check's report. Most A-grade fixes are mechanical; the patterns are documented in Anatomy of an A-grade MCP server:

Each fix re-runs the scan in the next PR. The grade rebuilds. Authors in our corpus moved from F to A in roughly five PRs of mechanical work; the median was three.

Related questions

Does the Action work on private repos?

Yes — the Pro plan ($19/mo) includes private-repo scanning via the Action. The Team plan ($99/mo for 10 seats) adds SSO, policy export, and an audit log of gate decisions across the org's repos.

What if I don't have an API key — can I run static-only?

Yes. Without an API key the LLM-assist axis is skipped; the static layer runs locally on the GitHub-Action runner. Grade is reported with a "static-only" footnote so reviewers know the LLM-probe axis was not evaluated. Free for any public repo.

Does the Action upload SARIF for native code scanning integration?

Yes — set upload-sarif: true in the inputs. SkillAudit findings appear in the GitHub Security tab alongside CodeQL, Semgrep, and any other SARIF uploaders you have wired up. Useful when your security team already lives in that view.

How fast is the Action?

Static-only: under 30 seconds for a typical MCP server. Static + LLM-assist: 60-90 seconds for one new server, scaling with tool count. Cached if the upstream repo's HEAD hasn't moved since the last scan.

Can I run it on a schedule, not just on PRs?

Yes — the Action can be triggered from a schedule: workflow to re-scan all currently-installed MCP servers nightly. Useful for catching upstream regressions: a server that was an A last week may have shipped a credential leak in a patch release.

Further reading