@casys/mcp-bridge 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/_dnt.shims.d.ts +2 -0
- package/esm/_dnt.shims.d.ts.map +1 -0
- package/esm/_dnt.shims.js +57 -0
- package/esm/adapters/base-adapter.d.ts +25 -0
- package/esm/adapters/base-adapter.d.ts.map +1 -0
- package/esm/adapters/base-adapter.js +86 -0
- package/esm/adapters/line/adapter.d.ts +11 -0
- package/esm/adapters/line/adapter.d.ts.map +1 -0
- package/esm/adapters/line/adapter.js +10 -0
- package/esm/adapters/line/types.d.ts +25 -0
- package/esm/adapters/line/types.d.ts.map +1 -0
- package/esm/adapters/line/types.js +4 -0
- package/esm/adapters/telegram/adapter.d.ts +11 -0
- package/esm/adapters/telegram/adapter.d.ts.map +1 -0
- package/esm/adapters/telegram/adapter.js +10 -0
- package/esm/adapters/telegram/platform-adapter.d.ts +40 -0
- package/esm/adapters/telegram/platform-adapter.d.ts.map +1 -0
- package/esm/adapters/telegram/platform-adapter.js +214 -0
- package/esm/adapters/telegram/sdk-bridge.d.ts +8 -0
- package/esm/adapters/telegram/sdk-bridge.d.ts.map +1 -0
- package/esm/adapters/telegram/sdk-bridge.js +22 -0
- package/esm/adapters/telegram/types.d.ts +93 -0
- package/esm/adapters/telegram/types.d.ts.map +1 -0
- package/esm/adapters/telegram/types.js +6 -0
- package/esm/client/bridge.js +424 -0
- package/esm/core/adapter.d.ts +88 -0
- package/esm/core/adapter.d.ts.map +1 -0
- package/esm/core/adapter.js +10 -0
- package/esm/core/bridge-client.d.ts +77 -0
- package/esm/core/bridge-client.d.ts.map +1 -0
- package/esm/core/bridge-client.js +275 -0
- package/esm/core/message-router.d.ts +71 -0
- package/esm/core/message-router.d.ts.map +1 -0
- package/esm/core/message-router.js +187 -0
- package/esm/core/protocol.d.ts +116 -0
- package/esm/core/protocol.d.ts.map +1 -0
- package/esm/core/protocol.js +203 -0
- package/esm/core/resource-resolver.d.ts +27 -0
- package/esm/core/resource-resolver.d.ts.map +1 -0
- package/esm/core/resource-resolver.js +85 -0
- package/esm/core/transport.d.ts +46 -0
- package/esm/core/transport.d.ts.map +1 -0
- package/esm/core/transport.js +85 -0
- package/esm/core/types.d.ts +187 -0
- package/esm/core/types.d.ts.map +1 -0
- package/esm/core/types.js +35 -0
- package/esm/mod.d.ts +36 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +33 -0
- package/esm/package.json +3 -0
- package/esm/resource-server/csp.d.ts +36 -0
- package/esm/resource-server/csp.d.ts.map +1 -0
- package/esm/resource-server/csp.js +36 -0
- package/esm/resource-server/injector.d.ts +18 -0
- package/esm/resource-server/injector.d.ts.map +1 -0
- package/esm/resource-server/injector.js +39 -0
- package/esm/resource-server/server.d.ts +107 -0
- package/esm/resource-server/server.d.ts.map +1 -0
- package/esm/resource-server/server.js +483 -0
- package/esm/resource-server/session.d.ts +60 -0
- package/esm/resource-server/session.d.ts.map +1 -0
- package/esm/resource-server/session.js +86 -0
- package/esm/resource-server/telegram-auth.d.ts +45 -0
- package/esm/resource-server/telegram-auth.d.ts.map +1 -0
- package/esm/resource-server/telegram-auth.js +161 -0
- package/package.json +31 -0
- package/script/_dnt.shims.d.ts +2 -0
- package/script/_dnt.shims.d.ts.map +1 -0
- package/script/_dnt.shims.js +60 -0
- package/script/adapters/base-adapter.d.ts +25 -0
- package/script/adapters/base-adapter.d.ts.map +1 -0
- package/script/adapters/base-adapter.js +113 -0
- package/script/adapters/line/adapter.d.ts +11 -0
- package/script/adapters/line/adapter.d.ts.map +1 -0
- package/script/adapters/line/adapter.js +14 -0
- package/script/adapters/line/types.d.ts +25 -0
- package/script/adapters/line/types.d.ts.map +1 -0
- package/script/adapters/line/types.js +5 -0
- package/script/adapters/telegram/adapter.d.ts +11 -0
- package/script/adapters/telegram/adapter.d.ts.map +1 -0
- package/script/adapters/telegram/adapter.js +14 -0
- package/script/adapters/telegram/platform-adapter.d.ts +40 -0
- package/script/adapters/telegram/platform-adapter.d.ts.map +1 -0
- package/script/adapters/telegram/platform-adapter.js +241 -0
- package/script/adapters/telegram/sdk-bridge.d.ts +8 -0
- package/script/adapters/telegram/sdk-bridge.d.ts.map +1 -0
- package/script/adapters/telegram/sdk-bridge.js +48 -0
- package/script/adapters/telegram/types.d.ts +93 -0
- package/script/adapters/telegram/types.d.ts.map +1 -0
- package/script/adapters/telegram/types.js +7 -0
- package/script/client/bridge.js +424 -0
- package/script/core/adapter.d.ts +88 -0
- package/script/core/adapter.d.ts.map +1 -0
- package/script/core/adapter.js +11 -0
- package/script/core/bridge-client.d.ts +77 -0
- package/script/core/bridge-client.d.ts.map +1 -0
- package/script/core/bridge-client.js +302 -0
- package/script/core/message-router.d.ts +71 -0
- package/script/core/message-router.d.ts.map +1 -0
- package/script/core/message-router.js +191 -0
- package/script/core/protocol.d.ts +116 -0
- package/script/core/protocol.d.ts.map +1 -0
- package/script/core/protocol.js +230 -0
- package/script/core/resource-resolver.d.ts +27 -0
- package/script/core/resource-resolver.d.ts.map +1 -0
- package/script/core/resource-resolver.js +89 -0
- package/script/core/transport.d.ts +46 -0
- package/script/core/transport.d.ts.map +1 -0
- package/script/core/transport.js +112 -0
- package/script/core/types.d.ts +187 -0
- package/script/core/types.d.ts.map +1 -0
- package/script/core/types.js +38 -0
- package/script/mod.d.ts +36 -0
- package/script/mod.d.ts.map +1 -0
- package/script/mod.js +76 -0
- package/script/package.json +3 -0
- package/script/resource-server/csp.d.ts +36 -0
- package/script/resource-server/csp.d.ts.map +1 -0
- package/script/resource-server/csp.js +39 -0
- package/script/resource-server/injector.d.ts +18 -0
- package/script/resource-server/injector.d.ts.map +1 -0
- package/script/resource-server/injector.js +42 -0
- package/script/resource-server/server.d.ts +107 -0
- package/script/resource-server/server.d.ts.map +1 -0
- package/script/resource-server/server.js +487 -0
- package/script/resource-server/session.d.ts +60 -0
- package/script/resource-server/session.d.ts.map +1 -0
- package/script/resource-server/session.js +90 -0
- package/script/resource-server/telegram-auth.d.ts +45 -0
- package/script/resource-server/telegram-auth.d.ts.map +1 -0
- package/script/resource-server/telegram-auth.js +164 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Apps Bridge — Client-side runtime.
|
|
3
|
+
*
|
|
4
|
+
* Injected by the resource server into MCP App HTML pages.
|
|
5
|
+
* Intercepts postMessage calls from the MCP App class, routes them
|
|
6
|
+
* to the resource server via WebSocket, and dispatches responses
|
|
7
|
+
* back as MessageEvents.
|
|
8
|
+
*
|
|
9
|
+
* Query parameters (set by the resource server on the <script> tag):
|
|
10
|
+
* - platform: "telegram" | "line"
|
|
11
|
+
* - session: session ID (created by the resource server)
|
|
12
|
+
*
|
|
13
|
+
* @module bridge.js
|
|
14
|
+
*/
|
|
15
|
+
(function () {
|
|
16
|
+
"use strict";
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Read configuration from script tag query params
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
var currentScript = document.currentScript;
|
|
23
|
+
if (!currentScript) {
|
|
24
|
+
console.error("[bridge.js] Cannot find own <script> element.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var scriptUrl = new URL(currentScript.src);
|
|
29
|
+
var PLATFORM = scriptUrl.searchParams.get("platform") || "unknown";
|
|
30
|
+
var SESSION_ID = scriptUrl.searchParams.get("session");
|
|
31
|
+
if (!SESSION_ID) {
|
|
32
|
+
console.error("[bridge.js] Missing 'session' query parameter.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var WS_BASE = scriptUrl.origin;
|
|
37
|
+
var WS_URL = WS_BASE.replace(/^http/, "ws") + "/bridge?session=" + SESSION_ID;
|
|
38
|
+
var DEBUG = scriptUrl.searchParams.get("debug") === "1";
|
|
39
|
+
|
|
40
|
+
function log() {
|
|
41
|
+
if (DEBUG) {
|
|
42
|
+
console.log.apply(console, ["[bridge.js]"].concat(Array.prototype.slice.call(arguments)));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
log("Platform:", PLATFORM, "Session:", SESSION_ID);
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// JSON-RPC helpers
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
function isJsonRpc(msg) {
|
|
53
|
+
return msg && typeof msg === "object" && msg.jsonrpc === "2.0";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isRequest(msg) {
|
|
57
|
+
return "method" in msg && "id" in msg;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isResponse(msg) {
|
|
61
|
+
return ("result" in msg || "error" in msg) && "id" in msg;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Platform detection: Telegram theme
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
function getTelegramTheme() {
|
|
69
|
+
var tg = window.Telegram && window.Telegram.WebApp;
|
|
70
|
+
if (!tg) {
|
|
71
|
+
return {
|
|
72
|
+
theme: "light",
|
|
73
|
+
styles: { variables: {} },
|
|
74
|
+
platform: "mobile",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var isDark = tg.colorScheme === "dark";
|
|
79
|
+
var tp = tg.themeParams || {};
|
|
80
|
+
var variables = {};
|
|
81
|
+
var keys = Object.keys(tp);
|
|
82
|
+
for (var i = 0; i < keys.length; i++) {
|
|
83
|
+
variables["--tg-" + keys[i].replace(/_/g, "-")] = tp[keys[i]];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
theme: isDark ? "dark" : "light",
|
|
88
|
+
styles: { variables: variables },
|
|
89
|
+
platform: "mobile",
|
|
90
|
+
locale: tg.initDataUnsafe && tg.initDataUnsafe.user
|
|
91
|
+
? tg.initDataUnsafe.user.language_code
|
|
92
|
+
: undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Request tracking (for matching responses to outgoing requests)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
var pendingRequests = {}; // id -> { resolve, reject, timer }
|
|
101
|
+
var REQUEST_TIMEOUT_MS = 30000;
|
|
102
|
+
var appInitialized = false;
|
|
103
|
+
var earlyNotifications = []; // notifications received before ui/initialize
|
|
104
|
+
|
|
105
|
+
function trackRequest(id) {
|
|
106
|
+
return new Promise(function (resolve, reject) {
|
|
107
|
+
var timer = setTimeout(function () {
|
|
108
|
+
delete pendingRequests[id];
|
|
109
|
+
reject(new Error("Request " + id + " timed out after " + REQUEST_TIMEOUT_MS + "ms"));
|
|
110
|
+
}, REQUEST_TIMEOUT_MS);
|
|
111
|
+
|
|
112
|
+
pendingRequests[id] = { resolve: resolve, reject: reject, timer: timer };
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveRequest(id, result) {
|
|
117
|
+
var entry = pendingRequests[id];
|
|
118
|
+
if (entry) {
|
|
119
|
+
clearTimeout(entry.timer);
|
|
120
|
+
delete pendingRequests[id];
|
|
121
|
+
entry.resolve(result);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function rejectRequest(id, error) {
|
|
126
|
+
var entry = pendingRequests[id];
|
|
127
|
+
if (entry) {
|
|
128
|
+
clearTimeout(entry.timer);
|
|
129
|
+
delete pendingRequests[id];
|
|
130
|
+
entry.reject(error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// ui/initialize handler (synthesized locally)
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
function handleInitialize(requestId) {
|
|
139
|
+
var hostCtx = getTelegramTheme();
|
|
140
|
+
|
|
141
|
+
var response = {
|
|
142
|
+
jsonrpc: "2.0",
|
|
143
|
+
id: requestId,
|
|
144
|
+
result: {
|
|
145
|
+
protocolVersion: "2025-11-25",
|
|
146
|
+
hostInfo: {
|
|
147
|
+
name: "@casys/mcp-apps-bridge",
|
|
148
|
+
version: "0.1.0",
|
|
149
|
+
},
|
|
150
|
+
hostCapabilities: {
|
|
151
|
+
serverTools: { listChanged: false },
|
|
152
|
+
serverResources: { listChanged: false },
|
|
153
|
+
logging: {},
|
|
154
|
+
openLinks: {},
|
|
155
|
+
},
|
|
156
|
+
hostContext: hostCtx,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
dispatchToApp(response);
|
|
161
|
+
|
|
162
|
+
// App is now ready — flush any notifications received before init
|
|
163
|
+
appInitialized = true;
|
|
164
|
+
if (earlyNotifications.length > 0) {
|
|
165
|
+
log("Flushing", earlyNotifications.length, "early notification(s)");
|
|
166
|
+
for (var i = 0; i < earlyNotifications.length; i++) {
|
|
167
|
+
dispatchToApp(earlyNotifications[i]);
|
|
168
|
+
}
|
|
169
|
+
earlyNotifications = [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Dispatch to App (via MessageEvent on window)
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
function dispatchToApp(message) {
|
|
178
|
+
log("-> App:", message.method || ("id" in message ? "response#" + message.id : "?"));
|
|
179
|
+
// Use real postMessage so event.source === window (=== window.parent in standalone).
|
|
180
|
+
// The MCP Apps SDK checks event.source — dispatchEvent leaves it null.
|
|
181
|
+
// Use the script origin as targetOrigin instead of "*" to prevent message leaking.
|
|
182
|
+
_realPostMessage(message, scriptUrl.origin);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// WebSocket connection
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
var ws = null;
|
|
190
|
+
var reconnectAttempts = 0;
|
|
191
|
+
var MAX_RECONNECT = 5;
|
|
192
|
+
var authenticated = false;
|
|
193
|
+
|
|
194
|
+
function connectWs() {
|
|
195
|
+
log("Connecting to", WS_URL);
|
|
196
|
+
ws = new WebSocket(WS_URL);
|
|
197
|
+
|
|
198
|
+
ws.onopen = function () {
|
|
199
|
+
log("WebSocket connected");
|
|
200
|
+
reconnectAttempts = 0;
|
|
201
|
+
|
|
202
|
+
// Send auth immediately if Telegram SDK is available
|
|
203
|
+
var tg = window.Telegram && window.Telegram.WebApp;
|
|
204
|
+
if (tg && tg.initData) {
|
|
205
|
+
log("Sending Telegram auth...");
|
|
206
|
+
ws.send(JSON.stringify({ type: "auth", initData: tg.initData }));
|
|
207
|
+
} else {
|
|
208
|
+
// No Telegram SDK — server will decide if auth is required
|
|
209
|
+
log("No Telegram SDK, dispatching ready without auth");
|
|
210
|
+
authenticated = true;
|
|
211
|
+
window.dispatchEvent(new CustomEvent("mcp-bridge-ready", {
|
|
212
|
+
detail: { platform: PLATFORM, session: SESSION_ID },
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
ws.onmessage = function (event) {
|
|
218
|
+
try {
|
|
219
|
+
var msg = JSON.parse(event.data);
|
|
220
|
+
// Handle auth responses (non-JSON-RPC)
|
|
221
|
+
if (msg && msg.type === "auth_ok") {
|
|
222
|
+
log("Authenticated, userId:", msg.userId);
|
|
223
|
+
authenticated = true;
|
|
224
|
+
window.dispatchEvent(new CustomEvent("mcp-bridge-ready", {
|
|
225
|
+
detail: { platform: PLATFORM, session: SESSION_ID, userId: msg.userId },
|
|
226
|
+
}));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (msg && msg.type === "auth_error") {
|
|
231
|
+
console.error("[bridge.js] Authentication failed:", msg.error);
|
|
232
|
+
window.dispatchEvent(new CustomEvent("mcp-bridge-auth-error", {
|
|
233
|
+
detail: { error: msg.error },
|
|
234
|
+
}));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!isJsonRpc(msg)) return;
|
|
239
|
+
|
|
240
|
+
// Responses: resolve tracked requests
|
|
241
|
+
if (isResponse(msg)) {
|
|
242
|
+
if ("error" in msg) {
|
|
243
|
+
rejectRequest(msg.id, msg.error);
|
|
244
|
+
} else {
|
|
245
|
+
resolveRequest(msg.id, msg.result);
|
|
246
|
+
}
|
|
247
|
+
// The trackRequest().then() will call dispatchToApp
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Notifications from server
|
|
252
|
+
if ("method" in msg && !("id" in msg)) {
|
|
253
|
+
if (!appInitialized) {
|
|
254
|
+
log("Queuing early notification:", msg.method);
|
|
255
|
+
earlyNotifications.push(msg);
|
|
256
|
+
} else {
|
|
257
|
+
dispatchToApp(msg);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch (err) {
|
|
261
|
+
console.warn("[bridge.js] Failed to parse WS message:", err);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
ws.onclose = function () {
|
|
266
|
+
log("WebSocket disconnected");
|
|
267
|
+
ws = null;
|
|
268
|
+
if (reconnectAttempts < MAX_RECONNECT) {
|
|
269
|
+
reconnectAttempts++;
|
|
270
|
+
var delay = Math.min(1000 * Math.pow(2, reconnectAttempts - 1), 10000);
|
|
271
|
+
log("Reconnecting in", delay, "ms (attempt", reconnectAttempts + ")");
|
|
272
|
+
setTimeout(connectWs, delay);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
ws.onerror = function () {
|
|
277
|
+
log("WebSocket error");
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function sendToServer(message) {
|
|
282
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
283
|
+
console.warn("[bridge.js] WebSocket not connected. Dropping message:", message);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (!authenticated) {
|
|
287
|
+
console.warn("[bridge.js] Not authenticated yet. Dropping message:", message);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
ws.send(JSON.stringify(message));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// Intercept postMessage (App -> Bridge)
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
// Save the REAL postMessage before we monkey-patch it.
|
|
298
|
+
// Used by dispatchToApp() so that event.source === window,
|
|
299
|
+
// which the MCP Apps SDK checks (event.source === window.parent).
|
|
300
|
+
var _realPostMessage = window.postMessage.bind(window);
|
|
301
|
+
|
|
302
|
+
var originalPostMessage = window.parent !== window
|
|
303
|
+
? window.parent.postMessage.bind(window.parent)
|
|
304
|
+
: null;
|
|
305
|
+
|
|
306
|
+
function interceptPostMessage() {
|
|
307
|
+
if (window.parent === window) {
|
|
308
|
+
// Not in a frame — intercept window.postMessage instead for standalone mode
|
|
309
|
+
log("Intercepting window.postMessage (standalone mode)");
|
|
310
|
+
var origSelf = window.postMessage.bind(window);
|
|
311
|
+
window.postMessage = function (message, targetOrigin, transfer) {
|
|
312
|
+
if (isJsonRpc(message)) {
|
|
313
|
+
handleOutgoing(message);
|
|
314
|
+
} else {
|
|
315
|
+
origSelf(message, targetOrigin, transfer);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
log("Intercepting window.parent.postMessage (iframe mode)");
|
|
322
|
+
window.parent.postMessage = function (message, targetOrigin, transfer) {
|
|
323
|
+
if (isJsonRpc(message)) {
|
|
324
|
+
handleOutgoing(message);
|
|
325
|
+
} else if (originalPostMessage) {
|
|
326
|
+
originalPostMessage(message, targetOrigin, transfer);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function handleOutgoing(message) {
|
|
332
|
+
log("<- App:", message.method || "response");
|
|
333
|
+
|
|
334
|
+
// ui/initialize — handle locally
|
|
335
|
+
if (message.method === "ui/initialize" && "id" in message) {
|
|
336
|
+
handleInitialize(message.id);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ui/open-link — handle locally via Telegram API or fallback
|
|
341
|
+
if (message.method === "ui/open-link" && "id" in message) {
|
|
342
|
+
var url = message.params && message.params.url;
|
|
343
|
+
if (url) {
|
|
344
|
+
var tg = window.Telegram && window.Telegram.WebApp;
|
|
345
|
+
if (tg && tg.openLink) {
|
|
346
|
+
tg.openLink(url);
|
|
347
|
+
} else {
|
|
348
|
+
window.open(url, "_blank");
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
dispatchToApp({ jsonrpc: "2.0", id: message.id, result: {} });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Forward to resource server via WebSocket
|
|
356
|
+
sendToServer(message);
|
|
357
|
+
|
|
358
|
+
// Track requests for response matching
|
|
359
|
+
if (isRequest(message)) {
|
|
360
|
+
trackRequest(message.id)
|
|
361
|
+
.then(function (result) {
|
|
362
|
+
dispatchToApp({ jsonrpc: "2.0", id: message.id, result: result });
|
|
363
|
+
})
|
|
364
|
+
.catch(function (err) {
|
|
365
|
+
dispatchToApp({
|
|
366
|
+
jsonrpc: "2.0",
|
|
367
|
+
id: message.id,
|
|
368
|
+
error: { code: -32603, message: err.message || String(err) },
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---------------------------------------------------------------------------
|
|
375
|
+
// Telegram lifecycle events
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
function setupTelegramEvents() {
|
|
379
|
+
var tg = window.Telegram && window.Telegram.WebApp;
|
|
380
|
+
if (!tg) return;
|
|
381
|
+
|
|
382
|
+
// Tell Telegram we're ready
|
|
383
|
+
tg.ready();
|
|
384
|
+
if (!tg.isExpanded) tg.expand();
|
|
385
|
+
|
|
386
|
+
// Theme changes
|
|
387
|
+
if (typeof tg.onEvent === "function") {
|
|
388
|
+
tg.onEvent("themeChanged", function () {
|
|
389
|
+
var ctx = getTelegramTheme();
|
|
390
|
+
dispatchToApp({
|
|
391
|
+
jsonrpc: "2.0",
|
|
392
|
+
method: "ui/notifications/host-context-changed",
|
|
393
|
+
params: { theme: ctx.theme, styles: ctx.styles },
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
tg.onEvent("viewportChanged", function () {
|
|
398
|
+
dispatchToApp({
|
|
399
|
+
jsonrpc: "2.0",
|
|
400
|
+
method: "ui/notifications/host-context-changed",
|
|
401
|
+
params: {
|
|
402
|
+
containerDimensions: {
|
|
403
|
+
width: window.innerWidth,
|
|
404
|
+
height: tg.viewportStableHeight || window.innerHeight,
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// Boot
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
|
|
416
|
+
interceptPostMessage();
|
|
417
|
+
connectWs();
|
|
418
|
+
|
|
419
|
+
if (PLATFORM === "telegram") {
|
|
420
|
+
setupTelegramEvents();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
log("Bridge initialized for", PLATFORM);
|
|
424
|
+
})();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract adapter interfaces for platform integrations.
|
|
3
|
+
*
|
|
4
|
+
* Two adapter levels:
|
|
5
|
+
* - `McpAppsAdapter`: Low-level message transport (postMessage replacement).
|
|
6
|
+
* - `PlatformAdapter`: High-level platform SDK abstraction (theme, viewport,
|
|
7
|
+
* native UI, auth). Used by BridgeClient to synthesize HostContext and
|
|
8
|
+
* translate platform events into MCP notifications.
|
|
9
|
+
*/
|
|
10
|
+
import type { AdapterConfig, ContainerDimensions, HostContext, LifecycleEvent, McpAppsMessage } from "./types.js";
|
|
11
|
+
/** Handler function invoked when the host sends a message to the MCP App. */
|
|
12
|
+
export type MessageHandler = (message: McpAppsMessage) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Low-level transport adapter contract.
|
|
15
|
+
*
|
|
16
|
+
* Replaces the iframe postMessage channel with a platform-specific
|
|
17
|
+
* communication mechanism. Each platform implements this to bridge
|
|
18
|
+
* JSON-RPC messages between the MCP App and the resource server.
|
|
19
|
+
*
|
|
20
|
+
* Lifecycle: `init()` -> use -> `destroy()`.
|
|
21
|
+
*/
|
|
22
|
+
export interface McpAppsAdapter {
|
|
23
|
+
/** Unique platform identifier (e.g. `"telegram"`, `"line"`). */
|
|
24
|
+
readonly platform: string;
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the adapter with platform-specific configuration.
|
|
27
|
+
* Must be called before any other method.
|
|
28
|
+
*/
|
|
29
|
+
init(config: AdapterConfig): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Send a JSON-RPC message from the MCP App to the host.
|
|
32
|
+
* Throws if the adapter has not been initialized.
|
|
33
|
+
*/
|
|
34
|
+
sendToHost(message: McpAppsMessage): void;
|
|
35
|
+
/**
|
|
36
|
+
* Register a handler that is called when the host sends a message
|
|
37
|
+
* to the MCP App.
|
|
38
|
+
*/
|
|
39
|
+
onMessageFromHost(handler: MessageHandler): void;
|
|
40
|
+
/**
|
|
41
|
+
* Tear down the adapter, releasing all resources and listeners.
|
|
42
|
+
* After calling `destroy()`, the adapter must not be reused.
|
|
43
|
+
*/
|
|
44
|
+
destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
/** Handler for platform lifecycle events. */
|
|
47
|
+
export type LifecycleEventHandler = (event: LifecycleEvent) => void;
|
|
48
|
+
/**
|
|
49
|
+
* High-level platform adapter contract.
|
|
50
|
+
*
|
|
51
|
+
* Abstracts platform-specific SDK features (theme, viewport, native UI,
|
|
52
|
+
* auth) into a uniform interface that the BridgeClient uses to:
|
|
53
|
+
* - Build the initial `HostContext` for `ui/initialize`
|
|
54
|
+
* - Map platform events to `ui/notifications/host-context-changed`
|
|
55
|
+
* - Execute platform-specific actions (open link, expand viewport)
|
|
56
|
+
*/
|
|
57
|
+
export interface PlatformAdapter {
|
|
58
|
+
/** Platform name (e.g. `"telegram"`, `"line"`). */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/**
|
|
61
|
+
* Initialize the platform SDK and return the initial host context.
|
|
62
|
+
* This is called once during BridgeClient startup.
|
|
63
|
+
*/
|
|
64
|
+
initialize(): Promise<HostContext>;
|
|
65
|
+
/** Get the current theme from the platform. */
|
|
66
|
+
getTheme(): "light" | "dark";
|
|
67
|
+
/** Get the current container dimensions from the platform. */
|
|
68
|
+
getContainerDimensions(): ContainerDimensions;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to platform lifecycle events (theme change, viewport
|
|
71
|
+
* resize, activation, teardown).
|
|
72
|
+
*/
|
|
73
|
+
onLifecycleEvent(handler: LifecycleEventHandler): void;
|
|
74
|
+
/** Open an external link using the platform's native method. */
|
|
75
|
+
openLink(url: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Send a message via the platform's native messaging (if supported).
|
|
78
|
+
*
|
|
79
|
+
* WARNING (Telegram): `sendData()` closes the Mini App. Use with caution.
|
|
80
|
+
*/
|
|
81
|
+
sendMessage?(text: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Get platform-specific auth data for forwarding to the MCP server.
|
|
84
|
+
* E.g. Telegram `initData`, LIFF access token.
|
|
85
|
+
*/
|
|
86
|
+
getAuthData?(): Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/core/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AAMpB,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3C;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAE1C;;;OAGG;IACH,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAEjD;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,6CAA6C;AAC7C,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAEnC,+CAA+C;IAC/C,QAAQ,IAAI,OAAO,GAAG,MAAM,CAAC;IAE7B,8DAA8D;IAC9D,sBAAsB,IAAI,mBAAmB,CAAC;IAE9C;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAEvD,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC;;;;OAIG;IACH,WAAW,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,WAAW,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract adapter interfaces for platform integrations.
|
|
3
|
+
*
|
|
4
|
+
* Two adapter levels:
|
|
5
|
+
* - `McpAppsAdapter`: Low-level message transport (postMessage replacement).
|
|
6
|
+
* - `PlatformAdapter`: High-level platform SDK abstraction (theme, viewport,
|
|
7
|
+
* native UI, auth). Used by BridgeClient to synthesize HostContext and
|
|
8
|
+
* translate platform events into MCP notifications.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PlatformAdapter } from "./adapter.js";
|
|
2
|
+
import type { BridgeTransport } from "./transport.js";
|
|
3
|
+
import type { HostContext } from "./types.js";
|
|
4
|
+
/** Options for creating a BridgeClient. */
|
|
5
|
+
export interface BridgeClientOptions {
|
|
6
|
+
/** WebSocket URL of the resource server bridge endpoint. */
|
|
7
|
+
readonly serverUrl: string;
|
|
8
|
+
/** Platform adapter instance (Telegram, LINE, etc.). */
|
|
9
|
+
readonly platform: PlatformAdapter;
|
|
10
|
+
/** Transport implementation. Defaults to WebSocketTransport if not provided. */
|
|
11
|
+
readonly transport: BridgeTransport;
|
|
12
|
+
/** Session ID assigned by the resource server. */
|
|
13
|
+
readonly sessionId: string;
|
|
14
|
+
/** Bridge name/version reported in ui/initialize. */
|
|
15
|
+
readonly bridgeInfo?: {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly version: string;
|
|
18
|
+
};
|
|
19
|
+
/** Request timeout in ms. Defaults to 30_000. */
|
|
20
|
+
readonly requestTimeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The MCP Apps Bridge client.
|
|
24
|
+
*
|
|
25
|
+
* Injected into the MCP App HTML by the resource server (`bridge.js`).
|
|
26
|
+
* Intercepts postMessage, routes via WebSocket, translates platform events.
|
|
27
|
+
*/
|
|
28
|
+
export declare class BridgeClient {
|
|
29
|
+
private readonly platform;
|
|
30
|
+
private readonly transport;
|
|
31
|
+
private readonly router;
|
|
32
|
+
private readonly options;
|
|
33
|
+
private hostContext;
|
|
34
|
+
private started;
|
|
35
|
+
private originalPostMessage;
|
|
36
|
+
constructor(options: BridgeClientOptions);
|
|
37
|
+
/**
|
|
38
|
+
* Start the bridge:
|
|
39
|
+
* 1. Initialize the platform adapter to get HostContext
|
|
40
|
+
* 2. Connect transport to resource server
|
|
41
|
+
* 3. Intercept postMessage from App class
|
|
42
|
+
* 4. Listen for platform lifecycle events
|
|
43
|
+
* 5. Register ui/initialize handler to synthesize response
|
|
44
|
+
*/
|
|
45
|
+
start(): Promise<void>;
|
|
46
|
+
/** Stop the bridge, restore postMessage, disconnect transport. */
|
|
47
|
+
destroy(): void;
|
|
48
|
+
/** Whether the bridge is currently running. */
|
|
49
|
+
get isStarted(): boolean;
|
|
50
|
+
/** The current host context (null before start). */
|
|
51
|
+
get currentHostContext(): HostContext | null;
|
|
52
|
+
private interceptPostMessage;
|
|
53
|
+
private restorePostMessage;
|
|
54
|
+
/**
|
|
55
|
+
* Handle an outgoing message from the MCP App (intercepted from postMessage).
|
|
56
|
+
*
|
|
57
|
+
* Requests that the bridge handles locally (ui/initialize, ui/open-link)
|
|
58
|
+
* are dispatched to the router. All other messages are forwarded to the
|
|
59
|
+
* resource server via transport.
|
|
60
|
+
*/
|
|
61
|
+
private handleOutgoingMessage;
|
|
62
|
+
/**
|
|
63
|
+
* Handle an incoming message from the resource server.
|
|
64
|
+
*
|
|
65
|
+
* Responses are routed to the pending request tracker (which resolves the
|
|
66
|
+
* Promise created in handleOutgoingMessage — that Promise already calls
|
|
67
|
+
* dispatchToApp, so we must NOT dispatch responses again here).
|
|
68
|
+
*
|
|
69
|
+
* Notifications are dispatched directly to the App class.
|
|
70
|
+
*/
|
|
71
|
+
private handleIncomingMessage;
|
|
72
|
+
/** Dispatch a message to the MCP App class as a MessageEvent. */
|
|
73
|
+
private dispatchToApp;
|
|
74
|
+
private handleInitialize;
|
|
75
|
+
private handleLifecycleEvent;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=bridge-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-client.d.ts","sourceRoot":"","sources":["../../src/core/bridge-client.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAEV,WAAW,EAIZ,MAAM,YAAY,CAAC;AAGpB,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,iDAAiD;IACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAE9C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,OAAO,CAAS;IAGxB,OAAO,CAAC,mBAAmB,CAA0F;gBAEzG,OAAO,EAAE,mBAAmB;IAOxC;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C5B,kEAAkE;IAClE,OAAO,IAAI,IAAI;IAOf,+CAA+C;IAC/C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,oDAAoD;IACpD,IAAI,kBAAkB,IAAI,WAAW,GAAG,IAAI,CAE3C;IAMD,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,kBAAkB;IAe1B;;;;;;OAMG;YACW,qBAAqB;IAiDnC;;;;;;;;OAQG;YACW,qBAAqB;IAenC,iEAAiE;IACjE,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,gBAAgB;IAoCxB,OAAO,CAAC,oBAAoB;CA0C7B"}
|