mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
feat: Add Flaky MCP to headless OpenCode sessions (no-changelog) (#37809)
This commit is contained in:
parent
a9b713be58
commit
fcabdcae3c
|
|
@ -65,7 +65,7 @@ It runs the turn. It sends the result to the turn's resume URL. It uses no
|
|||
tunnel, no open port, and no domain.
|
||||
|
||||
The worker starts on each container start (`postStartCommand`). It needs three
|
||||
secrets and uses one optional secret. Add them at
|
||||
secrets and uses three optional secrets. Add them at
|
||||
[github.com/settings/codespaces](https://github.com/settings/codespaces), the
|
||||
same way as `ANTHROPIC_API_KEY`:
|
||||
|
||||
|
|
@ -73,6 +73,7 @@ same way as `ANTHROPIC_API_KEY`:
|
|||
- `N8N_DEQUEUE_URL` — the n8n webhook that returns a pending turn.
|
||||
- `OPENROUTER_API_KEY` — the model provider key for Sol.
|
||||
- `SLACK_BOT_TOKEN` — optional bot token for progress messages. It needs `chat:write` only.
|
||||
- `FLAKY_MCP_URL` and `FLAKY_MCP_TOKEN` — optional Flaky MCP connection for OpenCode.
|
||||
|
||||
The worker explicitly reloads `/usr/local/lib/codespaces-env.sh` when its tmux
|
||||
session starts. An existing tmux server can otherwise retain an environment
|
||||
|
|
@ -147,11 +148,12 @@ session rarely needs a cold `pnpm install` or a full `pnpm build`. Both are slow
|
|||
|
||||
## Flaky tools (MCP)
|
||||
|
||||
Claude sessions get the `flaky` MCP server automatically: Currents
|
||||
Claude sessions and the headless OpenCode worker get the `flaky` MCP server automatically: Currents
|
||||
flaky/quarantine data, the `qa_*` BigQuery dataset, Sentry RCA, live Linear,
|
||||
and repo investigation. Login registers it from the repo-level
|
||||
`FLAKY_MCP_TOKEN` / `FLAKY_MCP_URL` secrets. Forks have no secrets and skip
|
||||
it. Tell the agent to call `get_flaky_context` first — it returns the rules
|
||||
and repo investigation. The worker keeps the token in its environment and puts
|
||||
only an environment reference in the OpenCode config. Claude login registers
|
||||
the same server without writing the token to disk. Forks have no secrets and
|
||||
skip it. Tell the agent to call `get_flaky_context` first — it returns the rules
|
||||
the tools assume.
|
||||
|
||||
## Quality and security skills (Claude plugins)
|
||||
|
|
|
|||
|
|
@ -19,9 +19,24 @@ const INITIAL_POLL_INTERVAL_MS = 3000;
|
|||
const MAX_POLL_INTERVAL_MS = 30_000;
|
||||
const SLACK_UPDATE_INTERVAL_MS = 1500;
|
||||
const SLACK_TEXT_LIMIT = 3900;
|
||||
const OPENCODE_CONFIG_CONTENT = JSON.stringify({
|
||||
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
|
||||
});
|
||||
|
||||
export function openCodeConfig(environment) {
|
||||
const config = {
|
||||
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
|
||||
};
|
||||
if (environment.FLAKY_MCP_URL && environment.FLAKY_MCP_TOKEN) {
|
||||
config.mcp = {
|
||||
flaky: {
|
||||
type: 'remote',
|
||||
url: environment.FLAKY_MCP_URL,
|
||||
enabled: true,
|
||||
oauth: false,
|
||||
headers: { Authorization: 'Bearer {env:FLAKY_MCP_TOKEN}' },
|
||||
},
|
||||
};
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function posNum(name, fallback) {
|
||||
const raw = process.env[name];
|
||||
|
|
@ -74,7 +89,7 @@ export function openCodeEnvironment(environment) {
|
|||
delete childEnvironment.AGENT_WORKER_TOKEN;
|
||||
delete childEnvironment.N8N_DEQUEUE_URL;
|
||||
delete childEnvironment.SLACK_BOT_TOKEN;
|
||||
childEnvironment.OPENCODE_CONFIG_CONTENT = OPENCODE_CONFIG_CONTENT;
|
||||
childEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify(openCodeConfig(childEnvironment));
|
||||
return childEnvironment;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,13 @@ import { PassThrough } from 'node:stream';
|
|||
import { test } from 'node:test';
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
|
||||
import { openCodeEnvironment, pollOnce, runOpenCode, startSlackProgress } from './agent-worker.mjs';
|
||||
import {
|
||||
openCodeConfig,
|
||||
openCodeEnvironment,
|
||||
pollOnce,
|
||||
runOpenCode,
|
||||
startSlackProgress,
|
||||
} from './agent-worker.mjs';
|
||||
|
||||
const turn = {
|
||||
turnId: 'turn-1',
|
||||
|
|
@ -341,3 +347,32 @@ test('keeps broker credentials out of the OpenCode process', () => {
|
|||
GITHUB_TOKEN: 'github',
|
||||
});
|
||||
});
|
||||
|
||||
test('configures Flaky MCP without serializing its token', () => {
|
||||
const token = 'flaky-secret';
|
||||
const environment = openCodeEnvironment({
|
||||
FLAKY_MCP_URL: 'https://example.com/mcp',
|
||||
FLAKY_MCP_TOKEN: token,
|
||||
OPENROUTER_API_KEY: 'openrouter',
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(environment.OPENCODE_CONFIG_CONTENT), {
|
||||
provider: { openrouter: { options: { apiKey: '{env:OPENROUTER_API_KEY}' } } },
|
||||
mcp: {
|
||||
flaky: {
|
||||
type: 'remote',
|
||||
url: 'https://example.com/mcp',
|
||||
enabled: true,
|
||||
oauth: false,
|
||||
headers: { Authorization: 'Bearer {env:FLAKY_MCP_TOKEN}' },
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(environment.FLAKY_MCP_TOKEN, token);
|
||||
assert.equal(environment.OPENCODE_CONFIG_CONTENT.includes(token), false);
|
||||
});
|
||||
|
||||
test('omits Flaky MCP when either setting is missing', () => {
|
||||
assert.equal(openCodeConfig({ FLAKY_MCP_URL: 'https://example.com/mcp' }).mcp, undefined);
|
||||
assert.equal(openCodeConfig({ FLAKY_MCP_TOKEN: 'flaky-secret' }).mcp, undefined);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user