chapter: 08·part: The Team (safe, reviewed, patterned)

Tools & MCP: Real Capabilities, Governed

Give the Repo Assistant real capabilities with the tools: block and MCP servers while keeping every capability governed.

Objective

By the end of this chapter you can give the Repo Assistant real capabilities — querying GitHub, fetching web pages, running shell commands, calling third-party services — through the tools: block and MCP servers, while keeping every capability governed by the security model from Chapter 7.

This chapter targets gh aw v0.88.7. We keep the Repo Assistant's GitHub/web-fetch workflow and add tools only where the task needs them. Compilation evidence below is not a claim of live triage, MCP connectivity, or browser success.

Concept: agents need tools, safely

A language model on its own can only reason about the text in front of it. To be useful on your repo it needs to act on the world: look up related issues, read a linked spec, run a linter, query a service. Those actions are tools — the bridge between the model's judgment and real systems.

The Model Context Protocol (MCP) is an open protocol that lets an agent discover and call capabilities offered by a “server” — for example, a GitHub server or a database server. It gives different systems a common interface; it does not make every tool an MCP tool or every server equally trustworthy (Using MCPs, v0.88.7).

The tension: capability vs. exposure

Every tool you add is also new attack surface. A tool that reads private data feeds the second leg of the lethal trifecta; a tool that reaches the network feeds the third. A naive “give the agent everything” approach maximizes usefulness and risk together. So the discipline is the same as with permissions: grant the fewest tools the task needs, each scoped as tightly as possible — then check which controls actually enforce that scope.

As in Chapter 5, portable intent does not mean interchangeable security contracts. A shell restriction must be supported by the selected engine. Likewise, MCP describes an interaction protocol, not one universal execution boundary. These distinctions become the Bash engine contract and MCP transport review below.

In gh-aw: the tools: block, MCP servers, the MCP gateway

The tools: block turns least-capability intent into concrete tool configuration. Availability and enforcement depend on the engine; review its supported features rather than assuming identical behavior from identical YAML (Tools; engine feature comparison).

Built-in tools

A handful ship with gh-aw. The most common:

ToolGrants
github:GitHub MCP reads selected through toolsets (issues, repos…); keep agent permissions read-only
bash:shell commands; per-command allowlisting is supported by Copilot, Claude, and Gemini, not Codex
edit:editing files in the workspace
web-fetch: / web-search:fetch a page / search the web; search availability is engine-dependent
playwright:browser automation through @playwright/cli and Bash, not built-in Playwright MCP

Bash: an allowlist needs an enforcing engine

A small default command set is a starting point, not a promise that shell access is harmless. Prefer an explicit list for the task. The Copilot-backed test-improver workflow in Chapter 10 demonstrates a complete workflow with a Bash command allowlist and, separately, the edit tool.

In v0.88.7, a strict Codex workflow with a restricted Bash allowlist fails compilation. Even a list containing only echo is rejected: Codex cannot enforce per-command allowlisting and would silently ignore that restriction at runtime. This is an error, not merely a warning (capability diagnostics change; target engine contracts).

If the restriction matters, keep it and use a supporting engine — Copilot, Claude, or Gemini. Do not remove the list, grant unrestricted shell, or disable strict mode just to silence the error. An engine change also requires the authentication, billing, tool, and network review from Chapter 5; it is not an equivalent security configuration by substitution.

Playwright: browser capability through a CLI

Browser work adds another way to read untrusted content and reach network destinations. The built-in tools.playwright configuration now arranges installation of @playwright/cli, its skills, and the requested browsers before the agent runs. Chromium is the default browser; the agent uses playwright-cli commands through Bash (Playwright, v0.88.7).

Frontmatter excerpt from examples/ch08/playwright-cli.md — a complete compile-only provisioning fixture, not a browser test
tools:
  playwright:
network:
  allowed: [defaults, playwright]

Omit mode. Legacy mode: cli remains accepted, but mode: mcp is a compile error, even though the schema retains that value to provide a migration diagnostic. For an actual browser task, update the prompt and tool names too: use commands such as playwright-cli snapshot through Bash, not MCP tool names such as browser_snapshot. A custom Playwright MCP server would be a separately pinned, reviewed integration, not an automatic migration.

The version field pins @playwright/cli, not a browser or MCP package. The focused target reference uses 0.1.18; do not transplant the stale 1.56.1 example from the general tools page as a CLI pin. This fixture leaves the compiler default in place.

The playwright network bundle supports browser provisioning; a real external page also needs its permitted destination in network.allowed. The fixture asks for no work and was compiled, not run. Package downloads and navigation were not tested. Do not ask the agent to install missing packages during its run.

Custom MCP servers

For a capability beyond the built-ins, mcp-servers: declares the service and allowed narrows the callable tool names. This applies the same least-capability principle; it does not vet the implementation behind those names (MCP tool filtering).

ILLUSTRATIVE frontmatter excerpt from examples/ch08/mcp-shape.md — HTTPS MCP syntax only, NOT a working Slack integration
network:
  allowed: [defaults, mcp.example.invalid]
mcp-servers:
  example:
    url: https://mcp.example.invalid/mcp
    allowed: [lookup_reference]

Do not run this fixture. mcp.example.invalid is a reserved placeholder endpoint, lookup_reference is a placeholder tool name, and provider authentication is deliberately unspecified. The complete fixture strictly compiled on v0.88.7; that validates configuration shape, not the endpoint, tool catalog, or authentication. A real service needs all three verified before adaptation (target frontmatter schema).

Slack remains an optional use case, not a ready-to-run recipe here. Slack's official hosted MCP documentation (inspected 2026-09-16) describes Streamable HTTP and confidential OAuth with user authorization. That does not establish compatibility with an npm/bot-token recipe or unattended Actions authentication. The earlier installation and tool-name guesses have no verified contract in this chapter; a registry TLS failure would not prove that a package does not exist.

The MCP gateway and transport boundaries

The gateway mediates MCP access and enforces the allowed tool filter. Tool filtering is not the same as process isolation. Review the chosen transport and generated runtime configuration instead of assuming a local, separate container for every server (custom server types; MCP gateway specification).

DeclarationExecution and trust boundaryReview
Process / stdio: command + argsLocal executable code communicates over standard input/output. Stdio names the transport, not a sandbox guarantee; inspect how the compiler wraps and launches it.Executable and dependency pins, effective process/container boundary, filesystem access, and environment variables.
Container: containerA packaged local server has a container boundary, whose strength depends on its runtime configuration.Image pin, mounts, privileges, credentials, and network access. A container is not permission to grant broad access.
Remote HTTP: urlThe provider runs the server elsewhere. A gateway connection does not put that service inside your local sandbox.Endpoint ownership, authentication, allowed tools, data sent to the service, and the provider's handling of that data.

Local process/container integrations can receive environment variables through env; HTTP integrations need a provider-appropriate authentication contract. Keep credentials narrowly scoped. The workflow firewall constrains the paths it mediates, not a remote provider's internal network or subsequent use of received data.

Availability is also part of the contract. Servers are startup-critical by default. Optional-server startup failures can warn and let the workflow continue without that server, but at least one server must still connect. Make only genuinely dispensable enrichment optional, and have the assistant disclose missing evidence rather than call a required check complete (startup criticality).

The target's MCP inspection tooling also discovers server-provided prompts; pagination and credential-redaction handling improved during this interval (v0.88.7 inspection implementation). Treat discovered prompts and tool descriptions as external input, not higher-priority policy. Redaction reduces exposure; it does not certify logs as secret-free. Inspection of a named workflow can start processes or contact services: only CLI help and source were inspected here, not a live gh aw mcp inspect session or server connection (inspection command behavior).

When to add a tool or MCP server (and when it's a risk)

Add a tool when the task genuinely can't be done without it — and stop there. The question to ask of every tool is: “if the agent were hijacked, what could it do with this?”

NeedReach for
look up related issues / PRs / codegithub: with the narrowest toolsets
read a linked doc or specweb-fetch: + the domain in network.allowed
run a build or a linterbash: with an explicit command allowlist and an engine that supports it
inspect a rendered page or test a browser interactionplaywright: with CLI-based instructions and the required network destinations
read context from a third-party servicea vetted mcp-servers: entry with a narrow allowed list, reviewed authentication, and an understood transport boundary

When not to

  • Don't grant bash: [":*"] casually. Unrestricted shell is close to unrestricted power. Allowlist exactly the commands the task runs.
  • Don't add an MCP server you haven't vetted. It is either code you execute or a service entrusted with your data. Pin local dependencies/images, review hosted providers, scope allowed tools and credentials, and limit the network paths you control.
  • Don't widen the network just to make a tool work. Broader egress increases the remaining exfiltration exposure discussed in Chapter 7. Review destinations and the data sent to them; add only the specific domains the task requires.
  • Don't confuse tool filtering with write mediation. Keep custom MCP tools read-only in these patterns. A third-party write tool with its own credential does not automatically inherit the repository's safe-outputs: boundary. Do not expose an unreviewed write tool just to demonstrate an integration; GitHub changes still belong in the governed output path from Chapter 6.

Worked example: Repo Assistant queries an MCP server

Let's give the Repo Assistant its first real capabilities. The workflow asks it to query the GitHub MCP server to find duplicate issues and use web-fetch to read a linked spec — a much richer triage than reading the issue text alone.

Frontmatter excerpt from examples/ch08/repo-assistant-tools.md — unchanged GitHub/web-fetch configuration with at most one triage-comment safe output
permissions:
  contents: read
  issues: read
engine: copilot
network:
  allowed:
    - defaults
    - github
tools:
  github:
    toolsets: [issues, repos]
  web-fetch:
safe-outputs:
  add-comment:
    max: 1

The github tool is an MCP integration, scoped here to the issues and repos toolsets. Toolsets select API families; the read-only GitHub integration and agent permissions provide the authority boundary (GitHub tools, v0.88.7). Custom services do not automatically inherit it.

web-fetch uses the governed network path from Chapter 7. The source permits defaults and github, not arbitrary documentation hosts. If a linked spec is outside that policy, the assistant should disclose the missing context rather than improvise a bypass or broaden egress. Even an allowed page remains untrusted input.

Notice what stayed constant: the declared GitHub permissions: are still read-only, and the intended triage write remains at most one add-comment safe output. We added reach, not a direct API write credential. The boundary does not guarantee that the comment is correct or harmless.

Compile in an isolated copy using the fixed v0.88.7 CLI — commands, not a live-run transcript
gh aw version
# Confirm v0.88.7 before compiling.
gh aw compile examples/ch08/repo-assistant-tools.md --strict

The versioned preflight report, content/research/updates/v0.88.7/preflight-verification.json, records a strict compile PASS and an emitted lock for the earlier triage source body. The consolidated post-review refresh also compiled the revised source successfully: v0.88.7, exit code 0 and a nonempty lock with exact-version/strict metadata, recorded in content/research/updates/v0.88.7/verification.json. Its YAML, task instructions, and limits remain unchanged. The two additional sources reproduce positive research fixtures: mcp-shape.md passed strict compilation with effective strict metadata, and playwright-cli.md passed strict compilation plus validation. These are compile-only checks, not proof of provider authentication, tool availability, browser provisioning, or navigation.

Recap & what's next

You can now add capabilities deliberately, while recognizing the exposure each one introduces:

  • Agents need tools to act; MCP is the open protocol that exposes capabilities uniformly — but every tool is also attack surface.
  • The tools: block grants built-in tools (github, bash, edit, web-fetch, playwright…); mcp-servers: adds custom servers with an allowed tool list.
  • Engine contracts matter: strict Codex workflows cannot use restricted Bash allowlists. Keep the restriction and select a reviewed supporting engine, not a broader grant.
  • The MCP gateway filters tool exposure, but process, container, and remote HTTP integrations have different trust boundaries. It does not turn a hosted service into a local sandbox.
  • Built-in Playwright uses playwright-cli through Bash. Migrate prompts as well as configuration; a compile PASS is not a successful browser test.
  • Grant the fewest tools, scoped tightest; review credentials and dependencies, preserve strict mode, and keep third-party write tools out of the read-only patterns. Compilation does not approve secrets or certify runtime integrations.

What's next. That completes the machinery — triggers, engines, safe outputs, security, tools. In Part II's payoff, we assemble it into production-shaped patterns. Chapter 9: Continuous Triage & Docs ships two mini-products the Repo Assistant runs on its own.