Skip to content

Add MCP API key management tool#109

Merged
IlyaasK merged 10 commits into
mainfrom
codex/api-keys-mcp-tool
Jun 2, 2026
Merged

Add MCP API key management tool#109
IlyaasK merged 10 commits into
mainfrom
codex/api-keys-mcp-tool

Conversation

@IlyaasK
Copy link
Copy Markdown
Contributor

@IlyaasK IlyaasK commented May 29, 2026

Summary

  • add manage_api_keys MCP tool for create/list/get/update/delete
  • support project-scoped API-key creation via project_id and optional days_to_expire
  • wire the API-key capability into MCP registration after projects
  • centralize MCP text, JSON, paginated JSON, and error responses in src/lib/mcp/responses.ts
  • share project/API-key list pagination schema in src/lib/mcp/schemas.ts, matching the API's backend-clamped pagination behavior
  • mark project/API-key validation and SDK failures as MCP error results with isError: true

Agent Experience / Flow

This PR lets agents provision credentials for automation without leaving MCP. The important agent experience detail is that API-key creation returns the plaintext key once, while every later read path returns masked metadata only.

Typical project-scoped flow:

  1. Agent resolves the resource boundary with manage_projects list or manage_projects create and captures project_id.
  2. Agent calls manage_api_keys create name=<purposeful-name> project_id=<project_id> days_to_expire=<n> when a downstream workflow needs a project-scoped key.
  3. Agent immediately hands the plaintext key to the caller or stores it only in the explicitly requested destination, because it cannot be retrieved again.
  4. Agent verifies key metadata with manage_api_keys get or list; these return masked values suitable for audit and UI display.
  5. Agent uses manage_api_keys update for name changes and delete for revocation when the workflow is complete.

Typical org-wide flow:

  1. Agent calls manage_api_keys create name=<purposeful-name> without project_id only when org-wide access is explicitly intended.
  2. Agent uses days_to_expire for temporary automation keys so cleanup does not depend only on manual deletion.
  3. Agent lists existing keys before creating a new one to avoid duplicate long-lived credentials.

Agent safety notes:

  • Treat the create response as secret material. Do not include plaintext API keys in PR comments, logs, or routine summaries.
  • Prefer project-scoped keys when the workflow has a known project boundary.
  • Prefer short expirations for smoke tests and one-off automation.
  • Invalid tool inputs and caught SDK/API failures now return MCP error results instead of successful plain text, so agents can recover from failures reliably.
  • manage_projects and manage_api_keys now share the same response helpers, pagination envelope, pagination input schema, and failure semantics across the project-to-key workflow.

Authorization Boundary

MCP forwards the caller's bearer token to the Kernel SDK/API and does not implement a separate API-key authorization policy. The API must remain the source of truth for who may create org-wide keys, create project-scoped keys, read key metadata, update names, or revoke keys.

API-side authorization audit/enforcement is tracked separately in kernel/kernel: https://github.com/kernel/kernel/issues/2297

Validation

  • bunx prettier --check src/lib/mcp/schemas.ts src/lib/mcp/responses.ts src/lib/mcp/tools/api-keys.ts src/lib/mcp/tools/projects.ts
  • git diff --check
  • KERNEL_CLI_PROD_CLIENT_ID=dummy KERNEL_CLI_STAGING_CLIENT_ID=dummy KERNEL_CLI_DEV_CLIENT_ID=dummy NEXT_PUBLIC_CLERK_DOMAIN=example.clerk.accounts.dev NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk CLERK_SECRET_KEY=sk_test_dummy REDIS_URL=redis://localhost:6379 bun run build
  • localhost MCP CRUD smoke against http://127.0.0.1:3002/mcp with API_BASE_URL=http://127.0.0.1:3001: initialized MCP, verified manage_api_keys was listed, created a scratch project, created a project-scoped API key, confirmed plaintext key is returned on create only, got/updated/listed/deleted the key, then deleted the scratch project

Notes

  • bun run format:check still fails on pre-existing AGENTS.md formatting outside this PR scope.
  • The first sandboxed bun run build attempt failed only because next/font could not fetch Google Fonts; the rerun with network access passed.

Note

High Risk
Introduces credential lifecycle (create with one-time plaintext, revoke) over MCP; misconfiguration or weak API-side auth would expose high-impact operations despite forwarding the caller's token.

Overview
Adds manage_api_keys so agents can create, list, get, update, and delete Kernel API keys via MCP, including optional project scoping and expiry on create. Create is the only path that returns the plaintext key; later reads stay masked.

MCP registration is refactored into a toolset registry driven by KERNEL_MCP_DISABLED_TOOLSETS, so self-hosted deployments can skip registering sensitive tools (e.g. api_keys / manage_api_keys). Invalid env values fail at startup with a clear error.

manage_projects and the new tool share responses.ts (JSON, paginated lists, isError: true on validation/SDK failures) and schemas.ts for list pagination hints aligned with API clamping. README and .env.example document the new tool count and gating option.

Reviewed by Cursor Bugbot for commit 8074fb9. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mcp Ready Ready Preview, Comment Jun 2, 2026 8:49pm

@IlyaasK IlyaasK marked this pull request as ready for review May 29, 2026 19:43
@firetiger-agent
Copy link
Copy Markdown

Monitoring Plan: API key management via MCP tool

What this PR does: Lets AI agents create, list, rename, and delete Kernel API keys directly through the MCP protocol, without leaving their context.

Intended effect:

  • POST /org/api_keys success rate: baseline 100% (9 of 9 creates succeeded over 7 days); confirmed if new MCP-originated creates return HTTP 201 with zero 5xx.
  • GET /org/api_keys availability: baseline 0 server errors (24 requests, 0 errors over 7 days); confirmed if list/get calls return 200 without error.

Risks:

  • Auth token not forwarded - GET /org/api_keys 401 rate, alert if > 20% of calls return 401/403 over any 30-minute window post-deploy.
  • Create/delete 500s - /org/api_keys POST or /org/api_keys/{id} DELETE 5xx count, alert if any HTTP 500 appears (baseline: 0 over 7 days).
  • MCP server startup failure - kernel-mcp-server pod health, alert if crash loop or health check failure occurs within 5 minutes of deploy.

Status updates will be posted automatically on this PR as monitoring progresses.

View monitor

@IlyaasK IlyaasK requested a review from masnwilliams June 1, 2026 19:07
Base automatically changed from codex/projects-mcp-tool to main June 2, 2026 15:37
rgarcia
rgarcia previously approved these changes Jun 2, 2026
Copy link
Copy Markdown
Contributor

@rgarcia rgarcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Verified the tool against @onkernel/sdk@0.58.0:

  • All five calls match the SDK surface (create/list/retrieve/update/delete, param shapes, and the offset-pagination accessors getPaginatedItems/has_more/next_offset).
  • Masking holds at the type level: read paths return APIKey (masked_key only); only create returns CreatedAPIKey with the plaintext key.
  • Consistent with the established manage_* action-multiplexed pattern (mirrors manage_projects closely).

Non-blocking notes (most inherited from manage_projects, so not regressions — worth addressing across the family rather than blocking here):

  1. Error results (both validation guards and the catch) return plain text content without isError: true, so the protocol-level result reads as success. Pre-existing across these tools.
  2. limit/offset use z.number() rather than .int() (days_to_expire correctly uses .int().min(1).max(3650)).
  3. Security surface is already noted in the description: any valid MCP token can mint org-wide keys and delete keys (API-authorization concern, not fixable in the tool layer), and the plaintext key lands in model context/transcript — the "treat as secret" guidance is prompt-level, not enforced.

If schema-level guarantees are ever wanted over prose-level ((create, update) …) descriptions, a discriminated union via registerTool({ inputSchema }) is the upgrade path, but I wouldn't churn the whole repo for it.

Mark API-key tool validation and caught SDK failures as MCP error results so callers do not see failed operations as successful text responses.

Require integer pagination inputs at the MCP schema boundary so agents get immediate validation feedback before invoking the SDK.
Move text, JSON, paginated, and error tool responses into one MCP response helper so projects and API keys do not drift in protocol semantics.

Use the shared helper in manage_projects and manage_api_keys, including isError results for validation and caught SDK failures.
Use one shared MCP pagination schema for projects and API keys so both tools match the API's forgiving pagination contract.

API keys now rely on the backend clamp behavior instead of rejecting values that projects pass through.
Comment thread src/lib/mcp/schemas.ts
Copy link
Copy Markdown
Collaborator

@masnwilliams masnwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed — code is clean and consistent with the manage_* pattern, typechecks, SDK calls verified. zod validation on days_to_expire (.int().min(1).max(3650)) is a nice touch. import + registration correct.

the substantive discussion here is agent-fit, not code quality.

agent-fit (worth a product decision)

  • create (api-keys.ts:62) mints a long-lived credential and returns the plaintext key once — which the agent then echoes into the MCP response / chat transcript. a key that outlives the conversation landing in logs/history is a real secret-hygiene concern.
  • delete (api-keys.ts:148) revokes a key — an agent revoking a key in active use can break a running integration.

read side is well-guarded (list/get only return masked_key). for reference, how established servers handle this:

  • Stripe exposes destructive writes (refunds) but relies on server-side scope enforcement — a read-only restricted key physically can't mutate.
  • GitHub keeps secrets write-only (values never readable) and routes the token via env var, not a tool arg; also supports --read-only and --toolsets gating.

suggestions: (1) add MCP annotations (destructiveHint on delete, readOnlyHint on list/get); (2) confirm a read-only Kernel API key rejects apiKeys.create/delete server-side so the guarantee doesn't depend on the model; (3) consider toolset gating so a deployment can opt out of key management entirely.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f838f6e. Configure here.

Comment thread src/lib/mcp/register.ts
@IlyaasK IlyaasK requested a review from masnwilliams June 2, 2026 20:13
Copy link
Copy Markdown
Collaborator

@masnwilliams masnwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow-up review — the annotations + gating additions address the agent-fit feedback well. nice work, and the shared paginationParams / paginatedJsonResponse / toolErrorResponse cleanup is a bonus. typechecks clean. a few notes, none blocking on this PR's own code:

coordinate before merge

  • collides with #112. both PRs independently add src/lib/mcp/responses.ts (not on main) and refactor register.ts. whichever lands second will hard-conflict. worth deciding merge order now and rebasing the other — ideally one of them owns responses.ts and the toolset-registration refactor, and the other builds on it.

suggestions

  • openWorldHint: true on manage_api_keys is debatable — that hint signals interaction with an external open world (web search, third-party sites). key management hits Kernel's own API, so false reads more accurately. the hint genuinely fits something like browser_curl / manage_auth_connections.
  • gating is a denylist (KERNEL_MCP_DISABLED_TOOLSETS), so new/sensitive tools are exposed by default. GitHub's --toolsets is an allowlist (default-deny), which is safer for credential-type tools. denylist is a legit choice for backwards-compat — just flagging it as a conscious call, especially since #104 (managed-auth) will ride on this same mechanism.
  • destructiveHint: true on a multiplexed manage_* tool means clients will prompt for confirmation even on list/get. acceptable conservative tradeoff given one tool spans read + write; only fixable by splitting reads into their own tool, which probably isn't worth it.

the gating mechanism itself is clean — type-safe registration table, alias resolution, all/none, hard error on unknown values. good to go on my end once the #112 overlap is sorted.

Copy link
Copy Markdown
Collaborator

@masnwilliams masnwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving — annotations + the env-driven toolset gating fully address the agent-fit feedback, and the openWorldHint correction lands it accurately. clean and typechecks. one non-code item to settle outside this PR: coordinate merge order with #112 since both add responses.ts and refactor register.ts (whichever merges second will need a rebase).

@IlyaasK IlyaasK merged commit 9b4df41 into main Jun 2, 2026
10 checks passed
@IlyaasK IlyaasK deleted the codex/api-keys-mcp-tool branch June 2, 2026 21:29
@firetiger-agent
Copy link
Copy Markdown

No issues detected at 1h post-deploy across Production.

Signals (all healthy):

  • /org/api_keys endpoint 5xx rate: 0 (baseline: 0)
  • Auth errors (401/403): 0 (no regression)
  • Overall API error rate: 0.30% at 21:00Z, within baseline 0.45–2.77% — 22:00Z spike (5.7%) is pre-existing Unikraft infra noise (244k+ "cannot idle TTL" + 6.6k+ "failed to provision" errors), matching Jun 1 22:00Z (3.8%) and other historical spikes
  • MCP tool registration: No startup errors or crash logs

Risks watched (all clean): Auth forwarding, key exposure in errors, 500s on create/delete, MCP server stability

No /org/api_keys traffic observed yet post-deploy — expected given ~10 req/day baseline and early window. Next check will confirm intended effect (first 201/200 from MCP-authenticated sessions).

View monitor

@firetiger-agent
Copy link
Copy Markdown

Intended effect confirmed at ~24h post-deploy in Production.

Signals:

  • /org/api_keys POST 201 (create): 14 successful calls
  • /org/api_keys 5xx rate: 0 (baseline: 0)
  • Auth error rate: 1/14 (7%, within baseline 21%)
  • Overall API error rate: oscillating 0.1–5% (identical pre-deploy pattern, no step-change)

Risks watched (all clean): Auth forwarding, key exposure in error logs, 500s on create/delete, MCP server stability.

The manage_api_keys MCP tool is routing API key creates to the backend correctly. No issues detected. Monitoring continues through 72h window (closes ~Jun 5 21:30Z).

View monitor

@firetiger-agent
Copy link
Copy Markdown

Verdict: no_issue after 72h (3.003 days) across Production.

Final evidence:

  • POST /org/api_keys: 41 successful creates (201) post-deploy vs. 11 pre-deploy (3.7x increase confirms MCP tool active use)
  • All API key CRUD endpoints: 56 total calls, 0 server errors (5xx)
  • Auth error rate: 12.5% (1 of 8 GET requests), well below 20% alert threshold and pre-deploy baseline
  • Overall API error rate: oscillating 0.1–11.5% throughout window, identical to documented pre-existing Unikraft infra pattern
  • MCP tool registration: No startup failures or crash indicators
  • Error logging: No manage_api_keys ERROR logs, no key exposure detected

The manage_api_keys MCP tool deployed cleanly and is operating normally in production. All API key CRUD operations succeed at healthy rates with zero server errors. Safe to close monitoring.

View monitor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants