@runsnative/mcp-server 0.2.0 → 0.4.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.
Files changed (98) hide show
  1. package/README.md +7 -1
  2. package/dist/components-inline.js +15478 -0
  3. package/dist/dev-host.js +68 -0
  4. package/dist/iframe-runtime.js +103 -0
  5. package/dist/index.js +2 -103
  6. package/dist/server.js +127 -0
  7. package/dist/surface/assets.js +89 -0
  8. package/dist/surface/gathering-card.js +162 -0
  9. package/dist/surface/iframe-runtime-entry.js +8 -0
  10. package/dist/surface/package-surface.js +59 -0
  11. package/dist/tokens/tokens-arctic-frost.css +1320 -0
  12. package/dist/tokens/tokens-botanical-garden.css +1320 -0
  13. package/dist/tokens/tokens-bridgesai.css +1269 -0
  14. package/dist/tokens/tokens-clay-studio.css +1320 -0
  15. package/dist/tokens/tokens-daisy-acid.css +1320 -0
  16. package/dist/tokens/tokens-daisy-aqua.css +1320 -0
  17. package/dist/tokens/tokens-daisy-autumn.css +1320 -0
  18. package/dist/tokens/tokens-daisy-black.css +1321 -0
  19. package/dist/tokens/tokens-daisy-bumblebee.css +1320 -0
  20. package/dist/tokens/tokens-daisy-business.css +1320 -0
  21. package/dist/tokens/tokens-daisy-cmyk.css +1321 -0
  22. package/dist/tokens/tokens-daisy-coffee.css +1320 -0
  23. package/dist/tokens/tokens-daisy-corporate.css +1320 -0
  24. package/dist/tokens/tokens-daisy-cupcake.css +1320 -0
  25. package/dist/tokens/tokens-daisy-cyberpunk.css +1320 -0
  26. package/dist/tokens/tokens-daisy-dark.css +1320 -0
  27. package/dist/tokens/tokens-daisy-dim.css +1320 -0
  28. package/dist/tokens/tokens-daisy-dracula.css +1320 -0
  29. package/dist/tokens/tokens-daisy-emerald.css +1320 -0
  30. package/dist/tokens/tokens-daisy-fantasy.css +1320 -0
  31. package/dist/tokens/tokens-daisy-forest.css +1320 -0
  32. package/dist/tokens/tokens-daisy-garden.css +1320 -0
  33. package/dist/tokens/tokens-daisy-halloween.css +1320 -0
  34. package/dist/tokens/tokens-daisy-lemonade.css +1320 -0
  35. package/dist/tokens/tokens-daisy-light.css +1320 -0
  36. package/dist/tokens/tokens-daisy-lofi.css +1321 -0
  37. package/dist/tokens/tokens-daisy-luxury.css +1320 -0
  38. package/dist/tokens/tokens-daisy-night.css +1320 -0
  39. package/dist/tokens/tokens-daisy-nord.css +1320 -0
  40. package/dist/tokens/tokens-daisy-pastel.css +1320 -0
  41. package/dist/tokens/tokens-daisy-retro.css +1320 -0
  42. package/dist/tokens/tokens-daisy-sunset.css +1320 -0
  43. package/dist/tokens/tokens-daisy-synthwave.css +1320 -0
  44. package/dist/tokens/tokens-daisy-valentine.css +1320 -0
  45. package/dist/tokens/tokens-daisy-winter.css +1320 -0
  46. package/dist/tokens/tokens-daisy-wireframe.css +1321 -0
  47. package/dist/tokens/tokens-daisyui-base.css +1269 -0
  48. package/dist/tokens/tokens-desert-rose.css +1320 -0
  49. package/dist/tokens/tokens-dotorg-site.css +1269 -0
  50. package/dist/tokens/tokens-forest-canopy.css +1320 -0
  51. package/dist/tokens/tokens-golden-hour.css +1320 -0
  52. package/dist/tokens/tokens-harlow-pets.css +1320 -0
  53. package/dist/tokens/tokens-kehribar.css +1320 -0
  54. package/dist/tokens/tokens-midnight-galaxy.css +1320 -0
  55. package/dist/tokens/tokens-modern-minimalist.css +1270 -0
  56. package/dist/tokens/tokens-ocean-depths.css +1320 -0
  57. package/dist/tokens/tokens-showcase-base.css +1269 -0
  58. package/dist/tokens/tokens-smoke-bold.css +1269 -0
  59. package/dist/tokens/tokens-smoke-friendly.css +1269 -0
  60. package/dist/tokens/tokens-smoke-minimal.css +1281 -0
  61. package/dist/tokens/tokens-smoke-premium.css +1280 -0
  62. package/dist/tokens/tokens-smoke-professional.css +1269 -0
  63. package/dist/tokens/tokens-smoke-retail.css +1321 -0
  64. package/dist/tokens/tokens-smoke-startup.css +1269 -0
  65. package/dist/tokens/tokens-solvlys.css +1320 -0
  66. package/dist/tokens/tokens-sunset-boulevard.css +1320 -0
  67. package/dist/tokens/tokens-tech-innovation.css +1320 -0
  68. package/dist/tokens/tokens-theme-atrium.css +1320 -0
  69. package/dist/tokens/tokens-theme-catalyst.css +1320 -0
  70. package/dist/tokens/tokens-theme-circuit.css +1320 -0
  71. package/dist/tokens/tokens-theme-dispatch.css +1320 -0
  72. package/dist/tokens/tokens-theme-dither.css +1320 -0
  73. package/dist/tokens/tokens-theme-ember.css +1320 -0
  74. package/dist/tokens/tokens-theme-fanfare.css +1320 -0
  75. package/dist/tokens/tokens-theme-ferro.css +1321 -0
  76. package/dist/tokens/tokens-theme-folio.css +1320 -0
  77. package/dist/tokens/tokens-theme-hamara.css +1270 -0
  78. package/dist/tokens/tokens-theme-komorebi.css +1321 -0
  79. package/dist/tokens/tokens-theme-kyo.css +1269 -0
  80. package/dist/tokens/tokens-theme-lattice.css +1320 -0
  81. package/dist/tokens/tokens-theme-ma.css +1321 -0
  82. package/dist/tokens/tokens-theme-marquee.css +1320 -0
  83. package/dist/tokens/tokens-theme-meridian.css +1320 -0
  84. package/dist/tokens/tokens-theme-nimbus.css +1320 -0
  85. package/dist/tokens/tokens-theme-plasma.css +1320 -0
  86. package/dist/tokens/tokens-theme-plinth.css +1320 -0
  87. package/dist/tokens/tokens-theme-quartz.css +1320 -0
  88. package/dist/tokens/tokens-theme-sus.css +1320 -0
  89. package/dist/tokens/tokens-theme-trellis.css +1269 -0
  90. package/dist/tokens/tokens-theme-vesper.css +1320 -0
  91. package/dist/tokens/tokens-theme-wabi.css +1320 -0
  92. package/dist/tools/get-marker-capture.js +18 -3
  93. package/dist/tools/navigate.js +18 -2
  94. package/dist/tools/render-gathering.js +88 -0
  95. package/dist/tools/set-instance-variant.js +18 -3
  96. package/dist/tools/set-theme.js +18 -2
  97. package/package.json +5 -2
  98. package/dist/tools/surface-preview.js +0 -36
@@ -4,13 +4,28 @@ const CROP_PREFIX = 'data:image/png;base64,';
4
4
  export const GET_MARKER_CAPTURE_TOOL = {
5
5
  name: 'get_marker_capture',
6
6
  description: 'Fetch pending marker captures from the linked runsnative.org tab — regions the user circled ' +
7
- 'with the marker overlay. Each capture carries the resolved component-instance address, its ' +
8
- 'render inputs, an optional user note, and the pixel crop as an image. Single delivery: a ' +
9
- 'capture is returned once. Requires an active linked session (link_session first).',
7
+ 'with the marker overlay. Use when the user says they marked, circled, or pointed at something ' +
8
+ 'on the site, or asks "can you see what I selected?". Each capture carries the resolved ' +
9
+ 'component-instance address (usable with set_instance_variant), its render inputs, an optional ' +
10
+ 'user note, and the pixel crop as an image so you see exactly what they saw. Single delivery: a ' +
11
+ 'capture is returned once. Requires an active linked session (call link_session first).',
10
12
  inputSchema: {
11
13
  type: 'object',
12
14
  properties: {},
13
15
  },
16
+ // LSP §5.1 (linked-session-protocol-v0.1, mukadra repo) — session-targeting,
17
+ // but the READ leg of the bridge: it drains the session's capture queue
18
+ // rather than enacting a command, so there is no allow-list `enactment`
19
+ // (the field is optional per §5.1).
20
+ _meta: {
21
+ lsp: {
22
+ target: 'session',
23
+ tier: 'reversible',
24
+ consent: 'implicit',
25
+ entitlement: 'free',
26
+ visibility: 'advertised',
27
+ },
28
+ },
14
29
  };
15
30
  export async function handleGetMarkerCapture(_args) {
16
31
  const pairId = getLinkedPairId();
@@ -2,8 +2,11 @@ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { postWorkerApi, getLinkedPairId } from '../bridge-client.js';
3
3
  export const NAVIGATE_TOOL = {
4
4
  name: 'navigate',
5
- description: 'Navigate the linked runsnative.org tab to a path. Slash shim: /rn-go <path>. ' +
6
- 'Reversible — the user can navigate back. Requires an active linked session (link_session first).',
5
+ description: "Send the user's linked runsnative.org tab to a different page. Use when the user asks to see, open, or go to " +
6
+ 'something on the site a component page, a docs section, the gallery so they watch their own tab move ' +
7
+ 'instead of following a pasted link. The command is enqueued and the tab navigates within one poll interval. ' +
8
+ 'Reversible — the user can navigate back. Requires an active linked session (call link_session first). ' +
9
+ 'Slash shim: /rn-go <path>.',
7
10
  inputSchema: {
8
11
  type: 'object',
9
12
  properties: {
@@ -11,6 +14,19 @@ export const NAVIGATE_TOOL = {
11
14
  },
12
15
  required: ['path'],
13
16
  },
17
+ // LSP §5.1 (linked-session-protocol-v0.1, mukadra repo) — marks this as a
18
+ // session-targeting capability. `enactment` names the worker allow-list
19
+ // entry this tool submits (JUNE-626).
20
+ _meta: {
21
+ lsp: {
22
+ target: 'session',
23
+ tier: 'reversible',
24
+ consent: 'implicit',
25
+ entitlement: 'free',
26
+ visibility: 'advertised',
27
+ enactment: 'navigation.navigate',
28
+ },
29
+ },
14
30
  };
15
31
  export async function handleNavigate(args) {
16
32
  const path = args['path'];
@@ -0,0 +1,88 @@
1
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
+ import { getWorkerApi } from '../bridge-client.js';
3
+ import { componentsBundle, iframeRuntime, themeCss } from '../surface/assets.js';
4
+ import { buildGatheringCardHtml, GATHERING_CARD_URI } from '../surface/gathering-card.js';
5
+ import { SURFACE_MIME_TYPE } from '../surface/package-surface.js';
6
+ // Display labels for the brands offered by the create-gathering form
7
+ // (compare.js). Unknown brand_ids fall back to the id itself client-side.
8
+ const BRAND_LABELS = {
9
+ 'dotorg-site': 'RunsNative',
10
+ 'harlow-pets': 'Harlow Pets',
11
+ 'clay-studio': 'Kiln & Clay Studio',
12
+ };
13
+ export const RENDER_GATHERING_TOOL = {
14
+ name: 'render_gathering',
15
+ description: "Render the user's most recent Convene gathering as a live, branded, interactive card " +
16
+ 'inside this conversation (an MCP App surface — real run-* components, the tenant\'s own ' +
17
+ 'theme, a working View action). Use as the finale of the create-a-gathering exercise, ' +
18
+ 'right after the user has created their gathering on the site. Requires the tenant ' +
19
+ 'API token (RUNSNATIVE_TENANT_TOKEN) so the gathering can be fetched from the tenant store.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {},
23
+ required: [],
24
+ },
25
+ _meta: {
26
+ ui: { resourceUri: GATHERING_CARD_URI },
27
+ // Legacy key for hosts predating _meta.ui (ext-apps compatibility shim).
28
+ 'ui/resourceUri': GATHERING_CARD_URI,
29
+ },
30
+ };
31
+ export const GATHERING_CARD_RESOURCE = {
32
+ uri: GATHERING_CARD_URI,
33
+ name: 'Gathering Card',
34
+ description: 'Self-contained MCP App shell that renders a Convene gathering as a branded RunsNative card. ' +
35
+ 'Data arrives via the tool-result notification; nothing external is loaded.',
36
+ mimeType: SURFACE_MIME_TYPE,
37
+ };
38
+ export async function readGatheringCardResource() {
39
+ const html = buildGatheringCardHtml({
40
+ componentsBundle: componentsBundle(),
41
+ iframeRuntime: iframeRuntime(),
42
+ });
43
+ return {
44
+ contents: [{ uri: GATHERING_CARD_URI, mimeType: SURFACE_MIME_TYPE, text: html }],
45
+ };
46
+ }
47
+ export async function handleRenderGathering() {
48
+ const res = await getWorkerApi('/tenant/gatherings');
49
+ if (res.status === 401) {
50
+ throw new McpError(ErrorCode.InvalidParams, 'Not authenticated with the tenant store. Set RUNSNATIVE_TENANT_TOKEN in the MCP server ' +
51
+ 'config (the same tenant the user signs into on the site — a linked session guarantees they match).');
52
+ }
53
+ if (!res.ok) {
54
+ const body = await res.text().catch(() => '');
55
+ throw new McpError(ErrorCode.InternalError, `Worker error ${res.status}: ${body}`);
56
+ }
57
+ const data = (await res.json());
58
+ const gatherings = data.gatherings ?? [];
59
+ if (gatherings.length === 0) {
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: 'No gatherings found for this tenant yet. Create one first — on the Convene page ' +
65
+ '(/compare/), sign in and submit the create-gathering form (exercise step 2) — then call this tool again.',
66
+ },
67
+ ],
68
+ };
69
+ }
70
+ // Rows arrive ORDER BY created_at DESC — the first is the most recent.
71
+ const gathering = gatherings[0];
72
+ const css = await themeCss(gathering.brand_id);
73
+ return {
74
+ content: [
75
+ {
76
+ type: 'text',
77
+ text: `Rendered "${gathering.title}" (${gathering.brand_id}) as a live card in the conversation. ` +
78
+ 'The card is the real surface — same components and theme as the site, with a working View action.',
79
+ },
80
+ ],
81
+ structuredContent: {
82
+ gathering,
83
+ theme_css: css,
84
+ theme_landed: css !== null,
85
+ brand_label: BRAND_LABELS[gathering.brand_id] ?? null,
86
+ },
87
+ };
88
+ }
@@ -4,9 +4,11 @@ import { postWorkerApi, getLinkedPairId } from '../bridge-client.js';
4
4
  const RUN_BUTTON_VARIANTS = ['primary', 'secondary', 'outline', 'ghost', 'danger', 'link'];
5
5
  export const SET_INSTANCE_VARIANT_TOOL = {
6
6
  name: 'set_instance_variant',
7
- description: 'Change the variant of a specific run-button instance on the linked runsnative.org tab, ' +
8
- 'targeted by the instance_id captured via the marker overlay. Slash shim: /rn-set-variant. ' +
9
- 'Reversible the user can switch back at any time. Requires an active linked session (link_session first).',
7
+ description: 'Restyle ONE specific run-button on the linked runsnative.org tab — the instance the user circled with the ' +
8
+ 'marker overlay leaving every other element untouched. Use after get_marker_capture hands you an ' +
9
+ 'address.instanceId and the user wants that exact button tried in a different variant. The command is enqueued ' +
10
+ 'and the tab applies it within one poll interval, if the instance is still on the page. Reversible — set it ' +
11
+ 'back at any time. Requires an active linked session (call link_session first). Slash shim: /rn-set-variant.',
10
12
  inputSchema: {
11
13
  type: 'object',
12
14
  properties: {
@@ -21,6 +23,19 @@ export const SET_INSTANCE_VARIANT_TOOL = {
21
23
  },
22
24
  required: ['instance_id', 'variant'],
23
25
  },
26
+ // LSP §5.1 (linked-session-protocol-v0.1, mukadra repo) — marks this as a
27
+ // session-targeting capability. `enactment` names the worker allow-list
28
+ // entry this tool submits (JUNE-626).
29
+ _meta: {
30
+ lsp: {
31
+ target: 'session',
32
+ tier: 'reversible',
33
+ consent: 'implicit',
34
+ entitlement: 'free',
35
+ visibility: 'advertised',
36
+ enactment: 'instance.setVariant',
37
+ },
38
+ },
24
39
  };
25
40
  export async function handleSetInstanceVariant(args) {
26
41
  const instanceId = args['instance_id'];
@@ -2,8 +2,11 @@ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { postWorkerApi, getLinkedPairId } from '../bridge-client.js';
3
3
  export const SET_THEME_TOOL = {
4
4
  name: 'set_theme',
5
- description: 'Apply a theme (color palette) to the linked runsnative.org tab. Slash shim: /rn-theme <theme>. ' +
6
- 'Reversible the user can switch back at any time. Requires an active linked session (link_session first).',
5
+ description: "Restyle the user's linked runsnative.org tab with a named theme colors, surfaces, and typography change live " +
6
+ 'in their browser. Use when the user asks to change how the site looks, try a different brand feel, or compare ' +
7
+ 'palettes. The command is enqueued and the tab applies it within one poll interval; nothing is saved — fully ' +
8
+ 'reversible, switch again or back at any time. Requires an active linked session (call link_session first). ' +
9
+ 'Slash shim: /rn-theme <theme>.',
7
10
  inputSchema: {
8
11
  type: 'object',
9
12
  properties: {
@@ -11,6 +14,19 @@ export const SET_THEME_TOOL = {
11
14
  },
12
15
  required: ['theme'],
13
16
  },
17
+ // LSP §5.1 (linked-session-protocol-v0.1, mukadra repo) — marks this as a
18
+ // session-targeting capability. `enactment` names the worker allow-list
19
+ // entry this tool submits (JUNE-626).
20
+ _meta: {
21
+ lsp: {
22
+ target: 'session',
23
+ tier: 'reversible',
24
+ consent: 'implicit',
25
+ entitlement: 'free',
26
+ visibility: 'advertised',
27
+ enactment: 'theme.setTheme',
28
+ },
29
+ },
14
30
  };
15
31
  export async function handleSetTheme(args) {
16
32
  const theme = args['theme'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runsnative/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/"
@@ -15,7 +15,7 @@
15
15
  "runsnative-mcp": "./dist/index.js"
16
16
  },
17
17
  "scripts": {
18
- "build": "tsc",
18
+ "build": "tsc && node scripts/build-iframe-runtime.mjs",
19
19
  "dev": "tsc --watch",
20
20
  "start": "node dist/index.js",
21
21
  "http-server": "node dist/http-server.js",
@@ -24,10 +24,13 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
+ "@modelcontextprotocol/ext-apps": "^1.7.4",
27
28
  "@modelcontextprotocol/sdk": "^1.0.0"
28
29
  },
29
30
  "devDependencies": {
31
+ "@runsnative/components": "^0.5.0",
30
32
  "@types/node": "^25.6.0",
33
+ "esbuild": "^0.25.0",
31
34
  "playwright": "^1.58.2",
32
35
  "sharp": "^0.33.0",
33
36
  "typescript": "^5.5.3",
@@ -1,36 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { resolve, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
6
- export const SURFACE_PREVIEW_TOOL = {
7
- name: 'surface_preview',
8
- description: 'Renders a RunsNative component fixture (run-table + run-button) inside an MCP App ' +
9
- 'iframe. Used to verify that RunsNative web components hydrate correctly in the claude.ai ' +
10
- 'MCP Apps sandbox. Returns HTML content. This tool exists for RUN-477 verification — ' +
11
- 'it is not a production surface.',
12
- inputSchema: {
13
- type: 'object',
14
- properties: {},
15
- required: [],
16
- },
17
- };
18
- export function handleSurfacePreview() {
19
- // Read fixture from the fixtures/ sibling dir at call time, not compile time,
20
- // so hot-reloading the fixture doesn't require rebuilding the server.
21
- // In dist/, __dirname resolves to dist/tools/; fixture is at ../../fixtures/.
22
- const fixturePath = resolve(__dirname, '../../fixtures/surface-preview.html');
23
- const html = readFileSync(fixturePath, 'utf8');
24
- return {
25
- content: [
26
- {
27
- type: 'resource',
28
- resource: {
29
- uri: 'data:text/html;charset=utf-8,surface-preview',
30
- mimeType: 'text/html',
31
- text: html,
32
- },
33
- },
34
- ],
35
- };
36
- }