@oss-scout/mcp 0.12.0 → 0.12.1

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.1";
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.1", // x-release-please-version
19
19
  };
20
20
  /**
21
21
  * Build a fully-wired MCP server (tools + resources) around a scout.
@@ -24420,7 +24420,7 @@ function object(shape, params) {
24420
24420
  return new ZodMiniObject(def);
24421
24421
  }
24422
24422
 
24423
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
24423
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
24424
24424
  function isZ4Schema(s) {
24425
24425
  const schema = s;
24426
24426
  return !!schema._zod;
@@ -24504,17 +24504,33 @@ function normalizeObjectSchema(schema) {
24504
24504
  }
24505
24505
  return void 0;
24506
24506
  }
24507
+ function getDotPath(path5) {
24508
+ if (path5.length === 0) {
24509
+ return "object root";
24510
+ }
24511
+ return path5.reduce((acc, seg, index) => {
24512
+ if (index === 0) {
24513
+ return String(seg);
24514
+ }
24515
+ if (typeof seg === "number") {
24516
+ return `${acc}[${seg}]`;
24517
+ }
24518
+ return `${acc}.${seg}`;
24519
+ }, "");
24520
+ }
24507
24521
  function getParseErrorMessage(error48) {
24508
24522
  if (error48 && typeof error48 === "object") {
24523
+ if ("issues" in error48 && Array.isArray(error48.issues) && error48.issues.length > 0) {
24524
+ return error48.issues.map((i) => {
24525
+ if (!i.path?.length) {
24526
+ return i.message;
24527
+ }
24528
+ return `${i.message} at ${getDotPath(i.path)}`;
24529
+ }).join("\n");
24530
+ }
24509
24531
  if ("message" in error48 && typeof error48.message === "string") {
24510
24532
  return error48.message;
24511
24533
  }
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
24534
  try {
24519
24535
  return JSON.stringify(error48);
24520
24536
  } catch {
@@ -26710,7 +26726,7 @@ function date4(params) {
26710
26726
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
26711
26727
  config(en_default2());
26712
26728
 
26713
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
26729
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
26714
26730
  var LATEST_PROTOCOL_VERSION = "2025-11-25";
26715
26731
  var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
26716
26732
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
@@ -26720,10 +26736,9 @@ var ProgressTokenSchema = union([string2(), number2().int()]);
26720
26736
  var CursorSchema = string2();
26721
26737
  var TaskCreationParamsSchema = looseObject({
26722
26738
  /**
26723
- * Time in milliseconds to keep task results available after completion.
26724
- * If null, the task has unlimited lifetime until manually cleaned up.
26739
+ * Requested duration in milliseconds to retain task from creation.
26725
26740
  */
26726
- ttl: union([number2(), _null3()]).optional(),
26741
+ ttl: number2().optional(),
26727
26742
  /**
26728
26743
  * Time in milliseconds to wait between task status requests.
26729
26744
  */
@@ -27023,7 +27038,11 @@ var ClientCapabilitiesSchema = object2({
27023
27038
  /**
27024
27039
  * Present if the client supports task creation.
27025
27040
  */
27026
- tasks: ClientTasksCapabilitySchema.optional()
27041
+ tasks: ClientTasksCapabilitySchema.optional(),
27042
+ /**
27043
+ * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
27044
+ */
27045
+ extensions: record(string2(), AssertObjectSchema).optional()
27027
27046
  });
27028
27047
  var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
27029
27048
  /**
@@ -27084,7 +27103,11 @@ var ServerCapabilitiesSchema = object2({
27084
27103
  /**
27085
27104
  * Present if the server supports task creation.
27086
27105
  */
27087
- tasks: ServerTasksCapabilitySchema.optional()
27106
+ tasks: ServerTasksCapabilitySchema.optional(),
27107
+ /**
27108
+ * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
27109
+ */
27110
+ extensions: record(string2(), AssertObjectSchema).optional()
27088
27111
  });
27089
27112
  var InitializeResultSchema = ResultSchema.extend({
27090
27113
  /**
@@ -27276,6 +27299,12 @@ var ResourceSchema = object2({
27276
27299
  * The MIME type of this resource, if known.
27277
27300
  */
27278
27301
  mimeType: optional(string2()),
27302
+ /**
27303
+ * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
27304
+ *
27305
+ * This can be used by Hosts to display file sizes and estimate context window usage.
27306
+ */
27307
+ size: optional(number2()),
27279
27308
  /**
27280
27309
  * Optional annotations for the client.
27281
27310
  */
@@ -28228,7 +28257,7 @@ var UrlElicitationRequiredError = class extends McpError {
28228
28257
  }
28229
28258
  };
28230
28259
 
28231
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
28260
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
28232
28261
  function isTerminal(status) {
28233
28262
  return status === "completed" || status === "failed" || status === "cancelled";
28234
28263
  }
@@ -29517,7 +29546,7 @@ var zodToJsonSchema = (schema, options) => {
29517
29546
  return combined;
29518
29547
  };
29519
29548
 
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
29549
+ // ../../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
29550
  function mapMiniTarget(t) {
29522
29551
  if (!t)
29523
29552
  return "draft-7";
@@ -29559,7 +29588,7 @@ function parseWithCompat(schema, data) {
29559
29588
  return result.data;
29560
29589
  }
29561
29590
 
29562
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29591
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29563
29592
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
29564
29593
  var Protocol = class {
29565
29594
  constructor(_options) {
@@ -29600,6 +29629,9 @@ var Protocol = class {
29600
29629
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request2, extra) => {
29601
29630
  const handleTaskResult = async () => {
29602
29631
  const taskId = request2.params.taskId;
29632
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
29633
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
29634
+ }
29603
29635
  if (this._taskMessageQueue) {
29604
29636
  let queuedMessage;
29605
29637
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -29630,12 +29662,12 @@ var Protocol = class {
29630
29662
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
29631
29663
  }
29632
29664
  if (!isTerminal(task.status)) {
29633
- await this._waitForTaskUpdate(taskId, extra.signal);
29665
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
29634
29666
  return await handleTaskResult();
29635
29667
  }
29636
29668
  if (isTerminal(task.status)) {
29637
29669
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
29638
- this._clearTaskQueue(taskId);
29670
+ this._clearTaskQueue(taskId, extra.sessionId);
29639
29671
  return {
29640
29672
  ...result,
29641
29673
  _meta: {
@@ -29672,7 +29704,7 @@ var Protocol = class {
29672
29704
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
29673
29705
  }
29674
29706
  await this._taskStore.updateTaskStatus(request2.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
29675
- this._clearTaskQueue(request2.params.taskId);
29707
+ this._clearTaskQueue(request2.params.taskId, extra.sessionId);
29676
29708
  const cancelledTask = await this._taskStore.getTask(request2.params.taskId, extra.sessionId);
29677
29709
  if (!cancelledTask) {
29678
29710
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request2.params.taskId}`);
@@ -29800,6 +29832,19 @@ var Protocol = class {
29800
29832
  const handler2 = this._requestHandlers.get(request2.method) ?? this.fallbackRequestHandler;
29801
29833
  const capturedTransport = this._transport;
29802
29834
  const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
29835
+ const sessionId = capturedTransport?.sessionId;
29836
+ const store = this._taskStore;
29837
+ let relatedTaskFound = true;
29838
+ let relatedTaskLookup;
29839
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
29840
+ relatedTaskFound = false;
29841
+ relatedTaskLookup = (async () => {
29842
+ if (!await store.getTask(relatedTaskId, sessionId)) {
29843
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
29844
+ }
29845
+ relatedTaskFound = true;
29846
+ })();
29847
+ }
29803
29848
  if (handler2 === void 0) {
29804
29849
  const errorResponse = {
29805
29850
  jsonrpc: "2.0",
@@ -29809,7 +29854,10 @@ var Protocol = class {
29809
29854
  message: "Method not found"
29810
29855
  }
29811
29856
  };
29812
- if (relatedTaskId && this._taskMessageQueue) {
29857
+ if (relatedTaskId && relatedTaskLookup) {
29858
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
29859
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error48) => this._onerror(new Error(`Failed to send an error response: ${error48}`)));
29860
+ } else if (relatedTaskId && this._taskMessageQueue) {
29813
29861
  this._enqueueTaskMessage(relatedTaskId, {
29814
29862
  type: "error",
29815
29863
  message: errorResponse,
@@ -29860,7 +29908,10 @@ var Protocol = class {
29860
29908
  closeSSEStream: extra?.closeSSEStream,
29861
29909
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
29862
29910
  };
29863
- Promise.resolve().then(() => {
29911
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
29912
+ if (relatedTaskLookup && abortController.signal.aborted) {
29913
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
29914
+ }
29864
29915
  if (taskCreationParams) {
29865
29916
  this.assertTaskHandlerCapability(request2.method);
29866
29917
  }
@@ -29895,7 +29946,7 @@ var Protocol = class {
29895
29946
  ...error48["data"] !== void 0 && { data: error48["data"] }
29896
29947
  }
29897
29948
  };
29898
- if (relatedTaskId && this._taskMessageQueue) {
29949
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
29899
29950
  await this._enqueueTaskMessage(relatedTaskId, {
29900
29951
  type: "error",
29901
29952
  message: errorResponse,
@@ -30375,7 +30426,7 @@ var Protocol = class {
30375
30426
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
30376
30427
  }
30377
30428
  const maxQueueSize = this._options?.maxTaskQueueSize;
30378
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
30429
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
30379
30430
  }
30380
30431
  /**
30381
30432
  * Clears the message queue for a task and rejects any pending request resolvers.
@@ -30404,12 +30455,13 @@ var Protocol = class {
30404
30455
  * Uses polling to check for updates at the task's configured poll interval.
30405
30456
  * @param taskId The task ID to wait for
30406
30457
  * @param signal Abort signal to cancel the wait
30458
+ * @param sessionId Session of the request that waits, passed to the task store
30407
30459
  * @returns Promise that resolves when an update occurs or rejects if aborted
30408
30460
  */
30409
- async _waitForTaskUpdate(taskId, signal) {
30461
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
30410
30462
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
30411
30463
  try {
30412
- const task = await this._taskStore?.getTask(taskId);
30464
+ const task = await this._taskStore?.getTask(taskId, sessionId);
30413
30465
  if (task?.pollInterval) {
30414
30466
  interval = task.pollInterval;
30415
30467
  }
@@ -30513,7 +30565,7 @@ function mergeCapabilities(base, additional) {
30513
30565
  return result;
30514
30566
  }
30515
30567
 
30516
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
30568
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
30517
30569
  var import_ajv = __toESM(require_ajv(), 1);
30518
30570
  var import_ajv_formats = __toESM(require_dist(), 1);
30519
30571
  function createDefaultAjvInstance() {
@@ -30581,7 +30633,7 @@ var AjvJsonSchemaValidator = class {
30581
30633
  }
30582
30634
  };
30583
30635
 
30584
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30636
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30585
30637
  var ExperimentalServerTasks = class {
30586
30638
  constructor(_server) {
30587
30639
  this._server = _server;
@@ -30794,7 +30846,7 @@ var ExperimentalServerTasks = class {
30794
30846
  }
30795
30847
  };
30796
30848
 
30797
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30849
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30798
30850
  function assertToolsCallTaskCapability(requests, method, entityName) {
30799
30851
  if (!requests) {
30800
30852
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -30829,7 +30881,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
30829
30881
  }
30830
30882
  }
30831
30883
 
30832
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30884
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30833
30885
  var Server = class extends Protocol {
30834
30886
  /**
30835
30887
  * Initializes this server with the given name and version information.
@@ -30895,16 +30947,7 @@ var Server = class extends Protocol {
30895
30947
  if (!methodSchema) {
30896
30948
  throw new Error("Schema is missing a method literal");
30897
30949
  }
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
- }
30950
+ const methodValue = getLiteralValue(methodSchema);
30908
30951
  if (typeof methodValue !== "string") {
30909
30952
  throw new Error("Schema method literal must be a string");
30910
30953
  }
@@ -31209,7 +31252,7 @@ var Server = class extends Protocol {
31209
31252
  }
31210
31253
  };
31211
31254
 
31212
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
31255
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
31213
31256
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
31214
31257
  function isCompletable(schema) {
31215
31258
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -31223,7 +31266,7 @@ var McpZodTypeKind;
31223
31266
  McpZodTypeKind2["Completable"] = "McpCompletable";
31224
31267
  })(McpZodTypeKind || (McpZodTypeKind = {}));
31225
31268
 
31226
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
31269
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
31227
31270
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
31228
31271
  function validateToolName(name) {
31229
31272
  const warnings = [];
@@ -31281,7 +31324,7 @@ function validateAndWarnToolName(name) {
31281
31324
  return result.isValid;
31282
31325
  }
31283
31326
 
31284
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
31327
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
31285
31328
  var ExperimentalMcpServerTasks = class {
31286
31329
  constructor(_mcpServer) {
31287
31330
  this._mcpServer = _mcpServer;
@@ -31296,7 +31339,43 @@ var ExperimentalMcpServerTasks = class {
31296
31339
  }
31297
31340
  };
31298
31341
 
31299
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
31342
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
31343
+ function toolInputElementCount(value, max) {
31344
+ let count = 0;
31345
+ const stack = [value];
31346
+ while (stack.length > 0) {
31347
+ const node = stack.pop();
31348
+ if (node === null || typeof node !== "object")
31349
+ continue;
31350
+ if (Array.isArray(node)) {
31351
+ for (const child of node) {
31352
+ if (++count > max)
31353
+ return count;
31354
+ if (child !== null && typeof child === "object")
31355
+ stack.push(child);
31356
+ }
31357
+ } else {
31358
+ for (const key in node) {
31359
+ if (!Object.prototype.hasOwnProperty.call(node, key))
31360
+ continue;
31361
+ if (++count > max)
31362
+ return count;
31363
+ const child = node[key];
31364
+ if (child !== null && typeof child === "object")
31365
+ stack.push(child);
31366
+ }
31367
+ }
31368
+ }
31369
+ return count;
31370
+ }
31371
+ function resolveMaxToolInputElements(value) {
31372
+ if (value === void 0 || value === Infinity)
31373
+ return void 0;
31374
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
31375
+ throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
31376
+ }
31377
+ return value;
31378
+ }
31300
31379
  var McpServer = class {
31301
31380
  constructor(serverInfo, options) {
31302
31381
  this._registeredResources = {};
@@ -31308,6 +31387,7 @@ var McpServer = class {
31308
31387
  this._resourceHandlersInitialized = false;
31309
31388
  this._promptHandlersInitialized = false;
31310
31389
  this.server = new Server(serverInfo, options);
31390
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
31311
31391
  }
31312
31392
  /**
31313
31393
  * Access experimental features.
@@ -31438,12 +31518,15 @@ var McpServer = class {
31438
31518
  * Validates tool input arguments against the tool's input schema.
31439
31519
  */
31440
31520
  async validateToolInput(tool, args, toolName) {
31521
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
31522
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
31523
+ }
31441
31524
  if (!tool.inputSchema) {
31442
31525
  return void 0;
31443
31526
  }
31444
31527
  const inputObj = normalizeObjectSchema(tool.inputSchema);
31445
31528
  const schemaToParse = inputObj ?? tool.inputSchema;
31446
- const parseResult = await safeParseAsync2(schemaToParse, args);
31529
+ const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
31447
31530
  if (!parseResult.success) {
31448
31531
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
31449
31532
  const errorMessage2 = getParseErrorMessage(error48);
@@ -31681,7 +31764,7 @@ var McpServer = class {
31681
31764
  }
31682
31765
  if (prompt.argsSchema) {
31683
31766
  const argsObj = normalizeObjectSchema(prompt.argsSchema);
31684
- const parseResult = await safeParseAsync2(argsObj, request2.params.arguments);
31767
+ const parseResult = await safeParseAsync2(argsObj, request2.params.arguments ?? {});
31685
31768
  if (!parseResult.success) {
31686
31769
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
31687
31770
  const errorMessage2 = getParseErrorMessage(error48);
@@ -32088,12 +32171,21 @@ var EMPTY_COMPLETION_RESULT = {
32088
32171
  }
32089
32172
  };
32090
32173
 
32091
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32174
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32092
32175
  var import_node_process = __toESM(require("node:process"), 1);
32093
32176
 
32094
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
32177
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
32178
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
32095
32179
  var ReadBuffer = class {
32180
+ constructor(options) {
32181
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
32182
+ }
32096
32183
  append(chunk) {
32184
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
32185
+ if (newSize > this._maxBufferSize) {
32186
+ this.clear();
32187
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
32188
+ }
32097
32189
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
32098
32190
  }
32099
32191
  readMessage() {
@@ -32119,20 +32211,26 @@ function serializeMessage(message) {
32119
32211
  return JSON.stringify(message) + "\n";
32120
32212
  }
32121
32213
 
32122
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.28.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32214
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
32123
32215
  var StdioServerTransport = class {
32124
- constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout) {
32216
+ constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout, options) {
32125
32217
  this._stdin = _stdin;
32126
32218
  this._stdout = _stdout;
32127
- this._readBuffer = new ReadBuffer();
32128
32219
  this._started = false;
32129
32220
  this._ondata = (chunk) => {
32130
- this._readBuffer.append(chunk);
32131
- this.processReadBuffer();
32221
+ try {
32222
+ this._readBuffer.append(chunk);
32223
+ this.processReadBuffer();
32224
+ } catch (error48) {
32225
+ this.onerror?.(error48);
32226
+ this.close().catch(() => {
32227
+ });
32228
+ }
32132
32229
  };
32133
32230
  this._onerror = (error48) => {
32134
32231
  this.onerror?.(error48);
32135
32232
  };
32233
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
32136
32234
  }
32137
32235
  /**
32138
32236
  * Starts listening for messages on stdin.
@@ -36643,7 +36741,15 @@ var SkippedIssueSchema = external_exports3.looseObject({
36643
36741
  title: external_exports3.string(),
36644
36742
  skippedAt: external_exports3.string(),
36645
36743
  /** Why it was skipped: the vetter's reasons, or absent for a manual skip (#343). */
36646
- reason: external_exports3.string().optional()
36744
+ reason: external_exports3.string().optional(),
36745
+ /**
36746
+ * Whether this entry is permanent. Manual skips are permanent (true); vetter-
36747
+ * derived skips for transient conditions (linked PR closed, own PR in flight,
36748
+ * accumulated soft-skips) are non-permanent (false) and eligible for
36749
+ * cullExpiredSkips. Entries without this field (written before #348) default
36750
+ * to true so they are never silently dropped.
36751
+ */
36752
+ permanent: external_exports3.boolean().default(true)
36647
36753
  });
36648
36754
  var HorizonSchema = external_exports3.enum(["quick-win", "bigger-bet"]);
36649
36755
  var SavedCandidateSchema = external_exports3.looseObject({
@@ -36745,8 +36851,17 @@ var ScoutPreferencesSchema = external_exports3.looseObject({
36745
36851
  */
36746
36852
  featuresSplitRatio: external_exports3.number().min(0).max(1).default(0.6)
36747
36853
  });
36854
+ var CURRENT_STATE_REVISION = 1;
36748
36855
  var ScoutStateSchema = external_exports3.looseObject({
36749
36856
  version: external_exports3.literal(1),
36857
+ /**
36858
+ * Highest compatible migration applied to this state (see
36859
+ * `parseScoutState`). Unlike `version`, a bump here never locks out an
36860
+ * older binary: the key round-trips through the loose schema, and every
36861
+ * migration it tracks leaves the shape valid for version 1 readers.
36862
+ * State written before the counter existed reads as 0.
36863
+ */
36864
+ stateRevision: external_exports3.number().int().min(0).default(CURRENT_STATE_REVISION),
36750
36865
  preferences: ScoutPreferencesSchema.default(() => ScoutPreferencesSchema.parse({})),
36751
36866
  repoScores: external_exports3.record(external_exports3.string(), RepoScoreSchema).default({}),
36752
36867
  starredRepos: external_exports3.array(external_exports3.string()).default([]),
@@ -36810,7 +36925,32 @@ var ScoutStateSchema = external_exports3.looseObject({
36810
36925
  }))
36811
36926
  });
36812
36927
  function parseScoutState(raw) {
36813
- return ScoutStateSchema.parse(raw);
36928
+ return ScoutStateSchema.parse(migrateScoutState(raw));
36929
+ }
36930
+ var LEGACY_INTER_PHASE_DELAY_MS = 3e4;
36931
+ function migrateScoutState(raw) {
36932
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
36933
+ return raw;
36934
+ }
36935
+ const state = raw;
36936
+ const revision = typeof state.stateRevision === "number" ? state.stateRevision : 0;
36937
+ if (revision >= CURRENT_STATE_REVISION)
36938
+ return raw;
36939
+ let migrated = state;
36940
+ if (revision < 1)
36941
+ migrated = dropInheritedInterPhaseDelay(migrated);
36942
+ return { ...migrated, stateRevision: CURRENT_STATE_REVISION };
36943
+ }
36944
+ function dropInheritedInterPhaseDelay(state) {
36945
+ const prefs = state.preferences;
36946
+ if (typeof prefs !== "object" || prefs === null || Array.isArray(prefs)) {
36947
+ return state;
36948
+ }
36949
+ const preferences = prefs;
36950
+ if (preferences.interPhaseDelayMs !== LEGACY_INTER_PHASE_DELAY_MS) {
36951
+ return state;
36952
+ }
36953
+ return { ...state, preferences: { ...preferences, interPhaseDelayMs: 0 } };
36814
36954
  }
36815
36955
 
36816
36956
  // ../core/dist/core/issue-filtering.js
@@ -39213,7 +39353,7 @@ async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories
39213
39353
  const pushedSince = thirtyDaysAgo.toISOString().split("T")[0];
39214
39354
  const categoryTopics = getTopicsForCategories(projectCategories);
39215
39355
  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();
39356
+ const phase3Query = `is:issue is:open no:assignee ${langQuery} ${topicQuery} pushed:>=${pushedSince} archived:false`.replace(/ +/g, " ").trim();
39217
39357
  try {
39218
39358
  const data = await searchIssuesGraphQLFirst(octokit, {
39219
39359
  q: phase3Query,
@@ -40253,6 +40393,7 @@ function mergeStates(local, remote) {
40253
40393
  ...local,
40254
40394
  ...remote,
40255
40395
  version: 1,
40396
+ stateRevision: Math.max(local.stateRevision, remote.stateRevision),
40256
40397
  preferences,
40257
40398
  preferencesUpdatedAt,
40258
40399
  tombstones,
@@ -40499,10 +40640,12 @@ var OssScout = class {
40499
40640
  }
40500
40641
  /**
40501
40642
  * Multi-strategy issue search. Returns scored, sorted candidates.
40502
- * Skipped issues are excluded; the skip list is permanent (#343).
40643
+ * Skipped issues are excluded; permanent skips never re-surface; transient
40644
+ * vetter skips age out after 90 days (#343, #348).
40503
40645
  */
40504
40646
  async search(options) {
40505
40647
  this.evictStaleCacheEntries();
40648
+ this.cullExpiredSkips();
40506
40649
  const skippedUrls = new Set((this.state.skippedIssues ?? []).map((s) => s.url));
40507
40650
  if (options?.excludeRecentlySurfaced ?? true) {
40508
40651
  const ttlDays = options?.recentlySurfacedTtlDays ?? 7;
@@ -41005,11 +41148,17 @@ var OssScout = class {
41005
41148
  for (const c of candidates) {
41006
41149
  if (c.recommendation === "skip") {
41007
41150
  existing.delete(c.issue.url);
41151
+ const TERMINAL_REASONS = /* @__PURE__ */ new Set([
41152
+ "Issue is closed",
41153
+ "Linked PR already merged"
41154
+ ]);
41155
+ const permanent = c.reasonsToSkip.some((r) => TERMINAL_REASONS.has(r));
41008
41156
  this.skipIssue(c.issue.url, {
41009
41157
  repo: c.issue.repo,
41010
41158
  number: c.issue.number,
41011
41159
  title: c.issue.title,
41012
- reason: c.reasonsToSkip.join("; ") || "vetter: skip"
41160
+ reason: c.reasonsToSkip.join("; ") || "vetter: skip",
41161
+ permanent
41013
41162
  });
41014
41163
  continue;
41015
41164
  }
@@ -41063,12 +41212,20 @@ var OssScout = class {
41063
41212
  }
41064
41213
  // ── Skip List ───────────────────────────────────────────────────────
41065
41214
  /**
41066
- * Skip an issue — excludes it from future searches permanently (#343).
41215
+ * Skip an issue. Pass `permanent: false` for vetter-derived transient skips
41216
+ * so they expire via cullExpiredSkips (#348); manual skips default to permanent.
41067
41217
  */
41068
41218
  skipIssue(url2, metadata) {
41069
41219
  const existing = this.state.skippedIssues ?? [];
41070
- if (existing.some((s) => s.url === url2))
41220
+ const hit = existing.find((s) => s.url === url2);
41221
+ if (hit) {
41222
+ if (metadata?.permanent === true && hit.permanent !== true) {
41223
+ hit.permanent = true;
41224
+ this.dirty = true;
41225
+ }
41071
41226
  return;
41227
+ }
41228
+ const permanent = metadata?.permanent ?? true;
41072
41229
  this.state.skippedIssues = [
41073
41230
  ...existing,
41074
41231
  {
@@ -41077,6 +41234,7 @@ var OssScout = class {
41077
41234
  number: metadata?.number ?? 0,
41078
41235
  title: metadata?.title ?? "",
41079
41236
  skippedAt: (/* @__PURE__ */ new Date()).toISOString(),
41237
+ permanent,
41080
41238
  ...metadata?.reason ? { reason: metadata.reason } : {}
41081
41239
  }
41082
41240
  ];
@@ -41110,8 +41268,11 @@ var OssScout = class {
41110
41268
  this.dirty = true;
41111
41269
  }
41112
41270
  /**
41113
- * Remove skipped issues older than maxDays (default 90). Not called by
41114
- * search since #343 (skips are permanent); available for an explicit prune.
41271
+ * Remove non-permanent skipped issues older than maxDays (default 90).
41272
+ * Called at the start of each search (#348) to re-surface issues whose
41273
+ * transient block condition (closed competing PR, own in-flight PR, soft-skip
41274
+ * accumulation) has aged out. Permanent entries (manual skips, terminal vetter
41275
+ * reasons) are never touched.
41115
41276
  * @returns The number of expired entries that were removed.
41116
41277
  */
41117
41278
  cullExpiredSkips(maxDays = 90) {
@@ -41119,6 +41280,8 @@ var OssScout = class {
41119
41280
  cutoff.setDate(cutoff.getDate() - maxDays);
41120
41281
  const before = (this.state.skippedIssues ?? []).length;
41121
41282
  this.state.skippedIssues = (this.state.skippedIssues ?? []).filter((s) => {
41283
+ if (s.permanent !== false)
41284
+ return true;
41122
41285
  const d = new Date(s.skippedAt);
41123
41286
  if (isNaN(d.getTime())) {
41124
41287
  return true;
@@ -41441,6 +41604,7 @@ function registerTools(server, scout) {
41441
41604
  ]
41442
41605
  };
41443
41606
  } catch (err) {
41607
+ if (scout.isDirty()) await scout.checkpoint().catch(() => false);
41444
41608
  const msg = err instanceof Error ? err.message : String(err);
41445
41609
  return {
41446
41610
  content: [{ type: "text", text: `Error: ${msg}` }],
@@ -41720,7 +41884,7 @@ function registerResources(server, scout) {
41720
41884
  // src/index.ts
41721
41885
  var SERVER_INFO = {
41722
41886
  name: "oss-scout-mcp",
41723
- version: "0.12.0"
41887
+ version: "0.12.1"
41724
41888
  // x-release-please-version
41725
41889
  };
41726
41890
  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.1",
4
4
  "description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,9 +16,9 @@
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.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^25.5.0",