@apollo/mcp-impostor-host 0.1.0 → 0.2.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.
@@ -1,3 +1,4 @@
1
+ import type { McpUiMessageRequest, McpUiOpenLinkRequest } from "@modelcontextprotocol/ext-apps/app-bridge";
1
2
  import type { CallToolResult } from "@modelcontextprotocol/sdk/types";
2
3
  import { type FrameLocator } from "@playwright/test";
3
4
  import type { McpHost } from "./types.js";
@@ -14,6 +15,24 @@ export interface CallToolResponse {
14
15
  }
15
16
  export interface McpHostConnection {
16
17
  callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResponse>;
18
+ readonly messageRequests: ReadonlyArray<McpUiMessageRequest["params"]>;
19
+ waitForMessageRequest(options?: {
20
+ /**
21
+ * Amount of time in milliseconds before the call times out.
22
+ *
23
+ * @default 5000
24
+ */
25
+ timeout?: number;
26
+ }): Promise<McpUiMessageRequest["params"]>;
27
+ readonly openLinkRequests: ReadonlyArray<McpUiOpenLinkRequest["params"]>;
28
+ waitForOpenLinkRequest(options?: {
29
+ /**
30
+ * Amount of time in milliseconds before the call times out.
31
+ *
32
+ * @default 5000
33
+ */
34
+ timeout?: number;
35
+ }): Promise<McpUiOpenLinkRequest["params"]>;
17
36
  }
18
37
  export interface McpHostFixture {
19
38
  connect(options: Host.ConnectOptions): Promise<McpHostConnection>;
@@ -1 +1 @@
1
- {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/playwright/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,SAAS,EAAE,OAAO,CAAC;KACpB;CACF;AAID,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACnE;AAED,eAAO,MAAM,IAAI;aAA0B,cAAc;sGAmCvD,CAAC"}
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/playwright/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,SAAS,EAAE,OAAO,CAAC;KACpB;CACF;AAKD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvE,qBAAqB,CAAC,OAAO,CAAC,EAAE;QAC9B;;;;WAIG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,EAAE,aAAa,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,sBAAsB,CAAC,OAAO,CAAC,EAAE;QAC/B;;;;WAIG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACnE;AAED,eAAO,MAAM,IAAI;aAA0B,cAAc;sGAqIvD,CAAC"}
@@ -1,8 +1,33 @@
1
1
  import { test as base } from "@playwright/test";
2
2
  const DEFAULT_HARNESS_URL = "http://localhost:8080/";
3
+ const DEFAULT_MESSAGE_TIMEOUT = 5000;
3
4
  export const test = base.extend({
4
5
  mcpHost: async ({ page }, use) => {
5
6
  await page.goto(DEFAULT_HARNESS_URL);
7
+ const messageRequests = [];
8
+ let controller;
9
+ const stream = new ReadableStream({
10
+ start(c) {
11
+ controller = c;
12
+ },
13
+ });
14
+ const reader = stream.getReader();
15
+ const openLinkRequests = [];
16
+ let openLinkController;
17
+ const openLinkStream = new ReadableStream({
18
+ start(c) {
19
+ openLinkController = c;
20
+ },
21
+ });
22
+ const openLinkReader = openLinkStream.getReader();
23
+ await page.exposeFunction("__playwrightPushMessage", (params) => {
24
+ messageRequests.push(params);
25
+ controller.enqueue(params);
26
+ });
27
+ await page.exposeFunction("__playwrightSendOpenLinkRequest", (params) => {
28
+ openLinkRequests.push(params);
29
+ openLinkController.enqueue(params);
30
+ });
6
31
  const fixture = {
7
32
  async connect(options) {
8
33
  await page.evaluate((url) => window.__mcpHost.connect(url), options.url);
@@ -16,6 +41,38 @@ export const test = base.extend({
16
41
  .contentFrame();
17
42
  return { ...response, appFrame };
18
43
  },
44
+ get messageRequests() {
45
+ return messageRequests;
46
+ },
47
+ waitForMessageRequest(options) {
48
+ let timer;
49
+ return Promise.race([
50
+ reader.read().then(({ value }) => {
51
+ clearTimeout(timer);
52
+ return value;
53
+ }),
54
+ new Promise((_, reject) => {
55
+ const timeout = options?.timeout ?? DEFAULT_MESSAGE_TIMEOUT;
56
+ timer = setTimeout(() => reject(new Error(`[@apollo/mcp-impostor-host:playwright] Timeout waiting for message request.`)), timeout);
57
+ }),
58
+ ]);
59
+ },
60
+ get openLinkRequests() {
61
+ return openLinkRequests;
62
+ },
63
+ waitForOpenLinkRequest(options) {
64
+ let timer;
65
+ return Promise.race([
66
+ openLinkReader.read().then(({ value }) => {
67
+ clearTimeout(timer);
68
+ return value;
69
+ }),
70
+ new Promise((_, reject) => {
71
+ const timeout = options?.timeout ?? DEFAULT_MESSAGE_TIMEOUT;
72
+ timer = setTimeout(() => reject(new Error(`[@apollo/mcp-impostor-host:playwright] Timeout waiting for open link request.`)), timeout);
73
+ }),
74
+ ]);
75
+ },
19
76
  };
20
77
  },
21
78
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/playwright/fixture.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,IAAI,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAWnE,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAmBrD,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAA8B;IAC3D,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE;QAC/B,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAErC,MAAM,OAAO,GAAmB;YAC9B,KAAK,CAAC,OAAO,CAAC,OAAO;gBACnB,MAAM,IAAI,CAAC,QAAQ,CACjB,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EACtC,OAAO,CAAC,GAAG,CACZ,CAAC;gBAEF,OAAO;oBACL,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI;wBACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAClC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,EACzD,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;wBAEF,MAAM,QAAQ,GAAG,IAAI;6BAClB,OAAO,CAAC,QAAQ,CAAC;6BACjB,YAAY,EAAE;6BACd,OAAO,CAAC,QAAQ,CAAC;6BACjB,YAAY,EAAE,CAAC;wBAElB,OAAO,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,CAAC;oBACnC,CAAC;iBACF,CAAC;YACJ,CAAC;SACF,CAAC;QAEF,sDAAsD;QACtD,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC;QAEnB,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzE,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/playwright/fixture.ts"],"names":[],"mappings":"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"}
@@ -158,7 +158,7 @@ container holding the app. Specify either width or maxWidth, and either height o
158
158
  `;break;case`id`:s=t.includes(`\0`)?void 0:t;break;case`retry`:/^\d+$/.test(t)?r(parseInt(t,10)):n(new GD(`Invalid \`retry\` value: "${t}"`,{type:`invalid-retry`,value:t,line:i}));break;default:n(new GD(`Unknown field "${e.length>20?`${e.slice(0,20)}\u2026`:e}"`,{type:`unknown-field`,field:e,value:t,line:i}));break}}function p(){c.length>0&&t({id:s,event:l||void 0,data:c.endsWith(`
159
159
  `)?c.slice(0,-1):c}),s=void 0,c=``,l=``}function m(e={}){a&&e.consume&&d(a),o=!0,s=void 0,c=``,l=``,a=``}return{feed:u,reset:m}}function JD(e){let t=[],n=``,r=0;for(;r<e.length;){let i=e.indexOf(`\r`,r),a=e.indexOf(`
160
160
  `,r),o=-1;if(i!==-1&&a!==-1?o=Math.min(i,a):i===-1?a!==-1&&(o=a):o=i===e.length-1?-1:i,o===-1){n=e.slice(r);break}else{let n=e.slice(r,o);t.push(n),r=o+1,e[r-1]===`\r`&&e[r]===`
161
- `&&r++}}return[t,n]}var YD=class extends TransformStream{constructor({onError:e,onRetry:t,onComment:n}={}){let r;super({start(i){r=qD({onEvent:e=>{i.enqueue(e)},onError(t){e===`terminate`?i.error(t):typeof e==`function`&&e(t)},onRetry:t,onComment:n})},transform(e){r.feed(e)}})}},XD={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2},ZD=class extends Error{constructor(e,t){super(`Streamable HTTP error: ${t}`),this.code=e}},QD=class{constructor(e,t){this._hasCompletedAuthFlow=!1,this._url=e,this._resourceMetadataUrl=void 0,this._scope=void 0,this._requestInit=t?.requestInit,this._authProvider=t?.authProvider,this._fetch=t?.fetch,this._fetchWithInit=AE(t?.fetch,t?.requestInit),this._sessionId=t?.sessionId,this._reconnectionOptions=t?.reconnectionOptions??XD}async _authThenStart(){if(!this._authProvider)throw new mD(`No auth provider`);let e;try{e=await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(e){throw this.onerror?.(e),e}if(e!==`AUTHORIZED`)throw new mD;return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){let e={};if(this._authProvider){let t=await this._authProvider.tokens();t&&(e.Authorization=`Bearer ${t.access_token}`)}this._sessionId&&(e[`mcp-session-id`]=this._sessionId),this._protocolVersion&&(e[`mcp-protocol-version`]=this._protocolVersion);let t=kE(this._requestInit?.headers);return new Headers({...e,...t})}async _startOrAuthSse(e){let{resumptionToken:t}=e;try{let n=await this._commonHeaders();n.set(`Accept`,`text/event-stream`),t&&n.set(`last-event-id`,t);let r=await(this._fetch??fetch)(this._url,{method:`GET`,headers:n,signal:this._abortController?.signal});if(!r.ok){if(await r.body?.cancel(),r.status===401&&this._authProvider)return await this._authThenStart();if(r.status===405)return;throw new ZD(r.status,`Failed to open SSE stream: ${r.statusText}`)}this._handleSseStream(r.body,e,!0)}catch(e){throw this.onerror?.(e),e}}_getNextReconnectionDelay(e){if(this._serverRetryMs!==void 0)return this._serverRetryMs;let t=this._reconnectionOptions.initialReconnectionDelay,n=this._reconnectionOptions.reconnectionDelayGrowFactor,r=this._reconnectionOptions.maxReconnectionDelay;return Math.min(t*n**+e,r)}_scheduleReconnection(e,t=0){let n=this._reconnectionOptions.maxRetries;if(t>=n){this.onerror?.(Error(`Maximum reconnection attempts (${n}) exceeded.`));return}let r=this._getNextReconnectionDelay(t);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(e).catch(n=>{this.onerror?.(Error(`Failed to reconnect SSE stream: ${n instanceof Error?n.message:String(n)}`)),this._scheduleReconnection(e,t+1)})},r)}_handleSseStream(e,t,n){if(!e)return;let{onresumptiontoken:r,replayMessageId:i}=t,a,o=!1,s=!1;(async()=>{try{let t=e.pipeThrough(new TextDecoderStream).pipeThrough(new YD({onRetry:e=>{this._serverRetryMs=e}})).getReader();for(;;){let{value:e,done:n}=await t.read();if(n)break;if(e.id&&(a=e.id,o=!0,r?.(e.id)),e.data&&(!e.event||e.event===`message`))try{let t=bs.parse(JSON.parse(e.data));_s(t)&&(s=!0,i!==void 0&&(t.id=i)),this.onmessage?.(t)}catch(e){this.onerror?.(e)}}(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted&&this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){if(this.onerror?.(Error(`SSE stream disconnected: ${e}`)),(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted)try{this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){this.onerror?.(Error(`Failed to reconnect: ${e instanceof Error?e.message:String(e)}`))}}})()}async start(){if(this._abortController)throw Error(`StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.`);this._abortController=new AbortController}async finishAuth(e){if(!this._authProvider)throw new mD(`No auth provider`);if(await wD(this._authProvider,{serverUrl:this._url,authorizationCode:e,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new mD(`Failed to authorize`)}async close(){this._reconnectionTimeout&&=(clearTimeout(this._reconnectionTimeout),void 0),this._abortController?.abort(),this.onclose?.()}async send(e,t){try{let{resumptionToken:n,onresumptiontoken:r}=t||{};if(n){this._startOrAuthSse({resumptionToken:n,replayMessageId:ps(e)?e.id:void 0}).catch(e=>this.onerror?.(e));return}let i=await this._commonHeaders();i.set(`content-type`,`application/json`),i.set(`accept`,`application/json, text/event-stream`);let a={...this._requestInit,method:`POST`,headers:i,body:JSON.stringify(e),signal:this._abortController?.signal},o=await(this._fetch??fetch)(this._url,a),s=o.headers.get(`mcp-session-id`);if(s&&(this._sessionId=s),!o.ok){let t=await o.text().catch(()=>null);if(o.status===401&&this._authProvider){if(this._hasCompletedAuthFlow)throw new ZD(401,`Server returned 401 after successful authentication`);let{resourceMetadataUrl:t,scope:n}=OD(o);if(this._resourceMetadataUrl=t,this._scope=n,await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new mD;return this._hasCompletedAuthFlow=!0,this.send(e)}if(o.status===403&&this._authProvider){let{resourceMetadataUrl:t,scope:n,error:r}=OD(o);if(r===`insufficient_scope`){let r=o.headers.get(`WWW-Authenticate`);if(this._lastUpscopingHeader===r)throw new ZD(403,`Server returned 403 after trying upscoping`);if(n&&(this._scope=n),t&&(this._resourceMetadataUrl=t),this._lastUpscopingHeader=r??void 0,await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch})!==`AUTHORIZED`)throw new mD;return this.send(e)}}throw new ZD(o.status,`Error POSTing to endpoint: ${t}`)}if(this._hasCompletedAuthFlow=!1,this._lastUpscopingHeader=void 0,o.status===202){await o.body?.cancel(),Is(e)&&this._startOrAuthSse({resumptionToken:void 0}).catch(e=>this.onerror?.(e));return}let c=(Array.isArray(e)?e:[e]).filter(e=>`method`in e&&`id`in e&&e.id!==void 0).length>0,l=o.headers.get(`content-type`);if(c)if(l?.includes(`text/event-stream`))this._handleSseStream(o.body,{onresumptiontoken:r},!1);else if(l?.includes(`application/json`)){let e=await o.json(),t=Array.isArray(e)?e.map(e=>bs.parse(e)):[bs.parse(e)];for(let e of t)this.onmessage?.(e)}else throw await o.body?.cancel(),new ZD(-1,`Unexpected content type: ${l}`);else await o.body?.cancel()}catch(e){throw this.onerror?.(e),e}}get sessionId(){return this._sessionId}async terminateSession(){if(this._sessionId)try{let e=await this._commonHeaders(),t={...this._requestInit,method:`DELETE`,headers:e,signal:this._abortController?.signal},n=await(this._fetch??fetch)(this._url,t);if(await n.body?.cancel(),!n.ok&&n.status!==405)throw new ZD(n.status,`Failed to terminate session: ${n.statusText}`);this._sessionId=void 0}catch(e){throw this.onerror?.(e),e}}setProtocolVersion(e){this._protocolVersion=e}get protocolVersion(){return this._protocolVersion}async resumeStream(e,t){await this._startOrAuthSse({resumptionToken:e,onresumptiontoken:t?.onresumptiontoken})}},$D={name:`@apollo/mcp-impostor-host`,version:`0.1.0`,description:`A test host for MCP Apps — impersonates a real MCP Apps host (e.g. Claude Desktop) for end-to-end testing`,license:`MIT`,type:`module`,main:`dist/core/index.js`,module:`dist/core/index.js`,types:`dist/core/index.d.ts`,bin:{"serve-impostor-host":`bin/serve-impostor-host.js`},files:[`dist`,`bin`],imports:{"#package.json":`./package.json`},exports:{".":{types:`./dist/core/index.d.ts`,default:`./dist/core/index.js`},"./playwright":{types:`./dist/playwright/index.d.ts`,default:`./dist/playwright/index.js`},"./react":{types:`./dist/react/index.d.ts`,default:`./dist/react/index.js`}},scripts:{clean:`rimraf dist`,prebuild:`npm run clean`,build:`npm-run-all build:*`,"build:lib":`tsc --build tsconfig.build.json`,"build:harness":`vite build --mode harness`,"build:sandbox":`vite build --mode sandbox`,test:`playwright test`,typecheck:`tsc --build tsconfig.json`,format:`prettier --write .`,"format:check":`prettier --check .`,attw:`npm run build && attw --pack . --ignore-rules cjs-resolves-to-esm --profile node16`,lint:`eslint .`,"changeset-publish":`npm run build && changeset publish`},peerDependencies:{"@playwright/test":`>=1.40.0`,react:`^19.0.0`,"react-dom":`^19.0.0`},peerDependenciesMeta:{"@playwright/test":{optional:!0},react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@arethetypeswrong/cli":`^0.18.2`,"@changesets/changelog-github":`^0.6.0`,"@changesets/cli":`^2.30.0`,"@eslint/js":`^9.39.4`,"@hono/mcp":`^0.2.5`,"@playwright/test":`^1.59.1`,"@types/node":`^25.5.2`,"@types/react":`^19.2.14`,"@types/react-dom":`^19.2.3`,eslint:`^9.39.4`,"eslint-plugin-import":`^2.32.0`,"eslint-plugin-react-hooks":`^7.0.1`,globals:`^17.4.0`,"npm-run-all":`^4.1.5`,prettier:`^3.8.1`,react:`^19.2.4`,"react-dom":`^19.2.4`,rimraf:`^6.1.3`,tsx:`^4.21.0`,typescript:`^6.0.2`,"typescript-eslint":`^8.58.1`,vite:`^8.0.7`,"vite-plugin-singlefile":`^2.3.2`,zod:`^4.3.6`},dependencies:{"@hono/node-server":`^1.19.13`,"@mcp-ui/client":`^7.0.0`,"@modelcontextprotocol/ext-apps":`^1.3.2`,"@modelcontextprotocol/sdk":`^1.29.0`,hono:`^4.12.12`}},eO=class extends Bl{_registeredMethods=new Set;_eventSlots=new Map;onEventDispatch(e,t){}_ensureEventSlot(e){let t=this._eventSlots.get(e);if(!t){let n=this.eventSchemas[e];if(!n)throw Error(`Unknown event: ${String(e)}`);t={listeners:[]},this._eventSlots.set(e,t);let r=n.shape.method.value;this._registeredMethods.add(r);let i=t;super.setNotificationHandler(n,t=>{let n=t.params;this.onEventDispatch(e,n),i.onHandler?.(n);for(let e of[...i.listeners])e(n)})}return t}setEventHandler(e,t){let n=this._ensureEventSlot(e);n.onHandler&&t&&console.warn(`[MCP Apps] on${String(e)} handler replaced. Use addEventListener("${String(e)}", …) to add multiple listeners without replacing.`),n.onHandler=t}getEventHandler(e){return this._eventSlots.get(e)?.onHandler}addEventListener(e,t){this._ensureEventSlot(e).listeners.push(t)}removeEventListener(e,t){let n=this._eventSlots.get(e);if(!n)return;let r=n.listeners.indexOf(t);r!==-1&&n.listeners.splice(r,1)}setRequestHandler=(e,t)=>{this._assertMethodNotRegistered(e,`setRequestHandler`),super.setRequestHandler(e,t)};setNotificationHandler=(e,t)=>{this._assertMethodNotRegistered(e,`setNotificationHandler`),super.setNotificationHandler(e,t)};warnIfRequestHandlerReplaced(e,t,n){t&&n&&console.warn(`[MCP Apps] ${e} handler replaced. Previous handler will no longer be called.`)}replaceRequestHandler=(e,t)=>{let n=e.shape.method.value;this._registeredMethods.add(n),super.setRequestHandler(e,t)};_assertMethodNotRegistered(e,t){let n=e.shape.method.value;if(this._registeredMethods.has(n))throw Error(`Handler for "${n}" already registered (via ${t}). Use addEventListener() to attach multiple listeners, or the on* setter for replace semantics.`);this._registeredMethods.add(n)}},tO=`2026-01-26`,nO=M([F(`light`),F(`dark`)]).describe(`Color theme preference for the host environment.`),rO=M([F(`inline`),F(`fullscreen`),F(`pip`)]).describe(`Display mode for UI presentation.`),iO=N(M([F(`--color-background-primary`),F(`--color-background-secondary`),F(`--color-background-tertiary`),F(`--color-background-inverse`),F(`--color-background-ghost`),F(`--color-background-info`),F(`--color-background-danger`),F(`--color-background-success`),F(`--color-background-warning`),F(`--color-background-disabled`),F(`--color-text-primary`),F(`--color-text-secondary`),F(`--color-text-tertiary`),F(`--color-text-inverse`),F(`--color-text-ghost`),F(`--color-text-info`),F(`--color-text-danger`),F(`--color-text-success`),F(`--color-text-warning`),F(`--color-text-disabled`),F(`--color-border-primary`),F(`--color-border-secondary`),F(`--color-border-tertiary`),F(`--color-border-inverse`),F(`--color-border-ghost`),F(`--color-border-info`),F(`--color-border-danger`),F(`--color-border-success`),F(`--color-border-warning`),F(`--color-border-disabled`),F(`--color-ring-primary`),F(`--color-ring-secondary`),F(`--color-ring-inverse`),F(`--color-ring-info`),F(`--color-ring-danger`),F(`--color-ring-success`),F(`--color-ring-warning`),F(`--font-sans`),F(`--font-mono`),F(`--font-weight-normal`),F(`--font-weight-medium`),F(`--font-weight-semibold`),F(`--font-weight-bold`),F(`--font-text-xs-size`),F(`--font-text-sm-size`),F(`--font-text-md-size`),F(`--font-text-lg-size`),F(`--font-heading-xs-size`),F(`--font-heading-sm-size`),F(`--font-heading-md-size`),F(`--font-heading-lg-size`),F(`--font-heading-xl-size`),F(`--font-heading-2xl-size`),F(`--font-heading-3xl-size`),F(`--font-text-xs-line-height`),F(`--font-text-sm-line-height`),F(`--font-text-md-line-height`),F(`--font-text-lg-line-height`),F(`--font-heading-xs-line-height`),F(`--font-heading-sm-line-height`),F(`--font-heading-md-line-height`),F(`--font-heading-lg-line-height`),F(`--font-heading-xl-line-height`),F(`--font-heading-2xl-line-height`),F(`--font-heading-3xl-line-height`),F(`--border-radius-xs`),F(`--border-radius-sm`),F(`--border-radius-md`),F(`--border-radius-lg`),F(`--border-radius-xl`),F(`--border-radius-full`),F(`--border-width-regular`),F(`--shadow-hairline`),F(`--shadow-sm`),F(`--shadow-md`),F(`--shadow-lg`)]).describe(`CSS variable keys available to MCP apps for theming.`).describe(`Style variables for theming MCP apps.
161
+ `&&r++}}return[t,n]}var YD=class extends TransformStream{constructor({onError:e,onRetry:t,onComment:n}={}){let r;super({start(i){r=qD({onEvent:e=>{i.enqueue(e)},onError(t){e===`terminate`?i.error(t):typeof e==`function`&&e(t)},onRetry:t,onComment:n})},transform(e){r.feed(e)}})}},XD={initialReconnectionDelay:1e3,maxReconnectionDelay:3e4,reconnectionDelayGrowFactor:1.5,maxRetries:2},ZD=class extends Error{constructor(e,t){super(`Streamable HTTP error: ${t}`),this.code=e}},QD=class{constructor(e,t){this._hasCompletedAuthFlow=!1,this._url=e,this._resourceMetadataUrl=void 0,this._scope=void 0,this._requestInit=t?.requestInit,this._authProvider=t?.authProvider,this._fetch=t?.fetch,this._fetchWithInit=AE(t?.fetch,t?.requestInit),this._sessionId=t?.sessionId,this._reconnectionOptions=t?.reconnectionOptions??XD}async _authThenStart(){if(!this._authProvider)throw new mD(`No auth provider`);let e;try{e=await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})}catch(e){throw this.onerror?.(e),e}if(e!==`AUTHORIZED`)throw new mD;return await this._startOrAuthSse({resumptionToken:void 0})}async _commonHeaders(){let e={};if(this._authProvider){let t=await this._authProvider.tokens();t&&(e.Authorization=`Bearer ${t.access_token}`)}this._sessionId&&(e[`mcp-session-id`]=this._sessionId),this._protocolVersion&&(e[`mcp-protocol-version`]=this._protocolVersion);let t=kE(this._requestInit?.headers);return new Headers({...e,...t})}async _startOrAuthSse(e){let{resumptionToken:t}=e;try{let n=await this._commonHeaders();n.set(`Accept`,`text/event-stream`),t&&n.set(`last-event-id`,t);let r=await(this._fetch??fetch)(this._url,{method:`GET`,headers:n,signal:this._abortController?.signal});if(!r.ok){if(await r.body?.cancel(),r.status===401&&this._authProvider)return await this._authThenStart();if(r.status===405)return;throw new ZD(r.status,`Failed to open SSE stream: ${r.statusText}`)}this._handleSseStream(r.body,e,!0)}catch(e){throw this.onerror?.(e),e}}_getNextReconnectionDelay(e){if(this._serverRetryMs!==void 0)return this._serverRetryMs;let t=this._reconnectionOptions.initialReconnectionDelay,n=this._reconnectionOptions.reconnectionDelayGrowFactor,r=this._reconnectionOptions.maxReconnectionDelay;return Math.min(t*n**+e,r)}_scheduleReconnection(e,t=0){let n=this._reconnectionOptions.maxRetries;if(t>=n){this.onerror?.(Error(`Maximum reconnection attempts (${n}) exceeded.`));return}let r=this._getNextReconnectionDelay(t);this._reconnectionTimeout=setTimeout(()=>{this._startOrAuthSse(e).catch(n=>{this.onerror?.(Error(`Failed to reconnect SSE stream: ${n instanceof Error?n.message:String(n)}`)),this._scheduleReconnection(e,t+1)})},r)}_handleSseStream(e,t,n){if(!e)return;let{onresumptiontoken:r,replayMessageId:i}=t,a,o=!1,s=!1;(async()=>{try{let t=e.pipeThrough(new TextDecoderStream).pipeThrough(new YD({onRetry:e=>{this._serverRetryMs=e}})).getReader();for(;;){let{value:e,done:n}=await t.read();if(n)break;if(e.id&&(a=e.id,o=!0,r?.(e.id)),e.data&&(!e.event||e.event===`message`))try{let t=bs.parse(JSON.parse(e.data));_s(t)&&(s=!0,i!==void 0&&(t.id=i)),this.onmessage?.(t)}catch(e){this.onerror?.(e)}}(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted&&this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){if(this.onerror?.(Error(`SSE stream disconnected: ${e}`)),(n||o)&&!s&&this._abortController&&!this._abortController.signal.aborted)try{this._scheduleReconnection({resumptionToken:a,onresumptiontoken:r,replayMessageId:i},0)}catch(e){this.onerror?.(Error(`Failed to reconnect: ${e instanceof Error?e.message:String(e)}`))}}})()}async start(){if(this._abortController)throw Error(`StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.`);this._abortController=new AbortController}async finishAuth(e){if(!this._authProvider)throw new mD(`No auth provider`);if(await wD(this._authProvider,{serverUrl:this._url,authorizationCode:e,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new mD(`Failed to authorize`)}async close(){this._reconnectionTimeout&&=(clearTimeout(this._reconnectionTimeout),void 0),this._abortController?.abort(),this.onclose?.()}async send(e,t){try{let{resumptionToken:n,onresumptiontoken:r}=t||{};if(n){this._startOrAuthSse({resumptionToken:n,replayMessageId:ps(e)?e.id:void 0}).catch(e=>this.onerror?.(e));return}let i=await this._commonHeaders();i.set(`content-type`,`application/json`),i.set(`accept`,`application/json, text/event-stream`);let a={...this._requestInit,method:`POST`,headers:i,body:JSON.stringify(e),signal:this._abortController?.signal},o=await(this._fetch??fetch)(this._url,a),s=o.headers.get(`mcp-session-id`);if(s&&(this._sessionId=s),!o.ok){let t=await o.text().catch(()=>null);if(o.status===401&&this._authProvider){if(this._hasCompletedAuthFlow)throw new ZD(401,`Server returned 401 after successful authentication`);let{resourceMetadataUrl:t,scope:n}=OD(o);if(this._resourceMetadataUrl=t,this._scope=n,await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetchWithInit})!==`AUTHORIZED`)throw new mD;return this._hasCompletedAuthFlow=!0,this.send(e)}if(o.status===403&&this._authProvider){let{resourceMetadataUrl:t,scope:n,error:r}=OD(o);if(r===`insufficient_scope`){let r=o.headers.get(`WWW-Authenticate`);if(this._lastUpscopingHeader===r)throw new ZD(403,`Server returned 403 after trying upscoping`);if(n&&(this._scope=n),t&&(this._resourceMetadataUrl=t),this._lastUpscopingHeader=r??void 0,await wD(this._authProvider,{serverUrl:this._url,resourceMetadataUrl:this._resourceMetadataUrl,scope:this._scope,fetchFn:this._fetch})!==`AUTHORIZED`)throw new mD;return this.send(e)}}throw new ZD(o.status,`Error POSTing to endpoint: ${t}`)}if(this._hasCompletedAuthFlow=!1,this._lastUpscopingHeader=void 0,o.status===202){await o.body?.cancel(),Is(e)&&this._startOrAuthSse({resumptionToken:void 0}).catch(e=>this.onerror?.(e));return}let c=(Array.isArray(e)?e:[e]).filter(e=>`method`in e&&`id`in e&&e.id!==void 0).length>0,l=o.headers.get(`content-type`);if(c)if(l?.includes(`text/event-stream`))this._handleSseStream(o.body,{onresumptiontoken:r},!1);else if(l?.includes(`application/json`)){let e=await o.json(),t=Array.isArray(e)?e.map(e=>bs.parse(e)):[bs.parse(e)];for(let e of t)this.onmessage?.(e)}else throw await o.body?.cancel(),new ZD(-1,`Unexpected content type: ${l}`);else await o.body?.cancel()}catch(e){throw this.onerror?.(e),e}}get sessionId(){return this._sessionId}async terminateSession(){if(this._sessionId)try{let e=await this._commonHeaders(),t={...this._requestInit,method:`DELETE`,headers:e,signal:this._abortController?.signal},n=await(this._fetch??fetch)(this._url,t);if(await n.body?.cancel(),!n.ok&&n.status!==405)throw new ZD(n.status,`Failed to terminate session: ${n.statusText}`);this._sessionId=void 0}catch(e){throw this.onerror?.(e),e}}setProtocolVersion(e){this._protocolVersion=e}get protocolVersion(){return this._protocolVersion}async resumeStream(e,t){await this._startOrAuthSse({resumptionToken:e,onresumptiontoken:t?.onresumptiontoken})}},$D={name:`@apollo/mcp-impostor-host`,version:`0.2.0`,description:`A test host for MCP Apps — impersonates a real MCP Apps host (e.g. Claude Desktop) for end-to-end testing`,license:`MIT`,type:`module`,main:`dist/core/index.js`,module:`dist/core/index.js`,types:`dist/core/index.d.ts`,bin:{"serve-impostor-host":`bin/serve-impostor-host.js`},files:[`dist`,`bin`],imports:{"#package.json":`./package.json`},exports:{".":{types:`./dist/core/index.d.ts`,default:`./dist/core/index.js`},"./playwright":{types:`./dist/playwright/index.d.ts`,default:`./dist/playwright/index.js`},"./react":{types:`./dist/react/index.d.ts`,default:`./dist/react/index.js`}},scripts:{clean:`rimraf dist`,prebuild:`npm run clean`,build:`npm-run-all build:*`,"build:lib":`tsc --build tsconfig.build.json`,"build:harness":`vite build --mode harness`,"build:sandbox":`vite build --mode sandbox`,test:`playwright test`,typecheck:`tsc --build tsconfig.json`,format:`prettier --write .`,"format:check":`prettier --check .`,attw:`npm run build && attw --pack . --ignore-rules cjs-resolves-to-esm --profile node16`,lint:`eslint .`,"changeset-publish":`npm run build && changeset publish`},peerDependencies:{"@playwright/test":`>=1.40.0`,react:`^19.0.0`,"react-dom":`^19.0.0`},peerDependenciesMeta:{"@playwright/test":{optional:!0},react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@arethetypeswrong/cli":`^0.18.2`,"@changesets/changelog-github":`^0.6.0`,"@changesets/cli":`^2.30.0`,"@eslint/js":`^9.39.4`,"@hono/mcp":`^0.2.5`,"@playwright/test":`^1.59.1`,"@types/node":`^25.5.2`,"@types/react":`^19.2.14`,"@types/react-dom":`^19.2.3`,eslint:`^9.39.4`,"eslint-plugin-import":`^2.32.0`,"eslint-plugin-react-hooks":`^7.0.1`,globals:`^17.4.0`,"npm-run-all":`^4.1.5`,prettier:`^3.8.1`,react:`^19.2.4`,"react-dom":`^19.2.4`,rimraf:`^6.1.3`,tsx:`^4.21.0`,typescript:`^6.0.2`,"typescript-eslint":`^8.58.1`,vite:`^8.0.7`,"vite-plugin-singlefile":`^2.3.2`,zod:`^4.3.6`},dependencies:{"@hono/node-server":`^1.19.13`,"@mcp-ui/client":`^7.0.0`,"@modelcontextprotocol/ext-apps":`^1.3.2`,"@modelcontextprotocol/sdk":`^1.29.0`,hono:`^4.12.12`}},eO=class extends Bl{_registeredMethods=new Set;_eventSlots=new Map;onEventDispatch(e,t){}_ensureEventSlot(e){let t=this._eventSlots.get(e);if(!t){let n=this.eventSchemas[e];if(!n)throw Error(`Unknown event: ${String(e)}`);t={listeners:[]},this._eventSlots.set(e,t);let r=n.shape.method.value;this._registeredMethods.add(r);let i=t;super.setNotificationHandler(n,t=>{let n=t.params;this.onEventDispatch(e,n),i.onHandler?.(n);for(let e of[...i.listeners])e(n)})}return t}setEventHandler(e,t){let n=this._ensureEventSlot(e);n.onHandler&&t&&console.warn(`[MCP Apps] on${String(e)} handler replaced. Use addEventListener("${String(e)}", …) to add multiple listeners without replacing.`),n.onHandler=t}getEventHandler(e){return this._eventSlots.get(e)?.onHandler}addEventListener(e,t){this._ensureEventSlot(e).listeners.push(t)}removeEventListener(e,t){let n=this._eventSlots.get(e);if(!n)return;let r=n.listeners.indexOf(t);r!==-1&&n.listeners.splice(r,1)}setRequestHandler=(e,t)=>{this._assertMethodNotRegistered(e,`setRequestHandler`),super.setRequestHandler(e,t)};setNotificationHandler=(e,t)=>{this._assertMethodNotRegistered(e,`setNotificationHandler`),super.setNotificationHandler(e,t)};warnIfRequestHandlerReplaced(e,t,n){t&&n&&console.warn(`[MCP Apps] ${e} handler replaced. Previous handler will no longer be called.`)}replaceRequestHandler=(e,t)=>{let n=e.shape.method.value;this._registeredMethods.add(n),super.setRequestHandler(e,t)};_assertMethodNotRegistered(e,t){let n=e.shape.method.value;if(this._registeredMethods.has(n))throw Error(`Handler for "${n}" already registered (via ${t}). Use addEventListener() to attach multiple listeners, or the on* setter for replace semantics.`);this._registeredMethods.add(n)}},tO=`2026-01-26`,nO=M([F(`light`),F(`dark`)]).describe(`Color theme preference for the host environment.`),rO=M([F(`inline`),F(`fullscreen`),F(`pip`)]).describe(`Display mode for UI presentation.`),iO=N(M([F(`--color-background-primary`),F(`--color-background-secondary`),F(`--color-background-tertiary`),F(`--color-background-inverse`),F(`--color-background-ghost`),F(`--color-background-info`),F(`--color-background-danger`),F(`--color-background-success`),F(`--color-background-warning`),F(`--color-background-disabled`),F(`--color-text-primary`),F(`--color-text-secondary`),F(`--color-text-tertiary`),F(`--color-text-inverse`),F(`--color-text-ghost`),F(`--color-text-info`),F(`--color-text-danger`),F(`--color-text-success`),F(`--color-text-warning`),F(`--color-text-disabled`),F(`--color-border-primary`),F(`--color-border-secondary`),F(`--color-border-tertiary`),F(`--color-border-inverse`),F(`--color-border-ghost`),F(`--color-border-info`),F(`--color-border-danger`),F(`--color-border-success`),F(`--color-border-warning`),F(`--color-border-disabled`),F(`--color-ring-primary`),F(`--color-ring-secondary`),F(`--color-ring-inverse`),F(`--color-ring-info`),F(`--color-ring-danger`),F(`--color-ring-success`),F(`--color-ring-warning`),F(`--font-sans`),F(`--font-mono`),F(`--font-weight-normal`),F(`--font-weight-medium`),F(`--font-weight-semibold`),F(`--font-weight-bold`),F(`--font-text-xs-size`),F(`--font-text-sm-size`),F(`--font-text-md-size`),F(`--font-text-lg-size`),F(`--font-heading-xs-size`),F(`--font-heading-sm-size`),F(`--font-heading-md-size`),F(`--font-heading-lg-size`),F(`--font-heading-xl-size`),F(`--font-heading-2xl-size`),F(`--font-heading-3xl-size`),F(`--font-text-xs-line-height`),F(`--font-text-sm-line-height`),F(`--font-text-md-line-height`),F(`--font-text-lg-line-height`),F(`--font-heading-xs-line-height`),F(`--font-heading-sm-line-height`),F(`--font-heading-md-line-height`),F(`--font-heading-lg-line-height`),F(`--font-heading-xl-line-height`),F(`--font-heading-2xl-line-height`),F(`--font-heading-3xl-line-height`),F(`--border-radius-xs`),F(`--border-radius-sm`),F(`--border-radius-md`),F(`--border-radius-lg`),F(`--border-radius-xl`),F(`--border-radius-full`),F(`--border-width-regular`),F(`--shadow-hairline`),F(`--shadow-sm`),F(`--shadow-md`),F(`--shadow-lg`)]).describe(`CSS variable keys available to MCP apps for theming.`).describe(`Style variables for theming MCP apps.
162
162
 
163
163
  Individual style keys are optional - hosts may provide any subset of these values.
164
164
  Values are strings containing CSS values (colors, sizes, font stacks, etc.).
@@ -204,7 +204,7 @@ Boolean requesting whether a visible border and background is provided by the ho
204
204
  - omitted: host decides border`)});var vO=j({method:F(`ui/request-display-mode`),params:j({mode:rO.describe(`The display mode being requested.`)})});j({mode:rO.describe(`The display mode that was actually set. May differ from requested if not supported.`)}).passthrough();var yO=M([F(`model`),F(`app`)]).describe(`Tool visibility scope - who can access the tool.`);j({resourceUri:O().optional(),visibility:A(yO).optional().describe(`Who can access this tool. Default: ["model", "app"]
205
205
  - "model": Tool visible to and callable by the agent
206
206
  - "app": Tool callable by the app from this server only`)}),j({mimeTypes:A(O()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')});var bO=j({method:F(`ui/download-file`),params:j({contents:A(M([Fc,Ic])).describe(`Resource contents to download — embedded (inline data) or linked (host fetches). Uses standard MCP resource types.`)})}),xO=j({method:F(`ui/message`),params:j({role:F(`user`).describe(`Message role, currently only "user" is supported.`),content:A(Lc).describe(`Message content blocks (text, image, etc.).`)})});j({method:F(`ui/notifications/sandbox-resource-ready`),params:j({html:O().describe(`HTML content to load into the inner iframe.`),sandbox:O().optional().describe(`Optional override for the inner iframe's sandbox attribute.`),csp:sO.optional().describe(`CSP configuration from resource metadata.`),permissions:cO.optional().describe(`Sandbox permissions from resource metadata.`)})}),j({method:F(`ui/notifications/tool-result`),params:Kc.describe(`Standard MCP tool execution result.`)});var SO=j({toolInfo:j({id:ds.optional().describe(`JSON-RPC id of the tools/call request.`),tool:Uc.describe(`Tool definition including name, inputSchema, etc.`)}).optional().describe(`Metadata of the tool call that instantiated this App.`),theme:nO.optional().describe(`Current color theme preference.`),styles:dO.optional().describe(`Style configuration for theming the app.`),displayMode:rO.optional().describe(`How the UI is currently displayed.`),availableDisplayModes:A(rO).optional().describe(`Display modes the host supports.`),containerDimensions:M([j({height:k().describe(`Fixed container height in pixels.`)}),j({maxHeight:M([k(),$a()]).optional().describe(`Maximum container height in pixels.`)})]).and(M([j({width:k().describe(`Fixed container width in pixels.`)}),j({maxWidth:M([k(),$a()]).optional().describe(`Maximum container width in pixels.`)})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
207
- container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:O().optional().describe(`User's language and region preference in BCP 47 format.`),timeZone:O().optional().describe(`User's timezone in IANA format.`),userAgent:O().optional().describe(`Host application identifier.`),platform:M([F(`web`),F(`desktop`),F(`mobile`)]).optional().describe(`Platform type for responsive design decisions.`),deviceCapabilities:j({touch:Za().optional().describe(`Whether the device supports touch input.`),hover:Za().optional().describe(`Whether the device supports hover interactions.`)}).optional().describe(`Device input capabilities.`),safeAreaInsets:j({top:k().describe(`Top safe area inset in pixels.`),right:k().describe(`Right safe area inset in pixels.`),bottom:k().describe(`Bottom safe area inset in pixels.`),left:k().describe(`Left safe area inset in pixels.`)}).optional().describe(`Mobile safe area boundaries in pixels.`)}).passthrough();j({method:F(`ui/notifications/host-context-changed`),params:SO.describe(`Partial context update containing only changed fields.`)});var CO=j({method:F(`ui/update-model-context`),params:j({content:A(Lc).optional().describe(`Context content blocks (text, image, etc.).`),structuredContent:N(O(),ao().describe(`Structured content for machine-readable context data.`)).optional().describe(`Structured content for machine-readable context data.`)})}),wO=j({method:F(`ui/initialize`),params:j({appInfo:Es.describe(`App identification (name and version).`),appCapabilities:gO.describe(`Features and capabilities this app provides.`),protocolVersion:O().describe(`Protocol version this app supports.`)})});j({protocolVersion:O().describe(`Negotiated protocol version string (e.g., "2025-11-21").`),hostInfo:Es.describe(`Host application identification and version.`),hostCapabilities:hO.describe(`Features and capabilities provided by the host.`),hostContext:SO.describe(`Rich context about the host environment.`)}).passthrough();var TO=class{eventTarget;eventSource;messageListener;constructor(e=window.parent,t){this.eventTarget=e,this.eventSource=t,this.messageListener=e=>{if(t&&e.source!==this.eventSource){console.debug(`Ignoring message from unknown source`,e);return}let n=bs.safeParse(e.data);n.success?(console.debug(`Parsed message`,n.data),this.onmessage?.(n.data)):e.data?.jsonrpc===`2.0`?(console.error(`Failed to parse message`,n.error.message,e),this.onerror?.(Error(`Invalid JSON-RPC message received: `+n.error.message))):console.debug(`Ignoring non-JSON-RPC message`,n.error.message,e)}}async start(){window.addEventListener(`message`,this.messageListener)}async send(e,t){e.method!==`ui/notifications/tool-input-partial`&&console.debug(`Sending message`,e),this.eventTarget.postMessage(e,`*`)}async close(){window.removeEventListener(`message`,this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion},EO=`ui/resourceUri`,DO=`text/html;profile=mcp-app`;function OO(e){let t=e._meta?.ui?.resourceUri;if(t===void 0&&(t=e._meta?.[EO]),typeof t==`string`&&t.startsWith(`ui://`))return t;if(t!==void 0)throw Error(`Invalid UI resource URI: ${JSON.stringify(t)}`)}function kO(e){if(!e)return``;let t=[];return e.camera&&t.push(`camera`),e.microphone&&t.push(`microphone`),e.geolocation&&t.push(`geolocation`),e.clipboardWrite&&t.push(`clipboard-write`),t.join(`; `)}var AO=[tO],jO=class extends eO{_client;_hostInfo;_capabilities;_appCapabilities;_hostContext={};_appInfo;eventSchemas={sizechange:lO,sandboxready:oO,initialized:_O,requestteardown:mO,loggingmessage:tl};constructor(e,t,n,r){super(r),this._client=e,this._hostInfo=t,this._capabilities=n,this._hostContext=r?.hostContext||{},this.setRequestHandler(wO,e=>this._oninitialize(e)),this.setRequestHandler(Ls,(e,t)=>(this.onping?.(e.params,t),{})),this.replaceRequestHandler(vO,e=>({mode:this._hostContext.displayMode??`inline`}))}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler(`sizechange`)}set onsizechange(e){this.setEventHandler(`sizechange`,e)}get onsandboxready(){return this.getEventHandler(`sandboxready`)}set onsandboxready(e){this.setEventHandler(`sandboxready`,e)}get oninitialized(){return this.getEventHandler(`initialized`)}set oninitialized(e){this.setEventHandler(`initialized`,e)}_onmessage;get onmessage(){return this._onmessage}set onmessage(e){this.warnIfRequestHandlerReplaced(`onmessage`,this._onmessage,e),this._onmessage=e,this.replaceRequestHandler(xO,async(e,t)=>{if(!this._onmessage)throw Error(`No onmessage handler set`);return this._onmessage(e.params,t)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(e){this.warnIfRequestHandlerReplaced(`onopenlink`,this._onopenlink,e),this._onopenlink=e,this.replaceRequestHandler(aO,async(e,t)=>{if(!this._onopenlink)throw Error(`No onopenlink handler set`);return this._onopenlink(e.params,t)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(e){this.warnIfRequestHandlerReplaced(`ondownloadfile`,this._ondownloadfile,e),this._ondownloadfile=e,this.replaceRequestHandler(bO,async(e,t)=>{if(!this._ondownloadfile)throw Error(`No ondownloadfile handler set`);return this._ondownloadfile(e.params,t)})}get onrequestteardown(){return this.getEventHandler(`requestteardown`)}set onrequestteardown(e){this.setEventHandler(`requestteardown`,e)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(e){this.warnIfRequestHandlerReplaced(`onrequestdisplaymode`,this._onrequestdisplaymode,e),this._onrequestdisplaymode=e,this.replaceRequestHandler(vO,async(e,t)=>{if(!this._onrequestdisplaymode)throw Error(`No onrequestdisplaymode handler set`);return this._onrequestdisplaymode(e.params,t)})}get onloggingmessage(){return this.getEventHandler(`loggingmessage`)}set onloggingmessage(e){this.setEventHandler(`loggingmessage`,e)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(e){this.warnIfRequestHandlerReplaced(`onupdatemodelcontext`,this._onupdatemodelcontext,e),this._onupdatemodelcontext=e,this.replaceRequestHandler(CO,async(e,t)=>{if(!this._onupdatemodelcontext)throw Error(`No onupdatemodelcontext handler set`);return this._onupdatemodelcontext(e.params,t)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(e){this.warnIfRequestHandlerReplaced(`oncalltool`,this._oncalltool,e),this._oncalltool=e,this.replaceRequestHandler(Jc,async(e,t)=>{if(!this._oncalltool)throw Error(`No oncalltool handler set`);return this._oncalltool(e.params,t)})}sendToolListChanged(e={}){return this.notification({method:`notifications/tools/list_changed`,params:e})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(e){this.warnIfRequestHandlerReplaced(`onlistresources`,this._onlistresources,e),this._onlistresources=e,this.replaceRequestHandler(uc,async(e,t)=>{if(!this._onlistresources)throw Error(`No onlistresources handler set`);return this._onlistresources(e.params,t)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(e){this.warnIfRequestHandlerReplaced(`onlistresourcetemplates`,this._onlistresourcetemplates,e),this._onlistresourcetemplates=e,this.replaceRequestHandler(fc,async(e,t)=>{if(!this._onlistresourcetemplates)throw Error(`No onlistresourcetemplates handler set`);return this._onlistresourcetemplates(e.params,t)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(e){this.warnIfRequestHandlerReplaced(`onreadresource`,this._onreadresource,e),this._onreadresource=e,this.replaceRequestHandler(gc,async(e,t)=>{if(!this._onreadresource)throw Error(`No onreadresource handler set`);return this._onreadresource(e.params,t)})}sendResourceListChanged(e={}){return this.notification({method:`notifications/resources/list_changed`,params:e})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(e){this.warnIfRequestHandlerReplaced(`onlistprompts`,this._onlistprompts,e),this._onlistprompts=e,this.replaceRequestHandler(Dc,async(e,t)=>{if(!this._onlistprompts)throw Error(`No onlistprompts handler set`);return this._onlistprompts(e.params,t)})}sendPromptListChanged(e={}){return this.notification({method:`notifications/prompts/list_changed`,params:e})}assertCapabilityForMethod(e){}assertRequestHandlerCapability(e){}assertNotificationCapability(e){}assertTaskCapability(e){throw Error(`Tasks are not supported in MCP Apps`)}assertTaskHandlerCapability(e){throw Error(`Task handlers are not supported in MCP Apps`)}getCapabilities(){return this._capabilities}async _oninitialize(e){let t=e.params.protocolVersion;return this._appCapabilities=e.params.appCapabilities,this._appInfo=e.params.appInfo,{protocolVersion:AO.includes(t)?t:tO,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(e){let t={},n=!1;for(let r of Object.keys(e)){let i=this._hostContext[r],a=e[r];MO(i,a)||(t[r]=a,n=!0)}n&&(this._hostContext=e,this.sendHostContextChange(t))}sendHostContextChange(e){return this.notification({method:`ui/notifications/host-context-changed`,params:e})}sendToolInput(e){return this.notification({method:`ui/notifications/tool-input`,params:e})}sendToolInputPartial(e){return this.notification({method:`ui/notifications/tool-input-partial`,params:e})}sendToolResult(e){return this.notification({method:`ui/notifications/tool-result`,params:e})}sendToolCancelled(e){return this.notification({method:`ui/notifications/tool-cancelled`,params:e})}sendSandboxResourceReady(e){return this.notification({method:`ui/notifications/sandbox-resource-ready`,params:e})}teardownResource(e,t){return this.request({method:`ui/resource-teardown`,params:e},fO,t)}sendResourceTeardown=this.teardownResource;callTool(e,t){return this.request({method:`tools/call`,params:e},Kc,t)}listTools(e,t){return this.request({method:`tools/list`,params:e},Gc,t)}async connect(e){if(this.transport)throw Error(`AppBridge is already connected. Call close() before connecting again.`);if(this._client){let e=this._client.getServerCapabilities();if(!e)throw Error(`Client server capabilities not available`);e.tools&&(this.oncalltool=async(e,t)=>this._client.request({method:`tools/call`,params:e},Kc,{signal:t.signal}),e.tools.listChanged&&this._client.setNotificationHandler(Yc,e=>this.sendToolListChanged(e.params))),e.resources&&(this.onlistresources=async(e,t)=>this._client.request({method:`resources/list`,params:e},dc,{signal:t.signal}),this.onlistresourcetemplates=async(e,t)=>this._client.request({method:`resources/templates/list`,params:e},pc,{signal:t.signal}),this.onreadresource=async(e,t)=>this._client.request({method:`resources/read`,params:e},_c,{signal:t.signal}),e.resources.listChanged&&this._client.setNotificationHandler(vc,e=>this.sendResourceListChanged(e.params))),e.prompts&&(this.onlistprompts=async(e,t)=>this._client.request({method:`prompts/list`,params:e},Oc,{signal:t.signal}),e.prompts.listChanged&&this._client.setNotificationHandler(Bc,e=>this.sendPromptListChanged(e.params)))}return super.connect(e)}};function MO(e,t){return JSON.stringify(e)===JSON.stringify(t)}[`default-src 'none'`,`script-src 'self' 'unsafe-inline'`,`style-src 'self' 'unsafe-inline'`,`img-src 'self' data:`,`media-src 'self' data:`,`connect-src 'none'`].join(`; `);function NO(e,t){if(!e)throw Error(`[@apollo/mcp-impostor-host] ${t}`)}function PO(){let e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var FO={debug:0,info:1,warn:2,error:3},IO=`[@apollo/mcp-impostor-host]`,LO=class{#e;constructor(e){this.#e=FO[e?.level??`info`]}debug(...e){this.#e<=FO.debug&&console.debug(IO,...e)}info(...e){this.#e<=FO.info&&console.log(IO,...e)}warn(...e){this.#e<=FO.warn&&console.warn(IO,...e)}error(...e){this.#e<=FO.error&&console.error(IO,...e)}},RO=class extends EventTarget{dispatchTypedEvent(e,t){return super.dispatchEvent(t)}},zO=class extends RO{#e;toolsByName=new Map;resourcesByUri=new Map;#t;#n=!1;constructor(e,t){super(),this.#e=e,this.#t=t.logger;for(let e of t.tools)this.toolsByName.set(e.name,e);for(let e of t.resources)this.resourcesByUri.set(e.uri,e)}get client(){return this.#e}get closed(){return this.#n}get logger(){return this.#t}callTool(e,t){let n=this.toolsByName.get(e);return NO(n,`Tool not found: '${e}'.`),{tool:n,input:t,resultPromise:this.#e.callTool({name:e,arguments:t})}}async close(){await this.#e.close(),this.#n=!0,this.dispatchTypedEvent(`close`,new CustomEvent(`close`))}async getUiResource(e){NO(e.startsWith(`ui://`),`Expected a UI resource URI (ui:// scheme). Got: '${e}'`);let t=await this.#e.readResource({uri:e});NO(t,`Resource not found: '${e}'.`),NO(t.contents.length===1,`Expected resource to contain exactly 1 content item. Got ${t.contents.length}.`);let n=t.contents[0];NO(n.mimeType===DO,`Unexpected mime type for resource: '${n.mimeType}'.`);let r=`blob`in n?atob(n.blob):n.text,i=this.resourcesByUri.get(e),a=n._meta?.ui??i?._meta?.ui;return{html:r,csp:a?.csp,permissions:a?.permissions}}},BO=class{#e;#t;#n=!1;constructor(e){this.#e=new OE({name:`@apollo/mcp-impostor-host`,version:$D.version},{capabilities:{extensions:Pw}}),this.#t=new LO({level:e?.logLevel})}async connect(e){NO(!this.#n,"Host only supports one connection at a time. Call `close` on the connection before connecting again.");let t=new QD(new URL(e.url));await this.#e.connect(t),this.#n=!0;let[n,r]=await Promise.all([this.#r(),this.#i()]),i=new zO(this.#e,{tools:n,resources:r,logger:this.#t});return i.addEventListener(`close`,()=>{this.#n=!1}),i}async#r(){let e=async t=>{let{tools:n,nextCursor:r}=await this.#e.listTools({cursor:t});return r?n.concat(await e(r)):n};return e()}async#i(){let e=async t=>{let{resources:n,nextCursor:r}=await this.#e.listResources({cursor:t});return r?n.concat(await e(r)):n};return e()}};function VO({url:e,connection:t,execution:n}){let r=n?OO(n.tool):null,i=!!(t&&n&&r),a=(0,I.useCallback)(i=>{if(!t||!n||!r)return;NO(!t.closed,`The connection is already closed.`);let a=!1,o=!0,s;function c(){o=!1,a&&s?.teardownResource({}).catch(()=>{}),s?.close()}return t.addEventListener(`close`,c),t.getUiResource(r).then(async r=>{if(!o)return;let c=kO(r.permissions);c&&i.setAttribute(`allow`,c),i.src=HO(e,r),await UO(i);let l=t.client.getServerCapabilities();s=new jO(t.client,{name:`@apollo/mcp-impostor-host`,version:$D.version},{serverTools:l?.tools,serverResources:l?.resources},{hostContext:{platform:`web`}});let u=PO();s.oninitialized=()=>{a=!0,u.resolve()},await s.connect(new TO(i.contentWindow,i.contentWindow)),s.sendSandboxResourceReady(r),await u.promise,s.sendToolInput({arguments:n.input});let d=await n.resultPromise;s.sendToolResult(d)}).catch(e=>{e instanceof Error&&t.logger.error(e.message,e.cause)}),()=>{o=!1,c(),t.removeEventListener(`close`,c)}},[t,n,r,e]);return i?(0,Wl.jsx)(`iframe`,{ref:a,sandbox:`allow-scripts allow-same-origin allow-forms`,style:{border:`none`,width:`100dvw`,height:`100dvh`,backgroundColor:`transparent`}}):null}function HO(e,t){let n=new URL(e);return t.csp&&Object.keys(t.csp).length>0&&n.searchParams.set(`csp`,JSON.stringify(t.csp)),n.href}function UO(e){return new Promise((t,n)=>{function r(){window.removeEventListener(`message`,a),e.removeEventListener(`error`,i)}function i(e){n(Error(`Could not load iframe`,{cause:e.error})),r()}function a(n){n.source===e.contentWindow&&n.data&&typeof n.data==`object`&&`method`in n.data&&n.data?.method===`ui/notifications/sandbox-proxy-ready`&&(t(),r())}window.addEventListener(`message`,a),e.addEventListener(`error`,i)})}var WO=`http://${window.location.hostname===`localhost`?`127.0.0.1`:`localhost`}:${window.location.port}/sandbox.html`;function GO(){let[e]=(0,I.useState)(()=>new BO),[t,n]=(0,I.useState)(null),[r,i]=(0,I.useState)(null);return(0,I.useEffect)(()=>{window.__mcpHost={async connect(t){n(await e.connect({url:t}))},async callTool(e,n){NO(t,"Host not connected. Call `connect()` before calling executing a tool call.");let r=t.callTool(e,n);return i(r),{result:await r.resultPromise,input:r.input}},async teardown(){t&&(await t.close(),n(null),i(null))}}},[e,t]),(0,Wl.jsx)(VO,{connection:t,execution:r,url:WO})}(0,Jo.createRoot)(document.getElementById(`root`)).render((0,Wl.jsx)(GO,{}));</script>
207
+ container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:O().optional().describe(`User's language and region preference in BCP 47 format.`),timeZone:O().optional().describe(`User's timezone in IANA format.`),userAgent:O().optional().describe(`Host application identifier.`),platform:M([F(`web`),F(`desktop`),F(`mobile`)]).optional().describe(`Platform type for responsive design decisions.`),deviceCapabilities:j({touch:Za().optional().describe(`Whether the device supports touch input.`),hover:Za().optional().describe(`Whether the device supports hover interactions.`)}).optional().describe(`Device input capabilities.`),safeAreaInsets:j({top:k().describe(`Top safe area inset in pixels.`),right:k().describe(`Right safe area inset in pixels.`),bottom:k().describe(`Bottom safe area inset in pixels.`),left:k().describe(`Left safe area inset in pixels.`)}).optional().describe(`Mobile safe area boundaries in pixels.`)}).passthrough();j({method:F(`ui/notifications/host-context-changed`),params:SO.describe(`Partial context update containing only changed fields.`)});var CO=j({method:F(`ui/update-model-context`),params:j({content:A(Lc).optional().describe(`Context content blocks (text, image, etc.).`),structuredContent:N(O(),ao().describe(`Structured content for machine-readable context data.`)).optional().describe(`Structured content for machine-readable context data.`)})}),wO=j({method:F(`ui/initialize`),params:j({appInfo:Es.describe(`App identification (name and version).`),appCapabilities:gO.describe(`Features and capabilities this app provides.`),protocolVersion:O().describe(`Protocol version this app supports.`)})});j({protocolVersion:O().describe(`Negotiated protocol version string (e.g., "2025-11-21").`),hostInfo:Es.describe(`Host application identification and version.`),hostCapabilities:hO.describe(`Features and capabilities provided by the host.`),hostContext:SO.describe(`Rich context about the host environment.`)}).passthrough();var TO=class{eventTarget;eventSource;messageListener;constructor(e=window.parent,t){this.eventTarget=e,this.eventSource=t,this.messageListener=e=>{if(t&&e.source!==this.eventSource){console.debug(`Ignoring message from unknown source`,e);return}let n=bs.safeParse(e.data);n.success?(console.debug(`Parsed message`,n.data),this.onmessage?.(n.data)):e.data?.jsonrpc===`2.0`?(console.error(`Failed to parse message`,n.error.message,e),this.onerror?.(Error(`Invalid JSON-RPC message received: `+n.error.message))):console.debug(`Ignoring non-JSON-RPC message`,n.error.message,e)}}async start(){window.addEventListener(`message`,this.messageListener)}async send(e,t){e.method!==`ui/notifications/tool-input-partial`&&console.debug(`Sending message`,e),this.eventTarget.postMessage(e,`*`)}async close(){window.removeEventListener(`message`,this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion},EO=`ui/resourceUri`,DO=`text/html;profile=mcp-app`;function OO(e){let t=e._meta?.ui?.resourceUri;if(t===void 0&&(t=e._meta?.[EO]),typeof t==`string`&&t.startsWith(`ui://`))return t;if(t!==void 0)throw Error(`Invalid UI resource URI: ${JSON.stringify(t)}`)}function kO(e){if(!e)return``;let t=[];return e.camera&&t.push(`camera`),e.microphone&&t.push(`microphone`),e.geolocation&&t.push(`geolocation`),e.clipboardWrite&&t.push(`clipboard-write`),t.join(`; `)}var AO=[tO],jO=class extends eO{_client;_hostInfo;_capabilities;_appCapabilities;_hostContext={};_appInfo;eventSchemas={sizechange:lO,sandboxready:oO,initialized:_O,requestteardown:mO,loggingmessage:tl};constructor(e,t,n,r){super(r),this._client=e,this._hostInfo=t,this._capabilities=n,this._hostContext=r?.hostContext||{},this.setRequestHandler(wO,e=>this._oninitialize(e)),this.setRequestHandler(Ls,(e,t)=>(this.onping?.(e.params,t),{})),this.replaceRequestHandler(vO,e=>({mode:this._hostContext.displayMode??`inline`}))}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler(`sizechange`)}set onsizechange(e){this.setEventHandler(`sizechange`,e)}get onsandboxready(){return this.getEventHandler(`sandboxready`)}set onsandboxready(e){this.setEventHandler(`sandboxready`,e)}get oninitialized(){return this.getEventHandler(`initialized`)}set oninitialized(e){this.setEventHandler(`initialized`,e)}_onmessage;get onmessage(){return this._onmessage}set onmessage(e){this.warnIfRequestHandlerReplaced(`onmessage`,this._onmessage,e),this._onmessage=e,this.replaceRequestHandler(xO,async(e,t)=>{if(!this._onmessage)throw Error(`No onmessage handler set`);return this._onmessage(e.params,t)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(e){this.warnIfRequestHandlerReplaced(`onopenlink`,this._onopenlink,e),this._onopenlink=e,this.replaceRequestHandler(aO,async(e,t)=>{if(!this._onopenlink)throw Error(`No onopenlink handler set`);return this._onopenlink(e.params,t)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(e){this.warnIfRequestHandlerReplaced(`ondownloadfile`,this._ondownloadfile,e),this._ondownloadfile=e,this.replaceRequestHandler(bO,async(e,t)=>{if(!this._ondownloadfile)throw Error(`No ondownloadfile handler set`);return this._ondownloadfile(e.params,t)})}get onrequestteardown(){return this.getEventHandler(`requestteardown`)}set onrequestteardown(e){this.setEventHandler(`requestteardown`,e)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(e){this.warnIfRequestHandlerReplaced(`onrequestdisplaymode`,this._onrequestdisplaymode,e),this._onrequestdisplaymode=e,this.replaceRequestHandler(vO,async(e,t)=>{if(!this._onrequestdisplaymode)throw Error(`No onrequestdisplaymode handler set`);return this._onrequestdisplaymode(e.params,t)})}get onloggingmessage(){return this.getEventHandler(`loggingmessage`)}set onloggingmessage(e){this.setEventHandler(`loggingmessage`,e)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(e){this.warnIfRequestHandlerReplaced(`onupdatemodelcontext`,this._onupdatemodelcontext,e),this._onupdatemodelcontext=e,this.replaceRequestHandler(CO,async(e,t)=>{if(!this._onupdatemodelcontext)throw Error(`No onupdatemodelcontext handler set`);return this._onupdatemodelcontext(e.params,t)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(e){this.warnIfRequestHandlerReplaced(`oncalltool`,this._oncalltool,e),this._oncalltool=e,this.replaceRequestHandler(Jc,async(e,t)=>{if(!this._oncalltool)throw Error(`No oncalltool handler set`);return this._oncalltool(e.params,t)})}sendToolListChanged(e={}){return this.notification({method:`notifications/tools/list_changed`,params:e})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(e){this.warnIfRequestHandlerReplaced(`onlistresources`,this._onlistresources,e),this._onlistresources=e,this.replaceRequestHandler(uc,async(e,t)=>{if(!this._onlistresources)throw Error(`No onlistresources handler set`);return this._onlistresources(e.params,t)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(e){this.warnIfRequestHandlerReplaced(`onlistresourcetemplates`,this._onlistresourcetemplates,e),this._onlistresourcetemplates=e,this.replaceRequestHandler(fc,async(e,t)=>{if(!this._onlistresourcetemplates)throw Error(`No onlistresourcetemplates handler set`);return this._onlistresourcetemplates(e.params,t)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(e){this.warnIfRequestHandlerReplaced(`onreadresource`,this._onreadresource,e),this._onreadresource=e,this.replaceRequestHandler(gc,async(e,t)=>{if(!this._onreadresource)throw Error(`No onreadresource handler set`);return this._onreadresource(e.params,t)})}sendResourceListChanged(e={}){return this.notification({method:`notifications/resources/list_changed`,params:e})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(e){this.warnIfRequestHandlerReplaced(`onlistprompts`,this._onlistprompts,e),this._onlistprompts=e,this.replaceRequestHandler(Dc,async(e,t)=>{if(!this._onlistprompts)throw Error(`No onlistprompts handler set`);return this._onlistprompts(e.params,t)})}sendPromptListChanged(e={}){return this.notification({method:`notifications/prompts/list_changed`,params:e})}assertCapabilityForMethod(e){}assertRequestHandlerCapability(e){}assertNotificationCapability(e){}assertTaskCapability(e){throw Error(`Tasks are not supported in MCP Apps`)}assertTaskHandlerCapability(e){throw Error(`Task handlers are not supported in MCP Apps`)}getCapabilities(){return this._capabilities}async _oninitialize(e){let t=e.params.protocolVersion;return this._appCapabilities=e.params.appCapabilities,this._appInfo=e.params.appInfo,{protocolVersion:AO.includes(t)?t:tO,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(e){let t={},n=!1;for(let r of Object.keys(e)){let i=this._hostContext[r],a=e[r];MO(i,a)||(t[r]=a,n=!0)}n&&(this._hostContext=e,this.sendHostContextChange(t))}sendHostContextChange(e){return this.notification({method:`ui/notifications/host-context-changed`,params:e})}sendToolInput(e){return this.notification({method:`ui/notifications/tool-input`,params:e})}sendToolInputPartial(e){return this.notification({method:`ui/notifications/tool-input-partial`,params:e})}sendToolResult(e){return this.notification({method:`ui/notifications/tool-result`,params:e})}sendToolCancelled(e){return this.notification({method:`ui/notifications/tool-cancelled`,params:e})}sendSandboxResourceReady(e){return this.notification({method:`ui/notifications/sandbox-resource-ready`,params:e})}teardownResource(e,t){return this.request({method:`ui/resource-teardown`,params:e},fO,t)}sendResourceTeardown=this.teardownResource;callTool(e,t){return this.request({method:`tools/call`,params:e},Kc,t)}listTools(e,t){return this.request({method:`tools/list`,params:e},Gc,t)}async connect(e){if(this.transport)throw Error(`AppBridge is already connected. Call close() before connecting again.`);if(this._client){let e=this._client.getServerCapabilities();if(!e)throw Error(`Client server capabilities not available`);e.tools&&(this.oncalltool=async(e,t)=>this._client.request({method:`tools/call`,params:e},Kc,{signal:t.signal}),e.tools.listChanged&&this._client.setNotificationHandler(Yc,e=>this.sendToolListChanged(e.params))),e.resources&&(this.onlistresources=async(e,t)=>this._client.request({method:`resources/list`,params:e},dc,{signal:t.signal}),this.onlistresourcetemplates=async(e,t)=>this._client.request({method:`resources/templates/list`,params:e},pc,{signal:t.signal}),this.onreadresource=async(e,t)=>this._client.request({method:`resources/read`,params:e},_c,{signal:t.signal}),e.resources.listChanged&&this._client.setNotificationHandler(vc,e=>this.sendResourceListChanged(e.params))),e.prompts&&(this.onlistprompts=async(e,t)=>this._client.request({method:`prompts/list`,params:e},Oc,{signal:t.signal}),e.prompts.listChanged&&this._client.setNotificationHandler(Bc,e=>this.sendPromptListChanged(e.params)))}return super.connect(e)}};function MO(e,t){return JSON.stringify(e)===JSON.stringify(t)}[`default-src 'none'`,`script-src 'self' 'unsafe-inline'`,`style-src 'self' 'unsafe-inline'`,`img-src 'self' data:`,`media-src 'self' data:`,`connect-src 'none'`].join(`; `);function NO(e,t){if(!e)throw Error(`[@apollo/mcp-impostor-host] ${t}`)}function PO(){let e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var FO={debug:0,info:1,warn:2,error:3},IO=`[@apollo/mcp-impostor-host]`,LO=class{#e;constructor(e){this.#e=FO[e?.level??`info`]}debug(...e){this.#e<=FO.debug&&console.debug(IO,...e)}info(...e){this.#e<=FO.info&&console.log(IO,...e)}warn(...e){this.#e<=FO.warn&&console.warn(IO,...e)}error(...e){this.#e<=FO.error&&console.error(IO,...e)}},RO=class extends EventTarget{dispatchTypedEvent(e,t){return super.dispatchEvent(t)}},zO=class extends RO{#e;toolsByName=new Map;resourcesByUri=new Map;#t;#n=!1;constructor(e,t){super(),this.#e=e,this.#t=t.logger;for(let e of t.tools)this.toolsByName.set(e.name,e);for(let e of t.resources)this.resourcesByUri.set(e.uri,e)}get client(){return this.#e}get closed(){return this.#n}get logger(){return this.#t}callTool(e,t){let n=this.toolsByName.get(e);return NO(n,`Tool not found: '${e}'.`),{tool:n,input:t,resultPromise:this.#e.callTool({name:e,arguments:t})}}async close(){await this.#e.close(),this.#n=!0,this.dispatchTypedEvent(`close`,new CustomEvent(`close`))}async getUiResource(e){NO(e.startsWith(`ui://`),`Expected a UI resource URI (ui:// scheme). Got: '${e}'`);let t=await this.#e.readResource({uri:e});NO(t,`Resource not found: '${e}'.`),NO(t.contents.length===1,`Expected resource to contain exactly 1 content item. Got ${t.contents.length}.`);let n=t.contents[0];NO(n.mimeType===DO,`Unexpected mime type for resource: '${n.mimeType}'.`);let r=`blob`in n?atob(n.blob):n.text,i=this.resourcesByUri.get(e),a=n._meta?.ui??i?._meta?.ui;return{html:r,csp:a?.csp,permissions:a?.permissions}}},BO=class{#e;#t;#n=!1;constructor(e){this.#e=new OE({name:`@apollo/mcp-impostor-host`,version:$D.version},{capabilities:{extensions:Pw}}),this.#t=new LO({level:e?.logLevel})}async connect(e){NO(!this.#n,"Host only supports one connection at a time. Call `close` on the connection before connecting again.");let t=new QD(new URL(e.url));await this.#e.connect(t),this.#n=!0;let[n,r]=await Promise.all([this.#r(),this.#i()]),i=new zO(this.#e,{tools:n,resources:r,logger:this.#t});return i.addEventListener(`close`,()=>{this.#n=!1}),i}async#r(){let e=async t=>{let{tools:n,nextCursor:r}=await this.#e.listTools({cursor:t});return r?n.concat(await e(r)):n};return e()}async#i(){let e=async t=>{let{resources:n,nextCursor:r}=await this.#e.listResources({cursor:t});return r?n.concat(await e(r)):n};return e()}};function VO({url:e,connection:t,execution:n,onMessage:r,onOpenLink:i}){let a=n?OO(n.tool):null,o=!!(t&&n&&a),s=(0,I.useRef)(r),c=(0,I.useRef)(i);(0,I.useLayoutEffect)(()=>{s.current=r,c.current=i});let l=(0,I.useCallback)(r=>{if(!t||!n||!a)return;NO(!t.closed,`The connection is already closed.`);let i=!1,o=!0,l;function u(){o=!1,i&&l?.teardownResource({}).catch(()=>{}),l?.close()}return t.addEventListener(`close`,u),t.getUiResource(a).then(async a=>{if(!o)return;let u=kO(a.permissions);u&&r.setAttribute(`allow`,u),r.src=HO(e,a),await UO(r);let d=t.client.getServerCapabilities();l=new jO(t.client,{name:`@apollo/mcp-impostor-host`,version:$D.version},{serverTools:d?.tools,serverResources:d?.resources,message:{},openLinks:{}},{hostContext:{platform:`web`}});let f=PO();l.oninitialized=()=>{i=!0,f.resolve()},l.onmessage=async e=>(s.current?.(e),{}),l.onopenlink=async e=>(c.current?.(e),{}),await l.connect(new TO(r.contentWindow,r.contentWindow)),l.sendSandboxResourceReady(a),await f.promise,l.sendToolInput({arguments:n.input});let p=await n.resultPromise;l.sendToolResult(p)}).catch(e=>{e instanceof Error&&t.logger.error(e.message,e.cause)}),()=>{o=!1,u(),t.removeEventListener(`close`,u)}},[t,n,a,e]);return o?(0,Wl.jsx)(`iframe`,{ref:l,sandbox:`allow-scripts allow-same-origin allow-forms`,style:{border:`none`,width:`100dvw`,height:`100dvh`,backgroundColor:`transparent`}}):null}function HO(e,t){let n=new URL(e);return t.csp&&Object.keys(t.csp).length>0&&n.searchParams.set(`csp`,JSON.stringify(t.csp)),n.href}function UO(e){return new Promise((t,n)=>{function r(){window.removeEventListener(`message`,a),e.removeEventListener(`error`,i)}function i(e){n(Error(`Could not load iframe`,{cause:e.error})),r()}function a(n){n.source===e.contentWindow&&n.data&&typeof n.data==`object`&&`method`in n.data&&n.data?.method===`ui/notifications/sandbox-proxy-ready`&&(t(),r())}window.addEventListener(`message`,a),e.addEventListener(`error`,i)})}var WO=`http://${window.location.hostname===`localhost`?`127.0.0.1`:`localhost`}:${window.location.port}/sandbox.html`;function GO(){let[e]=(0,I.useState)(()=>new BO),[t,n]=(0,I.useState)(null),[r,i]=(0,I.useState)(null);return(0,I.useEffect)(()=>{window.__mcpHost={async connect(t){n(await e.connect({url:t}))},async callTool(e,n){NO(t,"Host not connected. Call `connect()` before calling executing a tool call.");let r=t.callTool(e,n);return i(r),{result:await r.resultPromise,input:r.input}},async teardown(){t&&(await t.close(),n(null),i(null))}}},[e,t]),(0,Wl.jsx)(VO,{connection:t,execution:r,url:WO,onMessage:e=>window.__playwrightPushMessage(e),onOpenLink:e=>window.__playwrightSendOpenLinkRequest(e)})}(0,Jo.createRoot)(document.getElementById(`root`)).render((0,Wl.jsx)(GO,{}));</script>
208
208
  </head>
209
209
 
210
210
  <body>
@@ -1,10 +1,13 @@
1
+ import { type McpUiMessageRequest, type McpUiOpenLinkRequest } from "@modelcontextprotocol/ext-apps/app-bridge";
1
2
  import type { HostConnection } from "../core/index.js";
2
3
  export declare namespace Sandbox {
3
4
  interface Props {
4
5
  connection: HostConnection | null;
5
6
  execution: HostConnection.ToolExecution | null;
6
7
  url: string;
8
+ onMessage?: (params: McpUiMessageRequest["params"]) => void;
9
+ onOpenLink?: (params: McpUiOpenLinkRequest["params"]) => void;
7
10
  }
8
11
  }
9
- export declare function Sandbox({ url, connection, execution }: Sandbox.Props): import("react/jsx-runtime").JSX.Element | null;
12
+ export declare function Sandbox({ url, connection, execution, onMessage, onOpenLink, }: Sandbox.Props): import("react/jsx-runtime").JSX.Element | null;
10
13
  //# sourceMappingURL=Sandbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sandbox.d.ts","sourceRoot":"","sources":["../../src/react/Sandbox.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,CAAC,OAAO,WAAW,OAAO,CAAC;IAC/B,UAAiB,KAAK;QACpB,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;QAClC,SAAS,EAAE,cAAc,CAAC,aAAa,GAAG,IAAI,CAAC;QAC/C,GAAG,EAAE,MAAM,CAAC;KACb;CACF;AAED,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,KAAK,kDA2GpE"}
1
+ {"version":3,"file":"Sandbox.d.ts","sourceRoot":"","sources":["../../src/react/Sandbox.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAG1B,MAAM,2CAA2C,CAAC;AAKnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,CAAC,OAAO,WAAW,OAAO,CAAC;IAC/B,UAAiB,KAAK;QACpB,UAAU,EAAE,cAAc,GAAG,IAAI,CAAC;QAClC,SAAS,EAAE,cAAc,CAAC,aAAa,GAAG,IAAI,CAAC;QAC/C,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;QAC5D,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;KAC/D;CACF;AAED,wBAAgB,OAAO,CAAC,EACtB,GAAG,EACH,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,GACX,EAAE,OAAO,CAAC,KAAK,kDA8Hf"}
@@ -1,12 +1,18 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { AppBridge, buildAllowAttribute, getToolUiResourceUri, PostMessageTransport, SANDBOX_PROXY_READY_METHOD, } from "@modelcontextprotocol/ext-apps/app-bridge";
3
- import { useCallback } from "react";
3
+ import { useCallback, useLayoutEffect, useRef } from "react";
4
4
  import pkg from "#package.json";
5
5
  import { invariant } from "../utilities/invariant.js";
6
6
  import { promiseWithResolvers } from "../utilities/promiseWithResolvers.js";
7
- export function Sandbox({ url, connection, execution }) {
7
+ export function Sandbox({ url, connection, execution, onMessage, onOpenLink, }) {
8
8
  const resourceUri = execution ? getToolUiResourceUri(execution.tool) : null;
9
9
  const hasUiResource = !!(connection && execution && resourceUri);
10
+ const onMessageRef = useRef(onMessage);
11
+ const onOpenLinkRef = useRef(onOpenLink);
12
+ useLayoutEffect(() => {
13
+ onMessageRef.current = onMessage;
14
+ onOpenLinkRef.current = onOpenLink;
15
+ });
10
16
  const refCallback = useCallback((iframe) => {
11
17
  if (!connection || !execution || !resourceUri)
12
18
  return;
@@ -38,6 +44,8 @@ export function Sandbox({ url, connection, execution }) {
38
44
  bridge = new AppBridge(connection.client, { name: "@apollo/mcp-impostor-host", version: pkg.version }, {
39
45
  serverTools: capabilities?.tools,
40
46
  serverResources: capabilities?.resources,
47
+ message: {},
48
+ openLinks: {},
41
49
  }, {
42
50
  hostContext: {
43
51
  platform: "web",
@@ -48,6 +56,14 @@ export function Sandbox({ url, connection, execution }) {
48
56
  initialized = true;
49
57
  init.resolve();
50
58
  };
59
+ bridge.onmessage = async (params) => {
60
+ onMessageRef.current?.(params);
61
+ return {};
62
+ };
63
+ bridge.onopenlink = async (params) => {
64
+ onOpenLinkRef.current?.(params);
65
+ return {};
66
+ };
51
67
  await bridge.connect(new PostMessageTransport(iframe.contentWindow, iframe.contentWindow));
52
68
  bridge.sendSandboxResourceReady(uiResource);
53
69
  await init.promise;
@@ -1 +1 @@
1
- {"version":3,"file":"Sandbox.js","sourceRoot":"","sources":["../../src/react/Sandbox.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Sandbox.js","sourceRoot":"","sources":["../../src/react/Sandbox.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo/mcp-impostor-host",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A test host for MCP Apps — impersonates a real MCP Apps host (e.g. Claude Desktop) for end-to-end testing",
5
5
  "license": "MIT",
6
6
  "type": "module",