@myspec/mcp-server 0.1.2 → 0.1.3-next.42

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.
Files changed (2) hide show
  1. package/dist/index.js +65 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3333,8 +3333,14 @@ var PackCache = class {
3333
3333
  this.entries.delete(oldestKey);
3334
3334
  }
3335
3335
  const lines = splitIntoLines(content);
3336
- const totalPages = Math.max(1, Math.ceil(lines.length / PAGE_SIZE_LINES));
3337
- const entry = { outputId, lines, createdAt: Date.now(), totalPages };
3336
+ const pages = planPages(lines);
3337
+ const entry = {
3338
+ outputId,
3339
+ lines,
3340
+ createdAt: Date.now(),
3341
+ totalPages: pages.length,
3342
+ pages
3343
+ };
3338
3344
  this.entries.set(outputId, entry);
3339
3345
  return entry;
3340
3346
  }
@@ -3360,6 +3366,56 @@ function splitIntoLines(text) {
3360
3366
  if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
3361
3367
  return parts;
3362
3368
  }
3369
+ var FILES_OPEN = "<files>";
3370
+ var FILES_CLOSE = "</files>";
3371
+ var FILE_CLOSE = "</file>";
3372
+ function planPages(lines) {
3373
+ if (lines.length === 0) {
3374
+ return [{ start: 0, end: 0 }];
3375
+ }
3376
+ let filesOpenIdx = -1;
3377
+ let filesCloseIdx = -1;
3378
+ for (let i = 0; i < lines.length; i += 1) {
3379
+ const t = lines[i]?.trim();
3380
+ if (filesOpenIdx === -1) {
3381
+ if (t === FILES_OPEN) filesOpenIdx = i;
3382
+ } else if (t === FILES_CLOSE) {
3383
+ filesCloseIdx = i;
3384
+ break;
3385
+ }
3386
+ }
3387
+ if (filesOpenIdx !== -1 && filesCloseIdx === -1) filesCloseIdx = lines.length;
3388
+ const insideFiles = (end) => filesOpenIdx !== -1 && filesCloseIdx !== -1 && end > filesOpenIdx && end <= filesCloseIdx;
3389
+ const pages = [];
3390
+ let cursor = 0;
3391
+ while (cursor < lines.length) {
3392
+ let end = Math.min(cursor + PAGE_SIZE_LINES, lines.length);
3393
+ if (end < lines.length && insideFiles(end)) {
3394
+ let back = -1;
3395
+ for (let i = end - 1; i >= cursor; i -= 1) {
3396
+ if (lines[i]?.trim() === FILE_CLOSE) {
3397
+ back = i;
3398
+ break;
3399
+ }
3400
+ }
3401
+ if (back >= cursor) {
3402
+ end = back + 1;
3403
+ } else {
3404
+ let fwd = -1;
3405
+ for (let i = end; i < lines.length; i += 1) {
3406
+ if (lines[i]?.trim() === FILE_CLOSE) {
3407
+ fwd = i;
3408
+ break;
3409
+ }
3410
+ }
3411
+ end = fwd >= 0 ? fwd + 1 : lines.length;
3412
+ }
3413
+ }
3414
+ pages.push({ start: cursor, end });
3415
+ cursor = end;
3416
+ }
3417
+ return pages;
3418
+ }
3363
3419
  async function runPack(opts) {
3364
3420
  const subpath = opts.args.subpath ?? ".";
3365
3421
  const targetDir = await resolveSafePath(opts.realRoot, subpath);
@@ -3417,18 +3473,17 @@ var InvalidPageError = class extends Error {
3417
3473
  totalPages;
3418
3474
  };
3419
3475
  function buildPage(entry, page) {
3420
- const start = (page - 1) * PAGE_SIZE_LINES;
3421
- const end = Math.min(start + PAGE_SIZE_LINES, entry.lines.length);
3422
- const sliceLines = entry.lines.slice(start, end);
3476
+ const plan = entry.pages[page - 1];
3477
+ const body = plan ? entry.lines.slice(plan.start, plan.end) : [];
3423
3478
  const hasMore = page < entry.totalPages;
3424
3479
  return {
3425
3480
  outputId: entry.outputId,
3426
3481
  page,
3427
3482
  totalPages: entry.totalPages,
3428
3483
  totalLines: entry.lines.length,
3429
- pageLines: sliceLines.length,
3484
+ pageLines: body.length,
3430
3485
  hasMore,
3431
- content: sliceLines.join("\n"),
3486
+ content: body.join("\n"),
3432
3487
  ...hasMore && {
3433
3488
  nextPage: {
3434
3489
  tool: "local_pack_codebase_read_page",
@@ -3442,7 +3497,7 @@ async function invokeRepomix(exec, targetDir, args, timeoutMs) {
3442
3497
  const outputFile = join(workDir, "pack.txt");
3443
3498
  const options = {
3444
3499
  compress: true,
3445
- style: "plain",
3500
+ style: "xml",
3446
3501
  quiet: true,
3447
3502
  output: outputFile,
3448
3503
  securityCheck: true,
@@ -3956,7 +4011,7 @@ function registerLocalFsTools(server, opts) {
3956
4011
  },
3957
4012
  {
3958
4013
  name: "local_pack_codebase",
3959
- description: "Pack the entire local codebase (under the granted --root) into a single plain-text bundle using `npx repomix@latest --style plain --compress`. Prefer this over many `local_fs_read_file` calls when you need broad codebase context. Output is paginated at 2500 lines per page; the response includes `outputId`, `page`, `totalPages`, `totalLines`, `pageLines`, `hasMore`, and a `nextPage` hint. Use `local_pack_codebase_read_page` to fetch subsequent pages by `outputId`. Repeat calls with the same `subpath`/`includePatterns`/`ignorePatterns` return the same `outputId` and are served instantly from the 30-minute cache without re-running repomix.",
4014
+ description: "Pack the entire local codebase (under the granted --root) into a single XML bundle using `npx repomix@latest --style xml --compress`. Prefer this over many `local_fs_read_file` calls when you need broad codebase context. Output is paginated at ~2500 lines per page; a `<file>` block is never split across a page boundary, but the `<files>` root wrapper spans pages (its opener is on the first files page and its closer on the last) \u2014 always keep reading while `hasMore` is true rather than stopping at a `</files>`. The response includes `outputId`, `page`, `totalPages`, `totalLines`, `pageLines`, `hasMore`, and a `nextPage` hint. Use `local_pack_codebase_read_page` to fetch subsequent pages by `outputId`. Repeat calls with the same `subpath`/`includePatterns`/`ignorePatterns` return the same `outputId` and are served instantly from the 30-minute cache without re-running repomix.",
3960
4015
  inputSchema: jsonSchemaFromZod(packCodebaseInput),
3961
4016
  handler: async (args) => {
3962
4017
  try {
@@ -3969,7 +4024,7 @@ function registerLocalFsTools(server, opts) {
3969
4024
  },
3970
4025
  {
3971
4026
  name: "local_pack_codebase_read_page",
3972
- description: "Read a specific page of a previously packed codebase output. Provide the `outputId` returned by `local_pack_codebase` and the 1-indexed `page` to fetch. Pages are 2500 lines of plain text. Cache entries expire after 30 minutes; on cache miss, re-run `local_pack_codebase` (same args return the same outputId \u2014 re-pack is cheap, served from cache).",
4027
+ description: "Read a specific page of a previously packed codebase output. Provide the `outputId` returned by `local_pack_codebase` and the 1-indexed `page` to fetch. Each page is up to ~2500 lines of XML; no `<file>` block is split across pages, but the `<files>` root wrapper spans pages \u2014 keep fetching while `hasMore` is true, do not stop at a `</files>`. Cache entries expire after 30 minutes; on cache miss, re-run `local_pack_codebase` (same args return the same outputId \u2014 re-pack is cheap, served from cache).",
3973
4028
  inputSchema: jsonSchemaFromZod(packCodebaseReadPageInput),
3974
4029
  handler: (args) => {
3975
4030
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myspec/mcp-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.3-next.42",
4
4
  "description": "MySpec MCP server — exposes MySpec platform projects, files and attachments to MCP-aware clients via OAuth-authenticated access tokens.",
5
5
  "type": "module",
6
6
  "repository": {