Thalovant MCP Server

MCP serverDev tools

Connect MCP clients to Thalovant control-plane and hub runtime APIs over stdio or Streamable HTTP.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Add to setup to save this item as a reference. ahel cannot run it, and signing in will not install it.

Getting started

  1. Save this item in Your setup as a reference.
  2. Read the source or reference documentation for its setup requirements. Saving it here does not connect it to your AI.
  3. Check this page for availability before trying to install it through ahel.

From the project's README

As published by thalovant/thalovant-mcp in README.md.

Public-ready MCP server for Thalovant control-plane and hub runtime APIs.

It uses the official Thalovant Node.js SDK and the production MCP TypeScript SDK over stdio and Streamable HTTP, so it works with local MCP hosts such as Claude Desktop, Codex, Cursor, and remote MCP clients.

What It Includes

  • stdio transport for local agents.
  • Streamable HTTP transport at /mcp for remote agents.
  • OAuth-style protected resource metadata at /.well-known/oauth-protected-resource.
  • Static bearer tokens for simple/private deployments.
  • JWT/JWKS and OAuth token introspection for production remote deployments.
  • Per-principal Thalovant credentials and tool policy.
  • Host/origin validation, CORS, rate limiting, body limits, secure headers, and session binding.
  • Optional resumability event storage and JSONL audit logs.
  • Docker, Compose, Kubernetes, CI, npm package metadata, and MCP registry server.json.

Why TypeScript

Thalovant publishes SDKs for Python, Node.js, Go, and Rust. This server uses Node.js because @thalovant/sdk directly exposes the Thalovant control plane, identity loading, WSS/HTTPS/MQTT runtime clients, memory, analytics, and context helpers, while @modelcontextprotocol/sdk is the best-supported path for cross-agent stdio and Streamable HTTP servers.

Install

npm install
npm run build

Node.js 20 or newer is required.

Control-Plane Auth

Public hub discovery does not need Thalovant credentials. Private control-plane tools and runtime hub tools read credentials only from the MCP server environment or server-side principal credential files. Do not pass API tokens or passwords through chat or tool arguments.

The server selects control-plane auth in this order:

  1. THALOVANT_API_TOKEN — a scoped Thalovant API token. Recommended.
  2. THALOVANT_ACCESS_TOKEN — a pre-issued session access token.
  3. THALOVANT_EMAIL + THALOVANT_PASSWORD — interactive-account login fallback.

When a token is set, the server never calls the login endpoint. thalovant_config_status reports the active mode as controlPlaneAuthMode without revealing token values.

With none of these configured, a person can sign the server in through the browser instead: see Device Sign-In. The token that sign-in mints is used only when nothing above is configured.

Device Sign-In

A tool call cannot wait for a person, so the device flow (RFC 8628) is two tools:

  1. thalovant_begin_device_login with optional scopes (an empty list, like none, asks for the API's default), clientName and clientId answers loginId, userCode, verificationUri, verificationUriComplete, interval and expiresIn. Show the person the URL and the code. clientId signs in as a registered app, thalovant-home-assistant for Home Assistant: the approval page shows the platform's name for it as verified, and approving it again replaces the token it already holds. An id the API does not know is refused (400 unknown_client).
  2. thalovant_poll_device_login with that loginId asks once. outcome is pending (poll again after interval seconds, already five seconds longer for good if the API asked to slow down), approved (with tokenId, scopes and expiresAt), expired or denied.

The device code and the token never reach the model: the server keeps both, bound to the principal that began the sign-in, for the life of the process. Once approved, every control-plane tool for that principal uses the token when no credential is configured. thalovant_revoke_device_login signs out: it revokes that token (a token may always revoke itself, and one already revoked counts as revoked) and forgets it; signing out again answers alreadyRevoked without a request, and configured tokens are never touched. A sign-in reaches only the configured API origin (THALOVANT_API_URL, or the default), since the verification URL it answers with is shown to a person. A Home Assistant link asks for hubs:read, clients:read and clients:write, which is also all a Free plan can approve.

API Tokens (Recommended For AI Agents And CI)

Scoped API tokens are the right credential for AI and automation use: they are minted from the Thalovant dashboard (or through the device flow), carry only the scopes you grant, can be revoked individually, and never involve your account password or MFA. Tokens start with tvpat_.

export THALOVANT_API_TOKEN="tvpat_..."
export THALOVANT_API_URL="https://api.thalovant.com"

npm start

Minimum scopes for the full control-plane tool surface:

ScopeUsed by
hubs:readthalovant_list_hubs, thalovant_get_hub, thalovant_get_analytics_overview, thalovant_list_marketplace_skills, thalovant_list_runtime_groups, thalovant_get_runtime_group, thalovant_get_runtime_group_config, the guarded merge read in thalovant_update_runtime_group_config, and the hub lookup inside thalovant_create_client_identity
hubs:inspectthalovant_get_hub_runtime_capabilities, thalovant_list_runtime_group_marketplace, thalovant_list_runtime_group_inventory, thalovant_list_hub_skills, thalovant_list_hub_skill_history
hubs:writeAll hub and runtime-group provisioning: thalovant_create_hub, thalovant_update_hub, thalovant_release_hub, thalovant_create_runtime_group, thalovant_update_runtime_group, thalovant_update_runtime_group_config, thalovant_release_runtime_group, thalovant_install_runtime_group_skill, thalovant_uninstall_runtime_group_skill, the per-hub thalovant_install_hub_skill, thalovant_update_hub_skill, thalovant_remove_hub_skill, the hub rating tools, and the opt-in delete tools
clients:writethalovant_create_client_identity (POST /v1/clients) and the opt-in thalovant_delete_client
memory:readthalovant_list_memory_items, thalovant_get_memory_summary, thalovant_get_memory_item
memory:writethalovant_create_memory_item, thalovant_update_memory_item, thalovant_delete_memory_item

The hub scopes imply one another: hubs:write grants hubs:read, which grants hubs:inspect and hubs:preview. Minting a token with hubs:read is therefore enough for every discovery tool in the table above.

Scope is not the whole story for provisioning. Every hub and runtime-group write also requires a paid plan, and the API checks scope before the plan, so the two failure modes are ordered:

  • A token missing the scope fails 403 Insufficient scopes. Free-plan API tokens are capped at hubs:read, clients:read, and clients:write, so on the free tier provisioning fails with this 403 and never reaches the 402.
  • A correctly scoped token on a free plan fails 402 API access requires a paid plan.
  • thalovant_install_runtime_group_skill can fail with a second, distinct 402, This skill requires paid marketplace access for the tenant plan., when the plan is paid but does not include access_tier: paid catalog entries.

Discovery is deliberately not paid-gated: a free-tier token can browse the marketplace catalog and set hub ratings, but cannot install skills or provision hubs. Use thalovant_list_runtime_group_marketplace before installing — it reports installable, purchase_required, and access_message per skill, which turns an opaque 402 into a decision you can make up front.

Grant fewer scopes for narrower deployments: a read-only assistant needs only hubs:read and memory:read, and a discovery-only agent that browses skills but never provisions needs hubs:read alone. thalovant_get_analytics_overview with admin: true additionally requires an admin account with admin:analytics, which API tokens for regular use should not carry. Runtime hub tools (thalovant_ask, thalovant_send_action, and friends) use Thalovant client identities, not control-plane tokens.

Hub Etags

thalovant_update_hub and thalovant_delete_hub use optimistic locking and require the hub's current etag, sent as If-Match. The etag is only available in the body of the hub resource — the API sends no ETag response header — so an agent must call thalovant_get_hub first and pass the etag field from that response. A missing or stale value fails 412 ETag mismatch and changes nothing; re-fetch and retry. name, namespace, and domain are immutable after creation, so thalovant_update_hub does not accept them at all; send only the fields you are changing rather than round-tripping a whole hub resource. Runtime-group writes do not use etags.

Hub Skills

thalovant_list_hub_skills, thalovant_install_hub_skill, thalovant_update_hub_skill, and thalovant_remove_hub_skill select a hub's attached runtime group; all hubs sharing it are affected. A hub can start with no skills at all and gain them one at a time; a change applies live on the hub in about 15 seconds with no restart. hubId must be the hub UUID — the authenticated hub routes reject slugs.

thalovant_list_hub_skills returns the whole GET /v1/hubs/{hub_id}/skills envelope: hub_id, runtime_group_id, observed_at, source, the runtime's phase and message, and data, one row per skill (possibly empty) with skill, title, marketplace_skill_id, package_name, source_type, install_source, version, version_pin, installed_version, observed_version, previous_version, latest_version, available_version, update_available, changelog, active, state, the runtime's phase, message and last error, and last_transition_at. state is one of pending, installed, failed, removing, drifted, quarantined, or unmanaged; a change in progress shows as pending.

Each write answers 202 with operation_id, hub_id, runtime_group_id, skill, version (null for a removal), previous_version, and state (installing, updating, or removing); pass wait: true to poll that operation every 2 s until it converges (installed, or removed for a removal; a failed or timed_out operation raises an error carrying its error_message), with a 120 s default timeoutMs, or follow it yourself with thalovant_get_operation. Installing a skill that is already installed at another version performs an update; the same version fails 409 with code skill_version_already_installed. A hub with no runtime group fails 404 with code hub_without_runtime_group (a plain 404 means an unknown hub or a skill that is not installed), and an unresolvable latest or an invalid version fails 422. Errors are RFC 7807 problem bodies; the tool error keeps the short message, which appends the root code in parentheses, for example Thalovant API request failed with HTTP 409: Skill version already installed. (skill_version_already_installed), and then the code and the body's other fields (see Tool Errors).

Listing needs hubs:inspect (implied by hubs:read); the writes need hubs:write and a paid plan, and because scope is checked before plan a free-plan token sees 403, never 402. Hub-restricted tokens (a hub_ids allowlist) are honoured on all four routes. The server uses the published Node SDK hub-skill methods and preserves MCP cancellation checks during polling. A failed status read retains the accepted operation ID; use thalovant_get_operation with that ID instead of submitting the write again. No new poll starts at or after the polling deadline. That deadline does not cancel an HTTP request already in flight.

Login Fallback

export THALOVANT_EMAIL="you@example.com"
export THALOVANT_PASSWORD="..."

export THALOVANT_PROFILE="prod"
export THALOVANT_API_URL="https://api.thalovant.com"

npm start

If neither a token nor email/password is configured, authenticated control-plane tools fail with a clear error naming the supported options.

Configured API tokens and login credentials are bound to the origin of their configured apiUrl or THALOVANT_API_URL (default https://api.thalovant.com). A tool argument cannot redirect those credentials to another origin. Equivalent URL spellings and paths on the same origin are allowed; custom origins must be configured alongside their credentials. Anonymous public discovery may still select a custom API URL.

Link Home Assistant

A home controller such as Home Assistant links to a hub with a connection of its own kind:

  1. Sign in, when no token is configured: Device Sign-In.
  2. thalovant_create_client_identity with connectionType: "home_assistant". The API must answer with the same kind; one that made an ordinary connection instead has it deleted again and the call fails. The result carries clientId, connectionType and operationId.
  3. thalovant_wait_for_admission with that operationId. A hub admits a new connection in about ninety seconds and refuses it until then. outcome is admitted, failed or timeout. A failure on the platform carries the operation's errorCode (and status null); the API refusing the wait itself carries its status, code and detail. A timeout means the connection may still be admitted: call the tool again rather than creating another connection. A 429 is waited out for the time the API names (in its body, else Retry-After, else RateLimit-Reset); one asking for longer than is left is a timeout at once. A token the API refuses (401, 403) and an API out of reach are tool errors of their own, each with a hint, never a failed admission. It reads only, honours cancellation, and never follows a links.self to another origin than the API's.

Each refusal of step 2 names what to do next after the API's own text (see Tool Errors): a kind the API does not know yet, a plan that does not allow the connection, a hub that already holds its one Home Assistant link (with the client_id holding it), or a token the API refused (sign in again). thalovant_delete_client removes a connection (If-Match, reading the etag first when none is given, retrying once on 412, and counting a connection already gone as deleted); it is a destructive tool the operator enables.

Answering the hub's requests afterwards is the integration's own long-lived job, on the data plane; an MCP tool call holds a hub connection only for its own length, so that part of the link is not a tool.

Local Stdio

The server speaks MCP over stdio and does not write logs to stdout.

Runtime hub tools load local identities in this order:

  1. identityFile tool argument.
  2. configPath or profile tool argument.
  3. Thalovant SDK environment identity variables.
  4. The default Thalovant SDK config profile.

Keep Thalovant identity files secret. The SDK expects protected config files such as ~/.config/thalovant/config.yaml with mode 0600.

Streamable HTTP

Remote mode uses MCP Streamable HTTP at /mcp and requires bearer authentication by default.

export MCP_TRANSPORT="http"
export MCP_HTTP_HOST="127.0.0.1"
export MCP_HTTP_PORT="3000"
export MCP_HTTP_AUTH_TOKEN="$(openssl rand -hex 32)"
export MCP_HTTP_ALLOWED_HOSTS="127.0.0.1:3000,localhost:3000"

npm run start:http

Clients connect to:

http://127.0.0.1:3000/mcp
Authorization: Bearer <token>

Health checks are available at /healthz and /readyz.

For public deployments, set the public URL and exact host/origin allowlists:

export MCP_HTTP_HOST="0.0.0.0"
export MCP_HTTP_PORT="3000"
export MCP_PUBLIC_URL="https://mcp.example.com"
export MCP_HTTP_ALLOWED_HOSTS="mcp.example.com"
export MCP_HTTP_ALLOWED_ORIGINS="https://agent.example.com"

Remote Auth

Use static bearer tokens only for local, private, or single-tenant deployments:

export MCP_HTTP_AUTH_TOKEN="$(openssl rand -hex 32)"
# or
export MCP_HTTP_AUTH_TOKENS="token-a,token-b"

Use JWT/JWKS for production resource-server validation:

export MCP_HTTP_AUTH_MODE="jwt"
export MCP_OAUTH_ISSUER="https://auth.example.com/"
export MCP_OAUTH_JWKS_URL="https://auth.example.com/.well-known/jwks.json"
export MCP_OAUTH_AUDIENCE="https://mcp.example.com/mcp"
export MCP_OAUTH_AUTHORIZATION_SERVERS="https://auth.example.com/"
export MCP_OAUTH_REQUIRED_SCOPES="mcp:thalovant"

Use introspection when your authorization server issues opaque tokens:

export MCP_HTTP_AUTH_MODE="introspection"
export MCP_OAUTH_INTROSPECTION_URL="https://auth.example.com/oauth2/introspect"
export MCP_OAUTH_CLIENT_ID="mcp-server-client"
export MCP_OAUTH_CLIENT_SECRET="..."
export MCP_OAUTH_AUDIENCE="https://mcp.example.com/mcp"
export MCP_OAUTH_AUTHORIZATION_SERVERS="https://auth.example.com/"
export MCP_OAUTH_REQUIRED_SCOPES="mcp:thalovant"

The server publishes protected resource metadata at:

https://mcp.example.com/.well-known/oauth-protected-resource

401 responses include WWW-Authenticate with a resource_metadata pointer for MCP clients that support OAuth discovery.

Principal Credentials

For multi-user remote deployments, do not share one Thalovant access token across all MCP users. Map each authenticated MCP principal to its own Thalovant control-plane token, runtime identity, and tool policy.

Single file:

export THALOVANT_PRINCIPAL_CREDENTIALS_FILE="/run/secrets/thalovant-principals.json"

Directory mode:

export THALOVANT_PRINCIPAL_CREDENTIALS_DIR="/run/secrets/thalovant-principals"

Directory files are named <sha256(principal-id)>.json. The server checks the OAuth subject, principal id, and client id. See examples/principal-credentials.sample.json.

Keep this disabled for multi-user deployments unless you intentionally want every remote principal to use the server environment's Thalovant credentials:

export THALOVANT_ALLOW_SHARED_CREDENTIALS="false"

Runtime identityFile, configPath, profile, and fromEnv tool arguments are disabled for remote principals by default. Set MCP_HTTP_ALLOW_CLIENT_CREDENTIAL_PATHS=true only for trusted private deployments.

Policy, Audit, And Resumability

Global tool policy:

export MCP_TOOL_ALLOWLIST="thalovant_*"
export MCP_TOOL_DENYLIST="thalovant_delete_memory_item"

Per-principal credential files may also include allowedTools and deniedTools.

Both are call-time filters, and an empty allowlist means "allow everything". They cannot make a tool default-off or hide it from tools/list, which is why the two destructive control-plane tools are gated separately by THALOVANT_ENABLE_DESTRUCTIVE_TOOLS. See Destructive Tools.

Audit logs:

export MCP_AUDIT_LOG="stderr" # off, stderr, file, or both
export MCP_AUDIT_LOG_FILE="/var/log/thalovant-mcp/audit.jsonl"
export MCP_AUDIT_INCLUDE_ARGS="false"

Audit entries are JSONL and credential-shaped fields are redacted.

Streamable HTTP resumability defaults to an in-memory event store. Use a file-backed store for single-instance restarts:

export MCP_EVENT_STORE_FILE="/var/lib/thalovant-mcp/events.jsonl"

HTTP Hardening

  • Bearer auth is required unless MCP_HTTP_ALLOW_UNAUTHENTICATED=true is explicitly set.
  • Host headers are allowlisted to reduce DNS rebinding risk.
  • Browser Origin headers are rejected unless they exactly match MCP_HTTP_ALLOWED_ORIGINS.
  • CORS exposes only MCP session/protocol headers.
  • Sessions use cryptographically random ids and are bound to the authenticated principal.
  • Request bodies are capped by MCP_HTTP_MAX_BODY_BYTES, defaulting to 1 MiB.
  • Fixed-window rate limiting defaults to 120 MCP requests per minute per client address.
  • Security headers include nosniff, DENY framing, no referrer, and a restrictive CSP.

Useful HTTP environment variables:

MCP_HTTP_PATH=/mcp
MCP_HTTP_RATE_LIMIT_MAX=120
MCP_HTTP_RATE_LIMIT_WINDOW_MS=60000
MCP_HTTP_SESSION_TTL_MS=3600000
MCP_HTTP_MAX_BODY_BYTES=1048576
MCP_HTTP_ENABLE_JSON_RESPONSE=false
MCP_HTTP_TRUST_PROXY=false

Claude Desktop

Recommended: authenticate with a scoped API token so the MCP config never contains your account password.

{
  "mcpServers": {
    "thalovant": {
      "command": "node",
      "args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
      "env": {
        "THALOVANT_API_TOKEN": "tvpat_...",
        "THALOVANT_PROFILE": "prod"
      }
    }
  }
}

Codex

Use the same stdio command in your MCP client config:

{
  "mcpServers": {
    "thalovant": {
      "command": "node",
      "args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
      "env": {
        "THALOVANT_API_TOKEN": "tvpat_...",
        "THALOVANT_PROFILE": "prod"
      }
    }
  }
}

Tools

Read-only:

  • thalovant_config_status
  • thalovant_list_public_hubs
  • thalovant_get_public_hub
  • thalovant_list_hubs
  • thalovant_get_hub
  • thalovant_get_operation
  • thalovant_wait_for_admission
  • thalovant_identity_status
  • thalovant_healthcheck
  • thalovant_intent_inventory
  • thalovant_wait_for_event
  • thalovant_get_analytics_overview
  • thalovant_list_memory_items
  • thalovant_get_memory_summary
  • thalovant_get_memory_item

Skill and runtime-group discovery (read-only):

  • thalovant_list_marketplace_skills
  • thalovant_list_runtime_group_marketplace
  • thalovant_list_runtime_group_inventory
  • thalovant_list_runtime_groups
  • thalovant_get_runtime_group
  • thalovant_get_runtime_group_config
  • thalovant_get_hub_runtime_capabilities

Sign-in (see Device Sign-In):

  • thalovant_begin_device_login
  • thalovant_poll_device_login
  • thalovant_revoke_device_login

Writes or hub events:

  • thalovant_create_client_identity
  • thalovant_ask
  • thalovant_query
  • thalovant_send_action
  • thalovant_send_code
  • thalovant_emit_event
  • thalovant_create_memory_item
  • thalovant_update_memory_item
  • thalovant_delete_memory_item

Hub and runtime-group provisioning:

  • thalovant_create_hub
  • thalovant_update_hub
  • thalovant_release_hub
  • thalovant_set_hub_rating
  • thalovant_clear_hub_rating
  • thalovant_create_runtime_group
  • thalovant_update_runtime_group
  • thalovant_update_runtime_group_config
  • thalovant_release_runtime_group
  • thalovant_install_runtime_group_skill
  • thalovant_uninstall_runtime_group_skill

Hub skills, acting on the hub’s shared runtime (see Hub Skills; the list tool is read-only):

  • thalovant_list_hub_skills
  • thalovant_list_hub_skill_history
  • thalovant_install_hub_skill
  • thalovant_update_hub_skill
  • thalovant_remove_hub_skill

Destructive, not registered unless explicitly enabled (see Destructive Tools):

  • thalovant_delete_hub
  • thalovant_delete_runtime_group
  • thalovant_delete_client

Tool outputs redact credential-shaped fields. thalovant_create_client_identity does not return secret identity material; pass savePath when you want the full identity written to a local file with mode 0600. savePath is confined to the server's identity directory (THALOVANT_MCP_IDENTITY_DIR, default <config-dir>/thalovant/identities): pass a plain filename, since absolute paths outside that directory and .. traversal are rejected, so a model cannot drop a credential file into a git working tree or synced folder. thalovant_config_status reports the active identityDir.

Shortened here. Read the whole README on GitHub.

Signals

Last commit
Oct 2026
Weekly_downloads
774 weekly_downloads
Advanced
Delivery
thalovant-mcp MCP server → your ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-thalovant-thalovant-mcp
Source
github.com/thalovant/thalovant-mcp