Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe
Front Page / AI Tools / Deep Dive

Build a GitHub Actions Security MCP Server for Agent-Safe CI/CD

After Google deleted three ADK workflows (Aug 4, 2026) over an agent-to-agent privilege boundary failure in CI/CD, agent-safe pipelines are the security story of the summer. This guide builds gh-actions-sec, a FastMCP TypeScript server that gives agents read-only visibility into GitHub Actions posture: workflow permissions, secret exposure, action allowlists, and run logs — so a CI/CD agent can audit and harden pipelines without ever holding write credentials.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 16, 2026 Published
|
Aug 16, 2026 Updated
|
10 Minutes Reading Time
Core Takeaways for Founders & Builders
  • gh-actions-sec gives agents read-only GitHub Actions posture: workflow permissions, secret exposure, action allowlists, and run logs — no write credentials needed.
  • It is the agent-safe CI/CD companion to the Aug 4, 2026 ADK workflow deletion: audit pipelines without ever letting an agent hold privileged write access.
  • Five tools with inputSchema contracts — listWorkflows, getWorkflowPermissions, scanSecrets, checkActionAllowlist, getRecentRuns — cover the audit surface.
  • A read-only PAT or GitHub App token with contents:read keeps the agent powerful for auditing and harmless for changing anything.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

On August 4, 2026, Google deleted three GitHub Actions workflows from google/adk-python after Pillar Security demonstrated that a public GitHub issue could trigger a privileged agent into code execution on a CI runner. The root cause was an agent-to-agent privilege boundary failure — and the lesson for every team is that CI/CD is where agent privilege decisions become security incidents. This guide builds gh-actions-sec: a FastMCP TypeScript server that gives AI agents read-only visibility into GitHub Actions security posture — workflow permissions, secret exposure, action allowlists, and run logs — so agents can audit and harden pipelines without ever holding write credentials. The MCP directory has tracked the agent-security wave; this is the tool surface that makes CI/CD auditing agent-safe.

Why read-only agent tooling for CI/CD

The ADK incident was possible because a privileged agent was steered by untrusted content. The safest way to let agents work on CI/CD security is to remove the privilege from the agent entirely: the agent audits, recommends, and files the findings; a human (or a separate, gated process) applies changes. That split is the core design decision of gh-actions-sec. Every tool is read-only against the GitHub REST API, scoped by a token with contents:read at most. An agent can discover every over-privileged job in your repo — and cannot change a single one of them. That is the AI workflows trust-boundary discipline applied to the CI/CD control plane.

Architecture

flowchart LR
    A[Claude / Cursor / Agent] -->|MCP JSON-RPC| B[gh-actions-sec FastMCP Server]
    B -->|REST API, read-only token| C[GitHub Actions]
    C --> D[Workflows]
    C --> E[Secrets usage]
    C --> F[Action versions]
    B --> G[Audit log]

Project setup

mkdir gh-actions-sec && cd gh-actions-sec
npm init -y
npm install @modelcontextprotocol/sdk fastmcp dotenv
# .env
GITHUB_TOKEN=ghp_read_only_contents_read
GITHUB_OWNER=acme
GITHUB_REPO=platform
ALLOWED_ACTIONS=actions/checkout@v4,actions/setup-node@v4,github/codeql-action@v3
MAX_WORKFLOWS=50
MCP_PORT=3002

Server implementation

// server.ts — gh-actions-sec FastMCP TypeScript server
import { FastMCP } from 'fastmcp';
import dotenv from 'dotenv';
dotenv.config();

const token = process.env.GITHUB_TOKEN!;
const owner = process.env.GITHUB_OWNER!;
const repo = process.env.GITHUB_REPO!;
const allowedActions = new Set((process.env.ALLOWED_ACTIONS || '').split(','));

async function gh(path: string) {
  const res = await fetch(`https://api.github.com/repos/${owner}/${repo}${path}`, {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
  });
  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${path}`);
  return res.json();
}

const server = new FastMCP({ name: 'gh-actions-sec', version: '0.1.0' });

server.addTool({
  name: 'listWorkflows',
  description: 'List all workflow files in the repository with their paths and states.',
  inputSchema: { type: 'object', properties: {}, required: [] },
  async execute() {
    const data = await gh('/actions/workflows');
    const wfs = (data.workflows || []).slice(0, Number(process.env.MAX_WORKFLOWS || 50));
    return { count: wfs.length, workflows: wfs.map((w: any) => ({ id: w.id, name: w.name, path: w.path, state: w.state })) };
  },
});

server.addTool({
  name: 'getWorkflowPermissions',
  description: 'Fetch the raw workflow YAML and report permissions blocks per job, flagging write-scoped permissions.',
  inputSchema: { type: 'object', properties: { workflowPath: { type: 'string' } }, required: ['workflowPath'] },
  async execute(args: any) {
    const content = await gh(`/contents/${args.workflowPath}`);
    const yaml = Buffer.from(content.content, 'base64').toString('utf-8');
    const flagged = (yaml.match(/permissions:([^
]|
\s+[^
])*/g) || [])
      .filter((b: string) => /write|all/.test(b));
    return { workflow: args.workflowPath, permissionsBlocks: flagged, hasWriteScopedPermissions: flagged.length > 0 };
  },
});

server.addTool({
  name: 'scanSecrets',
  description: 'Report secrets referenced across workflow files so reviewers can verify scoping.',
  inputSchema: { type: 'object', properties: {}, required: [] },
  async execute() {
    const data = await gh('/actions/workflows');
    const secrets: string[] = [];
    for (const w of (data.workflows || []).slice(0, 10)) {
      const c = await gh(`/contents/${w.path}`);
      const yaml = Buffer.from(c.content, 'base64').toString('utf-8');
      for (const m of yaml.matchAll(/\$\{\{\s*secrets\.([A-Z0-9_]+)\s*\}\}/g)) {
        secrets.push(m[1]);
      }
    }
    return { secretsReferenced: [...new Set(secrets)] };
  },
});

server.addTool({
  name: 'checkActionAllowlist',
  description: 'Check every actions/* reference in workflows against the configured allowlist.',
  inputSchema: { type: 'object', properties: {}, required: [] },
  async execute() {
    const data = await gh('/actions/workflows');
    const violations: string[] = [];
    for (const w of (data.workflows || []).slice(0, 10)) {
      const c = await gh(`/contents/${w.path}`);
      const yaml = Buffer.from(c.content, 'base64').toString('utf-8');
      for (const m of yaml.matchAll(/uses:\s*([^\s]+)/g)) {
        const action = m[1];
        if (action.startsWith('actions/') && !allowedActions.has(action)) violations.push(action);
      }
    }
    return { violations, allowlist: [...allowedActions] };
  },
});

server.addTool({
  name: 'getRecentRuns',
  description: 'List recent workflow runs with conclusions for status review.',
  inputSchema: { type: 'object', properties: { workflowId: { type: 'number' }, limit: { type: 'number', minimum: 1, maximum: 20 } }, required: ['workflowId'] },
  async execute(args: any) {
    const data = await gh(`/actions/workflows/${args.workflowId}/runs?per_page=${Math.min(args.limit || 10, 20)}`);
    return { runs: (data.workflow_runs || []).map((r: any) => ({ id: r.id, status: r.status, conclusion: r.conclusion, created: r.created_at })) };
  },
});

async function main() {
  await server.start({ transportType: 'http', port: Number(process.env.MCP_PORT || 3002) });
  console.log('gh-actions-sec listening on', process.env.MCP_PORT || 3002);
}
main().catch((e) => { console.error(e); process.exit(1); });

Client configuration (mcpServers)

{
  "mcpServers": {
    "gh-actions-sec": {
      "command": "npx",
      "args": ["tsx", "server.ts"],
      "env": {
        "GITHUB_TOKEN": "ghp_read_only_contents_read",
        "GITHUB_OWNER": "acme",
        "GITHUB_REPO": "platform",
        "ALLOWED_ACTIONS": "actions/checkout@v4,actions/setup-node@v4,github/codeql-action@v3"
      }
    }
  }
}

Security guide

Three rules keep this agent-safe. First, read-only token. Use a PAT or GitHub App with contents:read (plus actions:read) — never write scopes. Second, server-side secrets. The token lives in the server's environment; agents only ever see tool output. Third, audit. Log every tool call — agent, tool, workflow, flags — to an append-only store so pipeline reviews are reviewable themselves. This is the least-privilege, provenance-aware discipline the AI workflows library prescribes for every CI/CD agent.

The audit workflow

The natural agent loop: (1) listWorkflows, (2) for each, getWorkflowPermissions and flag write-scoped jobs, (3) checkActionAllowlist for unapproved actions, (4) scanSecrets for references, (5) getRecentRuns for status, then (6) write a hardening report for humans to action. Everything is read-only, so the agent can audit the entire pipeline without becoming the attack surface the ADK incident exposed.

Interpreting the results

Each tool returns a deliberately small, actionable surface. getWorkflowPermissions returns the raw permissions blocks plus a boolean flag for write-scoped or all permissions, so the agent (or a human reading its report) can immediately see which jobs hold more privilege than their uses steps justify. checkActionAllowlist returns every actions reference that is not on the configured allowlist — this is the tool that would have caught a workflow drifting to an unpinned or unknown action. scanSecrets returns the deduplicated set of secret names referenced across workflows, which is the starting point for reviewing whether those secrets are scoped to the jobs that need them or are ambiently available to the whole repository. When these three tools agree on a finding — an over-privileged job using a non-allowlisted action with an ambient secret — you have a concrete hardening ticket, not a vague review of CI.

Deploying to production

The server runs best as a small container with the read-only token injected from your secrets store, a health endpoint, and outbound access limited to the GitHub API. Keep the token scoped with contents:read and actions:read only — never write, never metadata write. For multi-repo support, extend the server to accept the owner and repo as tool parameters with a per-repo allowlist, so one deployment can audit the whole organization without each repo shipping its own token. Add the audit log to your SIEM so pipeline review activity is itself reviewable. If you want the agent to also file findings, add a separate fileFinding tool that creates a draft issue with a low-privilege token and human-triggered approval — creating the ticket is safe, applying the fix is not.

The bottom line

The ADK workflow deletion on August 4, 2026 proved that CI/CD agents with too much privilege are a liability. gh-actions-sec flips the model: agents get complete visibility and zero write capability. Read-only tools, scoped tokens, allowlists, and audit make pipeline auditing safe to delegate. Build it, wire it in, and let your agents find the over-privileged jobs before an attacker's GitHub issue does. More agent-security patterns are in the AI workflows library.

There is a second, quieter reason to run a read-only audit server like gh-actions-sec: it converts CI/CD security from a periodic review into a continuous, agent-observable property. Every time a developer adds a workflow, changes a permissions block, or bumps an action version, the audit surface changes — and a weekly human review will miss most of those changes. With the MCP server wired into an agent that runs on a schedule, the delta is caught the same day: new workflow appears, permissions widen, action drifts off the allowlist, and a report lands in the security channel before the change has even been merged. That continuous posture is the difference between auditing pipelines and actually securing them. The pattern composes with the least-privilege discipline the AI workflows library applies to every agent tool, and with the trust-boundary lessons from the ADK incident that started this wave.

One more implementation detail that pays off quickly: give the server a small caching layer for workflow file contents, keyed by commit SHA. The GitHub contents API is rate-limited, and a read-heavy audit loop that re-fetches unchanged workflow files burns the budget for no signal. Cache per path with an expiry short enough to catch real changes — minutes, not hours — and invalidate on push events if your deployment can listen for them. The caching decision is small, but it is the difference between a server that audits comfortably all day and one that trips GitHub rate limits by mid-morning. It also makes the server friendlier to schedule as a continuous daemon, which is the deployment mode that actually keeps CI/CD posture current.

Frequently Asked Questions

What is gh-actions-sec?

A FastMCP TypeScript server exposing read-only GitHub Actions security tools — workflow permissions, secret exposure, action allowlists, run logs — to AI agents.

Why build it now?

After Google deleted three ADK workflows on Aug 4, 2026 over an agent-to-agent privilege boundary failure, agent-safe CI/CD auditing is the priority — and agents should audit without holding write credentials.

What tools does it expose?

listWorkflows, getWorkflowPermissions, scanSecrets, checkActionAllowlist, and getRecentRuns, each with inputSchema JSON contracts and result caps.

How is it secured?

A read-only GitHub token (PAT or GitHub App with contents:read) is held server-side; agents never see it, and every call is authorized and audited.

What does the agent workflow look like?

An audit loop: list workflows, review each job's permissions, flag over-privileged jobs, check actions against an allowlist, and report secrets usage — all read-only.

Closing thoughts

Agent-safe CI/CD is the security discipline of 2026, and the ADK incident is the proof point. Give agents full visibility and zero write privilege — read-only tools, scoped tokens, allowlists, audit — and they become your best pipeline auditors without becoming your biggest risk. Build gh-actions-sec into your stack and apply the trust-boundary patterns from the AI workflows library."

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
A FastMCP TypeScript server exposing read-only GitHub Actions security tools — workflow permissions, secret exposure, action allowlists, run logs — to AI agents.
After Google deleted three ADK workflows on Aug 4, 2026 over an agent-to-agent privilege boundary failure, agent-safe CI/CD auditing is the priority — and agents should audit without holding write credentials.
listWorkflows, getWorkflowPermissions, scanSecrets, checkActionAllowlist, and getRecentRuns, each with inputSchema JSON contracts and result caps.
A read-only GitHub token (PAT or GitHub App with contents:read) is held server-side; agents never see it, and every call is authorized and audited.
An audit loop: list workflows, review each job's permissions, flag over-privileged jobs, check actions against an allowlist, and report secrets usage — all read-only.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Briefing AI Tools

Vercel AI SDK Tool Calling React: 5 Steps (2026)

Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...

Deepak Bagada Deepak Bagada
12m read
Breaking AI Tools

Fact-Density vs. Word Count: The New SEO for 2026

Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...

Deepak Bagada Deepak Bagada
4m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc