@oss-scout/mcp 0.12.0 → 0.12.2

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.d.ts CHANGED
@@ -12,7 +12,7 @@ export { registerResources } from "./resources.js";
12
12
  /** Server identity, kept in one place for the bin and any embedding host. */
13
13
  export declare const SERVER_INFO: {
14
14
  readonly name: "oss-scout-mcp";
15
- readonly version: "0.12.0";
15
+ readonly version: "0.12.2";
16
16
  };
17
17
  /**
18
18
  * Build a fully-wired MCP server (tools + resources) around a scout.
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export { registerResources } from "./resources.js";
15
15
  /** Server identity, kept in one place for the bin and any embedding host. */
16
16
  export const SERVER_INFO = {
17
17
  name: "oss-scout-mcp",
18
- version: "0.12.0", // x-release-please-version
18
+ version: "0.12.2", // x-release-please-version
19
19
  };
20
20
  /**
21
21
  * Build a fully-wired MCP server (tools + resources) around a scout.
@@ -7,7 +7,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __commonJS = (cb, mod) => function __require() {
10
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ try {
11
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
+ } catch (e) {
13
+ throw mod = 0, e;
14
+ }
11
15
  };
12
16
  var __export = (target, all) => {
13
17
  for (var name in all)
@@ -24420,7 +24424,7 @@ function object(shape, params) {
24420
24424
  return new ZodMiniObject(def);
24421
24425
  }
24422
24426
 
24423
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
24427
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
24424
24428
  function isZ4Schema(s) {
24425
24429
  const schema = s;
24426
24430
  return !!schema._zod;
@@ -24504,17 +24508,33 @@ function normalizeObjectSchema(schema) {
24504
24508
  }
24505
24509
  return void 0;
24506
24510
  }
24511
+ function getDotPath(path5) {
24512
+ if (path5.length === 0) {
24513
+ return "object root";
24514
+ }
24515
+ return path5.reduce((acc, seg, index) => {
24516
+ if (index === 0) {
24517
+ return String(seg);
24518
+ }
24519
+ if (typeof seg === "number") {
24520
+ return `${acc}[${seg}]`;
24521
+ }
24522
+ return `${acc}.${seg}`;
24523
+ }, "");
24524
+ }
24507
24525
  function getParseErrorMessage(error48) {
24508
24526
  if (error48 && typeof error48 === "object") {
24527
+ if ("issues" in error48 && Array.isArray(error48.issues) && error48.issues.length > 0) {
24528
+ return error48.issues.map((i) => {
24529
+ if (!i.path?.length) {
24530
+ return i.message;
24531
+ }
24532
+ return `${i.message} at ${getDotPath(i.path)}`;
24533
+ }).join("\n");
24534
+ }
24509
24535
  if ("message" in error48 && typeof error48.message === "string") {
24510
24536
  return error48.message;
24511
24537
  }
24512
- if ("issues" in error48 && Array.isArray(error48.issues) && error48.issues.length > 0) {
24513
- const firstIssue = error48.issues[0];
24514
- if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
24515
- return String(firstIssue.message);
24516
- }
24517
- }
24518
24538
  try {
24519
24539
  return JSON.stringify(error48);
24520
24540
  } catch {
@@ -26710,7 +26730,7 @@ function date4(params) {
26710
26730
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
26711
26731
  config(en_default2());
26712
26732
 
26713
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
26733
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
26714
26734
  var LATEST_PROTOCOL_VERSION = "2025-11-25";
26715
26735
  var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
26716
26736
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
@@ -26720,10 +26740,9 @@ var ProgressTokenSchema = union([string2(), number2().int()]);
26720
26740
  var CursorSchema = string2();
26721
26741
  var TaskCreationParamsSchema = looseObject({
26722
26742
  /**
26723
- * Time in milliseconds to keep task results available after completion.
26724
- * If null, the task has unlimited lifetime until manually cleaned up.
26743
+ * Requested duration in milliseconds to retain task from creation.
26725
26744
  */
26726
- ttl: union([number2(), _null3()]).optional(),
26745
+ ttl: number2().optional(),
26727
26746
  /**
26728
26747
  * Time in milliseconds to wait between task status requests.
26729
26748
  */
@@ -27023,7 +27042,11 @@ var ClientCapabilitiesSchema = object2({
27023
27042
  /**
27024
27043
  * Present if the client supports task creation.
27025
27044
  */
27026
- tasks: ClientTasksCapabilitySchema.optional()
27045
+ tasks: ClientTasksCapabilitySchema.optional(),
27046
+ /**
27047
+ * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
27048
+ */
27049
+ extensions: record(string2(), AssertObjectSchema).optional()
27027
27050
  });
27028
27051
  var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
27029
27052
  /**
@@ -27084,7 +27107,11 @@ var ServerCapabilitiesSchema = object2({
27084
27107
  /**
27085
27108
  * Present if the server supports task creation.
27086
27109
  */
27087
- tasks: ServerTasksCapabilitySchema.optional()
27110
+ tasks: ServerTasksCapabilitySchema.optional(),
27111
+ /**
27112
+ * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
27113
+ */
27114
+ extensions: record(string2(), AssertObjectSchema).optional()
27088
27115
  });
27089
27116
  var InitializeResultSchema = ResultSchema.extend({
27090
27117
  /**
@@ -27276,6 +27303,12 @@ var ResourceSchema = object2({
27276
27303
  * The MIME type of this resource, if known.
27277
27304
  */
27278
27305
  mimeType: optional(string2()),
27306
+ /**
27307
+ * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
27308
+ *
27309
+ * This can be used by Hosts to display file sizes and estimate context window usage.
27310
+ */
27311
+ size: optional(number2()),
27279
27312
  /**
27280
27313
  * Optional annotations for the client.
27281
27314
  */
@@ -28228,7 +28261,7 @@ var UrlElicitationRequiredError = class extends McpError {
28228
28261
  }
28229
28262
  };
28230
28263
 
28231
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
28264
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
28232
28265
  function isTerminal(status) {
28233
28266
  return status === "completed" || status === "failed" || status === "cancelled";
28234
28267
  }
@@ -29517,7 +29550,7 @@ var zodToJsonSchema = (schema, options) => {
29517
29550
  return combined;
29518
29551
  };
29519
29552
 
29520
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
29553
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
29521
29554
  function mapMiniTarget(t) {
29522
29555
  if (!t)
29523
29556
  return "draft-7";
@@ -29559,7 +29592,7 @@ function parseWithCompat(schema, data) {
29559
29592
  return result.data;
29560
29593
  }
29561
29594
 
29562
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29595
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29563
29596
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
29564
29597
  var Protocol = class {
29565
29598
  constructor(_options) {
@@ -29600,6 +29633,9 @@ var Protocol = class {
29600
29633
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request2, extra) => {
29601
29634
  const handleTaskResult = async () => {
29602
29635
  const taskId = request2.params.taskId;
29636
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
29637
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
29638
+ }
29603
29639
  if (this._taskMessageQueue) {
29604
29640
  let queuedMessage;
29605
29641
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -29630,12 +29666,12 @@ var Protocol = class {
29630
29666
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
29631
29667
  }
29632
29668
  if (!isTerminal(task.status)) {
29633
- await this._waitForTaskUpdate(taskId, extra.signal);
29669
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
29634
29670
  return await handleTaskResult();
29635
29671
  }
29636
29672
  if (isTerminal(task.status)) {
29637
29673
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
29638
- this._clearTaskQueue(taskId);
29674
+ this._clearTaskQueue(taskId, extra.sessionId);
29639
29675
  return {
29640
29676
  ...result,
29641
29677
  _meta: {
@@ -29672,7 +29708,7 @@ var Protocol = class {
29672
29708
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
29673
29709
  }
29674
29710
  await this._taskStore.updateTaskStatus(request2.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
29675
- this._clearTaskQueue(request2.params.taskId);
29711
+ this._clearTaskQueue(request2.params.taskId, extra.sessionId);
29676
29712
  const cancelledTask = await this._taskStore.getTask(request2.params.taskId, extra.sessionId);
29677
29713
  if (!cancelledTask) {
29678
29714
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request2.params.taskId}`);
@@ -29800,6 +29836,19 @@ var Protocol = class {
29800
29836
  const handler2 = this._requestHandlers.get(request2.method) ?? this.fallbackRequestHandler;
29801
29837
  const capturedTransport = this._transport;
29802
29838
  const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
29839
+ const sessionId = capturedTransport?.sessionId;
29840
+ const store = this._taskStore;
29841
+ let relatedTaskFound = true;
29842
+ let relatedTaskLookup;
29843
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
29844
+ relatedTaskFound = false;
29845
+ relatedTaskLookup = (async () => {
29846
+ if (!await store.getTask(relatedTaskId, sessionId)) {
29847
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
29848
+ }
29849
+ relatedTaskFound = true;
29850
+ })();
29851
+ }
29803
29852
  if (handler2 === void 0) {
29804
29853
  const errorResponse = {
29805
29854
  jsonrpc: "2.0",
@@ -29809,7 +29858,10 @@ var Protocol = class {
29809
29858
  message: "Method not found"
29810
29859
  }
29811
29860
  };
29812
- if (relatedTaskId && this._taskMessageQueue) {
29861
+ if (relatedTaskId && relatedTaskLookup) {
29862
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
29863
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error48) => this._onerror(new Error(`Failed to send an error response: ${error48}`)));
29864
+ } else if (relatedTaskId && this._taskMessageQueue) {
29813
29865
  this._enqueueTaskMessage(relatedTaskId, {
29814
29866
  type: "error",
29815
29867
  message: errorResponse,
@@ -29860,7 +29912,10 @@ var Protocol = class {
29860
29912
  closeSSEStream: extra?.closeSSEStream,
29861
29913
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
29862
29914
  };
29863
- Promise.resolve().then(() => {
29915
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
29916
+ if (relatedTaskLookup && abortController.signal.aborted) {
29917
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
29918
+ }
29864
29919
  if (taskCreationParams) {
29865
29920
  this.assertTaskHandlerCapability(request2.method);
29866
29921
  }
@@ -29895,7 +29950,7 @@ var Protocol = class {
29895
29950
  ...error48["data"] !== void 0 && { data: error48["data"] }
29896
29951
  }
29897
29952
  };
29898
- if (relatedTaskId && this._taskMessageQueue) {
29953
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
29899
29954
  await this._enqueueTaskMessage(relatedTaskId, {
29900
29955
  type: "error",
29901
29956
  message: errorResponse,
@@ -30375,7 +30430,7 @@ var Protocol = class {
30375
30430
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
30376
30431
  }
30377
30432
  const maxQueueSize = this._options?.maxTaskQueueSize;
30378
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
30433
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
30379
30434
  }
30380
30435
  /**
30381
30436
  * Clears the message queue for a task and rejects any pending request resolvers.
@@ -30404,12 +30459,13 @@ var Protocol = class {
30404
30459
  * Uses polling to check for updates at the task's configured poll interval.
30405
30460
  * @param taskId The task ID to wait for
30406
30461
  * @param signal Abort signal to cancel the wait
30462
+ * @param sessionId Session of the request that waits, passed to the task store
30407
30463
  * @returns Promise that resolves when an update occurs or rejects if aborted
30408
30464
  */
30409
- async _waitForTaskUpdate(taskId, signal) {
30465
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
30410
30466
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
30411
30467
  try {
30412
- const task = await this._taskStore?.getTask(taskId);
30468
+ const task = await this._taskStore?.getTask(taskId, sessionId);
30413
30469
  if (task?.pollInterval) {
30414
30470
  interval = task.pollInterval;
30415
30471
  }
@@ -30513,7 +30569,7 @@ function mergeCapabilities(base, additional) {
30513
30569
  return result;
30514
30570
  }
30515
30571
 
30516
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
30572
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
30517
30573
  var import_ajv = __toESM(require_ajv(), 1);
30518
30574
  var import_ajv_formats = __toESM(require_dist(), 1);
30519
30575
  function createDefaultAjvInstance() {
@@ -30581,7 +30637,7 @@ var AjvJsonSchemaValidator = class {
30581
30637
  }
30582
30638
  };
30583
30639
 
30584
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30640
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30585
30641
  var ExperimentalServerTasks = class {
30586
30642
  constructor(_server) {
30587
30643
  this._server = _server;
@@ -30794,7 +30850,7 @@ var ExperimentalServerTasks = class {
30794
30850
  }
30795
30851
  };
30796
30852
 
30797
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30853
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30798
30854
  function assertToolsCallTaskCapability(requests, method, entityName) {
30799
30855
  if (!requests) {
30800
30856
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -30829,7 +30885,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
30829
30885
  }
30830
30886
  }
30831
30887
 
30832
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30888
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30833
30889
  var Server = class extends Protocol {
30834
30890
  /**
30835
30891
  * Initializes this server with the given name and version information.
@@ -30895,16 +30951,7 @@ var Server = class extends Protocol {
30895
30951
  if (!methodSchema) {
30896
30952
  throw new Error("Schema is missing a method literal");
30897
30953
  }
30898
- let methodValue;
30899
- if (isZ4Schema(methodSchema)) {
30900
- const v4Schema = methodSchema;
30901
- const v4Def = v4Schema._zod?.def;
30902
- methodValue = v4Def?.value ?? v4Schema.value;
30903
- } else {
30904
- const v3Schema = methodSchema;
30905
- const legacyDef = v3Schema._def;
30906
- methodValue = legacyDef?.value ?? v3Schema.value;
30907
- }
30954
+ const methodValue = getLiteralValue(methodSchema);
30908
30955
  if (typeof methodValue !== "string") {
30909
30956
  throw new Error("Schema method literal must be a string");
30910
30957
  }
@@ -31209,7 +31256,7 @@ var Server = class extends Protocol {
31209
31256
  }
31210
31257
  };
31211
31258
 
31212
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
31259
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
31213
31260
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
31214
31261
  function isCompletable(schema) {
31215
31262
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -31223,7 +31270,7 @@ var McpZodTypeKind;
31223
31270
  McpZodTypeKind2["Completable"] = "McpCompletable";
31224
31271
  })(McpZodTypeKind || (McpZodTypeKind = {}));
31225
31272
 
31226
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
31273
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
31227
31274
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
31228
31275
  function validateToolName(name) {
31229
31276
  const warnings = [];
@@ -31281,7 +31328,7 @@ function validateAndWarnToolName(name) {
31281
31328
  return result.isValid;
31282
31329
  }
31283
31330
 
31284
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
31331
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
31285
31332
  var ExperimentalMcpServerTasks = class {
31286
31333
  constructor(_mcpServer) {
31287
31334
  this._mcpServer = _mcpServer;
@@ -31296,7 +31343,43 @@ var ExperimentalMcpServerTasks = class {
31296
31343
  }
31297
31344
  };
31298
31345
 
31299
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
31346
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
31347
+ function toolInputElementCount(value, max) {
31348
+ let count = 0;
31349
+ const stack = [value];
31350
+ while (stack.length > 0) {
31351
+ const node = stack.pop();
31352
+ if (node === null || typeof node !== "object")
31353
+ continue;
31354
+ if (Array.isArray(node)) {
31355
+ for (const child of node) {
31356
+ if (++count > max)
31357
+ return count;
31358
+ if (child !== null && typeof child === "object")
31359
+ stack.push(child);
31360
+ }
31361
+ } else {
31362
+ for (const key in node) {
31363
+ if (!Object.prototype.hasOwnProperty.call(node, key))
31364
+ continue;
31365
+ if (++count > max)
31366
+ return count;
31367
+ const child = node[key];
31368
+ if (child !== null && typeof child === "object")
31369
+ stack.push(child);
31370
+ }
31371
+ }
31372
+ }
31373
+ return count;
31374
+ }
31375
+ function resolveMaxToolInputElements(value) {
31376
+ if (value === void 0 || value === Infinity)
31377
+ return void 0;
31378
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
31379
+ throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
31380
+ }
31381
+ return value;
31382
+ }
31300
31383
  var McpServer = class {
31301
31384
  constructor(serverInfo, options) {
31302
31385
  this._registeredResources = {};
@@ -31308,6 +31391,7 @@ var McpServer = class {
31308
31391
  this._resourceHandlersInitialized = false;
31309
31392
  this._promptHandlersInitialized = false;
31310
31393
  this.server = new Server(serverInfo, options);
31394
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
31311
31395
  }
31312
31396
  /**
31313
31397
  * Access experimental features.
@@ -31438,12 +31522,15 @@ var McpServer = class {
31438
31522
  * Validates tool input arguments against the tool's input schema.
31439
31523
  */
31440
31524
  async validateToolInput(tool, args, toolName) {
31525
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
31526
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
31527
+ }
31441
31528
  if (!tool.inputSchema) {
31442
31529
  return void 0;
31443
31530
  }
31444
31531
  const inputObj = normalizeObjectSchema(tool.inputSchema);
31445
31532
  const schemaToParse = inputObj ?? tool.inputSchema;
31446
- const parseResult = await safeParseAsync2(schemaToParse, args);
31533
+ const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
31447
31534
  if (!parseResult.success) {
31448
31535
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
31449
31536
  const errorMessage2 = getParseErrorMessage(error48);
@@ -31681,7 +31768,7 @@ var McpServer = class {
31681
31768
  }
31682
31769
  if (prompt.argsSchema) {
31683
31770
  const argsObj = normalizeObjectSchema(prompt.argsSchema);
31684
- const parseResult = await safeParseAsync2(argsObj, request2.params.arguments);
31771
+ const parseResult = await safeParseAsync2(argsObj, request2.params.arguments ?? {});
31685
31772
  if (!parseResult.success) {
31686
31773
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
31687
31774
  const errorMessage2 = getParseErrorMessage(error48);
@@ -32088,12 +32175,21 @@ var EMPTY_COMPLETION_RESULT = {
32088
32175
  }
32089
32176
  };
32090
32177
 
32091
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32178
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32092
32179
  var import_node_process = __toESM(require("node:process"), 1);
32093
32180
 
32094
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
32181
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
32182
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
32095
32183
  var ReadBuffer = class {
32184
+ constructor(options) {
32185
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
32186
+ }
32096
32187
  append(chunk) {
32188
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
32189
+ if (newSize > this._maxBufferSize) {
32190
+ this.clear();
32191
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
32192
+ }
32097
32193
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
32098
32194
  }
32099
32195
  readMessage() {
@@ -32119,20 +32215,26 @@ function serializeMessage(message) {
32119
32215
  return JSON.stringify(message) + "\n";
32120
32216
  }
32121
32217
 
32122
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32218
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32123
32219
  var StdioServerTransport = class {
32124
- constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout) {
32220
+ constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout, options) {
32125
32221
  this._stdin = _stdin;
32126
32222
  this._stdout = _stdout;
32127
- this._readBuffer = new ReadBuffer();
32128
32223
  this._started = false;
32129
32224
  this._ondata = (chunk) => {
32130
- this._readBuffer.append(chunk);
32131
- this.processReadBuffer();
32225
+ try {
32226
+ this._readBuffer.append(chunk);
32227
+ this.processReadBuffer();
32228
+ } catch (error48) {
32229
+ this.onerror?.(error48);
32230
+ this.close().catch(() => {
32231
+ });
32232
+ }
32132
32233
  };
32133
32234
  this._onerror = (error48) => {
32134
32235
  this.onerror?.(error48);
32135
32236
  };
32237
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
32136
32238
  }
32137
32239
  /**
32138
32240
  * Starts listening for messages on stdin.
@@ -36643,7 +36745,15 @@ var SkippedIssueSchema = external_exports3.looseObject({
36643
36745
  title: external_exports3.string(),
36644
36746
  skippedAt: external_exports3.string(),
36645
36747
  /** Why it was skipped: the vetter's reasons, or absent for a manual skip (#343). */
36646
- reason: external_exports3.string().optional()
36748
+ reason: external_exports3.string().optional(),
36749
+ /**
36750
+ * Whether this entry is permanent. Manual skips are permanent (true); vetter-
36751
+ * derived skips for transient conditions (linked PR closed, own PR in flight,
36752
+ * accumulated soft-skips) are non-permanent (false) and eligible for
36753
+ * cullExpiredSkips. Entries without this field (written before #348) default
36754
+ * to true so they are never silently dropped.
36755
+ */
36756
+ permanent: external_exports3.boolean().default(true)
36647
36757
  });
36648
36758
  var HorizonSchema = external_exports3.enum(["quick-win", "bigger-bet"]);
36649
36759
  var SavedCandidateSchema = external_exports3.looseObject({
@@ -36745,8 +36855,17 @@ var ScoutPreferencesSchema = external_exports3.looseObject({
36745
36855
  */
36746
36856
  featuresSplitRatio: external_exports3.number().min(0).max(1).default(0.6)
36747
36857
  });
36858
+ var CURRENT_STATE_REVISION = 1;
36748
36859
  var ScoutStateSchema = external_exports3.looseObject({
36749
36860
  version: external_exports3.literal(1),
36861
+ /**
36862
+ * Highest compatible migration applied to this state (see
36863
+ * `parseScoutState`). Unlike `version`, a bump here never locks out an
36864
+ * older binary: the key round-trips through the loose schema, and every
36865
+ * migration it tracks leaves the shape valid for version 1 readers.
36866
+ * State written before the counter existed reads as 0.
36867
+ */
36868
+ stateRevision: external_exports3.number().int().min(0).default(CURRENT_STATE_REVISION),
36750
36869
  preferences: ScoutPreferencesSchema.default(() => ScoutPreferencesSchema.parse({})),
36751
36870
  repoScores: external_exports3.record(external_exports3.string(), RepoScoreSchema).default({}),
36752
36871
  starredRepos: external_exports3.array(external_exports3.string()).default([]),
@@ -36810,7 +36929,32 @@ var ScoutStateSchema = external_exports3.looseObject({
36810
36929
  }))
36811
36930
  });
36812
36931
  function parseScoutState(raw) {
36813
- return ScoutStateSchema.parse(raw);
36932
+ return ScoutStateSchema.parse(migrateScoutState(raw));
36933
+ }
36934
+ var LEGACY_INTER_PHASE_DELAY_MS = 3e4;
36935
+ function migrateScoutState(raw) {
36936
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
36937
+ return raw;
36938
+ }
36939
+ const state = raw;
36940
+ const revision = typeof state.stateRevision === "number" ? state.stateRevision : 0;
36941
+ if (revision >= CURRENT_STATE_REVISION)
36942
+ return raw;
36943
+ let migrated = state;
36944
+ if (revision < 1)
36945
+ migrated = dropInheritedInterPhaseDelay(migrated);
36946
+ return { ...migrated, stateRevision: CURRENT_STATE_REVISION };
36947
+ }
36948
+ function dropInheritedInterPhaseDelay(state) {
36949
+ const prefs = state.preferences;
36950
+ if (typeof prefs !== "object" || prefs === null || Array.isArray(prefs)) {
36951
+ return state;
36952
+ }
36953
+ const preferences = prefs;
36954
+ if (preferences.interPhaseDelayMs !== LEGACY_INTER_PHASE_DELAY_MS) {
36955
+ return state;
36956
+ }
36957
+ return { ...state, preferences: { ...preferences, interPhaseDelayMs: 0 } };
36814
36958
  }
36815
36959
 
36816
36960
  // ../core/dist/core/issue-filtering.js
@@ -39213,7 +39357,7 @@ async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories
39213
39357
  const pushedSince = thirtyDaysAgo.toISOString().split("T")[0];
39214
39358
  const categoryTopics = getTopicsForCategories(projectCategories);
39215
39359
  const topicQuery = categoryTopics.length > 0 ? `topic:${categoryTopics[0]}` : "";
39216
- const phase3Query = `is:issue is:open no:assignee ${langQuery} ${topicQuery} stars:>=${minStars} pushed:>=${pushedSince} archived:false`.replace(/ +/g, " ").trim();
39360
+ const phase3Query = `is:issue is:open no:assignee ${langQuery} ${topicQuery} pushed:>=${pushedSince} archived:false`.replace(/ +/g, " ").trim();
39217
39361
  try {
39218
39362
  const data = await searchIssuesGraphQLFirst(octokit, {
39219
39363
  q: phase3Query,
@@ -40253,6 +40397,7 @@ function mergeStates(local, remote) {
40253
40397
  ...local,
40254
40398
  ...remote,
40255
40399
  version: 1,
40400
+ stateRevision: Math.max(local.stateRevision, remote.stateRevision),
40256
40401
  preferences,
40257
40402
  preferencesUpdatedAt,
40258
40403
  tombstones,
@@ -40499,10 +40644,12 @@ var OssScout = class {
40499
40644
  }
40500
40645
  /**
40501
40646
  * Multi-strategy issue search. Returns scored, sorted candidates.
40502
- * Skipped issues are excluded; the skip list is permanent (#343).
40647
+ * Skipped issues are excluded; permanent skips never re-surface; transient
40648
+ * vetter skips age out after 90 days (#343, #348).
40503
40649
  */
40504
40650
  async search(options) {
40505
40651
  this.evictStaleCacheEntries();
40652
+ this.cullExpiredSkips();
40506
40653
  const skippedUrls = new Set((this.state.skippedIssues ?? []).map((s) => s.url));
40507
40654
  if (options?.excludeRecentlySurfaced ?? true) {
40508
40655
  const ttlDays = options?.recentlySurfacedTtlDays ?? 7;
@@ -41005,11 +41152,17 @@ var OssScout = class {
41005
41152
  for (const c of candidates) {
41006
41153
  if (c.recommendation === "skip") {
41007
41154
  existing.delete(c.issue.url);
41155
+ const TERMINAL_REASONS = /* @__PURE__ */ new Set([
41156
+ "Issue is closed",
41157
+ "Linked PR already merged"
41158
+ ]);
41159
+ const permanent = c.reasonsToSkip.some((r) => TERMINAL_REASONS.has(r));
41008
41160
  this.skipIssue(c.issue.url, {
41009
41161
  repo: c.issue.repo,
41010
41162
  number: c.issue.number,
41011
41163
  title: c.issue.title,
41012
- reason: c.reasonsToSkip.join("; ") || "vetter: skip"
41164
+ reason: c.reasonsToSkip.join("; ") || "vetter: skip",
41165
+ permanent
41013
41166
  });
41014
41167
  continue;
41015
41168
  }
@@ -41063,12 +41216,21 @@ var OssScout = class {
41063
41216
  }
41064
41217
  // ── Skip List ───────────────────────────────────────────────────────
41065
41218
  /**
41066
- * Skip an issue — excludes it from future searches permanently (#343).
41219
+ * Skip an issue. Pass `permanent: false` for vetter-derived transient skips
41220
+ * so they expire via cullExpiredSkips (#348); manual skips default to permanent.
41067
41221
  */
41068
41222
  skipIssue(url2, metadata) {
41069
41223
  const existing = this.state.skippedIssues ?? [];
41070
- if (existing.some((s) => s.url === url2))
41224
+ const hit = existing.find((s) => s.url === url2);
41225
+ if (hit) {
41226
+ if (metadata?.permanent === true && hit.permanent !== true) {
41227
+ hit.permanent = true;
41228
+ hit.skippedAt = (/* @__PURE__ */ new Date()).toISOString();
41229
+ this.dirty = true;
41230
+ }
41071
41231
  return;
41232
+ }
41233
+ const permanent = metadata?.permanent ?? true;
41072
41234
  this.state.skippedIssues = [
41073
41235
  ...existing,
41074
41236
  {
@@ -41077,6 +41239,7 @@ var OssScout = class {
41077
41239
  number: metadata?.number ?? 0,
41078
41240
  title: metadata?.title ?? "",
41079
41241
  skippedAt: (/* @__PURE__ */ new Date()).toISOString(),
41242
+ permanent,
41080
41243
  ...metadata?.reason ? { reason: metadata.reason } : {}
41081
41244
  }
41082
41245
  ];
@@ -41110,25 +41273,35 @@ var OssScout = class {
41110
41273
  this.dirty = true;
41111
41274
  }
41112
41275
  /**
41113
- * Remove skipped issues older than maxDays (default 90). Not called by
41114
- * search since #343 (skips are permanent); available for an explicit prune.
41276
+ * Remove non-permanent skipped issues older than maxDays (default 90).
41277
+ * Called at the start of each search (#348) to re-surface issues whose
41278
+ * transient block condition (closed competing PR, own in-flight PR, soft-skip
41279
+ * accumulation) has aged out. Permanent entries (manual skips, terminal vetter
41280
+ * reasons) are never touched. Culled URLs get deletion tombstones so a gist
41281
+ * merge with another machine's older copy does not bring them back.
41115
41282
  * @returns The number of expired entries that were removed.
41116
41283
  */
41117
41284
  cullExpiredSkips(maxDays = 90) {
41118
41285
  const cutoff = /* @__PURE__ */ new Date();
41119
41286
  cutoff.setDate(cutoff.getDate() - maxDays);
41120
- const before = (this.state.skippedIssues ?? []).length;
41287
+ const culledUrls = [];
41121
41288
  this.state.skippedIssues = (this.state.skippedIssues ?? []).filter((s) => {
41289
+ if (s.permanent !== false)
41290
+ return true;
41122
41291
  const d = new Date(s.skippedAt);
41123
41292
  if (isNaN(d.getTime())) {
41124
41293
  return true;
41125
41294
  }
41126
- return d >= cutoff;
41295
+ if (d >= cutoff)
41296
+ return true;
41297
+ culledUrls.push(s.url);
41298
+ return false;
41127
41299
  });
41128
- const culled = before - this.state.skippedIssues.length;
41129
- if (culled > 0)
41300
+ if (culledUrls.length > 0) {
41301
+ this.addTombstones(culledUrls);
41130
41302
  this.dirty = true;
41131
- return culled;
41303
+ }
41304
+ return culledUrls.length;
41132
41305
  }
41133
41306
  // ── Persistence ─────────────────────────────────────────────────────
41134
41307
  /**
@@ -41441,6 +41614,7 @@ function registerTools(server, scout) {
41441
41614
  ]
41442
41615
  };
41443
41616
  } catch (err) {
41617
+ if (scout.isDirty()) await scout.checkpoint().catch(() => false);
41444
41618
  const msg = err instanceof Error ? err.message : String(err);
41445
41619
  return {
41446
41620
  content: [{ type: "text", text: `Error: ${msg}` }],
@@ -41720,7 +41894,7 @@ function registerResources(server, scout) {
41720
41894
  // src/index.ts
41721
41895
  var SERVER_INFO = {
41722
41896
  name: "oss-scout-mcp",
41723
- version: "0.12.0"
41897
+ version: "0.12.2"
41724
41898
  // x-release-please-version
41725
41899
  };
41726
41900
  function createServer(scout) {
package/dist/tools.js CHANGED
@@ -106,6 +106,12 @@ export function registerTools(server, scout) {
106
106
  };
107
107
  }
108
108
  catch (err) {
109
+ // State mutated before the failure (notably the search-rotation
110
+ // cursors, which advance even when a run finds nothing) would
111
+ // otherwise be dropped, so the same dry strategy/repo window leads
112
+ // every retry. The CLI persists this in its own catch; mirror it.
113
+ if (scout.isDirty())
114
+ await scout.checkpoint().catch(() => false);
109
115
  const msg = err instanceof Error ? err.message : String(err);
110
116
  return {
111
117
  content: [{ type: "text", text: `Error: ${msg}` }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-scout/mcp",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,18 +16,18 @@
16
16
  "dist/"
17
17
  ],
18
18
  "dependencies": {
19
- "@modelcontextprotocol/sdk": "^1.27.1",
19
+ "@modelcontextprotocol/sdk": "^1.32.1",
20
20
  "zod": "^4.3.6",
21
- "@oss-scout/core": "^1.8.0"
21
+ "@oss-scout/core": "^1.8.2"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^25.5.0",
25
- "@vitest/coverage-v8": "^4.1.4",
26
- "esbuild": "^0.27.4",
27
- "tsx": "^4.21.0",
25
+ "@vitest/coverage-v8": "^4.1.11",
26
+ "esbuild": "^0.28.1",
27
+ "tsx": "^4.23.15",
28
28
  "typescript": "^5.9.3",
29
29
  "vite": "^8.0.5",
30
- "vitest": "^4.1.4"
30
+ "vitest": "^4.1.11"
31
31
  },
32
32
  "keywords": [
33
33
  "mcp",