00 Documents linked from this hub
The hub is the index. Each card below is a real document we add over time. Today: Strategy & Quick Wins (this page). Future: implementation plan, design specs, progress dashboard, and any artifact that needs a public URL.
Strategy & Quick Wins
Vision, architecture, RCS message anatomy, M0–M4 plan, the 5 QWs, risks, open asks.
Implementation Plan
Sprint-by-sprint engineering plan: server, widget, auth, deploy, observability, cost model.
Design Specs
Component library, iPhone-frame widget specs, theme tokens, UX principles for the ChatGPT surface.
Progress Dashboard
Live KPI panel: QWs shipped, MCP tools live, ChatGPT installs, paid-tier conversions, ARR.
Use Cases
Persona-driven scenarios: agency marketer, brand operator, dev integrating RCS, regulator reviewing.
Carrier Validation Reference
Validated against MNO RBM APIs. Country matrix. The moat: we are the only ChatGPT app that knows.
Brand & Marketing
RCS X app store listing, ChatGPT app store submission copy, landing page, social assets.
API Reference
Auto-generated from the MCP server: every tool, every input schema, every output contract.
01 Vision
RCS is the carrier-grade successor to SMS. It's supported by every major MNO, runs natively on Android, and works on iPhone via the standard Messages app. But it has a discoverability problem: brand operators have to imagine what their message will look like before pressing send — and what gets rendered on iPhone vs Android vs carrier fallback can be wildly different.
RCS X solves this by making the entire message pipeline conversational. You tell ChatGPT what you want to send. ChatGPT calls our MCP server. Our MCP server returns a tool result that includes both the validation verdict and an interactive widget showing exactly how the message renders on an iPhone. You click "Send to device" and it pushes to the RCS X iOS app, which renders on a real device for one last proof. Then it ships.
Why now
OpenAI shipped the Apps SDK in 2025, standardized on MCP. Every ChatGPT install is now a potential install for our app. Distribution is solved for us.
The moat
Carrier-grade validation. RBM APIs vary by MNO. We are the only ChatGPT app that knows that +1-415 from AT&T resolves differently than the same number on T-Mobile.
Why a widget
A text-only ChatGPT tool result forces the model to describe the rendering. A widget shows it. Same information, 10× trust, 10× conversion to "Send."
02 Architecture — the 3-surface view
The MCP server is the spine. ChatGPT and the iOS app are both clients. Both consume the same tools, see the same data, and render the same widget bundle. The server is the single source of truth for what an RCS message is and whether it will render correctly.
What the server is responsible for
- Tool definition — every tool's JSON Schema input/output is the wire contract. ChatGPT reasons from it; the iOS app calls against it.
- Validation logic — `validate_carrier` is the moat. It knows the rules for 80+ MNOs and will return a verdict the model can act on.
- Widget bundle hosting — a single HTML file, versioned, served as `embedded resource` with `_meta["ui.resourceUri"]`.
- Audit + persistence — D1 holds messages, validation results, and the per-tenant carrier cache. Auditable, replayable.
03 Anatomy of an RCS message — the live widget preview
Below is a live render of what the ChatGPT widget will display when the model calls preview_render. The user sees this inside ChatGPT before sending, can edit the message, and clicks "Push to iPhone" to fire it into the iOS app for real-device proof.
What you see
- Header bar — verified sender (Acme Bank), 9:41 timestamp, RCS badge indicating rich-message session.
- Inbound bubble — the previous turn (gray, iMessage-style).
- Outbound card — our message rendered: title, body, image, expiration, and 2 suggested reply buttons (View Offer / Save for Later).
- Typing indicator — animates while the next message loads; this is the live "is the user going to tap a button?" wait state.
What the model sees
A structured tool result containing the RCS payload (recipient, type, media URL, suggested replies), the carrier verdict (PASS / WARN with reason), and the widget HTML returned inline. The widget is the visual proof; the JSON is the contract.
{
"recipient": "+14155551234",
"type": "rich_card",
"body": "25x points on travel. Expires Sep 30.",
"media": "https://cdn.rcsx.app/cards/travel.png",
"suggested_replies": [
{ "label": "View Offer", "postback": "VIEW_OFFER" },
{ "label": "Save for Later", "postback": "SAVE_LATER" }
],
"validation": { "verdict": "PASS", "carrier": "T-Mobile US" }
}
04 Quick wins — 7 days from zero to ChatGPT-installable
The first week is just proving the wire works. We don't need to invent anything new — we need to copy the OpenAI reference patterns and stand up the minimum viable version of our specific use case.
QW1 · Fork & rename the reference M0
Clone pizzaz_server_node from openai/openai-apps-sdk-examples. Rename to rcsx_server_node. Replace the pizza tools with our 5 RCS tools (create_message, preview_render, validate_carrier, send_to_emulator, send_test). Working MCP server by Friday.
QW2 · Build the iMessage widget M0
Single HTML file with one web component. Left panel: input fields (recipient, body, media, replies). Right panel: an iPhone frame that renders the message live, exactly as you see in §03. ~300 lines of HTML + CSS + JS.
QW3 · Wire widget to MCP M0
Use window.openai.toolInput and toolOutput to pipe the form values into the tool call. The widget triggers callTool("create_message", {...}). Result lands in toolOutput, which the widget re-renders. End-to-end loop closes.
QW4 · ngrok + Loom demo Ship
Expose localhost:8000 via ngrok. Add the connector in ChatGPT developer mode. Record a 60-second Loom: "Ask ChatGPT to send an RCS, see the iPhone preview, click 'Push to iPhone,' watch the iOS app receive it." That's our launch video.
QW5 · Submit to ChatGPT Apps store Revenue
App submission flow: store listing, screenshots, description, privacy policy, App Submission Guidelines compliance. Once approved, every ChatGPT install is a potential install for us. Distribution is solved.
Out of scope (intentionally) Later
Carrier-validation moat (M2), paid tier (M3), iOS-app parity (M4). All real revenue work, all gated on M1 install base. Don't gold-plate QW1-QW4.
05 Roadmap — M0 to M4
Each milestone is independently shippable and verifiable. M0 is end-to-end. M1 is distribution. M2 is the moat. M3 is revenue. M4 closes the loop with the iOS app.
It works on localhost
ChatGPT Apps store
validate_carrier ships
Agency pricing live
One backend, two clients
M0 · Local end-to-end
QW1-QW4 done. Working app in ChatGPT developer mode. Loom published. Validation here: install + invoke + see widget + call tool successfully.
M1 · Public listing
Submit to Apps SDK. Get listed in ChatGPT apps store + Codex plugin. First 100 installs. Marketing site & landing live.
M2 · Carrier-validation moat
Add validate_carrier backed by real RBM API data. Country matrix. This is the only ChatGPT app that knows MNO rules.
M3 · Paid tier for agencies
Per the Apps SDK monetization guide. Seat-based pricing for marketing teams. Pro tier unlocks carrier validation + bulk send.
M4 · iOS app parity
Syed's app points at the same MCP server, not a parallel backend. One source of truth. iOS app becomes the mobile companion, not a separate product.
After M4
Internationalization (locale-aware rendering), B2B2C partnerships (carrier co-marketing), enterprise SSO + SOC 2, a developer tier for non-RCS messaging (WhatsApp Business, Viber).
06 MCP server — the spine
The server is a single Cloudflare Worker that exposes MCP over Streamable HTTP. The Tools SDK handles protocol details; we focus on the RCS domain logic. Reference: openai/openai-apps-sdk-examples, pizzaz_server_node.
Tool surface (v1)
| Tool | Input | Output | Side effects |
|---|---|---|---|
create_message | recipient, body, media URL, suggested replies | message_id, draft payload, validation hints | writes to D1 |
preview_render | message_id | structured payload + widget HTML | none (read-only) |
validate_carrier | recipient, message | verdict, carrier, warnings, suggestions | cached in D1 |
send_to_emulator | message_id | emulator status, push token | APNs push to iOS app |
send_test | message_id | delivery receipt, MNO response | calls RBM API (paid tier) |
Why Cloudflare Workers
- Streamable HTTP native — no reverse proxy work, no SSE plumbing. We just expose
/mcp. - D1 + KV at the edge — SQLite + key-value right next to the model. Sub-50ms tool latency globally.
- Wrangler one-command deploy — already wired in this workspace. Morsy's CF account, no new infrastructure.
- Durable Objects for the widget session state (per-conversation
widgetSessionId) if we need it.
Server instructions (the model-facing prompt)
You are RCS X, an agent that helps users compose, preview, validate, and send RCS Business Messaging. Always: (1) collect the recipient number, (2) draft the message, (3) preview it in the widget before sending, (4) validate carrier compatibility, (5) confirm with the user before send_to_emulator or send_test. Never send a message without an explicit "yes" from the human. If carrier validation returns WARN, surface the reason and propose an alternative.
07 The ChatGPT widget — what gets rendered inline
The widget is a single HTML file containing a web component. It lives at ui://rcsx/widget.html, served as an embedded resource by the MCP server with the right _meta["ui.resourceUri"] metadata. Inside ChatGPT, it renders in a sandboxed iframe and talks to the host via the window.openai bridge.
Anatomy of a widget call (what the model emits)
// tool call from the model
{
"name": "preview_render",
"arguments": { "message_id": "msg_8x2kq" }
}
// tool result from our server
{
"content": [
{ "type": "text", "text": "Preview generated for T-Mobile US." }
],
"_meta": {
"ui": {
"resourceUri": "ui://rcsx/widget.html",
"title": "RCS preview · 25x points on travel"
}
}
}
// ChatGPT fetches ui://rcsx/widget.html, renders it in an iframe,
// wires window.openai = { toolInput, toolOutput, theme, displayMode, ... }
// The widget can then call callTool("send_to_emulator", {...}) to act.
What the widget reads & writes
Reads
window.openai.toolInput— model-passed argswindow.openai.toolOutput— server responsewindow.openai.displayMode— inline / pip / fullscreenwindow.openai.theme— light / dark, follow ChatGPTwindow.openai.widgetState— persisted state
Writes
setWidgetState({...})— persists across turnscallTool(name, args)— call another MCP toolrequestDisplayMode("fullscreen")— expandopenExternal(url)— open a link in new tabsendFollowUpMessage(text)— nudge the model
UI components (v1 widget set)
| Component | Purpose | Where it appears |
|---|---|---|
rcs-phone-frame | iPhone mockup with status bar, notch, scrollable thread | right panel of the widget |
rcs-message-bubble | in / out bubble, supports text + media + suggested replies | inside the phone frame |
rcs-recipient-input | validated phone input with country code | left panel of the widget |
rcs-carrier-badge | shows the detected carrier + validation verdict | top of the widget |
rcs-suggested-reply-editor | add/remove suggested reply buttons | left panel, below body |
08 Distribution — how it reaches users
Three channels, in order of leverage.
1. ChatGPT Apps store Primary
OpenAI's official discovery surface. Every ChatGPT install is a potential install for us. Submission is approval-gated, not self-serve, today. Once approved, we're listed next to the Apps SDK reference examples.
2. Codex plugin Bonus
Every published Apps SDK app automatically becomes a Codex plugin. Developer audience: same ChatGPT account, IDE/CLI surface. Free distribution to a different persona.
3. MCP endpoint Devs
Our /mcp URL is publicly accessible. Any MCP-compatible client (Claude Desktop, Cursor, custom agents) can connect. We're the RCS transport for the agent internet, not just ChatGPT.
Why this is a 10× improvement over the X-token path
@rcsxPlatform auth token. ChatGPT is the discovery layer; we ship through them. The X account, if it ever works again, becomes a marketing surface for ChatGPT install CTAs — not a distribution bottleneck.
09 Risks & mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Apps SDK approval is slow / denied | Medium | High | ChatGPT developer mode lets us ship and demo without approval. Iterate on UX, then submit. If denied, ship as a pure MCP server (path 3 above). |
| ChatGPT model doesn't call our tools reliably | Medium | Medium | Server instructions encode the call order. Tool descriptions are explicit. Iterate tool descriptions based on real call logs. |
| iOS app contract not aligned with MCP | Medium | Medium | QW1 is the proof: fork the reference, ship working in 2 days. If Syed's app needs a different transport, we adapt the server, not the widget. |
| Carrier validation data is incomplete or wrong | Medium | High | Start with the 5 largest US MNOs (AT&T, T-Mobile, Verizon, Dish, US Cellular). Expand to top 25 international before claiming M2. |
| Widget iframe limitations (no localStorage, no third-party cookies, etc.) | Low | Medium | Apps SDK runtime gives us widgetState and callTool for persistence. No need for localStorage in v1. |
| Cloudflare Workers pricing surprises (D1, KV, DO bandwidth) | Low | Low | Workers free tier covers M0-M1. Set billing alerts at $20, $100, $500. Track per-tenant cost in D1. |
| OpenAI changes the Apps SDK protocol | Low | High | Pin the SDK version, follow the changelog (/changelog). The Apps SDK is open MCP — even if OpenAI pivots, the server works with any MCP host. |
10 Open asks — what we need to move
Three answers unblock QW1. Send them as a reply, we'll start the same day.
| # | Ask | Why | Default if no answer |
|---|---|---|---|
| 1 | Repo location | Where do I put rcsx_server_node? New GitHub repo under your personal org, or under RCS X's existing org? |
I'll create a new rcsx-platform/rcsx-server repo. You can move it later. |
| 2 | Deploy target | Cloudflare Workers (my default, free, fast) — or do you have an existing RCS X backend I should hook into? | Cloudflare Workers. We can bridge to Render or anywhere else via outbound HTTP if needed. |
| 3 | iPhone frame asset | Do you have the iOS frame SVG/PNG from Syed's app, or should I use an open-source iPhone frame as placeholder? | I'll use the open-source devices.css iPhone 15 frame until Syed ships one. |