@abtnode/blocklet-services 1.16.52-beta-20250916-025146-35d976f4 → 1.16.52-beta-20250917-070942-7ee57044

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/api/routes/mcp.js CHANGED
@@ -4,13 +4,27 @@ const get = require('lodash/get');
4
4
  const getBlockletInfo = require('@blocklet/meta/lib/info');
5
5
  const { checkPublicAccess } = require('@blocklet/meta/lib/util');
6
6
  // eslint-disable-next-line import/no-unresolved
7
- const { StreamableHTTPServerTransport } = require('@blocklet/mcp/server/streamableHttp.js');
7
+ const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
8
+ // eslint-disable-next-line import/no-unresolved
9
+ const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
10
+ // eslint-disable-next-line import/no-unresolved
11
+ const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
12
+ const { z } = require('zod');
13
+ const flatten = require('lodash/flatten');
14
+ const { version } = require('../../package.json');
8
15
 
9
16
  const { initMcpServer } = require('../services/mcp/server');
10
17
  const logger = require('../libs/logger')('mcp:server:routes');
11
18
 
12
19
  const isMCPSupported = (b) => get(b.meta, 'capabilities.mcp', false);
13
20
 
21
+ // Zod schema for MCP tools request validation
22
+ const mcpToolsRequestSchema = z.object({
23
+ name: z.string().min(1, 'Tool name is required'),
24
+ componentDid: z.string().min(1, 'Component DID is required'),
25
+ input: z.any(),
26
+ });
27
+
14
28
  const transport = new StreamableHTTPServerTransport({
15
29
  sessionIdGenerator: undefined, // set to undefined for stateless servers
16
30
  });
@@ -130,5 +144,225 @@ module.exports = {
130
144
  }
131
145
  }
132
146
  });
147
+
148
+ server.get(
149
+ joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/mcp/tools'),
150
+ /**
151
+ * @see https://team.arcblock.io/comment/discussions/aPYG3ocSGNIhFHFr5wFcquZx#53f1867b-3439-43c8-b63c-bf53fffcfe8c
152
+ * @param {import('express').Request} req
153
+ * @param {import('express').Response} res
154
+ * @returns
155
+ */
156
+ async (req, res) => {
157
+ if (!req.user) {
158
+ return res.status(401).json({ error: 'Unauthorized' });
159
+ }
160
+ const blocklet = await req.getBlocklet();
161
+ const info = getBlockletInfo(blocklet);
162
+ const mapServers = blocklet.children
163
+ .map((x) => {
164
+ if (isMCPSupported(x)) {
165
+ return {
166
+ name: x.meta.title,
167
+ description: x.meta.description,
168
+ componentDid: x.meta.did,
169
+ appPid: blocklet.appPid,
170
+ endpoint: joinURL(info.appUrl, x.mountPoint, '/mcp'),
171
+ };
172
+ }
173
+
174
+ return null;
175
+ })
176
+ .filter(Boolean);
177
+
178
+ const toolMartixs = await Promise.all(
179
+ mapServers.map(async (x) => {
180
+ const client = new Client({
181
+ name: 'blocklet-service-mcp-client',
182
+ version,
183
+ });
184
+ const currentTransport = new StreamableHTTPClientTransport(new URL(x.endpoint), {
185
+ requestInit: {
186
+ headers: {
187
+ Cookie: req.headers.cookie,
188
+ },
189
+ },
190
+ });
191
+ await client.connect(currentTransport);
192
+ const { tools } = await client.listTools().catch((error) => {
193
+ console.error('error listing tools', error);
194
+ return { tools: [] };
195
+ });
196
+
197
+ return tools.map((tool) => ({
198
+ ...tool,
199
+ appPid: x.appPid,
200
+ componentDid: x.componentDid,
201
+ }));
202
+ })
203
+ );
204
+ const tools = flatten(toolMartixs);
205
+
206
+ return res.send(tools);
207
+ }
208
+ );
209
+
210
+ server.get(
211
+ joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/mcp/tools.js'),
212
+ /**
213
+ *
214
+ * @see https://team.arcblock.io/comment/discussions/aPYG3ocSGNIhFHFr5wFcquZx#53f1867b-3439-43c8-b63c-bf53fffcfe8c
215
+ * @param {import('express').Request} req
216
+ * @param {import('express').Response} res
217
+ * @returns
218
+ */
219
+ async (req, res) => {
220
+ const blocklet = await req.getBlocklet();
221
+ const info = getBlockletInfo(blocklet);
222
+ const toolsApiUrl = joinURL(info.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/mcp/tools');
223
+
224
+ // 定义为 js
225
+ res.type('js');
226
+
227
+ // @note: 这么做的好处是,未登录到登录成功之后,页面没有刷新也能很快地注册 tools
228
+ return res.send(`
229
+ function getCookiesAsObject() {
230
+ const cookieStr = document.cookie;
231
+ if (!cookieStr.trim()) {
232
+ return {};
233
+ }
234
+
235
+ const cookies = {};
236
+ const cookiePairs = cookieStr.split('; ');
237
+
238
+ cookiePairs.forEach(pair => {
239
+ const eqIndex = pair.indexOf('=');
240
+ if (eqIndex === -1) {
241
+ return;
242
+ }
243
+
244
+ const key = pair.substring(0, eqIndex);
245
+ const value = pair.substring(eqIndex + 1);
246
+ const decodedKey = decodeURIComponent(key);
247
+ const decodedValue = decodeURIComponent(value);
248
+ cookies[decodedKey] = decodedValue;
249
+ });
250
+
251
+ return cookies;
252
+ }
253
+
254
+ async function registerTools() {
255
+ try {
256
+ const cookies = getCookiesAsObject();
257
+ if (cookies.login_token) {
258
+ // 发送 GET 请求获取工具列表,携带 cookie
259
+ const response = await fetch('${toolsApiUrl}', {
260
+ method: 'GET',
261
+ credentials: 'include', // 自动携带 cookie
262
+ headers: {
263
+ 'Content-Type': 'application/json'
264
+ }
265
+ });
266
+
267
+ if (response.ok) {
268
+ const tools = await response.json();
269
+ dsBridge.call("arc__registerTools", tools);
270
+ console.info("registerTools success");
271
+ } else {
272
+ console.error("Failed to fetch tools:", response.status, response.statusText);
273
+ dsBridge.call("arc__registerTools", []);
274
+ }
275
+ } else {
276
+ dsBridge.call("arc__registerTools", []);
277
+ }
278
+ } catch (error) {
279
+ console.error("registerTools", error);
280
+ dsBridge.call("arc__registerTools", []);
281
+ }
282
+ }
283
+
284
+ let lastLoginToken = null;
285
+ async function watchCookieChanges() {
286
+ const cookies = getCookiesAsObject();
287
+ const currentLoginToken = cookies.login_token || null;
288
+
289
+ if (currentLoginToken !== lastLoginToken) {
290
+ lastLoginToken = currentLoginToken;
291
+ await registerTools();
292
+ }
293
+ }
294
+
295
+ registerTools();
296
+
297
+ const cookies = getCookiesAsObject();
298
+ lastLoginToken = cookies.login_token || null;
299
+ setInterval(watchCookieChanges, 1000);
300
+ `);
301
+ }
302
+ );
303
+
304
+ server.post(
305
+ joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/mcp/tools'),
306
+ /**
307
+ * @see https://team.arcblock.io/comment/discussions/aPYG3ocSGNIhFHFr5wFcquZx#53f1867b-3439-43c8-b63c-bf53fffcfe8c
308
+ * @param {import('express').Request} req
309
+ * @param {import('express').Response} res
310
+ *
311
+ */
312
+ async (req, res) => {
313
+ if (!req.user) {
314
+ return res.status(401).json({ error: 'Unauthorized' });
315
+ }
316
+
317
+ // Validate request body with Zod
318
+ let name;
319
+ let componentDid;
320
+ let input;
321
+ try {
322
+ const validatedData = await mcpToolsRequestSchema.parseAsync(req.body);
323
+ ({ name, componentDid, input } = validatedData);
324
+ } catch (error) {
325
+ console.error(error);
326
+ if (error instanceof z.ZodError) {
327
+ return res.status(400).json({
328
+ error: error.errors.map((e) => ({
329
+ field: e.path.join('.'),
330
+ message: e.message,
331
+ })),
332
+ });
333
+ }
334
+ return res.status(400).json({ error: error.message });
335
+ }
336
+
337
+ const blocklet = await req.getBlocklet();
338
+ const info = getBlockletInfo(blocklet);
339
+ const mountPoint = blocklet.children.find((x) => {
340
+ return x.meta.did === componentDid && isMCPSupported(x);
341
+ })?.mountPoint;
342
+ if (!mountPoint) {
343
+ return res.status(404).json({ error: `Component not found by ${componentDid}` });
344
+ }
345
+
346
+ const endpoint = joinURL(info.appUrl, mountPoint, '/mcp');
347
+ const client = new Client({
348
+ name: 'blocklet-service-proxy',
349
+ version,
350
+ });
351
+ const currentTransport = new StreamableHTTPClientTransport(new URL(endpoint), {
352
+ requestInit: {
353
+ headers: {
354
+ Cookie: req.headers.cookie,
355
+ },
356
+ },
357
+ });
358
+ await client.connect(currentTransport);
359
+ const output = await client.callTool({
360
+ name,
361
+ arguments: input,
362
+ });
363
+
364
+ return res.send(output);
365
+ }
366
+ );
133
367
  },
134
368
  };
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable import/no-unresolved */
2
2
  const { joinURL } = require('ufo');
3
3
  const { OAUTH_ENDPOINTS, OAUTH_CLIENT_SECRET_TTL, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
4
- const { authorizationHandler } = require('@blocklet/mcp/server/auth/handlers/authorize.js');
5
- const { tokenHandler } = require('@blocklet/mcp/server/auth/handlers/token.js');
6
- const { revocationHandler } = require('@blocklet/mcp/server/auth/handlers/revoke.js');
7
- const { clientRegistrationHandler } = require('@blocklet/mcp/server/auth/handlers/register.js');
4
+ const { authorizationHandler } = require('@modelcontextprotocol/sdk/server/auth/handlers/authorize.js');
5
+ const { tokenHandler } = require('@modelcontextprotocol/sdk/server/auth/handlers/token.js');
6
+ const { revocationHandler } = require('@modelcontextprotocol/sdk/server/auth/handlers/revoke.js');
7
+ const { clientRegistrationHandler } = require('@modelcontextprotocol/sdk/server/auth/handlers/register.js');
8
8
 
9
9
  const { createBlockletOAuthServerProvider } = require('../../services/oauth/server');
10
10
  const { redirectWithoutCache, getRedirectUrl } = require('../../util');
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line import/no-unresolved
2
- const { McpServer } = require('@blocklet/mcp/server/mcp.js');
2
+ const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
3
3
  const path = require('path');
4
4
  const { initDbhubServer } = require('@blocklet/dbhub');
5
5
  const logger = require('../../libs/logger')('mcp:server');
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.52-beta-20250916-025146-35d976f4",
6
+ "version": "1.16.52-beta-20250917-070942-7ee57044",
7
7
  "description": "Provide unified services for every blocklet",
8
8
  "main": "api/index.js",
9
9
  "files": [
@@ -34,17 +34,17 @@
34
34
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
35
35
  "license": "Apache-2.0",
36
36
  "dependencies": {
37
- "@abtnode/analytics": "1.16.52-beta-20250916-025146-35d976f4",
38
- "@abtnode/auth": "1.16.52-beta-20250916-025146-35d976f4",
39
- "@abtnode/connect-storage": "1.16.52-beta-20250916-025146-35d976f4",
40
- "@abtnode/constant": "1.16.52-beta-20250916-025146-35d976f4",
41
- "@abtnode/core": "1.16.52-beta-20250916-025146-35d976f4",
42
- "@abtnode/cron": "1.16.52-beta-20250916-025146-35d976f4",
43
- "@abtnode/db-cache": "1.16.52-beta-20250916-025146-35d976f4",
44
- "@abtnode/logger": "1.16.52-beta-20250916-025146-35d976f4",
45
- "@abtnode/models": "1.16.52-beta-20250916-025146-35d976f4",
46
- "@abtnode/router-templates": "1.16.52-beta-20250916-025146-35d976f4",
47
- "@abtnode/util": "1.16.52-beta-20250916-025146-35d976f4",
37
+ "@abtnode/analytics": "1.16.52-beta-20250917-070942-7ee57044",
38
+ "@abtnode/auth": "1.16.52-beta-20250917-070942-7ee57044",
39
+ "@abtnode/connect-storage": "1.16.52-beta-20250917-070942-7ee57044",
40
+ "@abtnode/constant": "1.16.52-beta-20250917-070942-7ee57044",
41
+ "@abtnode/core": "1.16.52-beta-20250917-070942-7ee57044",
42
+ "@abtnode/cron": "1.16.52-beta-20250917-070942-7ee57044",
43
+ "@abtnode/db-cache": "1.16.52-beta-20250917-070942-7ee57044",
44
+ "@abtnode/logger": "1.16.52-beta-20250917-070942-7ee57044",
45
+ "@abtnode/models": "1.16.52-beta-20250917-070942-7ee57044",
46
+ "@abtnode/router-templates": "1.16.52-beta-20250917-070942-7ee57044",
47
+ "@abtnode/util": "1.16.52-beta-20250917-070942-7ee57044",
48
48
  "@arcblock/did": "1.24.0",
49
49
  "@arcblock/did-connect-js": "1.24.0",
50
50
  "@arcblock/did-ext": "1.24.0",
@@ -54,19 +54,18 @@
54
54
  "@arcblock/jwt": "1.24.0",
55
55
  "@arcblock/validator": "1.24.0",
56
56
  "@arcblock/ws": "1.24.0",
57
- "@blocklet/constant": "1.16.52-beta-20250916-025146-35d976f4",
57
+ "@blocklet/constant": "1.16.52-beta-20250917-070942-7ee57044",
58
58
  "@blocklet/dbhub": "^0.2.9",
59
- "@blocklet/env": "1.16.52-beta-20250916-025146-35d976f4",
59
+ "@blocklet/env": "1.16.52-beta-20250917-070942-7ee57044",
60
60
  "@blocklet/error": "^0.2.5",
61
61
  "@blocklet/form-builder": "^0.1.12",
62
62
  "@blocklet/form-collector": "^0.1.8",
63
- "@blocklet/images": "1.16.52-beta-20250916-025146-35d976f4",
64
- "@blocklet/js-sdk": "1.16.52-beta-20250916-025146-35d976f4",
65
- "@blocklet/mcp": "^1.10.2",
66
- "@blocklet/meta": "1.16.52-beta-20250916-025146-35d976f4",
67
- "@blocklet/rate-limit": "1.16.52-beta-20250916-025146-35d976f4",
68
- "@blocklet/sdk": "1.16.52-beta-20250916-025146-35d976f4",
69
- "@blocklet/server-js": "1.16.52-beta-20250916-025146-35d976f4",
63
+ "@blocklet/images": "1.16.52-beta-20250917-070942-7ee57044",
64
+ "@blocklet/js-sdk": "1.16.52-beta-20250917-070942-7ee57044",
65
+ "@blocklet/meta": "1.16.52-beta-20250917-070942-7ee57044",
66
+ "@blocklet/rate-limit": "1.16.52-beta-20250917-070942-7ee57044",
67
+ "@blocklet/sdk": "1.16.52-beta-20250917-070942-7ee57044",
68
+ "@blocklet/server-js": "1.16.52-beta-20250917-070942-7ee57044",
70
69
  "@blocklet/theme": "^3.1.40",
71
70
  "@blocklet/theme-builder": "0.4.6",
72
71
  "@blocklet/uploader-server": "^0.2.10",
@@ -76,6 +75,7 @@
76
75
  "@iconify-icons/material-symbols": "^1.2.58",
77
76
  "@iconify-icons/tabler": "^1.2.95",
78
77
  "@iconify/react": "^5.2.1",
78
+ "@modelcontextprotocol/sdk": "^1.18.0",
79
79
  "@ocap/client": "1.24.0",
80
80
  "@ocap/util": "1.24.0",
81
81
  "@react-email/components": "^0.0.34",
@@ -125,7 +125,7 @@
125
125
  "whatwg-url": "14.0.0"
126
126
  },
127
127
  "devDependencies": {
128
- "@abtnode/ux": "1.16.52-beta-20250916-025146-35d976f4",
128
+ "@abtnode/ux": "1.16.52-beta-20250917-070942-7ee57044",
129
129
  "@arcblock/bridge": "^3.1.40",
130
130
  "@arcblock/did-connect-react": "^3.1.40",
131
131
  "@arcblock/icons": "^3.1.40",
@@ -135,7 +135,7 @@
135
135
  "@blocklet/did-space-react": "^1.1.23",
136
136
  "@blocklet/launcher-layout": "^3.1.40",
137
137
  "@blocklet/payment-react": "^1.20.8",
138
- "@blocklet/tracker": "1.16.52-beta-20250916-025146-35d976f4",
138
+ "@blocklet/tracker": "1.16.52-beta-20250917-070942-7ee57044",
139
139
  "@blocklet/ui-react": "^3.1.40",
140
140
  "@blocklet/uploader": "^0.2.10",
141
141
  "@emotion/react": "^11.14.0",
@@ -215,5 +215,5 @@
215
215
  "url": "https://github.com/ArcBlock/blocklet-server/issues",
216
216
  "email": "shijun@arcblock.io"
217
217
  },
218
- "gitHead": "a1a95bbc637d8954abcb3b2d675037ec309ad7e3"
218
+ "gitHead": "ac4ec647df35a480b28351b3bca6d3e59c64fff1"
219
219
  }