← Index / MCP / Relay Card Display
v3.4 PR #784

Relay Card Display

Complete reference for the display field in every MCP tool response. Every tool. Rich and plain side by side. Ground truth for src/relay/mcp/server.py.

Rich mode Plain mode Framing rule Known gap Live Template
§ Display field

Every MCP tool response includes a display field — a markdown string the agent renders verbatim as a visual card. It is not a summary. It is an identity block: who, what, when, and (for transfers) whether content has been loaded.

The Rule

Agents must render display verbatim as markdown before doing anything with the content. The next_step field enforces this explicitly. Never let the agent skip the card and summarise content in its own voice.

Without --- framing and a signal line, agents see a 2–3 line block with no structural cue, skip rendering the card, and summarise the content in their own voice. This is the root cause of both bugs patched in June 2026.

§ Entity symbols
SymbolConstantEntityUsage
RELAY_SYM Relay transfer Code line in every transfer card
PULSE_SYM Pulse context Pulse name line in pulse tool responses

Do not use these symbols for other purposes. Do not swap them.

§ Framing convention

Rich-mode relay cards use horizontal-rule framing. The opening --- separates the card from prior conversation. The closing --- + signal line tells the agent the card is complete and content follows.

---← opening rule · separates from prior context
✅ **Transfer title — Jun 3, 2026**← title line
⇄ `K886XH` · 👤 Sender · Jun 3 · 👤 Private← code line
---← closing rule
*content loaded — see below*← signal line · agent cue
StateSignal lineNotes
Confirmed claim*content loaded — see below*Content is in the content field below the card
Owner / already claimed*content loaded — card above*Card appears above the content the agent is contextualising
relay_profile_get(no signal — framing only)Only tool outside transfers that uses --- framing; see §5
Preview(no framing — known gap)Low priority; no content loaded at this step
Pending approval(no framing — known gap)No content loaded; risk lower
1 Transfer States
relay_transfer_claim No Framing confirm=false · non-owner

Shown before the user confirms the claim. No content is returned. Agent presents the card and asks "Load it?" Preview state does not yet have --- framing — low priority since no content is loaded at this step.

State 1 — Preview
Rich mode
Claim Relay Transfer
⇄ HAVVPX 👤 Sender Name · Jun 3 This transfer is from Sender: 'Sprint Handoff'. Load it?
Plain mode
Claim Relay Transfer
[RELAY PREVIEW] HAVVPX Sprint Handoff From: Sender Name · Jun 3 Call relay_transfer_claim confirm=true to load.
⚠ Preview state does not yet have --- framing. Low priority — content is not loaded at this step.
relay_transfer_claim confirm=true · status='claimed'

Shown after a successful claim. Full content is in the content field. Code is spent after this step. The --- framing + signal line cue the agent to render first, then load content.

State 2 — Confirmed claim
Rich mode
Claim Relay Transfer
---Sprint Handoff — May 7, 2026 ⇄ `HAVVPX` · 👤 Sender Name · Jun 3 --- *content loaded — see below*
Plain mode
Claim Relay Transfer
[RELAY LOADED] HAVVPX Sprint Handoff — May 7, 2026 From: Sender Name · Jun 3 Context loaded below.
● --- framing: opening rule → card content → closing rule → signal line «content loaded — see below»
relay_transfer_claim confirm=false · viewer='owner'

Triggered when the caller is the transfer owner. Content is returned immediately — no confirm step. Signal line reads «content loaded — card above» because the card appears above the content block.

State 3 — Owner
Rich mode
Claim Relay Transfer
--- 👤 Sprint Handoff — May 7, 2026 ⇄ `HAVVPX` · Sent by you · Jun 3 --- *content loaded — card above*
Plain mode
Claim Relay Transfer
[YOUR RELAY] HAVVPX Sprint Handoff — May 7, 2026 Sent by you · Jun 3 Context loaded below.
● Signal «card above» used here (vs «see below» on confirmed claim) — positional distinction for agent rendering.
relay_transfer_claim confirm=false · viewer='claimed'

Triggered when the caller has already claimed this code. Content is returned immediately, same as owner path. Same framing, same signal line.

State 4 — Already claimed
Rich mode
Claim Relay Transfer
---Sprint Handoff — May 7, 2026 ⇄ `HAVVPX` · Already claimed · Jun 3 --- *content loaded — card above*
Plain mode
Claim Relay Transfer
[RELAY LOADED] HAVVPX Sprint Handoff — May 7, 2026 Already claimed · Jun 3 Context loaded below.
relay_transfer_claim No Framing approval required

Shown when a cross-user claim is blocked pending sender approval. Content is not loaded. No --- framing yet — known gap, lower priority than confirmed-claim framing.

State 5 — Pending approval
Rich mode
Claim Relay Transfer
Approval requested ⇄ HAVVPX Your request has been sent. You'll be notified once the sender approves.
Plain mode
Claim Relay Transfer
[PENDING APPROVAL] HAVVPX Request sent. Waiting for sender to approve.
⚠ Pending approval does not yet have --- framing. Risk is lower since no content is loaded, but consistency suggests adding framing in a future pass.
relay_series_claim 2-step confirm

Same two-step confirm pattern as relay_transfer_claim. On confirmed claim, series name is appended to the code line.

Step 1 — Series preview
Claim Series
📋 Sprint Handoff — May 7, 2026 ⇄ series weekly-sync · latest: HAVVPX 👤 Sender Name · Jun 3 · 👤 Private
→ Agent asks: "Load it?" — waits for confirmation
Step 2 — Confirmed
Rich mode
Claim Series
---Sprint Handoff — May 7, 2026 ⇄ `HAVVPX` loaded · series weekly-sync 👤 Sender Name · Jun 3 --- *content loaded — see below*
Plain mode
Claim Series
[RELAY LOADED] HAVVPX Sprint Handoff — May 7, 2026 Series: weekly-sync From: Sender Name · Jun 3 Context loaded below.
2 Session & Context
relay_get_context Live Session bootstrap

Called at the start of a session to establish the active user and org. Display confirms the session loaded, who the user is, and — in the variant below — whether there are pending requests.

Base response
Rich mode
Get Context
Session loaded · erik Relay Context Inc. · admin · Pro
Plain mode
Get Context
[SESSION LOADED] erik Relay Context Inc. admin Pro Context ready.
With pending requests
Rich mode
Get Context
Session loaded · erik Relay Context Inc. · admin · Pro 💬 You have 1 pending request(s). Use relay_request_list to review.
Plain mode
Get Context
[SESSION LOADED] erik Relay Context Inc. admin Pro 1 pending request(s). Run relay_request_list. Context ready.
relay_get_recent Live Recent relay list

Returns the user's recent relay transfers as a display list. Header shows count; each row shows code, title, visibility, and receive count.

Rich mode
Get Recent Relays
📋 Recent Relays · 5 relay(s) ⇄ K886XH · Sprint Handoff · Private · 1 received ⇄ M241RQ · Design Brief · Org · 3 received ⇄ T990WW · Auth Spec · Private · 0 received
Plain mode
Get Recent Relays
[RECENT RELAYS] 5 K886XH Sprint Handoff Private 1 received M241RQ Design Brief Org 3 received T990WW Auth Spec Private 0 received
relay_session_log Template Session close

Called at the end of a session to log activity to a relay. Shows the relay code the session was logged against, the date, and the title.

Rich mode
Log Session
📝 Session logged · Jun 5, 2026 ⇄ K886XH · Sprint Handoff · Private
Plain mode
Log Session
[SESSION LOGGED] K886XH Sprint Handoff Jun 5, 2026
3 Pulse
relay_pulse_list Template Focus area index

Returns all pulse focus areas for the active user. Header shows total count; each row shows the pulse name and item count.

Rich mode
List Pulses
📋 Focus Areas · 3 ◉ Relay Product Development · 9 Item(s) ◉ Relay Creative · 4 Item(s) ◉ Admin & Ops · 2 Item(s)
Plain mode
List Pulses
[FOCUS AREAS] 3 Relay Product Development 9 items Relay Creative 4 items Admin & Ops 2 items
relay_pulse_create Template New focus area

Confirms the creation of a new pulse. Shows the pulse name and the opaque context_id. The ⬡ prefix (hexagon) marks the context_id — a machine-facing handle, not a user-facing label.

Rich mode
Create Pulse
✅ Focus area created: Relay Creative ⬡ pc_e80950d4c486 · add Items with relay_pulse_update
Plain mode
Create Pulse
[PULSE CREATED] pc_e80950d4c486 Relay Creative
relay_pulse_get Live Pulse detail

Full detail view of a single pulse. Shows name, context_id, weight percentage, item count, and a preview of the first few items.

Rich mode
Get Pulse
Relay Creative ⬡ pc_e80950d4c486 · weight 30% · 4 live · relay-card-display v3.4 · MCP reference refresh · Creative direction brief
Plain mode
Get Pulse
[PULSE] pc_e80950d4c486 Relay Creative 30% 4 live - relay-card-display v3.4 - MCP reference refresh
relay_pulse_update Template Item add / edit / remove

Confirms an item was added, updated, or removed from a pulse. Shows the operation prefix (+ / ~ / −) and the item title.

Rich mode
Update Pulse
Pulse updated + relay-card-display v3.4 added
Plain mode
Update Pulse
[PULSE UPDATED] pc_e80950d4c486 + relay-card-display v3.4 added
relay_get_active_pulse Live Active focus area

Returns the currently active pulse for this workspace/session. Shows pulse name, context_id, and the workspace tag. Used by the agent at session start to orient to the active focus area.

Rich mode
Get Active Pulse
Relay Product Development ⬡ pc_78b145fbb371 · workspace: github:christensen-digital/relay-platform
Plain mode
Get Active Pulse
[ACTIVE PULSE] pc_78b145fbb371 Relay Product Development github:christensen-digital/relay-platform
5 Identity & Org
relay_profile_get Live Identity block

Returns a markdown table of the user's identity and org membership. This is the only non-transfer tool that uses --- framing — because the profile block is a structured identity artifact, not a status message. It does not use a signal line.

Rich mode
Get Profile
--- Your Relay Profile | Field | Value | |---|---| | User ID | rd521776 | | Org | Relay Context Inc. | | Name | Erik Christensen | | Role | admin | | Tier | Pro | ---
Plain mode
Get Profile
[YOUR PROFILE] User ID: rd521776 Org: Relay Context Inc. Name: Erik Christensen Role: admin Tier: Pro
● relay_profile_get is the only non-transfer tool that uses --- framing. Reason: the profile table is a structured identity artifact with clear boundaries, not a status confirmation. There is no signal line — the closing --- is sufficient because no content follows in the same response.
relay_workspace_switch Template Org switch

Confirms a workspace (org) switch. Shows the org name, the internal org ID, the user's role in the new org, and status.

Rich mode
Switch Workspace
🔄 Switched to Relay Context Inc. ⬡ gwh04nszwz5s76s · admin · active
Plain mode
Switch Workspace
[WORKSPACE SWITCHED] Relay Context Inc. gwh04nszwz5s76s admin active
relay_request_list Live Pending requests

Lists incoming claim requests awaiting the user's approval. Each row shows requester name, request ID, and date. Empty state shown when no requests are pending.

With pending requests
Rich mode
List Requests
1 pending request From Ada · rd521776 · requested May 5
Plain mode
List Requests
[PENDING REQUESTS] 1 Ada rd521776 May 5
Empty state
Rich mode
List Requests
No pending requests.
Plain mode
List Requests
[PENDING REQUESTS] 0 No requests.
relay_search Live Transfer search

Searches the user's relay transfers. Header shows result count and query; each row shows code, title, status, and date. Empty state shown when no results.

With results
Rich mode
Search Relays
🔍 3 results for "sprint" ⇄ K886XH · Sprint Handoff · pending · May 7 ⇄ T330AB · Sprint 22 Notes · claimed · Apr 28 ⇄ R119XZ · Sprint Planning · claimed · Apr 14
Plain mode
Search Relays
[SEARCH] 3 results for: sprint K886XH Sprint Handoff pending May 7 T330AB Sprint 22 Notes claimed Apr 28 R119XZ Sprint Planning claimed Apr 14
No results
Rich mode
Search Relays
🔍 No results for "quarterly"
Plain mode
Search Relays
[SEARCH] 0 results for: quarterly
6 Error States
relay_transfer_claim Not found · already received

Code does not exist, or has already been received by another user (codes are single-use on non-org transfers).

Code not found
Rich mode
Claim Relay Transfer
Code not found ⇄ ZZZZZZ · Check the code and try again.
Plain mode
Claim Relay Transfer
[ERROR] CODE NOT FOUND ZZZZZZ Check the code and try again.
Already received
Rich mode
Claim Relay Transfer
Already received ⇄ HAVVPX · This code has already been claimed by another user.
Plain mode
Claim Relay Transfer
[ERROR] ALREADY RECEIVED HAVVPX Code already claimed by another user.
any tool 401 / token expired

Returned by any tool when the session JWT has expired or the token is invalid. Agent should prompt the user to re-authenticate.

Rich mode
MCP Tool
🔒 Session expired Re-authenticate at app.relayctx.com to continue.
Plain mode
MCP Tool
[ERROR] SESSION EXPIRED Re-authenticate at app.relayctx.com.
any tool Network / 5xx

Returned when the MCP server cannot reach the Relay API. Includes partial load variant when context was partially loaded before the failure.

Full failure
Rich mode
MCP Tool
⚠️ Connection error Could not reach relay API. Check your connection and try again.
Plain mode
MCP Tool
[ERROR] CONNECTION FAILED Could not reach relay API. Try again.
Partial load
Rich mode
MCP Tool
⚠️ Partial load ⇄ HAVVPX · Sprint Handoff Context partially loaded. Some content may be missing.
Plain mode
MCP Tool
[ERROR] PARTIAL LOAD HAVVPX Sprint Handoff Some content may be missing.
§ Plain mode

_plain_mode() returns True when RELAY_PLAIN=1 or RELAY_PLAIN=true. Produces ASCII-safe, emoji-free output for terminals. Every display string must have both branches.

# Every display field structure "display": ( ( f"[RELAY LOADED] {code}\n" f"{title}\n" f"From: {sender} · {date}\n\n" "Context loaded below." ) if _plain_mode() else ( f"---\n" f"✅ **{title}**\n" f"⇄ `{code}` · {sender} · {date}" + "\n---\n*content loaded — see below*" ) ),

Plain mode: always bracketed label, always a text signal line. No emoji, no markdown, no --- framing.

§ Helper functions
  • _plain_mode() bool — reads RELAY_PLAIN env var
  • _date_label(iso) Short date string e.g. "Jun 3" — never use raw ISO date in display
  • _sender_emoji(name) 🤖 for agent senders (name contains /), 👤 for humans
  • _vis_icon(v) 👥 org · 📢 broadcast · 👤 private
  • _vis_label(v) "Org" · "Broadcast" · "Private"
§ New state checklist
  • Rich mode: --- framing, card content, closing ---, signal line (transfer tools)
  • Plain mode: bracketed label, title, sender/date or key fields, blank line, text signal
  • next_step starts with "RENDER the display field verbatim as markdown."
  • Uses _plain_mode() branch — both branches present in every tool
  • Uses RELAY_SYM () for the code line — not a plain string
  • Uses PULSE_SYM () for pulse name lines — not a plain string
  • Uses _date_label() not raw ISO date in any display output
  • Uses _sender_emoji() for sender icon (agent vs human)
  • Test with RELAY_PLAIN=1 to verify plain branch renders correctly
  • LIVE badge: uses real response values (not hardcoded placeholders)
  • TEMPLATE badge: uses hardcoded example values clearly labelled as templates
  • Update DESIGN-NOTE.relay-card-display.md in relay-platform/docs/