Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies = [
# `client.services` catalog resource and the extended
# `client.dependencies` (component-level track + alert sensitivity)
# that the services/dependencies tool modules call directly.
"devhelm>=1.3.0",
"devhelm>=1.4.0",
"fastmcp>=3.2.3,<4",
]

Expand Down
8 changes: 8 additions & 0 deletions src/devhelm_mcp/tools/api_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ def list_api_keys(api_token: str | None = None) -> ToolResult:
except DevhelmError as e:
raise_tool_error(e)

@mcp.tool()
def get_api_key(key_id: str, api_token: str | None = None) -> ToolResult:
"""Get a single API key's metadata by id. The secret value is never returned."""
try:
return serialize(get_client(api_token).api_keys.get(key_id))
except DevhelmError as e:
raise_tool_error(e)

@mcp.tool()
def create_api_key(
body: CreateApiKeyRequest, api_token: str | None = None
Expand Down
14 changes: 12 additions & 2 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import pytest

from devhelm_mcp.server import mcp
from devhelm_mcp.server import _strip_internal_schema_fields, mcp

RegisteredTools = dict[str, Any]

Expand Down Expand Up @@ -75,6 +75,7 @@
"delete_webhook",
"test_webhook",
"list_api_keys",
"get_api_key",
"create_api_key",
"revoke_api_key",
"delete_api_key",
Expand Down Expand Up @@ -141,7 +142,16 @@

@pytest.fixture(scope="module")
def registered_tools() -> RegisteredTools:
tools = asyncio.run(mcp.list_tools())
# Mirror production startup: the schema strip (api_token / managedBy) is
# applied in the HTTP lifespan and stdio entrypoint, NOT at import time
# (that was moved out in the v0.7.2 hotfix to avoid an asyncio.run() crash
# under Uvicorn). The fixture must apply it too, otherwise it asserts
# against the pre-strip schema and the hidden-field tests fail.
async def _build() -> list[Any]:
await _strip_internal_schema_fields()
return list(await mcp.list_tools())

tools = asyncio.run(_build())
return {t.name: t for t in tools}


Expand Down
8 changes: 4 additions & 4 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading