Server data from the Official MCP Registry
Connect MCP clients to Thalovant control-plane and hub runtime APIs over stdio or Streamable HTTP.
About
Connect MCP clients to Thalovant control-plane and hub runtime APIs over stdio or Streamable HTTP.
Security Report
Valid MCP server (2 strong, 2 medium validity signals). 5 known CVEs in dependencies (0 critical, 3 high severity) Package registry verified. Imported from the Official MCP Registry.
3 files analyzed · 6 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-thalovant-thalovant-mcp": {
"args": [
"-y",
"@thalovant/mcp-server"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
Last Edit: Codex - 2026-11-07 - Motive: Add the canonical Thalovant logo with light and dark theme support.
Thalovant MCP Server
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
/mcpfor 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.
- Chat app tools: connectors, bridges and their status in words, pause and resume, chats, test messages and the plan's allowance. Bot tokens stay in the console.
- 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
Run the published package with npx -y @thalovant/mcp-server, or build it from a checkout:
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:
THALOVANT_API_TOKEN— a scoped Thalovant API token. Recommended.THALOVANT_ACCESS_TOKEN— a pre-issued session access token.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:
thalovant_begin_device_loginwith optionalscopes(an empty list, like none, asks for the API's default),clientNameandclientIdanswersloginId,userCode,verificationUri,verificationUriComplete,intervalandexpiresIn. Show the person the URL and the code.clientIdsigns in as a registered app,thalovant-home-assistantfor 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 (400unknown_client).thalovant_poll_device_loginwith thatloginIdasks once.outcomeispending(poll again afterintervalseconds, already five seconds longer for good if the API asked to slow down),approved(withtokenId,scopesandexpiresAt),expiredordenied.
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:
| Scope | Used by |
|---|---|
hubs:read | thalovant_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, the hub lookup inside thalovant_create_client_identity, and the chat app reads (thalovant_list_chat_app_connectors, thalovant_get_bridge_networks, thalovant_list_bridges, thalovant_get_bridge) |
hubs:inspect | thalovant_get_hub_runtime_capabilities, thalovant_list_runtime_group_marketplace, thalovant_list_runtime_group_inventory, thalovant_list_hub_skills, thalovant_list_hub_skill_history |
hubs:write | All 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, the chat app writes (thalovant_pause_bridge, thalovant_resume_bridge, thalovant_update_bridge, thalovant_send_bridge_test_message), thalovant_list_bridge_chats (it changes nothing but asks the chat network, so the API serves it only to hubs:write and a paid plan), and the opt-in delete tools |
clients:write | thalovant_create_client_identity (POST /v1/clients) and the opt-in thalovant_delete_client |
memory:read | thalovant_list_memory_items, thalovant_get_memory_summary, thalovant_get_memory_item |
memory:write | thalovant_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 athubs:read,clients:read, andclients: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_skillcan fail with a second, distinct 402,This skill requires paid marketplace access for the tenant plan., when the plan is paid but does not includeaccess_tier: paidcatalog 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. A skill the store does not install carries unavailable_reason (licensing or unpublished) with installable and purchase_required false, and is listed there only for a group that already has it.
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). A skill the store does not install for licensing reasons (YouTube, YouTube Music, Spotify, SoundCloud and Bandcamp: the service's terms do not allow a hosted assistant to play its content) fails 409 with code skill_unavailable_licensing and reason: licensing, which thalovant_list_marketplace_skills announces per skill as unavailable_reason and installable; no plan, scope or version changes that. A skill with no published release fails 409 with code skill_unavailable_unpublished and reason: unpublished, before any paid-plan 402: it cannot be installed or bought until a release is published. A non-admin token does not see such a skill in thalovant_list_marketplace_skills at all.
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:
- Sign in, when no token is configured: Device Sign-In.
thalovant_create_client_identitywithconnectionType: "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 carriesclientId,connectionTypeandoperationId.thalovant_wait_for_admissionwith thatoperationId. A hub admits a new connection in about ninety seconds and refuses it until then.outcomeisadmitted,failedortimeout. A failure on the platform carries the operation'serrorCode(andstatusnull); the API refusing the wait itself carries itsstatus,codeanddetail. Atimeoutmeans 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, elseRetry-After, elseRateLimit-Reset); one asking for longer than is left is atimeoutat 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 alinks.selfto 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.
Chat Apps
A hub can answer in a chat network through a chat app: Telegram and Discord are offered, and Matrix, Mattermost and Twitch appear as beta when the API lists them. Each connection of a hub to a chat network is a bridge (/v1/bridges). The person makes the bot in the chat network, then connects it to a hub in the Thalovant console.
Tokens and secrets stay in the console. Creating a bridge and replacing its token or secrets need the bot's credential, and no tool of this server takes a credential as an argument (see Control-Plane Auth), so there is no tool for them. The descriptions and outputs say where to go instead: Connections > Add connection > Chat apps, or the hub's Chat apps tab, and Replace token on the bot. thalovant_update_bridge refuses a setting whose name looks like a token or secret, at any depth, before any request, naming it by its dotted path and never its value.
| Tool | What it does | Kind |
|---|---|---|
thalovant_list_chat_app_connectors | The chat apps (GET /v1/bridges/connectors) in en or fr: status (offered, beta), whether a bridge can be created now, description and setup steps, the secrets the console asks for (by key and label, never a value), the settings form, what a chat id looks like (targets), the mention rule, session scopes and helpers | read-only |
thalovant_get_bridge_networks | Networks offered and the plan's allowance (GET /v1/bridges/networks): limit, used, remaining, paid_plan, can_create, and a summary in words. Free plans get 0. With hubId, the hub owner's allowance | read-only |
thalovant_list_bridges | Bridges of one hub (hubId) or every bridge the token may see (ownerId narrows it; limit 1-500) | read-only |
thalovant_get_bridge | One bridge, with its status in plain words | read-only |
thalovant_list_bridge_chats | The chats, channels or rooms the bot can see, to choose from; manual_entry: true when the ids are typed instead. Changes nothing, but needs hubs:write and a paid plan (it asks the network) | read-only, write scope |
thalovant_pause_bridge | PATCH paused: true: stops answering everywhere, keeps chats, settings and token. Always allowed | write |
thalovant_resume_bridge | PATCH paused: false. Needs a paid plan with room in its allowance | write |
thalovant_update_bridge | The chats it answers in (allowed, which replaces the whole list), requireMention, sessionScope, and non-secret settings | write |
thalovant_send_bridge_test_message | Posts a short "Connected to ." line in one allowed chat | write |
thalovant_delete_bridge | Deletes the bridge and its stored token; opt-in like the other destructive tools | destructive |
Each bridge comes back with the API's own members (network, connector, state, allowed, require_mention, session_scope, paused, hold, bot, invite_links, secrets_set by name, etag, timestamps, and operation when the change queued one) and status:
reasonis the API's customer-safe status, andsummarysays it in words:Setup(no chats chosen yet),Starting,Connected,Paused,Unavailable,TokenMissingandTokenRejected(both send the person to Replace token in the console). A reason this server does not know yet is kept and named.phase,ready, andapplied:trueonce the bridge carries the latest change,falsewhile it is on its way,nullin setup.holdsays why the platform paused a bridge:plan_limit(the plan allows fewer running chat apps, after a downgrade; it resumes on its own once there is room) oraccount_suspended. It isnullfor the owner's own pause.
The platform's own diagnostic sentence and condition (status.message, status.operator_reason, sent to administrators' tokens) are never included. Outputs are built from the members the API documents rather than passed through, so a member added later does not reach the model unreviewed; settings and operation go through the usual secret redaction.
The write tools take an optional etag (the bridge's etag field; there is no ETag header), sent as If-Match: a stale one fails 412 and changes nothing. Telegram lists chats only while its bridge is paused (409 bridge_running), so the flow is pause, send /start@<bot> in the new group, list, update allowed, resume. A refusal keeps the API's code, sentence and fields (see Tool Errors) and adds a hint by code: the settings a bridge_settings_invalid or bridge_url_* refusal names (names only), the plan's allowance after 402 or 403 plan_limit, the wait after 429, and the console for anything about a token.
Reads need hubs:read, except thalovant_list_bridge_chats: it changes nothing, but it asks the chat network on the bot's behalf, so it needs hubs:write, the hub's owner or a workspace manager, and a paid plan, like the writes. Writes need the same, except pausing and deleting, which always work on any plan. THALOVANT_MCP_READONLY keeps the four reads a hubs:read token can call and leaves thalovant_list_bridge_chats out.
Local Stdio
The server speaks MCP over stdio and does not write logs to stdout.
Runtime hub tools load local identities in this order:
identityFiletool argument.configPathorprofiletool argument.- Thalovant SDK environment identity variables.
- 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=trueis explicitly set. - Host headers are allowlisted to reduce DNS rebinding risk.
- Browser
Originheaders are rejected unless they exactly matchMCP_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,DENYframing, 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": "npx",
"args": ["-y", "@thalovant/mcp-server"],
"env": {
"THALOVANT_API_TOKEN": "tvpat_...",
"THALOVANT_PROFILE": "prod"
}
}
}
}
Codex
Use the same stdio command in your MCP client config:
{
"mcpServers": {
"thalovant": {
"command": "npx",
"args": ["-y", "@thalovant/mcp-server"],
"env": {
"THALOVANT_API_TOKEN": "tvpat_...",
"THALOVANT_PROFILE": "prod"
}
}
}
}
Tools
Read-only:
thalovant_config_statusthalovant_list_public_hubsthalovant_get_public_hubthalovant_list_hubsthalovant_get_hubthalovant_get_operationthalovant_wait_for_admissionthalovant_identity_statusthalovant_healthcheckthalovant_intent_inventorythalovant_wait_for_eventthalovant_get_analytics_overviewthalovant_list_memory_itemsthalovant_get_memory_summarythalovant_get_memory_item
Skill and runtime-group discovery (read-only):
thalovant_list_marketplace_skillsthalovant_list_runtime_group_marketplacethalovant_list_runtime_group_inventorythalovant_list_runtime_groupsthalovant_get_runtime_groupthalovant_get_runtime_group_configthalovant_get_hub_runtime_capabilities
Sign-in (see Device Sign-In):
thalovant_begin_device_loginthalovant_poll_device_loginthalovant_revoke_device_login
Writes or hub events:
thalovant_create_client_identitythalovant_askthalovant_querythalovant_send_actionthalovant_send_codethalovant_emit_eventthalovant_create_memory_itemthalovant_update_memory_itemthalovant_delete_memory_item
Hub and runtime-group provisioning:
thalovant_create_hubthalovant_update_hubthalovant_release_hubthalovant_set_hub_ratingthalovant_clear_hub_ratingthalovant_create_runtime_groupthalovant_update_runtime_groupthalovant_update_runtime_group_configthalovant_release_runtime_groupthalovant_install_runtime_group_skillthalovant_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_skillsthalovant_list_hub_skill_historythalovant_install_hub_skillthalovant_update_hub_skillthalovant_remove_hub_skill
Chat apps (see Chat Apps; the first five are read-only, and thalovant_list_bridge_chats needs hubs:write):
thalovant_list_chat_app_connectorsthalovant_get_bridge_networksthalovant_list_bridgesthalovant_get_bridgethalovant_list_bridge_chatsthalovant_pause_bridgethalovant_resume_bridgethalovant_update_bridgethalovant_send_bridge_test_message
Destructive, not registered unless explicitly enabled (see Destructive Tools):
thalovant_delete_hubthalovant_delete_runtime_groupthalovant_delete_clientthalovant_delete_bridge
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.
thalovant_intent_inventory uses the runtime identity and accepts languages,
describe, fallback, speakable, sentence, slots, exampleLimit, and a per-query/batch timeoutMs. It returns registered
intents and examples, fallback skills, fallbacks_known, listed_in, and
may_answer by requested language. listed_in names the hub language each
requested language was listed in. A hub lists nothing under a language it did
not load, so a language with no sentences is listed in its usual form (en-CA
in en-US) or in the closest language the hub loaded, and examples are
rendered in that language. When listed_in differs from what was asked, the
sentences are in another language, and presentation.notes says so. Fallback
skills come from ovos.skills.list, in skill id order, with priority: null:
the hub does not say in which order it tries them. A missing or denied
fallback-skill query remains unknown, rather than being reported as a known
empty list. The optional probe adds at most 1500ms. A silent listing can use engine manifests; disable this with
fallback: false. Both new tools are available in read-only mode and respect
per-principal policy. thalovant_get_operation reads an operation ID returned by
provisioning without replaying the write.
The additive presentation object contains the shared SDK inventory shape:
cache_version, hub_id, hub_name, source, generated_at, skills, notes,
and languages_present. Skill titles are derived from IDs. Runtime discovery
cannot prove catalog locales or hub display metadata, so those fields remain
unknown (empty lists/strings), with explanatory notes. Intent language order is
explicit and survives JSON key sorting. Each tool call retains its own identity
lease; the server does not persist inventory or share a session across callers.
Tool Errors
When the Thalovant API refuses a control-plane call, the tool result is an error whose text starts with the SDK's one-line message (which names the HTTP status and can be shortened) and then says what the API said, in full:
Thalovant API request failed with HTTP 403: Only an administrator can run an image the platform does not release: bus may be one of ghcr.io/thalovant/ovos-messagebus:2026.09.2, ... (platform_image_required)
code: platform_image_required
detail: Only an administrator can run an image the platform does not release: bus may be one of ghcr.io/thalovant/ovos-messagebus:2026.09.2, ghcr.io/thalovant/ovos-messagebus:2026.09.3-alpha.1, ghcr.io/thalovant/ovos-messagebus:2026.08.7; core may be any tag or digest of ghcr.io/thalovant/ovos-core.
fields: {"allowed_images":{"bus":[...],"core":[...]},"allowed_repositories":{"core":"ghcr.io/thalovant/ovos-core"},"component":"runtime_group","refused_images":{...}}
codeis the API's machine-readable code, when it sent one.detailis the whole sentence, and appears only when the first line had to shorten it.fieldsis every other member of the error body as compact JSON (the Problem+JSONtype,title,statusandinstanceare left out), with the same secret redaction as every other tool output. A value a validation error echoes back from the request -- each entry'sinputin adetailorerrorslist, whatever its key or shape -- is replaced with"[omitted]"there and never reaches the first line. A plan refusal carriesresource,limit,usedandplanhere. A chat app refusal about settings or secrets carries the API's ownfieldslist here: their names, never their values. Its hint repeats the names.
A hint may follow for statuses that need one. A platform_image_required or
plan_limit 403 gets its own hint rather than the general scope hint.
A 409 skill_unavailable_licensing says the store refuses the skill for its
licensing and where the catalog announces it, instead of the version hint. A
409 skill_unavailable_unpublished says the skill cannot be installed because
no release of it has been published, that no plan, scope, version or retry
changes that and there is nothing to buy, and where the listings show it; any
other skill_unavailable_* code gets the store's refusal in general terms. A
memory create or update refused with 409 memory_limit (the plan's remembered
items are used up; a Free plan keeps one, limit and upgrade_plan are in the
fields) names the delete tool that frees a slot, the update tool that changes
the item that exists, and the plan to move to; other memory refusals carry the
SDK's message alone.
Request hints, embedded audio, and configuration merging
thalovant_ask accepts sttLang, an ordered pipeline, and location
with required city and optional region, country, timezone, latitude,
and longitude. Hints apply to the current request. Omit unused hints;
an empty pipeline is ignored, and blank language or city strings are rejected.
Ask and Query return language, ordered event metadata, hasAudio and
droppedMedia. Embedded hex stays out of text output. Opt into separate MCP
audio blocks with includeAudio: true; unknown formats become embedded binary
resource blocks. Clip and reply limits are 4 MiB and 16 MiB respectively,
checked by the SDK before retention. The client owns playback. No skill-supplied
path or URL is fetched. Inventory speakable: true renders examples without
changing the raw patterns; slots supplies illustrative slot values, and
exampleLimit limits examples per language to 1–20 (default 2).
Configuration updates default to merge: true: GET the revision, deep-merge
the caller's delta and PUT with expected_revision. Only HTTP 412 retries,
with a fresh read and at most three total attempts. This requires hubs:read,
hubs:write, and a paid plan. An older API without revision support fails before
writing. There is no unconditional fallback. Supplied personas replace;
omitted personas stay unchanged. For an intentional full replacement, pass
merge: false, requiring hubs:write, a paid plan, and coordination with other writers.
Guarded merges reject integers outside JavaScript's safe integer range in the
delta or stored configuration before writing. Store large identifiers as strings
or use an SDK that preserves large JSON integers.
Destructive Tools
thalovant_delete_hub, thalovant_delete_runtime_group, thalovant_delete_client and thalovant_delete_bridge are disabled by default. They are not merely blocked when called — they are never registered, so they do not appear in tools/list and a model cannot see or attempt them.
A long-lived control-plane token combined with an always-available delete tool is a categorically different risk from a read or update tool: deleting a hub also deletes its dependent clients and ACLs, and none of it is reversible. So these are opt-in:
export THALOVANT_ENABLE_DESTRUCTIVE_TOOLS="true"
Accepted true values are 1, true, yes, and on; anything else, including unset, leaves the tools off. The flag is read when a server instance is created. Restart the server process with the updated environment after changing it. thalovant_config_status reports the current state as destructiveToolsEnabled and lists the tools the flag controls.
This is a separate mechanism from the existing tool policy, deliberately. MCP_TOOL_ALLOWLIST / MCP_TOOL_DENYLIST and the per-principal allowedTools / deniedTools are call-time filters where an empty allowlist means "allow everything"; they cannot express a tool that is off until an operator turns it on, and they cannot hide a tool from tools/list. Once THALOVANT_ENABLE_DESTRUCTIVE_TOOLS is set the delete tools are ordinary tools again and remain subject to that policy, so the two layers compose:
# Register delete tools, then deny their use by every principal.
export THALOVANT_ENABLE_DESTRUCTIVE_TOOLS="true"
export MCP_TOOL_DENYLIST="thalovant_delete_hub,thalovant_delete_runtime_group"
The global deny applies to every principal and cannot be overridden by a principal's allowedTools. To restrict only selected principals, leave these tools out of the global denylist and use those principals' deniedTools instead.
Deleting a hub still requires a current etag (412 otherwise), and deleting a runtime group fails with 409 while it is the workspace default or still has hubs attached. Deleting a client cuts off the device or link using it; its etag is read first when none is given. Deleting a chat app bridge removes its stored token, so connecting the bot again means pasting the token in the console; to stop a bridge for now, pause it instead.
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
