> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP App HTML

> Serve built MCP App widgets for iframe hosts

The MCP App HTML endpoint serves the same built single-file widgets that are exposed through MCP `resources/read`, but over authenticated REST for hosts that do not have a live MCP client.

## Endpoint

```http theme={null}
GET /api/v2/mcp-apps/:view/mcp-app.html
```

Use this endpoint when an Apostra-hosted UI needs to render a `ui://agentic-api/.../mcp-app.html` widget in a sandboxed iframe. The response body is HTML text and should be passed to the iframe as `srcdoc`; hosts provide widget data over the MCP Apps bridge after the iframe initializes.

## Path parameters

| Parameter | Type   | Description                                                                   |
| --------- | ------ | ----------------------------------------------------------------------------- |
| `view`    | string | Built widget slug. Must contain only lowercase letters, numbers, and hyphens. |

`view` must match a registered MCP App resource. MCP clients discover the live
set through `resources/list`; use the slug between `ui://agentic-api/` and
`/mcp-app.html`. A REST-only host should use the resource URI supplied by Apostra tool or rendering contract and treat the slug as opaque. Do not
hard-code a short "supported views" list: the registered set evolves with the
product and includes compatibility aliases for persisted messages.

## Tool-result contract

A conformant MCP host normally does not call this endpoint directly. A
widget-bearing tool declares or returns `_meta.ui.resourceUri`, and its standard
tool result carries:

* compact model-facing text in `content`;
* the app payload in `structuredContent`;
* optional host/app-only data in `_meta`.

The host reads the referenced `ui://` resource, renders it in a sandboxed
iframe, and delivers the tool result over the MCP Apps bridge. The REST HTML
endpoint exists for authenticated Apostra-hosted surfaces that need the
same built resource without a live MCP resource client.

## Page capability and tool bridge

An interactive Page may receive a session-bound capability from `open_page`.
The capability is returned in app-only metadata and delivered to the Page over
the app bridge; it is not model-visible. The host routes Page calls through its
scoped proxy and must not expose Page aliases as general-purpose tools.

The Teach Page uses the host-only `teach_search_materials`,
`teach_get_material`, `teach_save_material`, and `teach_renew_capability`
aliases for Material listing and exact reads, seller-private URL and file
additions, upload finalization, Material-owned candidate decisions, and
capability renewal. MCP clients continue to use the public `search`, `get`, and
`save_material` tools. The bridge does not permit generic Material writes or
return source-access or visual-preview URLs. Private-file uploads receive an
expiring write URL only for the browser `PUT`; the Page keeps that URL out of
rendered state and model context, then finalizes the reservation through the
canonical Material lifecycle. Teach accepts PDF, PowerPoint (`.pptx`), Excel
(`.xlsx`), CSV, PNG, JPEG, GIF, and WebP files up to 100 MB.

The Library Page uses the host-only `library_search_materials`,
`library_get_material`, `library_search_requests`, `library_search_rfps`,
`library_get_rfp_turn`, `library_open_proposal_pass`, `library_save_material`,
`library_save_request`, `library_save_rfp`, and `library_renew_capability`
aliases for document and unit browsing, exact reads pinned to a source
revision, request and response listing, opening the authorised Proposal Pass
for a response, uploads, request closure, and capability renewal. MCP clients
continue to use the public `search`, `get`, `save_material`,
`save_library_request`, `save_rfp`, and `open_proposal_pass` tools.

A missing, expired, wrong-session, or wrong-resource Page capability returns
`ACCESS_DENIED`, as does any call shape outside the Page allowlist. Teach also
returns `ACCESS_DENIED` rather than rejecting a destination-owned candidate.
Teach presents it as a handoff; direct MCP clients record rejection with public
`save_material`, while acceptance stays with the candidate's canonical typed
owner.

## Response

Successful responses return the built HTML as text:

```http theme={null}
HTTP/1.1 200 OK
Content-Type: text/html;profile=mcp-app
Cache-Control: private, max-age=300
X-Content-Type-Options: nosniff
Content-Security-Policy: sandbox; default-src 'none'; script-src 'none'; base-uri 'none'; frame-ancestors 'none'

<!DOCTYPE html>
...
```

The response is intentionally cacheable only by the authenticated caller. Hosts should still apply their own iframe sandbox and bridge-level allowlists before sending data or actions into the widget.

## Errors

| Status | Code               | When                                                     |
| ------ | ------------------ | -------------------------------------------------------- |
| `400`  | `VALIDATION_ERROR` | `view` contains unsupported characters.                  |
| `404`  | `NOT_FOUND`        | No built MCP App widget exists for the requested `view`. |

## Example

```bash theme={null}
curl https://api.apostra.com/api/v2/mcp-apps/seller-dashboard/mcp-app.html \
  -H "Authorization: Bearer scope3_your_api_key_here" \
  -H "Accept: text/html"
```
