singular-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@singular-mcp-server update lower-third with headline 'Election Results'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
singular-mcp-server
A comprehensive Model Context Protocol server for reading, controlling, and orchestrating Singular.live live broadcast graphics compositions from Claude Desktop or any MCP client.
It lets an agent discover a control app's structure, fill its control nodes (text, images, colors…), animate sub-compositions in and out, push low-latency data, and run a newsroom rundown → graphics workflow over templates you build once in Composer.
What this is (and isn't)
Singular's public API controls compositions; it does not author them. You build the graphics (lower-thirds, full-frames, tickers, bugs) once in the browser Composer, and this server lets agents fill and sequence that kit of parts in real time. Authoring composition structure, account listing, token minting, media upload, and webhooks are not exposed by any public Singular API. See docs/SINGULAR_API.md for the API reference.
Installation
Prerequisites: Node.js ≥ 18 (developed on Node 22) and npm.
# 1. Clone
git clone https://github.com/dqbuilds/singular-mcp-server.git
cd singular-mcp-server
# 2. Install dependencies
npm install
# 3. Build (compiles TypeScript to dist/)
npm run build
# 4. (optional) Verify — runs the mock-API integration test; no Singular account needed
npm testThis produces the runnable server at dist/index.js. Next, configure a Singular
app token and point your MCP client at it (below).
Install globally (optional). From the repo,
npm linkputs asingular-mcp-servercommand on your PATH; then your MCP client can use"command": "singular-mcp-server"instead of an absolutenode dist/index.jspath.
Related MCP server: OpenCut Controller
Configuration
Secrets come only from the environment. Copy .env.example → .env (the
server itself reads real environment variables; use your process manager or the
MCP client config to set them).
Variable | Required | Default | Purpose |
| no | — | Default control-app token when a tool omits |
| no |
| Control REST base. |
| no |
| Data Stream base. |
| no | — | Optional default sub-composition name. |
| no |
| On-disk alias→token registry (owner-only perms). |
| no |
| Per-request timeout. |
| no |
|
|
Where do tokens come from? Copy each app token from the Singular Dashboard
(inspector "i" → URLs and Token; UNO </>; or Studio → Manage Access → App
Token). The API cannot mint them.
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"singular": {
"command": "node",
"args": ["/absolute/path/to/singular-mcp-server/dist/index.js"],
"env": { "SINGULAR_APP_TOKEN": "your-token-here" }
}
}
}You can omit SINGULAR_APP_TOKEN and instead register apps by alias at runtime
with singular_register_app.
Tools (25)
Every tool takes response_format (markdown default, or json) and returns
both human text and structuredContent. App-targeting tools accept an app
alias (from the registry) or a raw appToken, falling back to the env default.
Discovery & read
singular_get_app_metadata— ids, name, output/preview URLs.singular_get_model— structural model: sub-compositions + typed control nodes.singular_get_control_state— current live state + values.singular_list_subcompositions— model + live values joined (best overview).singular_find_nodes— filter nodes bytype(e.g.image) / title.
Content control
singular_update_content— fill nodes across one or more sub-comps in one PATCH.singular_set_image— set an image node by URL (validates it's an image node + URL reachability).singular_reset_nodes— reset nodes to their reset values (clear a template).
Animation & playout
singular_animate_state— take sub-comps In/Out (multiple targets per call).singular_update_and_animate— fill + take to air atomically (no stale flash).singular_take_out_all— clear the whole app (TakeOutAllOutput).
Data streams
singular_register_data_stream— store a stream's private token by alias.singular_list_data_streams— list registered stream aliases.singular_push_datastream— PUT low-latency JSON (≤ 60 KB) to a stream.
Media & output
singular_get_output_urls— live output / broadcast / thumbnail URLs.singular_check_image_url— pre-flight an image URL.
Orchestration & rundown
singular_register_app/singular_list_apps/singular_remove_app— the alias registry.singular_map_rundown_template— bind an item type → sub-comp + field map.singular_list_rundown_templates— list mappings.singular_prepare_item— dry-run: resolve a story item to a payload, no air.singular_play_rundown_item— take an item to air (optional server-side auto-out).singular_poll_onair_state— poll what's on air (+ diff since last poll).singular_server_info— server config/health (no secrets).
For agents
The server sends usage guidance to clients as its MCP instructions at connect
time, and every tool has a self-contained description with its args and return
shape. The full playbook — the discover-before-write rule, the canonical flow,
value formats, and gotchas — is in AGENTS.md.
Newsroom workflow example
1. singular_register_app { alias: "evening-news", token: "…" }
2. singular_find_nodes { app: "evening-news", type: "image" } # discover the kit
3. singular_map_rundown_template {
name: "lower-third", appAlias: "evening-news",
subCompositionName: "LowerThird",
fieldMap: { headline: "titleNode", photo: "bannerImage" } }
4. singular_prepare_item { template: "lower-third",
fields: { headline: "Markets rally", photo: "https://cdn/…jpg" } } # verify
5. singular_play_rundown_item { template: "lower-third",
fields: { headline: "Markets rally", photo: "https://cdn/…jpg" },
auto_out_ms: 8000 } # to air
6. singular_poll_onair_state { app: "evening-news" }An agent can drive this from a rundown/PDF: extract stories + image URLs, map them to templates, and play them to air.
Security
App tokens are bearer-equivalent secrets. They are stored server-side in the registry (file mode
0600, dir0700), never returned to the model, and redacted from logs (including inside request URLs).Host images on your own reachable URL; there is no upload API.
stdio logging is on stderr only (stdout is the JSON-RPC channel).
Limitations (from Singular's API, not this server)
Authoring compositions, listing an account's assets, minting tokens, uploading media, server-side rendering, and webhooks are all unavailable via public API. On-air awareness is by polling. Auto-out scheduling is in-memory (not restart-safe). See docs/SINGULAR_API.md.
Roadmap
Optional Streamable HTTP transport.
Data Node tools over the
apiv1Data Node API.Batch rundown ingestion helpers (PDF/rundown → items).
In-memory control-model cache with TTL.
Development
npm run dev # tsx watch
npm run typecheck # tsc --noEmit
npm run inspector # MCP Inspector against the built serverAvailable Tools
25 toolssingular_animate_stateAnimate sub-compositions In/OutA
Trigger animation state transitions to take sub-compositions on or off air. Accepts multiple targets for a coordinated transition in one call. This does NOT change content — combine with update_content first, or use update_and_animate to do both atomically.
Args: app/appToken; targets: [{ subCompositionName | subCompositionId, state: In|Out|Out1|Out2 }]; response_format. Returns { success, count }.
Inputs: targets (required), app, appToken, response_format.
singular_check_image_urlValidate an image URLARead-onlyIdempotent
Check that an image URL is well-formed, reachable, and looks like an image (by Content-Type or extension) before using it in set_image. Useful for pre-flighting rundown graphics.
Args: imageUrl; response_format. Returns { ok, status, contentType, reason }.
Inputs: imageUrl (required), response_format.
singular_find_nodesFind control nodes by type or titleARead-onlyIdempotent
Search all control nodes across the app's compositions, filtered by type and/or title substring. E.g. type='image' locates every fillable image node (the common newsroom case), type='text' for headlines. Saves hand-parsing the model tree.
Args: app/appToken; type (optional, e.g. 'image'); title_contains (optional); response_format. Returns { count, nodes: [{ subCompositionId, subCompositionName, id, title, type }] }.
Inputs: app, type, appToken, title_contains, +1 more.
singular_get_app_metadataGet control-app metadataARead-onlyIdempotent
Get an app instance's metadata: numeric ids, name, folder, linked composition id, thumbnail preview URL, live output URLs, and the derived control/command/model API URLs.
Args: app (alias) or appToken; response_format. Returns { id, name, accountId, compositionId, folder, thumbnail, outputUrl, broadcastOutputUrl, ...URLs }. Use this to confirm a token points at the intended app and to grab a preview/output URL.
Inputs: app, appToken, response_format.
singular_get_control_stateGet current live control stateARead-onlyIdempotent
Get the current on-air/staged state per sub-composition: its animation state (In/Out/Out1/Out2) and the current payload values keyed by control-node id. Use to see what is live right now.
Args: app/appToken; response_format. Returns { entries: [{ subCompositionId, subCompositionName, state, mainComposition, payload }] }.
Inputs: app, appToken, response_format.
singular_get_modelGet control model (structure + node types)ARead-onlyIdempotent
Get the structural model of an app: every sub-composition and its control nodes with id, title, type (lowercase: text, textarea, number, image, color, checkbox, audio, json, timecontrol, button), default/reset values, and index. This is the schema-discovery call to run BEFORE writing content — payload keys must be control-node ids from here.
Args: app/appToken; sub_composition (optional name/id filter); response_format. Returns { subCompositions: [{ id, name, state, nodeCount, nodes: [{ id, title, type, index, defaultValue, resetValue }] }] }.
Inputs: app, appToken, response_format, sub_composition.
singular_get_output_urlsGet app output & preview URLsARead-onlyIdempotent
Return the app instance's live output URL, broadcast output URL, and auto-generated thumbnail preview URL (from metadata). Hand these to a human or a browser renderer to see what's on air. Note: Singular renders client-side; there is no server-side frame/snapshot render API.
Args: app/appToken; response_format. Returns { outputUrl, broadcastOutputUrl, thumbnail, name }.
Inputs: app, appToken, response_format.
singular_list_appsList registered appsARead-onlyIdempotent
List app aliases registered on this server (names + when added; never tokens). Args: response_format. Returns { count, apps: [{ alias, name, addedAt }] }.
Inputs: response_format.
singular_list_data_streamsList registered data streamsARead-onlyIdempotent
List the aliases of data streams registered on this server (no tokens). Args: response_format. Returns { count, dataStreams: [{ alias, addedAt }] }.
Inputs: response_format.
singular_list_rundown_templatesList rundown templatesARead-onlyIdempotent
List mapped rundown templates with their app, sub-composition, and field maps. Args: response_format. Returns { count, templates: [...] }.
Inputs: response_format.
singular_list_subcompositionsList sub-compositions (structure + current values)ARead-onlyIdempotent
Convenience read that joins the model (structure + types) with the live control state (current values) by sub-composition id — one flat view per sub-composition with each node's id, title, type, and current value. The best single call to understand "what can I fill and what's in it now".
Args: app/appToken; response_format. Returns { subCompositions: [{ id, name, state, nodes: [{ id, title, type, currentValue, defaultValue, resetValue }] }] }.
Inputs: app, appToken, response_format.
singular_map_rundown_templateMap a rundown item type to a templateA
Bind a rundown item type (e.g. 'lower-third', 'ots', 'full-frame', 'ticker') to a specific app + sub-composition, plus a field map from story field names → control-node ids. Used by prepare_item and play_rundown_item. By default validates the sub-composition and node ids against the live model.
Args: name; appAlias; subCompositionName | subCompositionId; fieldMap (storyField → nodeId); validate (default true); response_format. Returns { name, appAlias, subComposition, fieldCount, validation }.
Inputs: name (required), appAlias (required), fieldMap (required), validate, +3 more.
singular_play_rundown_itemTake a rundown item to airA
Resolve a rundown item from its template and take it to air: fills the mapped payload and animates the sub-composition In, atomically. Optionally schedules an automatic Out after auto_out_ms (server-side, in-memory — does not survive a restart).
Args: template (name); fields (storyField → value); auto_out_ms (optional); response_format. Returns { template, subComposition, sentPayload, state, autoOutMs, unmappedFields, missingNodes }.
Inputs: fields (required), template (required), auto_out_ms, response_format.
singular_poll_onair_statePoll which sub-compositions are on airARead-onlyIdempotent
Poll the live control state and report which sub-compositions are currently In (on air) vs Out, plus what changed since the last poll of this app (Singular has no webhooks, so polling is the only awareness mechanism).
Args: app/appToken; response_format. Returns { onAir: [...], offAir: [...], changed: [{ subComposition, from, to }] }.
Inputs: app, appToken, response_format.
singular_prepare_itemResolve a rundown item (dry run — does not go to air)ARead-onlyIdempotent
Resolve a rundown item against its template WITHOUT sending anything: maps story fields to control-node ids, validates them against the live model, and returns exactly the payload that play_rundown_item would send. Use to preview/verify before air.
Args: template (name); fields (storyField → value); response_format. Returns { template, subComposition, payload, unmappedFields, missingNodes, wouldSend }.
Inputs: fields (required), template (required), response_format.
singular_push_datastreamPush data to a data streamA
PUT a JSON payload to a data stream for sub-300ms delivery into any composition linked to it (scores, clocks, live stats) — bypasses the control API's per-minute rate limits. The payload shape must match what the linked sub-composition expects (configured in the Dashboard). Hard limit: 60 KB per package.
Args: stream (alias) or privateToken; data (JSON object); response_format. Returns { success, bytes }.
Inputs: data (required), stream, privateToken, response_format.
singular_register_appRegister a control-app token under an aliasA
Store a Singular app token under a friendly alias so later tools reference the app by alias (the raw token is never returned to the model). By default verifies the token by fetching metadata and uses the app's real name.
Args: alias; token; name (optional); verify (default true); response_format. Returns { alias, name, verified }.
Inputs: alias (required), token (required), name, verify, +1 more.
singular_register_data_streamRegister a data-stream private tokenA
Store a data stream's PRIVATE token under an alias so push_datastream can reference it by name (the raw token is never echoed back). Create the stream and get its private token from the Dashboard → Data Stream Manager.
Args: alias; privateToken; response_format. Returns { alias }.
Inputs: alias (required), privateToken (required), response_format.
singular_remove_appRemove a registered appA
Delete an app alias (and its stored token) from the registry. Args: alias; response_format. Returns { alias, removed }.
Inputs: alias (required), response_format.
singular_reset_nodesReset control nodes to their reset valuesADestructive
Clear/reset content by writing each targeted node back to its resetValue (from the model). Target specific nodeIds, or omit them to reset every node in the sub-composition — handy to blank a template between stories/segments. This changes on-air content if the sub-composition is live.
Args: app/appToken; subCompositionName | subCompositionId; nodeIds (optional — default all nodes in the sub-composition); response_format. Returns { success, subComposition, resetCount, nodeIds }.
Inputs: app, nodeIds, appToken, response_format, +2 more.
singular_server_infoSingular MCP server infoARead-onlyIdempotent
Report this server's runtime configuration and health without revealing secrets. Shows the API base URLs, whether a default token is set (boolean only), registry location and counts, and the HTTP timeout.
Args: response_format. Returns { server, version, apiBase, datastreamBase, defaultAppTokenConfigured, registryPath, registeredApps, registeredDataStreams, rundownTemplates, httpTimeoutMs }.
Inputs: response_format.
singular_set_imageSet an image control node by URLA
Set an image-type control node to a hosted image URL (the only way to place imagery — Singular has no upload API, so the asset must already be reachable at a public URL). Validates that the target node is type 'image' (via get_model) and, by default, that the URL is reachable and looks like an image before sending.
Args: app/appToken; subCompositionName | subCompositionId; nodeId (the image node's id) OR nodeTitle (matched from the model); imageUrl; validate_url (default true); force (default false — set true to send even if validation fails); response_format. Returns { success, subComposition, nodeId, imageUrl, urlCheck }.
Inputs: imageUrl (required), app, force, nodeId, +6 more.
singular_take_out_allTake out all output (clear the app)ADestructive
Animate every overlay in the control app to its Out state — the 'clear the screen' / segment-end / panic control. Uses POST /command { action: 'TakeOutAllOutput' } (the only documented command action).
Args: app/appToken; response_format. Returns { success }.
Inputs: app, appToken, response_format.
singular_update_and_animateFill content and animate in one callA
Atomically set a sub-composition's payload AND its animation state in a single PATCH, so the template is filled and taken to air together (avoids a flash of stale content that a separate fill-then-animate can cause).
Args: app/appToken; subCompositionName | subCompositionId; payload (control-node id → value); state (In|Out|Out1|Out2); response_format. Returns { success, subComposition, state }.
Inputs: state (required), payload (required), app, appToken, +3 more.
singular_update_contentUpdate control-node contentA
Fill control nodes of one or more sub-compositions in a single PATCH. Each update targets a sub-composition (by name or id) and supplies a payload mapping control-node ids → values.
Value formats by node type: text/textarea→string, number→number, image/audio→URL string, color→{r,g,b,a} (0–255) or hex string, checkbox→boolean, selection→option value, json→object. Node ids come from get_model / find_nodes.
Batching multiple sub-compositions here (rather than many calls) conserves rate limit. This sets content only — it does NOT animate; use animate_state or update_and_animate to take on/off air.
Args: app/appToken; updates: [{ subCompositionName | subCompositionId, payload }]; response_format. Returns { success, updatedCount }.
Inputs: updates (required), app, appToken, response_format.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 25 tools
Every tool has a clearly distinct purpose—from registration and discovery to content updates, animation, and rundown management. Even similar tools like update_content, update_and_animate, set_image, and reset_nodes are clearly differentiated by their descriptions and use cases.
All 25 tools follow a consistent singular_verb_noun pattern. Verbs like 'list', 'get', 'register', 'update' are used uniformly, and compound nouns (e.g., 'onair_state', 'output_urls') maintain readability without mixing conventions.
At 25 tools, the count is slightly above the ideal range (3–15), but each tool serves a necessary function within the complex domain of broadcast graphics control. The set feels comprehensive without being bloated.
The tool surface covers registration, discovery, content manipulation, animation, rundown workflow, and server introspection. Minor gaps exist—no tool to remove data streams or delete rundown templates—but these are secondary and do not critically impede core workflows.
Related MCP Connectors
Manage SRG+ hubs, channels, content, assets, users, and workspaces from any MCP-aware AI agent.
Build and run visual creative-production workflows from your AI agent.
Manage 24/7 live streams, media, playback queues, schedules, and multistreaming from AI assistants.
AI media built for production. AI orchestration for image, video and audio, in any AI app.
Related MCP Servers
- AlicenseDqualityBmaintenanceEnables AI assistants to control Unreal Engine via Remote Control API for game development automation, including asset management, actor control, level editing, animation, physics, visual effects, and cinematics creation through natural language.11,219 npm1,032MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI models to fully automate and control the OpenCut Video Editor through a comprehensive set of MCP tools for timeline manipulation, scene management, media handling, and rendering.71 npm10MIT
- AlicenseCqualityDmaintenanceEnables AI assistants to manage digital signage infrastructure through the Revel Digital REST API, allowing natural language control of devices, content, playlists, and system monitoring.6328 npmApache 2.0
- FlicenseNot gradedqualityBmaintenanceEnables AI co-pilots to interact with TradingView charts, manage alerts via REST API, automate morning briefs with custom trading rules, and perform real-time market analysis.-