@khanglvm/relay 0.13.8 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/docs/AGENT.md +23 -0
- package/package.json +1 -1
- package/skills/relay/SKILL.md +26 -3
- package/src/cli.js +96 -9
- package/src/mcp.js +4 -3
- package/src/server.js +262 -22
- package/src/spec.js +8 -1
- package/src/ui/annotate.js +35 -7
- package/src/ui/app.js +152 -4
- package/src/ui/blocks.css +62 -0
- package/src/ui/blocks.js +151 -0
- package/src/ui/style.css +52 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ step, leaving any boards you have open untouched.
|
|
|
43
43
|
relay also runs as an **MCP App** ([SEP-1865](https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp))
|
|
44
44
|
— so the same board renders **inline, right in the conversation**, on Claude
|
|
45
45
|
desktop **and mobile** and in Codex, no browser tab. `rly mcp` is a
|
|
46
|
-
|
|
46
|
+
stdio MCP server with no npm runtime dependencies; register it once and the agent gets two tools,
|
|
47
47
|
`relay_ask` (collect decisions/feedback with real form controls) and
|
|
48
48
|
`relay_show` (present a plan, diagram, diff, table, or prototype):
|
|
49
49
|
|
|
@@ -81,16 +81,18 @@ unchanged; pick whichever surface fits.
|
|
|
81
81
|
| Feedback = another wall of text | Click any chart point, diagram node, table cell, or sentence to comment — the agent replies and the thread grows on the board |
|
|
82
82
|
|
|
83
83
|
Everything autosaves in real time (drafts survive timeouts), multiple boards
|
|
84
|
-
run at once, and the package has **zero runtime dependencies** — plain
|
|
85
|
-
Node ≥ 18
|
|
84
|
+
run at once, and the package has **zero npm runtime dependencies** — plain
|
|
85
|
+
Node ≥ 18. Browser-side Chart.js, Mermaid, and Viz.js assets are vendored in
|
|
86
|
+
the package and lazy-loaded offline when a board actually needs them.
|
|
86
87
|
|
|
87
88
|
## Learn more
|
|
88
89
|
|
|
89
90
|
| | |
|
|
90
91
|
|---|---|
|
|
91
92
|
| `rly help` | every command at a glance |
|
|
92
|
-
| `rly git pick` / `rly git cherry-pick` | choose commit actions and rank the order directly on a board |
|
|
93
|
+
| `rly git pick` / `rly git cherry-pick` | choose commit actions and rank the order directly on a board; add `--code` to cherry-pick with split code review and per-hunk Apply/Skip/Hold |
|
|
93
94
|
| `rly git conflict [files…]` | auto-detect unmerged conflict files, or open specific local paths, and return resolved content in `result.blockEdits` |
|
|
95
|
+
| `rly share <board-id>` | activate/list/revoke same-Wi-Fi reviewer or collaborator links for a running browser board |
|
|
94
96
|
| `rly view <file.md> …` | open a quick read-only board that renders local markdown file(s), data files, or PDFs — library-free; great for plans, READMEs, reports, quotes |
|
|
95
97
|
| `rly install --target <agent>` | write relay's rules into an agent's instruction file — `claude` `codex` `cursor` `copilot` `kiro` `windsurf` `cline` `gemini` `opencode` `droid` `agents`; `--all`, `--scope`, `--print`, `--list` |
|
|
96
98
|
| `rly upgrade` | update the CLI **and** refresh the skill in one step (safe around open boards; `--dry-run`, `--cli-only`, `--skill-only`) |
|
|
@@ -102,14 +104,14 @@ Node ≥ 18; Chart.js / Mermaid / Graphviz are vendored and lazy-loaded offline.
|
|
|
102
104
|
## Development
|
|
103
105
|
|
|
104
106
|
```sh
|
|
105
|
-
npm test #
|
|
107
|
+
npm test # smoke tests with no external services (spawns real servers, fake-submits)
|
|
106
108
|
```
|
|
107
109
|
|
|
108
110
|
## Changelog
|
|
109
111
|
|
|
110
112
|
### 0.11.0 — render inline inside the Claude & Codex apps (MCP App)
|
|
111
113
|
- **`rly mcp` — relay as an MCP App** ([SEP-1865](https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp),
|
|
112
|
-
extension `io.modelcontextprotocol/ui`). A
|
|
114
|
+
extension `io.modelcontextprotocol/ui`). A stdio MCP server with no npm runtime dependencies
|
|
113
115
|
that declares a `ui://relay/board` resource (`text/html;profile=mcp-app`) and
|
|
114
116
|
two tools, **`relay_ask`** and **`relay_show`**, linked to it via
|
|
115
117
|
`_meta.ui.resourceUri` (plus `openai/outputTemplate` for ChatGPT/Codex). The
|
|
@@ -172,7 +174,8 @@ npm test # zero-dep smoke tests (spawns real servers, fake-submits)
|
|
|
172
174
|
- **Durable drafts / rescue** — every autosave mirrors to `localStorage`; a
|
|
173
175
|
board whose connection drops blocks further input instead of losing it, and
|
|
174
176
|
`rly rescue <id>` re-serves on the same port so an open tab reconnects.
|
|
175
|
-
- Still **zero runtime dependencies**, offline, and cross-platform.
|
|
177
|
+
- Still **zero npm runtime dependencies**, offline, and cross-platform. Vendored
|
|
178
|
+
browser libraries are loaded only for boards that need them.
|
|
176
179
|
|
|
177
180
|
### 0.9.1
|
|
178
181
|
- The board **intro renders as markdown** (bold/italic/code/links/lists).
|
package/docs/AGENT.md
CHANGED
|
@@ -71,6 +71,28 @@ Answers **autosave in real time** as the user fills the board — a page reload
|
|
|
71
71
|
restores them, and a draft survives timeouts/cancellation (included in those
|
|
72
72
|
results), so partial input is never lost.
|
|
73
73
|
|
|
74
|
+
## Same-Wi-Fi sharing
|
|
75
|
+
|
|
76
|
+
Browser boards are local-owner only by default. A same-Wi-Fi phone/tablet/other
|
|
77
|
+
laptop gets a locked page until sharing is explicitly activated. The owner can
|
|
78
|
+
click **Share** at the bottom of the board, or you can manage the same links for
|
|
79
|
+
the user:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
rly share b-xxxxx # list active share roles
|
|
83
|
+
rly share b-xxxxx --role review # reviewer: add comments only
|
|
84
|
+
rly share b-xxxxx --role collab # collaborator: edit/comment/submit
|
|
85
|
+
rly share b-xxxxx --role review --revoke # revoke one role
|
|
86
|
+
rly share b-xxxxx --revoke --all # revoke all active links
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use `review` unless the user explicitly wants the other device to submit as the
|
|
90
|
+
owner. Reviewers cannot change answers, submit, edit comments, or delete
|
|
91
|
+
comments; their autosaves merge new comments into the existing draft.
|
|
92
|
+
Collaborators can edit answers, comment, open allowed local file links, and
|
|
93
|
+
submit. Shared viewers refresh from the live draft when another viewer saves,
|
|
94
|
+
deferred while someone is typing/commenting.
|
|
95
|
+
|
|
74
96
|
## Inline mode — relay as an MCP App (Claude & Codex apps)
|
|
75
97
|
|
|
76
98
|
Everything above is the **CLI** surface (you run `rly` in a terminal and read
|
|
@@ -166,6 +188,7 @@ Pick/cherry-pick commits or resolve conflicts on a board:
|
|
|
166
188
|
```sh
|
|
167
189
|
rly git pick --limit 20 # checklist: pick/cherry-pick/drop + rank order
|
|
168
190
|
rly git cherry-pick main..feature # checklist: cherry-pick/skip/hold + rank order
|
|
191
|
+
rly git cherry-pick --code main..feature # adds split code review with Apply/Skip/Hold per hunk
|
|
169
192
|
rly git conflict # auto-detect unmerged files with conflict markers
|
|
170
193
|
rly git conflict src/app.js # resolve a specific conflicted file
|
|
171
194
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanglvm/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Question boards with rich blocks (markdown, charts, mermaid, tables, code, diffs, video, sandboxed HTML), clickable local file-links, and element-level annotations for AI coding agents (Claude Code, Codex, …): ask users structured questions, present interactive visuals, collect inline comments, read answers as JSON — in a local browser board OR rendered INLINE inside the Claude & Codex apps as an MCP App (SEP-1865).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
package/skills/relay/SKILL.md
CHANGED
|
@@ -46,7 +46,7 @@ at — show it in a relay board instead of printing it.**
|
|
|
46
46
|
| Gather requirements / plan approval / feedback round | **rly** |
|
|
47
47
|
| Architecture or flow that benefits from a diagram | **rly** (mermaid block) |
|
|
48
48
|
| "Show me the diff" / git diff / code changes / before-after | **rly** (`diff` block — run `git diff`, render it; never dump it in the terminal) |
|
|
49
|
-
| Pick/cherry-pick commits or resolve conflict files | **rly git** (`rly git pick`, `rly git cherry-pick`, `rly git conflict`) |
|
|
49
|
+
| Pick/cherry-pick commits or resolve conflict files | **rly git** (`rly git pick`, `rly git cherry-pick --code`, `rly git conflict`) |
|
|
50
50
|
| A demo, screen recording or walkthrough | **rly** (`video` block) |
|
|
51
51
|
| Point the user at a file to open (log, capture, report) | **rly** (a clickable local file-link in markdown) |
|
|
52
52
|
| Let the user read a markdown file (README, plan, report) | **rly view file.md** (or a `markdown` block with `mdFile`) — never dump the file into the terminal |
|
|
@@ -174,6 +174,28 @@ Exit codes: 0 submitted · 2 timeout · 3 cancelled · 5 not found. On
|
|
|
174
174
|
timeout/cancel the result still contains the autosaved `draft` of partial
|
|
175
175
|
answers and any annotations written so far.
|
|
176
176
|
|
|
177
|
+
## Same-Wi-Fi sharing
|
|
178
|
+
|
|
179
|
+
Browser boards are local-owner only by default. A phone/tablet/other laptop on
|
|
180
|
+
the same Wi-Fi gets a locked page until sharing is explicitly activated. The
|
|
181
|
+
owner can click **Share** at the bottom of the board, or an agent can manage the
|
|
182
|
+
same links with `rly share`:
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
rly share b-xxxxx # list active share roles
|
|
186
|
+
rly share b-xxxxx --role review # activate a reviewer link
|
|
187
|
+
rly share b-xxxxx --role collab # activate a collaborator link
|
|
188
|
+
rly share b-xxxxx --role review --revoke # revoke one role
|
|
189
|
+
rly share b-xxxxx --revoke --all # revoke all active share links
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Use `review` when the other device should add comments only. Reviewers cannot
|
|
193
|
+
change answers, submit, edit comments, or delete comments; their autosaves merge
|
|
194
|
+
new comments into the existing draft. Use `collab` only when the user explicitly
|
|
195
|
+
wants that device to act as the owner: collaborators can edit answers, comment,
|
|
196
|
+
open allowed local file links, and submit. Shared viewers refresh from the live
|
|
197
|
+
draft when another viewer saves, deferred while someone is typing/commenting.
|
|
198
|
+
|
|
177
199
|
## Minimal spec
|
|
178
200
|
|
|
179
201
|
```json
|
|
@@ -347,8 +369,9 @@ the option (and stays annotatable); the label row selects. Use per-option
|
|
|
347
369
|
blocks whenever a question's choices have visual/example context; skip them
|
|
348
370
|
for plainly textual options.
|
|
349
371
|
|
|
350
|
-
Chart.js, Mermaid, and Graphviz are **vendored and lazy-loaded** — the
|
|
351
|
-
|
|
372
|
+
Chart.js, Mermaid, and Graphviz are **vendored and lazy-loaded** — the package
|
|
373
|
+
has no npm runtime dependencies, but these browser assets are bundled for rich
|
|
374
|
+
blocks. PlantUML uses the public plantuml.com server by default;
|
|
352
375
|
pass `"server"` for a self-hosted instance. Legacy `"html"` / `"htmlFile"` /
|
|
353
376
|
`"htmlHeight"` on root or questions are still accepted and normalised automatically.
|
|
354
377
|
|
package/src/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ const VALUED_FLAGS = new Set([
|
|
|
33
33
|
'file', 'html', 'html-file', 'title', 'intro', 'timeout', 'port',
|
|
34
34
|
'submit-label', 'height', 'limit', 'target', 'id', 'replies',
|
|
35
35
|
'on-result', 'notify-cmd', 'idle-grace', 'scope', 'range',
|
|
36
|
-
'host', 'token', 'allow-origin',
|
|
36
|
+
'host', 'token', 'allow-origin', 'role',
|
|
37
37
|
]);
|
|
38
38
|
|
|
39
39
|
function camel(key) {
|
|
@@ -313,6 +313,19 @@ function gitCommitRows(range, limit) {
|
|
|
313
313
|
.filter((r) => r.sha && r.short);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
function gitCommitDiff(commit) {
|
|
317
|
+
return runGit([
|
|
318
|
+
'--no-pager',
|
|
319
|
+
'show',
|
|
320
|
+
'--format=',
|
|
321
|
+
'--patch',
|
|
322
|
+
'--find-renames',
|
|
323
|
+
'--find-copies',
|
|
324
|
+
'--no-ext-diff',
|
|
325
|
+
commit,
|
|
326
|
+
], `git show ${commit} failed`).trimEnd();
|
|
327
|
+
}
|
|
328
|
+
|
|
316
329
|
async function cmdGit(rest) {
|
|
317
330
|
const args = parseArgs(rest);
|
|
318
331
|
const modes = new Set(['pick', 'cherry-pick', 'cherrypick', 'conflict', 'conflicts', 'resolve']);
|
|
@@ -323,11 +336,13 @@ async function cmdGit(rest) {
|
|
|
323
336
|
|
|
324
337
|
const blocks = [];
|
|
325
338
|
const questions = [];
|
|
339
|
+
let reviewDiffCount = 0;
|
|
326
340
|
|
|
327
341
|
if (mode === 'pick' || mode === 'cherry-pick') {
|
|
328
342
|
const range = args.range || args._[0] || '';
|
|
329
343
|
const commits = gitCommitRows(range, args.limit);
|
|
330
344
|
if (!commits.length) throw new CliError(`git log${range ? ` ${range}` : ''} produced no commits to show.`);
|
|
345
|
+
const withCode = args.code === true || args.patch === true || args.diff === true;
|
|
331
346
|
blocks.push({
|
|
332
347
|
type: 'table',
|
|
333
348
|
columns: ['short', 'date', 'author', 'subject'],
|
|
@@ -351,13 +366,32 @@ async function cmdGit(rest) {
|
|
|
351
366
|
? [{ value: 'cherry-pick', label: 'Cherry-pick', tone: 'ok' }, { value: 'skip', label: 'Skip', tone: 'muted' }, { value: 'hold', label: 'Hold', tone: 'warn' }]
|
|
352
367
|
: [{ value: 'pick', label: 'Pick', tone: 'ok' }, { value: 'cherry-pick', label: 'Cherry-pick', tone: 'warn' }, { value: 'drop', label: 'Drop', tone: 'bad' }],
|
|
353
368
|
});
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
369
|
+
if (options.length > 1) {
|
|
370
|
+
questions.push({
|
|
371
|
+
id: 'commit_order',
|
|
372
|
+
type: 'rank',
|
|
373
|
+
label: mode === 'cherry-pick' ? 'Cherry-pick order' : 'Preferred commit order',
|
|
374
|
+
description: 'Drag or move commits into the order the agent should apply them.',
|
|
375
|
+
options,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
if (withCode) {
|
|
379
|
+
for (const c of commits) {
|
|
380
|
+
const diff = gitCommitDiff(c.sha);
|
|
381
|
+
if (!diff.trim()) continue;
|
|
382
|
+
blocks.push({
|
|
383
|
+
type: 'diff',
|
|
384
|
+
title: `${c.short} ${c.subject}`,
|
|
385
|
+
filename: `${c.short} ${c.subject}`,
|
|
386
|
+
commit: c.sha,
|
|
387
|
+
reviewKind: mode,
|
|
388
|
+
review: true,
|
|
389
|
+
view: 'split',
|
|
390
|
+
diff,
|
|
391
|
+
});
|
|
392
|
+
reviewDiffCount++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
const paths = mode === 'conflict' ? gitConflictPaths(args._) : gitConflictPaths([]);
|
|
@@ -381,7 +415,9 @@ async function cmdGit(rest) {
|
|
|
381
415
|
: mode === 'cherry-pick' ? 'Cherry-pick commits' : 'Pick commits');
|
|
382
416
|
const intro = args.intro || (conflictCount
|
|
383
417
|
? 'Resolve each conflict on the board. The submitted result includes result.blockEdits with per-hunk choices and a full resolved file preview for every conflict block.'
|
|
384
|
-
:
|
|
418
|
+
: reviewDiffCount
|
|
419
|
+
? 'Review commit code hunks on the board. Apply, skip, or hold each hunk; submit returns commit actions plus diff-review hunk choices as JSON.'
|
|
420
|
+
: 'Pick commit actions and order on the board. Submit returns the selected actions and rank order as JSON.');
|
|
385
421
|
const spec = normalizeSpec({ title, intro, blocks, questions, submitLabel: 'Submit git choices' });
|
|
386
422
|
await assertSpecReady(spec);
|
|
387
423
|
const record = createBoard(spec);
|
|
@@ -743,6 +779,45 @@ async function cmdResult(args) {
|
|
|
743
779
|
return 5;
|
|
744
780
|
}
|
|
745
781
|
|
|
782
|
+
async function cmdShare(args) {
|
|
783
|
+
const record = mustLoad(args._[0]);
|
|
784
|
+
const running = loadRunning(record.id);
|
|
785
|
+
if (!running || !isAlive(running.pid)) {
|
|
786
|
+
throw new CliError(`board "${record.id}" is not running. Use \`rly reopen ${record.id} --detach\` first.`, 5);
|
|
787
|
+
}
|
|
788
|
+
const role = args.all ? 'all' : args.role;
|
|
789
|
+
if (role !== undefined && !['collab', 'review', 'all'].includes(role)) {
|
|
790
|
+
throw new CliError('--role must be collab or review.', 4);
|
|
791
|
+
}
|
|
792
|
+
const shareUrl = new URL('/api/share', running.url);
|
|
793
|
+
let res;
|
|
794
|
+
try {
|
|
795
|
+
if (args.revoke) {
|
|
796
|
+
res = await fetch(shareUrl, {
|
|
797
|
+
method: 'DELETE',
|
|
798
|
+
headers: { 'content-type': 'application/json' },
|
|
799
|
+
body: JSON.stringify({ role: role || 'all' }),
|
|
800
|
+
});
|
|
801
|
+
} else if (role && role !== 'all') {
|
|
802
|
+
res = await fetch(shareUrl, {
|
|
803
|
+
method: 'POST',
|
|
804
|
+
headers: { 'content-type': 'application/json' },
|
|
805
|
+
body: JSON.stringify({ role }),
|
|
806
|
+
});
|
|
807
|
+
} else {
|
|
808
|
+
res = await fetch(shareUrl, { method: 'GET' });
|
|
809
|
+
}
|
|
810
|
+
} catch (err) {
|
|
811
|
+
throw new CliError(`could not reach board "${record.id}" at ${running.url}: ${String((err && err.message) || err)}`, 5);
|
|
812
|
+
}
|
|
813
|
+
const body = await res.json().catch(() => null);
|
|
814
|
+
if (!res.ok) {
|
|
815
|
+
throw new CliError((body && body.error) || `share request failed with HTTP ${res.status}`, res.status === 400 ? 4 : 5);
|
|
816
|
+
}
|
|
817
|
+
printJson({ status: args.revoke ? 'revoked' : role && role !== 'all' ? 'active' : 'shares', boardId: record.id, ...body });
|
|
818
|
+
return 0;
|
|
819
|
+
}
|
|
820
|
+
|
|
746
821
|
function cmdList(args) {
|
|
747
822
|
const running = listRunning();
|
|
748
823
|
if (args.json) {
|
|
@@ -1020,6 +1095,10 @@ the user should view — put it in relay instead of printing it.**
|
|
|
1020
1095
|
- Point the user at a file with a clickable local path in a markdown block; embed a
|
|
1021
1096
|
screen recording with a \`video\` block; when answer choices are visual, give each
|
|
1022
1097
|
option its own visual (\`options[].blocks\`) so the user picks by looking.
|
|
1098
|
+
- Same-Wi-Fi sharing is locked by default. When the user wants another device to
|
|
1099
|
+
review or co-fill a running board, manage explicit links with \`rly share <id>\`:
|
|
1100
|
+
\`--role review\` creates a comments-only reviewer link, \`--role collab\` creates
|
|
1101
|
+
an edit/comment/submit link, and \`--revoke\` disables active links.
|
|
1023
1102
|
- **There's a purpose-built component for most content — use the MOST SPECIFIC one,
|
|
1024
1103
|
never plain prose when a block fits.** Blocks: \`table\` (sortable/filterable/CSV,
|
|
1025
1104
|
load from .csv/.json), \`chart\`, \`kpi\` (stat cards), \`mermaid\`/\`graphviz\`/\`plantuml\`,
|
|
@@ -1515,6 +1594,7 @@ USAGE
|
|
|
1515
1594
|
--title; other args pass to git: rly diff --staged | HEAD~1 | -- path)
|
|
1516
1595
|
rly git pick [range] board for choosing pick/cherry-pick/drop actions plus commit order
|
|
1517
1596
|
rly git cherry-pick [range] board for choosing commits and cherry-pick order
|
|
1597
|
+
add --code for split code view with per-hunk Apply/Skip/Hold
|
|
1518
1598
|
rly git conflict [files…] board for resolving conflict-marker files; with no files, auto-detects
|
|
1519
1599
|
unmerged git files and returns resolved content in result.blockEdits
|
|
1520
1600
|
rly wait <id> [--timeout 3600] block until board finishes, print result JSON
|
|
@@ -1524,6 +1604,11 @@ USAGE
|
|
|
1524
1604
|
NB: also writes the FULL result to "resultFile" (first field) —
|
|
1525
1605
|
read that file if your shell truncates stdout; never pipe to head/tail
|
|
1526
1606
|
rly result <id> result/status now (includes live autosaved draft + presence while open)
|
|
1607
|
+
rly share <id> list active same-Wi-Fi share links for a running board
|
|
1608
|
+
rly share <id> --role review activate a reviewer link (comments only)
|
|
1609
|
+
rly share <id> --role collab activate a collaborator link (edit/comment/submit)
|
|
1610
|
+
rly share <id> --role review --revoke
|
|
1611
|
+
revoke one share role; use --revoke --all to disable all roles
|
|
1527
1612
|
rly list [--json] running boards
|
|
1528
1613
|
rly open [id] re-open the browser tab of a running board
|
|
1529
1614
|
rly reopen <id> [--replies f.json] serve a saved board again, prefilled with saved answers
|
|
@@ -1607,6 +1692,8 @@ export async function main(argv) {
|
|
|
1607
1692
|
return await cmdWait(parseArgs(rest));
|
|
1608
1693
|
case 'result':
|
|
1609
1694
|
return await cmdResult(parseArgs(rest));
|
|
1695
|
+
case 'share':
|
|
1696
|
+
return await cmdShare(parseArgs(rest));
|
|
1610
1697
|
case 'list':
|
|
1611
1698
|
return cmdList(parseArgs(rest));
|
|
1612
1699
|
case 'open':
|
package/src/mcp.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// mcp.js — relay as an MCP App (SEP-1865, extension "io.modelcontextprotocol/ui").
|
|
2
2
|
//
|
|
3
|
-
// `rly mcp` starts
|
|
4
|
-
// host that supports MCP Apps (Claude desktop/mobile, Codex,
|
|
5
|
-
// boards render INLINE in the conversation instead of opening a
|
|
3
|
+
// `rly mcp` starts an MCP server over stdio with no npm runtime dependencies.
|
|
4
|
+
// Register it with a host that supports MCP Apps (Claude desktop/mobile, Codex,
|
|
5
|
+
// …) and relay's boards render INLINE in the conversation instead of opening a
|
|
6
|
+
// browser tab:
|
|
6
7
|
//
|
|
7
8
|
// • the server declares a UI resource ui://relay/board (text/html;profile=mcp-app)
|
|
8
9
|
// • the tools `relay_ask` / `relay_show` link to it via _meta.ui.resourceUri
|