revoke_api_key
Disable an API key by its ID to revoke access without permanently deleting it.
Instructions
Revoke an API key (disables it without deleting).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| key_id | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/devhelm_mcp/tools/api_keys.py:40-47 (handler)The `revoke_api_key` tool handler — decorated with @mcp.tool(), it calls the SDK's `api_keys.revoke(key_id)` and returns a success string or raises a ToolError on failure.
@mcp.tool() def revoke_api_key(key_id: str, api_token: str | None = None) -> str: """Revoke an API key (disables it without deleting).""" try: get_client(api_token).api_keys.revoke(key_id) return "API key revoked successfully." except DevhelmError as e: raise_tool_error(e) - src/devhelm_mcp/server.py:109-110 (registration)The `api_keys` module (containing `revoke_api_key`) is registered via `mod.register(mcp)` which calls the `register(mcp: FastMCP)` function in api_keys.py, decorating the function with `@mcp.tool()`.
for mod in ALL_TOOL_MODULES: mod.register(mcp) - src/devhelm_mcp/server.py:101-101 (registration)The `api_keys` module is listed in `ALL_TOOL_MODULES` ensuring its `register()` function gets called.
api_keys, - src/devhelm_mcp/client.py:107-136 (helper)The `get_client()` helper builds a Devhelm SDK client used by the handler to call `api_keys.revoke()`.
def get_client(api_token: str | None = None) -> Devhelm: """Build a Devhelm SDK client from the user's API token. Token resolution is delegated to :func:`resolve_api_token`, so callers can pass the value through from a tool argument *or* leave it ``None`` and let the helper pick the token up from the active HTTP request's ``Authorization: Bearer …`` header (hosted ``/mcp``) or from the ``DEVHELM_API_TOKEN`` env var (stdio). This is the single seam every tool goes through, so a missing / mistyped token surfaces in exactly one place. Overrides the SDK's default surface (``sdk-py``) with ``mcp`` so the API attributes traffic to the MCP server rather than to bare-SDK use. The SDK's ``X-DevHelm-Sdk-Name`` header is preserved, so the API can still see *which* SDK version this MCP server release is built on for debugging client-version skew. Detecting the host MCP client (Cursor vs Claude Desktop vs ...) is a follow-up: ``fastmcp.Context.session.client_params.clientInfo`` carries that info, but threading Context through every tool would be a wide surgery against the no-callsite-changes goal of this PR. The wire contract already supports ``X-DevHelm-Mcp-Client`` via ``surface_metadata`` so we can layer it in later without an API change. """ return Devhelm( token=resolve_api_token(api_token), base_url=API_BASE_URL, surface="mcp", surface_version=_server_version(), ) - src/devhelm_mcp/client.py:205-222 (helper)The `raise_tool_error()` helper converts SDK errors into FastMCP ToolError so the tool returns isError=true on failure.
def raise_tool_error(err: DevhelmError) -> NoReturn: """Convert an SDK error into a FastMCP ``ToolError`` so ``isError=true``. Per the MCP spec, upstream API failures must surface as ``CallToolResult.isError = true`` so the LLM can distinguish a tool that *ran but failed* from one that *succeeded with an error message in the response* — those have the same shape on the wire otherwise. The previous behavior returned ``format_error(err)`` as a regular tool return value (``isError = false``), which caused agents to confidently report success after a 4xx/5xx (silent-corruption bug from the round-3 DevEx audit). FastMCP catches the ``ToolError`` raised here and serializes it into ``CallToolResult(isError=True, content=[...])``, preserving the human-readable formatted message for the LLM. See https://modelcontextprotocol.io/specification/server/tools#error-handling. """ raise ToolError(format_error(err)) from err