Skip to main content
Glama
devhelmhq

DevHelm MCP Server

Official
by devhelmhq

get_monitor

Retrieve a single monitor's full configuration by providing its unique ID.

Instructions

Get a single monitor by ID, including its full configuration.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
monitor_idYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The get_monitor tool handler: takes a monitor_id and optional api_token, calls the DevHelm SDK client's monitors.get() method, and serializes the result. Registered via @mcp.tool() decorator inside the register() function.
    @mcp.tool()
    def get_monitor(monitor_id: str, api_token: str | None = None) -> ToolResult:
        """Get a single monitor by ID, including its full configuration."""
        try:
            return serialize(get_client(api_token).monitors.get(monitor_id))
        except DevhelmError as e:
            raise_tool_error(e)
  • The monitors module is imported and its register() function is called in the server startup loop (line 109-110: for mod in ALL_TOOL_MODULES: mod.register(mcp)). This is how get_monitor gets registered on the FastMCP instance.
    ALL_TOOL_MODULES = [
        monitors,
        incidents,
        forensics,
        alert_channels,
        notification_policies,
        environments,
        secrets,
        tags,
        resource_groups,
        webhooks,
        api_keys,
        dependencies,
        deploy_lock,
        maintenance_windows,
        status,
        status_pages,
    ]
    
    for mod in ALL_TOOL_MODULES:
        mod.register(mcp)
  • The register() function in monitors.py that decorates get_monitor (and all other monitor tools) with @mcp.tool() to register them on the FastMCP server.
    def register(mcp: FastMCP) -> None:
  • get_client() resolves the API token and creates a DevHelm SDK client. This is used by get_monitor to make the API call.
    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(),
        )
  • serialize() converts the SDK response (Pydantic models) into JSON-safe dicts for the tool result.
    def serialize(data: object) -> dict[str, Any] | list[dict[str, Any]]:
        """Serialize Pydantic models / dicts / lists to JSON-safe shapes.
    
        The signature returns the legacy `dict | list-of-dict` envelope that
        every MCP tool is wired against, but the recursive work is delegated
        to `_serialize_value` which is fully typed. We narrow at this single
        boundary, so removing the `Any` from tool implementations only
        requires changing this function's return type without touching any
        callers.
        """
        value = _serialize_value(data)
        if isinstance(value, dict):
            return value
        if isinstance(value, list):
            # Tools always feed `serialize` a list of model instances or
            # dicts; the recursive call returns a list of either dicts
            # (Pydantic / dict items) or scalars (which would be a tool
            # bug). Reject scalars loudly so the LLM sees the error.
            out: list[dict[str, Any]] = []
            for item in value:
                if not isinstance(item, dict):
                    raise TypeError(
                        "serialize() expected list items to be dicts or Pydantic "
                        f"models, got {type(item).__name__}."
                    )
                out.append(item)
            return out
        raise TypeError(
            "serialize() expected a Pydantic model, dict, or list of those; "
            f"got {type(data).__name__}."
        )
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the burden. It only states what is returned ('full configuration') but does not disclose any behavioral traits like auth requirements, rate limits, or idempotency. It adds minimal transparency beyond the obvious read operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no unnecessary words, efficiently conveying the purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a simple tool with one parameter and an output schema (not shown), the description is mostly adequate. However, it could briefly note that this is a read-only operation or any prerequisites, but for a basic get, it is sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must add meaning. It mentions 'by ID' but does not elaborate on the format, constraints, or how to obtain the ID. Little additional value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (Get), resource (a single monitor by ID), and scope (full configuration), making it distinct from list_monitors and other get_* tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives like list_monitors or get_monitor_versions. Context for selection is absent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/devhelmhq/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server