- cloudflare-pages-preview/README.md: replace secrets table with per-secret subsections giving exact CF dashboard navigation for obtaining the API token (My Profile > API Tokens > Custom Token, Account > Pages > Edit permission) and account ID (right sidebar on any domain/Workers overview), plus explicit guidance to store each as a named repo secret - CLAUDE.md: README secrets section must now document the exact UI path to obtain each secret, minimum permission scope, input mapping, and storage location
4.3 KiB
Adding new actions to this repo
This repo holds composite GitHub Actions reused across MCP-family repos. Before adding a new action, confirm the logic is (or will be) needed in more than one caller repo — single-repo logic should stay in that repo's workflow.
Directory layout
Each action lives in its own directory. If a feature has multiple related entry points (e.g. deploy + cleanup), group them under a common parent:
<action-name>/
README.md # usage docs, inputs/outputs, caller requirements
action.yml # the composite action
or for multi-action groups:
<group-name>/
README.md # covers all actions in the group
<sub-action>/
action.yml
<sub-action>/
action.yml
Caller references:
modelcontextprotocol/actions/<action-name>@mainmodelcontextprotocol/actions/<group-name>/<sub-action>@main
Composite action conventions
Required fields
Every action.yml must include:
name— human-readable namedescription— one or two sentences on what it doesinputs— withdescriptionandrequiredfor each; usedefaultfor optional inputsruns.using: composite
Every run: step needs an explicit shell:
Composite actions do not inherit a default shell. Omitting shell: fails at
runtime, not validation time.
- name: Do something
shell: bash
run: |
echo "..."
Pass inputs to scripts via env:, not interpolation
Direct interpolation of ${{ inputs.* }} into run: or script: bodies is
an injection vector. Pass values through env: and read them as environment
variables or process.env.*:
- shell: bash
env:
MY_INPUT: ${{ inputs.my-input }}
run: |
echo "${MY_INPUT}"
- uses: actions/github-script@v8
env:
MY_INPUT: ${{ inputs.my-input }}
with:
script: |
const val = process.env.MY_INPUT;
Do not actions/checkout inside a composite action
The caller has already checked out; the composite runs in that workspace. Document in the README if a checkout is required on the caller side.
Secrets are always caller-provided
Composite actions cannot access secrets.* directly. Define inputs for any
credentials and document in the README which secrets the caller must configure
and pass through.
github-token input
If the action posts comments, creates issues, or otherwise calls the GitHub
API, accept a github-token input with default ${{ github.token }}. This
lets callers override with a PAT if needed:
github-token:
description: GitHub token for API calls
required: false
default: ${{ github.token }}
Outputs
Expose anything a caller might want to reference downstream. Wire outputs from
steps to the action's outputs: block:
outputs:
url:
description: Deployment URL
value: ${{ steps.deploy.outputs.url }}
README requirements
Each action (or group) must have a README.md covering:
-
What it does — one paragraph per action
-
Prerequisites — external resources that must exist (CF project, etc.)
-
Required secrets — dedicated section covering every secret the caller must configure. For each secret, document:
- Where to get it — exact navigation path in the external dashboard/UI (e.g. "Cloudflare → My Profile → API Tokens → Create Token")
- Minimum permission scope — the least privilege that makes the action work
- Which action input it maps to — so callers know how to wire
secrets.*intowith: - How to store it — "repo secret named e.g.
FOO_TOKEN" (suggest a name; actual name is caller's choice)
If no secrets are needed, state that explicitly.
-
Caller requirements —
permissions:, fork-PR guards, concurrency -
Usage — a minimal but complete caller workflow example
-
Inputs/outputs — tables with name, required, default, description
Registering the action
Add a row to the root README.md table with the action path and
a one-line description.
Validation
- Parse check:
python3 -c "import yaml; yaml.safe_load(open('path/action.yml'))" - Structure: verify every
run:step hasshell: - Full verification requires a live caller workflow — open a PR in a consuming
repo that references
@<your-branch>and confirm end-to-end behavior before merging