Skip to main content
Glama

Screen for AI

Render HTML on a screen

display_render_html

Installs self-contained HTML on one authorized screen and returns its display operation. Inputs: device; html; optional title and source_label; optional page_id; security_mode (one of the screen's support.security_modes); runtime (required_api "0" gives the page window.AISurface, "none" a plain page); ttl_seconds (display lifetime, null: none); client_request_id (repeating a request with the same id returns the same operation). The screen's support field lists its security modes, runtime APIs and size limits. page_id (optional, a name of the caller's choosing such as "weather") becomes the page's view_id and each render a new revision of it: sending the same page_id again shows the new revision in that page's place instead of adding another page to its page history (an earlier revision that holds no saved answers is replaced); without page_id every render is a new page. Each revision has its own saved state, which starts empty: answers saved in an earlier revision reach a new one only through initial_state. The screen's account agents, and Wi-Fi clients joined to its account, update each other's pages with the same page_id; another sender's page with that page_id stays as it is. A runtime API 0 page must call AISurface.ready() and then AISurface.signalReady({ health: "ok" }) within a few seconds of loading (6 s in the current app): a page that does not signal ready is not shown and its render fails with DEVICE_REPORTED_FAILURE. On slow phone-owned screens signalReady's answer arrives only once the page is shown (10-15 s on an Android 5 tablet over the internet); older app versions end the wait after 5 s with RUNTIME_NOT_READY although the page is then shown. A page whose controls wait for that answer stays unusable meanwhile, or for good after the timeout. Until the page is shown, emit() fails with VIEW_NOT_ACTIVE (on older app versions state calls too), and a later call succeeds. AISurface calls return Promises: setState(key, value) saves one value (key: a lowercase letter, then lowercase letters, digits, _ . -, at most 64 characters, like "answer"; value: any JSON), replaceState({ key: value, ... }) replaces all saved values, getState(key?) reads one or all, removeState(key), emit(type, data) records an event, getContext() returns the page context, on(event, listener) subscribes to host events. setState resolves once the value is saved on the screen. A coded refusal (INVALID_STATE_KEY, STATE_STORAGE_FULL, VIEW_NOT_ACTIVE) means this write was not saved. A mutation timeout (RUNTIME_NOT_READY with retryable: false) has an uncertain outcome: the write may have committed before its reply was lost. An authorized getState readback can confirm the current value; a failed readback leaves it uncertain, and a newer edit can supersede the timed-out value. Repeating an event-producing action can duplicate its effect. A page that shows "Saved" before the Promise resolves can show it for an answer that was lost. getState("choice") resolves to the saved value itself, such as "Plate" (not { choice: "Plate" }), or null for a key with no saved value; getState() resolves to one object of every saved key and value. After a reload the page shows what its own script rebuilds at load: text derived from an answer, such as the price of the chosen item, appears again when the script computes it from the value getState returns. Page state (AISurface.setState) is kept on the screen with the page and is the saved state the agent reads; a phone restart can drop events nobody has read yet, so state is the durable record of an answer. ttl_seconds sets the page's display lifetime on the screen (null: no lifetime), after which the screen returns to its Start page. A lifetime does not keep the display lit: the phone turns its display off after its own screen timeout unless the owner's Keep screen awake setting is on (screen_settings.keep_screen_awake in the screen's status). Emoji come from the screen's own system font, which holds only the emoji that existed when its Android version shipped; an emoji the font lacks shows as an empty box. Words and basic symbols (✓ ★ → • ⚠ ♥ ☀) display on every screen. Measured fonts: Android 5 (android_api 21 and 22) shows only the original 2010 emoji, for example ☕ 🍕 🍝 🍲 ✅ 😀, and boxes for every emoji added since 2014, such as 🌶 🌮 🥑 🥢 🥡; Android 8.0 (android_api 26) shows emoji up to 2017 and boxes for those added since 2018, such as 🧁 🥳 🧋; Android 16 (android_api 36) showed every emoji tested, up to 2020. android_api is in each screen's listing and status; null means not reported, so the screen may be old. A page shorter than its screen sits at the top of the page area, so its controls reach the lower part of the screen only when the page fills the area's height, for example a full-height container (min-height: 100vh) with the controls last, above the menu button's covered height. A person's tap opens only http and https links. Other link schemes, such as sms:, tel: and mailto:, stay blocked, so a page cannot start a message, call or email; a page can show the recipient and text for the person to send themselves. Status: queued, received and persisted precede applied; only applied means the screen shows the page, and the returned operation_id identifies the operation in later status reads. A page's scripts run again each time the screen loads it again (an app restart, recovery after a crash, showing it again from the page history), so a value a script saves at load replaces an answer saved after it. initial_state (1-64 saved-state keys with JSON values, at most 768 bytes) is written into the new page's saved state once, when the screen first admits that page and before its scripts run; later loads leave it as it is, and getState and the page's saved-state read return it until the page saves another value. A revised page carries an earlier answer this way and shows it from getState; without initial_state it starts with no saved answers. For example, after the saved state of page_id "bookclub-vote" reads values {"book": "Emma"}, the revision sent with page_id "bookclub-vote" and initial_state {"book": "Emma"} reads "Emma" from getState("book") at load and can show it selected; the person's next saved choice then replaces it, and later loads keep that choice. Screens whose support.render_fields lists initial_state accept it; others answer DEVICE_CAPABILITY_UNSUPPORTED, and the page can be sent without it. Answers saved on a screen stay with that page on the screen and reach the agent only as that page's saved state; no screen tool writes them into another system, such as a school platform, a calendar or a shop. The result's page_warnings (absent when there are none) name problems found in the page before delivery: an inline script with a syntax error, or one the screen's browser cannot parse (judged from its reported webview_version; Android 5's WebView 37 reads only ES5, without arrow functions, let/const, classes or async), a runtime API 0 page with no AISurface.signalReady call, speechSynthesis, SpeechRecognition, getUserMedia or a file input (no screen spoke from a page in tests, and pages get no camera, microphone, photo or file), AISurface.on with a name that is no host event, and AISurface or --ais-overlay-* in a page without runtime API 0. The page is delivered either way. Where the screen's support.limits.operations_retained is set, the service keeps only that many recent operations for the screen. A repeated request with the same client_request_id returns the original operation while it is kept; after it has been removed, the same request is a new operation and shows the page again.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "device": "<device>",
      "html": "<html>"
    },
    "name": "display_render_html"
  }
}

Input Schema

NameRequiredDescriptionDefault
htmlYes
titleNo
deviceYes
page_idNoThe page's name of the caller's choosing, which becomes its view_id. A later send with the same page_id is a new revision shown in that page's place; each revision has its own saved state, which starts empty unless initial_state supplies earlier answers.
runtimeNo
ttl_secondsNoSeconds the screen shows the page before it returns to its Start page (null: until another page replaces it). It does not keep the display lit: the phone turns its display off after its own screen timeout unless the owner's Keep screen awake setting is on (screen_settings.keep_screen_awake in the screen's status).
source_labelNo
initial_stateNoSaved answers the new page starts with, written once when the screen first admits it, before its scripts run. A new revision of a page (the same page_id) receives no answers from earlier revisions otherwise. Example: the saved state of page_id "bookclub-vote" held values {"book": "Emma"}; the revision sent with page_id "bookclub-vote" and initial_state {"book": "Emma"} reads "Emma" from getState("book") and can show it selected.
security_modeNonormal
replace_policyNoactivate_when_healthy
client_request_idNo

Output Schema

NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

No schema history has been recorded yet.

TDQS

A3.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses extensive operational behavior: readiness signaling requirements, state durability, error codes, idempotency via client_request_id, TTL behavior, emoji font limits, link scheme restrictions, and page_warnings. This is far richer than the readOnlyHint, openWorldHint, idempotentHint, and destructiveHint annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely long and dense, with many run-on parentheticals and edge cases packed into single sentences. While information-rich, it is not appropriately sized or front-loaded for an agent selecting and invoking the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter tool with nested runtime objects and an output schema, the description covers most behavioral and operational details thoroughly, including readiness, state, errors, and limits. It misses replace_policy and remains overly verbose, but it is largely complete for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only 27% schema description coverage, the description compensates substantially by explaining page_id revisions, runtime APIs, ttl_seconds, client_request_id idempotency, initial_state semantics, and security_mode constraints. It does not explain replace_policy and only lists device, html, title, and source_label without adding much meaning, so it falls short of perfect coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb, resource, and scope: 'Installs self-contained HTML on one authorized screen and returns its display operation.' This distinguishes it from multi-screen broadcast tools by scoping it to 'one authorized screen,' though it does not explicitly name a sibling alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides almost no explicit when-to-use guidance versus siblings such as display_broadcast_html, display_show_template, or page_show. Usage is only implied through details like page_id revisions and security_mode, leaving the agent to infer selection criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources