Skip to content

feat(mcp): inline match card with Accept/Pass buttons for ChatGPT (MCP Apps) - #1877

Open
serefyarar wants to merge 1 commit into
devfrom
seref/ind-682-chatgpt-in-app-experience
Open

serefyarar wants to merge 1 commit into
devfrom
seref/ind-682-chatgpt-in-app-experience

Conversation

@serefyarar

Copy link
Copy Markdown
Contributor

Closes IND-682's first milestone: the in-chat interactive experience.

What

ChatGPT (and any MCP Apps host) can now render one opportunity as an inline card with Accept / Pass buttons, instead of plain text.

  • services/api/src/lib/mcp/mcp.ui.ts (new): a self-contained ui://index/match-card-v1.html resource (mimeType text/html;profile=mcp-app). The card shows the peer (initials avatar — nothing external loads), headline, summary, and cosign line; its buttons call accept_opportunity / reject_opportunity over the host bridge (window.openai.callTool), so a click goes through the same tools, annotations, and approval gates as a model-initiated call. Light + dark, system fonts, max two actions, no logo — per the Apps SDK inline-card guidelines.
  • show_opportunity_card tool (in mcp.tools.ts): same fetch as get_opportunity, returns structuredContent validated by an outputSchema, and carries _meta.ui.resourceUri + the openai/outputTemplate alias. Hosts without UI support never fetch the resource and can keep using get_opportunity.
  • CSP: connectDomains: [], resourceDomains: [] — the card needs nothing external.
  • Submission manifest: chatgpt-app-submission.json gains the new tool's annotations/justifications (read-only; the write tools keep their own destructiveHint).

Verified

Loaded the template in a real browser against a stubbed window.openai host:

  1. Card renders (avatar, name, headline, summary, cosign, Accept + Pass).
  2. Accept → callTool('accept_opportunity', …) → "You said yes — your chat with Jordan Rivera is open." + Open chat.
  3. Open chat → openExternal({ href: <chat url> }).

Terminal states (already committed / rejected / expired) render as notes without buttons. tsc --noEmit and eslint pass.

Before resubmission (not in this PR)

  • _meta.ui.domain must be set to a unique per-plugin domain at submission time (currently defaults to OpenAI's sandbox domain).
  • Review requires desktop + mobile testing of the card and a demo account.

🤖 Generated with Claude Code

…s hosts

Hosts that render MCP UI components (ChatGPT, per the Apps SDK) can now
show one opportunity as an interactive card. A new show_opportunity_card
tool carries _meta.ui.resourceUri (and the openai/outputTemplate alias)
pointing at a self-contained ui:// HTML resource; its Accept and Pass
buttons call accept_opportunity and reject_opportunity over the host
bridge, so a click rides the same tools, annotations, and approval gates
as the model's own calls.

The card allows no external connects or resources (initials avatar, CSP
empty), follows the inline-card rules (two actions max, system fonts, no
logo, light and dark), and hosts without UI support never fetch the
resource. The submission manifest gains the new tool's annotations.

Verified in a browser against a stubbed window.openai host: render,
Accept -> accept_opportunity -> chat-open state -> openExternal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant