Skip to main content
Glama
nahook-mcp

nahook-mcp

Official
by getnahook

nahook-mcp

The official Model Context Protocol server for Nahook — drive webhook deliveries, endpoints, and event triggers from Claude Desktop, Cursor, Cline, and any other MCP-compatible AI assistant.

The MCP server ships as a subcommand of the nahook CLI. One binary, one credentials file, one install path.

Glama

What it does

Once installed and authenticated, your AI assistant can:

  • Inspect endpoints, deliveries, and per-attempt logs to debug failed webhooks.

  • Retry failed or dead-lettered deliveries.

  • Trigger events and fan them out to every subscribed endpoint.

  • Send webhooks directly to a single endpoint.

  • Manage endpoints (create, update, pause, resume) — never delete, by design.

Write operations are tagged with the MCP destructiveHint/readOnlyHint annotations so MCP clients surface a per-call human-approval prompt before anything mutates state.

Related MCP server: shipmail-mcp

Security guardrails

Webhook payloads are third-party data, so the server treats everything that flows back into the model's context as a potential prompt-injection channel:

  • Untrusted content is fenced. Every producer- or receiver-authored value that reaches the model — webhook payloads (get_delivery with include_payload), delivery idempotency keys, and receiver error messages (list_attempts) — is wrapped in explicit <<<UNTRUSTED CONTENT ...>>> delimiters with embedded fence markers neutralized, and the server instructions tell the model to treat fenced content as inert data, never as instructions.

  • Payloads are capped. Bodies over 256 KB are truncated (flagged via payload_truncated) so a hostile producer can't stuff the model's context. Tune the cap with NAHOOK_MCP_PAYLOAD_CAP=<bytes> in the MCP client's environment.

  • No delete tools, by design. Nothing in the tool surface can destroy data.

  • update_endpoint is marked destructive. It overwrites live endpoint config (repointing its URL is the primitive an injection would target), so its destructiveHint is true and clients surface a prominent approval prompt.

  • Write tools rely on human approval. Per the MCP spec, annotations are advisory hints, not access control — the security boundary is the client's per-call approval prompt, which applies to every non-read tool. The additive write tools (create_endpoint, send_to_endpoint, trigger_event) are annotated destructiveHint: false per their spec semantics; keep human-in-the-loop approval enabled in your MCP client and don't blanket-allow them. If you want a hard guarantee of no writes, run the server without an ingestion key (disables sending) and treat management tools as approval-gated.

  • Secrets never reach the model. Endpoint signing secrets and auth tokens are omitted from every tool output; whoami returns only the token's public id.

  • Split credentials. Sending (trigger_event, send_to_endpoint) requires a separate ingestion key — omit it and the server is management/read-only for ingestion.

Client-side measures can't make prompt injection impossible — pair them with your MCP client's tool-approval prompts.

Install

The MCP server is a subcommand of the nahook CLI. Install the CLI, then add it to your AI client.

1. Install the CLI

macOS / Linux (Homebrew):

brew install getnahook/tap/nahook

Linux / macOS (install script):

curl -fsSL https://cli.nahook.com/install.sh | sh

See getnahook/nahook-cli for other install options.

2. Log in once

nahook login

This opens your browser, walks you through device-grant authentication, and writes credentials to ~/.nahook/config.toml.

3. Wire the MCP server into your AI client

Claude Desktop & Claude Code CLI:

claude mcp add nahook -- nahook mcp serve

Cursor — edit ~/.cursor/mcp.json:

{
  "mcpServers": {
    "nahook": {
      "command": "nahook",
      "args": ["mcp", "serve"]
    }
  }
}

Cline (VS Code) — Cline → Settings → MCP Servers → add:

{
  "nahook": {
    "command": "nahook",
    "args": ["mcp", "serve"]
  }
}

Zed — add to settings.json:

{
  "assistant": {
    "mcp_servers": {
      "nahook": {
        "command": "nahook",
        "args": ["mcp", "serve"]
      }
    }
  }
}

Other MCP-compatible clients (Continue, Windsurf, Goose, …) follow the same command + args shape.

Tools

Tool

What it does

whoami

Local config sanity check — workspace, region, token id, expiry.

list_endpoints

List every endpoint in the current workspace.

get_endpoint

Fetch one endpoint by ep_xxx.

create_endpoint

Create a new endpoint. Defaults to the workspace's default environment; also accepts slugs like production.

update_endpoint

Partial patch — pause/resume, change URL, update description.

list_environments

List every environment in the workspace.

list_deliveries

Page through an endpoint's deliveries, newest-first.

get_delivery

Fetch one delivery by del_xxx. Pass include_payload: true to also fetch the original webhook body — critical for debugging.

list_attempts

List every attempt against a delivery (useful for debugging failures).

retry_delivery

Re-enqueue a failed or dead-lettered delivery.

trigger_event

Fire an event by type — the backend fans it out to every subscriber.

send_to_endpoint

Send a webhook directly to one endpoint.

Authentication model

The MCP server uses two separate credentials:

  • CLI login token (nhc_…) — written by nahook login. Powers read tools and management operations on endpoints / deliveries / environments.

  • Ingestion key (nhk_…) — the same key your SDKs use. Powers trigger_event and send_to_endpoint. Set it via NAHOOK_INGESTION_KEY=nhk_... in the MCP client's environment, or add ingestion_key = "nhk_..." to ~/.nahook/config.toml. The server fails loudly if a write tool is called without one configured.

Permissions are evaluated per request against your workspace role — the MCP token has exactly the permissions you have in the dashboard, nothing more.

Where the code lives

The MCP server implementation is part of the nahook CLI source tree. This repo exists as a discoverability surface — README, MCP directory entries, and packaging metadata. There is no separate codebase to maintain.

If you want to file a bug, request a tool, or contribute: please open the issue at getnahook/nahook-cli.

License

MIT — same as the CLI.

Available Tools

12 tools
create_endpointA

Create a new endpoint in the current workspace. If environment_id is omitted, the workspace's default environment is used. environment_id accepts either an env_xxx id or a slug like "production"/"staging". Returns the created endpoint with its newly-generated public id. Example: user says "create a webhook for https://example.com/hook" → call with url="https://example.com/hook". If the user explicitly names an environment ("in staging"), pass it; otherwise omit and the default is used. For the full endpoint schema, see resource nahook://schemas/endpoint.

Inputs: url (required), type, description, environment_id. Returns: endpoint

get_deliveryA
Read-onlyIdempotent

Fetch a single delivery by its public id (del_xxx). Returns status, attempt count, timestamps. Pass include_payload=true to also fetch the original webhook body — critical for debugging why a delivery failed. Adds one extra HTTP round-trip. Example: "why did del_xyz fail?" → call with delivery_id="del_xyz", include_payload=true to see the body the producer sent, then follow up with list_attempts to see what each HTTP attempt returned. For the full delivery schema and status value meanings, see resources nahook://schemas/delivery and nahook://schemas/delivery-statuses.

Inputs: delivery_id (required), include_payload. Returns: delivery, payload, payload_processing

get_endpointA
Read-onlyIdempotent

Fetch a single endpoint by its public id (ep_xxx). Example: user says "show me ep_abc" or "details of ep_abc". If the user references an endpoint without naming a specific ep_xxx id, call list_endpoints first to discover ids. For the full endpoint schema, see resource nahook://schemas/endpoint.

Inputs: endpoint_id (required). Returns: endpoint

list_attemptsA
Read-onlyIdempotent

List every recorded attempt for a delivery, oldest first. Useful for debugging why a delivery failed. Example: "why did del_xyz fail?" → pair this with get_delivery (include_payload=true). get_delivery shows what the producer sent; list_attempts shows what each receiver attempt returned. Together they tell you whether the failure was producer-side (bad payload) or receiver-side (5xx, timeout).

Inputs: delivery_id (required). Returns: attempts

list_deliveriesA
Read-onlyIdempotent

List deliveries for an endpoint, newest-first. Paginate by passing next_cursor back as cursor on subsequent calls. Returns up to 200 per page (default 50). Example: "show me failed deliveries for ep_acme" → endpoint_id="ep_acme", status="failed". Requires an endpoint_id — if the user names a specific delivery (del_xxx) instead, use get_delivery. Valid status filters: pending, delivering, delivered, failed, scheduled_retry, dead_letter. For the full delivery schema and status value meanings, see resources nahook://schemas/delivery and nahook://schemas/delivery-statuses.

Inputs: endpoint_id (required), limit, cursor, status. Returns: deliveries, next_cursor

list_endpointsA
Read-onlyIdempotent

List every endpoint in the current workspace. Returns id, type, url, status (active/paused), is_active, created_at, and last_delivery_at for each. Example: user says "show me my webhooks" or "list my endpoints" → call with no args. This is the natural first call whenever the user names operations on an endpoint without giving a specific ep_xxx id — use the returned ids in follow-up calls. For the full endpoint schema, see resource nahook://schemas/endpoint.

Returns: endpoints

list_environmentsA
Read-onlyIdempotent

List every environment in the current workspace. Use the returned id (env_xxx) or slug as environment_id when creating endpoints. Example: user says "what environments do I have". Most workspaces have a single default environment — only call this when the user mentions environments explicitly or you need to disambiguate where a new endpoint should land.

Returns: environments

retry_deliveryA

Re-enqueue a failed or dead-lettered delivery. The backend returns 409 if the delivery is in any other state. Returns the updated delivery row. Example: "retry del_xyz" → call with delivery_id="del_xyz". Only works when the delivery's status is failed or dead_letter — calling on delivered, pending, delivering, or scheduled_retry returns 409. Check status with get_delivery first if uncertain. For which statuses accept retry and which don't, see resource nahook://schemas/delivery-statuses.

Inputs: delivery_id (required). Returns: delivery, payload, payload_processing

send_to_endpointA
Idempotent

Send a webhook directly to one endpoint (skips event-type fan-out). Pass idempotency_key for safe retries — duplicate keys return the original delivery id. Example: "send a test webhook to ep_abc with payload {...}" → endpoint_id="ep_abc", payload={...}. Use this when the user names a specific endpoint to target; use trigger_event when the user names an event type that should fan out to all subscribers.

Inputs: endpoint_id (required), payload (required), idempotency_key. Returns: delivery_id, idempotency_key, status

trigger_eventA

Fire an event by event type — the backend fans it out to every endpoint subscribed to that type. Returns one delivery id per subscriber (empty if no subscribers). Use send_to_endpoint when you want to target one specific endpoint instead. Example: "fire an order.created event with order_id=ord_123" → event_type="order.created", payload={"order_id": "ord_123"}. An empty delivery_ids array means no endpoint subscribes to that event type — inspect subscriptions with list_endpoints if the user expected a non-empty result.

Inputs: event_type (required), payload (required). Returns: event_type_id, delivery_ids, status

update_endpointA
Idempotent

Patch an existing endpoint. Only fields explicitly set in the input are sent to the API; omit a field to leave it unchanged. Common uses: pause/resume via is_active, redirect via url. Example: "pause ep_abc" / "disable ep_abc" → endpoint_id="ep_abc", is_active=false. "resume ep_abc" / "enable ep_abc" → is_active=true. "point ep_abc at https://new.com" → endpoint_id="ep_abc", url="https://new.com". PATCH semantics matter: passing url="" would clear the URL, so don't include fields the user didn't ask to change. For the full endpoint schema, see resource nahook://schemas/endpoint.

Inputs: endpoint_id (required), url, description, is_active. Returns: endpoint

whoamiA
Read-onlyIdempotent

Return the workspace, region, and token id the local Nahook CLI is logged into. Use this as a first call to confirm credentials are in place. Reads ~/.nahook/config.toml; never hits the network. Example: "am I logged in?", "what workspace am I in?". Also worth calling defensively before other tools if you suspect credentials might be missing — it tells you whether nahook login has run.

Returns: logged_in, workspace, region, token_id, +2 more

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 12 tools

    Disambiguation5/5

    Each tool targets a distinct resource and action. There is no ambiguity between tools like create_endpoint vs update_endpoint, or list_deliveries vs list_attempts. send_to_endpoint and trigger_event are clearly differentiated by direct vs fan-out behavior.

    Naming Consistency4/5

    Tools consistently use verb_noun naming in snake_case. The only outlier is 'whoami', which follows a common convention but deviates from the pattern. Overall, the naming is predictable and readable.

    Tool Count5/5

    12 tools provide a well-scoped set for managing webhook endpoints, deliveries, and events. The count is neither too small nor too large, covering essential operations without being overwhelming.

    Completeness2/5

    The tool set is missing critical operations such as deleting an endpoint and managing subscriptions (which are implied by trigger_event). There is no way to list, add, or remove event subscriptions, which would be necessary for a complete webhook management system.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      C
      maintenance
      MCP Server for full Easypanel control via Claude Code, Cursor, and Claude Desktop. Provides 37 tools for deploy, logs, env vars, domains, databases, and monitoring with built-in safety guards.
      57
      309 npm
      4
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      Official MCP server for Shipmail, enabling agents to manage domains, mailboxes, messages, threads, webhooks, and suppressions via natural language.
      100
      837 npm
      1
      MIT
    • A
      license
      B
      quality
      D
      maintenance
      MCP server for the Inngest Cloud REST API to inspect functions, runs, and events in production from Claude Code, Cursor, or any MCP client.
      8
      25 npm
      1
      MIT