From 7df571bcdf5ac5ac73658de460aa7f921df01ad5 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Sun, 20 Sep 2026 20:47:34 -0700 Subject: [PATCH 1/5] docs(mcp): fix 5 audit findings in World MCP server docs - model-context-protocol/world-docs.mdx: list the two undocumented tools (query_docs_filesystem_world_documentation, submit_feedback) in the Available tools table and call out submit_feedback as the server's one non-read-only tool - model-context-protocol/developer-portal.mdx: warn that rotate_world_id_signing_key's private key passes through the assistant's session/transcript, unlike the one-time portal UI dialog - model-context-protocol/index.mdx: correct the Client support framing so Codex isn't implied to be stdio-only (it has native streamable-HTTP support via `codex mcp add --url`) - model-context-protocol/developer-portal.mdx: document the JSON-RPC auth error shape (code -32001) and that missing vs. invalid keys are not distinguishable from the error alone - model-context-protocol/developer-portal.mdx: document retry/idempotency semantics for rotate_world_id_signing_key and submit_app_for_review, recommending state re-reads instead of trusting the call's own result --- model-context-protocol/developer-portal.mdx | 20 ++++++++++++++++++++ model-context-protocol/index.mdx | 2 +- model-context-protocol/world-docs.mdx | 6 +++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/model-context-protocol/developer-portal.mdx b/model-context-protocol/developer-portal.mdx index 962f6f2..5a027f7 100644 --- a/model-context-protocol/developer-portal.mdx +++ b/model-context-protocol/developer-portal.mdx @@ -26,6 +26,16 @@ https://developer.world.org/api/mcp Developer portal API keys start with `api_`. +## Authentication errors + +Requests with a missing, malformed, or invalid API key all return the same JSON-RPC error: + +```json +{"jsonrpc": "2.0", "id": 1, "error": {"code": -32001, "message": "API key is required."}} +``` + +This message is returned even when a well-formed `api_...` key is present but not valid, so a missing key and a rejected key are not distinguishable from the error alone. If you see this error with a key you believe is correct, re-copy it from the Developer Portal rather than assuming the failure has another cause. The endpoint only accepts `POST`; a `GET` request returns HTTP 405 with `allow: POST, OPTIONS`. + ## Connect your client Replace `api_...` with the API key you copied from the Developer Portal. @@ -139,9 +149,19 @@ upload_app_image { app_id, image_type: "content_card", image_base64 | source_url upload_app_image { app_id, image_type: "showcase_1", image_base64 | source_url } ``` +## Timeouts and retries + +`rotate_world_id_signing_key` and `submit_app_for_review` are not guaranteed idempotent. If the underlying call times out or the connection drops after the server has already processed the request, retrying it can repeat the mutation rather than simply resurface the original result: + +- Retrying `rotate_world_id_signing_key` after an unclear result generates another new signing key; it does not replay the previous one, and the key from the interrupted call becomes unrecoverable. Before retrying, call `get_world_id_signing_key` to check whether the signer address already changed. +- Retrying `submit_app_for_review` after an unclear result may resubmit the app for review. Before retrying, call `get_app_config` to check the app's current review status. + +Have the assistant confirm the outcome by re-reading state (`get_world_id_signing_key`, `get_world_id_registration_status`, or `get_app_config`) rather than trusting the original call's own success or failure signal alone. + ## Security notes - Store generated World ID private keys immediately. They are returned once and are not recoverable from the portal. +- `rotate_world_id_signing_key` returns the private key as an MCP tool call result, not a one-time on-screen dialog. That result passes through the AI assistant's model context and, depending on the client, may be persisted to disk in session transcripts or debug logs. Treat the returned key as sensitive within that session, and rotate again immediately if it was ever generated through an MCP client whose transcripts or logs aren't under your team's control. - Use a separate API key per local agent or project when possible. - Delete or rotate API keys that are no longer needed. - Confirm destructive actions before asking the assistant to rotate a signer key or submit an app for review. diff --git a/model-context-protocol/index.mdx b/model-context-protocol/index.mdx index 887360e..1fb6b75 100644 --- a/model-context-protocol/index.mdx +++ b/model-context-protocol/index.mdx @@ -36,7 +36,7 @@ World provides two MCP servers: ## Client support -Both MCP servers use streamable HTTP. Most modern MCP clients can connect directly to an HTTP MCP server. Clients that only support stdio can connect through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote). +Both MCP servers use streamable HTTP. Most modern MCP clients, including Codex CLI (`codex mcp add --url `), can connect directly to an HTTP MCP server. Clients that only support stdio can connect through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote). Keep developer portal API keys scoped to trusted local MCP clients. The Developer Portal MCP can mutate apps in your team. diff --git a/model-context-protocol/world-docs.mdx b/model-context-protocol/world-docs.mdx index d3c4817..a357e94 100644 --- a/model-context-protocol/world-docs.mdx +++ b/model-context-protocol/world-docs.mdx @@ -65,6 +65,10 @@ The docs MCP does not require authentication. | Tool | Purpose | | --- | --- | | `search_world_documentation` | Search and retrieve relevant World documentation for the current task. | +| `query_docs_filesystem_world_documentation` | Run shell-like queries (`rg`, `grep`, `find`, `tree`, `ls`, `cat`, `head`, `tail`, `stat`, `wc`, `sort`, `uniq`, `cut`, `sed`, `awk`, `jq`) over the full docs corpus, including OpenAPI specs. Use it to read a full page or spec rather than a search snippet. | +| `submit_feedback` | Submit feedback about the documentation to the World docs team. | + +Apart from `submit_feedback`, every tool on this server is read-only. `submit_feedback` is the one tool that writes data (it files feedback to the docs team) rather than only reading it. ## Suggested prompts @@ -78,4 +82,4 @@ Search the World docs for World ID verification and explain which endpoint this ## When to use it -Use the docs MCP for read-only documentation lookup. To create or configure apps in the developer portal, use the [Developer Portal MCP](/model-context-protocol/developer-portal). +Use the docs MCP for documentation lookup and filesystem-style queries over the docs corpus. Apart from `submit_feedback`, it is read-only. To create or configure apps in the developer portal, use the [Developer Portal MCP](/model-context-protocol/developer-portal). From 1d74193bfc21cf66fe0efbda5db7f3f80e0888aa Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 10:21:10 -0700 Subject: [PATCH 2/5] polish: tighten wording, match house style Trims the verbose prose the earlier audit pass added in model-context-protocol/{developer-portal,world-docs}.mdx down to this repo's terse, declarative style, and reconciles the Codex "Connect your client" examples with index.mdx's Client support claim: current Codex CLI (0.149.1) supports native streamable-HTTP MCP servers via `--url`/`--bearer-token-env-var`, so both Codex tabs now use that instead of the mcp-remote stdio bridge (verified against `codex mcp add --help` and a live `codex mcp add`/`get` round-trip). --- model-context-protocol/developer-portal.mdx | 21 ++++++++++----------- model-context-protocol/world-docs.mdx | 8 +++----- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/model-context-protocol/developer-portal.mdx b/model-context-protocol/developer-portal.mdx index 5a027f7..c18abcc 100644 --- a/model-context-protocol/developer-portal.mdx +++ b/model-context-protocol/developer-portal.mdx @@ -28,13 +28,13 @@ Developer portal API keys start with `api_`. ## Authentication errors -Requests with a missing, malformed, or invalid API key all return the same JSON-RPC error: +A missing, malformed, or invalid API key returns the same JSON-RPC error — even a well-formed `api_...` key that just isn't valid, so a missing key and a rejected key look identical: ```json {"jsonrpc": "2.0", "id": 1, "error": {"code": -32001, "message": "API key is required."}} ``` -This message is returned even when a well-formed `api_...` key is present but not valid, so a missing key and a rejected key are not distinguishable from the error alone. If you see this error with a key you believe is correct, re-copy it from the Developer Portal rather than assuming the failure has another cause. The endpoint only accepts `POST`; a `GET` request returns HTTP 405 with `allow: POST, OPTIONS`. +If you see this with a key you believe is correct, re-copy it from the Developer Portal. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. ## Connect your client @@ -52,11 +52,10 @@ Replace `api_...` with the API key you copied from the Developer Portal. ```bash + export WORLD_DEVELOPER_API_KEY=api_... codex mcp add world-developer-portal \ - --env WORLD_DEVELOPER_API_KEY=api_... \ - -- npx -y mcp-remote https://developer.world.org/api/mcp \ - --transport http-only \ - --header 'Authorization:Bearer ${WORLD_DEVELOPER_API_KEY}' + --url https://developer.world.org/api/mcp \ + --bearer-token-env-var WORLD_DEVELOPER_API_KEY ``` @@ -151,17 +150,17 @@ upload_app_image { app_id, image_type: "showcase_1", image_base64 | source_url } ## Timeouts and retries -`rotate_world_id_signing_key` and `submit_app_for_review` are not guaranteed idempotent. If the underlying call times out or the connection drops after the server has already processed the request, retrying it can repeat the mutation rather than simply resurface the original result: +`rotate_world_id_signing_key` and `submit_app_for_review` aren't idempotent. Retrying after a timeout or dropped connection can repeat the mutation instead of just resurfacing the original result: -- Retrying `rotate_world_id_signing_key` after an unclear result generates another new signing key; it does not replay the previous one, and the key from the interrupted call becomes unrecoverable. Before retrying, call `get_world_id_signing_key` to check whether the signer address already changed. -- Retrying `submit_app_for_review` after an unclear result may resubmit the app for review. Before retrying, call `get_app_config` to check the app's current review status. +- `rotate_world_id_signing_key`: a retry generates a new key rather than replaying the old one, and the interrupted call's key becomes unrecoverable. Check `get_world_id_signing_key` first for the current signer address. +- `submit_app_for_review`: a retry can resubmit the app. Check `get_app_config` first for its review status. -Have the assistant confirm the outcome by re-reading state (`get_world_id_signing_key`, `get_world_id_registration_status`, or `get_app_config`) rather than trusting the original call's own success or failure signal alone. +Confirm the outcome by re-reading state (`get_world_id_signing_key`, `get_world_id_registration_status`, `get_app_config`) rather than trusting the original call's success or failure signal. ## Security notes - Store generated World ID private keys immediately. They are returned once and are not recoverable from the portal. -- `rotate_world_id_signing_key` returns the private key as an MCP tool call result, not a one-time on-screen dialog. That result passes through the AI assistant's model context and, depending on the client, may be persisted to disk in session transcripts or debug logs. Treat the returned key as sensitive within that session, and rotate again immediately if it was ever generated through an MCP client whose transcripts or logs aren't under your team's control. +- `rotate_world_id_signing_key` returns the private key as a tool call result, not a one-time dialog — it passes through the assistant's model context and, depending on the client, may be persisted in session transcripts or debug logs. Treat it as sensitive, and rotate again if it was generated through a client whose logs aren't under your team's control. - Use a separate API key per local agent or project when possible. - Delete or rotate API keys that are no longer needed. - Confirm destructive actions before asking the assistant to rotate a signer key or submit an app for review. diff --git a/model-context-protocol/world-docs.mdx b/model-context-protocol/world-docs.mdx index a357e94..902aa74 100644 --- a/model-context-protocol/world-docs.mdx +++ b/model-context-protocol/world-docs.mdx @@ -28,7 +28,7 @@ The docs MCP does not require authentication. ```bash - codex mcp add world-docs -- npx -y mcp-remote https://docs.world.org/mcp --transport http-only + codex mcp add world-docs --url https://docs.world.org/mcp ``` @@ -65,11 +65,9 @@ The docs MCP does not require authentication. | Tool | Purpose | | --- | --- | | `search_world_documentation` | Search and retrieve relevant World documentation for the current task. | -| `query_docs_filesystem_world_documentation` | Run shell-like queries (`rg`, `grep`, `find`, `tree`, `ls`, `cat`, `head`, `tail`, `stat`, `wc`, `sort`, `uniq`, `cut`, `sed`, `awk`, `jq`) over the full docs corpus, including OpenAPI specs. Use it to read a full page or spec rather than a search snippet. | +| `query_docs_filesystem_world_documentation` | Run shell-like queries (`rg`, `grep`, `find`, `tree`, `ls`, `cat`, `head`, `tail`, `stat`, `wc`, `sort`, `uniq`, `cut`, `sed`, `awk`, `jq`) over the full docs corpus, including OpenAPI specs, to read a full page or spec instead of a search snippet. | | `submit_feedback` | Submit feedback about the documentation to the World docs team. | -Apart from `submit_feedback`, every tool on this server is read-only. `submit_feedback` is the one tool that writes data (it files feedback to the docs team) rather than only reading it. - ## Suggested prompts ```text @@ -82,4 +80,4 @@ Search the World docs for World ID verification and explain which endpoint this ## When to use it -Use the docs MCP for documentation lookup and filesystem-style queries over the docs corpus. Apart from `submit_feedback`, it is read-only. To create or configure apps in the developer portal, use the [Developer Portal MCP](/model-context-protocol/developer-portal). +Use the docs MCP for documentation lookup and filesystem-style queries over the docs corpus (read-only except for `submit_feedback`). To create or configure apps in the developer portal, use the [Developer Portal MCP](/model-context-protocol/developer-portal). From 75a1fa683b6e3b5c4ad2b4795e2ca9df6d7cccbd Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 16:54:08 -0700 Subject: [PATCH 3/5] fix: address 2 Codex review findings on PR #192 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Authentication errors: the troubleshooting step told users to "re-copy" a lost/miscopied API key from the Developer Portal, but the doc's own Setup section says keys are shown only once and can't be re-displayed. Point to secure storage instead, with generating a new key as the real fallback. - Security notes (P1): the advice to "rotate again if generated through an untrusted client" didn't actually fix anything if you rotate through that same untrusted client — the replacement key leaks the identical way. Now requires switching to a trusted, log-controlled channel before rotating. --- model-context-protocol/developer-portal.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model-context-protocol/developer-portal.mdx b/model-context-protocol/developer-portal.mdx index c18abcc..9339960 100644 --- a/model-context-protocol/developer-portal.mdx +++ b/model-context-protocol/developer-portal.mdx @@ -34,7 +34,7 @@ A missing, malformed, or invalid API key returns the same JSON-RPC error — eve {"jsonrpc": "2.0", "id": 1, "error": {"code": -32001, "message": "API key is required."}} ``` -If you see this with a key you believe is correct, re-copy it from the Developer Portal. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. +If you see this with a key you believe is correct, check the secure storage where you saved it at creation (it's shown only once and the portal can't display it again) — if it's genuinely lost, generate a new key and update every client using the old one. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. ## Connect your client @@ -160,7 +160,7 @@ Confirm the outcome by re-reading state (`get_world_id_signing_key`, `get_world_ ## Security notes - Store generated World ID private keys immediately. They are returned once and are not recoverable from the portal. -- `rotate_world_id_signing_key` returns the private key as a tool call result, not a one-time dialog — it passes through the assistant's model context and, depending on the client, may be persisted in session transcripts or debug logs. Treat it as sensitive, and rotate again if it was generated through a client whose logs aren't under your team's control. +- `rotate_world_id_signing_key` returns the private key as a tool call result, not a one-time dialog — it passes through the assistant's model context and, depending on the client, may be persisted in session transcripts or debug logs. Treat it as sensitive. If it was generated through a client whose logs aren't under your team's control, don't just rotate again from that same client — the replacement key would leak through the identical path. Switch to a trusted, log-controlled client or channel first, then rotate. - Use a separate API key per local agent or project when possible. - Delete or rotate API keys that are no longer needed. - Confirm destructive actions before asking the assistant to rotate a signer key or submit an app for review. From 8b020840a6ca32fe0994036c1817f6186c12ad9f Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Tue, 22 Sep 2026 18:47:19 -0700 Subject: [PATCH 4/5] docs(mcp): distinguish missing and rejected API keys --- model-context-protocol/developer-portal.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model-context-protocol/developer-portal.mdx b/model-context-protocol/developer-portal.mdx index 9339960..54cbb1a 100644 --- a/model-context-protocol/developer-portal.mdx +++ b/model-context-protocol/developer-portal.mdx @@ -28,13 +28,13 @@ Developer portal API keys start with `api_`. ## Authentication errors -A missing, malformed, or invalid API key returns the same JSON-RPC error — even a well-formed `api_...` key that just isn't valid, so a missing key and a rejected key look identical: +Authentication failures use JSON-RPC error code `-32001`. The message distinguishes missing or malformed credentials from a key that was parsed but rejected: ```json {"jsonrpc": "2.0", "id": 1, "error": {"code": -32001, "message": "API key is required."}} ``` -If you see this with a key you believe is correct, check the secure storage where you saved it at creation (it's shown only once and the portal can't display it again) — if it's genuinely lost, generate a new key and update every client using the old one. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. +For a parsed but rejected key, the message is `API key is not valid.`. Check that the client sends `Authorization: Bearer ` and copies the complete key from secure storage. Keys are shown only once; if yours is lost, generate a new key and update every client using the old one. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. ## Connect your client From d3eb366dff9020302416c032422175691e79920f Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Wed, 23 Sep 2026 15:02:23 -0700 Subject: [PATCH 5/5] docs(mcp): correct retry and security guidance - Timeouts: a submit_app_for_review retry fails with "Only unverified apps can be submitted."; a rotate_world_id_signing_key retry returns rotation_in_progress until the RP is registered again; cover configure_world_id; recovery is to wait, then rotate from a trusted channel - Security: cover configure_world_id, name the Developer Portal dashboard as the trusted rotation channel, keep keys out of committed files - Auth errors: HTTP 200 with -32001, and every method including initialize needs the key; fix a double period - Connect: Claude Code uses local scope, Cursor reads ${env:...}, VS Code prompts through a password input, and the Codex env var is set persistently instead of with a one-off export --- model-context-protocol/developer-portal.mdx | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/model-context-protocol/developer-portal.mdx b/model-context-protocol/developer-portal.mdx index 54cbb1a..4ad2e8c 100644 --- a/model-context-protocol/developer-portal.mdx +++ b/model-context-protocol/developer-portal.mdx @@ -28,17 +28,17 @@ Developer portal API keys start with `api_`. ## Authentication errors -Authentication failures use JSON-RPC error code `-32001`. The message distinguishes missing or malformed credentials from a key that was parsed but rejected: +Every method, including `initialize`, requires the API key. Authentication failures return HTTP 200 with JSON-RPC error code `-32001`, not an HTTP 401. The message distinguishes missing or malformed credentials from a key that was parsed but rejected: ```json {"jsonrpc": "2.0", "id": 1, "error": {"code": -32001, "message": "API key is required."}} ``` -For a parsed but rejected key, the message is `API key is not valid.`. Check that the client sends `Authorization: Bearer ` and copies the complete key from secure storage. Keys are shown only once; if yours is lost, generate a new key and update every client using the old one. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. +For a parsed but rejected key, the message is `API key is not valid.` Check that the client sends `Authorization: Bearer ` and copies the complete key from secure storage. Keys are shown only once; if yours is lost, generate a new key and update every client using the old one. The endpoint only accepts `POST`; `GET` returns HTTP 405 with `allow: POST, OPTIONS`. ## Connect your client -Replace `api_...` with the API key you copied from the Developer Portal. +Replace `api_...` with the API key you copied from the Developer Portal. Codex and Cursor read the key from the `WORLD_DEVELOPER_API_KEY` environment variable at startup, so set it persistently (for example, in your shell profile, loaded from a secrets manager) rather than with a one-off `export`. VS Code prompts for the key once and stores it securely. @@ -46,13 +46,12 @@ Replace `api_...` with the API key you copied from the Developer Portal. claude mcp add world-developer-portal \ https://developer.world.org/api/mcp \ --transport http \ - --scope project \ + --scope local \ --header "Authorization: Bearer api_..." ``` ```bash - export WORLD_DEVELOPER_API_KEY=api_... codex mcp add world-developer-portal \ --url https://developer.world.org/api/mcp \ --bearer-token-env-var WORLD_DEVELOPER_API_KEY @@ -67,7 +66,7 @@ Replace `api_...` with the API key you copied from the Developer Portal. "world-developer-portal": { "url": "https://developer.world.org/api/mcp", "headers": { - "Authorization": "Bearer api_..." + "Authorization": "Bearer ${env:WORLD_DEVELOPER_API_KEY}" } } } @@ -79,12 +78,20 @@ Replace `api_...` with the API key you copied from the Developer Portal. ```json { + "inputs": [ + { + "type": "promptString", + "id": "world-developer-api-key", + "description": "World Developer Portal API key", + "password": true + } + ], "servers": { "world-developer-portal": { "type": "http", "url": "https://developer.world.org/api/mcp", "headers": { - "Authorization": "Bearer api_..." + "Authorization": "Bearer ${input:world-developer-api-key}" } } } @@ -150,17 +157,16 @@ upload_app_image { app_id, image_type: "showcase_1", image_base64 | source_url } ## Timeouts and retries -`rotate_world_id_signing_key` and `submit_app_for_review` aren't idempotent. Retrying after a timeout or dropped connection can repeat the mutation instead of just resurfacing the original result: - -- `rotate_world_id_signing_key`: a retry generates a new key rather than replaying the old one, and the interrupted call's key becomes unrecoverable. Check `get_world_id_signing_key` first for the current signer address. -- `submit_app_for_review`: a retry can resubmit the app. Check `get_app_config` first for its review status. +A timeout or dropped connection doesn't tell you whether a call went through, and these tools don't replay the original result. Re-read state (`get_world_id_signing_key`, `get_world_id_registration_status`, `get_app_config`) before you retry: -Confirm the outcome by re-reading state (`get_world_id_signing_key`, `get_world_id_registration_status`, `get_app_config`) rather than trusting the original call's success or failure signal. +- `configure_world_id` and `rotate_world_id_signing_key`: the private key is only in the original response. If that response is lost, the key is unrecoverable, but the portal still applies its signer address. Retrying `configure_world_id` returns the existing registration with `signing_key: null`. Retrying `rotate_world_id_signing_key` fails with `-32004` (`rotation_in_progress`) until the registration status is `registered` again. If `get_world_id_signing_key` shows a signer address you don't hold, wait until `get_world_id_registration_status` reports `registered`, then rotate again from a trusted channel (see [Security notes](#security-notes)). +- `submit_app_for_review`: if the first call went through, a retry fails with `-32004` `Only unverified apps can be submitted.` Check `get_app_config` for the review status instead. ## Security notes - Store generated World ID private keys immediately. They are returned once and are not recoverable from the portal. -- `rotate_world_id_signing_key` returns the private key as a tool call result, not a one-time dialog — it passes through the assistant's model context and, depending on the client, may be persisted in session transcripts or debug logs. Treat it as sensitive. If it was generated through a client whose logs aren't under your team's control, don't just rotate again from that same client — the replacement key would leak through the identical path. Switch to a trusted, log-controlled client or channel first, then rotate. +- `configure_world_id` and `rotate_world_id_signing_key` return the private key as a tool call result, not a one-time dialog — it passes through the assistant's model context and, depending on the client, may be persisted in session transcripts or debug logs. Treat it as sensitive. If it was generated through a client whose logs aren't under your team's control, don't just rotate again from that same client — the replacement key would leak through the identical path. Rotate from the Developer Portal dashboard instead: it generates the new key in your browser and sends only the signer address. +- Keep API keys out of files you commit. Claude Code's `--scope project` writes the header to `.mcp.json`, which is meant to be committed; the examples above use local scope, an environment variable, or an input prompt instead. - Use a separate API key per local agent or project when possible. - Delete or rotate API keys that are no longer needed. - Confirm destructive actions before asking the assistant to rotate a signer key or submit an app for review.