vantage-peers-mcp 2.3.5 → 2.4.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,109 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.4.0] — 2026-05-28 — M3 iframeEmbedSessions + __VP_TOOL_RESULT__ stream marker + ack-checklist
4
+
5
+ **Mission instance** : `sigma-vantage-peers-mcp-gui-iframe-embed-v1` (k5730xct6rvrwkvxhy5t5js12d87jwfw).
6
+ **Pi sign-off** : PI_AUTHORIZED_TASK_ID=`k1793m1qgn0zaay6r87dhvsh7187kwya` (PROD-DEPLOY-AUTHORIZED).
7
+ **Eta sign-off** : ETA_APPROVED_TASK_ID=`k171ep964sxabbrgmb21fk9axd87ka1n` at commit `338a7b9e6130ce69dc5fe7f3e2e9ecc4648b4f6a` (Day 79 SHA-pinned).
8
+ **Merge** : PR #545 squash `f509c8d92f0b142bc063a0e9dd070e1993cc729b`.
9
+
10
+ M3 delivers the session registry and stream-marker protocol that connects the VP MCP server
11
+ to the Gen UI iframe bridge. All marker emission is gated behind `VP_EMIT_UI_MARKERS=1`
12
+ so production behaviour is unchanged until the bridge is deployed.
13
+
14
+ ### Convex schema — `iframeEmbedSessions` table
15
+
16
+ NEW table `iframeEmbedSessions` in `convex/schema.ts` :
17
+ - Fields : `sessionId` (string), `tenantId` (optional string), `origin` (string),
18
+ `userId` (optional string), `createdAt` (number), `lastSeenAt` (number),
19
+ `expiresAt` (number), `revoked` (boolean).
20
+ - Indexes : `by_session_id` on `["sessionId"]`, `by_origin_expires` on `["origin", "expiresAt"]`.
21
+
22
+ NEW `convex/iframeEmbedSessions.ts` — 4 operations :
23
+ - `createSession` mutation — inserts a new session row.
24
+ - `getSession` query — returns session or null (null for expired / revoked).
25
+ - `touchSession` mutation — bumps `lastSeenAt` to now; returns bool.
26
+ - `revokeSession` mutation — sets `revoked=true`; returns bool.
27
+
28
+ ### Stream marker — `mcp-server/src/ui-resources/stream-marker.ts`
29
+
30
+ NEW `MARKER_START = "__VP_TOOL_RESULT__"`, `MARKER_END = "__END__"`.
31
+
32
+ NEW `wrapToolResult(payload: VpToolResult): string` :
33
+ - Validates via `VpToolResultSchema`, throws `TypeError` on schema failure.
34
+ - Returns `__VP_TOOL_RESULT__<json>__END__`.
35
+
36
+ NEW `parseToolResult(text: string): VpToolResult | null` :
37
+ - Extracts marker substring (handles bare, embedded, surrounding text).
38
+ - Returns validated `VpToolResult` or null on any failure (no-throw contract).
39
+
40
+ ### MCP tools — marker emission gated by `VP_EMIT_UI_MARKERS=1`
41
+
42
+ `mcp-server/src/tools.ts` — 6 tools now append `wrapToolResult(...)` after the JSON payload
43
+ when `VP_EMIT_UI_MARKERS=1` (default OFF) :
44
+
45
+ | Tool | kind |
46
+ |-----------------------|--------------------|
47
+ | `list_tasks` | `tasks-table` |
48
+ | `list_messages` | `messages-feed` |
49
+ | `get_diary` | `diary-entry` |
50
+ | `list_missions` | `mission-timeline` |
51
+ | `list_briefing_notes` | `briefing-note` |
52
+ | `list_memories` | `memory-quote` |
53
+
54
+ Change is surgical — existing return shape is preserved; marker is appended as a new line.
55
+
56
+ ### Ack checklist
57
+
58
+ NEW `docs/M3-ACK-CHECKLIST.md` — bilingual FR/EN post-deploy verification checklist
59
+ for Marie + Ismaël. Covers: package install, primitive reads, Shadow DOM scoping,
60
+ stream marker emit + parse, bilingual spot check, WCAG AA (contrast + role attrs),
61
+ default-OFF guard.
62
+
63
+ ### Tests
64
+
65
+ 15+ new vitest cases (≥264 total after M3, baseline 253 after M2) :
66
+ - `mcp-server/src/__tests__/m3-stream-marker.test.ts` — 14 cases:
67
+ `wrapToolResult` ×6 valid kinds, ×2 throws on invalid, `parseToolResult` roundtrip,
68
+ non-marker text ×2, embedded text, malformed JSON ×2, schema rejects unknown kind ×2.
69
+ - `convex/iframeEmbedSessions.test.ts` — 7 cases:
70
+ create+get, optional fields, getSession unknown, expired session null,
71
+ touchSession updates lastSeenAt, touchSession unknown false,
72
+ revokeSession marks revoked (getSession null), revokeSession unknown false.
73
+
74
+ 0 regression on M1+M2 suites (253/253 baseline).
75
+
76
+ ---
77
+
78
+ ## [Unreleased] — M1 SEP-1865 ui:// resources backend + M2 primitives + Zod schemas
79
+
80
+ **Mission instance** : `sigma-vantage-peers-mcp-gui-iframe-embed-v1` (k5730xct6rvrwkvxhy5t5js12d87jwfw).
81
+ **Template VR consumed** : `gui-iframe-embed-v1` v1.0.0 (jx7bzk0x1086tgwgj2zrssk2pn87k1ga).
82
+
83
+ M1 Foundation (adapted MCP-pure paradigm per Pi arbitrage Day 84) :
84
+ - NEW `mcp-server/src/ui-resources/index.ts` : URI parser `ui://vp/v1/<primitive>?<query>` + primitive registry + handler factory.
85
+ - NEW `mcp-server/src/ui-resources/primitives/tasks-table.ts` : M1 MVP primitive returning HTML inline (Shadow DOM scoped CSS) — WCAG AA + bilingual FR+EN.
86
+ - `mcp-server/server-http.ts` : wired `ListResourcesRequestSchema` + `ReadResourceRequestSchema` MCP handlers on the existing McpServer instance.
87
+
88
+ Tests : 14 new vitest cases (`src/__tests__/ui-resources-sep-1865.test.ts`) — URI parsing, primitive registry, render variants (empty, populated, FR), backend arg forwarding, XSS escape, error fallback, limit clamping, unknown primitive rejection. 0 regression on existing suites.
89
+
90
+ ### M2 — Resolve 5 Gaps + Bearer sha256 hardening (adapted MCP-pure paradigm)
91
+
92
+ 5 new ui:// primitives :
93
+ - `messages-feed` (`messages:listMessages` backend — channel filter applied client-side)
94
+ - `diary-entry` (`diary:get` single-entry + `diary:list` multi-entry backend)
95
+ - `mission-timeline` (`missions:list` backend with fields=lite)
96
+ - `briefing-note` (`briefingNotes:get` by noteId OR `briefingNotes:list` by topic backend)
97
+ - `memory-quote` (`memories:listMemories` backend — supports both plain-array and paginated result shapes)
98
+
99
+ Zod discriminated union schemas : `mcp-server/src/ui-resources/schemas.ts` exports `VpTaskPayloadSchema` + `VpMessagePayloadSchema` + `VpDiaryEntryPayloadSchema` + `VpMissionPayloadSchema` + `VpBriefingNotePayloadSchema` + `VpMemoryPayloadSchema` + `VpToolResultSchema` (discriminated union by `kind`). Cross-fleet ready for Mu vantage-bridge sidepanel S3 consumer.
100
+
101
+ Bearer sha256 validation : Already in place since v2.3.4 DCR security fix. `mcp-server/src/auth.ts` line 275 calls `sha256Hex(token)` before every Convex lookup (layers 2 and 4). Raw token never reaches Convex. No further hardening needed in M2.
102
+
103
+ Tests : 42 new vitest cases in `src/__tests__/ui-resources-m2-primitives.test.ts` (target was ≥22). Covers : PRIMITIVES registry (6 entries), each of 5 new primitives (empty + populated + FR labels + XSS escape + error fallback = 5 cases each), Zod schema roundtrip (VpToolResultSchema all 6 variants accepted, malformed rejected, individual payload schema validations). 0 regression on M1 17 cases + 194 other MCP tests (253/253 total).
104
+
105
+ M3 next : Registry json-render + `__VP_TOOL_RESULT__<json>` stream marker + smoke E2E + ack-checklist + PI-SIGNED Convex prod deploy + visual ack Marie/Ismaël.
106
+
3
107
  ## v2.3.5 — 2026-05-28
4
108
 
5
109
  **Critical hotfix** — v2.3.3 (PR #539) shipped the backend filters `createdBy` + `updatedSince` and the Zod schema exports but did NOT wire those params into the 4 list MCP tool args blocks. Pi pull-cycle quickstart `list_tasks createdBy="pi" status="review" fields="lite"` was silently dropping `createdBy` at the MCP boundary and returning all visible tasks. Auto-clamp safeguard (Day 83) also could not trigger because Zod `.default(50)` / `.default(20)` on `limit` overrode the absent-value signal before it reached the backend.
package/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  [![npm version](https://img.shields.io/npm/v/vantage-peers-mcp)](https://www.npmjs.com/package/vantage-peers-mcp)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/vantage-peers-mcp)](https://www.npmjs.com/package/vantage-peers-mcp)
5
5
  [![License: FSL-1.1-Apache-2.0](https://img.shields.io/badge/license-FSL--1.1--Apache--2.0-blue)](https://github.com/vantageos-agency/vantage-peers/blob/main/LICENSE)
6
- [![Tests: 82/82](https://img.shields.io/badge/MCP_tools-82_registered-green)]()
6
+ [![Tests: 84/84](https://img.shields.io/badge/MCP_tools-84_registered-green)]()
7
7
 
8
8
  MCP server for [VantagePeers](https://vantagepeers.com) — shared memory, messaging, and task coordination for AI agent teams.
9
9
 
10
- 82 tools across 18 categories: memory, profiles, tasks, missions, mission templates, messages, diary, briefing notes, search (RAG), issues, fix patterns, error monitoring, deployments, business units, components, mandates, recurring tasks, and session.
10
+ 84 tools across 18 categories: memory, profiles, tasks, missions, mission templates, messages, diary, briefing notes, search (RAG), issues, fix patterns, error monitoring, deployments, business units, components, mandates, recurring tasks, and session. All tools ship with ChatGPT Apps SDK annotations (`readOnlyHint`, `openWorldHint`, `destructiveHint`) for native UX in ChatGPT custom connectors.
11
11
 
12
12
  ## Quick start
13
13
 
@@ -74,7 +74,7 @@ VantagePeers ships a built-in OAuth 2.1 authorization server so Claude.ai web ca
74
74
 
75
75
  The server also reads `CONVEX_URL` from `.env.local` in the parent directory if not set via environment.
76
76
 
77
- ## Tools (82)
77
+ ## Tools (84)
78
78
 
79
79
  ### Memory (6)
80
80
  `store_memory`, `recall`, `list_memories`, `soft_delete_memory`, `get_memory`, `store_episode`
@@ -400,6 +400,17 @@ All orchestrator names are open strings — any lowercase name is accepted. The
400
400
 
401
401
  ## Changelog
402
402
 
403
+ ### 2.4.1 — 2026-05-30 (Day 88)
404
+ - fix(dcr): `oauthDcr:validateAccessToken` exposed as PUBLIC `query` (was `internalQuery`, unreachable via `ConvexHttpClient.query()` → Path 3 DCR returned 401 even with valid token) — issue #556 / PR #557.
405
+ - fix(dcr): `WWW-Authenticate` header now emits `Bearer resource_metadata="..."` per MCP spec §Protected Resource Metadata Discovery (was `resource="..."` — broke Claude.ai PRM discovery bootstrap on 401) — PR #557.
406
+ - feat(mcp): ChatGPT Apps SDK tool annotations on all 84 tools (`readOnlyHint`, `openWorldHint`, `destructiveHint`) — 34 read-only + 41 write + 9 destructive — PR #555.
407
+ - security(dcr): DCR scope isolation — new `public-readonly` profile + cross-tenant assertion tests + `scopeProfile` forced to `client-generic` for auto-discovery flow (never `master`) — PR #554.
408
+ - docs(cloud): dedicated `/docs/cloud/` section in vantage-peers-site for VantagePeers Cloud (multi-tenant, multi-clients MCP: Claude.ai, ChatGPT, Claude Code, Codex) — site PR #120.
409
+
410
+ ### 2.4.0 — 2026-05-29 (Day 86)
411
+ - feat(m3): `iframeEmbedSessions` table + `__VP_TOOL_RESULT__` stream marker + ack-checklist primitive — PR #545.
412
+ - feat(v0.0.2-auth): `credentials:issueBearerFromClerk` httpAction + audit log + iter 2 P1 fixes — PR #546.
413
+
403
414
  ### 2.3.0 — 2026-05-26
404
415
  - `list_tasks`, `list_missions`, `list_tasks_by_mission`, `list_briefing_notes` now accept `fields=lite` for compact payloads (less tokens).
405
416
  - Status filters now accept arrays and aliases: `status=["todo","in_progress"]`, `status="open"` (expands to non-terminal), `status="active"` (in_progress only on tasks; plan+execute on missions), `status="all"` (no filter).
@@ -25,13 +25,14 @@
25
25
  * NODE_ENV — set to "production" on Railway
26
26
  */
27
27
  import { readFileSync } from "node:fs";
28
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
28
+ import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
29
29
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
30
30
  import { ConvexHttpClient } from "convex/browser";
31
31
  import { Hono } from "hono";
32
32
  import { cors } from "hono/cors";
33
33
  import { bearerAuthMiddleware, internalClient, masterOnlyMiddleware, sha256Base64Url, sha256Hex, } from "./src/auth.js";
34
34
  import { registerTools } from "./src/tools.js";
35
+ import { listUiResources, readUiResource } from "./src/ui-resources/index.js";
35
36
  let pkg;
36
37
  try {
37
38
  // Source mode: server-http.ts → ./package.json = mcp-server/package.json
@@ -563,6 +564,31 @@ app.all("/mcp", bearerAuthMiddleware(), async (c) => {
563
564
  version: pkg.version,
564
565
  });
565
566
  registerTools(server, convex, oauthCtx);
567
+ // SEP-1865 ui:// resources for Generative UI primitives
568
+ // Uses McpServer.resource() high-level API with a ResourceTemplate so that
569
+ // resources/list (via listCallback) and resources/read both work.
570
+ // URI pattern: ui://vp/v1/{primitive} — query params read from the URL object.
571
+ const uiResourceTemplate = new ResourceTemplate("ui://vp/v1/{primitive}", {
572
+ list: async () => ({ resources: listUiResources() }),
573
+ });
574
+ server.resource("vp-ui", uiResourceTemplate, {
575
+ description: "SEP-1865 VantagePeers Generative UI primitives (HTML inline, Shadow DOM scoped)",
576
+ }, async (uri) => {
577
+ const fetchConvex = async (functionName, args) => {
578
+ // biome-ignore lint/suspicious/noExplicitAny: Convex string API
579
+ return convex.query(functionName, args);
580
+ };
581
+ const resource = await readUiResource(uri.toString(), fetchConvex);
582
+ return {
583
+ contents: [
584
+ {
585
+ uri: resource.uri,
586
+ mimeType: resource.mimeType,
587
+ text: resource.text,
588
+ },
589
+ ],
590
+ };
591
+ });
566
592
  const transport = new WebStandardStreamableHTTPServerTransport();
567
593
  await server.connect(transport);
568
594
  return transport.handleRequest(c.req.raw);
package/dist/src/auth.js CHANGED
@@ -139,11 +139,13 @@ export function checkNamespaceWrite(ctx, namespace) {
139
139
  // ─────────────────────────────────────────────────────────────────────────────
140
140
  export function bearerAuthMiddleware() {
141
141
  return async (c, next) => {
142
- // RFC 6750 §3 point clients at the protected-resource metadata so
143
- // Claude.ai's OAuth connector can bootstrap discovery from any 401.
142
+ // MCP spec §"Protected Resource Metadata Discovery Requirements" + RFC 6750 §3 —
143
+ // the param MUST be `resource_metadata=` (not `resource=`). Claude.ai's OAuth
144
+ // connector looks for `resource_metadata=` to bootstrap PRM discovery; with
145
+ // `resource=` the entire DCR chain breaks before any token is issued.
144
146
  const publicBaseUrl = process.env.PUBLIC_BASE_URL ??
145
147
  "https://vantage-peers-production.up.railway.app";
146
- const wwwAuthHeader = `Bearer resource="${publicBaseUrl}/.well-known/oauth-protected-resource"`;
148
+ const wwwAuthHeader = `Bearer resource_metadata="${publicBaseUrl}/.well-known/oauth-protected-resource"`;
147
149
  const authHeader = c.req.header("Authorization");
148
150
  if (!authHeader?.startsWith("Bearer ")) {
149
151
  c.header("WWW-Authenticate", wwwAuthHeader);
@@ -215,6 +217,10 @@ export function bearerAuthMiddleware() {
215
217
  // ── (3) DCR OAuth token — check oauthTokens via oauthDcr:validateAccessToken
216
218
  // Uses raw token (not hashed) — the DCR table stores tokens in plaintext.
217
219
  // This path handles Claude.ai clients registered via POST /register.
220
+ // NOTE: validateAccessToken is exposed as a PUBLIC query (not internalQuery)
221
+ // because ConvexHttpClient.query() only resolves public functions. Making it
222
+ // internal silently breaks the DCR path (#556). Security: lookup is keyed
223
+ // on the high-entropy opaque token; returns null on miss with no PII echo.
218
224
  let dcrResult = null;
219
225
  try {
220
226
  dcrResult = (await internalClient().query(