Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/host-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dev": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run serve\""
},
"dependencies": {
"@modelcontextprotocol/ext-apps": "^1.0.0",
"@modelcontextprotocol/ext-apps": "1.1.2",
"@modelcontextprotocol/sdk": "^1.24.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This directory contains repository governance and architecture documentation.
- `roomd-release-readiness-checklist.md`: release gate checklist tied to conformance evidence.
- `real-mcp-integration-testing.md`: canonical real MCP integration fixture and test workflow.
- `lifecycle-contract-source-of-truth.md`: canonical lifecycle contract and drift-check workflow.
- `upstream-ext-apps-strict-init-reproducer.md`: upstream strict-init dependency tracking and blocker record.

## Update Workflow

Expand Down
2 changes: 1 addition & 1 deletion docs/real-mcp-integration-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Included specs:
- Host is intentionally not started, so `app_initialized` is expected to be missing.
- `e2e/playwright/roomctl-real-server-host-lifecycle.e2e.spec.ts`
- Positive lifecycle path with real MCP server + roomd + host.
- Asserts `bridge_connected` then `app_initialized`, and confirms default `tool-call` behavior.
- Asserts `bridge_connected` then `app_initialized`, confirms default `tool-call` behavior, and rejects duplicate host `app_initialized` evidence for a mounted instance.

Both specs set `MCP_APP_ROOM_CONFIG` for roomctl calls (without passing explicit `--config`) to validate deterministic config resolution precedence in real workflows.

Expand Down
53 changes: 53 additions & 0 deletions docs/upstream-ext-apps-strict-init-reproducer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Upstream ext-apps Strict Init Reproducer

## Scope

This note tracks the strict-mode initialization idempotency dependency gap in
`@modelcontextprotocol/ext-apps` and the local controls in `mcp-app-room`.

## Upstream Status (as of 2026-03-08)

- Upstream issue: [modelcontextprotocol/ext-apps#542](https://github.com/modelcontextprotocol/ext-apps/issues/542)
- Upstream PR: [modelcontextprotocol/ext-apps#543](https://github.com/modelcontextprotocol/ext-apps/pull/543)
- Upstream state: issue `OPEN`, PR `OPEN` (not merged, not released)

## Local Reproducer Signals

Canonical positive lifecycle suite:

```bash
npm run test:integration:real-mcp
```

Primary regression lock:

- `e2e/playwright/roomctl-real-server-host-lifecycle.e2e.spec.ts`
- requires `app_initialized` evidence for mounted host instance
- asserts no duplicate host `app_initialized` event for the same instance

This prevents silent regressions where strict-mode transport sequencing accepts
duplicate lifecycle progression.

## Local Defense-In-Depth

- Host room bootstrap keeps bridge wiring + evidence reporting isolated in
`apps/host-web/src/room-canvas/room-app-instance.tsx`.
- Dependency is pinned to exact `@modelcontextprotocol/ext-apps@1.1.2` in
workspace manifests to prevent unreviewed semver drift while upstream fix is
unresolved.

## Blocker Record

- Owner: `team-platform` (this repository), `modelcontextprotocol/ext-apps` maintainers (upstream)
- Blocked since: 2026-03-08
- Earliest next check: 2026-03-15
- Exit criteria:
1. Upstream PR merges.
2. Upstream package release contains fix.
3. This repo pins to released fixed version and reruns `npm run verify` and
`npm run test:integration:real-mcp`.

## GOTCHA

Do not relax the duplicate `app_initialized` assertion to make flaky runs pass.
If this assertion fails, treat it as lifecycle truth regression and escalate.
11 changes: 11 additions & 0 deletions e2e/playwright/roomctl-real-server-host-lifecycle.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,17 @@ test.describe("roomctl lifecycle evidence with full real MCP fixture + host", ()
roomId,
]);
expect(state.status).toBe(200);
const evidence = (state.body.state?.evidence ?? []) as Array<Record<string, any>>;
const hostInstanceEvidence = evidence.filter(
(item) => item.source === "host" && item.instanceId === "integration-1",
);
const appInitializedEvents = hostInstanceEvidence.filter(
(item) => item.event === "app_initialized",
);
// GOTCHA: Strict-mode bridge races have historically duplicated accepted
// initialize progression. Keep this lock strict to prevent regressions.
expect(appInitializedEvents).toHaveLength(1);

const instances = (state.body.state?.assurance?.instances ?? []) as Array<Record<string, any>>;
const assurance = instances.find((instance) => instance.instanceId === "integration-1");
expect(assurance?.level).toBe("ui_app_initialized");
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion services/roomd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"conformance:check": "tsx src/conformance/check-threshold.ts"
},
"dependencies": {
"@modelcontextprotocol/ext-apps": "^1.0.0",
"@modelcontextprotocol/ext-apps": "1.1.2",
"@modelcontextprotocol/sdk": "^1.24.0",
"@types/better-sqlite3": "^7.6.13",
"better-sqlite3": "^12.6.2",
Expand Down