@mcp-b/transports 1.1.2-beta.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +266 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JSONRPCMessage } from "@
|
|
2
|
-
import { Transport, TransportSendOptions } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
1
|
+
import { JSONRPCMessage, Transport, TransportSendOptions } from "@mcp-b/webmcp-ts-sdk";
|
|
3
2
|
|
|
4
3
|
//#region src/browser-types.d.ts
|
|
4
|
+
|
|
5
5
|
/**
|
|
6
6
|
* Unique identifier for an event in the event store
|
|
7
7
|
*/
|
|
@@ -456,28 +456,269 @@ declare class IframeParentTransport implements Transport {
|
|
|
456
456
|
}
|
|
457
457
|
//#endregion
|
|
458
458
|
//#region src/TabClientTransport.d.ts
|
|
459
|
+
/**
|
|
460
|
+
* Configuration options for TabClientTransport.
|
|
461
|
+
*
|
|
462
|
+
* @see {@link TabClientTransport}
|
|
463
|
+
*/
|
|
459
464
|
interface TabClientTransportOptions {
|
|
460
|
-
/**
|
|
465
|
+
/**
|
|
466
|
+
* Expected origin of the server window for security validation.
|
|
467
|
+
*
|
|
468
|
+
* **Security**: This origin is checked against `event.origin` for all incoming
|
|
469
|
+
* messages to prevent cross-origin attacks. Only messages from this origin will
|
|
470
|
+
* be processed.
|
|
471
|
+
*
|
|
472
|
+
* @example 'https://example.com'
|
|
473
|
+
* @example 'http://localhost:3000'
|
|
474
|
+
*/
|
|
461
475
|
targetOrigin: string;
|
|
462
|
-
/**
|
|
476
|
+
/**
|
|
477
|
+
* Channel identifier for message routing.
|
|
478
|
+
*
|
|
479
|
+
* Multiple transports can coexist on the same page by using different channel IDs.
|
|
480
|
+
* This allows for isolated communication channels between different MCP clients
|
|
481
|
+
* and servers.
|
|
482
|
+
*
|
|
483
|
+
* @default 'mcp-default'
|
|
484
|
+
*/
|
|
463
485
|
channelId?: string;
|
|
486
|
+
/**
|
|
487
|
+
* Request timeout in milliseconds.
|
|
488
|
+
*
|
|
489
|
+
* If a request doesn't receive a response within this time, a timeout error
|
|
490
|
+
* is synthesized and delivered to the client. This prevents infinite hangs
|
|
491
|
+
* when the server becomes unresponsive due to:
|
|
492
|
+
* - Page navigation
|
|
493
|
+
* - JavaScript errors
|
|
494
|
+
* - Network issues
|
|
495
|
+
* - Server crashes
|
|
496
|
+
*
|
|
497
|
+
* **Design rationale**: 10 seconds is appropriate for most tool operations.
|
|
498
|
+
* For operations that may take longer (e.g., complex computations, slow network
|
|
499
|
+
* requests), increase this value via the configuration option.
|
|
500
|
+
*
|
|
501
|
+
* @default 10000 (10 seconds)
|
|
502
|
+
* @see {@link _handleRequestTimeout} for timeout implementation
|
|
503
|
+
*/
|
|
504
|
+
requestTimeout?: number;
|
|
464
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Client-side transport for same-window MCP communication via postMessage.
|
|
508
|
+
*
|
|
509
|
+
* This transport connects an MCP client to a TabServerTransport running in the same
|
|
510
|
+
* window. Communication occurs via the browser's `window.postMessage()` API, which
|
|
511
|
+
* provides:
|
|
512
|
+
* - Same-window message passing (no network overhead)
|
|
513
|
+
* - Origin validation for security
|
|
514
|
+
* - Asynchronous message delivery
|
|
515
|
+
*
|
|
516
|
+
* **Architecture**:
|
|
517
|
+
* ```
|
|
518
|
+
* ┌─────────────────┐ ┌──────────────────┐
|
|
519
|
+
* │ MCP Client │ postMessage() │ MCP Server │
|
|
520
|
+
* │ (This side) │ ←─────────────────→│ (TabServerTransport)
|
|
521
|
+
* └─────────────────┘ └──────────────────┘
|
|
522
|
+
* ```
|
|
523
|
+
*
|
|
524
|
+
* **Key features**:
|
|
525
|
+
* - Request timeout to prevent infinite hangs (default 10s)
|
|
526
|
+
* - Server ready detection via handshake
|
|
527
|
+
* - Origin validation for security
|
|
528
|
+
* - Channel-based message routing
|
|
529
|
+
*
|
|
530
|
+
* **Use cases**:
|
|
531
|
+
* - MCP client running in content script connecting to page context
|
|
532
|
+
* - MCP client in extension popup connecting to background page
|
|
533
|
+
* - Testing and development scenarios
|
|
534
|
+
*
|
|
535
|
+
* @example Basic usage
|
|
536
|
+
* ```typescript
|
|
537
|
+
* const transport = new TabClientTransport({
|
|
538
|
+
* targetOrigin: 'https://example.com',
|
|
539
|
+
* channelId: 'my-mcp-channel',
|
|
540
|
+
* requestTimeout: 10000, // Optional (default)
|
|
541
|
+
* });
|
|
542
|
+
*
|
|
543
|
+
* // Wait for server to be ready
|
|
544
|
+
* await transport.start();
|
|
545
|
+
* await transport.serverReadyPromise;
|
|
546
|
+
*
|
|
547
|
+
* // Now safe to send messages
|
|
548
|
+
* await transport.send({
|
|
549
|
+
* jsonrpc: '2.0',
|
|
550
|
+
* id: 1,
|
|
551
|
+
* method: 'tools/call',
|
|
552
|
+
* params: { name: 'my_tool', arguments: {} }
|
|
553
|
+
* });
|
|
554
|
+
* ```
|
|
555
|
+
*
|
|
556
|
+
* @example With custom timeout
|
|
557
|
+
* ```typescript
|
|
558
|
+
* const transport = new TabClientTransport({
|
|
559
|
+
* targetOrigin: '*',
|
|
560
|
+
* requestTimeout: 60000, // 60 seconds for slow operations
|
|
561
|
+
* });
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @see {@link TabServerTransport} for the server-side implementation
|
|
565
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage} for postMessage API
|
|
566
|
+
*/
|
|
465
567
|
declare class TabClientTransport implements Transport {
|
|
568
|
+
/** Transport state flag */
|
|
466
569
|
private _started;
|
|
467
|
-
|
|
468
|
-
private
|
|
570
|
+
/** Expected origin for message validation */
|
|
571
|
+
private readonly _targetOrigin;
|
|
572
|
+
/** Channel ID for message routing */
|
|
573
|
+
private readonly _channelId;
|
|
574
|
+
/** Request timeout in milliseconds */
|
|
575
|
+
private readonly _requestTimeout;
|
|
576
|
+
/** Message event listener */
|
|
469
577
|
private _messageHandler?;
|
|
578
|
+
/**
|
|
579
|
+
* Promise that resolves when the server is ready to receive messages.
|
|
580
|
+
*
|
|
581
|
+
* **Usage**: Always `await` this promise after calling `start()` and before
|
|
582
|
+
* sending messages. Sending messages before the server is ready may result
|
|
583
|
+
* in lost messages.
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* ```typescript
|
|
587
|
+
* await transport.start();
|
|
588
|
+
* await transport.serverReadyPromise;
|
|
589
|
+
* // Now safe to send messages
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
470
592
|
readonly serverReadyPromise: Promise<void>;
|
|
471
|
-
|
|
472
|
-
private
|
|
593
|
+
/** Internal resolver for serverReadyPromise */
|
|
594
|
+
private readonly _serverReadyResolve;
|
|
595
|
+
/** Internal rejector for serverReadyPromise */
|
|
596
|
+
private readonly _serverReadyReject;
|
|
597
|
+
/**
|
|
598
|
+
* Active request tracking for timeout management.
|
|
599
|
+
*
|
|
600
|
+
* **Key**: Request ID (from JSON-RPC message)
|
|
601
|
+
* **Value**: Timeout ID and original request
|
|
602
|
+
*
|
|
603
|
+
* When a response is received, the entry is removed and timeout cleared.
|
|
604
|
+
* If timeout expires first, an error response is synthesized.
|
|
605
|
+
*/
|
|
606
|
+
private readonly _activeRequests;
|
|
607
|
+
/** Callback invoked when transport closes */
|
|
473
608
|
onclose?: () => void;
|
|
609
|
+
/** Callback invoked on errors */
|
|
474
610
|
onerror?: (error: Error) => void;
|
|
611
|
+
/** Callback invoked when message received */
|
|
475
612
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
613
|
+
/**
|
|
614
|
+
* Creates a new TabClientTransport instance.
|
|
615
|
+
*
|
|
616
|
+
* **Note**: The transport is not started automatically. Call `start()` to begin
|
|
617
|
+
* listening for messages.
|
|
618
|
+
*
|
|
619
|
+
* @param options - Configuration options
|
|
620
|
+
* @throws {Error} If targetOrigin is not specified
|
|
621
|
+
*/
|
|
476
622
|
constructor(options: TabClientTransportOptions);
|
|
623
|
+
/**
|
|
624
|
+
* Starts the transport by registering message listeners.
|
|
625
|
+
*
|
|
626
|
+
* **Lifecycle**:
|
|
627
|
+
* 1. Register `window.addEventListener('message', ...)` handler
|
|
628
|
+
* 2. Send server ready check (in case server started first)
|
|
629
|
+
* 3. Wait for server ready signal via `serverReadyPromise`
|
|
630
|
+
*
|
|
631
|
+
* **Note**: Always `await transport.serverReadyPromise` after calling this method
|
|
632
|
+
* and before sending messages.
|
|
633
|
+
*
|
|
634
|
+
* @throws {Error} If transport is already started
|
|
635
|
+
*/
|
|
477
636
|
start(): Promise<void>;
|
|
478
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Sends a JSON-RPC message to the server.
|
|
639
|
+
*
|
|
640
|
+
* **Request timeout**: If this is a request (has `method` and `id`), a timeout
|
|
641
|
+
* is started. If the server doesn't respond within `requestTimeout` milliseconds,
|
|
642
|
+
* an error response is synthesized.
|
|
643
|
+
*
|
|
644
|
+
* **Await server ready**: This method automatically awaits `serverReadyPromise`
|
|
645
|
+
* before sending, ensuring messages aren't lost.
|
|
646
|
+
*
|
|
647
|
+
* @param message - JSON-RPC message to send
|
|
648
|
+
* @throws {Error} If transport is not started
|
|
649
|
+
*/
|
|
479
650
|
send(message: JSONRPCMessage): Promise<void>;
|
|
651
|
+
/**
|
|
652
|
+
* Closes the transport and cleans up resources.
|
|
653
|
+
*
|
|
654
|
+
* **Cleanup performed**:
|
|
655
|
+
* - Removes message event listener
|
|
656
|
+
* - Clears all active request timeouts
|
|
657
|
+
* - Rejects server ready promise if still pending
|
|
658
|
+
* - Invokes `onclose` callback
|
|
659
|
+
*
|
|
660
|
+
* **Note**: After calling this method, the transport cannot be reused.
|
|
661
|
+
* Create a new instance if needed.
|
|
662
|
+
*/
|
|
480
663
|
close(): Promise<void>;
|
|
664
|
+
/**
|
|
665
|
+
* Sends a server ready check message.
|
|
666
|
+
*
|
|
667
|
+
* This prompts the server to respond with 'mcp-server-ready' signal,
|
|
668
|
+
* resolving the `serverReadyPromise`. Useful when the server may have
|
|
669
|
+
* started before the client.
|
|
670
|
+
*
|
|
671
|
+
* @private
|
|
672
|
+
*/
|
|
673
|
+
private _sendCheckReady;
|
|
674
|
+
/**
|
|
675
|
+
* Starts timeout tracking for a request.
|
|
676
|
+
*
|
|
677
|
+
* **Behavior**: After `requestTimeout` milliseconds, if no response is received,
|
|
678
|
+
* `_handleRequestTimeout` is called to synthesize an error response.
|
|
679
|
+
*
|
|
680
|
+
* **Note**: Only requests (messages with `method` and `id`) are tracked.
|
|
681
|
+
* Notifications (no `id`) and responses are not tracked.
|
|
682
|
+
*
|
|
683
|
+
* @param message - JSON-RPC request message
|
|
684
|
+
* @private
|
|
685
|
+
*/
|
|
686
|
+
private _startRequestTimeout;
|
|
687
|
+
/**
|
|
688
|
+
* Clears timeout tracking for a response.
|
|
689
|
+
*
|
|
690
|
+
* Called when a response (with `result` or `error`) is received.
|
|
691
|
+
* Clears the timeout and removes the tracking entry.
|
|
692
|
+
*
|
|
693
|
+
* @param message - JSON-RPC response message
|
|
694
|
+
* @private
|
|
695
|
+
*/
|
|
696
|
+
private _clearRequestTimeout;
|
|
697
|
+
/**
|
|
698
|
+
* Handles request timeout by synthesizing an error response.
|
|
699
|
+
*
|
|
700
|
+
* **Error response format** (JSON-RPC 2.0):
|
|
701
|
+
* ```json
|
|
702
|
+
* {
|
|
703
|
+
* "jsonrpc": "2.0",
|
|
704
|
+
* "id": "<request-id>",
|
|
705
|
+
* "error": {
|
|
706
|
+
* "code": -32000,
|
|
707
|
+
* "message": "Request timeout - server may have navigated or become unresponsive",
|
|
708
|
+
* "data": {
|
|
709
|
+
* "timeoutMs": 10000,
|
|
710
|
+
* "originalMethod": "tools/call"
|
|
711
|
+
* }
|
|
712
|
+
* }
|
|
713
|
+
* }
|
|
714
|
+
* ```
|
|
715
|
+
*
|
|
716
|
+
* **Error code**: `-32000` (Server error) per JSON-RPC 2.0 specification.
|
|
717
|
+
*
|
|
718
|
+
* @param requestId - ID of the timed-out request
|
|
719
|
+
* @private
|
|
720
|
+
*/
|
|
721
|
+
private _handleRequestTimeout;
|
|
481
722
|
}
|
|
482
723
|
//#endregion
|
|
483
724
|
//#region src/TabServerTransport.d.ts
|
|
@@ -493,12 +734,28 @@ declare class TabServerTransport implements Transport {
|
|
|
493
734
|
private _channelId;
|
|
494
735
|
private _messageHandler?;
|
|
495
736
|
private _clientOrigin?;
|
|
737
|
+
private _beforeUnloadHandler?;
|
|
738
|
+
private _cleanupInterval?;
|
|
739
|
+
private _pendingRequests;
|
|
740
|
+
private readonly REQUEST_TIMEOUT_MS;
|
|
496
741
|
onclose?: () => void;
|
|
497
742
|
onerror?: (error: Error) => void;
|
|
498
743
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
499
744
|
constructor(options: TabServerTransportOptions);
|
|
500
745
|
start(): Promise<void>;
|
|
501
746
|
send(message: JSONRPCMessage): Promise<void>;
|
|
747
|
+
/**
|
|
748
|
+
* Handle page navigation by sending interrupted responses for all pending requests.
|
|
749
|
+
* Called during beforeunload event.
|
|
750
|
+
* @private
|
|
751
|
+
*/
|
|
752
|
+
private _handleBeforeUnload;
|
|
753
|
+
/**
|
|
754
|
+
* Clean up stale requests that have been pending for too long.
|
|
755
|
+
* Called periodically to prevent memory leaks.
|
|
756
|
+
* @private
|
|
757
|
+
*/
|
|
758
|
+
private _cleanupStaleRequests;
|
|
502
759
|
close(): Promise<void>;
|
|
503
760
|
}
|
|
504
761
|
//#endregion
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/browser-types.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/UserScriptClientTransport.ts","../src/UserScriptServerTransport.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/browser-types.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/UserScriptClientTransport.ts","../src/UserScriptServerTransport.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAKY,KALA,OAAA,GAKQ,MAAA;AAKpB;AAUA;AAkBA;AAO8C,KAxClC,QAAA,GAwCkC,MAAA;;;;AAkB7B,UArDA,iBAAA,CAqDmB;EAOE;;;EA2B3B,UAAA,CAAA,EAnFI,OAmFJ;;AAMX;AAOA;AAQA;AAMiB,UAxGA,aAAA,CAwGqB;EASrB;;;EAGN,UAAA,EAAA,MAAA;EAAc;AAKzB;AA6BA;EAMa,QAAA,EAAA,OAMH;EAGG;AAQb;AAKA;EAMa,aAA4B,EAAA,OAAA;AAKzC;;;;AC5MiB,UDiCA,aAAA,CCjCA;EAkDJ;;;;;;EAyIoC,SAAA,CAAA,QAAA,CAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EDnJH,OCmJG,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EDnJuB,WCmJvB,EAAA;EAAuB;;;;qCD7InC;;;AE9CrC;AAuBA;EAcoB,WAAA,CAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;;;;AA6EE,UFxDL,mBAAA,CEwDK;EAA2B;;;;;;sCFjDX,oBAAoB;;AGzE1D;AAyBA;;EAWwB,UAAA,CAAA,QAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAED;;;;EAoHN,gBAAA,EAAA,QAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAjI4B;;;;;ACzB7C;AA6BA;EAQsC,aAAA,EAAA,EJ0DnB,aI1DmB;EAKlB;;;EAsBH,MAAA,CAAA,EJoCN,aIpCM;;;;;AAnCsC,UJ6EtC,SAAA,SAAkB,MI7EoB,CAAA;QJ8E/C;;;AKtGR;AAsHA;AA8BsC,ULxCrB,oBAAA,CKwCqB;EAuBlB,IAAA,EAAA,iBAAA;EAGI,gBAAA,EAAA,MAAA;EAWD,eAAA,CAAA,EAAA,MAAA;EA6BN,aAAA,EAAA,OAAA;EA2EK,QAAA,ELhLV,QKgLU;;AAqCL,ULlNA,eAAA,CKkNA;EAhN0B,IAAA,EAAA,WAAA;EAAS,OAAA,ELAzC,OKAyC;WLCzC;;UAGM,qBAAA;EM/HA,IAAA,EAAA,kBAAA;EAOJ,OAAA,EN0HF,OM1HE;EAmBO,OAAA,ENwGT,cMxGS;;;;;AA8FmB,UNgBtB,WAAA,CMhBsB;EAuHtB,OAAA,ENtGN,OMsGM;EAxO0B,QAAA,ENmI/B,QMnI+B;EAAS,OAAA,ENoIzC,cMpIyC;;;;ACCnC,aPwIL,iBAAA;EOrFC,KAAA,GAAA,OAAA;EAqBO,OAAA,GAAA,SAAA;EACI,IAAA,GAAA,MAAA;EAED,OAAA,GAAA,SAAA;EAcN,IAAA,GAAA,MAAA;EAkGK,IAAA,GAAA,MAAA;EAA2B,KAAA,GAAA,OAAA;EAAuB,UAAA,GAAA,YAAA;EAuBvD,SAAA,GAAA,WAAA;EA/JiC,iBAAA,GAAA,mBAAA;EAAS,qBAAA,GAAA,uBAAA;;;;ECnD/C,sBAAA,GAAA,wBAAgC;EAyB/B,cAAA,GAAA,eAA0B;EAcnB,WAAA,GAAA,aAAA;EACI,iBAAA,GAAA,mBAAA;;;;;;AA6EgD,cRgD3D,WQhD2D,EAAA;EAyBvD,SAAA,IAAA,EAAA,0BAAA;EArHiC,SAAA,YAAA,EAAA,KAAA;CAAS;cRkJ9C;;;;;;;cASA;;;;;;cAQA;;;cAKA;;;;;cAMA;;;;UAKI,YAAA;;;;;;;;;AAjNjB;AAKY,UCAK,+BAAA,CDAG;EAKH;AAUjB;AAkBA;EAO8C,WAAA,CAAA,EAAA,MAAA;EAA0B;;;AAkBxE;EAOsC,QAAA,CAAA,EAAA,MAAA;EAAoB;;;;EAiCzC,aAAU,CAAA,EAAA,OAAA;EAOV;AAQjB;AAMA;AASA;EACW,oBAAA,CAAA,EAAA,MAAA;EACC;;;AAMZ;EA6Ba,cAGH,CAAA,EAAA,MAAA;EAGG;AASb;AAQA;AAKA;EAMa,iBAA4B,CAAA,EAAA,MAAA;EAKxB;;;;EC5MA,0BAAA,CAAA,EAAA,MAA+B;AAkDhD;;;;;;;;AAgKiB,cAhKJ,wBAAA,YAAoC,SAgKhC,CAAA;EAhKgC,QAAA,KAAA;EAAS,QAAA,YAAA;;;;EClD9C,QAAA,eAAA;EAuBC,QAAA,kBAAA;EAcO,QAAA,eAAA;EACI,QAAA,sBAAA;EAEJ,QAAO,UAAQ;EAAe,QAAA,SAAA;EAgBjC,QAAA,cAAA;EA0DK,QAAA,qBAAA;EAA2B,QAAA,eAAA;EAAuB,QAAA,kBAAA;EAyBvD,QAAA,2BAAA;EApHgC,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAS,OAAA,CAAA,EAAA,CAAA,KAAA,EDgDtC,KChDsC,EAAA,GAAA,IAAA;wBDiDlC;wBAED;;AElFvB;AAyBA;EAUoB,KAAA,CAAA,CAAA,EF6DH,OE7DG,CAAA,IAAA,CAAA;EACI;;;EA6FF,QAAA,QAAA;EAAiB;;;EAxGe,IAAA,CAAA,OAAA,EF0KhC,cE1KgC,EAAA,QAAA,CAAA,EF0KL,oBE1KK,CAAA,EF0KkB,OE1KlB,CAAA,IAAA,CAAA;;;;ECzBrC,KAAA,CAAA,CAAA,EH0NA,OG1NA,CAAA,IAAA,CAAA;EA6BJ;;;EAcW,QAAA,QAAA;EAED;;;EA4GgB,QAAA,kBAAA;EAwBtB;;;;;;;;;AJ9KjB;AAKY,KEAA,+BAAA,GFAQ;EAKH;AAUjB;AAkBA;;EAOwE,SAAA,CAAA,EAAA,OAAA;EAMnC;;AAYrC;;EAO0D,iBAAA,CAAA,EAAA,MAAA;CAsBvC;;;AAWnB;AAOA;AAQA;AAMA;AASA;;;AAGW,cE5GE,wBAAA,YAAoC,SF4GtC,CAAA;EAAc,QAAA,KAAA;EAKb,QAAA,QAAA;EA6BC,QAAA,eAGH;EAGG,QAAA,kBAMH;EAGG,QAAA,eAKH;EAGG,QAAA,QAEH;EAGG,QAAA,eAAA;EAMA,OAAA,CAAA,EAAA,GAA4B,GAAA,IAAA;EAKxB,OAAA,CAAA,EAAA,CAAA,KAAY,EEvKT,KFuKS,EAAA,GAAA,IAAA;wBEtKL;oBAEJ,MAAA,CAAO,OAAA,CAAQ,gBAAe;;ADxClD;AAkDA;EAqBoB,KAAA,CAAA,CAAA,ECfH,ODeG,CAAA,IAAA,CAAA;EACI;;;EAmHF,IAAA,CAAA,OAAA,ECzEA,cDyEA,EAAA,QAAA,CAAA,ECzE2B,oBDyE3B,CAAA,ECzEkD,ODyElD,CAAA,IAAA,CAAA;EAA2B;;;EAzIA,KAAA,CAAA,CAAA,ECyFhC,ODzFgC,CAAA,IAAA,CAAA;EAAS;;;;EClD9C;AAuBZ;;EAewB,QAAA,eAAA;EAEJ;;;EA0EE,QAAA,cAAA;EAA2B;;;EA3FA,iBAAA,CAAA,CAAA,EAAA;IAAS,MAAA,EAAA,MAAA;;;;IC/BzC,YAAA,EAAA,MAAA;EAyBJ,CAAA;;;;UAzBI,2BAAA;;EHGL,cAAO,EAAA,MAAA,EAAA;EAKP;EAKK,SAAA,CAAA,EAAA,MAAA;EAUA;EAkBA,kBAAa,CAAA,EAAA,MAAA;;;;;AAyB9B;;;;;;AAwCA;AAOA;AAQA;AAMA;AASA;;;AAGW,cGlHE,oBAAA,YAAgC,SHkHlC,CAAA;EAAc,QAAA,QAAA;EAKb,QAAA,eAAiB;EA6BhB,QAAA,UAGH;EAGG,QAAA,eAMH;EAGG,QAAA,aAKH;EAGG,QAAA,mBAEH;EAGG,iBAAA,mBAIH;EAEG,OAAA,CAAA,EAAA,GAA4B,GAAA,IAAA;EAKxB,OAAA,CAAA,EAAA,CAAA,KAAY,EGjLT,KHiLS,EAAA,GAAA,IAAA;wBGhLL;uBAED;WAUN;EFxCA,QAAA,oBAAA;EAkDJ,QAAA,wBAAyB;EAqBlB,QAAA,qBAAA;EACI,IAAA,CAAA,OAAA,EEiDF,cFjDE,CAAA,EEiDe,OFjDf,CAAA,IAAA,CAAA;EAED,KAAA,CAAA,CAAA,EEwEN,OFxEM,CAAA,IAAA,CAAA;;;;UGlFN,4BAAA;;EJGL,MAAA,EIDF,iBJCS;EAKP;EAKK,YAAA,EAAA,MAAiB;EAUjB;EAkBA,SAAA,CAAA,EAAA,MAAa;EAOgB;EAA0B,iBAAA,CAAA,EAAA,MAAA;;;AAkBxE;;;;;;AAwCA;AAOA;AAQA;AAMA;AASA;;;;;AAQA;AA6BA;AAMa,cItJA,qBAAA,YAAiC,SJ4JpC,CAAA;EAGG,QAAA,QAAA;EAQA,QAAA,OAEH;EAGG,QAAA,aAAA;EAMA,QAAA,UAA4B;EAKxB,QAAA,eAAY;;;+BI/KS;EH7BrB,QAAA,mBAAA;EAkDJ,QAAA,kBAAA;EAqBO,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACI,OAAA,CAAA,EAAA,CAAA,KAAA,EGtCJ,KHsCI,EAAA,GAAA,IAAA;EAED,SAAA,CAAA,EAAA,CAAA,OAAA,EGvCC,cHuCD,EAAA,GAAA,IAAA;EAcN,WAAA,CAAA,OAAA,EGnDM,4BHmDN;EAmGK,KAAA,CAAA,CAAA,EGnIL,OHmIK,CAAA,IAAA,CAAA;EAA2B,QAAA,cAAA;EAAuB,QAAA,uBAAA;EAuBvD,QAAA,oBAAA;EAhKgC,IAAA,CAAA,OAAA,EG+F3B,cH/F2B,CAAA,EG+FV,OH/FU,CAAA,IAAA,CAAA;EAAS,KAAA,CAAA,CAAA,EGuHzC,OHvHyC,CAAA,IAAA,CAAA;;;;;;ADvD1D;AAKA;AAKA;AAUiB,UKlBA,yBAAA,CLkBa;EAkBb;;;;;AAyBjB;;;;;EAkCwB,YAAA,EAAA,MAAA;EAMP;AAOjB;AAQA;AAMA;AASA;;;;;EAQY,SAAA,CAAA,EAAA,MAAA;EA6BC;AAMb;AASA;AAQA;AAKA;AAMA;AAKA;;;;AC5MA;AAkDA;;;;;;;EAyIwE,cAAA,CAAA,EAAA,MAAA;;;;;;;AC3LxE;AAuBA;;;;;;;;;;;;;;;AC/BA;AAyBA;;;;;;;;;;;;;ACzBA;AA6BA;;;;;;;;;;;;;;ACxBA;AAsHA;;;;;;;;;;AAAoD,cAAvC,kBAAA,YAA8B,SAAS,CAAA;;;;EC3HnC,iBAAA,aAAyB;EAO7B;EAmBO,iBAAA,UAAA;EACI;EAED,iBAAA,eAAA;EASN;EAkFK,QAAA,eAAA;EAAiB;;;;;;;AChHvC;AAmDA;;;;;;EAwIiD,SAAA,kBAAA,EF1CX,OE0CW,CAAA,IAAA,CAAA;EAAuB;EAuBvD,iBAAA,mBAAA;EA/JiC;EAAS,iBAAA,kBAAA;;;;ACnD3D;AAyBA;;;;;EAiCiB,iBAAA,eAAA;EA2DK;EAA2B,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAuB;EAyBvD,OAAA,CAAA,EAAA,CAAA,KAAA,EH0BG,KG1BH,EAAA,GAAA,IAAA;EArHiC;EAAS,SAAA,CAAA,EAAA,CAAA,OAAA,EHkJnC,cGlJmC,EAAA,GAAA,IAAA;;;;;;;;;;uBH6JpC;;;;;;;;;;;;;;WA6BN;;;;;;;;;;;;;;gBA2EK,iBAAiB;;;;;;;;;;;;;WAqCtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UC3UA,yBAAA;;ENGL,cAAO,EAAA,MAAA,EAAA;EAKP;EAKK,SAAA,CAAA,EAAA,MAAA;AAUjB;AAkBiB,cMlCJ,kBAAA,YAA8B,SNkCb,CAAA;EAOgB,QAAA,QAAA;EAA0B,QAAA,eAAA;EAMnC,QAAA,UAAA;EAAO,QAAA,eAAA;EAY3B,QAAA,aAAA;EAOqB,QAAA,oBAAA;EAAoB,QAAA,gBAAA;EAsBvC,QAAA,gBAAA;EAKR,iBAAA,kBAAA;EAAa,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAMP,OAAA,CAAA,EAAA,CAAA,KAAU,EMhFP,KNgFO,EACnB,GAAA,IAAA;EAMS,SAAA,CAAA,EAAA,CAAA,OAAA,EMtFO,cN2FZ,EAAA,GAAA,IAAQ;EAGH,WAAA,CAAA,OAAe,EM5FT,yBN+FZ;EAGM,KAAA,CAAA,CAAA,EMzFA,ONyFA,CAAA,IAAA,CAAA;EASA,IAAA,CAAA,OAAA,EMhBK,cNgBM,CAAA,EMhBW,ONgBX,CAAA,IAAA,CAAA;EACjB;;;;AAOX;EA6Ba,QAAA,mBAGH;EAGG;AASb;AAQA;AAKA;AAMA;EAKiB,QAAA,qBAAY;WM2BZ;;;;;;AN5OjB;AAKY,UOAK,gCAAA,CPAG;EAKH;AAUjB;AAkBA;EAO8C,WAAA,CAAA,EAAA,MAAA;EAA0B;;;AAkBxE;EAOsC,QAAA,CAAA,EAAA,MAAA;EAAoB;;;;EAiCzC,aAAU,CAAA,EAAA,OAAA;EAOV;AAQjB;AAMA;AASA;EACW,oBAAA,CAAA,EAAA,MAAA;EACC;;;AAMZ;EA6Ba,cAGH,CAAA,EAAA,MAAA;EAGG;AASb;AAQA;AAKA;EAMa,iBAA4B,CAAA,EAAA,MAAA;EAKxB;;;;EC5MA,0BAAA,CAAA,EAAA,MAA+B;AAkDhD;;;;;;;;;AAAiD,cMCpC,yBAAA,YAAqC,SNDD,CAAA;EAAS,QAAA,KAAA;;;;EClD9C,QAAA,kBAAA;EAuBC,QAAA,eAAA;EAcO,QAAA,kBAAA;EACI,QAAA,eAAA;EAEJ,QAAO,sBAAQ;EAAe,QAAA,UAAA;EAgBjC,QAAA,SAAA;EA0DK,QAAA,cAAA;EAA2B,QAAA,qBAAA;EAAuB,QAAA,eAAA;EAyBvD,QAAA,kBAAA;EApHgC,QAAA,2BAAA;EAAS,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;oBKiDtC;wBACI;wBAED;EJnFN;AAyBjB;;EAWwB,KAAA,CAAA,CAAA,EI6DP,OJ7DO,CAAA,IAAA,CAAA;EAED;;;EA2FgB,QAAA,QAAA;EAyBtB;;;gBIyCK,2BAA2B,uBAAuB;;;AHnMxE;EA6Ba,KAAA,CAAA,CAAA,EG6LI,OH7LJ,CAAA,IAAA,CAAA;EAQyB;;;EAQf,QAAA,QAAA;EAmBN;;;EAiHA,QAAA,kBAAA;EApJ6B;;;;;;;;;AJ1B9C;AAKY,KQAA,gCAAA,GRAQ;EAKH;AAUjB;AAkBA;;EAOwE,SAAA,CAAA,EAAA,OAAA;EAMnC;;AAYrC;;EAO0D,iBAAA,CAAA,EAAA,MAAA;CAsBvC;;;AAWnB;AAOA;AAQA;AAMA;AASA;;;;;AAQY,cQ/GC,yBAAA,YAAqC,SR+GrB,CAAA;EA6BhB,QAAA,KAAA;EAMA,QAAA,QAAA;EASA,QAAA,eAKH;EAGG,QAAA,kBAEH;EAGG,QAAA,eAAA;EAMA,QAAA,QAA4B;EAKxB,QAAA,eAAY;;oBQrKT;wBACI;EPxCP,WAAA,CAAA,IAAA,EO0CG,MAAA,CAAO,OAAA,CAAQ,IP1Ca,EAAA,OAAA,CAAA,EO0CE,gCP1CF;EAkDnC;;;EAwBU,KAAA,CAAA,CAAA,EOhBN,OPgBM,CAAA,IAAA,CAAA;EAcN;;;EAmGuD,IAAA,CAAA,OAAA,EOtElD,cPsEkD,EAAA,QAAA,CAAA,EOtEvB,oBPsEuB,CAAA,EOtEA,OPsEA,CAAA,IAAA,CAAA;EAuBvD;;;WOpEA;;;AN9IjB;EAuBa,QAAA,QAAA;EAcO;;;EAG8B,QAAA,eAAA;EAgBjC;;;EA0DuD,QAAA,cAAA;EAyBvD;;;;;;ICnJA,WAAA,EAAA,MAAA;IAyBJ,aAAA,EAAA,MAAqB;IAUd,YAAA,EAAA,MAAA;EACI,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{JSONRPCMessageSchema as e}from"@modelcontextprotocol/sdk/types.js";let t=function(e){return e.START=`start`,e.STARTED=`started`,e.STOP=`stop`,e.STOPPED=`stopped`,e.PING=`ping`,e.PONG=`pong`,e.ERROR=`error`,e.LIST_TOOLS=`list_tools`,e.CALL_TOOL=`call_tool`,e.TOOL_LIST_UPDATED=`tool_list_updated`,e.TOOL_LIST_UPDATED_ACK=`tool_list_updated_ack`,e.PROCESS_DATA=`process_data`,e.SERVER_STARTED=`server_started`,e.SERVER_STOPPED=`server_stopped`,e.ERROR_FROM_NATIVE_HOST=`error_from_native_host`,e.CONNECT_NATIVE=`connectNative`,e.PING_NATIVE=`ping_native`,e.DISCONNECT_NATIVE=`disconnect_native`,e}({});const n={NAME:`com.chromemcp.nativehost`,DEFAULT_PORT:12306},r={NATIVE_CONNECTION_FAILED:`Failed to connect to native host`,NATIVE_DISCONNECTED:`Native connection disconnected`,SERVER_STATUS_LOAD_FAILED:`Failed to load server status`,TOOL_EXECUTION_FAILED:`Tool execution failed`,SERVER_STATUS_SAVE_FAILED:`Failed to save server status`},i={TOOL_EXECUTED:`Tool executed successfully`,CONNECTION_ESTABLISHED:`Connection established`,SERVER_STARTED:`Server started successfully`,SERVER_STOPPED:`Server stopped successfully`},a={SERVER_STATUS:`serverStatus`},o={GET_SERVER_STATUS:`get_server_status`,REFRESH_SERVER_STATUS:`refresh_server_status`,SERVER_STATUS_CHANGED:`server_status_changed`},s=n.NAME;var c=class{_port;_extensionId;_portName;_messageHandler;_disconnectHandler;_isReconnecting=!1;_reconnectAttempts=0;_reconnectTimer;_currentReconnectDelay;_isStarted=!1;_isClosed=!1;_autoReconnect;_maxReconnectAttempts;_reconnectDelay;_maxReconnectDelay;_reconnectBackoffMultiplier;onclose;onerror;onmessage;constructor(e={}){this._extensionId=e.extensionId,this._portName=e.portName||`mcp`,this._autoReconnect=e.autoReconnect??!0,this._maxReconnectAttempts=e.maxReconnectAttempts??10,this._reconnectDelay=e.reconnectDelay??1e3,this._maxReconnectDelay=e.maxReconnectDelay??3e4,this._reconnectBackoffMultiplier=e.reconnectBackoffMultiplier??1.5,this._currentReconnectDelay=this._reconnectDelay}async start(){if(this._isStarted&&this._port){console.warn(`ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.`);return}this._isStarted=!0,this._isClosed=!1,await this._connect()}async _connect(){return new Promise((t,n)=>{if(!chrome?.runtime?.connect){n(Error(`Chrome runtime API not available. This transport must be used in a Chrome extension context.`));return}try{this._extensionId?this._port=chrome.runtime.connect(this._extensionId,{name:this._portName}):this._port=chrome.runtime.connect({name:this._portName}),this._messageHandler=t=>{try{if(t.type===`keep-alive`)return;let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{this._cleanup(),this._isStarted&&!this._isClosed&&this._autoReconnect?this._scheduleReconnect():this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);let r=chrome.runtime.lastError;if(r){if(this._cleanup(),this._isReconnecting&&this._isStarted&&!this._isClosed&&this._autoReconnect){n(Error(`Connection failed: ${r.message}`));return}n(Error(`Connection failed: ${r.message}`));return}this._reconnectAttempts=0,this._currentReconnectDelay=this._reconnectDelay,this._isReconnecting=!1,t()}catch(e){n(e)}})}async send(e,t){if(!this._isStarted)throw Error(`Transport not started`);if(this._isClosed)throw Error(`Transport is closed`);if(!this._port)throw Error(`Not connected`);try{this._port.postMessage(e)}catch(e){throw Error(`Failed to send message: ${e}`)}}async close(){if(this._isClosed=!0,this._isStarted=!1,this._reconnectTimer!==void 0&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=void 0),this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler)),this._port=void 0}_scheduleReconnect(){if(!(this._isReconnecting||this._isClosed||!this._isStarted)){if(this._isReconnecting=!0,this._reconnectAttempts>=this._maxReconnectAttempts){console.error(`Maximum reconnection attempts reached`),this._isReconnecting=!1,this.onerror?.(Error(`Maximum reconnection attempts reached`)),this.onclose?.();return}this._reconnectAttempts++,console.log(`Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`),this._reconnectTimer=setTimeout(()=>{this._attemptReconnect()},this._currentReconnectDelay),this._currentReconnectDelay=Math.min(this._currentReconnectDelay*this._reconnectBackoffMultiplier,this._maxReconnectDelay)}}async _attemptReconnect(){if(!(this._isClosed||!this._isStarted))try{if(chrome?.runtime?.sendMessage)try{await chrome.runtime.sendMessage({type:`ping`})}catch{}await this._connect(),console.log(`Reconnection successful`),this._isReconnecting=!1}catch(e){console.error(`Reconnection failed:`,e),this._scheduleReconnect()}}},l=class{_port;_started=!1;_messageHandler;_disconnectHandler;_keepAliveTimer;_options;_connectionInfo;onclose;onerror;onmessage;constructor(e,t={}){this._port=e,this._options={keepAlive:t.keepAlive??!0,keepAliveInterval:t.keepAliveInterval??1e3},this._connectionInfo={connectedAt:Date.now(),lastMessageAt:Date.now(),messageCount:0}}async start(){if(this._started)throw Error(`ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.`);if(!this._port)throw Error(`Port not available`);this._started=!0,this._messageHandler=t=>{try{if(this._connectionInfo.lastMessageAt=Date.now(),this._connectionInfo.messageCount++,t.type===`ping`){this._port.postMessage({type:`pong`});return}let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{console.log(`[ExtensionServerTransport] Client disconnected after ${Date.now()-this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`),this._cleanup(),this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler),this._options.keepAlive&&this._startKeepAlive(),console.log(`[ExtensionServerTransport] Started with client: ${this._port.sender?.id||`unknown`}`)}async send(e,t){if(!this._started)throw Error(`Transport not started`);if(!this._port)throw Error(`Not connected to client`);try{this._port.postMessage(e)}catch(e){throw chrome.runtime.lastError||!this._port?(this._cleanup(),this.onclose?.(),Error(`Client disconnected`)):Error(`Failed to send message: ${e}`)}}async close(){if(this._started=!1,this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0),this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler))}_startKeepAlive(){this._keepAliveTimer||=(console.log(`[ExtensionServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`),setInterval(()=>{if(!this._port){this._stopKeepAlive();return}try{this._port.postMessage({type:`keep-alive`,timestamp:Date.now()})}catch(e){console.error(`[ExtensionServerTransport] Keep-alive failed:`,e),this._stopKeepAlive()}},this._options.keepAliveInterval))}_stopKeepAlive(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0)}getConnectionInfo(){return{...this._connectionInfo,uptime:Date.now()-this._connectionInfo.connectedAt,isConnected:!!this._port&&this._started}}},u=class{_started=!1;_allowedOrigins;_channelId;_messageHandler;_clientOrigin;_serverReadyTimeout;_serverReadyRetryMs;onclose;onerror;onmessage;constructor(e){if(!e.allowedOrigins||e.allowedOrigins.length===0)throw Error(`At least one allowed origin must be specified`);this._allowedOrigins=e.allowedOrigins,this._channelId=e.channelId||`mcp-iframe`,this._serverReadyRetryMs=e.serverReadyRetryMs??250}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(!this._allowedOrigins.includes(t.origin)&&!this._allowedOrigins.includes(`*`)||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`client-to-server`)return;this._clientOrigin=t.origin;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-check-ready`){this.broadcastServerReady();return}try{let t=e.parse(n);this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this.broadcastServerReady()}broadcastServerReady(){window.parent&&window.parent!==window?(window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},`*`),this.clearServerReadyRetry()):this.scheduleServerReadyRetry()}scheduleServerReadyRetry(){this._serverReadyTimeout||=setTimeout(()=>{this._serverReadyTimeout=void 0,this._started&&this.broadcastServerReady()},this._serverReadyRetryMs)}clearServerReadyRetry(){this._serverReadyTimeout&&=(clearTimeout(this._serverReadyTimeout),void 0)}async send(e){if(!this._started)throw Error(`Transport not started`);if(!this._clientOrigin){console.warn(`[IframeChildTransport] No client connected, message not sent`);return}window.parent&&window.parent!==window?window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:e},this._clientOrigin):console.warn(`[IframeChildTransport] Not running in an iframe, message not sent`)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._started=!1,this._clientOrigin&&window.parent&&window.parent!==window&&window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-stopped`},`*`),this.clearServerReadyRetry(),this.onclose?.()}},d=class{_started=!1;_iframe;_targetOrigin;_channelId;_messageHandler;_checkReadyTimeout;_checkReadyRetryMs;serverReadyPromise;_serverReadyResolve;_serverReadyReject;onclose;onerror;onmessage;constructor(e){if(!e.iframe)throw Error(`iframe element is required`);if(!e.targetOrigin)throw Error(`targetOrigin must be explicitly set for security`);this._iframe=e.iframe,this._targetOrigin=e.targetOrigin,this._channelId=e.channelId||`mcp-iframe`,this._checkReadyRetryMs=e.checkReadyRetryMs??250;let{promise:t,resolve:n,reject:r}=Promise.withResolvers();this.serverReadyPromise=t,this._serverReadyResolve=n,this._serverReadyReject=r}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(t.origin!==this._targetOrigin||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`server-to-client`)return;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-server-ready`){this._serverReadyResolve(),this.clearCheckReadyRetry();return}if(typeof n==`string`&&n===`mcp-server-stopped`){console.log(`[IframeParentTransport] Received mcp-server-stopped event, closing transport`),this.close();return}try{let t=e.parse(n);this._serverReadyResolve(),this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this.sendCheckReady()}sendCheckReady(){let e=this._iframe.contentWindow;if(!e){console.warn(`[IframeParentTransport] iframe.contentWindow not available, will retry`),this.scheduleCheckReadyRetry();return}e.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:`mcp-check-ready`},this._targetOrigin)}scheduleCheckReadyRetry(){this._checkReadyTimeout||=setTimeout(()=>{this._checkReadyTimeout=void 0,this._started&&this.sendCheckReady()},this._checkReadyRetryMs)}clearCheckReadyRetry(){this._checkReadyTimeout&&=(clearTimeout(this._checkReadyTimeout),void 0)}async send(e){if(!this._started)throw Error(`Transport not started`);await this.serverReadyPromise;let t=this._iframe.contentWindow;if(!t)throw Error(`iframe.contentWindow not available`);t.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:e},this._targetOrigin)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._serverReadyReject(Error(`Transport closed before server ready`)),this.clearCheckReadyRetry(),this._started=!1,this.onclose?.()}},f=class{_started=!1;_targetOrigin;_channelId;_messageHandler;serverReadyPromise;_serverReadyResolve;_serverReadyReject;onclose;onerror;onmessage;constructor(e){if(!e.targetOrigin)throw Error(`targetOrigin must be explicitly set for security`);this._targetOrigin=e.targetOrigin,this._channelId=e.channelId||`mcp-default`;let{promise:t,resolve:n,reject:r}=Promise.withResolvers();this.serverReadyPromise=t,this._serverReadyResolve=()=>{n()},this._serverReadyReject=e=>{r(e)}}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(t.origin!==this._targetOrigin||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`server-to-client`)return;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-server-ready`){this._serverReadyResolve();return}if(typeof n==`string`&&n===`mcp-server-stopped`){console.log(`[TabClientTransport] Received mcp-server-stopped event, closing transport`),this.close();return}try{let t=e.parse(n);this._serverReadyResolve(),this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this.sendCheckReady()}sendCheckReady(){window.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:`mcp-check-ready`},this._targetOrigin)}async send(e){if(!this._started)throw Error(`Transport not started`);await this.serverReadyPromise,window.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:e},this._targetOrigin)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._serverReadyReject(Error(`Transport closed before server ready`)),this._started=!1,this.onclose?.()}},p=class{_started=!1;_allowedOrigins;_channelId;_messageHandler;_clientOrigin;onclose;onerror;onmessage;constructor(e){if(!e.allowedOrigins||e.allowedOrigins.length===0)throw Error(`At least one allowed origin must be specified`);this._allowedOrigins=e.allowedOrigins,this._channelId=e.channelId||`mcp-default`}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(!this._allowedOrigins.includes(t.origin)&&!this._allowedOrigins.includes(`*`)||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`client-to-server`)return;this._clientOrigin=t.origin;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-check-ready`){window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},this._clientOrigin);return}try{let t=e.parse(n);this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},`*`)}async send(e){if(!this._started)throw Error(`Transport not started`);let t=this._clientOrigin||`*`;this._clientOrigin||console.debug(`[TabServerTransport] Sending to unknown client origin (backwards compatibility mode)`),window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:e},t)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._started=!1,window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-stopped`},`*`),this.onclose?.()}},m=class{_port;_extensionId;_portName;_messageHandler;_disconnectHandler;_isReconnecting=!1;_reconnectAttempts=0;_reconnectTimer;_currentReconnectDelay;_isStarted=!1;_isClosed=!1;_autoReconnect;_maxReconnectAttempts;_reconnectDelay;_maxReconnectDelay;_reconnectBackoffMultiplier;onclose;onerror;onmessage;constructor(e={}){this._extensionId=e.extensionId,this._portName=e.portName||`mcp`,this._autoReconnect=e.autoReconnect??!0,this._maxReconnectAttempts=e.maxReconnectAttempts??10,this._reconnectDelay=e.reconnectDelay??1e3,this._maxReconnectDelay=e.maxReconnectDelay??3e4,this._reconnectBackoffMultiplier=e.reconnectBackoffMultiplier??1.5,this._currentReconnectDelay=this._reconnectDelay}async start(){if(this._isStarted&&this._port){console.warn(`UserScriptClientTransport already started! If using Client class, note that connect() calls start() automatically.`);return}this._isStarted=!0,this._isClosed=!1,await this._connect()}async _connect(){return new Promise((t,n)=>{if(!chrome?.runtime?.connect){n(Error(`Chrome runtime API not available. This transport must be used in a Chrome MV3 User Script context.`));return}try{this._extensionId?this._port=chrome.runtime.connect(this._extensionId,{name:this._portName}):this._port=chrome.runtime.connect({name:this._portName}),this._messageHandler=t=>{try{if(t.type===`keep-alive`)return;let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{this._cleanup(),this._isStarted&&!this._isClosed&&this._autoReconnect?this._scheduleReconnect():this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);let r=chrome.runtime.lastError;if(r){if(this._cleanup(),this._isReconnecting&&this._isStarted&&!this._isClosed&&this._autoReconnect){n(Error(`Connection failed: ${r.message}`));return}n(Error(`Connection failed: ${r.message}`));return}this._reconnectAttempts=0,this._currentReconnectDelay=this._reconnectDelay,this._isReconnecting=!1,t()}catch(e){n(e)}})}async send(e,t){if(!this._isStarted)throw Error(`Transport not started`);if(this._isClosed)throw Error(`Transport is closed`);if(!this._port)throw Error(`Not connected`);try{this._port.postMessage(e)}catch(e){throw Error(`Failed to send message: ${e}`)}}async close(){if(this._isClosed=!0,this._isStarted=!1,this._reconnectTimer!==void 0&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=void 0),this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler)),this._port=void 0}_scheduleReconnect(){if(!(this._isReconnecting||this._isClosed||!this._isStarted)){if(this._isReconnecting=!0,this._reconnectAttempts>=this._maxReconnectAttempts){console.error(`Maximum reconnection attempts reached`),this._isReconnecting=!1,this.onerror?.(Error(`Maximum reconnection attempts reached`)),this.onclose?.();return}this._reconnectAttempts++,console.log(`Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`),this._reconnectTimer=setTimeout(()=>{this._attemptReconnect()},this._currentReconnectDelay),this._currentReconnectDelay=Math.min(this._currentReconnectDelay*this._reconnectBackoffMultiplier,this._maxReconnectDelay)}}async _attemptReconnect(){if(!(this._isClosed||!this._isStarted))try{if(chrome?.runtime?.sendMessage)try{await chrome.runtime.sendMessage({type:`ping`})}catch{}await this._connect(),console.log(`Reconnection successful`),this._isReconnecting=!1}catch(e){console.error(`Reconnection failed:`,e),this._scheduleReconnect()}}},h=class{_port;_started=!1;_messageHandler;_disconnectHandler;_keepAliveTimer;_options;_connectionInfo;onclose;onerror;onmessage;constructor(e,t={}){this._port=e,this._options={keepAlive:t.keepAlive??!0,keepAliveInterval:t.keepAliveInterval??1e3},this._connectionInfo={connectedAt:Date.now(),lastMessageAt:Date.now(),messageCount:0}}async start(){if(this._started)throw Error(`UserScriptServerTransport already started! If using Server class, note that connect() calls start() automatically.`);if(!this._port)throw Error(`Port not available`);this._started=!0,this._messageHandler=t=>{try{if(this._connectionInfo.lastMessageAt=Date.now(),this._connectionInfo.messageCount++,t.type===`ping`){this._port.postMessage({type:`pong`});return}let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{console.log(`[UserScriptServerTransport] Client disconnected after ${Date.now()-this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`),this._cleanup(),this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler),this._options.keepAlive&&this._startKeepAlive(),console.log(`[UserScriptServerTransport] Started with client: ${this._port.sender?.id||`unknown`}`)}async send(e,t){if(!this._started)throw Error(`Transport not started`);if(!this._port)throw Error(`Not connected to client`);try{this._port.postMessage(e)}catch(e){throw chrome.runtime.lastError||!this._port?(this._cleanup(),this.onclose?.(),Error(`Client disconnected`)):Error(`Failed to send message: ${e}`)}}async close(){if(this._started=!1,this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0),this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler))}_startKeepAlive(){this._keepAliveTimer||=(console.log(`[UserScriptServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`),setInterval(()=>{if(!this._port){this._stopKeepAlive();return}try{this._port.postMessage({type:`keep-alive`,timestamp:Date.now()})}catch(e){console.error(`[UserScriptServerTransport] Keep-alive failed:`,e),this._stopKeepAlive()}},this._options.keepAliveInterval))}_stopKeepAlive(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0)}getConnectionInfo(){return{...this._connectionInfo,uptime:Date.now()-this._connectionInfo.connectedAt,isConnected:!!this._port&&this._started}}};export{o as BACKGROUND_MESSAGE_TYPES,r as ERROR_MESSAGES,c as ExtensionClientTransport,l as ExtensionServerTransport,s as HOST_NAME,u as IframeChildTransport,d as IframeParentTransport,n as NATIVE_HOST,t as NativeMessageType,a as STORAGE_KEYS,i as SUCCESS_MESSAGES,f as TabClientTransport,p as TabServerTransport,m as UserScriptClientTransport,h as UserScriptServerTransport};
|
|
1
|
+
import{JSONRPCMessageSchema as e}from"@mcp-b/webmcp-ts-sdk";let t=function(e){return e.START=`start`,e.STARTED=`started`,e.STOP=`stop`,e.STOPPED=`stopped`,e.PING=`ping`,e.PONG=`pong`,e.ERROR=`error`,e.LIST_TOOLS=`list_tools`,e.CALL_TOOL=`call_tool`,e.TOOL_LIST_UPDATED=`tool_list_updated`,e.TOOL_LIST_UPDATED_ACK=`tool_list_updated_ack`,e.PROCESS_DATA=`process_data`,e.SERVER_STARTED=`server_started`,e.SERVER_STOPPED=`server_stopped`,e.ERROR_FROM_NATIVE_HOST=`error_from_native_host`,e.CONNECT_NATIVE=`connectNative`,e.PING_NATIVE=`ping_native`,e.DISCONNECT_NATIVE=`disconnect_native`,e}({});const n={NAME:`com.chromemcp.nativehost`,DEFAULT_PORT:12306},r={NATIVE_CONNECTION_FAILED:`Failed to connect to native host`,NATIVE_DISCONNECTED:`Native connection disconnected`,SERVER_STATUS_LOAD_FAILED:`Failed to load server status`,TOOL_EXECUTION_FAILED:`Tool execution failed`,SERVER_STATUS_SAVE_FAILED:`Failed to save server status`},i={TOOL_EXECUTED:`Tool executed successfully`,CONNECTION_ESTABLISHED:`Connection established`,SERVER_STARTED:`Server started successfully`,SERVER_STOPPED:`Server stopped successfully`},a={SERVER_STATUS:`serverStatus`},o={GET_SERVER_STATUS:`get_server_status`,REFRESH_SERVER_STATUS:`refresh_server_status`,SERVER_STATUS_CHANGED:`server_status_changed`},s=n.NAME;var c=class{_port;_extensionId;_portName;_messageHandler;_disconnectHandler;_isReconnecting=!1;_reconnectAttempts=0;_reconnectTimer;_currentReconnectDelay;_isStarted=!1;_isClosed=!1;_autoReconnect;_maxReconnectAttempts;_reconnectDelay;_maxReconnectDelay;_reconnectBackoffMultiplier;onclose;onerror;onmessage;constructor(e={}){this._extensionId=e.extensionId,this._portName=e.portName||`mcp`,this._autoReconnect=e.autoReconnect??!0,this._maxReconnectAttempts=e.maxReconnectAttempts??10,this._reconnectDelay=e.reconnectDelay??1e3,this._maxReconnectDelay=e.maxReconnectDelay??3e4,this._reconnectBackoffMultiplier=e.reconnectBackoffMultiplier??1.5,this._currentReconnectDelay=this._reconnectDelay}async start(){if(this._isStarted&&this._port){console.warn(`ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.`);return}this._isStarted=!0,this._isClosed=!1,await this._connect()}async _connect(){return new Promise((t,n)=>{if(!chrome?.runtime?.connect){n(Error(`Chrome runtime API not available. This transport must be used in a Chrome extension context.`));return}try{this._extensionId?this._port=chrome.runtime.connect(this._extensionId,{name:this._portName}):this._port=chrome.runtime.connect({name:this._portName}),this._messageHandler=t=>{try{if(t.type===`keep-alive`)return;let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{this._cleanup(),this._isStarted&&!this._isClosed&&this._autoReconnect?this._scheduleReconnect():this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);let r=chrome.runtime.lastError;if(r){if(this._cleanup(),this._isReconnecting&&this._isStarted&&!this._isClosed&&this._autoReconnect){n(Error(`Connection failed: ${r.message}`));return}n(Error(`Connection failed: ${r.message}`));return}this._reconnectAttempts=0,this._currentReconnectDelay=this._reconnectDelay,this._isReconnecting=!1,t()}catch(e){n(e)}})}async send(e,t){if(!this._isStarted)throw Error(`Transport not started`);if(this._isClosed)throw Error(`Transport is closed`);if(!this._port)throw Error(`Not connected`);try{this._port.postMessage(e)}catch(e){throw Error(`Failed to send message: ${e}`)}}async close(){if(this._isClosed=!0,this._isStarted=!1,this._reconnectTimer!==void 0&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=void 0),this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler)),this._port=void 0}_scheduleReconnect(){if(!(this._isReconnecting||this._isClosed||!this._isStarted)){if(this._isReconnecting=!0,this._reconnectAttempts>=this._maxReconnectAttempts){console.error(`Maximum reconnection attempts reached`),this._isReconnecting=!1,this.onerror?.(Error(`Maximum reconnection attempts reached`)),this.onclose?.();return}this._reconnectAttempts++,console.log(`Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`),this._reconnectTimer=setTimeout(()=>{this._attemptReconnect()},this._currentReconnectDelay),this._currentReconnectDelay=Math.min(this._currentReconnectDelay*this._reconnectBackoffMultiplier,this._maxReconnectDelay)}}async _attemptReconnect(){if(!(this._isClosed||!this._isStarted))try{if(chrome?.runtime?.sendMessage)try{await chrome.runtime.sendMessage({type:`ping`})}catch{}await this._connect(),console.log(`Reconnection successful`),this._isReconnecting=!1}catch(e){console.error(`Reconnection failed:`,e),this._scheduleReconnect()}}},l=class{_port;_started=!1;_messageHandler;_disconnectHandler;_keepAliveTimer;_options;_connectionInfo;onclose;onerror;onmessage;constructor(e,t={}){this._port=e,this._options={keepAlive:t.keepAlive??!0,keepAliveInterval:t.keepAliveInterval??1e3},this._connectionInfo={connectedAt:Date.now(),lastMessageAt:Date.now(),messageCount:0}}async start(){if(this._started)throw Error(`ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.`);if(!this._port)throw Error(`Port not available`);this._started=!0,this._messageHandler=t=>{try{if(this._connectionInfo.lastMessageAt=Date.now(),this._connectionInfo.messageCount++,t.type===`ping`){this._port.postMessage({type:`pong`});return}let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{console.log(`[ExtensionServerTransport] Client disconnected after ${Date.now()-this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`),this._cleanup(),this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler),this._options.keepAlive&&this._startKeepAlive(),console.log(`[ExtensionServerTransport] Started with client: ${this._port.sender?.id||`unknown`}`)}async send(e,t){if(!this._started)throw Error(`Transport not started`);if(!this._port)throw Error(`Not connected to client`);try{this._port.postMessage(e)}catch(e){throw chrome.runtime.lastError||!this._port?(this._cleanup(),this.onclose?.(),Error(`Client disconnected`)):Error(`Failed to send message: ${e}`)}}async close(){if(this._started=!1,this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0),this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler))}_startKeepAlive(){this._keepAliveTimer||=(console.log(`[ExtensionServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`),setInterval(()=>{if(!this._port){this._stopKeepAlive();return}try{this._port.postMessage({type:`keep-alive`,timestamp:Date.now()})}catch(e){console.error(`[ExtensionServerTransport] Keep-alive failed:`,e),this._stopKeepAlive()}},this._options.keepAliveInterval))}_stopKeepAlive(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0)}getConnectionInfo(){return{...this._connectionInfo,uptime:Date.now()-this._connectionInfo.connectedAt,isConnected:!!this._port&&this._started}}},u=class{_started=!1;_allowedOrigins;_channelId;_messageHandler;_clientOrigin;_serverReadyTimeout;_serverReadyRetryMs;onclose;onerror;onmessage;constructor(e){if(!e.allowedOrigins||e.allowedOrigins.length===0)throw Error(`At least one allowed origin must be specified`);this._allowedOrigins=e.allowedOrigins,this._channelId=e.channelId||`mcp-iframe`,this._serverReadyRetryMs=e.serverReadyRetryMs??250}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(!this._allowedOrigins.includes(t.origin)&&!this._allowedOrigins.includes(`*`)||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`client-to-server`)return;this._clientOrigin=t.origin;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-check-ready`){this.broadcastServerReady();return}try{let t=e.parse(n);this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this.broadcastServerReady()}broadcastServerReady(){window.parent&&window.parent!==window?(window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},`*`),this.clearServerReadyRetry()):this.scheduleServerReadyRetry()}scheduleServerReadyRetry(){this._serverReadyTimeout||=setTimeout(()=>{this._serverReadyTimeout=void 0,this._started&&this.broadcastServerReady()},this._serverReadyRetryMs)}clearServerReadyRetry(){this._serverReadyTimeout&&=(clearTimeout(this._serverReadyTimeout),void 0)}async send(e){if(!this._started)throw Error(`Transport not started`);if(!this._clientOrigin){console.warn(`[IframeChildTransport] No client connected, message not sent`);return}window.parent&&window.parent!==window?window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:e},this._clientOrigin):console.warn(`[IframeChildTransport] Not running in an iframe, message not sent`)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._started=!1,this._clientOrigin&&window.parent&&window.parent!==window&&window.parent.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-stopped`},`*`),this.clearServerReadyRetry(),this.onclose?.()}},d=class{_started=!1;_iframe;_targetOrigin;_channelId;_messageHandler;_checkReadyTimeout;_checkReadyRetryMs;serverReadyPromise;_serverReadyResolve;_serverReadyReject;onclose;onerror;onmessage;constructor(e){if(!e.iframe)throw Error(`iframe element is required`);if(!e.targetOrigin)throw Error(`targetOrigin must be explicitly set for security`);this._iframe=e.iframe,this._targetOrigin=e.targetOrigin,this._channelId=e.channelId||`mcp-iframe`,this._checkReadyRetryMs=e.checkReadyRetryMs??250;let{promise:t,resolve:n,reject:r}=Promise.withResolvers();this.serverReadyPromise=t,this._serverReadyResolve=n,this._serverReadyReject=r}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(t.origin!==this._targetOrigin||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`server-to-client`)return;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-server-ready`){this._serverReadyResolve(),this.clearCheckReadyRetry();return}if(typeof n==`string`&&n===`mcp-server-stopped`){console.log(`[IframeParentTransport] Received mcp-server-stopped event, closing transport`),this.close();return}try{let t=e.parse(n);this._serverReadyResolve(),this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this.sendCheckReady()}sendCheckReady(){let e=this._iframe.contentWindow;if(!e){console.warn(`[IframeParentTransport] iframe.contentWindow not available, will retry`),this.scheduleCheckReadyRetry();return}e.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:`mcp-check-ready`},this._targetOrigin)}scheduleCheckReadyRetry(){this._checkReadyTimeout||=setTimeout(()=>{this._checkReadyTimeout=void 0,this._started&&this.sendCheckReady()},this._checkReadyRetryMs)}clearCheckReadyRetry(){this._checkReadyTimeout&&=(clearTimeout(this._checkReadyTimeout),void 0)}async send(e){if(!this._started)throw Error(`Transport not started`);await this.serverReadyPromise;let t=this._iframe.contentWindow;if(!t)throw Error(`iframe.contentWindow not available`);t.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:e},this._targetOrigin)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._serverReadyReject(Error(`Transport closed before server ready`)),this.clearCheckReadyRetry(),this._started=!1,this.onclose?.()}},f=class{_started=!1;_targetOrigin;_channelId;_requestTimeout;_messageHandler;serverReadyPromise;_serverReadyResolve;_serverReadyReject;_activeRequests=new Map;onclose;onerror;onmessage;constructor(e){if(!e.targetOrigin)throw Error(`targetOrigin must be explicitly set for security`);this._targetOrigin=e.targetOrigin,this._channelId=e.channelId??`mcp-default`,this._requestTimeout=e.requestTimeout??1e4;let{promise:t,resolve:n,reject:r}=Promise.withResolvers();this.serverReadyPromise=t,this._serverReadyResolve=n,this._serverReadyReject=r}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(t.origin!==this._targetOrigin||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`server-to-client`)return;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-server-ready`){this._serverReadyResolve();return}if(typeof n==`string`&&n===`mcp-server-stopped`){console.log(`[TabClientTransport] Received mcp-server-stopped event, closing transport`),this.close();return}try{let t=e.parse(n);this._serverReadyResolve(),this._clearRequestTimeout(t),this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this._sendCheckReady()}async send(e){if(!this._started)throw Error(`Transport not started`);await this.serverReadyPromise,`method`in e&&e.id!==void 0&&this._startRequestTimeout(e),window.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:e},this._targetOrigin)}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler);for(let[e,t]of this._activeRequests)clearTimeout(t.timeoutId);this._activeRequests.clear(),this._serverReadyReject(Error(`Transport closed before server ready`)),this._started=!1,this.onclose?.()}_sendCheckReady(){window.postMessage({channel:this._channelId,type:`mcp`,direction:`client-to-server`,payload:`mcp-check-ready`},this._targetOrigin)}_startRequestTimeout(e){if(!(`id`in e)||e.id===void 0)return;let t=setTimeout(()=>{this._handleRequestTimeout(e.id)},this._requestTimeout);this._activeRequests.set(e.id,{timeoutId:t,request:e})}_clearRequestTimeout(e){if((`result`in e||`error`in e)&&e.id!==void 0){let t=this._activeRequests.get(e.id);t&&(clearTimeout(t.timeoutId),this._activeRequests.delete(e.id))}}_handleRequestTimeout(e){let t=this._activeRequests.get(e);if(!t)return;this._activeRequests.delete(e);let n={jsonrpc:`2.0`,id:e,error:{code:-32e3,message:`Request timeout - server may have navigated or become unresponsive`,data:{timeoutMs:this._requestTimeout,originalMethod:`method`in t.request?t.request.method:void 0}}};this.onmessage?.(n)}},p=class{_started=!1;_allowedOrigins;_channelId;_messageHandler;_clientOrigin;_beforeUnloadHandler;_cleanupInterval;_pendingRequests=new Map;REQUEST_TIMEOUT_MS=3e5;onclose;onerror;onmessage;constructor(e){if(!e.allowedOrigins||e.allowedOrigins.length===0)throw Error(`At least one allowed origin must be specified`);this._allowedOrigins=e.allowedOrigins,this._channelId=e.channelId||`mcp-default`}async start(){if(this._started)throw Error(`Transport already started`);this._messageHandler=t=>{if(!this._allowedOrigins.includes(t.origin)&&!this._allowedOrigins.includes(`*`)||t.data?.channel!==this._channelId||t.data?.type!==`mcp`||t.data?.direction!==`client-to-server`)return;this._clientOrigin=t.origin;let n=t.data.payload;if(typeof n==`string`&&n===`mcp-check-ready`){window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},this._clientOrigin);return}try{let t=e.parse(n);`method`in t&&t.id!==void 0&&this._pendingRequests.set(t.id,{request:t,receivedAt:Date.now(),interruptedSent:!1}),this.onmessage?.(t)}catch(e){this.onerror?.(Error(`Invalid message: ${e instanceof Error?e.message:String(e)}`))}},window.addEventListener(`message`,this._messageHandler),this._started=!0,this._beforeUnloadHandler=()=>{this._handleBeforeUnload()},window.addEventListener(`beforeunload`,this._beforeUnloadHandler),this._cleanupInterval=setInterval(()=>{this._cleanupStaleRequests()},6e4),window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-ready`},`*`)}async send(e){if(!this._started)throw Error(`Transport not started`);if((`result`in e||`error`in e)&&e.id!==void 0){if(this._pendingRequests.get(e.id)?.interruptedSent){console.debug(`[TabServerTransport] Suppressing response for ${e.id} - interrupted response already sent`),this._pendingRequests.delete(e.id);return}this._pendingRequests.delete(e.id)}let t=this._clientOrigin||`*`;this._clientOrigin||console.debug(`[TabServerTransport] Sending to unknown client origin (backwards compatibility mode)`),window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:e},t)}_handleBeforeUnload(){let e=Array.from(this._pendingRequests.entries()).reverse();for(let[t,n]of e){n.interruptedSent=!0;let e={jsonrpc:`2.0`,id:t,result:{content:[{type:`text`,text:`Tool execution interrupted by page navigation`}],metadata:{navigationInterrupted:!0,originalMethod:`method`in n.request?n.request.method:`unknown`,timestamp:Date.now()}}};try{window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:e},this._clientOrigin||`*`)}catch(e){console.error(`[TabServerTransport] Failed to send beforeunload response:`,e)}}this._pendingRequests.clear()}_cleanupStaleRequests(){let e=Date.now(),t=[];for(let[n,r]of this._pendingRequests)e-r.receivedAt>this.REQUEST_TIMEOUT_MS&&t.push(n);if(t.length>0){console.warn(`[TabServerTransport] Cleaning up ${t.length} stale requests`);for(let e of t)this._pendingRequests.delete(e)}}async close(){this._messageHandler&&window.removeEventListener(`message`,this._messageHandler),this._beforeUnloadHandler&&window.removeEventListener(`beforeunload`,this._beforeUnloadHandler),this._cleanupInterval!==void 0&&clearInterval(this._cleanupInterval),this._pendingRequests.clear(),this._started=!1,window.postMessage({channel:this._channelId,type:`mcp`,direction:`server-to-client`,payload:`mcp-server-stopped`},`*`),this.onclose?.()}},m=class{_port;_extensionId;_portName;_messageHandler;_disconnectHandler;_isReconnecting=!1;_reconnectAttempts=0;_reconnectTimer;_currentReconnectDelay;_isStarted=!1;_isClosed=!1;_autoReconnect;_maxReconnectAttempts;_reconnectDelay;_maxReconnectDelay;_reconnectBackoffMultiplier;onclose;onerror;onmessage;constructor(e={}){this._extensionId=e.extensionId,this._portName=e.portName||`mcp`,this._autoReconnect=e.autoReconnect??!0,this._maxReconnectAttempts=e.maxReconnectAttempts??10,this._reconnectDelay=e.reconnectDelay??1e3,this._maxReconnectDelay=e.maxReconnectDelay??3e4,this._reconnectBackoffMultiplier=e.reconnectBackoffMultiplier??1.5,this._currentReconnectDelay=this._reconnectDelay}async start(){if(this._isStarted&&this._port){console.warn(`UserScriptClientTransport already started! If using Client class, note that connect() calls start() automatically.`);return}this._isStarted=!0,this._isClosed=!1,await this._connect()}async _connect(){return new Promise((t,n)=>{if(!chrome?.runtime?.connect){n(Error(`Chrome runtime API not available. This transport must be used in a Chrome MV3 User Script context.`));return}try{this._extensionId?this._port=chrome.runtime.connect(this._extensionId,{name:this._portName}):this._port=chrome.runtime.connect({name:this._portName}),this._messageHandler=t=>{try{if(t.type===`keep-alive`)return;let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{this._cleanup(),this._isStarted&&!this._isClosed&&this._autoReconnect?this._scheduleReconnect():this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler);let r=chrome.runtime.lastError;if(r){if(this._cleanup(),this._isReconnecting&&this._isStarted&&!this._isClosed&&this._autoReconnect){n(Error(`Connection failed: ${r.message}`));return}n(Error(`Connection failed: ${r.message}`));return}this._reconnectAttempts=0,this._currentReconnectDelay=this._reconnectDelay,this._isReconnecting=!1,t()}catch(e){n(e)}})}async send(e,t){if(!this._isStarted)throw Error(`Transport not started`);if(this._isClosed)throw Error(`Transport is closed`);if(!this._port)throw Error(`Not connected`);try{this._port.postMessage(e)}catch(e){throw Error(`Failed to send message: ${e}`)}}async close(){if(this._isClosed=!0,this._isStarted=!1,this._reconnectTimer!==void 0&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=void 0),this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler)),this._port=void 0}_scheduleReconnect(){if(!(this._isReconnecting||this._isClosed||!this._isStarted)){if(this._isReconnecting=!0,this._reconnectAttempts>=this._maxReconnectAttempts){console.error(`Maximum reconnection attempts reached`),this._isReconnecting=!1,this.onerror?.(Error(`Maximum reconnection attempts reached`)),this.onclose?.();return}this._reconnectAttempts++,console.log(`Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`),this._reconnectTimer=setTimeout(()=>{this._attemptReconnect()},this._currentReconnectDelay),this._currentReconnectDelay=Math.min(this._currentReconnectDelay*this._reconnectBackoffMultiplier,this._maxReconnectDelay)}}async _attemptReconnect(){if(!(this._isClosed||!this._isStarted))try{if(chrome?.runtime?.sendMessage)try{await chrome.runtime.sendMessage({type:`ping`})}catch{}await this._connect(),console.log(`Reconnection successful`),this._isReconnecting=!1}catch(e){console.error(`Reconnection failed:`,e),this._scheduleReconnect()}}},h=class{_port;_started=!1;_messageHandler;_disconnectHandler;_keepAliveTimer;_options;_connectionInfo;onclose;onerror;onmessage;constructor(e,t={}){this._port=e,this._options={keepAlive:t.keepAlive??!0,keepAliveInterval:t.keepAliveInterval??1e3},this._connectionInfo={connectedAt:Date.now(),lastMessageAt:Date.now(),messageCount:0}}async start(){if(this._started)throw Error(`UserScriptServerTransport already started! If using Server class, note that connect() calls start() automatically.`);if(!this._port)throw Error(`Port not available`);this._started=!0,this._messageHandler=t=>{try{if(this._connectionInfo.lastMessageAt=Date.now(),this._connectionInfo.messageCount++,t.type===`ping`){this._port.postMessage({type:`pong`});return}let n=e.parse(t);this.onmessage?.(n)}catch(e){this.onerror?.(Error(`Failed to parse message: ${e}`))}},this._disconnectHandler=()=>{console.log(`[UserScriptServerTransport] Client disconnected after ${Date.now()-this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`),this._cleanup(),this.onclose?.()},this._port.onMessage.addListener(this._messageHandler),this._port.onDisconnect.addListener(this._disconnectHandler),this._options.keepAlive&&this._startKeepAlive(),console.log(`[UserScriptServerTransport] Started with client: ${this._port.sender?.id||`unknown`}`)}async send(e,t){if(!this._started)throw Error(`Transport not started`);if(!this._port)throw Error(`Not connected to client`);try{this._port.postMessage(e)}catch(e){throw chrome.runtime.lastError||!this._port?(this._cleanup(),this.onclose?.(),Error(`Client disconnected`)):Error(`Failed to send message: ${e}`)}}async close(){if(this._started=!1,this._port)try{this._port.disconnect()}catch{}this._cleanup(),this.onclose?.()}_cleanup(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0),this._port&&(this._messageHandler&&this._port.onMessage.removeListener(this._messageHandler),this._disconnectHandler&&this._port.onDisconnect.removeListener(this._disconnectHandler))}_startKeepAlive(){this._keepAliveTimer||=(console.log(`[UserScriptServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`),setInterval(()=>{if(!this._port){this._stopKeepAlive();return}try{this._port.postMessage({type:`keep-alive`,timestamp:Date.now()})}catch(e){console.error(`[UserScriptServerTransport] Keep-alive failed:`,e),this._stopKeepAlive()}},this._options.keepAliveInterval))}_stopKeepAlive(){this._keepAliveTimer!==void 0&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=void 0)}getConnectionInfo(){return{...this._connectionInfo,uptime:Date.now()-this._connectionInfo.connectedAt,isConnected:!!this._port&&this._started}}};export{o as BACKGROUND_MESSAGE_TYPES,r as ERROR_MESSAGES,c as ExtensionClientTransport,l as ExtensionServerTransport,s as HOST_NAME,u as IframeChildTransport,d as IframeParentTransport,n as NATIVE_HOST,t as NativeMessageType,a as STORAGE_KEYS,i as SUCCESS_MESSAGES,f as TabClientTransport,p as TabServerTransport,m as UserScriptClientTransport,h as UserScriptServerTransport};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["error","error"],"sources":["../src/browser-types.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/UserScriptClientTransport.ts","../src/UserScriptServerTransport.ts"],"sourcesContent":["import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Unique identifier for an event in the event store\n */\nexport type EventId = string;\n\n/**\n * Unique identifier for a stream of events\n */\nexport type StreamId = string;\n\n/**\n * Options for connecting to an MCP server\n */\nexport interface MCPConnectOptions {\n /**\n * The event ID to resume from if reconnecting\n */\n resumeFrom?: EventId;\n}\n\n/**\n * Information about the MCP server\n */\nexport interface MCPServerInfo {\n /**\n * Unique identifier for this server instance\n */\n instanceId: string;\n /**\n * Whether the server maintains session state\n */\n stateful: boolean;\n /**\n * Whether the server has event storage enabled\n */\n hasEventStore: boolean;\n}\n\n/**\n * Event storage interface for accessing stored events\n */\nexport interface MCPEventStore {\n /**\n * Get stored events, optionally filtered by client and/or after a specific event\n * @param clientId - Optional client ID to filter events\n * @param afterEventId - Optional event ID to get events after\n * @param limit - Maximum number of events to return (default: 100)\n */\n getEvents(clientId?: string, afterEventId?: EventId, limit?: number): StoredEvent[];\n\n /**\n * Get the ID of the last event, optionally for a specific client\n * @param clientId - Optional client ID to filter by\n */\n getLastEventId(clientId?: string): EventId | null;\n\n /**\n * Clear stored events, optionally for a specific client\n * @param clientId - Optional client ID to clear events for\n */\n clearEvents(clientId?: string): void;\n}\n\n/**\n * The MCP interface exposed on window for browser environments\n */\nexport interface MCPBrowserInterface {\n /**\n * Connect a client to the MCP server\n * @param clientId - Unique identifier for the client\n * @param options - Optional connection options\n * @returns MessagePort for communication or null if connection fails\n */\n connect(clientId: string, options?: MCPConnectOptions): MessagePort | null;\n\n /**\n * Disconnect a client from the MCP server\n * @param clientId - The client ID to disconnect\n */\n disconnect(clientId: string): void;\n\n /**\n * Terminate a client's session and clean up all associated resources\n * @param clientId - The client ID to terminate\n */\n terminateSession?(clientId: string): void;\n\n /**\n * Check if the MCP server is available and running\n */\n isServerAvailable(): boolean;\n\n /**\n * Get information about the MCP server\n */\n getServerInfo(): MCPServerInfo;\n\n /**\n * Event storage access (only available in stateful mode with event store)\n */\n events?: MCPEventStore;\n}\n\n/**\n * Extended Window interface with MCP support\n */\nexport interface MCPWindow extends Window {\n mcp?: MCPBrowserInterface;\n}\n\n/**\n * Message types for internal MCP communication\n */\nexport interface MCPServerInfoMessage {\n type: 'mcp-server-info';\n serverInstanceId: string;\n serverSessionId?: string;\n hasEventStore: boolean;\n streamId: StreamId;\n}\n\nexport interface MCPEventMessage {\n type: 'mcp-event';\n eventId: EventId;\n message: JSONRPCMessage;\n}\n\nexport interface MCPReplayEventMessage {\n type: 'mcp-replay-event';\n eventId: EventId;\n message: JSONRPCMessage;\n}\n\n/**\n * Stored event with metadata for event sourcing\n */\nexport interface StoredEvent {\n eventId: EventId;\n streamId: StreamId;\n message: JSONRPCMessage;\n timestamp: number;\n clientId: string;\n}\n\nexport enum NativeMessageType {\n START = 'start',\n STARTED = 'started',\n STOP = 'stop',\n STOPPED = 'stopped',\n PING = 'ping',\n PONG = 'pong',\n ERROR = 'error',\n LIST_TOOLS = 'list_tools',\n CALL_TOOL = 'call_tool',\n TOOL_LIST_UPDATED = 'tool_list_updated',\n TOOL_LIST_UPDATED_ACK = 'tool_list_updated_ack',\n PROCESS_DATA = 'process_data',\n\n // Additional message types used in Chrome extension\n SERVER_STARTED = 'server_started',\n SERVER_STOPPED = 'server_stopped',\n ERROR_FROM_NATIVE_HOST = 'error_from_native_host',\n CONNECT_NATIVE = 'connectNative',\n PING_NATIVE = 'ping_native',\n DISCONNECT_NATIVE = 'disconnect_native',\n}\n\n/**\n * Chrome Extension Constants\n * Centralized configuration values and magic constants\n */\n\n// Native Host Configuration\nexport const NATIVE_HOST = {\n NAME: 'com.chromemcp.nativehost',\n DEFAULT_PORT: 12306,\n} as const;\n\n// Error Messages\nexport const ERROR_MESSAGES = {\n NATIVE_CONNECTION_FAILED: 'Failed to connect to native host',\n NATIVE_DISCONNECTED: 'Native connection disconnected',\n SERVER_STATUS_LOAD_FAILED: 'Failed to load server status',\n TOOL_EXECUTION_FAILED: 'Tool execution failed',\n SERVER_STATUS_SAVE_FAILED: 'Failed to save server status',\n} as const;\n\n// Success Messages\nexport const SUCCESS_MESSAGES = {\n TOOL_EXECUTED: 'Tool executed successfully',\n CONNECTION_ESTABLISHED: 'Connection established',\n SERVER_STARTED: 'Server started successfully',\n SERVER_STOPPED: 'Server stopped successfully',\n} as const;\n\n// Storage Keys\nexport const STORAGE_KEYS = {\n SERVER_STATUS: 'serverStatus',\n} as const;\n\n// Background script message types\nexport const BACKGROUND_MESSAGE_TYPES = {\n GET_SERVER_STATUS: 'get_server_status',\n REFRESH_SERVER_STATUS: 'refresh_server_status',\n SERVER_STATUS_CHANGED: 'server_status_changed',\n} as const;\n\nexport const HOST_NAME = NATIVE_HOST.NAME;\n\n/**\n * Server status management interface\n */\nexport interface ServerStatus {\n isRunning: boolean;\n port?: number;\n lastUpdated: number;\n}\n","import type {\n Transport,\n TransportSendOptions,\n} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for ExtensionClientTransport\n */\nexport interface ExtensionClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n\n /**\n * Enable automatic reconnection on disconnect\n * Default: true\n */\n autoReconnect?: boolean;\n\n /**\n * Maximum number of reconnection attempts\n * Default: 10\n */\n maxReconnectAttempts?: number;\n\n /**\n * Initial reconnection delay in milliseconds\n * Default: 1000\n */\n reconnectDelay?: number;\n\n /**\n * Maximum reconnection delay in milliseconds\n * Default: 30000\n */\n maxReconnectDelay?: number;\n\n /**\n * Reconnection backoff multiplier\n * Default: 1.5\n */\n reconnectBackoffMultiplier?: number;\n}\n\n/**\n * Client transport for Chrome extensions using Port-based messaging.\n * This transport can be used in content scripts, popup scripts, or sidepanel scripts\n * to connect to a server running in the background service worker.\n *\n * Features automatic reconnection to handle background service worker lifecycle.\n */\nexport class ExtensionClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private _extensionId: string | undefined;\n private _portName: string;\n private _messageHandler: ((message: any) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _isReconnecting = false;\n private _reconnectAttempts = 0;\n private _reconnectTimer: number | undefined;\n private _currentReconnectDelay: number;\n private _isStarted = false;\n private _isClosed = false;\n\n // Configuration\n private _autoReconnect: boolean;\n private _maxReconnectAttempts: number;\n private _reconnectDelay: number;\n private _maxReconnectDelay: number;\n private _reconnectBackoffMultiplier: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n this._autoReconnect = options.autoReconnect ?? true;\n this._maxReconnectAttempts = options.maxReconnectAttempts ?? 10;\n this._reconnectDelay = options.reconnectDelay ?? 1000;\n this._maxReconnectDelay = options.maxReconnectDelay ?? 30000;\n this._reconnectBackoffMultiplier = options.reconnectBackoffMultiplier ?? 1.5;\n this._currentReconnectDelay = this._reconnectDelay;\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._isStarted && this._port) {\n console.warn(\n 'ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n return;\n }\n\n this._isStarted = true;\n this._isClosed = false;\n\n await this._connect();\n }\n\n /**\n * Connects to the extension port\n */\n private async _connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, {\n name: this._portName,\n });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Handle keep-alive messages\n if (message.type === 'keep-alive') {\n // Just acknowledge receipt, no need to propagate\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n\n // Only attempt reconnection if we're started and not manually closed\n if (this._isStarted && !this._isClosed && this._autoReconnect) {\n this._scheduleReconnect();\n } else {\n this.onclose?.();\n }\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n\n // If we're reconnecting and hit an error, schedule another attempt\n if (this._isReconnecting && this._isStarted && !this._isClosed && this._autoReconnect) {\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n // Connection successful\n this._reconnectAttempts = 0;\n this._currentReconnectDelay = this._reconnectDelay;\n this._isReconnecting = false;\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._isStarted) {\n throw new Error('Transport not started');\n }\n\n if (this._isClosed) {\n throw new Error('Transport is closed');\n }\n\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._isClosed = true;\n this._isStarted = false;\n\n // Cancel any pending reconnection\n if (this._reconnectTimer !== undefined) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = undefined;\n }\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n\n /**\n * Schedules a reconnection attempt\n */\n private _scheduleReconnect(): void {\n if (this._isReconnecting || this._isClosed || !this._isStarted) {\n return;\n }\n\n this._isReconnecting = true;\n\n // Check if we've exceeded max attempts\n if (this._reconnectAttempts >= this._maxReconnectAttempts) {\n console.error('Maximum reconnection attempts reached');\n this._isReconnecting = false;\n this.onerror?.(new Error('Maximum reconnection attempts reached'));\n this.onclose?.();\n return;\n }\n\n this._reconnectAttempts++;\n\n console.log(\n `Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`\n );\n\n this._reconnectTimer = setTimeout(() => {\n this._attemptReconnect();\n }, this._currentReconnectDelay) as unknown as number;\n\n // Apply exponential backoff\n this._currentReconnectDelay = Math.min(\n this._currentReconnectDelay * this._reconnectBackoffMultiplier,\n this._maxReconnectDelay\n );\n }\n\n /**\n * Attempts to reconnect to the extension\n */\n private async _attemptReconnect(): Promise<void> {\n if (this._isClosed || !this._isStarted) {\n return;\n }\n\n try {\n // First, try to wake up the service worker by sending a message\n if (chrome?.runtime?.sendMessage) {\n try {\n await chrome.runtime.sendMessage({ type: 'ping' });\n } catch (_error) {\n // Service worker might not be ready yet\n }\n }\n\n // Attempt to connect\n await this._connect();\n\n console.log('Reconnection successful');\n this._isReconnecting = false;\n } catch (error) {\n console.error('Reconnection failed:', error);\n\n // Schedule another attempt\n this._scheduleReconnect();\n }\n }\n}\n","import type {\n Transport,\n TransportSendOptions,\n} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for ExtensionServerTransport\n */\nexport type ExtensionServerTransportOptions = {\n /**\n * Enable keep-alive mechanism to prevent service worker shutdown\n * Default: true\n */\n keepAlive?: boolean;\n\n /**\n * Keep-alive interval in milliseconds\n * Default: 25000 (25 seconds, less than Chrome's 30-second timeout)\n */\n keepAliveInterval?: number;\n};\n\n/**\n * Server transport for Chrome extensions using Port-based messaging.\n * This transport handles a single client connection through Chrome's port messaging API.\n * It should be used in the extension's background service worker.\n *\n * Features:\n * - Keep-alive mechanism to prevent service worker shutdown\n * - Graceful connection state management\n */\nexport class ExtensionServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler: ((message: any, port: chrome.runtime.Port) => void) | undefined;\n private _disconnectHandler: ((port: chrome.runtime.Port) => void) | undefined;\n private _keepAliveTimer: number | undefined;\n private _options: ExtensionServerTransportOptions;\n private _connectionInfo: {\n connectedAt: number;\n lastMessageAt: number;\n messageCount: number;\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port, options: ExtensionServerTransportOptions = {}) {\n this._port = port;\n this._options = {\n keepAlive: options.keepAlive ?? true,\n keepAliveInterval: options.keepAliveInterval ?? 1000,\n };\n this._connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Update connection info\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n\n // Handle ping messages for keep-alive\n if (message.type === 'ping') {\n this._port.postMessage({ type: 'pong' });\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n console.log(\n `[ExtensionServerTransport] Client disconnected after ${Date.now() - this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`\n );\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Start keep-alive mechanism if enabled\n if (this._options.keepAlive) {\n this._startKeepAlive();\n }\n\n console.log(\n `[ExtensionServerTransport] Started with client: ${this._port.sender?.id || 'unknown'}`\n );\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n // Check if the error is due to disconnection\n if (chrome.runtime.lastError || !this._port) {\n this._cleanup();\n this.onclose?.();\n throw new Error('Client disconnected');\n }\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n // Stop keep-alive timer\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n\n /**\n * Starts the keep-alive mechanism\n */\n private _startKeepAlive(): void {\n if (this._keepAliveTimer) {\n return;\n }\n\n console.log(\n `[ExtensionServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`\n );\n\n this._keepAliveTimer = setInterval(() => {\n if (!this._port) {\n this._stopKeepAlive();\n return;\n }\n\n try {\n // Send a keep-alive ping\n this._port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n console.error('[ExtensionServerTransport] Keep-alive failed:', error);\n this._stopKeepAlive();\n }\n }, this._options.keepAliveInterval!) as unknown as number;\n }\n\n /**\n * Stops the keep-alive mechanism\n */\n private _stopKeepAlive(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n }\n\n /**\n * Gets connection information\n */\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: !!this._port && this._started,\n };\n }\n}\n","import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface IframeChildTransportOptions {\n /** Whitelist of parent origins allowed to connect (for security) */\n allowedOrigins: string[];\n /** Optional channel name (default: 'mcp-iframe') */\n channelId?: string;\n /** Retry interval for broadcasting ready signal in milliseconds (default: 250) */\n serverReadyRetryMs?: number;\n}\n\n/**\n * IframeChildTransport - Server transport for iframe\n *\n * Use this transport when an iframe wants to expose an MCP server to its parent page.\n * Supports cross-origin communication.\n *\n * @example\n * ```typescript\n * const transport = new IframeChildTransport({\n * allowedOrigins: ['https://parent-app.com'],\n * });\n *\n * const server = new Server({ name: 'IframeApp', version: '1.0.0' });\n * await server.connect(transport);\n * ```\n */\nexport class IframeChildTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[];\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n private _serverReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private readonly _serverReadyRetryMs: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: IframeChildTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-iframe';\n this._serverReadyRetryMs = options.serverReadyRetryMs ?? 250;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n this._clientOrigin = event.origin;\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-check-ready') {\n this.broadcastServerReady();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n this.broadcastServerReady();\n }\n\n private broadcastServerReady() {\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n '*'\n );\n\n this.clearServerReadyRetry();\n } else {\n this.scheduleServerReadyRetry();\n }\n }\n\n private scheduleServerReadyRetry() {\n if (this._serverReadyTimeout) {\n return;\n }\n\n this._serverReadyTimeout = setTimeout(() => {\n this._serverReadyTimeout = undefined;\n if (this._started) {\n this.broadcastServerReady();\n }\n }, this._serverReadyRetryMs);\n }\n\n private clearServerReadyRetry() {\n if (this._serverReadyTimeout) {\n clearTimeout(this._serverReadyTimeout);\n this._serverReadyTimeout = undefined;\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._clientOrigin) {\n console.warn('[IframeChildTransport] No client connected, message not sent');\n return;\n }\n\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: message,\n },\n this._clientOrigin\n );\n } else {\n console.warn('[IframeChildTransport] Not running in an iframe, message not sent');\n }\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n\n if (this._clientOrigin && window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-stopped',\n },\n '*'\n );\n }\n\n this.clearServerReadyRetry();\n\n this.onclose?.();\n }\n}\n","import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface IframeParentTransportOptions {\n /** Reference to the iframe element */\n iframe: HTMLIFrameElement;\n /** Expected origin of the iframe (for security) */\n targetOrigin: string;\n /** Optional channel name (default: 'mcp-iframe') */\n channelId?: string;\n /** Retry interval for ready handshake in milliseconds (default: 250) */\n checkReadyRetryMs?: number;\n}\n\n/**\n * IframeParentTransport - Client transport for parent page\n *\n * Use this transport when the parent page wants to connect to an MCP server\n * running inside an iframe. Supports cross-origin communication.\n *\n * @example\n * ```typescript\n * const iframe = document.querySelector('iframe');\n * const transport = new IframeParentTransport({\n * iframe,\n * targetOrigin: 'https://iframe-app.com',\n * });\n *\n * const client = new Client({ name: 'Parent', version: '1.0.0' });\n * await client.connect(transport);\n * ```\n */\nexport class IframeParentTransport implements Transport {\n private _started = false;\n private _iframe: HTMLIFrameElement;\n private _targetOrigin: string;\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _checkReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private readonly _checkReadyRetryMs: number;\n public readonly serverReadyPromise: Promise<void>;\n private _serverReadyResolve: () => void;\n private _serverReadyReject: (reason: any) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: IframeParentTransportOptions) {\n if (!options.iframe) {\n throw new Error('iframe element is required');\n }\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._iframe = options.iframe;\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId || 'mcp-iframe';\n this._checkReadyRetryMs = options.checkReadyRetryMs ?? 250;\n\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n this.serverReadyPromise = promise;\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-server-ready') {\n this._serverReadyResolve();\n this.clearCheckReadyRetry();\n return;\n }\n\n if (typeof payload === 'string' && payload === 'mcp-server-stopped') {\n console.log('[IframeParentTransport] Received mcp-server-stopped event, closing transport');\n this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._serverReadyResolve();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n this.sendCheckReady();\n }\n\n private sendCheckReady() {\n const contentWindow = this._iframe.contentWindow;\n\n if (!contentWindow) {\n console.warn('[IframeParentTransport] iframe.contentWindow not available, will retry');\n this.scheduleCheckReadyRetry();\n return;\n }\n\n contentWindow.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: 'mcp-check-ready',\n },\n this._targetOrigin\n );\n }\n\n private scheduleCheckReadyRetry() {\n if (this._checkReadyTimeout) {\n return;\n }\n\n this._checkReadyTimeout = setTimeout(() => {\n this._checkReadyTimeout = undefined;\n if (this._started) {\n this.sendCheckReady();\n }\n }, this._checkReadyRetryMs);\n }\n\n private clearCheckReadyRetry() {\n if (this._checkReadyTimeout) {\n clearTimeout(this._checkReadyTimeout);\n this._checkReadyTimeout = undefined;\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n await this.serverReadyPromise;\n\n const contentWindow = this._iframe.contentWindow;\n\n if (!contentWindow) {\n throw new Error('iframe.contentWindow not available');\n }\n\n contentWindow.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n\n this._serverReadyReject(new Error('Transport closed before server ready'));\n\n this.clearCheckReadyRetry();\n\n this._started = false;\n this.onclose?.();\n }\n}\n","import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabClientTransportOptions {\n /** Origin expected from the server window (for security) */\n targetOrigin: string;\n /** Optional channel name (default: 'mcp-default') */\n channelId?: string;\n}\n\nexport class TabClientTransport implements Transport {\n private _started = false;\n private _targetOrigin: string;\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n public readonly serverReadyPromise: Promise<void>;\n private _serverReadyResolve: () => void;\n private _serverReadyReject: (reason: any) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId || 'mcp-default';\n\n // Create the server ready promise in constructor so it's available immediately\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n this.serverReadyPromise = promise;\n this._serverReadyResolve = () => {\n resolve();\n };\n this._serverReadyReject = (reason) => {\n reject(reason);\n };\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n const payload = event.data.payload;\n\n // Handle server ready signal\n if (typeof payload === 'string' && payload === 'mcp-server-ready') {\n this._serverReadyResolve();\n return;\n }\n\n // Handle server stopped signal\n if (typeof payload === 'string' && payload === 'mcp-server-stopped') {\n console.log('[TabClientTransport] Received mcp-server-stopped event, closing transport');\n this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._serverReadyResolve();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n // Send check-ready to prompt server if already started\n this.sendCheckReady();\n }\n\n private sendCheckReady() {\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: 'mcp-check-ready',\n },\n this._targetOrigin\n );\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n // Await server ready before sending any JSON-RPC message\n await this.serverReadyPromise;\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n\n // Reject the server ready promise if it hasn't been resolved yet\n this._serverReadyReject(new Error('Transport closed before server ready'));\n\n this._started = false;\n this.onclose?.();\n }\n}\n","import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface TabServerTransportOptions {\n /** Whitelist of origins allowed to connect (for security) */\n allowedOrigins: string[];\n /** Optional channel name (default: 'mcp-default') */\n channelId?: string;\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[];\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n this._clientOrigin = event.origin;\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-check-ready') {\n // Respond with server ready\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n this._clientOrigin\n );\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n // Broadcast server ready to all allowed origins\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n '*'\n );\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n // If we have a known client origin, use it (for security)\n // Otherwise, use '*' for backwards compatibility with clients that don't do the handshake\n const targetOrigin = this._clientOrigin || '*';\n\n if (!this._clientOrigin) {\n console.debug(\n '[TabServerTransport] Sending to unknown client origin (backwards compatibility mode)'\n );\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: message,\n },\n targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n\n // Post message to notify content scripts that the MCP server has stopped\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-stopped',\n },\n '*'\n );\n\n this.onclose?.();\n }\n}\n","import type {\n Transport,\n TransportSendOptions,\n} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for UserScriptClientTransport\n */\nexport interface UserScriptClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n\n /**\n * Enable automatic reconnection on disconnect\n * Default: true\n */\n autoReconnect?: boolean;\n\n /**\n * Maximum number of reconnection attempts\n * Default: 10\n */\n maxReconnectAttempts?: number;\n\n /**\n * Initial reconnection delay in milliseconds\n * Default: 1000\n */\n reconnectDelay?: number;\n\n /**\n * Maximum reconnection delay in milliseconds\n * Default: 30000\n */\n maxReconnectDelay?: number;\n\n /**\n * Reconnection backoff multiplier\n * Default: 1.5\n */\n reconnectBackoffMultiplier?: number;\n}\n\n/**\n * Client transport for Chrome MV3 User Scripts using Port-based messaging.\n * This transport can be used inside a User Script context to connect to the\n * extension's background service worker. On the extension side, connections\n * are received via chrome.runtime.onUserScriptConnect.\n *\n * Features automatic reconnection to handle background service worker lifecycle.\n */\nexport class UserScriptClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private _extensionId: string | undefined;\n private _portName: string;\n private _messageHandler: ((message: any) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _isReconnecting = false;\n private _reconnectAttempts = 0;\n private _reconnectTimer: number | undefined;\n private _currentReconnectDelay: number;\n private _isStarted = false;\n private _isClosed = false;\n\n // Configuration\n private _autoReconnect: boolean;\n private _maxReconnectAttempts: number;\n private _reconnectDelay: number;\n private _maxReconnectDelay: number;\n private _reconnectBackoffMultiplier: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: UserScriptClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n this._autoReconnect = options.autoReconnect ?? true;\n this._maxReconnectAttempts = options.maxReconnectAttempts ?? 10;\n this._reconnectDelay = options.reconnectDelay ?? 1000;\n this._maxReconnectDelay = options.maxReconnectDelay ?? 30000;\n this._reconnectBackoffMultiplier = options.reconnectBackoffMultiplier ?? 1.5;\n this._currentReconnectDelay = this._reconnectDelay;\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._isStarted && this._port) {\n console.warn(\n 'UserScriptClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n return;\n }\n\n this._isStarted = true;\n this._isClosed = false;\n\n await this._connect();\n }\n\n /**\n * Connects to the extension port\n */\n private async _connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome MV3 User Script context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension. From a user script, this triggers onUserScriptConnect on the extension side.\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, {\n name: this._portName,\n });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Handle keep-alive messages\n if ((message as any).type === 'keep-alive') {\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n\n // Only attempt reconnection if we're started and not manually closed\n if (this._isStarted && !this._isClosed && this._autoReconnect) {\n this._scheduleReconnect();\n } else {\n this.onclose?.();\n }\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n\n // If we're reconnecting and hit an error, schedule another attempt\n if (this._isReconnecting && this._isStarted && !this._isClosed && this._autoReconnect) {\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n // Connection successful\n this._reconnectAttempts = 0;\n this._currentReconnectDelay = this._reconnectDelay;\n this._isReconnecting = false;\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._isStarted) {\n throw new Error('Transport not started');\n }\n\n if (this._isClosed) {\n throw new Error('Transport is closed');\n }\n\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._isClosed = true;\n this._isStarted = false;\n\n // Cancel any pending reconnection\n if (this._reconnectTimer !== undefined) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = undefined;\n }\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n\n /**\n * Schedules a reconnection attempt\n */\n private _scheduleReconnect(): void {\n if (this._isReconnecting || this._isClosed || !this._isStarted) {\n return;\n }\n\n this._isReconnecting = true;\n\n // Check if we've exceeded max attempts\n if (this._reconnectAttempts >= this._maxReconnectAttempts) {\n console.error('Maximum reconnection attempts reached');\n this._isReconnecting = false;\n this.onerror?.(new Error('Maximum reconnection attempts reached'));\n this.onclose?.();\n return;\n }\n\n this._reconnectAttempts++;\n\n console.log(\n `Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`\n );\n\n this._reconnectTimer = setTimeout(() => {\n this._attemptReconnect();\n }, this._currentReconnectDelay) as unknown as number;\n\n // Apply exponential backoff\n this._currentReconnectDelay = Math.min(\n this._currentReconnectDelay * this._reconnectBackoffMultiplier,\n this._maxReconnectDelay\n );\n }\n\n /**\n * Attempts to reconnect to the extension\n */\n private async _attemptReconnect(): Promise<void> {\n if (this._isClosed || !this._isStarted) {\n return;\n }\n\n try {\n // First, try to wake up the service worker by sending a message\n if (chrome?.runtime?.sendMessage) {\n try {\n await chrome.runtime.sendMessage({ type: 'ping' });\n } catch (_error) {\n // Service worker might not be ready yet\n }\n }\n\n // Attempt to connect\n await this._connect();\n\n console.log('Reconnection successful');\n this._isReconnecting = false;\n } catch (error) {\n console.error('Reconnection failed:', error);\n\n // Schedule another attempt\n this._scheduleReconnect();\n }\n }\n}\n","import type {\n Transport,\n TransportSendOptions,\n} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { type JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Configuration options for UserScriptServerTransport\n */\nexport type UserScriptServerTransportOptions = {\n /**\n * Enable keep-alive mechanism to prevent service worker shutdown\n * Default: true\n */\n keepAlive?: boolean;\n\n /**\n * Keep-alive interval in milliseconds\n * Default: 25000 (25 seconds, less than Chrome's 30-second timeout)\n */\n keepAliveInterval?: number;\n};\n\n/**\n * Server transport for Chrome MV3 User Scripts using Port-based messaging.\n * This transport handles a single client connection through Chrome's port\n * messaging API. It should be used in the extension's background service\n * worker. Connections are initiated from User Scripts via chrome.runtime.connect\n * and received here via chrome.runtime.onUserScriptConnect.\n *\n * Features:\n * - Keep-alive mechanism to prevent service worker shutdown\n * - Graceful connection state management\n */\nexport class UserScriptServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler: ((message: unknown, port: chrome.runtime.Port) => void) | undefined;\n private _disconnectHandler: ((port: chrome.runtime.Port) => void) | undefined;\n private _keepAliveTimer: number | undefined;\n private _options: UserScriptServerTransportOptions;\n private _connectionInfo: {\n connectedAt: number;\n lastMessageAt: number;\n messageCount: number;\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port, options: UserScriptServerTransportOptions = {}) {\n this._port = port;\n this._options = {\n keepAlive: options.keepAlive ?? true,\n keepAliveInterval: options.keepAliveInterval ?? 1000,\n };\n this._connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'UserScriptServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: unknown) => {\n try {\n // Update connection info\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n\n // Handle ping messages for keep-alive\n const msg = message as { type?: string };\n if (msg.type === 'ping') {\n this._port.postMessage({ type: 'pong' });\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n console.log(\n `[UserScriptServerTransport] Client disconnected after ${Date.now() - this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`\n );\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Start keep-alive mechanism if enabled\n if (this._options.keepAlive) {\n this._startKeepAlive();\n }\n\n console.log(\n `[UserScriptServerTransport] Started with client: ${this._port.sender?.id || 'unknown'}`\n );\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n // Check if the error is due to disconnection\n if (chrome.runtime.lastError || !this._port) {\n this._cleanup();\n this.onclose?.();\n throw new Error('Client disconnected');\n }\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n // Stop keep-alive timer\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n\n /**\n * Starts the keep-alive mechanism\n */\n private _startKeepAlive(): void {\n if (this._keepAliveTimer) {\n return;\n }\n\n console.log(\n `[UserScriptServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`\n );\n\n this._keepAliveTimer = setInterval(() => {\n if (!this._port) {\n this._stopKeepAlive();\n return;\n }\n\n try {\n // Send a keep-alive ping\n this._port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n console.error('[UserScriptServerTransport] Keep-alive failed:', error);\n this._stopKeepAlive();\n }\n }, this._options.keepAliveInterval!) as unknown as number;\n }\n\n /**\n * Stops the keep-alive mechanism\n */\n private _stopKeepAlive(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n }\n\n /**\n * Gets connection information\n */\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: !!this._port && this._started,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["error","errorResponse: JSONRPCMessage","interruptedResponse: JSONRPCMessage","staleIds: (string | number)[]","error"],"sources":["../src/browser-types.ts","../src/ExtensionClientTransport.ts","../src/ExtensionServerTransport.ts","../src/IframeChildTransport.ts","../src/IframeParentTransport.ts","../src/TabClientTransport.ts","../src/TabServerTransport.ts","../src/UserScriptClientTransport.ts","../src/UserScriptServerTransport.ts"],"sourcesContent":["import type { JSONRPCMessage } from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Unique identifier for an event in the event store\n */\nexport type EventId = string;\n\n/**\n * Unique identifier for a stream of events\n */\nexport type StreamId = string;\n\n/**\n * Options for connecting to an MCP server\n */\nexport interface MCPConnectOptions {\n /**\n * The event ID to resume from if reconnecting\n */\n resumeFrom?: EventId;\n}\n\n/**\n * Information about the MCP server\n */\nexport interface MCPServerInfo {\n /**\n * Unique identifier for this server instance\n */\n instanceId: string;\n /**\n * Whether the server maintains session state\n */\n stateful: boolean;\n /**\n * Whether the server has event storage enabled\n */\n hasEventStore: boolean;\n}\n\n/**\n * Event storage interface for accessing stored events\n */\nexport interface MCPEventStore {\n /**\n * Get stored events, optionally filtered by client and/or after a specific event\n * @param clientId - Optional client ID to filter events\n * @param afterEventId - Optional event ID to get events after\n * @param limit - Maximum number of events to return (default: 100)\n */\n getEvents(clientId?: string, afterEventId?: EventId, limit?: number): StoredEvent[];\n\n /**\n * Get the ID of the last event, optionally for a specific client\n * @param clientId - Optional client ID to filter by\n */\n getLastEventId(clientId?: string): EventId | null;\n\n /**\n * Clear stored events, optionally for a specific client\n * @param clientId - Optional client ID to clear events for\n */\n clearEvents(clientId?: string): void;\n}\n\n/**\n * The MCP interface exposed on window for browser environments\n */\nexport interface MCPBrowserInterface {\n /**\n * Connect a client to the MCP server\n * @param clientId - Unique identifier for the client\n * @param options - Optional connection options\n * @returns MessagePort for communication or null if connection fails\n */\n connect(clientId: string, options?: MCPConnectOptions): MessagePort | null;\n\n /**\n * Disconnect a client from the MCP server\n * @param clientId - The client ID to disconnect\n */\n disconnect(clientId: string): void;\n\n /**\n * Terminate a client's session and clean up all associated resources\n * @param clientId - The client ID to terminate\n */\n terminateSession?(clientId: string): void;\n\n /**\n * Check if the MCP server is available and running\n */\n isServerAvailable(): boolean;\n\n /**\n * Get information about the MCP server\n */\n getServerInfo(): MCPServerInfo;\n\n /**\n * Event storage access (only available in stateful mode with event store)\n */\n events?: MCPEventStore;\n}\n\n/**\n * Extended Window interface with MCP support\n */\nexport interface MCPWindow extends Window {\n mcp?: MCPBrowserInterface;\n}\n\n/**\n * Message types for internal MCP communication\n */\nexport interface MCPServerInfoMessage {\n type: 'mcp-server-info';\n serverInstanceId: string;\n serverSessionId?: string;\n hasEventStore: boolean;\n streamId: StreamId;\n}\n\nexport interface MCPEventMessage {\n type: 'mcp-event';\n eventId: EventId;\n message: JSONRPCMessage;\n}\n\nexport interface MCPReplayEventMessage {\n type: 'mcp-replay-event';\n eventId: EventId;\n message: JSONRPCMessage;\n}\n\n/**\n * Stored event with metadata for event sourcing\n */\nexport interface StoredEvent {\n eventId: EventId;\n streamId: StreamId;\n message: JSONRPCMessage;\n timestamp: number;\n clientId: string;\n}\n\nexport enum NativeMessageType {\n START = 'start',\n STARTED = 'started',\n STOP = 'stop',\n STOPPED = 'stopped',\n PING = 'ping',\n PONG = 'pong',\n ERROR = 'error',\n LIST_TOOLS = 'list_tools',\n CALL_TOOL = 'call_tool',\n TOOL_LIST_UPDATED = 'tool_list_updated',\n TOOL_LIST_UPDATED_ACK = 'tool_list_updated_ack',\n PROCESS_DATA = 'process_data',\n\n // Additional message types used in Chrome extension\n SERVER_STARTED = 'server_started',\n SERVER_STOPPED = 'server_stopped',\n ERROR_FROM_NATIVE_HOST = 'error_from_native_host',\n CONNECT_NATIVE = 'connectNative',\n PING_NATIVE = 'ping_native',\n DISCONNECT_NATIVE = 'disconnect_native',\n}\n\n/**\n * Chrome Extension Constants\n * Centralized configuration values and magic constants\n */\n\n// Native Host Configuration\nexport const NATIVE_HOST = {\n NAME: 'com.chromemcp.nativehost',\n DEFAULT_PORT: 12306,\n} as const;\n\n// Error Messages\nexport const ERROR_MESSAGES = {\n NATIVE_CONNECTION_FAILED: 'Failed to connect to native host',\n NATIVE_DISCONNECTED: 'Native connection disconnected',\n SERVER_STATUS_LOAD_FAILED: 'Failed to load server status',\n TOOL_EXECUTION_FAILED: 'Tool execution failed',\n SERVER_STATUS_SAVE_FAILED: 'Failed to save server status',\n} as const;\n\n// Success Messages\nexport const SUCCESS_MESSAGES = {\n TOOL_EXECUTED: 'Tool executed successfully',\n CONNECTION_ESTABLISHED: 'Connection established',\n SERVER_STARTED: 'Server started successfully',\n SERVER_STOPPED: 'Server stopped successfully',\n} as const;\n\n// Storage Keys\nexport const STORAGE_KEYS = {\n SERVER_STATUS: 'serverStatus',\n} as const;\n\n// Background script message types\nexport const BACKGROUND_MESSAGE_TYPES = {\n GET_SERVER_STATUS: 'get_server_status',\n REFRESH_SERVER_STATUS: 'refresh_server_status',\n SERVER_STATUS_CHANGED: 'server_status_changed',\n} as const;\n\nexport const HOST_NAME = NATIVE_HOST.NAME;\n\n/**\n * Server status management interface\n */\nexport interface ServerStatus {\n isRunning: boolean;\n port?: number;\n lastUpdated: number;\n}\n","import {\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type Transport,\n type TransportSendOptions,\n} from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Configuration options for ExtensionClientTransport\n */\nexport interface ExtensionClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n\n /**\n * Enable automatic reconnection on disconnect\n * Default: true\n */\n autoReconnect?: boolean;\n\n /**\n * Maximum number of reconnection attempts\n * Default: 10\n */\n maxReconnectAttempts?: number;\n\n /**\n * Initial reconnection delay in milliseconds\n * Default: 1000\n */\n reconnectDelay?: number;\n\n /**\n * Maximum reconnection delay in milliseconds\n * Default: 30000\n */\n maxReconnectDelay?: number;\n\n /**\n * Reconnection backoff multiplier\n * Default: 1.5\n */\n reconnectBackoffMultiplier?: number;\n}\n\n/**\n * Client transport for Chrome extensions using Port-based messaging.\n * This transport can be used in content scripts, popup scripts, or sidepanel scripts\n * to connect to a server running in the background service worker.\n *\n * Features automatic reconnection to handle background service worker lifecycle.\n */\nexport class ExtensionClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private _extensionId: string | undefined;\n private _portName: string;\n private _messageHandler: ((message: any) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _isReconnecting = false;\n private _reconnectAttempts = 0;\n private _reconnectTimer: number | undefined;\n private _currentReconnectDelay: number;\n private _isStarted = false;\n private _isClosed = false;\n\n // Configuration\n private _autoReconnect: boolean;\n private _maxReconnectAttempts: number;\n private _reconnectDelay: number;\n private _maxReconnectDelay: number;\n private _reconnectBackoffMultiplier: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: ExtensionClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n this._autoReconnect = options.autoReconnect ?? true;\n this._maxReconnectAttempts = options.maxReconnectAttempts ?? 10;\n this._reconnectDelay = options.reconnectDelay ?? 1000;\n this._maxReconnectDelay = options.maxReconnectDelay ?? 30000;\n this._reconnectBackoffMultiplier = options.reconnectBackoffMultiplier ?? 1.5;\n this._currentReconnectDelay = this._reconnectDelay;\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._isStarted && this._port) {\n console.warn(\n 'ExtensionClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n return;\n }\n\n this._isStarted = true;\n this._isClosed = false;\n\n await this._connect();\n }\n\n /**\n * Connects to the extension port\n */\n private async _connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome extension context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, {\n name: this._portName,\n });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Handle keep-alive messages\n if (message.type === 'keep-alive') {\n // Just acknowledge receipt, no need to propagate\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n\n // Only attempt reconnection if we're started and not manually closed\n if (this._isStarted && !this._isClosed && this._autoReconnect) {\n this._scheduleReconnect();\n } else {\n this.onclose?.();\n }\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n\n // If we're reconnecting and hit an error, schedule another attempt\n if (this._isReconnecting && this._isStarted && !this._isClosed && this._autoReconnect) {\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n // Connection successful\n this._reconnectAttempts = 0;\n this._currentReconnectDelay = this._reconnectDelay;\n this._isReconnecting = false;\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._isStarted) {\n throw new Error('Transport not started');\n }\n\n if (this._isClosed) {\n throw new Error('Transport is closed');\n }\n\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._isClosed = true;\n this._isStarted = false;\n\n // Cancel any pending reconnection\n if (this._reconnectTimer !== undefined) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = undefined;\n }\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n\n /**\n * Schedules a reconnection attempt\n */\n private _scheduleReconnect(): void {\n if (this._isReconnecting || this._isClosed || !this._isStarted) {\n return;\n }\n\n this._isReconnecting = true;\n\n // Check if we've exceeded max attempts\n if (this._reconnectAttempts >= this._maxReconnectAttempts) {\n console.error('Maximum reconnection attempts reached');\n this._isReconnecting = false;\n this.onerror?.(new Error('Maximum reconnection attempts reached'));\n this.onclose?.();\n return;\n }\n\n this._reconnectAttempts++;\n\n console.log(\n `Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`\n );\n\n this._reconnectTimer = setTimeout(() => {\n this._attemptReconnect();\n }, this._currentReconnectDelay) as unknown as number;\n\n // Apply exponential backoff\n this._currentReconnectDelay = Math.min(\n this._currentReconnectDelay * this._reconnectBackoffMultiplier,\n this._maxReconnectDelay\n );\n }\n\n /**\n * Attempts to reconnect to the extension\n */\n private async _attemptReconnect(): Promise<void> {\n if (this._isClosed || !this._isStarted) {\n return;\n }\n\n try {\n // First, try to wake up the service worker by sending a message\n if (chrome?.runtime?.sendMessage) {\n try {\n await chrome.runtime.sendMessage({ type: 'ping' });\n } catch (_error) {\n // Service worker might not be ready yet\n }\n }\n\n // Attempt to connect\n await this._connect();\n\n console.log('Reconnection successful');\n this._isReconnecting = false;\n } catch (error) {\n console.error('Reconnection failed:', error);\n\n // Schedule another attempt\n this._scheduleReconnect();\n }\n }\n}\n","import {\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type Transport,\n type TransportSendOptions,\n} from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Configuration options for ExtensionServerTransport\n */\nexport type ExtensionServerTransportOptions = {\n /**\n * Enable keep-alive mechanism to prevent service worker shutdown\n * Default: true\n */\n keepAlive?: boolean;\n\n /**\n * Keep-alive interval in milliseconds\n * Default: 25000 (25 seconds, less than Chrome's 30-second timeout)\n */\n keepAliveInterval?: number;\n};\n\n/**\n * Server transport for Chrome extensions using Port-based messaging.\n * This transport handles a single client connection through Chrome's port messaging API.\n * It should be used in the extension's background service worker.\n *\n * Features:\n * - Keep-alive mechanism to prevent service worker shutdown\n * - Graceful connection state management\n */\nexport class ExtensionServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler: ((message: any, port: chrome.runtime.Port) => void) | undefined;\n private _disconnectHandler: ((port: chrome.runtime.Port) => void) | undefined;\n private _keepAliveTimer: number | undefined;\n private _options: ExtensionServerTransportOptions;\n private _connectionInfo: {\n connectedAt: number;\n lastMessageAt: number;\n messageCount: number;\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port, options: ExtensionServerTransportOptions = {}) {\n this._port = port;\n this._options = {\n keepAlive: options.keepAlive ?? true,\n keepAliveInterval: options.keepAliveInterval ?? 1000,\n };\n this._connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'ExtensionServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Update connection info\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n\n // Handle ping messages for keep-alive\n if (message.type === 'ping') {\n this._port.postMessage({ type: 'pong' });\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n console.log(\n `[ExtensionServerTransport] Client disconnected after ${Date.now() - this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`\n );\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Start keep-alive mechanism if enabled\n if (this._options.keepAlive) {\n this._startKeepAlive();\n }\n\n console.log(\n `[ExtensionServerTransport] Started with client: ${this._port.sender?.id || 'unknown'}`\n );\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n // Check if the error is due to disconnection\n if (chrome.runtime.lastError || !this._port) {\n this._cleanup();\n this.onclose?.();\n throw new Error('Client disconnected');\n }\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n // Stop keep-alive timer\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n\n /**\n * Starts the keep-alive mechanism\n */\n private _startKeepAlive(): void {\n if (this._keepAliveTimer) {\n return;\n }\n\n console.log(\n `[ExtensionServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`\n );\n\n this._keepAliveTimer = setInterval(() => {\n if (!this._port) {\n this._stopKeepAlive();\n return;\n }\n\n try {\n // Send a keep-alive ping\n this._port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n console.error('[ExtensionServerTransport] Keep-alive failed:', error);\n this._stopKeepAlive();\n }\n }, this._options.keepAliveInterval!) as unknown as number;\n }\n\n /**\n * Stops the keep-alive mechanism\n */\n private _stopKeepAlive(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n }\n\n /**\n * Gets connection information\n */\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: !!this._port && this._started,\n };\n }\n}\n","import { type JSONRPCMessage, JSONRPCMessageSchema, type Transport } from '@mcp-b/webmcp-ts-sdk';\n\nexport interface IframeChildTransportOptions {\n /** Whitelist of parent origins allowed to connect (for security) */\n allowedOrigins: string[];\n /** Optional channel name (default: 'mcp-iframe') */\n channelId?: string;\n /** Retry interval for broadcasting ready signal in milliseconds (default: 250) */\n serverReadyRetryMs?: number;\n}\n\n/**\n * IframeChildTransport - Server transport for iframe\n *\n * Use this transport when an iframe wants to expose an MCP server to its parent page.\n * Supports cross-origin communication.\n *\n * @example\n * ```typescript\n * const transport = new IframeChildTransport({\n * allowedOrigins: ['https://parent-app.com'],\n * });\n *\n * const server = new Server({ name: 'IframeApp', version: '1.0.0' });\n * await server.connect(transport);\n * ```\n */\nexport class IframeChildTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[];\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n private _serverReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private readonly _serverReadyRetryMs: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: IframeChildTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-iframe';\n this._serverReadyRetryMs = options.serverReadyRetryMs ?? 250;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n this._clientOrigin = event.origin;\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-check-ready') {\n this.broadcastServerReady();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n this.broadcastServerReady();\n }\n\n private broadcastServerReady() {\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n '*'\n );\n\n this.clearServerReadyRetry();\n } else {\n this.scheduleServerReadyRetry();\n }\n }\n\n private scheduleServerReadyRetry() {\n if (this._serverReadyTimeout) {\n return;\n }\n\n this._serverReadyTimeout = setTimeout(() => {\n this._serverReadyTimeout = undefined;\n if (this._started) {\n this.broadcastServerReady();\n }\n }, this._serverReadyRetryMs);\n }\n\n private clearServerReadyRetry() {\n if (this._serverReadyTimeout) {\n clearTimeout(this._serverReadyTimeout);\n this._serverReadyTimeout = undefined;\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._clientOrigin) {\n console.warn('[IframeChildTransport] No client connected, message not sent');\n return;\n }\n\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: message,\n },\n this._clientOrigin\n );\n } else {\n console.warn('[IframeChildTransport] Not running in an iframe, message not sent');\n }\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n this._started = false;\n\n if (this._clientOrigin && window.parent && window.parent !== window) {\n window.parent.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-stopped',\n },\n '*'\n );\n }\n\n this.clearServerReadyRetry();\n\n this.onclose?.();\n }\n}\n","import { type JSONRPCMessage, JSONRPCMessageSchema, type Transport } from '@mcp-b/webmcp-ts-sdk';\n\nexport interface IframeParentTransportOptions {\n /** Reference to the iframe element */\n iframe: HTMLIFrameElement;\n /** Expected origin of the iframe (for security) */\n targetOrigin: string;\n /** Optional channel name (default: 'mcp-iframe') */\n channelId?: string;\n /** Retry interval for ready handshake in milliseconds (default: 250) */\n checkReadyRetryMs?: number;\n}\n\n/**\n * IframeParentTransport - Client transport for parent page\n *\n * Use this transport when the parent page wants to connect to an MCP server\n * running inside an iframe. Supports cross-origin communication.\n *\n * @example\n * ```typescript\n * const iframe = document.querySelector('iframe');\n * const transport = new IframeParentTransport({\n * iframe,\n * targetOrigin: 'https://iframe-app.com',\n * });\n *\n * const client = new Client({ name: 'Parent', version: '1.0.0' });\n * await client.connect(transport);\n * ```\n */\nexport class IframeParentTransport implements Transport {\n private _started = false;\n private _iframe: HTMLIFrameElement;\n private _targetOrigin: string;\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _checkReadyTimeout: ReturnType<typeof setTimeout> | undefined;\n private readonly _checkReadyRetryMs: number;\n public readonly serverReadyPromise: Promise<void>;\n private _serverReadyResolve: () => void;\n private _serverReadyReject: (reason: any) => void;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: IframeParentTransportOptions) {\n if (!options.iframe) {\n throw new Error('iframe element is required');\n }\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._iframe = options.iframe;\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId || 'mcp-iframe';\n this._checkReadyRetryMs = options.checkReadyRetryMs ?? 250;\n\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n this.serverReadyPromise = promise;\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-server-ready') {\n this._serverReadyResolve();\n this.clearCheckReadyRetry();\n return;\n }\n\n if (typeof payload === 'string' && payload === 'mcp-server-stopped') {\n console.log('[IframeParentTransport] Received mcp-server-stopped event, closing transport');\n this.close();\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n this._serverReadyResolve();\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n this.sendCheckReady();\n }\n\n private sendCheckReady() {\n const contentWindow = this._iframe.contentWindow;\n\n if (!contentWindow) {\n console.warn('[IframeParentTransport] iframe.contentWindow not available, will retry');\n this.scheduleCheckReadyRetry();\n return;\n }\n\n contentWindow.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: 'mcp-check-ready',\n },\n this._targetOrigin\n );\n }\n\n private scheduleCheckReadyRetry() {\n if (this._checkReadyTimeout) {\n return;\n }\n\n this._checkReadyTimeout = setTimeout(() => {\n this._checkReadyTimeout = undefined;\n if (this._started) {\n this.sendCheckReady();\n }\n }, this._checkReadyRetryMs);\n }\n\n private clearCheckReadyRetry() {\n if (this._checkReadyTimeout) {\n clearTimeout(this._checkReadyTimeout);\n this._checkReadyTimeout = undefined;\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n await this.serverReadyPromise;\n\n const contentWindow = this._iframe.contentWindow;\n\n if (!contentWindow) {\n throw new Error('iframe.contentWindow not available');\n }\n\n contentWindow.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n\n this._serverReadyReject(new Error('Transport closed before server ready'));\n\n this.clearCheckReadyRetry();\n\n this._started = false;\n this.onclose?.();\n }\n}\n","import { type JSONRPCMessage, JSONRPCMessageSchema, type Transport } from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Configuration options for TabClientTransport.\n *\n * @see {@link TabClientTransport}\n */\nexport interface TabClientTransportOptions {\n /**\n * Expected origin of the server window for security validation.\n *\n * **Security**: This origin is checked against `event.origin` for all incoming\n * messages to prevent cross-origin attacks. Only messages from this origin will\n * be processed.\n *\n * @example 'https://example.com'\n * @example 'http://localhost:3000'\n */\n targetOrigin: string;\n\n /**\n * Channel identifier for message routing.\n *\n * Multiple transports can coexist on the same page by using different channel IDs.\n * This allows for isolated communication channels between different MCP clients\n * and servers.\n *\n * @default 'mcp-default'\n */\n channelId?: string;\n\n /**\n * Request timeout in milliseconds.\n *\n * If a request doesn't receive a response within this time, a timeout error\n * is synthesized and delivered to the client. This prevents infinite hangs\n * when the server becomes unresponsive due to:\n * - Page navigation\n * - JavaScript errors\n * - Network issues\n * - Server crashes\n *\n * **Design rationale**: 10 seconds is appropriate for most tool operations.\n * For operations that may take longer (e.g., complex computations, slow network\n * requests), increase this value via the configuration option.\n *\n * @default 10000 (10 seconds)\n * @see {@link _handleRequestTimeout} for timeout implementation\n */\n requestTimeout?: number;\n}\n\n/**\n * Request tracking information for timeout management.\n *\n * @internal Used by TabClientTransport to track pending requests\n */\ninterface ActiveRequestInfo {\n /** Timeout ID returned by setTimeout() */\n timeoutId: number;\n /** Original request message for error reporting */\n request: JSONRPCMessage;\n}\n\n/**\n * Client-side transport for same-window MCP communication via postMessage.\n *\n * This transport connects an MCP client to a TabServerTransport running in the same\n * window. Communication occurs via the browser's `window.postMessage()` API, which\n * provides:\n * - Same-window message passing (no network overhead)\n * - Origin validation for security\n * - Asynchronous message delivery\n *\n * **Architecture**:\n * ```\n * ┌─────────────────┐ ┌──────────────────┐\n * │ MCP Client │ postMessage() │ MCP Server │\n * │ (This side) │ ←─────────────────→│ (TabServerTransport)\n * └─────────────────┘ └──────────────────┘\n * ```\n *\n * **Key features**:\n * - Request timeout to prevent infinite hangs (default 10s)\n * - Server ready detection via handshake\n * - Origin validation for security\n * - Channel-based message routing\n *\n * **Use cases**:\n * - MCP client running in content script connecting to page context\n * - MCP client in extension popup connecting to background page\n * - Testing and development scenarios\n *\n * @example Basic usage\n * ```typescript\n * const transport = new TabClientTransport({\n * targetOrigin: 'https://example.com',\n * channelId: 'my-mcp-channel',\n * requestTimeout: 10000, // Optional (default)\n * });\n *\n * // Wait for server to be ready\n * await transport.start();\n * await transport.serverReadyPromise;\n *\n * // Now safe to send messages\n * await transport.send({\n * jsonrpc: '2.0',\n * id: 1,\n * method: 'tools/call',\n * params: { name: 'my_tool', arguments: {} }\n * });\n * ```\n *\n * @example With custom timeout\n * ```typescript\n * const transport = new TabClientTransport({\n * targetOrigin: '*',\n * requestTimeout: 60000, // 60 seconds for slow operations\n * });\n * ```\n *\n * @see {@link TabServerTransport} for the server-side implementation\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage} for postMessage API\n */\nexport class TabClientTransport implements Transport {\n /** Transport state flag */\n private _started = false;\n\n /** Expected origin for message validation */\n private readonly _targetOrigin: string;\n\n /** Channel ID for message routing */\n private readonly _channelId: string;\n\n /** Request timeout in milliseconds */\n private readonly _requestTimeout: number;\n\n /** Message event listener */\n private _messageHandler?: (event: MessageEvent) => void;\n\n /**\n * Promise that resolves when the server is ready to receive messages.\n *\n * **Usage**: Always `await` this promise after calling `start()` and before\n * sending messages. Sending messages before the server is ready may result\n * in lost messages.\n *\n * @example\n * ```typescript\n * await transport.start();\n * await transport.serverReadyPromise;\n * // Now safe to send messages\n * ```\n */\n public readonly serverReadyPromise: Promise<void>;\n\n /** Internal resolver for serverReadyPromise */\n private readonly _serverReadyResolve: () => void;\n\n /** Internal rejector for serverReadyPromise */\n private readonly _serverReadyReject: (reason: unknown) => void;\n\n /**\n * Active request tracking for timeout management.\n *\n * **Key**: Request ID (from JSON-RPC message)\n * **Value**: Timeout ID and original request\n *\n * When a response is received, the entry is removed and timeout cleared.\n * If timeout expires first, an error response is synthesized.\n */\n private readonly _activeRequests = new Map<string | number, ActiveRequestInfo>();\n\n /** Callback invoked when transport closes */\n onclose?: () => void;\n\n /** Callback invoked on errors */\n onerror?: (error: Error) => void;\n\n /** Callback invoked when message received */\n onmessage?: (message: JSONRPCMessage) => void;\n\n /**\n * Creates a new TabClientTransport instance.\n *\n * **Note**: The transport is not started automatically. Call `start()` to begin\n * listening for messages.\n *\n * @param options - Configuration options\n * @throws {Error} If targetOrigin is not specified\n */\n constructor(options: TabClientTransportOptions) {\n if (!options.targetOrigin) {\n throw new Error('targetOrigin must be explicitly set for security');\n }\n\n this._targetOrigin = options.targetOrigin;\n this._channelId = options.channelId ?? 'mcp-default';\n this._requestTimeout = options.requestTimeout ?? 10000; // Default 10 seconds\n\n // Initialize server ready promise using Promise.withResolvers() pattern\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n this.serverReadyPromise = promise;\n this._serverReadyResolve = resolve;\n this._serverReadyReject = reject;\n }\n\n /**\n * Starts the transport by registering message listeners.\n *\n * **Lifecycle**:\n * 1. Register `window.addEventListener('message', ...)` handler\n * 2. Send server ready check (in case server started first)\n * 3. Wait for server ready signal via `serverReadyPromise`\n *\n * **Note**: Always `await transport.serverReadyPromise` after calling this method\n * and before sending messages.\n *\n * @throws {Error} If transport is already started\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n // Security: Validate message origin\n if (event.origin !== this._targetOrigin) {\n return;\n }\n\n // Validate message envelope\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n // Validate message direction\n if (event.data?.direction !== 'server-to-client') {\n return;\n }\n\n const payload = event.data.payload;\n\n // Handle server ready signal\n if (typeof payload === 'string' && payload === 'mcp-server-ready') {\n this._serverReadyResolve();\n return;\n }\n\n // Handle server stopped signal\n if (typeof payload === 'string' && payload === 'mcp-server-stopped') {\n console.log('[TabClientTransport] Received mcp-server-stopped event, closing transport');\n this.close();\n return;\n }\n\n // Parse and validate JSON-RPC message\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n\n // Server is ready if it's sending messages\n this._serverReadyResolve();\n\n // Clear timeout for responses\n this._clearRequestTimeout(message);\n\n // Deliver message to client\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n // Prompt server to send ready signal (in case server started first)\n this._sendCheckReady();\n }\n\n /**\n * Sends a JSON-RPC message to the server.\n *\n * **Request timeout**: If this is a request (has `method` and `id`), a timeout\n * is started. If the server doesn't respond within `requestTimeout` milliseconds,\n * an error response is synthesized.\n *\n * **Await server ready**: This method automatically awaits `serverReadyPromise`\n * before sending, ensuring messages aren't lost.\n *\n * @param message - JSON-RPC message to send\n * @throws {Error} If transport is not started\n */\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n // Wait for server to be ready before sending\n await this.serverReadyPromise;\n\n // Start timeout tracking for requests (not notifications)\n if ('method' in message && message.id !== undefined) {\n this._startRequestTimeout(message);\n }\n\n // Send message via postMessage\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: message,\n },\n this._targetOrigin\n );\n }\n\n /**\n * Closes the transport and cleans up resources.\n *\n * **Cleanup performed**:\n * - Removes message event listener\n * - Clears all active request timeouts\n * - Rejects server ready promise if still pending\n * - Invokes `onclose` callback\n *\n * **Note**: After calling this method, the transport cannot be reused.\n * Create a new instance if needed.\n */\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n\n // Clear all active request timeouts\n for (const [_id, info] of this._activeRequests) {\n clearTimeout(info.timeoutId);\n }\n this._activeRequests.clear();\n\n // Reject server ready promise if still pending\n this._serverReadyReject(new Error('Transport closed before server ready'));\n\n this._started = false;\n this.onclose?.();\n }\n\n // ============================================================================\n // Private helper methods\n // ============================================================================\n\n /**\n * Sends a server ready check message.\n *\n * This prompts the server to respond with 'mcp-server-ready' signal,\n * resolving the `serverReadyPromise`. Useful when the server may have\n * started before the client.\n *\n * @private\n */\n private _sendCheckReady(): void {\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'client-to-server',\n payload: 'mcp-check-ready',\n },\n this._targetOrigin\n );\n }\n\n /**\n * Starts timeout tracking for a request.\n *\n * **Behavior**: After `requestTimeout` milliseconds, if no response is received,\n * `_handleRequestTimeout` is called to synthesize an error response.\n *\n * **Note**: Only requests (messages with `method` and `id`) are tracked.\n * Notifications (no `id`) and responses are not tracked.\n *\n * @param message - JSON-RPC request message\n * @private\n */\n private _startRequestTimeout(message: JSONRPCMessage): void {\n if (!('id' in message) || message.id === undefined) {\n return;\n }\n\n const timeoutId = setTimeout(() => {\n this._handleRequestTimeout(message.id!);\n }, this._requestTimeout) as unknown as number;\n\n this._activeRequests.set(message.id, {\n timeoutId,\n request: message,\n });\n }\n\n /**\n * Clears timeout tracking for a response.\n *\n * Called when a response (with `result` or `error`) is received.\n * Clears the timeout and removes the tracking entry.\n *\n * @param message - JSON-RPC response message\n * @private\n */\n private _clearRequestTimeout(message: JSONRPCMessage): void {\n if (('result' in message || 'error' in message) && message.id !== undefined) {\n const info = this._activeRequests.get(message.id);\n if (info) {\n clearTimeout(info.timeoutId);\n this._activeRequests.delete(message.id);\n }\n }\n }\n\n /**\n * Handles request timeout by synthesizing an error response.\n *\n * **Error response format** (JSON-RPC 2.0):\n * ```json\n * {\n * \"jsonrpc\": \"2.0\",\n * \"id\": \"<request-id>\",\n * \"error\": {\n * \"code\": -32000,\n * \"message\": \"Request timeout - server may have navigated or become unresponsive\",\n * \"data\": {\n * \"timeoutMs\": 10000,\n * \"originalMethod\": \"tools/call\"\n * }\n * }\n * }\n * ```\n *\n * **Error code**: `-32000` (Server error) per JSON-RPC 2.0 specification.\n *\n * @param requestId - ID of the timed-out request\n * @private\n */\n private _handleRequestTimeout(requestId: string | number): void {\n const info = this._activeRequests.get(requestId);\n if (!info) {\n return; // Already handled or cleared\n }\n\n this._activeRequests.delete(requestId);\n\n // Synthesize timeout error response per JSON-RPC 2.0 spec\n const errorResponse: JSONRPCMessage = {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32000, // Server error (JSON-RPC 2.0)\n message: 'Request timeout - server may have navigated or become unresponsive',\n data: {\n timeoutMs: this._requestTimeout,\n originalMethod: 'method' in info.request ? info.request.method : undefined,\n },\n },\n };\n\n // Deliver synthesized error as if server responded\n this.onmessage?.(errorResponse);\n }\n}\n","import { type JSONRPCMessage, JSONRPCMessageSchema, type Transport } from '@mcp-b/webmcp-ts-sdk';\n\nexport interface TabServerTransportOptions {\n /** Whitelist of origins allowed to connect (for security) */\n allowedOrigins: string[];\n /** Optional channel name (default: 'mcp-default') */\n channelId?: string;\n}\n\nexport class TabServerTransport implements Transport {\n private _started = false;\n private _allowedOrigins: string[];\n private _channelId: string;\n private _messageHandler?: (event: MessageEvent) => void;\n private _clientOrigin?: string;\n private _beforeUnloadHandler?: () => void;\n private _cleanupInterval?: number;\n private _pendingRequests = new Map<\n string | number,\n {\n request: JSONRPCMessage;\n receivedAt: number;\n interruptedSent: boolean;\n }\n >();\n private readonly REQUEST_TIMEOUT_MS = 300000; // 5 minutes\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: TabServerTransportOptions) {\n if (!options.allowedOrigins || options.allowedOrigins.length === 0) {\n throw new Error('At least one allowed origin must be specified');\n }\n\n this._allowedOrigins = options.allowedOrigins;\n this._channelId = options.channelId || 'mcp-default';\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('Transport already started');\n }\n\n this._messageHandler = (event: MessageEvent) => {\n if (!this._allowedOrigins.includes(event.origin) && !this._allowedOrigins.includes('*')) {\n return;\n }\n\n if (event.data?.channel !== this._channelId || event.data?.type !== 'mcp') {\n return;\n }\n\n if (event.data?.direction !== 'client-to-server') {\n return;\n }\n\n this._clientOrigin = event.origin;\n\n const payload = event.data.payload;\n\n if (typeof payload === 'string' && payload === 'mcp-check-ready') {\n // Respond with server ready\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n this._clientOrigin\n );\n return;\n }\n\n try {\n const message = JSONRPCMessageSchema.parse(payload);\n\n // Track incoming requests (messages with method and id, but not notifications)\n if ('method' in message && message.id !== undefined) {\n this._pendingRequests.set(message.id, {\n request: message,\n receivedAt: Date.now(),\n interruptedSent: false,\n });\n }\n\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(\n new Error(`Invalid message: ${error instanceof Error ? error.message : String(error)}`)\n );\n }\n };\n\n window.addEventListener('message', this._messageHandler);\n this._started = true;\n\n // Register beforeunload handler to send interrupted responses\n this._beforeUnloadHandler = () => {\n this._handleBeforeUnload();\n };\n window.addEventListener('beforeunload', this._beforeUnloadHandler);\n\n // Periodic cleanup of stale requests (every minute)\n this._cleanupInterval = setInterval(() => {\n this._cleanupStaleRequests();\n }, 60000) as unknown as number;\n\n // Broadcast server ready to all allowed origins\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-ready',\n },\n '*'\n );\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n // Check if we already sent an interrupted response for this request\n if (('result' in message || 'error' in message) && message.id !== undefined) {\n const info = this._pendingRequests.get(message.id);\n\n // Don't send if we already sent interrupted response (race condition prevention)\n if (info?.interruptedSent) {\n console.debug(\n `[TabServerTransport] Suppressing response for ${message.id} - interrupted response already sent`\n );\n this._pendingRequests.delete(message.id);\n return;\n }\n\n // Clear from pending when response sent normally\n this._pendingRequests.delete(message.id);\n }\n\n // If we have a known client origin, use it (for security)\n // Otherwise, use '*' for backwards compatibility with clients that don't do the handshake\n const targetOrigin = this._clientOrigin || '*';\n\n if (!this._clientOrigin) {\n console.debug(\n '[TabServerTransport] Sending to unknown client origin (backwards compatibility mode)'\n );\n }\n\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: message,\n },\n targetOrigin\n );\n }\n\n /**\n * Handle page navigation by sending interrupted responses for all pending requests.\n * Called during beforeunload event.\n * @private\n */\n private _handleBeforeUnload(): void {\n // Process most recent requests first (LIFO order) in case we run out of time\n const entries = Array.from(this._pendingRequests.entries()).reverse();\n\n for (const [id, info] of entries) {\n // Mark as interrupted to prevent double-send if tool completes during unload\n info.interruptedSent = true;\n\n const toolName = 'method' in info.request ? info.request.method : 'unknown';\n\n const interruptedResponse: JSONRPCMessage = {\n jsonrpc: '2.0',\n id,\n result: {\n content: [\n {\n type: 'text',\n text: 'Tool execution interrupted by page navigation',\n },\n ],\n metadata: {\n navigationInterrupted: true,\n originalMethod: toolName,\n timestamp: Date.now(),\n },\n },\n };\n\n try {\n // Synchronous postMessage - should complete before unload\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: interruptedResponse,\n },\n this._clientOrigin || '*'\n );\n } catch (error) {\n // Best effort - may fail in rare cases\n console.error('[TabServerTransport] Failed to send beforeunload response:', error);\n }\n }\n\n this._pendingRequests.clear();\n }\n\n /**\n * Clean up stale requests that have been pending for too long.\n * Called periodically to prevent memory leaks.\n * @private\n */\n private _cleanupStaleRequests(): void {\n const now = Date.now();\n const staleIds: (string | number)[] = [];\n\n for (const [id, info] of this._pendingRequests) {\n if (now - info.receivedAt > this.REQUEST_TIMEOUT_MS) {\n staleIds.push(id);\n }\n }\n\n if (staleIds.length > 0) {\n console.warn(`[TabServerTransport] Cleaning up ${staleIds.length} stale requests`);\n for (const id of staleIds) {\n this._pendingRequests.delete(id);\n }\n }\n }\n\n async close(): Promise<void> {\n if (this._messageHandler) {\n window.removeEventListener('message', this._messageHandler);\n }\n\n if (this._beforeUnloadHandler) {\n window.removeEventListener('beforeunload', this._beforeUnloadHandler);\n }\n\n if (this._cleanupInterval !== undefined) {\n clearInterval(this._cleanupInterval);\n }\n\n this._pendingRequests.clear();\n this._started = false;\n\n // Post message to notify content scripts that the MCP server has stopped\n window.postMessage(\n {\n channel: this._channelId,\n type: 'mcp',\n direction: 'server-to-client',\n payload: 'mcp-server-stopped',\n },\n '*'\n );\n\n this.onclose?.();\n }\n}\n","import {\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type Transport,\n type TransportSendOptions,\n} from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Configuration options for UserScriptClientTransport\n */\nexport interface UserScriptClientTransportOptions {\n /**\n * The extension ID to connect to (optional for same-extension connections)\n */\n extensionId?: string;\n\n /**\n * Port name for the connection\n * Default: 'mcp'\n */\n portName?: string;\n\n /**\n * Enable automatic reconnection on disconnect\n * Default: true\n */\n autoReconnect?: boolean;\n\n /**\n * Maximum number of reconnection attempts\n * Default: 10\n */\n maxReconnectAttempts?: number;\n\n /**\n * Initial reconnection delay in milliseconds\n * Default: 1000\n */\n reconnectDelay?: number;\n\n /**\n * Maximum reconnection delay in milliseconds\n * Default: 30000\n */\n maxReconnectDelay?: number;\n\n /**\n * Reconnection backoff multiplier\n * Default: 1.5\n */\n reconnectBackoffMultiplier?: number;\n}\n\n/**\n * Client transport for Chrome MV3 User Scripts using Port-based messaging.\n * This transport can be used inside a User Script context to connect to the\n * extension's background service worker. On the extension side, connections\n * are received via chrome.runtime.onUserScriptConnect.\n *\n * Features automatic reconnection to handle background service worker lifecycle.\n */\nexport class UserScriptClientTransport implements Transport {\n private _port: chrome.runtime.Port | undefined;\n private _extensionId: string | undefined;\n private _portName: string;\n private _messageHandler: ((message: any) => void) | undefined;\n private _disconnectHandler: (() => void) | undefined;\n private _isReconnecting = false;\n private _reconnectAttempts = 0;\n private _reconnectTimer: number | undefined;\n private _currentReconnectDelay: number;\n private _isStarted = false;\n private _isClosed = false;\n\n // Configuration\n private _autoReconnect: boolean;\n private _maxReconnectAttempts: number;\n private _reconnectDelay: number;\n private _maxReconnectDelay: number;\n private _reconnectBackoffMultiplier: number;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(options: UserScriptClientTransportOptions = {}) {\n this._extensionId = options.extensionId;\n this._portName = options.portName || 'mcp';\n this._autoReconnect = options.autoReconnect ?? true;\n this._maxReconnectAttempts = options.maxReconnectAttempts ?? 10;\n this._reconnectDelay = options.reconnectDelay ?? 1000;\n this._maxReconnectDelay = options.maxReconnectDelay ?? 30000;\n this._reconnectBackoffMultiplier = options.reconnectBackoffMultiplier ?? 1.5;\n this._currentReconnectDelay = this._reconnectDelay;\n }\n\n /**\n * Starts the transport by connecting to the extension port\n */\n async start(): Promise<void> {\n if (this._isStarted && this._port) {\n console.warn(\n 'UserScriptClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n return;\n }\n\n this._isStarted = true;\n this._isClosed = false;\n\n await this._connect();\n }\n\n /**\n * Connects to the extension port\n */\n private async _connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!chrome?.runtime?.connect) {\n reject(\n new Error(\n 'Chrome runtime API not available. This transport must be used in a Chrome MV3 User Script context.'\n )\n );\n return;\n }\n\n try {\n // Connect to the extension. From a user script, this triggers onUserScriptConnect on the extension side.\n if (this._extensionId) {\n this._port = chrome.runtime.connect(this._extensionId, {\n name: this._portName,\n });\n } else {\n this._port = chrome.runtime.connect({ name: this._portName });\n }\n\n // Set up message handler\n this._messageHandler = (message: any) => {\n try {\n // Handle keep-alive messages\n if ((message as any).type === 'keep-alive') {\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n this._cleanup();\n\n // Only attempt reconnection if we're started and not manually closed\n if (this._isStarted && !this._isClosed && this._autoReconnect) {\n this._scheduleReconnect();\n } else {\n this.onclose?.();\n }\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Check for immediate connection errors\n const error = chrome.runtime.lastError;\n if (error) {\n this._cleanup();\n\n // If we're reconnecting and hit an error, schedule another attempt\n if (this._isReconnecting && this._isStarted && !this._isClosed && this._autoReconnect) {\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n reject(new Error(`Connection failed: ${error.message}`));\n return;\n }\n\n // Connection successful\n this._reconnectAttempts = 0;\n this._currentReconnectDelay = this._reconnectDelay;\n this._isReconnecting = false;\n\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Sends a message to the server\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._isStarted) {\n throw new Error('Transport not started');\n }\n\n if (this._isClosed) {\n throw new Error('Transport is closed');\n }\n\n if (!this._port) {\n throw new Error('Not connected');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._isClosed = true;\n this._isStarted = false;\n\n // Cancel any pending reconnection\n if (this._reconnectTimer !== undefined) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = undefined;\n }\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n this._port = undefined;\n }\n\n /**\n * Schedules a reconnection attempt\n */\n private _scheduleReconnect(): void {\n if (this._isReconnecting || this._isClosed || !this._isStarted) {\n return;\n }\n\n this._isReconnecting = true;\n\n // Check if we've exceeded max attempts\n if (this._reconnectAttempts >= this._maxReconnectAttempts) {\n console.error('Maximum reconnection attempts reached');\n this._isReconnecting = false;\n this.onerror?.(new Error('Maximum reconnection attempts reached'));\n this.onclose?.();\n return;\n }\n\n this._reconnectAttempts++;\n\n console.log(\n `Scheduling reconnection attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts} in ${this._currentReconnectDelay}ms`\n );\n\n this._reconnectTimer = setTimeout(() => {\n this._attemptReconnect();\n }, this._currentReconnectDelay) as unknown as number;\n\n // Apply exponential backoff\n this._currentReconnectDelay = Math.min(\n this._currentReconnectDelay * this._reconnectBackoffMultiplier,\n this._maxReconnectDelay\n );\n }\n\n /**\n * Attempts to reconnect to the extension\n */\n private async _attemptReconnect(): Promise<void> {\n if (this._isClosed || !this._isStarted) {\n return;\n }\n\n try {\n // First, try to wake up the service worker by sending a message\n if (chrome?.runtime?.sendMessage) {\n try {\n await chrome.runtime.sendMessage({ type: 'ping' });\n } catch (_error) {\n // Service worker might not be ready yet\n }\n }\n\n // Attempt to connect\n await this._connect();\n\n console.log('Reconnection successful');\n this._isReconnecting = false;\n } catch (error) {\n console.error('Reconnection failed:', error);\n\n // Schedule another attempt\n this._scheduleReconnect();\n }\n }\n}\n","import {\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type Transport,\n type TransportSendOptions,\n} from '@mcp-b/webmcp-ts-sdk';\n\n/**\n * Configuration options for UserScriptServerTransport\n */\nexport type UserScriptServerTransportOptions = {\n /**\n * Enable keep-alive mechanism to prevent service worker shutdown\n * Default: true\n */\n keepAlive?: boolean;\n\n /**\n * Keep-alive interval in milliseconds\n * Default: 25000 (25 seconds, less than Chrome's 30-second timeout)\n */\n keepAliveInterval?: number;\n};\n\n/**\n * Server transport for Chrome MV3 User Scripts using Port-based messaging.\n * This transport handles a single client connection through Chrome's port\n * messaging API. It should be used in the extension's background service\n * worker. Connections are initiated from User Scripts via chrome.runtime.connect\n * and received here via chrome.runtime.onUserScriptConnect.\n *\n * Features:\n * - Keep-alive mechanism to prevent service worker shutdown\n * - Graceful connection state management\n */\nexport class UserScriptServerTransport implements Transport {\n private _port: chrome.runtime.Port;\n private _started = false;\n private _messageHandler: ((message: unknown, port: chrome.runtime.Port) => void) | undefined;\n private _disconnectHandler: ((port: chrome.runtime.Port) => void) | undefined;\n private _keepAliveTimer: number | undefined;\n private _options: UserScriptServerTransportOptions;\n private _connectionInfo: {\n connectedAt: number;\n lastMessageAt: number;\n messageCount: number;\n };\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(port: chrome.runtime.Port, options: UserScriptServerTransportOptions = {}) {\n this._port = port;\n this._options = {\n keepAlive: options.keepAlive ?? true,\n keepAliveInterval: options.keepAliveInterval ?? 1000,\n };\n this._connectionInfo = {\n connectedAt: Date.now(),\n lastMessageAt: Date.now(),\n messageCount: 0,\n };\n }\n\n /**\n * Starts the transport and begins handling messages\n */\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\n 'UserScriptServerTransport already started! If using Server class, note that connect() calls start() automatically.'\n );\n }\n\n if (!this._port) {\n throw new Error('Port not available');\n }\n\n this._started = true;\n\n // Set up message handler\n this._messageHandler = (message: unknown) => {\n try {\n // Update connection info\n this._connectionInfo.lastMessageAt = Date.now();\n this._connectionInfo.messageCount++;\n\n // Handle ping messages for keep-alive\n const msg = message as { type?: string };\n if (msg.type === 'ping') {\n this._port.postMessage({ type: 'pong' });\n return;\n }\n\n const mcpMessage = JSONRPCMessageSchema.parse(message);\n this.onmessage?.(mcpMessage);\n } catch (error) {\n this.onerror?.(new Error(`Failed to parse message: ${error}`));\n }\n };\n\n // Set up disconnect handler\n this._disconnectHandler = () => {\n console.log(\n `[UserScriptServerTransport] Client disconnected after ${Date.now() - this._connectionInfo.connectedAt}ms, processed ${this._connectionInfo.messageCount} messages`\n );\n this._cleanup();\n this.onclose?.();\n };\n\n this._port.onMessage.addListener(this._messageHandler);\n this._port.onDisconnect.addListener(this._disconnectHandler);\n\n // Start keep-alive mechanism if enabled\n if (this._options.keepAlive) {\n this._startKeepAlive();\n }\n\n console.log(\n `[UserScriptServerTransport] Started with client: ${this._port.sender?.id || 'unknown'}`\n );\n }\n\n /**\n * Sends a message to the client\n */\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n if (!this._started) {\n throw new Error('Transport not started');\n }\n\n if (!this._port) {\n throw new Error('Not connected to client');\n }\n\n try {\n this._port.postMessage(message);\n } catch (error) {\n // Check if the error is due to disconnection\n if (chrome.runtime.lastError || !this._port) {\n this._cleanup();\n this.onclose?.();\n throw new Error('Client disconnected');\n }\n throw new Error(`Failed to send message: ${error}`);\n }\n }\n\n /**\n * Closes the transport\n */\n async close(): Promise<void> {\n this._started = false;\n\n if (this._port) {\n try {\n this._port.disconnect();\n } catch (_error) {\n // Port might already be disconnected\n }\n }\n\n this._cleanup();\n this.onclose?.();\n }\n\n /**\n * Cleans up event listeners and references\n */\n private _cleanup(): void {\n // Stop keep-alive timer\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n\n if (this._port) {\n if (this._messageHandler) {\n this._port.onMessage.removeListener(this._messageHandler);\n }\n if (this._disconnectHandler) {\n this._port.onDisconnect.removeListener(this._disconnectHandler);\n }\n }\n }\n\n /**\n * Starts the keep-alive mechanism\n */\n private _startKeepAlive(): void {\n if (this._keepAliveTimer) {\n return;\n }\n\n console.log(\n `[UserScriptServerTransport] Starting keep-alive with ${this._options.keepAliveInterval}ms interval`\n );\n\n this._keepAliveTimer = setInterval(() => {\n if (!this._port) {\n this._stopKeepAlive();\n return;\n }\n\n try {\n // Send a keep-alive ping\n this._port.postMessage({ type: 'keep-alive', timestamp: Date.now() });\n } catch (error) {\n console.error('[UserScriptServerTransport] Keep-alive failed:', error);\n this._stopKeepAlive();\n }\n }, this._options.keepAliveInterval!) as unknown as number;\n }\n\n /**\n * Stops the keep-alive mechanism\n */\n private _stopKeepAlive(): void {\n if (this._keepAliveTimer !== undefined) {\n clearInterval(this._keepAliveTimer);\n this._keepAliveTimer = undefined;\n }\n }\n\n /**\n * Gets connection information\n */\n getConnectionInfo() {\n return {\n ...this._connectionInfo,\n uptime: Date.now() - this._connectionInfo.connectedAt,\n isConnected: !!this._port && this._started,\n };\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/transports",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Browser transport implementations for Model Context Protocol (MCP) - postMessage, Chrome extension messaging, and iframe communication for AI agents and LLMs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"dist"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"zod": "3.
|
|
59
|
+
"zod": "4.3.5",
|
|
60
|
+
"@mcp-b/webmcp-ts-sdk": "1.1.0"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@types/chrome": "^0.0.326",
|
|
@@ -64,14 +65,6 @@
|
|
|
64
65
|
"tsdown": "^0.15.10",
|
|
65
66
|
"typescript": "^5.8.3"
|
|
66
67
|
},
|
|
67
|
-
"peerDependencies": {
|
|
68
|
-
"@modelcontextprotocol/sdk": "^1.15.0"
|
|
69
|
-
},
|
|
70
|
-
"peerDependenciesMeta": {
|
|
71
|
-
"@modelcontextprotocol/sdk": {
|
|
72
|
-
"optional": false
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
68
|
"publishConfig": {
|
|
76
69
|
"access": "public",
|
|
77
70
|
"registry": "https://registry.npmjs.org/"
|