Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/aeon/references/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ The script is **sourced, never executed**: it can't abort the run, guards every

If a provider rotates the refresh token on each use, the old one dies immediately. Unless the replacement is **saved back**, the *next* run fails with `invalid_grant` — auth breaks one run later, not now.

Writing a secret needs a secrets-write credential, and **the default `GITHUB_TOKEN` cannot do it.** Add a fine-grained PAT with **Secrets: read/write** as **`MCP_SECRETS_PAT`** (or repo-wide `GH_GLOBAL`).
Writing a secret needs a secrets-write credential, and **the default `GITHUB_TOKEN` cannot do it.** Add a fine-grained PAT with **Secrets: read/write** as **`GH_SECRETS_PAT`** (or repo-wide `GH_GLOBAL`).

**All four catalog providers rotate — treat the PAT as required, not optional.**

Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/aeon/references/secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ Telegram is the fastest to set up and the only one with inline buttons and slash
| `GITHUB_TOKEN` | Built in — nothing to set. Scoped to this repo only |
| `GH_GLOBAL` | github.com/settings/tokens → Fine-grained → select repos → Contents, Pull requests, Issues (read/write). Needed for anything cross-repo (`github-monitor`, `pr-review`, `feature`, `changelog push-to`). Auto-promoted to the run's `GITHUB_TOKEN` |
| `GH_READ_PAT` | Same page, read-only. Optional — enriches cross-repo/private reads (`bd-radar`) without granting write |
| `MCP_SECRETS_PAT` | github.com/settings/personal-access-tokens → **add this repo under Repository access** (a PAT without it 404s) → Repository permissions → Secrets: Read and write. Only needed if you use OAuth-connected MCP servers |
| `GH_SECRETS_PAT` | github.com/settings/personal-access-tokens → **add this repo under Repository access** (a PAT without it 404s) → Repository permissions → Secrets: Read and write. Only needed for OAuth-connected MCP servers or the Grok X-account harness |

`MCP_SECRETS_PAT` gotcha: providers rotate the refresh token every run, and the runner needs this PAT to save each rotation back. Without it, auth breaks exactly one run after you connect. After adding it, re-connect any already-connected server once.
`GH_SECRETS_PAT` gotcha: providers rotate the refresh token every run, and the runner needs this PAT to save each rotation back. Without it, auth breaks exactly one run after you connect. After adding it, re-connect any already-connected server once.

## 4. Skill API keys — all optional

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/aeon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ jobs:
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
# secrets-write PAT so run-grok.sh setup can PERSIST a rotated grok refresh
# token back to GROK_CREDENTIALS - the durable-OAuth-past-6h fix (run-grok.sh §2b).
MCP_SECRETS_PAT: ${{ secrets.MCP_SECRETS_PAT }}
GH_SECRETS_PAT: ${{ secrets.GH_SECRETS_PAT }}
GH_GLOBAL: ${{ secrets.GH_GLOBAL }}
# Optional: seconds-before-expiry that triggers the grok OAuth refresh
# (run-grok.sh §2b, default 1800). Set a large value as a repo variable to
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ jobs:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
GROK_CREDENTIALS: ${{ secrets.GROK_CREDENTIALS }} # grok harness: X-account OAuth (restored by run-grok.sh)
MCP_SECRETS_PAT: ${{ secrets.MCP_SECRETS_PAT }} # persist rotated grok refresh token → GROK_CREDENTIALS (run-grok.sh §2b)
GH_SECRETS_PAT: ${{ secrets.GH_SECRETS_PAT }} # persist rotated grok refresh token → GROK_CREDENTIALS (run-grok.sh §2b)
GH_GLOBAL: ${{ secrets.GH_GLOBAL }}
GROK_OAUTH_SKEW: ${{ vars.GROK_OAUTH_SKEW }} # force grok OAuth refresh + persist every run when set large (durability check)
COINGECKO_API_KEY: ${{ secrets.COINGECKO_API_KEY }}
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ from or pin to; the template keeps serving the latest `main` to new forks.
on `ETHERSCAN_API_KEY`, the same Etherscan v2 key). (#760)
- **Dashboard: per-skill model picker tracks the active harness** — a skill's
detail panel now offers the selected harness's model ids. (#766)
- **Secret rename: `MCP_SECRETS_PAT` -> `GH_SECRETS_PAT`.** One standardized
secrets-write PAT now serves both the OAuth MCP refresh and the Grok X-account
refresh (still falling back to `GH_GLOBAL`). Operators using the old name
should re-add the PAT as `GH_SECRETS_PAT`; `GH_GLOBAL` users are unaffected.

### Fixed

Expand All @@ -47,7 +51,7 @@ from or pin to; the template keeps serving the latest `main` to new forks.
(§2b) now refreshes the access token before each run and **persists the rotated
`auth.json` back to the `GROK_CREDENTIALS` secret** - the same durable-refresh
contract as MCP OAuth. Persisting reuses the secrets-write PAT
(`MCP_SECRETS_PAT` / `GH_GLOBAL`); without it grok warns loudly. Also adds a
(`GH_SECRETS_PAT` / `GH_GLOBAL`); without it grok warns loudly. Also adds a
`grok)` case to the harness AUTH_MODE detection so a Connected X account is
labelled `native-oauth` instead of defaulting to `openrouter`. See
[docs/harnesses.md](docs/harnesses.md) and [docs/mcp-oauth.md](docs/mcp-oauth.md).
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export default function Dashboard() {
{showImport && <ImportModal onClose={() => setShowImport(false)} onImport={importSkill} />}
{showAuthModal && (harness === 'grok'
? <GrokAuthModal loading={grokLoading} onClose={() => setShowAuthModal(false)} onGrokAuth={(p) => setupGrokAuth(p)}
patSet={secrets.some(s => s.isSet && (s.name === 'MCP_SECRETS_PAT' || s.name === 'GH_GLOBAL'))}
patSet={secrets.some(s => s.isSet && (s.name === 'GH_SECRETS_PAT' || s.name === 'GH_GLOBAL'))}
onGoToSecret={(n) => { setShowAuthModal(false); goToSecret(n) }} />
: HARNESS_AUTH[harness]
? <HarnessAuthModal harness={harness} loading={harnessAuthLoading} onClose={() => setShowAuthModal(false)} onHarnessAuth={(p) => setupHarnessAuth(harness, p)} />
Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/components/GrokAuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface GrokAuthModalProps {
loading: boolean
onClose: () => void
onGrokAuth: (payload?: { key: string }) => void
// Whether a secrets-write PAT (MCP_SECRETS_PAT / GH_GLOBAL) is
// Whether a secrets-write PAT (GH_SECRETS_PAT / GH_GLOBAL) is
// set - the OAuth session rotates its refresh token and only survives past 6h if
// the runner can persist each rotation back to GROK_CREDENTIALS. Hides the note once set.
patSet: boolean
Expand All @@ -38,14 +38,14 @@ export function GrokAuthModal({ loading, onClose, onGrokAuth, patSet, onGoToSecr
{/* Secrets-PAT setup note (parallels McpPanel's). The X-account session
rotates its refresh token on every run; the runner can only save each
rotation back with a secrets-write PAT. Without one, a Connected account
works ~6h and then its auth breaks. Hidden once MCP_SECRETS_PAT / GH_GLOBAL
works ~6h and then its auth breaks. Hidden once GH_SECRETS_PAT / GH_GLOBAL
is set. */}
{!patSet && (
<div className="mt-3 border border-[rgba(250,250,250,0.10)] bg-aeon-panel px-[var(--space-md)] py-[var(--space-sm)]">
<p className="text-[10px] font-mono uppercase tracking-[0.14em] text-aeon-red mb-1.5">⚠ The X-account session won&apos;t keep working without a secrets PAT</p>
<p className="text-[11px] text-primary-40 leading-relaxed">
Grok rotates its refresh token on every run, and the runner needs a secrets-write credential to save each rotation - without it a Connected account works once (~6h), then its auth breaks. To set it up: create a fine-grained PAT at <a href="https://github.com/settings/personal-access-tokens" target="_blank" rel="noopener noreferrer" className="text-primary-70 underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">github.com/settings/personal-access-tokens</a>, add this repo under <span className="text-primary-70">Repository access</span>, grant <span className="text-primary-70">Secrets: Read and write</span>, and save it as{' '}
<button onClick={() => onGoToSecret('MCP_SECRETS_PAT')} title="Open in Settings to set this key" className="text-aeon-red-alert underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">MCP_SECRETS_PAT</button>
<button onClick={() => onGoToSecret('GH_SECRETS_PAT')} title="Open in Settings to set this key" className="text-aeon-red-alert underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">GH_SECRETS_PAT</button>
{' '}in Settings. Already Connected? Re-connect once after adding the PAT.
</p>
</div>
Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/components/McpPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ export function McpPanel({ harness, servers, loading, saving, secrets, busy, onS
on every run; the runner can only save each rotation back if a
secrets-write PAT is set. Without one, an OAuth MCP server works for
exactly one run after Connect and then its auth breaks. Shown until
MCP_SECRETS_PAT (or repo-wide GH_GLOBAL) exists; hidden once set. */}
{!(isSecretSet('MCP_SECRETS_PAT') || isSecretSet('GH_GLOBAL')) && (
GH_SECRETS_PAT (or repo-wide GH_GLOBAL) exists; hidden once set. */}
{!(isSecretSet('GH_SECRETS_PAT') || isSecretSet('GH_GLOBAL')) && (
<div className="mt-3 border border-[rgba(250,250,250,0.10)] bg-aeon-panel px-[var(--space-md)] py-[var(--space-sm)]">
<p className="text-[10px] font-mono uppercase tracking-[0.14em] text-aeon-red mb-1.5">⚠ OAuth MCP servers won&apos;t keep working without a secrets PAT</p>
<p className="text-[11px] text-primary-40 leading-relaxed">
Providers rotate their refresh token on every run, and the runner needs a secrets-write credential to save each rotation — without it a Connected server works once, then its auth breaks. To set it up: create a fine-grained PAT at <a href="https://github.com/settings/personal-access-tokens" target="_blank" rel="noopener noreferrer" className="text-primary-70 underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">github.com/settings/personal-access-tokens</a>, add this repo under <span className="text-primary-70">Repository access</span>, grant <span className="text-primary-70">Secrets: Read and write</span>, and save it as{' '}
<button onClick={() => onGoToSecret('MCP_SECRETS_PAT')} title="Open in Settings to set this key" className="text-aeon-red-alert underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">MCP_SECRETS_PAT</button>
<button onClick={() => onGoToSecret('GH_SECRETS_PAT')} title="Open in Settings to set this key" className="text-aeon-red-alert underline decoration-dotted underline-offset-2 hover:text-aeon-fg transition-colors">GH_SECRETS_PAT</button>
{' '}in Settings. Already Connected a server? Re-connect it once after adding the PAT.
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/lib/mcp-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const MCP_CATALOG: McpCatalogEntry[] = [
// (auth method "none"). Its ONLY advertised scope is "internal" — do NOT request
// offline_access here (glim needs it, Robinhood doesn't have it and would reject
// it); refresh tokens come from the refresh_token grant by default. Durable
// refresh (rotated-token persistence via MCP_SECRETS_PAT) is handled generically
// refresh (rotated-token persistence via GH_SECRETS_PAT) is handled generically
// by scripts/mcp-oauth-refresh.sh — see docs/mcp-oauth.md.
oauth: true,
},
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/lib/secrets-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const BUILTIN_SECRETS: Omit<Secret, 'isSet'>[] = [
{ name: 'ADMANAGE_API_KEY', group: 'Skill Keys', description: 'AdManage API key - ad-campaign skill (schedule-ads). From admanage.ai/api-docs' },
{ name: 'GH_GLOBAL', group: 'Skill Keys', description: 'GitHub PAT with cross-repo WRITE access - cross-repo skills & deploys (changelog push-to, feature external, deploy-prototype, vuln-scanner). Auto-promoted to the run\'s GITHUB_TOKEN. Create one at github.com/settings/tokens' },
{ name: 'GH_READ_PAT', group: 'Skill Keys', description: 'GitHub read-only PAT - optional. Enriches cross-repo / private-repo reads (bd-radar); kept separate from the write-capable GH_GLOBAL for least privilege. Without it those skills fall back to public data. Create a read-only token at github.com/settings/tokens' },
{ name: 'MCP_SECRETS_PAT', group: 'Skill Keys', description: 'Fine-grained GitHub PAT with Secrets: read/write on THIS repo - keeps OAuth-connected MCP servers working. Providers rotate the refresh token on every run; the runner needs this PAT to save each rotation back, otherwise auth breaks one run after Connect. Create at github.com/settings/personal-access-tokens: add this repo under Repository access (a PAT without it 404s) + Repository permissions > Secrets: Read and write. Then re-connect any already-connected server once' },
{ name: 'GH_SECRETS_PAT', group: 'Skill Keys', description: 'Fine-grained GitHub PAT with Secrets: read/write on THIS repo - keeps OAuth-connected MCP servers and the Grok X-account harness working. Providers rotate the refresh token on every run; the runner needs this PAT to save each rotation back, otherwise auth breaks one run after Connect. Create at github.com/settings/personal-access-tokens: add this repo under Repository access (a PAT without it 404s) + Repository permissions > Secrets: Read and write. Then re-connect any already-connected server once' },
{ name: 'BASE_RPC_URL', group: 'Skill Keys', description: 'Custom Base RPC endpoint - onchain Base skills (investigation-report, token-movers). Optional: a public RPC is used by default; set a paid endpoint to lift rate limits. Find a provider at docs.base.org/chain/node-providers' },
]

Expand Down
2 changes: 1 addition & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ cp docs/examples/mcp/.mcp.json.example .mcp.json # then edit, commit, push

The example ships two working servers — `github` (uses the runner's built-in `GITHUB_TOKEN`) and `sequential-thinking` (no-auth stdio). On the next run the runner loads `.mcp.json` and auto-allows every server's tools, so a skill can just say *"use the github MCP server to …"*. Reference a server's secret with `${VAR}` (never commit the value) and set it in the dashboard — the runner resolves it from the repo's secrets with zero workflow editing, and skips a server (with a warning) when its secret is missing rather than breaking the skill.

Or skip the file entirely: the dashboard's **MCP** tab writes `.mcp.json` for you, lists **Featured** servers ([Base](https://mcp.base.org), [Robinhood Trading](https://agent.robinhood.com), [glim.sh](https://glim.sh), [Executor](https://executor.sh)) for one-click install, and tells you which secret each server needs. The featured servers are OAuth-gated: **Connect** opens your browser to authorize, then keeps the tokens fresh across headless runs — including saving rotated refresh tokens, which needs a secrets-write PAT (`MCP_SECRETS_PAT`). Flow, PAT setup, and limits: [`docs/mcp-oauth.md`](mcp-oauth.md). Each featured server has a matching on-demand skill (`base-mcp`, `robinhood-mcp`, `glim-mcp`, `executor-mcp`) — dispatch it with a `var` to use the server from a run.
Or skip the file entirely: the dashboard's **MCP** tab writes `.mcp.json` for you, lists **Featured** servers ([Base](https://mcp.base.org), [Robinhood Trading](https://agent.robinhood.com), [glim.sh](https://glim.sh), [Executor](https://executor.sh)) for one-click install, and tells you which secret each server needs. The featured servers are OAuth-gated: **Connect** opens your browser to authorize, then keeps the tokens fresh across headless runs — including saving rotated refresh tokens, which needs a secrets-write PAT (`GH_SECRETS_PAT`). Flow, PAT setup, and limits: [`docs/mcp-oauth.md`](mcp-oauth.md). Each featured server has a matching on-demand skill (`base-mcp`, `robinhood-mcp`, `glim-mcp`, `executor-mcp`) — dispatch it with a `var` to use the server from a run.

## Cross-repo access

Expand Down
2 changes: 1 addition & 1 deletion docs/harnesses.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ that, `scripts/run-grok.sh` (§2b) refreshes the access token from the refresh t
before each run and **persists the rotated `auth.json` back to the `GROK_CREDENTIALS`
secret**. Persisting a secret needs a secrets-write credential - the default
`GITHUB_TOKEN` cannot - so set a fine-grained PAT with **Secrets: read/write** as
`MCP_SECRETS_PAT` (or `GH_GLOBAL`). Without the PAT, grok
`GH_SECRETS_PAT` (or `GH_GLOBAL`). Without the PAT, grok
warns loudly and auth breaks one run after the first post-expiry refresh. **After
adding the PAT, re-connect the X account once** to seed a valid refresh token (a token
already consumed by a prior run can't be revived by the PAT alone). Concurrent grok
Expand Down
4 changes: 2 additions & 2 deletions docs/mcp-oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ discovery notes) and their companion skills:

The glim and Executor loops were verified end-to-end on a live instance
(Connect → per-run refresh → PAT-persisted rotation → refresh off the persisted
token, no re-connect): every catalog provider rotates, so treat `MCP_SECRETS_PAT`
token, no re-connect): every catalog provider rotates, so treat `GH_SECRETS_PAT`
as **required** in practice, not optional.

## Code map
Expand Down Expand Up @@ -80,7 +80,7 @@ as **required** in practice, not optional.
and auth breaks one run later. Persisting a secret needs a **secrets-write
credential** — the default `GITHUB_TOKEN` cannot write secrets. To make refresh
durable for rotating providers, add a fine-grained PAT with **Secrets: read/write**
on this repo as the secret **`MCP_SECRETS_PAT`** (or a repo-wide `GH_GLOBAL`);
on this repo as the secret **`GH_SECRETS_PAT`** (or a repo-wide `GH_GLOBAL`);
`scripts/mcp-oauth-refresh.sh` then saves each rotated refresh token back to its
`MCP_<SLUG>_OAUTH` secret and warns loudly when it can't. **After adding the PAT,
re-connect the affected server once** to seed a valid refresh token — a refresh
Expand Down
Loading