nats mcp tool kinds - noetl/worker GitHub Wiki

nats and mcp tool kinds

The nats tool kind is a supported user-facing integration. It is one of several ways a playbook reaches an external data or messaging service the user brings themselves — alongside postgres, duckdb, http, and the object-store kinds. The endpoint comes from the step's own url or from a keychain credential alias: the user's broker, not the platform's.

Do not confuse it with the internal bus. NoETL's own command bus, event bus, and KV store run on EHDB and no longer speak NATS (noetl/ai-meta#212). That removal deleted the internal NATS client, listener, and KV buckets — it did not touch this tool, which lives in the separate noetl-tools crate with its own async-nats dependency. EHDB is internal transient infrastructure and is never exposed as a user store.

Verified end to end on worker v5.91.2 (the post-removal build) against a user-provided broker: kv_put → kv_get → kv_keys round-trips business data intact. Regression guard: tests/user_nats_tool_dispatch.rs. An earlier revision of this page called the kind "dead" — that was wrong, and noetl/ai-meta#219 (which proposed removing it) is closed as won't remove.

Added in noetl-tools 2.15.0 (nats) and 2.16.0 (mcp) — both are dispatched through the worker's existing dynamic ToolRegistry with no per-variant match arms in the worker code itself.

nats (2.15.0 / noetl/tools#12)

Bridges playbook steps to NATS JetStream, Key-Value store, and Object Store operations.

Playbook config shape

tool:
  kind: nats
  auth: my_nats_credential        # credential alias → { url, user?, password?, token? }
  operation: kv_get               # kv_get | kv_put | kv_delete | kv_keys | kv_purge |
                                  # object_get | object_put | object_delete | object_list |
                                  # object_info | js_publish | js_get_msg | js_stream_info
  bucket: my_bucket
  key: my_key

The credential alias is resolved via ctx.get_secret(alias) (keychain lookup) and must contain at minimum a url field. Inline url / user / password / token fields are also accepted directly in the config for simpler cases.

No subscriptions or pull operations — those would hold a worker slot while waiting for an external event, which violates the execution model (agents/rules/execution-model.md). Use JetStream push-to-subject patterns from a playbook-level callback step instead.

Return shape

{
  "status": "success",
  "operation": "kv_get",
  "bucket":    "my_bucket",
  "key":       "my_key",
  "value":     "stored-value"
}

Credential wiring

Set up via NOETL_KEYCHAIN_ENV_VARS (see worker credentials):

NOETL_KEYCHAIN_ENV_VARS=MY_NATS_CRED
MY_NATS_CRED='{"url":"nats://host:4222","user":"alice","password":"s3cret"}'

Playbook step:

tool:
  kind: nats
  auth: MY_NATS_CRED
  operation: kv_put
  bucket: session-cache
  key: "{{ execution_id }}"
  value: "{{ payload | tojson }}"

mcp (2.16.0 / noetl/tools#13)

Bridges playbook steps to MCP (Model Context Protocol) servers over JSON-RPC 2.0 + HTTP with optional SSE response parsing.

Playbook config shape

tool:
  kind: mcp
  endpoint: "http://localhost:8080/mcp"   # direct endpoint, OR
  server: kubernetes                       # key for NOETL_MCP_<SERVER>_ENDPOINT env var
  method: tools/call                       # tools/call | tools/list | health | <other>
  tool: get_pods                           # required for tools/call
  arguments:                               # required for tools/call
    namespace: default
  timeout: 30                              # seconds (default 60)

Endpoint resolution (priority order)

  1. config.endpoint (or config.url / config.server_url / config.base_url)
  2. NOETL_MCP_<SERVER>_ENDPOINT env var keyed by config.server slug
  3. NOETL_MCP_URL fallback env var

Return shape

{
  "status": "ok",
  "server": "kubernetes",
  "endpoint": "http://localhost:8080/mcp",
  "method": "tools/call",
  "tool": "get_pods",
  "arguments": { "namespace": "default" },
  "text": "...",
  "result": { ... }
}

On error: { "status": "error", "error": "<message>" }.

Session lifecycle

Each tool invocation POSTs an initialize request to get an Mcp-Session-Id header. Subsequent calls within the same invocation reuse that session id. Servers that omit the header are treated as stateless (no error).

Dispatch path in the worker

Both tool kinds follow the same worker dispatch path as all other kinds:

NATS pull → CommandExecutor::execute
  → ToolRegistry::execute_from_config(&ToolConfig { kind: "nats" | "mcp", ... })
  → NatsTool::execute | McpTool::execute
  → call.done event (inline / durable / shm fallback chain)

The tool_kind is the only discriminator. CommandExecutor carries no per-kind match arms — adding a new kind to noetl-tools and bumping the dep is the entire wiring.

Observability

noetl_worker_dispatch_duration_seconds{tool_kind="nats"} and {tool_kind="mcp"} appear in /metrics after the first dispatch of each kind. The histogram label is dynamic, so no metrics code changes are needed when new tool kinds land.

Related

⚠️ **GitHub.com Fallback** ⚠️