Yui MCP server | spec v5 (INT-3, OAuth INT-19, Claude + MCP App INT-7, ChatGPT INT-8 live on chatgpt.com, Grok INT-10, n8n INT-17, Grok app, Le Chat and Gemini app guides INT-24, Oct 6 2026)
Path D of spec/ADAPTERS.md. The code lives in the app repo, postscarcityai/yui supabase/functions/yui-mcp; this page is what an MCP client needs.
Any AI app that speaks MCP can put a screen on your phone. You keep talking to Claude, Cursor or your own agent on your laptop; it pushes a timer, a choice or a check-in to Yui, and your taps come back to it as tool results. The conversation stays where it is. Yui is the second screen.
Claude Code / Cursor / n8n --MCP tools--> yui-mcp --> rows in yui_messages --> Yui app
<--taps-------- <-- <-- a tap
- Endpoint:
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp - Transport: MCP streamable HTTP, stateless. Every request is one POST with a JSON-RPC message (or a batch) and gets
application/jsonback. No session id, no SSE stream: GET answers 405. - Auth: either OAuth 2.1 (paste the URL, sign in, approve it in Yui: below) or
Authorization: Bearer yui_ct_..., a connection token you get by pairing (the three steps below). The Claude, ChatGPT, Grok, Le Chat and Gemini apps take the URL and sign you in with OAuth; Claude Code, Cursor and n8n (below) can use either.
Claude
Every Claude surface reaches Yui through this server. Pick the one you use.
Claude on the web, desktop and phone
Needs a plan that allows custom connectors. Nothing here lists Yui in Claude's directory: it is your own connector, visible only to you.
- In Claude, open Settings > Connectors > Add custom connector.
- Name it Yui. URL:
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp. Leave the OAuth fields empty. - Tap Connect. Claude opens www.yuigui.com/connect; approve it in the Yui app (OAuth below).
- In a chat, turn Yui on under the tools menu and ask: "Put a 5 minute focus timer on my phone."
A connector added on the web also shows up in the desktop and phone apps. Where Claude draws MCP Apps, the screen also appears in the chat itself and you can tap it there (MCP App).
Claude Code
claude mcp add --transport http yui https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp
claude mcp get yui # "Needs authentication"
claude mcp login yui # opens the browser; approve in Yui
Run claude mcp get yui (or /mcp inside a session) before claude mcp login. The check gets the server's 401, which tells Claude Code where Yui's sign-in lives. Yui runs under a path on a shared host, so a cold login looks at the host's root, finds nothing and fails with Dynamic Client Registration rejected (HTTP 404). On a machine with no browser add --no-browser and paste the address you land on back into the prompt.
Prefer a token? Pair with a code and pass the header (below).
Agents on the Claude Agent SDK
The SDK takes the same server. Give it a connection token from pairing, and put the channel guide in the system prompt so the agent knows every screen it can draw:
import { query } from "@anthropic-ai/claude-agent-sdk";
// The full channel guide; a short form already rides in the tool descriptions.
const guide = await (await fetch("https://raw.githubusercontent.com/postscarcityai/yuigui/main/spec/CHANNEL.md")).text();
for await (const m of query({
prompt: "Ask me on my phone how the workout went, then log it.",
options: {
mcpServers: {
yui: {
type: "http",
url: "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp",
headers: { Authorization: `Bearer ${process.env.YUI_TOKEN}` }, // yui_ct_... from pairing
},
},
allowedTools: ["mcp__yui__yui_show", "mcp__yui__yui_answers", "mcp__yui__yui_say", "mcp__yui__yui_threads"],
systemPrompt: {
type: "preset",
preset: "claude_code",
append: "You reach the person's phone through the yui tools. Where the guide says to write a ```yui block, call yui_show with those lines instead.\n\n" + guide,
},
},
})) {
if (m.type === "result") console.log(m.result);
}
An agent that runs as a service with its own HTTP endpoint can use the webhook bridge instead.
ChatGPT
ChatGPT adds Yui the same way Claude does: one URL, OAuth, approved in the Yui app. It is your own connection in developer mode, visible only to you. Nothing here lists Yui in ChatGPT's app directory.
Checked live on chatgpt.com on Oct 1 2026, not only in a ChatGPT-shaped test host. The screen drew in the chat, a tap in it was answered, and window.openai carried the same tap on its own. Shots: /progress/int8-live-1-connector-light.webp through int8-live-4-openai-light.webp.
Add Yui in ChatGPT
Do it on chatgpt.com in a browser. ChatGPT now calls these plugins; the wording below is what the site said on Oct 1 2026. A Free account was enough: developer mode and a custom MCP plugin both worked on one, even though the directory's own apps offered "Upgrade to install".
- Settings > Security and login > Developer mode, turn it on. (Beside it sits Enforce CSP in developer mode, off by default. Left off, the chat marks the Yui card CSP off and the
openai/widgetCSPwe declare is not applied. Turn it on to see the CSP we ship.) - Open chatgpt.com/plugins, press Add plugin, then Create app, then Create MCP App.
- Name Yui, description "Screens on my phone". Server URL:
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp. Authentication: OAuth. Tick "I understand and want to continue". Create. Fill the URL before you look at Advanced OAuth settings: it reads Yui's metadata off the URL and shows what it discovered. Use that URL exactly, with no/mcpon the end. OpenAI's own page says to enter the server URL "including the/mcppath", and that is wrong for Yui. A/mcpsuffix looks fine at first, because the function ignores the path and answers the same 401 with the sameresource_metadataheader, but the OAuth step then fails withinvalid_target:yui-oauthonly acceptsresourceequal to the bare function URL. Measured Oct 1 2026. - On the plugin's page press Connect another account. ChatGPT opens www.yuigui.com/connect in a popup. Scan its QR with your iPhone (or type an Add agent code from Agents > Add agent in the app) and tap Allow in Yui (OAuth below). The popup closes itself and ChatGPT shows the account under Connected accounts. A new agent named ChatGPT shows up in Yui.
- Press Refresh on the plugin's page. Until you do, it says "No app tools available yet" — this is needed on the first connect, not only after a server change.
- Start a new chat, press + in the composer and pick Yui — Screens on my phone, then ask: "Ask me on my phone what we are having for lunch: Salad, Soup or Tacos."
What works
- Everything Claude gets:
yui_show,yui_answers,yui_say,yui_threads, the channel guide, taps back. - The screen draws in the chat too (MCP App), and a tap there reaches ChatGPT as your next message, while the phone thread shows it as your reply. Live: a tap on Soup posted
[yui] n1 choose choice=Soupas the next message and ChatGPT answered "Soup it is." - ChatGPT shows a short status while a tool runs: "Putting it on your phone", then "On your phone".
- The model's list holds six tools (
yui_answers,yui_library,yui_say,yui_show,yui_tables,yui_threads).yui_tapis not among them, which isopenai/visibility: "private"doing its job, and the view still calls it. - Each tap writes the same
kind: eventrow a phone tap writes, delivered and handled, withmeta.viaofmcp-app.
What differs from Claude
- Developer mode only. A listed app in ChatGPT's directory is a public submission with a review, and needs a fixed widget domain (
_meta.ui.domain). That waits for Chris's sign-off. - Confidential client. ChatGPT registers itself with a client secret (
client_secret_post) and comes back tohttps://chatgpt.com/connector_platform_oauth_redirect, orhttps://chatgpt.com/connector/oauth/<id>on older paths. Both register like any https callback. It sendsresource=with the MCP URL on/authorizeand/token, and Yui returnsisson the redirect. - Discovery. Yui's issuer sits under a path on Supabase's shared host, so the two root
.well-knownURLs never answer. On the yuigui backend they 404 with Supabase's own{"error":"requested path is invalid"}(measured Oct 1 2026; the retired PROOF project answered 401 there instead, so expect either). The MCP spec's third try,{issuer}/.well-known/openid-configuration, answers, and so does every.well-knownunder the MCP URL, since the function matches those paths by suffix. ChatGPT is fine either way: the 401 carriesresource_metadataand OpenAI follows it, and live on chatgpt.com it found Yui's OAuth settings from the URL alone and the connect flow went straight through. A client that only tries the root cannot find Yui (the same thing Claude Code does cold, see above). - Where the view runs. The screen is framed twice: an
https://<app-id>.web-sandbox.oaiusercontent.comiframe, and the view itself in anabout:blankdocument inside it. Nothing of ours needs to know, but it is why the screen's text is not in the page's own DOM. - Extra metadata. ChatGPT reads the MCP Apps keys and, for its older paths, its own:
openai/outputTemplate(same URI asui.resourceUri),openai/toolInvocation/invokingandinvoked,securitySchemesper tool (oauth2, scopeyui, top level and in_meta),openai/visibility: "private"plusopenai/widgetAccessible: trueonyui_tap, and on the resourceopenai/widgetCSP(the same image domains,connect_domains: []),openai/widgetDescriptionandopenai/widgetPrefersBorder. Other hosts ignore them. - window.openai. If the host never answers the MCP Apps bridge, the view falls back to ChatGPT's older
window.openai: it readstoolInput,toolOutputandtheme, and sends a tap withsendFollowUpMessageandcallTool("yui_tap"). Live on chatgpt.com both halves are there and work:toolInputheld thechooseline,toolOutputthescreen_id,idsandchat,themereadlight, and driving the fallback by hand (callTool("yui_tap", …)thensendFollowUpMessage) wrote the same handled event row and ChatGPT answered "Tea it is." Day to day the bridge answers first, so the fallback stays dormant: it is the belt, not the braces. - One agent per connection. Like every OAuth client, ChatGPT talks as the one agent you picked when you allowed it.
- The account's name. Under Connected accounts ChatGPT labels the connection "Chris's Yui account" and "Primary" whoever signs in. It is ChatGPT's own wording, not something Yui sends.
Grok
Grok app (grok.com)
Grok's connectors page takes a custom MCP server by URL. Yui is your own connector, visible only to you; nothing here lists it in Grok's catalog. xAI says connectors are open to all Grok users. On Grok Business and Enterprise a team admin has to provision connectors first.
- Open grok.com/connectors.
- Click New Connector, then Custom.
- Enter the URL:
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp. Name it Yui if it asks. - Finish the sign-in. Grok opens www.yuigui.com/connect; approve it in the Yui app (OAuth).
- In a chat, ask: "Put a 5 minute focus timer on my phone." Grok lists the tools Yui exposes (
yui_show,yui_answers,yui_say,yui_threads) and uses them in the conversation.
- Auth. xAI's page says "complete any required authentication" and does not name the methods a custom server may use. Yui answers a cold request with a 401 and the
WWW-Authenticateheader that points at its OAuth metadata, which is what an OAuth client needs. - Public server. Grok needs a server reachable on the internet. Yui's is.
- No screen in the chat. Grok does not draw MCP Apps; the screen shows on the phone, and your taps come back as tool results.
- Source: docs.x.ai/grok/connectors, page dated July 17 2026, read Oct 6 2026.
- Not tried live yet. It needs an xAI account, which is Chris's to use. The server side was checked without one (the checks under Guides checked without an account).
Grok API
xAI's Responses API takes remote MCP servers as tools in the request itself, so a Grok agent you run calls Yui's server with no MCP client of its own (INT-10). xAI's servers make the calls, over streamable HTTP, with the header you give them.
- Pair with a code and keep the
yui_ct_...token (three steps). Grok has no OAuth step for remote MCP, so it takes the token. - Put both keys in your shell, never in a file you share or a prompt:
export XAI_API_KEY=...andexport YUI_TOKEN=yui_ct_.... - Send a request with Yui as an
mcptool:
curl -s https://api.x.ai/v1/responses \
-H "Authorization: Bearer $XAI_API_KEY" -H "content-type: application/json" \
-d @- <<JSON
{
"model": "grok-4.7",
"input": [
{"role": "system", "content": "You reach the person's phone through the yui tools. Call yui_show for a screen, then yui_answers with wait=25 for the tap."},
{"role": "user", "content": "Ask me on my phone what we are having for lunch: Salad, Soup or Tacos."}
],
"tools": [{
"type": "mcp",
"server_url": "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp",
"server_label": "yui",
"server_description": "Screens on my phone",
"allowed_tools": ["yui_show", "yui_answers", "yui_say", "yui_threads"],
"authorization": "Bearer $YUI_TOKEN"
}]
}
JSON
The shell fills in $YUI_TOKEN; the example holds no token. In xAI's Python SDK the same tool is mcp(server_url=..., server_label="yui", allowed_tool_names=[...], authorization=f"Bearer {token}").
- What Grok is told. The tool descriptions and the server's instructions carry the short guide. For every screen Yui has, put the full guide in the system message, the way the Claude Agent SDK example above does (
spec/CHANNEL.md). - Leave out
yui_tap. It is app-only;allowed_toolskeeps it from the model. - A wait is part of the request. Grok calls the tools while it answers, so
yui_answers(wait=25)keeps that request open until the tap comes or the wait ends. Send the next request withprevious_response_idto keep waiting. - The token leaves your machine. xAI's servers hold it for the request so they can call Yui. It reaches only the agents paired to it, and removing that computer in the app revokes it.
- No screen in the chat. Grok does not draw MCP Apps; the screen shows on the phone.
- Not run yet. This follows xAI's remote MCP docs. The first live run waits on an xAI key, like the model path (
spec/MODELS.md, "Grok").
Le Chat
Mistral's Le Chat takes a custom MCP connector by URL (Mistral's docs now call the app "Work", under Vibe; the Connectors page is the same). Yui is your own connector. Nothing here lists it in Mistral's curated directory.
- Open the Connectors page in Le Chat.
- Click + Add Connector and switch to the Custom MCP Connector tab.
- Connector name:
yui(a unique name, no spaces or special characters). Server URL:https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp. Description is optional: "Screens on my phone". - Click Connect. Le Chat detects the sign-in method on its own: it finds OAuth 2.1 with dynamic client registration and sends you to www.yuigui.com/connect. Approve it in the Yui app (OAuth).
- After it connects, open My Connectors, then the Yui card, then Functions. Read functions can be set to Always allow; write functions ask each time until you say otherwise.
- In a chat, ask: "Put a 5 minute focus timer on my phone."
- Admin only. Adding a connector needs an administrator. On Free, Pro and Student plans the account owner is the administrator.
- What it does not use yet. Mistral says custom connectors do not yet support dynamic tool discovery, resources or prompt templates. Yui's tools are a fixed list and it needs none of those.
- If it says "MCP connection requires additional information or is invalid". Check the URL is exactly the one above (no
/sse), that the server answers over HTTPS, and that the 401 carriesWWW-Authenticate. Yui's does. - No screen in the chat. The screen shows on the phone, and your taps come back as tool results.
- Source: docs.mistral.ai MCP Connectors, read Oct 6 2026.
- Not tried live yet. It needs a Mistral account, which is Chris's to use. The server side was checked without one (the checks under Guides checked without an account).
Gemini
The Gemini app takes a custom app by MCP server URL. Yui is your own connection, visible only to you; nothing here lists it anywhere in Google's apps.
Google's page sets the limits, so check them first:
- 18 or over and in the US.
- Signed in with a personal Google Account (a work or school account cannot yet).
- Keep Activity on.
- English only, for now.
- You add the app in the Gemini web app; once connected it also works in the Gemini phone app.
- On a computer, go to gemini.google.com.
- At the bottom, click Settings, then Connected Apps. If Connected Apps is not there, click Personal Intelligence first, then Connected Apps.
- Under Custom apps, find "Add a custom app link to get started" (or click Add a custom app if you have one already).
- Enter the URL:
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp. Yui supports dynamic client registration, so leave Advanced features closed. - Click Next and follow the screens. Gemini sends you to www.yuigui.com/connect; approve it in the Yui app (OAuth).
- In a chat, type
@and pick Yui so Gemini uses it, then ask: "Put a 5 minute focus timer on my phone."
- Writes ask first. Google says Gemini asks you to confirm any write action.
yui_showandyui_saywrite to your phone, so expect a confirmation. - Disconnect. Under the same Custom apps list, turn it off, or use More details, then Disconnect (revokes the link) or Remove app. Removing it in the Yui app (the computer called after the client) also revokes the token at once.
- No screen in the chat. The screen shows on the phone, and your taps come back as tool results.
- Not Gemini Enterprise. That product has its own custom MCP server setup for companies, which this page does not cover.
- Source: Connect & manage custom apps for Gemini Apps, read Oct 6 2026.
- Not tried live yet. It needs a personal US Google account signed in to Gemini, which is Chris's to use. The server side was checked without one (the checks under Guides checked without an account).
Guides checked without an account
What the Grok app, Le Chat and Gemini guides each rely on, run against the live server on Oct 6 2026 with curl and no sign-in:
| Check | Result |
|---|---|
POST the MCP URL with no token |
401, and WWW-Authenticate: Bearer ... resource_metadata="https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp/.well-known/oauth-protected-resource" |
| Fetch that protected-resource metadata | 200, names the authorization server .../functions/v1/yui-oauth |
.../yui-oauth/.well-known/oauth-authorization-server |
200, with registration_endpoint, authorization_endpoint, token_endpoint, S256 PKCE, authorization_code and refresh_token |
.../yui-oauth/.well-known/openid-configuration |
200 |
GET the MCP URL |
405 (stateless streamable HTTP, POST only) |
That is what Le Chat's own curl checks and an OAuth 2.1 client with dynamic registration need. The signed-in step (the real app tap, the approval in Yui, a tool call) is the part marked "Not tried live yet" above. For the signed-in path against Yui itself, supabase/tests/mcp_test.py and mcp_oauth_e2e.py (Checks) run a generic OAuth client the same way.
n8n
n8n reaches Yui three ways (INT-17). Two use this server; the third uses the webhook bridge. Importable workflows for all three are in the app repo, adapters/n8n/workflows.
| Use it when | LLM | |
|---|---|---|
| Yui node | A workflow needs a person's answer: approve, pick one, fill a form | no |
| MCP Client Tool | An AI Agent node should decide when to ask | yes |
| Webhook trigger | Yui should be the chat front end of a workflow | no |
For the first two, pair a code as kind mcp and keep the yui_ct_... token (three steps). It goes in an n8n credential, never in a workflow.
The Yui node
"Yui: send screen / wait for answer", the community node n8n-nodes-yui in adapters/n8n. Drag it in after any trigger:
- Ask and Wait: shows the screen (Yui Lines, expressions allowed:
=choose "{{ $json.question }}" Yes|No) and holds until the tap. The next node getschoice,echo,value, the[yui]line andscreen_id. - Send Screen, then Wait for Answer later in the workflow by
screen_id. - Send Message: a plain chat line.
It calls yui_show, then yui_answers(wait=25) in a loop up to its Timeout (300 s by default; fail or output timed_out). Lines Yui can't read fail the node with the parser's message and nothing is sent. It is also usable as a tool for the AI Agent. Not on npm yet: install it from the repo (npm pack, then npm install the file in ~/.n8n/nodes; the README has the lines).
MCP Client Tool
- An AI Agent node with any chat model.
- Its tool: MCP Client Tool. Endpoint
https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp, Server Transport HTTP Streamable, Authentication Bearer Auth with the token (or MCP OAuth2, OAuth). - Tools to Include:
yui_show,yui_answers,yui_say. Leave outyui_tap, it is app-only. - Options, Timeout: 40000 ms or more.
yui_answersholds a call up to 25 s and n8n's default is 60 s, which is fine for one call, but leave room. - The system message: the tool descriptions carry a short guide, and n8n does not read MCP prompts, so spell out the steps. This one got a 7B local model (qwen2.5:7b on Ollama) through it:
You reach the person's phone through the Yui tools.
1. To ask a question with set answers, call yui_show. Its `lines` argument is one line that starts with the word choose, then the question in double quotes, then the options joined by |. Example lines: choose "Lunch?" Salad|Soup|Tacos
2. yui_show returns a screen_id. Call yui_answers with that exact screen_id and wait=25. If it says nothing yet, call it again.
3. Their tap comes back as a line like [yui] n1 choose choice=Soup. Then answer in one short sentence that names what they picked.
A bigger model can take the whole channel guide instead (spec/CHANNEL.md) for every screen Yui has. When a line doesn't parse, yui_show returns the parser's message as a tool error and the agent tries again; with a small model, give it room (Max Iterations 12).
Webhook trigger
A Webhook node (POST, respond with a Respond to Webhook node), your logic, then Respond to Webhook with {"reply": "..."}. Run the webhook bridge next to n8n and point it at the webhook: yui_webhook.py run --webhook http://127.0.0.1:5678/webhook/yui-turn. Every message arrives once as one POST; a tap is a message with kind: "event" and its JSON in event. Contract: spec/WEBHOOK.md.
Tested
adapters/n8n/tests/n8n_e2e.py runs a real n8n 2.40.7 with an empty user folder, the node installed from npm pack and the three workflows published, on a throwaway account in live Yui, 22 of 22 on Sep 25: the AI Agent put one choose screen up through the MCP Client Tool, it parsed with the YL parser, the tap came back through yui_answers and the agent said "You picked Soup for lunch."; the Yui node returned the tap as fields and the next node used it; the bridge's "hi" got a screen and the tap got an answer naming it. npm test in the package: 10 unit tests.
OAuth
In an app that adds MCP servers by URL (Claude's and ChatGPT's custom connectors, MCP Inspector, Claude Code with no header), give it the endpoint above and nothing else.
- The app gets a 401 from
yui-mcpand follows it to Yui's sign-in. - Your browser opens www.yuigui.com/connect, which names the app and where it will return to.
- Approve on your iPhone. On the phone itself, tap Open Yui. On a computer, scan the QR code with the iPhone camera. Yui asks which agent the app talks as: a new one named after the app, or one it had before. Tap Allow. No app build with this sheet yet? In Yui tap Agents > Add agent, name it, and type its 6-digit code on the page instead.
- The page sends you back to the app, signed in. No token to copy.
The app now reaches that one thread and nothing else. Remove it in Yui (Agents, its computer) and its tokens stop working at once.
What a client needs, for the curious:
| Protected resource metadata (RFC 9728) | GET .../yui-mcp/.well-known/oauth-protected-resource, also named in the 401's WWW-Authenticate: Bearer resource_metadata="..." |
| Authorization server | https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-oauth |
| Metadata (RFC 8414 / OIDC discovery) | .../yui-oauth/.well-known/oauth-authorization-server and .../yui-oauth/.well-known/openid-configuration (yui-mcp serves the same at its own .well-known paths) |
| Registration (RFC 7591) | POST .../yui-oauth/register. Public clients (none, PKCE) or client_secret_post / client_secret_basic. Redirect URIs: https, http only on localhost, or an app scheme |
| Authorize | GET .../yui-oauth/authorize: response_type=code, PKCE S256 required, resource must be the endpoint above, scope yui |
| Token | POST .../yui-oauth/token: authorization_code with code_verifier, refresh_token. Access tokens (yui_at_) last an hour. Refresh tokens (yui_rt_) last 60 days and work once: each refresh gives a new one, and playing an old one again revokes the connection |
| Revoke (RFC 7009) | POST .../yui-oauth/revoke: a refresh token ends the connection, an access token only itself |
Yui lives under a path on a shared host, so a client must use the resource_metadata URL from the 401 (or the .well-known paths under yui-mcp). The host's root /.well-known answers nothing.
Each approval is a connection like a paired computer: the same limits, the same kill switch, one line in the app. Only hashes of codes and tokens are kept. Sign in with Apple on the web page itself (for people with no iPhone in reach) needs an Apple Services ID and is not on yet.
Pair with a code: three steps
- In the Yui app: Agents > Add agent. Name it for the app you will connect ("Claude"). It shows a 6-digit code.
- Trade the code for a token. The code works once and lasts ten minutes:
The answer holdscurl -s https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-connect \ -H 'content-type: application/json' \ -d '{"action":"pair","code":"123456","remote_ref":"claude","kind":"mcp","host_name":"Claude Code"}'connector_token(yui_ct_...). It is shown once and stored only as a hash. Treat it like a password. - Add the server to your client. Claude Code:
Cursor (claude mcp add --transport http yui https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp \ --header "Authorization: Bearer yui_ct_..."~/.cursor/mcp.json) and any client that takes a URL and headers:{"mcpServers": {"yui": {"url": "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp", "headers": {"Authorization": "Bearer yui_ct_..."}}}}
Then ask: "Put a 5 minute focus timer on my phone." The agent calls yui_show with timer 5m Focus and your phone buzzes.
remote_ref is your name for this connection (letters, digits, ., _, -). One token can serve several agents: pair more codes with the same token in the authorization header, and the tools take an agent argument.
Tools
| Tool | Arguments | What it does |
|---|---|---|
yui_show |
lines (Yui Lines, no fence), text (optional chat line), agent |
Puts a screen in the thread. Returns the screen id and the ids its taps will carry (timer@brk gives brk, otherwise n1, n2...). Lines that do not parse are refused with the parser's message and nothing is sent, so the model can fix them. |
yui_answers |
screen_id (optional), wait (0 to 25 s), agent |
What the person sent back, oldest first: taps ([yui] n1 choose choice=Walk plus the event JSON) and anything they typed. With screen_id, only what came after that screen. wait holds the call open until something arrives; call again to keep waiting. Each answer is returned once. |
yui_say |
text, agent |
A plain chat message. |
yui_threads |
none | The agents this token serves, with unread counts. The first is the default. |
yui_tap |
screen_id, event, told_model |
App-only: the MCP App calls it for a tap in the screen drawn in the chat. Hosts hide it from the model. |
A typical turn: yui_show a question, then yui_answers(screen_id, wait=25) until the tap comes, then the next screen. A photo the person sends comes back as an hour-long signed link in photos.
Every screen and message buzzes the phone, unless that thread is already open.
What the model is told
An agent that has not read the channel guide sends text and never a screen, so the guide goes in three ways:
- Tool descriptions.
yui_showcarries a short form: every component in one line each, the|rule for options, tap ids, the rules. - Prompt
yui_guideand resourceyui://guide: the full channel guide (spec/CHANNEL.md, the same text Hermes and the webhook bridge get), with a short preamble that maps "write a yui block" to "call yui_show". - Server instructions in the
initializeanswer: when to reach for a screen, and to wait withyui_answers.
MCP App
Hosts that draw MCP Apps (Claude, ChatGPT, the ext-apps reference host) show the screen right in the chat as well as on the phone. The same Yui Lines, drawn by the web renderer from yuigui.com/playground.
yui_shownames the resource in_meta.ui.resourceUri:ui://yui/screen, typetext/html;profile=mcp-app. It is one HTML file with the renderer inside, built frommcp-app/in this repo and copied into the function bysupabase/scripts/sync_mcp_app.pyin the app repo.- Its result carries
structuredContent:screen_id,agent, the tapidsand thelines, which is what the view draws. Hosts that do not draw apps read the same text result as before. - Its CSP loads images only from Yui's own storage and fal's CDN, and connects nowhere. KaTeX's fonts are left out, so math falls back to the system serif.
- A tap goes back as the same event a phone tap sends. The view hands the host the line (
ui/message, so the model reads[yui] n1 choose choice=Soupas the person's next message) and then calls the app-only toolyui_tap, which writes the event row into the thread: the same[yui]body,meta{id, preset, value, echo, via: "mcp-app"}. If the host took the message, the row is written handled, soyui_answersdoes not hand it over a second time. If the host refused it,yui_answersreturns it like any tap. yui_tapanswers only for a component that is on that screen, in a thread the connection serves. Quiet events (a timer starting, a checklist tick) stay on the screen, as on the phone. The line and echo are worked out on the server from the event, never taken from the view.- Staged components (a timer, a deck) start as their pill in the chat, so a question on the same screen stays in view. Theme follows the host's light or dark, color scheme included.
- ChatGPT reads the same resource through its own aliases too; see What differs from Claude.
Limits and safety
- Each connection has its own rate bucket,
mcpinyui_limits: 60 calls at once, 30 a minute sustained. Past it, HTTP 429. Messages it writes also count against the host limits in the app repo README. - The token reaches only the threads of agents paired to it. Another agent's thread, another account, or a Hermes host's token (403
not_an_mcp_connector) are all out of reach. - Removing its computer ("Claude Code") in the app revokes the token at once. A suspended account or connection gets 403.
- The server never sees your MCP client's conversation, only the tool calls.
Checks
python3 supabase/tests/mcp_test.py in the app repo: 105 live checks on a throwaway account (auth, handshake, tools, a tap during a wait, once-only answers, scope, the rate limit, 15 for the MCP App, 13 for ChatGPT's shape, and 43 for OAuth: discovery, registration, PKCE and a wrong verifier, codes once, refresh rotation and reuse, deny, expiry, removing the computer, revoke). supabase/tests/mcp_oauth_e2e.py --sim <udid> runs the MCP TypeScript SDK's own OAuth client against the simulator: it registers, the app's sheet allows it, and it asks and gets a timer back. supabase/tests/mcp_claude_e2e.py --sim <udid> runs a real Claude Code against the simulator: it asks "Ready for a tabata?", the UI test taps Yes, and Claude puts up the timer. With --oauth it adds Yui the way the Claude section says (no header, claude mcp get, claude mcp login, approved with the Add agent code on the connect page). supabase/tests/mcp_app_host_e2e.py runs the MCP App in the MCP Apps reference host (modelcontextprotocol/ext-apps examples/basic-host) in dark and light: the host calls yui_show, draws ui://yui/screen, Playwright taps an option inside it, and the host gets the [yui] line while the thread gets the same event row. mcp_test.py checks the resource, the tool metadata and yui_tap too. supabase/tests/mcp_chatgpt_e2e.py plays ChatGPT's side from OpenAI's Apps SDK docs: OAuth as ChatGPT does it (discovery in the MCP spec's order, registration with ChatGPT's callback and a secret, PKCE, resource, refresh), the model's tool list without yui_tap, the template through openai/outputTemplate, then a host page that frames it under a CSP built from openai/widgetCSP, once over the MCP Apps bridge and once with window.openai only, and taps it: 32 checks.
Next
- Sign in with Apple on the connect page, for when no iPhone with Yui is at hand.
- ChatGPT's directory: not listed. A listed app is public and needs a fixed widget domain; Chris signs off first.
- Claude's directory: not listed. Listing is public outreach, Chris signs off first.