@topgunbuild/mcp-server 0.9.0 → 0.10.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/cli.js CHANGED
@@ -23,7 +23,7 @@ var QueryArgsSchema = zod.z.object({
23
23
  order: zod.z.enum(["asc", "desc"]).describe("Sort order: ascending or descending")
24
24
  }).optional().describe("Sort configuration"),
25
25
  limit: zod.z.number().optional().default(10).describe("Maximum number of results to return"),
26
- offset: zod.z.number().optional().default(0).describe("Number of results to skip (for pagination)")
26
+ cursor: zod.z.string().optional().describe("Opaque cursor for pagination (from previous response nextCursor)")
27
27
  });
28
28
  var MutateArgsSchema = zod.z.object({
29
29
  map: zod.z.string().describe("Name of the map to modify (e.g., 'tasks', 'users')"),
@@ -77,7 +77,7 @@ var toolSchemas = {
77
77
  description: "Sort configuration"
78
78
  },
79
79
  limit: { type: "number", description: "Maximum number of results to return", default: 10 },
80
- offset: { type: "number", description: "Number of results to skip (for pagination)", default: 0 }
80
+ cursor: { type: "string", description: "Opaque cursor for pagination (from previous response nextCursor)" }
81
81
  },
82
82
  required: ["map"]
83
83
  },
@@ -167,7 +167,7 @@ var toolSchemas = {
167
167
  // src/tools/query.ts
168
168
  var queryTool = {
169
169
  name: "topgun_query",
170
- description: "Query data from a TopGun map with filters and sorting. Use this to read data from the database. Supports filtering by field values and sorting by any field.",
170
+ description: "Query data from a TopGun map with filters and sorting. Use this to read data from the database. Supports filtering by field values, sorting, and cursor-based pagination.",
171
171
  inputSchema: toolSchemas.query
172
172
  };
173
173
  async function handleQuery(rawArgs, ctx) {
@@ -179,7 +179,7 @@ async function handleQuery(rawArgs, ctx) {
179
179
  isError: true
180
180
  };
181
181
  }
182
- const { map, filter, sort, limit, offset } = parseResult.data;
182
+ const { map, filter, sort, limit, cursor } = parseResult.data;
183
183
  if (ctx.config.allowedMaps && !ctx.config.allowedMaps.includes(map)) {
184
184
  return {
185
185
  content: [
@@ -192,39 +192,26 @@ async function handleQuery(rawArgs, ctx) {
192
192
  };
193
193
  }
194
194
  const effectiveLimit = Math.min(limit ?? ctx.config.defaultLimit, ctx.config.maxLimit);
195
- const effectiveOffset = offset ?? 0;
196
195
  try {
197
- const lwwMap = ctx.client.getMap(map);
198
- const allEntries = [];
199
- for (const [key, value] of lwwMap.entries()) {
200
- if (value !== null && typeof value === "object") {
201
- let matches = true;
202
- if (filter) {
203
- for (const [filterKey, filterValue] of Object.entries(filter)) {
204
- if (value[filterKey] !== filterValue) {
205
- matches = false;
206
- break;
207
- }
208
- }
209
- }
210
- if (matches) {
211
- allEntries.push({ key: String(key), value });
212
- }
213
- }
214
- }
196
+ const queryFilter = {
197
+ where: filter,
198
+ limit: effectiveLimit
199
+ };
215
200
  if (sort?.field) {
216
- allEntries.sort((a, b) => {
217
- const aVal = a.value[sort.field];
218
- const bVal = b.value[sort.field];
219
- if (aVal === bVal) return 0;
220
- if (aVal === void 0 || aVal === null) return 1;
221
- if (bVal === void 0 || bVal === null) return -1;
222
- const comparison = aVal < bVal ? -1 : 1;
223
- return sort.order === "desc" ? -comparison : comparison;
224
- });
201
+ queryFilter.sort = { [sort.field]: sort.order };
225
202
  }
226
- const paginatedEntries = allEntries.slice(effectiveOffset, effectiveOffset + effectiveLimit);
227
- if (paginatedEntries.length === 0) {
203
+ if (cursor) {
204
+ queryFilter.cursor = cursor;
205
+ }
206
+ const handle = ctx.client.query(map, queryFilter);
207
+ const results = await new Promise((resolve) => {
208
+ const unsubscribe = handle.subscribe((data) => {
209
+ unsubscribe();
210
+ resolve(data);
211
+ });
212
+ });
213
+ const paginationInfo = handle.getPaginationInfo();
214
+ if (results.length === 0) {
228
215
  return {
229
216
  content: [
230
217
  {
@@ -234,17 +221,24 @@ async function handleQuery(rawArgs, ctx) {
234
221
  ]
235
222
  };
236
223
  }
237
- const formatted = paginatedEntries.map((entry, idx) => `${idx + 1 + effectiveOffset}. [${entry.key}]: ${JSON.stringify(entry.value, null, 2)}`).join("\n\n");
238
- const totalInfo = allEntries.length > effectiveLimit ? `
224
+ const formatted = results.map((entry, idx) => {
225
+ const { _key, ...value } = entry;
226
+ return `${idx + 1}. [${_key}]: ${JSON.stringify(value, null, 2)}`;
227
+ }).join("\n\n");
228
+ let paginationText = "";
229
+ if (paginationInfo.hasMore && paginationInfo.nextCursor) {
230
+ paginationText = `
239
231
 
240
- (Showing ${effectiveOffset + 1}-${effectiveOffset + paginatedEntries.length} of ${allEntries.length} total)` : "";
232
+ ---
233
+ More results available. Use cursor: "${paginationInfo.nextCursor}" to fetch next page.`;
234
+ }
241
235
  return {
242
236
  content: [
243
237
  {
244
238
  type: "text",
245
- text: `Found ${paginatedEntries.length} result(s) in map '${map}':
239
+ text: `Found ${results.length} result(s) in map '${map}':
246
240
 
247
- ${formatted}${totalInfo}`
241
+ ${formatted}${paginationText}`
248
242
  }
249
243
  ]
250
244
  };