Summary: Required gh-aw frontmatter shared by every news workflow. MCP gateway setup. EP MCP client activation. Everything that is configuration, not behaviour.
Every news-*.md workflow defines runtimes:, network:, tools:, and its
own safe-outputs: block directly in the workflow frontmatter. The
mcp-servers: block is not inlined per workflow — it is provided via a
shared gh-aw import so the five MCP mounts (European Parliament, World Bank,
fetch-proxy, memory, sequential-thinking) stay in lockstep across all
workflows. The IMF integration uses the native TypeScript client for SDMX
parsing and request construction, with the shared fetch-proxy MCP server as
the primary gh-aw/AWF transport because direct HTTPS from the agent sandbox can
be blocked by Squid.
imports:
- shared/mcp/news-mcp-servers.md # provides mcp-servers: (all four mounts)The workflow frontmatter then adds the non-MCP configuration:
runtimes:
node:
version: "26" # Runner uses Node.js 26
# Network allowlist — uses ecosystem identifiers where possible (per
# upstream docs/reference/network.md §"Ecosystem Identifiers"):
# - `defaults` — basic infrastructure (certs, JSON schema, package mirrors)
# - `github` — all GitHub domains (replaces explicit github.com/api.github.com)
# - `node` — npm/npx (needed for MCP server boot via npx)
network:
allowed:
- defaults # basic infrastructure
- github # GitHub domains (replaces github.com/api.github.com)
- node # npm/npx installation
- data.europarl.europa.eu
- "*.europa.eu"
- api.imf.org # IMF SDMX 3.0 REST — sole authoritative economic source
- api.worldbank.org # WB MCP — non-economic domains
- hack23.com
- www.hack23.com
- riksdagsmonitor.com
- www.riksdagsmonitor.com
- euparliamentmonitor.com
- www.euparliamentmonitor.com
# Sandbox — the MCP gateway uses its upstream default keepalive to keep
# backend HTTP sessions warm. The agent ↔ gateway streamable-HTTP session
# lifetime cannot currently be configured from frontmatter —
# `engine.mcp.session-timeout` is advertised by the gh-aw v0.71.3 compiler
# but the bundled gateway image `ghcr.io/github/gh-aw-mcpg:v0.3.1` rejects
# the field with `additionalProperties 'sessionTimeout' not allowed` (run
# #25275823699 fingerprint). Until upstream ships a compatible gateway
# image, do NOT set `engine.mcp.session-timeout`. The workflow's 60-min
# `timeout-minutes` cap and the PR-call deadline at minute ≤ 45 remain
# authoritative.
sandbox:
agent: awf
mcp:
port: 8080
# Engine — `engine.mcp.session-timeout` is intentionally NOT set; see the
# comment block above. Without it, the gateway uses its upstream default
# session lifetime.
engine:
id: copilot
model: claude-sonnet-4.6
tools:
timeout: 180 # per-tool-call cap (bash, MCP, github, edit, web-fetch)
startup-timeout: 180 # MCP server boot via npx package-pull
github:
toolsets: [all] # all read toolsets EXCEPT `dependabot` (intentional)
bash: true
edit: # explicit file-edit tool for analysis artifact authoring
web-fetch: # fallback fetch for EP/IMF/WB pages when MCP misses
agentic-workflows: true
# Cache memory — restores partial Stage A/B work across runs so a failed
# safe-outputs PR call does not lose 20+ minutes of analysis. Compiler
# auto-injects restore + save steps using a workflow-scoped key; see
# upstream `reference/cache-memory.md`.
cache-memory:
key: news-<type>-${{ github.repository_owner }}
retention-days: 7
allowed-extensions: [".md", ".json", ".jsonl", ".txt", ".html"]The current shared mcp-servers: block (imported via
shared/mcp/news-mcp-servers.md) is:
mcp-servers:
european-parliament:
container: "node:26-alpine"
entrypoint: "npx"
entrypointArgs: ["-y", "european-parliament-mcp-server@1.4.0", "--timeout", "180000"]
env:
EP_REQUEST_TIMEOUT_MS: "180000"
world-bank:
container: "node:26-alpine"
entrypoint: "npx"
entrypointArgs: ["-y", "worldbank-mcp@1.0.1"]
fetch-proxy:
container: "node:26-alpine"
entrypoint: "node"
entrypointArgs: ["-e", "<inline IMF-only JSON-RPC fetch server>"]
memory:
container: "node:26-alpine"
entrypoint: "npx"
entrypointArgs: ["-y", "@modelcontextprotocol/server-memory"]
sequential-thinking:
container: "node:26-alpine"
entrypoint: "npx"
entrypointArgs: ["-y", "@modelcontextprotocol/server-sequential-thinking"]Do not duplicate this block back into individual workflows — edit the
shared component (.github/workflows/shared/mcp/news-mcp-servers.md) and
recompile.
Safe-outputs block: see 06-pr-and-safe-outputs.md §6.
- ❌ Never add
tools: ["*"]/allowed: ["*"]. The gh-aw MCP gateway (awmg) treats*as a literal tool name and exposes 0 tools. Omit the field entirely. - ❌ Never use
node:lts-alpineornode:26-alpine— MCP backend containers must match the workflow Node 26 runtime (node:26-alpine). - ✅
EP_REQUEST_TIMEOUT_MS: "180000"(180 s) handles slow feed endpoints. - ✅
fetch-proxymust expose exactlyfetch_url, must not use\nstring literals in the inlinenode -escript (gh-aw/docker argument decoding can turn them into invalid embedded newlines), and must constrain fetches tohttps://api.imf.org/external/sdmx/3.0/. - Other contexts (
copilot-setup-steps.yml,copilot-mcp.json) use the same 180 s EP timeout to avoid drift between local Copilot and gh-aw runs.
source scripts/mcp-setup.sh resolves gateway domain:port from
/home/runner/.copilot/mcp-config.json (default host.docker.internal:8080
on gh-aw v0.69+) and exports:
EP_MCP_GATEWAY_URL=http://host.docker.internal:8080/mcp/european-parliament
EP_MCP_GATEWAY_API_KEY=<extracted via node -e, no jq>
WORLD_BANK_MCP_SERVER_URL=http://host.docker.internal:8080/mcp/world-bank
FETCH_MCP_GATEWAY_URL=http://host.docker.internal:8080/mcp/fetch-proxy
IMF_API_BASE_URL=https://api.imf.org/external/sdmx/3.0
MCP_CLIENT_TIMEOUT_MS=180000
IMF_API_TIMEOUT_MS=120000
Token priority: gateway.apiKey → mcpServers['european-parliament'].headers.Authorization.
Port/domain priority: dynamic gateway.{port,domain} → default
host.docker.internal:8080. AWF allowlist has 80, 443, 8080.
Environment variables do NOT persist across bash blocks.
mcp-setup.sh, the generation script, andUSE_EP_MCP=truemust be in the same bash block.
source scripts/mcp-setup.sh
# Fallback: stdio mode verification
if [ -z "${EP_MCP_GATEWAY_URL:-}" ]; then
if [ -f "node_modules/.bin/european-parliament-mcp-server" ]; then
echo "✅ EP MCP binary found for stdio mode"
else
npm install --no-save european-parliament-mcp-server@1.4.0
fi
fi
export USE_EP_MCP=true
# No-op: Stage A data collection is handled within the workflow prompt body.
# Stage D article rendering uses: npm run generate-article -- --run "${ANALYSIS_DIR}"All article-generating workflows (news-breaking, news-week-in-review,
news-month-in-review, news-week-ahead, news-month-ahead,
news-committee-reports, news-motions, news-propositions) start the
IMF live probe immediately after this setup and before EP MCP fan-out.
The probe is mandatory — every article type carries an IMF minimum
≥1 indicator per 01-data-collection.md §4, and
Stage C fails any economic-context.md that cites IMF figures from agent
knowledge without cache/imf/*.json. The phrase "macro-context workflows"
in earlier revisions of this section was ambiguous; the rule now applies
to every article-generating workflow without exception. Only
news-translate.md (no analysis stage) is exempt.
mkdir -p "${ANALYSIS_DIR}/cache/imf"
scripts/imf-mcp-probe.sh > "${ANALYSIS_DIR}/cache/imf/probe-summary.json" &
IMF_PROBE_PID=$!
# Run EP MCP collection here, then:
wait "$IMF_PROBE_PID" || trueSelf-check before Stage B: confirm ${ANALYSIS_DIR}/cache/imf/probe-summary.json
exists (even when the probe returned {"available": false} — the file is
the provenance signal). A missing probe-summary.json is a Stage A defect:
re-run the probe synchronously before exiting Stage A.
IMF-unavailable degraded mode ({"available": false}). When the probe
file exists and reports {"available": false}, treat the run as an
explicit IMF-unavailable degraded mode:
- Provenance is satisfied by the saved probe summary (the
cache/imf/probe-summary.jsonfile is the audit record of the unavailability). - IMF minimums for this run are waived — Stage C does not RED on a
missing per-article-type IMF count when probe-summary records
available: false. economic-context.mdMUST NOT cite IMF figures from agent knowledge, MUST NOT claim IMF-backed completeness, and MUST surface the unavailability with a 🔴 marker plus the full probe error message in §"Data freshness".- Downstream stages (Stage D article render) MUST NOT inject IMF citations into prose.
- Fail-early exception: if the planned article type cannot remain
valid without IMF-backed data — currently
committee-reportsruns scoped to ECON / BUDG / INTA, where the IMF minimum is ≥3 indicators — fail early in Stage A after writing the probe summary, setGATE_RESULT=ANALYSIS_ONLY, and record the unavailability as the justification inruns/stage-a-fail.log. All other article types proceed in degraded mode.
The waiver applies only to runs where available: false is the
authentic probe outcome. Any IMF-cited prose must still be backed by
cache/imf/*.json files when the probe reports available: true.
${ARTICLE_TYPE_SLUG}, ${ANALYSIS_DIR}, and ${TODAY} are set by the
workflow's own Date Context Establishment block (see each news-*.md §Date
Context). Title/description are AI-generated per
04-article-generation.md §Keywords/Title rules.
All unified workflow runs write to the canonical stable folder:
analysis/daily/${DATE}/${ARTICLE_TYPE_SLUG}/
Use the scripts/resolve-analysis-dir.sh helper to echo this path and create
the standard subdirectories (no nested parameter expansion, AWF-safe):
TODAY=$(date -u +%Y-%m-%d)
ANALYSIS_DIR=$(scripts/resolve-analysis-dir.sh "$TODAY" "$ARTICLE_TYPE_SLUG")
echo "ANALYSIS_DIR=$ANALYSIS_DIR" >> "$GITHUB_ENV"Workflows pass this path to npm run generate-article -- --run "$ANALYSIS_DIR",
which reads the committed analysis artifacts and renders the article HTML.
Every 60-min news workflow exports WORKFLOW_START_EPOCH from its Stage A
Date-Context block (see shared/prompts/news-unified-stages.md). The
scripts/gh-aw-workflow-elapsed.sh helper consumes that env var and emits
elapsed-minute bookkeeping in three modes — use it instead of inlining the
two-line NOW_EPOCH=… ; ELAPSED_MIN=… snippet:
# Eval-friendly assignments (use anywhere you need the numbers):
eval "$(bash scripts/gh-aw-workflow-elapsed.sh env)"
# → ELAPSED_MIN, REMAINING_MIN, WORKFLOW_TIMEOUT_MIN
# Structured log line (drop between stages so the run log shows time pressure):
bash scripts/gh-aw-workflow-elapsed.sh status --stage C --tripwire 36 --name stage-c-exit
# → TIME_STATUS: elapsed=30m remaining=30m timeout=60m stage=C tripwire=stage-c-exit:36m budget=6m
# Guard mode (exits 1 when the tripwire is crossed — short-circuit slow ops):
bash scripts/gh-aw-workflow-elapsed.sh guard --tripwire 36 --name stage-c-exit \
|| { echo "Stage C tripwire crossed — force GATE_RESULT=ANALYSIS_ONLY"; export GATE_RESULT=ANALYSIS_ONLY; }Per-slug tripwires (look up in src/config/article-horizons.ts):
short/mid prospective & retrospective → 36 min; long-horizon prospective
→ 39 min; long-horizon retrospective → 38 min; electoral overlay →
42 min. The PR-call hard deadline is 45 min (target ≤ 42 standard,
≤ 47 electoral). All news workflows hard-cap at 60 min
(timeout-minutes: 60) regardless of MCP gateway state. The helper is
shell-safety compliant (no nested expansions, no eval of arbitrary
strings — see §47 of 00-scope-and-ground-rules.md).
Source: src/mcp/ep-mcp-client.ts → compiled
to scripts/mcp/ep-mcp-client.js.
| Mode | When | Activation |
|---|---|---|
| Gateway (HTTP) | AWF sandbox / agentic workflows | EP_MCP_GATEWAY_URL env var (set by mcp-setup.sh) |
| Stdio | Local dev / standard CI | Default when EP_MCP_GATEWAY_URL unset |
Env vars read by the client:
| Var | Purpose | Default |
|---|---|---|
EP_MCP_GATEWAY_URL |
Gateway URL | — (stdio if unset) |
EP_MCP_GATEWAY_API_KEY |
Gateway auth | — |
EP_REQUEST_TIMEOUT_MS |
Per-request timeout (ms) | 120000 |
MCP_CLIENT_TIMEOUT_MS |
Client-level timeout | 120000 |
EP_MCP_SERVER_PATH |
Binary path (stdio only) | european-parliament-mcp-server |
Every bash/shell tool call MUST include both command AND description
fields. Missing either field fails validation, wastes a turn, and can stall
the workflow.
Sandbox-safe patterns:
- ❌
$($(...))nested substitution — assign to a variable first. - ❌
${VAR:-$(cmd || cmd2)}default-with-fallback — useif/else. - ❌ Adjacent
${RANDOM}${RANDOM}— use$$and$(date +%s)on separate lines. - ❌
$(cmd < file)input redirection inside substitution — usecmd fileor pipe.
Pinned in compile-agentic-workflows.yml
(GH_AW_VERSION: "v0.69.3"). If this drifts, verify the current value in
that workflow file. Harden Runner pinned by SHA.
gh aw mcp inspect # list all
gh aw mcp inspect news-breaking # one workflow
gh aw mcp inspect news-breaking --server european-parliament # one server
gh aw mcp inspect news-breaking --server european-parliament --tool get_plenary_sessions