Connect an AI agent (MCP)
Connect Claude, ChatGPT, Claude Code, Cursor or the Claude API to Brix over MCP. Sign in from the app or use an API key. Endpoint, setup and tools.
Brix runs a Model Context Protocol (MCP) server. Connect an agent once and it can find your screens, explain why one is blank, cast content, build playlists and pull proof-of-play and uptime reports.
Connection details
| Setting | Value |
|---|---|
| Endpoint | https://api.brixsignage.com/v1/mcp |
| Transport | Streamable HTTP. POST one JSON-RPC 2.0 message; the reply is a JSON response. No SSE stream. |
| Auth | Sign in with OAuth 2.1 (claude.ai, ChatGPT and other connector apps), or Authorization: Bearer <api key> with the same keys as the REST API. |
| Protocol versions | 2024-11-05, 2025-03-26, 2025-06-18 |
| Rate limit | 30 requests per minute per caller, inside the workspace’s 600 per minute. Over it: HTTP 429 with Retry-After. |
| Methods | initialize, ping, tools/list, tools/call, resources/list, resources/templates/list, resources/read, prompts/list, prompts/get |
There are two ways to connect:
- Sign in (Claude, ChatGPT). In Claude, find Brix in the connector directory. In ChatGPT, add the endpoint as a plugin. In Claude you can also add it as a custom connector. The app opens a Brix page where you sign in and choose what it may do. No key to copy.
- API key (Claude Code, Cursor, Claude Desktop config file, the Claude and OpenAI APIs). Create a key and put it in the client’s config.
Either way, the permissions you pick decide what the agent can do. See Authentication and scopes.
Find Brix in Claude’s connector directory
Brix is listed in Claude’s connector directory: claude.ai/directory/connectors/brix.
- In Claude, go to Customize > Connectors, click + and search for Brix. Or open the listing link above.
- Click Connect.
- Claude opens a Brix page. Sign in, then pick the workspace, a location if you want to limit Claude to one, and the permission areas. Click Allow.
This works in claude.ai, Claude Desktop and the Claude mobile apps, which share the same connectors. On a Team or Enterprise plan, an owner first adds Brix under Organization settings > Connectors, then each member connects it from Customize > Connectors.
Add Brix by URL (ChatGPT plugin, or Claude custom connector)
Use these steps for ChatGPT, or in Claude if you prefer to add the endpoint yourself.
- Copy the endpoint:
https://api.brixsignage.com/v1/mcp. - Add it to the app.
- Claude: Customize > Connectors, click +, then Add custom connector. Enter a name and the endpoint, then click Add.
- ChatGPT: first turn on Developer mode under Settings > Security and login (your workspace policy can hide it). Then go to chatgpt.com/plugins and select +. Enter a name and description, paste the endpoint under Connection, and create it.
- The app opens a Brix page. Sign in, then pick the workspace, a location if you want to limit the app to one, and the permission areas. Click Allow.
The app acts as you, with only the areas you picked. Viewing screens is always included, so if you pick no areas, the app can only view screens. You cannot give the app a permission you do not hold.
Whatever areas you pick, an app connected by signing in cannot use billing, payment, invoice, API key, single sign-on, user or role routes. A person does these in the CMS. API keys are not affected.
Every connection is listed in the CMS under Settings > AI, API & MCP > Connected apps, with who connected it and when it was last used. Revoke ends it at once: the app’s next call is refused and it has to be connected again.
For client developers: the server follows the MCP authorization spec. A call without a token gets 401 with a WWW-Authenticate header that points to the protected-resource metadata at /.well-known/oauth-protected-resource/v1/mcp. The authorization server is https://api.brixsignage.com (metadata at /.well-known/oauth-authorization-server). It supports authorization code with PKCE (S256), dynamic client registration and client ID metadata documents. Access tokens last an hour. Refresh tokens rotate on every use. An access token from this flow works only at /v1/mcp.
API key clients
1. Create a key for the agent
In the CMS: Settings > AI, API & MCP > API keys > New key. Give it only the areas the agent needs. For a first try, a view-only key (Screens, Playlists, Schedules, Files, Proof of Play) is safe: the agent can answer questions but cannot change what plays.
The same page has a Model Context Protocol (MCP) section with the endpoint and Copy config and Copy command buttons.
Claude Code
claude mcp add --transport http brix https://api.brixsignage.com/v1/mcp \
--header "Authorization: Bearer $BRIX_API_KEY"
Then run claude and ask: “Which of my Brix screens are offline, and why?” Check the connection with /mcp.
Claude Desktop
To connect by signing in, connect Brix from Claude’s connector directory (see above). To use a key instead: Claude Desktop’s config file starts local (stdio) servers, so connect through the mcp-remote bridge. It needs Node.js. Open Settings > Developer > Edit Config and add:
{
"mcpServers": {
"brix": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://api.brixsignage.com/v1/mcp",
"--header", "Authorization:${BRIX_AUTH}"
],
"env": { "BRIX_AUTH": "Bearer ak_us_live_..." }
}
}
}
Restart Claude Desktop. The header value goes through env because some platforms split arguments that contain spaces.
Cursor
Add to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project):
{
"mcpServers": {
"brix": {
"url": "https://api.brixsignage.com/v1/mcp",
"headers": { "Authorization": "Bearer ${env:BRIX_API_KEY}" }
}
}
}
${env:BRIX_API_KEY} reads the key from your environment, so it stays out of the file. You can also paste the key in place of it. Open Cursor Settings > MCP to check that brix is connected.
Other clients that accept a remote URL with custom headers use the same shape. This is the config that Copy config in the CMS gives you:
{
"mcpServers": {
"brix": {
"url": "https://api.brixsignage.com/v1/mcp",
"headers": { "Authorization": "Bearer <YOUR_API_KEY>" }
}
}
}
OpenAI API
The OpenAI Responses API sends your key in a header to a remote MCP server:
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5",
"tools": [{
"type": "mcp",
"server_label": "brix",
"server_url": "https://api.brixsignage.com/v1/mcp",
"headers": { "Authorization": "Bearer '"$BRIX_API_KEY"'" },
"require_approval": "never"
}],
"input": "Which of my screens are offline?"
}'
"require_approval": "never" lets the model call tools without a pause. Use it with a view-only key, or set it to "always" and approve each call.
Claude API
The Messages API connects to remote MCP servers with the MCP connector:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 4096,
"mcp_servers": [{
"type": "url",
"name": "brix",
"url": "https://api.brixsignage.com/v1/mcp",
"authorization_token": "'"$BRIX_API_KEY"'"
}],
"tools": [{ "type": "mcp_toolset", "mcp_server_name": "brix" }],
"messages": [{ "role": "user", "content": "Which of my screens are offline?" }]
}'
Test the endpoint with curl
curl https://api.brixsignage.com/v1/mcp \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
A tool result comes back as a text block plus structuredContent. A failed tool call returns isError: true with the error text, so the agent can correct itself.
How the tools behave
- Start with
whoamiin an unfamiliar workspace. It returns the key, the workspace and its permissions. search_screensandsearch_mediaresolve names to real ids. Never guess an id.cast_contentshows existing content for a limited time (default one hour).assign_contentchanges what a screen plays by default.quick_postwrites an announcement and casts it in one step.list_api_routesfinds any documented REST endpoint the key allows, for anything without a dedicated tool.api_getreads it. Writes are separate tools:api_create(POST),api_update(PATCH or PUT) andapi_delete. Key clients set up before this split can still callcall_api; connections made by signing in cannot.- Every tool declares MCP annotations: read-only, destructive, idempotent, and whether it reaches outside Brix. Clients use them to decide when to ask you first.
- Content created over MCP lands as a draft where the location requires approval.
- Changes made over MCP are recorded in the workspace audit log. Reads are not.
Things to ask
- “Which of my screens are offline, and why?”
- “Why is the lobby screen blank? Show me what it’s displaying right now.”
- “Put the Summer Menu playlist on every screen at the Downtown location for the next two hours.”
- “How many times did the Burger Promo play last week, and on which screens?”
- “Give me this month’s uptime report, worst screens first.”
Your AI app asks before it runs a tool that changes what a screen shows.
Privacy, disconnecting and support
- The app sees only what the tools return for the workspace, location and permission areas you approved. Brix does not receive your conversation with the app, only the tool calls it makes.
- Disconnect any time in Brix under Settings > AI, API & MCP > Connected apps. Revoke ends the connection at once: the app’s next call is refused. Removing the connector or plugin in the app stops the app using it.
- A connection record is deleted 30 days after the connection ends (revoked, refresh token expired, or sign-in never completed). The audit log entries stay for the audit log’s normal retention period.
- Brix does not use data accessed through connected apps to train AI models. The app’s provider handles what the app receives under its own terms.
- How Brix handles data is in the Privacy Policy. For help, email [email protected].
- Service status is at status.brixsignage.com.
- To report a security problem, see Reporting a vulnerability.
Tool catalog
41 tools. Each needs the permission shown on the API key. Read-only tools never change anything; the others say what they change.
| Tool | Permission | Behavior |
|---|---|---|
capture_device_frame | screen.edit | creates |
screen_health | screen.view | read-only |
list_open_alerts | screen.view | read-only |
assign_content | screen.cast | changes existing data, idempotent |
send_command | screen.edit | changes existing data |
trigger_emergency_template | screen.edit | changes existing data |
upload_media_from_url | media.create | creates, reaches outside Brix |
mint_upload_url | media.create | creates |
confirm_media_upload | media.create | creates |
bulk_create | screen.view | creates |
search_screens | screen.view | read-only |
assign_screen_to_node | screen.edit | changes existing data, idempotent |
create_creatives | creative.create | creates |
whoami | screen.view | read-only |
list_api_routes | screen.view | read-only |
api_get | screen.view | read-only |
api_create | screen.view | changes existing data |
api_update | screen.view | changes existing data |
api_delete | screen.view | changes existing data, idempotent |
search_media | media.view | read-only |
list_emergency_templates | emergency-override.view | read-only |
list_pending_approvals | screen.view | read-only |
list_recycle_bin | screen.view | read-only |
update_playlist | playlist.edit | changes existing data |
update_media | media.edit | changes existing data, idempotent |
create_web_link | media.create | creates, reaches outside Brix |
configure_web_link | media.edit | creates, idempotent |
render_web_link | media.edit | creates, idempotent, reaches outside Brix |
restore_recycle_bin_item | screen.view | creates, idempotent |
decide_approval | screen.view | changes existing data |
cast_content | screen.cast | changes existing data, idempotent |
end_cast | screen.cast | changes existing data, idempotent |
quick_post | screen.cast | changes existing data |
create_screen_group | screen.edit | creates |
add_screens_to_group | screen.edit | creates, idempotent |
remove_screens_from_group | screen.edit | changes existing data, idempotent |
sync_data_source | integration.edit | creates, reaches outside Brix |
refresh_all_data_sources | integration.edit | creates, reaches outside Brix |
get_proof_of_play | playback-log.view | read-only |
get_uptime_report | screen.view | read-only |
search_audit_log | audit-log.view | read-only |
capture_device_frame
Ask a screen to capture a screenshot now and return the newest stored frame — `latestScreenshotId`, `latestFrameUrl`, `latestFrameAt`, and `fresh` (true when the frame post-dates this request). To see the picture, pass `latestFrameUrl` to api_get: the image comes back as { contentType, totalBytes, bodyBase64 }. Use to see what's actually on the panel.
| Argument | Type | Required | Description |
|---|---|---|---|
screenId | string | yes |
screen_health
Read one screen's health. `view` picks what comes back. 'why': why it shows what it shows — the resolution chain (deactivated / emergency / cast / scheduled / default). 'diagnose': for a screen that looks blank or wrong, one likely cause reasoned from verifiable signals plus a next-step checklist; the player cannot read TV/HDMI state, so a healthy stick gets a `guided: true` TV/input/cable checklist, a guess and not a detection. 'outages': every outage in the last `days` (default 30) with how long and a plain-language cause, plus a rollup sentence; planned darkness is listed but not counted. 'tv': what the TV panel did (switched off, other HDMI input, cable lost, back on) and its current state; only a Signage Stick over CEC produces rows; default window 14 days. For the fleet or a time window use get_uptime_report; for a live picture use capture_device_frame. Requires `screen.view`.
| Argument | Type | Required | Description |
|---|---|---|---|
screenId | string | yes | The screen ID, e.g. scr_aurora_42 |
view | why | diagnose | outages | tv | yes | |
days | number | no | outages and tv only: window in days. |
limit | number | no | outages and tv only: max rows; default 50. |
list_open_alerts
List currently-open alert events, newest first. Optionally filter by severity, screen or org node. Paginate with offset; the response reports total + hasMore.
| Argument | Type | Required | Description |
|---|---|---|---|
severity | info | warning | critical | no | |
screenId | string | no | |
nodeId | string | no | Limit to alerts raised at (or under) this org node. |
offset | integer | no | Skip this many matching alerts (default 0). |
assign_content
Push a playlist, schedule, app, media or layout to a screen — or to MANY screens at once via screenIds. Requires `screen.cast`.
| Argument | Type | Required | Description |
|---|---|---|---|
screenId | string | no | Single target screen. |
screenIds | array | no | Bulk alternative: assign the same content to every listed screen (max 200). |
contentKind | playlist | schedule | app | media | yes | |
contentId | string | no |
send_command
Send a runtime command (reboot, refresh, screenshot, clear-cache, cec-on, cec-off) to one screen or many (screenIds). Reboot interrupts what's on screen — confirm with the operator when unsure.
| Argument | Type | Required | Description |
|---|---|---|---|
screenId | string | no | |
screenIds | array | no | Bulk alternative: command every listed screen (max 200) in one call. |
kind | reboot | refresh | screenshot | clear-cache | cec-on | cec-off | yes |
trigger_emergency_template
Fire a pre-armed emergency template (Lockdown, Severe weather, Fire drill, …) across its in-scope screens — an immediate takeover. List ids first with the brix://emergency-templates resource; confirm scope with the operator.
| Argument | Type | Required | Description |
|---|---|---|---|
templateId | string | yes |
upload_media_from_url
Download a file from a public URL into Brix media. Returns the new asset id + URL. Use for migration — pull from a Drive share, S3, the customer's old-CMS export, etc. Max 500 MB; use mint_upload_url for larger.
| Argument | Type | Required | Description |
|---|---|---|---|
url | string | yes | Publicly fetchable http(s) URL. |
name | string | yes | Display name for the asset. |
folderId | string | no | |
tags | array | no |
mint_upload_url
Get a one-shot upload URL for a large file. The model's host PUTs bytes to it then calls confirm_media_upload with the same `uploadId`.
| Argument | Type | Required | Description |
|---|---|---|---|
contentType | string | yes | |
name | string | yes |
confirm_media_upload
Finalize an upload after PUTting bytes to a mint_upload_url. Creates the media_assets row.
| Argument | Type | Required | Description |
|---|---|---|---|
uploadId | string | yes | |
folderId | string | no | |
tags | array | no |
bulk_create
Create many records of one kind in one call — the migration and import tool. Pass exactly ONE of: `playlists` (max 500; items reference media or app-instance ids that already exist; needs `playlist.create`), `schedules` (max 200, up to 50 blocks each; times are HH:MM 24-hour in the SCREEN's timezone; needs `schedule.create`), `locations` (org-tree nodes, max 1000; each hangs off `parentId`, `parentExternalId` — an existing node's Location ID or an EARLIER node in this call — or the Space root; a node whose externalId already exists is skipped, so re-running is safe; needs `org-unit.create`), `celebrations` (Celebrations-app birthdays/anniversaries, max 100, each needs a name plus month/day; one bad row is reported, not fatal; needs `integration.create`).
| Argument | Type | Required | Description |
|---|---|---|---|
playlists | array | no | |
schedules | array | no | |
locations | array | no | |
celebrations | array | no |
search_screens
Filter the screen fleet. All criteria optional and AND-ed. Returns up to 200 per page; the response carries total + hasMore so you can page with `offset` instead of re-querying.
| Argument | Type | Required | Description |
|---|---|---|---|
q | string | no | Substring match on screen name. |
status | online | offline | pairing | no | |
nodeId | string | no | Limit to screens at (or under) this org node. |
tag | string | no | |
offset | integer | no | Skip this many matches (default 0). |
assign_screen_to_node
Move a screen to a different org node — useful for post-migration cleanup. Requires `screen.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
screenId | string | yes | |
nodeId | string | no | null to clear; otherwise the destination node id. |
create_creatives
Author new creatives (data-bound canvases) from scratch. This is how you BUILD templates programmatically. Each: { name, stage?:'landscape'|'portrait', backgroundUrl?, dataSourceId?, nodeId?, boxes:[…] }. A box is { kind, x, y, w, h, … } where x/y/w/h are 0–1 fractions of the stage and kind is one of text|field|list|image|shape|celebrations|event-board|signage|group|slot. Shapes use { shape:'rect'|'circle'|'line'|'triangle', fill, stroke, strokeWidth, cornerRadius }; text uses { text, color, fontWeight, align, fontSize? }; field/list bind via { field:'dot.path' }; image uses { url }; slot is a transparent hole the screen fills with real content, like a layout zone — { slot: { kind:'media'|'playlist'|'app', id, name } } naming a file / playlist / app in THIS workspace (or slot:null for an empty hole the customer fills later; borderRadius clips what plays there); signage is a full Signage Template Engine block — { signage: { archetype, style, mode, industry, orientation, content, anim } } — rendered 1:1 (layout + animations), usually one full-bleed box (x:0,y:0,w:1,h:1). Boxes are validated + clamped server-side; an invalid box rejects the whole call. Max 100 creatives per call. Designs also pass a quality gate (the Brix template standard): REJECTED with named findings when text pins under 24px (1920w scale), an authored contrast pair computes <3:1, or one layer fully hides another; WARNED as structured diagnostics for busy layouts (>36 content layers), >5 font sizes, >2 font families, hand-frozen list rows that should be ONE list box, static clock/date copy instead of {{now.*}} tokens, and competing loop animations. Treat warnings as defects: fix and re-emit.
| Argument | Type | Required | Description |
|---|---|---|---|
creatives | array | yes |
whoami
Who this connection is and what it may do. Useful at the start of work in an unfamiliar workspace. Returns the actor (an API key with the org node it is pinned to, or the signed-in user), the workspace id + name, and the flat permission set. A pinned key simply cannot see or write outside its branch, so this is usually the answer to an unexplained 403 or an empty list. The permission set is a planning hint: node-precise checks still run on every write.
list_api_routes
Lists the Brix REST API (https://brixsignage.com/developers/api/): every documented endpoint as { method, path, description }. Everything the CMS UI can do lives behind one of these routes. A GET route is read with api_get; POST, PUT/PATCH and DELETE routes go through api_create, api_update and api_delete. Narrow with `prefix` (e.g. '/v1/screens') or free-text `q` (matched against path + description). One page is 200 routes and the surface is larger than that: while the reply carries `nextOffset`, call again with that `offset` to see the rest.
| Argument | Type | Required | Description |
|---|---|---|---|
prefix | string | no | Path prefix filter, e.g. '/v1/playlists'. |
q | string | no | Case-insensitive substring match on path or description. |
offset | number | no | Skip this many matching routes — pass the `nextOffset` from the previous reply to page through the whole surface. |
api_get
Sends a GET request to the Brix REST API (reference: https://brixsignage.com/developers/api/) with this connection's own permissions and returns { status, body }. Find routes with list_api_routes. Query strings may be included in `path`. A route that answers with bytes (a media file, a poster, an invoice PDF, a screenshot) comes back as { contentType, totalBytes, bodyBase64 }. A 4xx/5xx returns the API's error body.
| Argument | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute API path starting with /v1/ with real ids in place of :params, optionally with ?query. |
api_create
Sends a POST request to the Brix REST API (reference: https://brixsignage.com/developers/api/) with this connection's own permissions: creates a record or runs an action (some POST routes act on screens, e.g. commands). Find POST routes with list_api_routes. Pass a JSON `body`, or `bodyBase64` + `contentType` for the few routes that take raw bytes; whole media files: upload_media_from_url (or mint_upload_url with an API key). Pass `idempotencyKey` on a create you might retry and the API dedupes on it. Returns { status, body }; a 4xx/5xx returns the API's error body.
| Argument | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute API path starting with /v1/ with real ids in place of :params, optionally with ?query. |
body | object | array | no | JSON request body. Sent as application/json. Ignored when `bodyBase64` is present. |
bodyBase64 | string | no | Raw request body, base64-encoded, for the routes that take bytes (POST /v1/media/:id/poster, /captions/upload, /captions/transcribe, PUT /v1/media/upload/:uploadId). Requires `contentType`. Capped at ~6 MB encoded. |
contentType | string | no | Content-Type for `bodyBase64`, e.g. image/jpeg, text/vtt, audio/wav. Required with bodyBase64. |
idempotencyKey | string | no | Forwarded as the `idempotency-key` header so a retried create cannot duplicate. Use any stable unique string per logical create. |
api_update
Sends a PATCH (default) or PUT request to the Brix REST API (reference: https://brixsignage.com/developers/api/) with this connection's own permissions, changing an existing record. Find PATCH/PUT routes with list_api_routes. Pass a JSON `body`, or `bodyBase64` + `contentType` for PUT /v1/media/upload/:uploadId. Returns { status, body }; a 4xx/5xx returns the API's error body.
| Argument | Type | Required | Description |
|---|---|---|---|
method | PATCH | PUT | no | PATCH (the default) or PUT, as the route lists it. |
path | string | yes | Absolute API path starting with /v1/ with real ids in place of :params, optionally with ?query. |
body | object | array | no | JSON request body. Sent as application/json. Ignored when `bodyBase64` is present. |
bodyBase64 | string | no | Raw request body, base64-encoded, for the routes that take bytes (POST /v1/media/:id/poster, /captions/upload, /captions/transcribe, PUT /v1/media/upload/:uploadId). Requires `contentType`. Capped at ~6 MB encoded. |
contentType | string | no | Content-Type for `bodyBase64`, e.g. image/jpeg, text/vtt, audio/wav. Required with bodyBase64. |
api_delete
Sends a DELETE request to the Brix REST API (reference: https://brixsignage.com/developers/api/) with this connection's own permissions. Most content deletes move the item to the recycle bin for 30 days (see list_recycle_bin); a purge route deletes permanently. Find DELETE routes with list_api_routes. Returns { status, body }; a 4xx/5xx returns the API's error body.
| Argument | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute API path starting with /v1/ with real ids in place of :params, optionally with ?query. |
search_media
Search the media library by name, kind, folder or tag. Resolves media names to asset ids before building playlists, casting or assigning. Returns id/name/kind/tags/state/duration per asset; results are node-scoped to this key.
| Argument | Type | Required | Description |
|---|---|---|---|
q | string | no | Case-insensitive substring match on asset name. |
kind | string | no | Asset kind filter, e.g. image | video | audio | pdf | web | rss. |
folderId | string | no | Only assets filed in this folder. |
tag | string | no | Exact tag match (case-insensitive). |
limit | integer | no | Page size (default 50). |
offset | integer | no | Skip this many matches (default 0). |
list_emergency_templates
List the pre-armed emergency takeover templates (Lockdown, Severe weather, Fire drill, …) with their ids, scope and severity. Pass an id to trigger_emergency_template — never guess one.
list_pending_approvals
List approval requests (content awaiting an approve/reject decision before it airs). Defaults to pending; pass state to see decided history. Content created over MCP lands as 'draft' when the location requires approval — check here to see what's waiting.
| Argument | Type | Required | Description |
|---|---|---|---|
state | pending | approved | rejected | withdrawn | no | Filter by decision state (default pending). |
limit | integer | no | Page size (default 50). |
list_recycle_bin
List soft-deleted items across every content kind within the 30-day restore window, newest deletion first, with what used them before deletion. Paged (default 50). Filter by kind or name to find one item. Pair with restore_recycle_bin_item for 'undo that delete' requests. Deleted SCREENS appear here too — DELETE /v1/screens/:id soft-deletes into the same 30-day window.
| Argument | Type | Required | Description |
|---|---|---|---|
kind | string | no | Only this kind, e.g. screen, playlist, media, schedule, layout. |
q | string | no | Case-insensitive match on the item's name. |
limit | integer | no | Page size (default 50). |
offset | integer | no | nextOffset from the previous page. |
update_playlist
Edit an EXISTING playlist: rename/describe it, set its play window, toggle shuffle, append items, remove items, patch item duration/fit, or reorder. Compose any subset in one call; each change runs through the same validation + approval invalidation as the CMS editor. Requires `playlist.edit` at the playlist's node.
| Argument | Type | Required | Description |
|---|---|---|---|
playlistId | string | yes | |
name | string | no | New display name. |
description | string | no | |
shuffle | boolean | no | |
fullscreen | boolean | no | When this playlist plays inside a layout, EVERY item fills the whole screen and the other zones hide. Per-item: patchItems[].fullscreen. |
startsAt | string | no | ISO datetime the playlist starts airing; null clears. |
expiresAt | string | no | ISO datetime the playlist stops airing; null clears. |
appendItems | array | no | |
patchItems | array | no | |
removeItemIds | array | no | |
reorderItemIds | array | no | The COMPLETE item order after the move — unknown ids are dropped, the rest keep this sequence. |
update_media
Update a media asset's metadata: rename it, retag it, move it to another folder, set alt text, set how it fills the screen, or give it an expiry (+ optional auto-archive into the recycle bin). Bytes are never touched. Requires `media.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
mediaId | string | yes | |
name | string | no | |
tags | array | no | Replaces the whole tag list. |
folderId | string | no | Destination folder in THIS workspace; null moves to the library root. |
altText | string | no | |
fit | contain | cover | fill | blur-fill | no | How this file fills any screen or zone it plays in: contain (whole picture, black bars), blur-fill (whole picture, blurred edges), cover (fills, crops the edges), fill (fills, distorts). Applies everywhere the file plays; a playlist slot can override it. |
expiresAt | string | no | ISO datetime after which the asset stops airing; null clears. |
autoArchiveOnExpiry | boolean | no | Soft-delete the asset into the recycle bin when it expires. |
create_web_link
Add a public web page or dashboard to the media library so it can be cast, scheduled or added to a playlist. Returns the new mediaId — pass it to configure_web_link to crop the page, set a refresh, or dismiss cookie banners. For a page only the screen's own network can reach, set onScreenNetwork: the address is stored but Brix's cloud never fetches it and the screen opens it directly. Requires `media.create`.
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | What operators will see in the library. |
url | string | yes | http(s) address. A private/loopback address is refused unless onScreenNetwork is true. |
folderId | string | no | Library folder; omit for the root. |
tags | array | no | |
onScreenNetwork | boolean | no | The page lives on the screen's own network (an intranet dashboard, a device web UI). |
configure_web_link
Change how a web link is rendered for screens: show only one part of the page, auto-dismiss cookie banners, auto-scroll a long page, set the refresh cadence, or set the browser size it is rendered at. Only the fields you pass are changed; a recorded sign-in is preserved. `showOnly` takes a CSS selector — everything else on the page is hidden, so a timetable or one dashboard panel fills the screen. Prefer an id (`#schedules`): class names change whenever the site is republished. Cropping needs a server-rendered snapshot, so setting showOnly switches the link to snapshot rendering. Requires `media.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
mediaId | string | yes | |
showOnly | string | no | CSS selector of the one element to show. Empty string shows the whole page again. |
acceptCookies | boolean | no | Click Accept on common cookie / consent / age-gate banners on every load. |
autoScrollPxPerSec | number | no | Scroll a long page top-to-bottom at this speed. 0 stops auto-scrolling. |
refreshSeconds | number | no | How often the page is re-loaded or re-rendered. 0 means never. |
renderWidth | number | no | Browser width the page is rendered at. Match the screen; 1920 by default. |
renderHeight | number | no | Browser height the page is rendered at. 1080 by default. |
render_web_link
Force a fresh server-side snapshot of a web link instead of waiting for its next scheduled refresh. Use it right after configure_web_link to see whether a crop selector actually matched. Only meaningful for snapshot-rendered links — a live page is loaded by the screen itself. Requires `media.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
mediaId | string | yes |
restore_recycle_bin_item
Restore ONE soft-deleted item from the recycle bin (kind from list_recycle_bin). Playlists/screens referencing it resume automatically where they still do. Each kind's own permission applies (e.g. playlist.delete restores playlists). Purging (permanent deletion) is not part of this tool.
| Argument | Type | Required | Description |
|---|---|---|---|
kind | screen | media | playlist | schedule | layout | creative | app | data-source | signage | banner | media-folder | yes | |
id | string | yes |
decide_approval
Approve or reject a pending approval request (from list_pending_approvals). Rejecting requires a reason. Only works when this key's identity is actually an approver for the request's chain — the API enforces that, not this tool.
| Argument | Type | Required | Description |
|---|---|---|---|
approvalId | string | yes | |
decision | approve | reject | yes | |
reason | string | no | Required for reject. |
cast_content
Show EXISTING content on screens as a TIMED takeover (a cast) — screens return to their normal schedule when it expires. Scope it to everything (all), an org node subtree, or explicit screenIds. This is the tool for 'put the lunch menu on the lobby screens until 2pm'. Requires `screen.cast`.
| Argument | Type | Required | Description |
|---|---|---|---|
contentKind | media | playlist | app | creative | schedule | yes | |
contentId | string | yes | |
scopeKind | all | node | screens | no | Default all — every screen this key can reach. |
scopeNodeId | string | no | With scopeKind=node: the org-node subtree to cast across. |
screenIds | array | no | With scopeKind=screens: explicit targets (max 500). |
expiresAt | string | no | ISO datetime the cast ends. Default: one hour from now. |
headline | string | no | Display name recorded for the cast. |
end_cast
End an active cast early — its screens return to scheduled content immediately. Requires `screen.cast`.
| Argument | Type | Required | Description |
|---|---|---|---|
castId | string | yes |
quick_post
Author a headline (+ optional body) announcement and put it on screens in ONE step — the agent twin of Quick Post. Creates a branded takeover creative and casts it across the chosen scope; if the scope reaches no screens, nothing is created. Needs creative.create AND screen.cast.
| Argument | Type | Required | Description |
|---|---|---|---|
headline | string | yes | |
body | string | no | |
tone | announcement | alert | celebrate | info | no | Picks the background treatment. Default announcement. |
scopeKind | all | node | screens | no | Default all. |
scopeNodeId | string | no | |
screenIds | array | no | |
expiresInSec | integer | no | How long the message stays up (default 3600). |
create_screen_group
Create a named, saved cohort of screens (optionally seeding members). Groups are the reusable target for bulk commands, casts and assignments. Requires `screen.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | |
description | string | no | |
screenIds | array | no | Initial membership; foreign/out-of-scope ids are skipped silently. |
add_screens_to_group
Add screens to a saved screen group. Idempotent; out-of-scope ids are skipped, never fatal. Requires `screen.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
groupId | string | yes | |
screenIds | array | yes |
remove_screens_from_group
Take screens out of a saved screen group — only the label drops, the screens themselves are untouched. Requires `screen.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
groupId | string | yes | |
screenIds | array | yes |
sync_data_source
Pull fresh data through one connected feed (CSV, Google Sheets, POS…) now, so data-bound apps and creatives show current values. Requires `integration.edit`.
| Argument | Type | Required | Description |
|---|---|---|---|
dataSourceId | string | yes |
refresh_all_data_sources
Sync EVERY connected data feed in this workspace once. Use before big announcements so menus/prices are current. Requires `integration.edit`.
get_proof_of_play
Proof-of-play rollup for a time window: plays, watch time, skips and failures per piece of content, plus which screens reported. Answers 'how did the promo actually do?'. Requires `playback-log.view`.
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | ISO datetime; default 7 days ago. |
to | string | no | ISO datetime; default now. |
screenId | string | no | Restrict to one screen's report. |
get_uptime_report
Uptime/SLA summary for a window: online % per screen, downtime windows (each with its cause), worst offenders first. Every screen, location and the fleet carry an `outageSummary` — cause counts, the top cause and a plain-language insight such as 'went down 6 times in the last 30 days, all Wi-Fi drops'. Requires `screen.view`.
| Argument | Type | Required | Description |
|---|---|---|---|
from | string | no | ISO datetime; default: the start of the outage-history window (30 days unless the workspace changed it). |
to | string | no | ISO datetime; default now. |
screenId | string | no |
search_audit_log
Read the workspace audit log, newest first — who changed what, including changes made by MCP tools themselves. Keyset-paged via cursor; `since` stops at a date. Requires `audit-log.view`.
| Argument | Type | Required | Description |
|---|---|---|---|
since | string | no | ISO datetime; only events at or after it, and no cursor past it. |
limit | integer | no | Page size (default 50). |
cursor | string | no | nextCursor from the previous page. |
Resources
Read with resources/read. Each one checks its own view permission.
| URI | What it returns |
|---|---|
brix://screens | Every screen in the workspace, with status + location + assigned content. |
brix://playlists | Playlists with their items (refKind/refId/duration), approval state and org node. |
brix://schedules | Time-of-day schedules and which screens they target. |
brix://alerts/open | Currently open alert events — anything offline / storage-critical / playback-failing. |
brix://org-tree | The hierarchical org-node structure (Spaces / districts / schools / rooms). |
brix://media | Images, videos, PDFs, web links and fonts — id, name, kind, folder, tags, state. |
brix://creatives | Canvas creatives (data-bound designs) with stage + box count. |
brix://apps | Installed app instances (weather, RSS, menus…) with their app key + node. |
brix://layouts | Multi-zone layouts a screen can be pointed at, each with its `theme` (the Brand Kit look — { source: "brand", mode?, radius? } — or null for a bare canvas). Zone geometry, bindings and per-zone `frame` / `radius` / `role` come from GET /v1/layouts/:id. |
brix://data-sources | Connected data feeds (CSV, Sheets, POS…) with connection + last-sync status. |
brix://screen-groups | Named saved cohorts of screens — the unit for bulk commands and casts. |
brix://emergency-templates | Pre-armed emergency takeover recipes; pass one's id to trigger_emergency_template. |
brix://approvals/pending | Content awaiting an approve/reject decision before it can air. |
brix://screens/{screenId}/why | The resolution chain (deactivated / emergency / cast / scheduled / default) for one screen. |
brix://playlists/{playlistId} | One playlist WITH its ordered items. |
brix://media/{mediaId}/usage | Where a media asset is used (playlists/creatives/layouts) and when it last played. |
Prompts
| Prompt | What it does | Arguments |
|---|---|---|
fleet_health_report | A daily ops digest: fleet status, open alerts, worst uptime offenders and what to do about each. | none |
diagnose_offline_screen | Walk the likely-cause diagnosis for one screen and hand back a guided next-step checklist. | screen_id (optional) |
onboard_new_location | Set up a new org location end-to-end: create the node, home its screens there and assign starter content. | location_name |
weekly_content_review | Proof-of-play rollup for the last 7 days: top content, skipped/failed plays, unused media worth pruning. | none |