@absolutejs/mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -150,16 +150,26 @@ var createMcpClient = (options) => {
150
150
  await notify("notifications/initialized");
151
151
  return isRecord(result) ? result : {};
152
152
  };
153
+ const MAX_LIST_PAGES = 40;
153
154
  const listTools = async () => {
154
- const result = await rpc("tools/list");
155
- const tools = isRecord(result) && Array.isArray(result.tools) ? result.tools : [];
156
- return tools.filter(isRecord).map((tool) => ({
157
- annotations: isRecord(tool.annotations) ? tool.annotations : undefined,
158
- description: typeof tool.description === "string" ? tool.description : undefined,
159
- inputSchema: isRecord(tool.inputSchema) ? tool.inputSchema : undefined,
160
- name: typeof tool.name === "string" ? tool.name : "",
161
- outputSchema: isRecord(tool.outputSchema) ? tool.outputSchema : undefined
162
- }));
155
+ const collected = [];
156
+ let cursor;
157
+ for (let page = 0;page < MAX_LIST_PAGES; page += 1) {
158
+ const result = await rpc("tools/list", cursor === undefined ? undefined : { cursor });
159
+ const tools = isRecord(result) && Array.isArray(result.tools) ? result.tools : [];
160
+ collected.push(...tools.filter(isRecord).map((tool) => ({
161
+ annotations: isRecord(tool.annotations) ? tool.annotations : undefined,
162
+ description: typeof tool.description === "string" ? tool.description : undefined,
163
+ inputSchema: isRecord(tool.inputSchema) ? tool.inputSchema : undefined,
164
+ name: typeof tool.name === "string" ? tool.name : "",
165
+ outputSchema: isRecord(tool.outputSchema) ? tool.outputSchema : undefined
166
+ })));
167
+ const next = isRecord(result) && typeof result.nextCursor === "string" ? result.nextCursor : undefined;
168
+ if (next === undefined)
169
+ break;
170
+ cursor = next;
171
+ }
172
+ return collected;
163
173
  };
164
174
  const callTool = async (name, args) => {
165
175
  const result = await rpc("tools/call", { arguments: args ?? {}, name });
@@ -169,8 +179,19 @@ var createMcpClient = (options) => {
169
179
  return { content: [], isError: false };
170
180
  };
171
181
  const listResources = async () => {
172
- const result = await rpc("resources/list");
173
- return isRecord(result) && Array.isArray(result.resources) ? result.resources : [];
182
+ const collected = [];
183
+ let cursor;
184
+ for (let page = 0;page < MAX_LIST_PAGES; page += 1) {
185
+ const result = await rpc("resources/list", cursor === undefined ? undefined : { cursor });
186
+ if (isRecord(result) && Array.isArray(result.resources)) {
187
+ collected.push(...result.resources);
188
+ }
189
+ const next = isRecord(result) && typeof result.nextCursor === "string" ? result.nextCursor : undefined;
190
+ if (next === undefined)
191
+ break;
192
+ cursor = next;
193
+ }
194
+ return collected;
174
195
  };
175
196
  const readResource = async (uri) => rpc("resources/read", { uri });
176
197
  const ping = async () => {
@@ -212,6 +233,26 @@ var unauthorized = (metadataUrl, detail) => new Response(JSON.stringify({
212
233
  // src/dispatch.ts
213
234
  var DEFAULT_PROTOCOLS = ["2025-06-18", "2025-03-26", "2024-11-05"];
214
235
  var DEFAULT_RESOURCE_MIME = "text/markdown";
236
+ var DEFAULT_LIST_PAGE_SIZE = 50;
237
+ var decodeCursor = (params) => {
238
+ if (!isRecord(params) || typeof params.cursor !== "string")
239
+ return 0;
240
+ try {
241
+ const parsed = Number.parseInt(atob(params.cursor), 10);
242
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
243
+ } catch {
244
+ return 0;
245
+ }
246
+ };
247
+ var encodeCursor = (offset) => btoa(String(offset));
248
+ var paginate = (items, offset, pageSize) => {
249
+ const page = items.slice(offset, offset + pageSize);
250
+ const nextOffset = offset + pageSize;
251
+ return {
252
+ items: page,
253
+ ...nextOffset < items.length ? { nextCursor: encodeCursor(nextOffset) } : {}
254
+ };
255
+ };
215
256
  var idOf = (message) => typeof message.id === "string" || typeof message.id === "number" ? message.id : null;
216
257
  var negotiateProtocol = (supported, params) => {
217
258
  const preferred = supported[0] ?? DEFAULT_PROTOCOLS[0] ?? "";
@@ -244,16 +285,19 @@ var initialize = (config, id, params) => {
244
285
  serverInfo: config.serverInfo
245
286
  });
246
287
  };
247
- var toolsList = async (config, caller, scopes, id) => {
288
+ var toolsList = async (config, caller, scopes, id, params) => {
248
289
  const tools = await config.tools({ caller, meta: {} });
290
+ const visible = Object.entries(tools).filter(([, tool]) => scopeAllows(tool, scopes)).map(([name, tool]) => ({
291
+ annotations: tool.annotations,
292
+ description: tool.description,
293
+ inputSchema: tool.inputSchema,
294
+ name,
295
+ ...tool.outputSchema === undefined ? {} : { outputSchema: tool.outputSchema }
296
+ }));
297
+ const { items, nextCursor } = paginate(visible, decodeCursor(params), config.listPageSize ?? DEFAULT_LIST_PAGE_SIZE);
249
298
  return rpcResult(id, {
250
- tools: Object.entries(tools).filter(([, tool]) => scopeAllows(tool, scopes)).map(([name, tool]) => ({
251
- annotations: tool.annotations,
252
- description: tool.description,
253
- inputSchema: tool.inputSchema,
254
- name,
255
- ...tool.outputSchema === undefined ? {} : { outputSchema: tool.outputSchema }
256
- }))
299
+ tools: items,
300
+ ...nextCursor === undefined ? {} : { nextCursor }
257
301
  });
258
302
  };
259
303
  var errorResult = (id, text) => rpcResult(id, { content: [{ text, type: "text" }], isError: true });
@@ -288,15 +332,18 @@ var toolsCall = async (config, caller, scopes, id, params) => {
288
332
  await config.onCall({ args, caller, meta, name, ok });
289
333
  return response;
290
334
  };
291
- var promptsList = (config, id) => {
335
+ var promptsList = (config, id, params) => {
292
336
  const definitions = config.prompts?.definitions ?? {};
337
+ const all = Object.entries(definitions).map(([name, def]) => ({
338
+ arguments: def.arguments ?? [],
339
+ description: def.description,
340
+ name,
341
+ title: def.title
342
+ }));
343
+ const { items, nextCursor } = paginate(all, decodeCursor(params), config.listPageSize ?? DEFAULT_LIST_PAGE_SIZE);
293
344
  return rpcResult(id, {
294
- prompts: Object.entries(definitions).map(([name, def]) => ({
295
- arguments: def.arguments ?? [],
296
- description: def.description,
297
- name,
298
- title: def.title
299
- }))
345
+ prompts: items,
346
+ ...nextCursor === undefined ? {} : { nextCursor }
300
347
  });
301
348
  };
302
349
  var promptsGet = async (config, caller, id, params) => {
@@ -320,11 +367,15 @@ var promptsGet = async (config, caller, id, params) => {
320
367
  messages: [{ content: { text, type: "text" }, role: "user" }]
321
368
  });
322
369
  };
323
- var resourcesList = async (config, caller, id) => {
370
+ var resourcesList = async (config, caller, id, params) => {
324
371
  const resources = config.resources;
325
372
  if (!resources)
326
373
  return rpcResult(id, { resources: [] });
327
- return rpcResult(id, { resources: await resources.list({ caller }) });
374
+ const { items, nextCursor } = paginate(await resources.list({ caller }), decodeCursor(params), config.listPageSize ?? DEFAULT_LIST_PAGE_SIZE);
375
+ return rpcResult(id, {
376
+ resources: items,
377
+ ...nextCursor === undefined ? {} : { nextCursor }
378
+ });
328
379
  };
329
380
  var resourcesRead = async (config, caller, id, params) => {
330
381
  const resources = config.resources;
@@ -357,17 +408,19 @@ var dispatchMcp = async (config, caller, scopes, message) => {
357
408
  return initialize(config, id, params);
358
409
  if (method === "ping")
359
410
  return rpcResult(id, {});
360
- if (method === "tools/list")
361
- return toolsList(config, caller, scopes, id);
411
+ if (method === "tools/list") {
412
+ return toolsList(config, caller, scopes, id, params);
413
+ }
362
414
  if (method === "tools/call") {
363
415
  return toolsCall(config, caller, scopes, id, params);
364
416
  }
365
417
  if (method === "prompts/list")
366
- return promptsList(config, id);
418
+ return promptsList(config, id, params);
367
419
  if (method === "prompts/get")
368
420
  return promptsGet(config, caller, id, params);
369
- if (method === "resources/list")
370
- return resourcesList(config, caller, id);
421
+ if (method === "resources/list") {
422
+ return resourcesList(config, caller, id, params);
423
+ }
371
424
  if (method === "resources/read") {
372
425
  return resourcesRead(config, caller, id, params);
373
426
  }
@@ -135,6 +135,8 @@ export type McpServerConfig<Caller> = {
135
135
  instructions?: string;
136
136
  /** The token issuer — used for discovery metadata and the challenge URL. */
137
137
  issuer: string;
138
+ /** Page size for tools/prompts/resources list pagination (default 50). */
139
+ listPageSize?: number;
138
140
  /** Fired after every `tools/call` for auditing. `meta` carries anything the
139
141
  * tool handler wrote during the call. */
140
142
  onCall?: (record: {
package/package.json CHANGED
@@ -37,5 +37,5 @@
37
37
  "typecheck": "tsc --noEmit --project tsconfig.json"
38
38
  },
39
39
  "types": "./dist/src/index.d.ts",
40
- "version": "0.1.0"
40
+ "version": "0.2.0"
41
41
  }