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.

View as Markdown

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

SettingValue
Endpointhttps://api.brixsignage.com/v1/mcp
TransportStreamable HTTP. POST one JSON-RPC 2.0 message; the reply is a JSON response. No SSE stream.
AuthSign 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 versions2024-11-05, 2025-03-26, 2025-06-18
Rate limit30 requests per minute per caller, inside the workspace’s 600 per minute. Over it: HTTP 429 with Retry-After.
Methodsinitialize, 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.

  1. In Claude, go to Customize > Connectors, click + and search for Brix. Or open the listing link above.
  2. Click Connect.
  3. 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.

  1. Copy the endpoint: https://api.brixsignage.com/v1/mcp.
  2. 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.
  3. 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 whoami in an unfamiliar workspace. It returns the key, the workspace and its permissions.
  • search_screens and search_media resolve names to real ids. Never guess an id.
  • cast_content shows existing content for a limited time (default one hour). assign_content changes what a screen plays by default. quick_post writes an announcement and casts it in one step.
  • list_api_routes finds any documented REST endpoint the key allows, for anything without a dedicated tool. api_get reads it. Writes are separate tools: api_create (POST), api_update (PATCH or PUT) and api_delete. Key clients set up before this split can still call call_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.

ToolPermissionBehavior
capture_device_framescreen.editcreates
screen_healthscreen.viewread-only
list_open_alertsscreen.viewread-only
assign_contentscreen.castchanges existing data, idempotent
send_commandscreen.editchanges existing data
trigger_emergency_templatescreen.editchanges existing data
upload_media_from_urlmedia.createcreates, reaches outside Brix
mint_upload_urlmedia.createcreates
confirm_media_uploadmedia.createcreates
bulk_createscreen.viewcreates
search_screensscreen.viewread-only
assign_screen_to_nodescreen.editchanges existing data, idempotent
create_creativescreative.createcreates
whoamiscreen.viewread-only
list_api_routesscreen.viewread-only
api_getscreen.viewread-only
api_createscreen.viewchanges existing data
api_updatescreen.viewchanges existing data
api_deletescreen.viewchanges existing data, idempotent
search_mediamedia.viewread-only
list_emergency_templatesemergency-override.viewread-only
list_pending_approvalsscreen.viewread-only
list_recycle_binscreen.viewread-only
update_playlistplaylist.editchanges existing data
update_mediamedia.editchanges existing data, idempotent
create_web_linkmedia.createcreates, reaches outside Brix
configure_web_linkmedia.editcreates, idempotent
render_web_linkmedia.editcreates, idempotent, reaches outside Brix
restore_recycle_bin_itemscreen.viewcreates, idempotent
decide_approvalscreen.viewchanges existing data
cast_contentscreen.castchanges existing data, idempotent
end_castscreen.castchanges existing data, idempotent
quick_postscreen.castchanges existing data
create_screen_groupscreen.editcreates
add_screens_to_groupscreen.editcreates, idempotent
remove_screens_from_groupscreen.editchanges existing data, idempotent
sync_data_sourceintegration.editcreates, reaches outside Brix
refresh_all_data_sourcesintegration.editcreates, reaches outside Brix
get_proof_of_playplayback-log.viewread-only
get_uptime_reportscreen.viewread-only
search_audit_logaudit-log.viewread-only

capture_device_frame

screen.editcreates

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.

ArgumentTypeRequiredDescription
screenIdstringyes

screen_health

screen.viewread-only

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`.

ArgumentTypeRequiredDescription
screenIdstringyesThe screen ID, e.g. scr_aurora_42
viewwhy | diagnose | outages | tvyes
daysnumbernooutages and tv only: window in days.
limitnumbernooutages and tv only: max rows; default 50.

list_open_alerts

screen.viewread-only

List currently-open alert events, newest first. Optionally filter by severity, screen or org node. Paginate with offset; the response reports total + hasMore.

ArgumentTypeRequiredDescription
severityinfo | warning | criticalno
screenIdstringno
nodeIdstringnoLimit to alerts raised at (or under) this org node.
offsetintegernoSkip this many matching alerts (default 0).

assign_content

screen.castchanges existing data, idempotent

Push a playlist, schedule, app, media or layout to a screen — or to MANY screens at once via screenIds. Requires `screen.cast`.

ArgumentTypeRequiredDescription
screenIdstringnoSingle target screen.
screenIdsarraynoBulk alternative: assign the same content to every listed screen (max 200).
contentKindplaylist | schedule | app | mediayes
contentIdstringno

send_command

screen.editchanges existing data

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.

ArgumentTypeRequiredDescription
screenIdstringno
screenIdsarraynoBulk alternative: command every listed screen (max 200) in one call.
kindreboot | refresh | screenshot | clear-cache | cec-on | cec-offyes

trigger_emergency_template

screen.editchanges existing data

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.

ArgumentTypeRequiredDescription
templateIdstringyes

upload_media_from_url

media.createcreates, reaches outside Brix

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.

ArgumentTypeRequiredDescription
urlstringyesPublicly fetchable http(s) URL.
namestringyesDisplay name for the asset.
folderIdstringno
tagsarrayno

mint_upload_url

media.createcreates

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`.

ArgumentTypeRequiredDescription
contentTypestringyes
namestringyes

confirm_media_upload

media.createcreates

Finalize an upload after PUTting bytes to a mint_upload_url. Creates the media_assets row.

ArgumentTypeRequiredDescription
uploadIdstringyes
folderIdstringno
tagsarrayno

bulk_create

screen.viewcreates

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`).

ArgumentTypeRequiredDescription
playlistsarrayno
schedulesarrayno
locationsarrayno
celebrationsarrayno

search_screens

screen.viewread-only

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.

ArgumentTypeRequiredDescription
qstringnoSubstring match on screen name.
statusonline | offline | pairingno
nodeIdstringnoLimit to screens at (or under) this org node.
tagstringno
offsetintegernoSkip this many matches (default 0).

assign_screen_to_node

screen.editchanges existing data, idempotent

Move a screen to a different org node — useful for post-migration cleanup. Requires `screen.edit`.

ArgumentTypeRequiredDescription
screenIdstringyes
nodeIdstringnonull to clear; otherwise the destination node id.

create_creatives

creative.createcreates

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.

ArgumentTypeRequiredDescription
creativesarrayyes

whoami

screen.viewread-only

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

screen.viewread-only

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.

ArgumentTypeRequiredDescription
prefixstringnoPath prefix filter, e.g. '/v1/playlists'.
qstringnoCase-insensitive substring match on path or description.
offsetnumbernoSkip this many matching routes — pass the `nextOffset` from the previous reply to page through the whole surface.

api_get

screen.viewread-only

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.

ArgumentTypeRequiredDescription
pathstringyesAbsolute API path starting with /v1/ with real ids in place of :params, optionally with ?query.

api_create

screen.viewchanges existing data

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.

ArgumentTypeRequiredDescription
pathstringyesAbsolute API path starting with /v1/ with real ids in place of :params, optionally with ?query.
bodyobject | arraynoJSON request body. Sent as application/json. Ignored when `bodyBase64` is present.
bodyBase64stringnoRaw 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.
contentTypestringnoContent-Type for `bodyBase64`, e.g. image/jpeg, text/vtt, audio/wav. Required with bodyBase64.
idempotencyKeystringnoForwarded as the `idempotency-key` header so a retried create cannot duplicate. Use any stable unique string per logical create.

api_update

screen.viewchanges existing data

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.

ArgumentTypeRequiredDescription
methodPATCH | PUTnoPATCH (the default) or PUT, as the route lists it.
pathstringyesAbsolute API path starting with /v1/ with real ids in place of :params, optionally with ?query.
bodyobject | arraynoJSON request body. Sent as application/json. Ignored when `bodyBase64` is present.
bodyBase64stringnoRaw 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.
contentTypestringnoContent-Type for `bodyBase64`, e.g. image/jpeg, text/vtt, audio/wav. Required with bodyBase64.

api_delete

screen.viewchanges existing data, idempotent

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.

ArgumentTypeRequiredDescription
pathstringyesAbsolute API path starting with /v1/ with real ids in place of :params, optionally with ?query.

search_media

media.viewread-only

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.

ArgumentTypeRequiredDescription
qstringnoCase-insensitive substring match on asset name.
kindstringnoAsset kind filter, e.g. image | video | audio | pdf | web | rss.
folderIdstringnoOnly assets filed in this folder.
tagstringnoExact tag match (case-insensitive).
limitintegernoPage size (default 50).
offsetintegernoSkip this many matches (default 0).

list_emergency_templates

emergency-override.viewread-only

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

screen.viewread-only

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.

ArgumentTypeRequiredDescription
statepending | approved | rejected | withdrawnnoFilter by decision state (default pending).
limitintegernoPage size (default 50).

list_recycle_bin

screen.viewread-only

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.

ArgumentTypeRequiredDescription
kindstringnoOnly this kind, e.g. screen, playlist, media, schedule, layout.
qstringnoCase-insensitive match on the item's name.
limitintegernoPage size (default 50).
offsetintegernonextOffset from the previous page.

update_playlist

playlist.editchanges existing data

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.

ArgumentTypeRequiredDescription
playlistIdstringyes
namestringnoNew display name.
descriptionstringno
shufflebooleanno
fullscreenbooleannoWhen this playlist plays inside a layout, EVERY item fills the whole screen and the other zones hide. Per-item: patchItems[].fullscreen.
startsAtstringnoISO datetime the playlist starts airing; null clears.
expiresAtstringnoISO datetime the playlist stops airing; null clears.
appendItemsarrayno
patchItemsarrayno
removeItemIdsarrayno
reorderItemIdsarraynoThe COMPLETE item order after the move — unknown ids are dropped, the rest keep this sequence.

update_media

media.editchanges existing data, idempotent

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`.

ArgumentTypeRequiredDescription
mediaIdstringyes
namestringno
tagsarraynoReplaces the whole tag list.
folderIdstringnoDestination folder in THIS workspace; null moves to the library root.
altTextstringno
fitcontain | cover | fill | blur-fillnoHow 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.
expiresAtstringnoISO datetime after which the asset stops airing; null clears.
autoArchiveOnExpirybooleannoSoft-delete the asset into the recycle bin when it expires.

restore_recycle_bin_item

screen.viewcreates, idempotent

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.

ArgumentTypeRequiredDescription
kindscreen | media | playlist | schedule | layout | creative | app | data-source | signage | banner | media-folderyes
idstringyes

decide_approval

screen.viewchanges existing data

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.

ArgumentTypeRequiredDescription
approvalIdstringyes
decisionapprove | rejectyes
reasonstringnoRequired for reject.

cast_content

screen.castchanges existing data, idempotent

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`.

ArgumentTypeRequiredDescription
contentKindmedia | playlist | app | creative | scheduleyes
contentIdstringyes
scopeKindall | node | screensnoDefault all — every screen this key can reach.
scopeNodeIdstringnoWith scopeKind=node: the org-node subtree to cast across.
screenIdsarraynoWith scopeKind=screens: explicit targets (max 500).
expiresAtstringnoISO datetime the cast ends. Default: one hour from now.
headlinestringnoDisplay name recorded for the cast.

end_cast

screen.castchanges existing data, idempotent

End an active cast early — its screens return to scheduled content immediately. Requires `screen.cast`.

ArgumentTypeRequiredDescription
castIdstringyes

quick_post

screen.castchanges existing data

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.

ArgumentTypeRequiredDescription
headlinestringyes
bodystringno
toneannouncement | alert | celebrate | infonoPicks the background treatment. Default announcement.
scopeKindall | node | screensnoDefault all.
scopeNodeIdstringno
screenIdsarrayno
expiresInSecintegernoHow long the message stays up (default 3600).

create_screen_group

screen.editcreates

Create a named, saved cohort of screens (optionally seeding members). Groups are the reusable target for bulk commands, casts and assignments. Requires `screen.edit`.

ArgumentTypeRequiredDescription
namestringyes
descriptionstringno
screenIdsarraynoInitial membership; foreign/out-of-scope ids are skipped silently.

add_screens_to_group

screen.editcreates, idempotent

Add screens to a saved screen group. Idempotent; out-of-scope ids are skipped, never fatal. Requires `screen.edit`.

ArgumentTypeRequiredDescription
groupIdstringyes
screenIdsarrayyes

remove_screens_from_group

screen.editchanges existing data, idempotent

Take screens out of a saved screen group — only the label drops, the screens themselves are untouched. Requires `screen.edit`.

ArgumentTypeRequiredDescription
groupIdstringyes
screenIdsarrayyes

sync_data_source

integration.editcreates, reaches outside Brix

Pull fresh data through one connected feed (CSV, Google Sheets, POS…) now, so data-bound apps and creatives show current values. Requires `integration.edit`.

ArgumentTypeRequiredDescription
dataSourceIdstringyes

refresh_all_data_sources

integration.editcreates, reaches outside Brix

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

playback-log.viewread-only

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`.

ArgumentTypeRequiredDescription
fromstringnoISO datetime; default 7 days ago.
tostringnoISO datetime; default now.
screenIdstringnoRestrict to one screen's report.

get_uptime_report

screen.viewread-only

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`.

ArgumentTypeRequiredDescription
fromstringnoISO datetime; default: the start of the outage-history window (30 days unless the workspace changed it).
tostringnoISO datetime; default now.
screenIdstringno

search_audit_log

audit-log.viewread-only

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`.

ArgumentTypeRequiredDescription
sincestringnoISO datetime; only events at or after it, and no cursor past it.
limitintegernoPage size (default 50).
cursorstringnonextCursor from the previous page.

Resources

Read with resources/read. Each one checks its own view permission.

URIWhat it returns
brix://screensEvery screen in the workspace, with status + location + assigned content.
brix://playlistsPlaylists with their items (refKind/refId/duration), approval state and org node.
brix://schedulesTime-of-day schedules and which screens they target.
brix://alerts/openCurrently open alert events — anything offline / storage-critical / playback-failing.
brix://org-treeThe hierarchical org-node structure (Spaces / districts / schools / rooms).
brix://mediaImages, videos, PDFs, web links and fonts — id, name, kind, folder, tags, state.
brix://creativesCanvas creatives (data-bound designs) with stage + box count.
brix://appsInstalled app instances (weather, RSS, menus…) with their app key + node.
brix://layoutsMulti-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-sourcesConnected data feeds (CSV, Sheets, POS…) with connection + last-sync status.
brix://screen-groupsNamed saved cohorts of screens — the unit for bulk commands and casts.
brix://emergency-templatesPre-armed emergency takeover recipes; pass one's id to trigger_emergency_template.
brix://approvals/pendingContent awaiting an approve/reject decision before it can air.
brix://screens/{screenId}/whyThe resolution chain (deactivated / emergency / cast / scheduled / default) for one screen.
brix://playlists/{playlistId}One playlist WITH its ordered items.
brix://media/{mediaId}/usageWhere a media asset is used (playlists/creatives/layouts) and when it last played.

Prompts

PromptWhat it doesArguments
fleet_health_reportA daily ops digest: fleet status, open alerts, worst uptime offenders and what to do about each.none
diagnose_offline_screenWalk the likely-cause diagnosis for one screen and hand back a guided next-step checklist.screen_id (optional)
onboard_new_locationSet up a new org location end-to-end: create the node, home its screens there and assign starter content.location_name
weekly_content_reviewProof-of-play rollup for the last 7 days: top content, skipped/failed plays, unused media worth pruning.none