nexu-io/open-design · error · Error
raw API keys are not accepted by Open Design MCP. Configure
Error message
raw API keys are not accepted by Open Design MCP. Configure Local BYOK in the Open Design UI and start that run from the local product instead.
What it means
startRun refuses to launch a run if args contains apiKey or byokProvider, or if args.inputs contains a field whose name matches a credential pattern (api_key, authorization, access_token, refresh_token, secret, password, checked recursively up to depth 20). Open Design MCP deliberately does not accept raw provider credentials; keys must live in Local BYOK configured through the UI.
Source
Thrown at apps/daemon/src/mcp.ts:2476
}
// Commission a generation run. The caller never runs the skill/plugin
// itself; we POST to /api/runs and the daemon spawns its own agent.
// Returns the runId immediately so the caller can poll get_run —
// start+poll because MCP is request/response and generation is
// minutes-long.
async function startRun(
baseUrl: string,
args: McpArgs,
options: HandleMcpToolCallOptions = {},
headers?: Record<string, string>,
) {
if (
Object.prototype.hasOwnProperty.call(args, 'apiKey')
|| Object.prototype.hasOwnProperty.call(args, 'byokProvider')
|| containsMcpCredentialField(args.inputs)
) {
throw new Error(
'raw API keys are not accepted by Open Design MCP. Configure Local BYOK in the Open Design UI and start that run from the local product instead.',
);
}
const { id, resolved, active } = await resolveProjectArg(baseUrl, args.project, headers);
if (args.requestId !== undefined) requireString(args.requestId, 'requestId');
if (
options.pluginAttribution
&& (typeof args.requestId !== 'string' || args.requestId.length === 0)
) {
throw pluginContractError(
'requestId is required for attributed start_run calls so a lost response can be retried without starting a second logical run',
);
}
const requestId =
typeof args.requestId === 'string' && args.requestId.length > 0
? args.requestId
: randomUUID();
const body: JsonObject = { projectId: id, clientRequestId: requestId };View on GitHub (pinned to 5be4028344)
Solutions
- Remove apiKey, byokProvider, and any credential-shaped fields from the MCP args.
- Configure the provider key via Local BYOK in the Open Design UI, then start the run from the local product.
- Move secrets into headers managed by the daemon, not into MCP inputs.
Example fix
// before
start_run({ apiKey: 'sk-...', inputs: { authorization: 'Bearer ...', prompt: 'hi' } })
// after
// set the key in Open Design UI -> Local BYOK, then:
start_run({ project: 'my-project', inputs: { prompt: 'hi' } }) Defensive patterns
Strategy: validation
Validate before calling
const CRED = /^(?:api[_-]?key|authorization|access[_-]?token|refresh[_-]?token|secret|password)$/iu;
function leaks(v: unknown, d = 0): boolean {
if (d > 20) return true;
if (!v || typeof v !== 'object') return false;
if (Array.isArray(v)) return v.some((x) => leaks(x, d + 1));
return Object.keys(v).some((k) => CRED.test(k)) || Object.values(v).some((x) => leaks(x, d + 1));
}
if ('apiKey' in args || 'byokProvider' in args || leaks(args.inputs)) {
throw new Error('strip credential fields; configure Local BYOK in the Open Design UI instead');
} Prevention
- Never pass provider keys through MCP args.
- Route all credentials through Local BYOK configured in the UI.
- Scan inputs for credential-shaped keys before submit.
When it happens
Trigger: Passing apiKey in the MCP call; setting byokProvider; embedding a token in inputs.headers.authorization or inputs.api_key; nested credential fields deeper in the inputs object.
Common situations: An agent porting a direct-provider script into MCP and passing its key; a user pasting a bearer token into inputs.
Related errors
- invalid brand id: ${input.brandId}
- invalid design system id: ${designSystemId}
- blocked non-public brand asset host: ${host}
- brand asset host resolves to a non-public address: ${host} -
- codex mcp add failed: ${failureDetail(result)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/1453883c367c3772.
Report an issue: GitHub.