Skip to content

MCP Apps

MCP Apps give you an interactive, visual interface on top of Bridge Town’s existing tools. When you or your agent calls a Bridge Town tool — list pull requests, schedule a model run, query data — you get the usual structured response. In hosts that support ui:// resources, you also get a rich UI that lets you act directly in the chat.

Apps are progressive enhancement: the same 75 MCP tools produce the same complete responses they always have. Apps add a visual layer; they do not change tool behaviour, add new tools, or alter the text content returned. Bridge Town ships 6 first-party apps.

The 6 built-in apps cover the most-used Bridge Town workflows. Every tool listed below was already part of the 75-tool surface before apps were introduced.

AppResource URIMapped tools
PR Workflowui://apps/pr-workflowcreate_branch, list_branches, diff, create_pull_request, list_pull_requests, get_pull_request, review_pull_request, merge_pull_request, merge_branch, delete_branch
Run Outputui://apps/run-outputrun, cancel_run, list_runs, get_run, get_run_output, export_run_output
Query Data Gridui://apps/query-data-gridlist_data_sources, query_data
Google Sheet Plannerui://apps/gsheet-plannerget_spreadsheet_metadata, batch_read_gsheet_ranges
Scheduled Runsui://apps/scheduled-runslist_scheduled_runs, create_scheduled_run, update_scheduled_run, delete_scheduled_run
Scenario Compareui://apps/scenario-comparecompare_branches, compare_runs

When you call a Bridge Town tool, the response always includes the full text/JSON result — that never changes. In hosts that support MCP Apps, a _meta.ui.resourceUri annotation on the tool definition tells the host which app can render an interactive view of the result.

┌────────────┐ tool call ┌───────────────┐ text result ┌────────────┐
│ AI Agent │ ────────────► │ MCP Server │ ──────────────► │ Agent │
│ │ │ │ │ (always) │
└────────────┘ └───────────────┘ └────────────┘
_meta.ui.resourceUri (on tool definition)
┌─────────────┐
│ App Bundle │ (only if host
│ (ui://) │ supports it)
└─────────────┘

Hosts that do not support ui:// resources simply ignore the _meta.ui annotation. The tool call, text result, and agent reasoning are unaffected — nothing breaks, nothing is hidden.

What you see depends on your host:

  • Compatible hosts that render ui:// resources display the interactive app alongside the text result.
  • Claude.ai uses Bridge Town’s Streamable HTTP connector. For Apps-capable sessions, Bridge Town includes _meta.ui.resourceUri and exposes the GET /mcp SSE back-channel used by the iframe runtime handshake. If you see text results but no widgets, see Troubleshooting Apps.
  • All other hosts receive the full text/JSON result as normal; the _meta.ui annotation is safely ignored.

Each app is a self-contained HTML bundle — no external scripts, no external stylesheets, no CDN dependencies. The entire app is served as a single ui:// resource, keeping rendering fast and avoiding CSP or network-isolation issues in sandboxed environments.

Use these checks to diagnose whether Apps are active server-side and whether the host would render them.

1. Verify Apps are active (full MCP handshake → tools/list_meta.ui.resourceUri)

In production, Bridge Town uses stateful Streamable HTTP so Apps-capable hosts can keep session state and open the SSE server-to-client channel. Both conditions must hold:

  • BRIDGE_TOWN_MCP_APPS_ENABLED=true on the server, and
  • The initialize request included extensions["io.modelcontextprotocol/ui"].mimeTypes containing "text/html;profile=mcp-app".

Perform the session-aware handshake to verify the production path:

Terminal window
# 1. initialize — advertise MCP Apps capability and capture Mcp-Session-Id
curl -fsS -D /tmp/bt-mcp-headers -o /tmp/bt-mcp-init \
-X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","clientInfo":{"name":"debug","version":"0.0.1"},"capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}}}}'
SESSION_ID=$(awk 'tolower($1)=="mcp-session-id:" {print $2}' /tmp/bt-mcp-headers | tr -d '\r')
# 2. notifications/initialized — complete handshake
curl -fsS -X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# 3. tools/list — check for _meta.ui.resourceUri
curl -fsS -X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| grep -o '"resourceUri":"[^"]*"'

Note: The Mcp-Session-Id header is required after initialize. Without it, stateful production requests cannot see the Apps capability that was negotiated during the session setup.

Expected output (when Apps are active):

"resourceUri":"ui://apps/pr-workflow"
"resourceUri":"ui://apps/run-output"
...

If _meta.ui.resourceUri is absent in production:

  • Server flag disabledBRIDGE_TOWN_MCP_APPS_ENABLED is not true on the deployed server
  • Client capability not advertised — the initialize request omitted extensions["io.modelcontextprotocol/ui"].mimeTypes
  • Server bug — flag is set and capability was advertised but metadata is missing; check server logs

2. Verify ui:// resources are registered (resources/list)

Terminal window
curl -fsS -X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}' \
| grep -o '"uri":"ui://apps/[^"]*"'

Six ui://apps/... entries confirm the server-side app bundles are live.

3. Verify app bundle MIME type (resources/read)

Read a specific app resource and confirm the MIME type is text/html;profile=mcp-app:

Terminal window
curl -fsS -X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/read","params":{"uri":"ui://apps/pr-workflow"}}' \
| grep -o '"mimeType":"[^"]*"'

Expected: "mimeType":"text/html;profile=mcp-app". Any other value means the host won’t recognise the bundle as an App.

4. Verify tool call annotates results (tools/callstructuredContent)

When Apps are active, tool calls that map to an app include a structuredContent field alongside the text response. structuredContent is standard MCP output for tools that define an output schema — it is not gated on client capability declaration. The text content is always present regardless of host rendering support.

Terminal window
curl -fsS -X POST https://api.bridgetown.builders/mcp \
-H "Authorization: Bearer btk_YOUR_TOKEN" \
-H "Mcp-Session-Id: $SESSION_ID" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_runs","arguments":{"project_id":"YOUR_PROJECT_ID"}}}' \
| grep -o '"structuredContent":{[^}]*}'

If structuredContent is present, the server is correctly annotating tool results. Absence means the server flag is off or a server-side bug — client capability declaration is not required for structuredContent in either stateless or stateful transports.

Apps appear active but don’t render in Claude.ai?

Bridge Town uses stateful Streamable HTTP in production. If tool definitions include _meta.ui.resourceUri but the iframe remains on “Connecting”, confirm the GET /mcp SSE channel is returning HTTP 200 for the same Mcp-Session-Id. A 405 on that channel means the iframe runtime cannot finish the handshake even though bundle discovery succeeded.

To force a fresh initialize handshake, remove and re-add the Bridge Town connector in Claude.ai.

If you are building a custom MCP host or agent, you can choose to support ui:// resources or ignore them:

  • To support apps: Advertise extensions["io.modelcontextprotocol/ui"].mimeTypes containing "text/html;profile=mcp-app" in your initialize request so Bridge Town includes _meta.ui.resourceUri in tools/list. structuredContent is standard tool output for app-mapped tools and is not gated on capability declaration. When a tool definition includes _meta.ui.resourceUri, fetch that resource after the tool call and render the returned HTML in a sandboxed iframe or webview. The HTML is self-contained and safe to render.
  • To ignore apps: Omit the extension from initialize. Bridge Town will suppress _meta.ui.resourceUri for your session, while tool calls and text responses remain unaffected.

Apps never change the semantics of a tool call. A create_pull_request call returns the same pull request data whether or not the PR Workflow app renders it.