lace-mcp 0.0.2-alpha.29 → 0.0.2-alpha.30

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/cli.js +167 -89
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -633,10 +633,10 @@ var init_uninstall = __esm({
633
633
  }
634
634
  });
635
635
 
636
- // ../../libs/shared/src/mcpReviewLoader.ts
636
+ // ../../libs/shared/src/mcp/mcpReviewLoader.ts
637
637
  var ReviewLoaderError;
638
638
  var init_mcpReviewLoader = __esm({
639
- "../../libs/shared/src/mcpReviewLoader.ts"() {
639
+ "../../libs/shared/src/mcp/mcpReviewLoader.ts"() {
640
640
  "use strict";
641
641
  ReviewLoaderError = class extends Error {
642
642
  contentText;
@@ -655,13 +655,13 @@ var init_mcpReviewLoader = __esm({
655
655
  }
656
656
  });
657
657
 
658
- // ../../libs/shared/src/errorMessages.ts
658
+ // ../../libs/shared/src/errors/errorMessages.ts
659
659
  function lookupErrorMessage(code) {
660
660
  return code ? ERROR_MESSAGES[code] : void 0;
661
661
  }
662
662
  var ERROR_MESSAGES;
663
663
  var init_errorMessages = __esm({
664
- "../../libs/shared/src/errorMessages.ts"() {
664
+ "../../libs/shared/src/errors/errorMessages.ts"() {
665
665
  "use strict";
666
666
  ERROR_MESSAGES = {
667
667
  // --- Auth / Session ---
@@ -16127,10 +16127,10 @@ var init_mcpAttachments = __esm({
16127
16127
  }
16128
16128
  });
16129
16129
 
16130
- // ../../libs/shared/src/reviewCommentRunBinding.ts
16130
+ // ../../libs/shared/src/review/reviewCommentRunBinding.ts
16131
16131
  var reviewCommentRunBindingSchema;
16132
16132
  var init_reviewCommentRunBinding = __esm({
16133
- "../../libs/shared/src/reviewCommentRunBinding.ts"() {
16133
+ "../../libs/shared/src/review/reviewCommentRunBinding.ts"() {
16134
16134
  "use strict";
16135
16135
  init_zod();
16136
16136
  reviewCommentRunBindingSchema = external_exports.object({
@@ -16953,7 +16953,7 @@ var init_reviewLoaderImages = __esm({
16953
16953
  }
16954
16954
  });
16955
16955
 
16956
- // ../../libs/shared/src/resolvePublishMode.ts
16956
+ // ../../libs/shared/src/publishing/resolvePublishMode.ts
16957
16957
  function requireTitle(title, message) {
16958
16958
  if (!title) throw new ReviewLoaderError(message);
16959
16959
  return title;
@@ -17000,7 +17000,7 @@ function resolvePublishMode(input) {
17000
17000
  };
17001
17001
  }
17002
17002
  var init_resolvePublishMode = __esm({
17003
- "../../libs/shared/src/resolvePublishMode.ts"() {
17003
+ "../../libs/shared/src/publishing/resolvePublishMode.ts"() {
17004
17004
  "use strict";
17005
17005
  init_mcpReviewLoader();
17006
17006
  }
@@ -17365,19 +17365,23 @@ async function fetchReviewElementComments(reviewId, config4, pageId) {
17365
17365
  }
17366
17366
  async function fetchDecisionThreadContext(decision) {
17367
17367
  const config4 = readMcpCredentials();
17368
- if (!config4) return null;
17368
+ if (!config4) return [];
17369
17369
  const reviewId = decision.review_id;
17370
- if (!reviewId) return null;
17370
+ if (!reviewId) return [];
17371
17371
  try {
17372
17372
  const res = await fetch(`${config4.apiBase}/lace/reviews/${encodeURIComponent(reviewId)}/thread-context`, {
17373
17373
  headers: jsonAuthHeaders(config4),
17374
17374
  signal: AbortSignal.timeout(API_FETCH_TIMEOUT_MS)
17375
17375
  });
17376
- if (!res.ok) return null;
17376
+ if (!res.ok) return [];
17377
17377
  const payload = await res.json();
17378
- return payload.context ?? null;
17378
+ return (payload.messages ?? []).map((message) => ({
17379
+ role: message.role,
17380
+ visibleText: message.visibleText,
17381
+ createdAt: message.createdAt
17382
+ }));
17379
17383
  } catch {
17380
- return null;
17384
+ return [];
17381
17385
  }
17382
17386
  }
17383
17387
  async function fetchDiscussionThreads(config4, reviewId) {
@@ -17438,8 +17442,7 @@ function createHttpReviewLoader() {
17438
17442
  const detail = await loadReviewCommentDetailFromHttp(decisionId, requireConfig());
17439
17443
  const decision = detail?.decision;
17440
17444
  if (!decision) return [];
17441
- const context2 = await fetchDecisionThreadContext(decision);
17442
- return context2 ? [{ text: context2 }] : [];
17445
+ return fetchDecisionThreadContext(decision);
17443
17446
  },
17444
17447
  async resolveDiscussionAccess(reviewId) {
17445
17448
  try {
@@ -34574,7 +34577,7 @@ var init_mcp = __esm({
34574
34577
  }
34575
34578
  });
34576
34579
 
34577
- // ../../libs/shared/src/mcpToolHelpers.ts
34580
+ // ../../libs/shared/src/mcp/mcpToolHelpers.ts
34578
34581
  function errorMessage(error54) {
34579
34582
  if (error54 instanceof Error) return error54.message;
34580
34583
  if (typeof error54 === "object" && error54 !== null && "message" in error54 && typeof error54.message === "string") {
@@ -34602,14 +34605,47 @@ function loaderErrorResponse(error54, fallback) {
34602
34605
  function loaderScope(scope) {
34603
34606
  return { orgId: scope.orgId, userId: scope.userId };
34604
34607
  }
34608
+ function extractErrorCode(result) {
34609
+ if (!result.isError || !result.structuredContent?.error) return void 0;
34610
+ const error54 = result.structuredContent.error;
34611
+ if (typeof error54 === "string") return error54;
34612
+ if (typeof error54 === "object" && error54 !== null && "code" in error54) return String(error54.code);
34613
+ return void 0;
34614
+ }
34615
+ function toolCompletedProps(result, toolName, duration_ms) {
34616
+ const error_code = extractErrorCode(result);
34617
+ return {
34618
+ tool: toolName,
34619
+ outcome: result.isError ? "error" : "success",
34620
+ duration_ms,
34621
+ ...error_code === void 0 ? {} : { error_code }
34622
+ };
34623
+ }
34624
+ function withToolMetrics(toolName, handler, opts) {
34625
+ return async (params) => {
34626
+ opts.onToolInvoked?.(toolName);
34627
+ const start = performance.now();
34628
+ try {
34629
+ const result = await handler(params);
34630
+ opts.onToolCompleted?.(toolCompletedProps(result, toolName, Math.round(performance.now() - start)));
34631
+ return result;
34632
+ } catch (error54) {
34633
+ const duration_ms = Math.round(performance.now() - start);
34634
+ opts.onToolError?.(error54, { toolName });
34635
+ const errorResult = loaderErrorResponse(error54, `Failed to execute ${toolName}. Please try again.`);
34636
+ opts.onToolCompleted?.(toolCompletedProps(errorResult, toolName, duration_ms));
34637
+ return errorResult;
34638
+ }
34639
+ };
34640
+ }
34605
34641
  var init_mcpToolHelpers = __esm({
34606
- "../../libs/shared/src/mcpToolHelpers.ts"() {
34642
+ "../../libs/shared/src/mcp/mcpToolHelpers.ts"() {
34607
34643
  "use strict";
34608
34644
  init_mcpReviewLoader();
34609
34645
  }
34610
34646
  });
34611
34647
 
34612
- // ../../libs/shared/src/mcpPromptRegistration.ts
34648
+ // ../../libs/shared/src/mcp/mcpPromptRegistration.ts
34613
34649
  function registerPublishPrototypePrompt(server2) {
34614
34650
  server2.registerPrompt(
34615
34651
  "publish-prototype",
@@ -34715,7 +34751,7 @@ function registerMcpPrompts({ server: server2, loader, getScope }) {
34715
34751
  registerCheckFeedbackPrompt(server2, loader, getScope);
34716
34752
  }
34717
34753
  var init_mcpPromptRegistration = __esm({
34718
- "../../libs/shared/src/mcpPromptRegistration.ts"() {
34754
+ "../../libs/shared/src/mcp/mcpPromptRegistration.ts"() {
34719
34755
  "use strict";
34720
34756
  init_completable();
34721
34757
  init_zod();
@@ -34723,7 +34759,7 @@ var init_mcpPromptRegistration = __esm({
34723
34759
  }
34724
34760
  });
34725
34761
 
34726
- // ../../libs/shared/src/mcpResourceRegistration.ts
34762
+ // ../../libs/shared/src/mcp/mcpResourceRegistration.ts
34727
34763
  function registerMcpResources({ server: server2, loader, getScope }) {
34728
34764
  server2.registerResource(
34729
34765
  "review",
@@ -34773,17 +34809,17 @@ function registerMcpResources({ server: server2, loader, getScope }) {
34773
34809
  );
34774
34810
  }
34775
34811
  var init_mcpResourceRegistration = __esm({
34776
- "../../libs/shared/src/mcpResourceRegistration.ts"() {
34812
+ "../../libs/shared/src/mcp/mcpResourceRegistration.ts"() {
34777
34813
  "use strict";
34778
34814
  init_mcp();
34779
34815
  init_mcpToolHelpers();
34780
34816
  }
34781
34817
  });
34782
34818
 
34783
- // ../../libs/shared/src/artifactPublishLimits.ts
34819
+ // ../../libs/shared/src/publishing/artifactPublishLimits.ts
34784
34820
  var MIB, MAX_INLINE_FILE_BYTES, MAX_INLINE_TOTAL_BYTES, MAX_ASSET_COUNT, MAX_ASSET_FILE_BYTES, MAX_ASSET_TOTAL_BYTES, MAX_ARCHIVE_BYTES, MAX_SOURCE_ARCHIVE_BYTES, MAX_ARCHIVE_FILE_BYTES, MAX_ARCHIVE_UNCOMPRESSED_BYTES, SHA256_HEX_RE;
34785
34821
  var init_artifactPublishLimits = __esm({
34786
- "../../libs/shared/src/artifactPublishLimits.ts"() {
34822
+ "../../libs/shared/src/publishing/artifactPublishLimits.ts"() {
34787
34823
  "use strict";
34788
34824
  MIB = 1024 * 1024;
34789
34825
  MAX_INLINE_FILE_BYTES = 25 * MIB;
@@ -34799,7 +34835,7 @@ var init_artifactPublishLimits = __esm({
34799
34835
  }
34800
34836
  });
34801
34837
 
34802
- // ../../libs/shared/src/mcpToolPublishReview.ts
34838
+ // ../../libs/shared/src/mcp/mcpToolPublishReview.ts
34803
34839
  function isPreparedResult(result) {
34804
34840
  return "phase" in result && result.phase === "awaiting_upload";
34805
34841
  }
@@ -34963,7 +34999,8 @@ function registerPublishReviewTool({
34963
34999
  loader,
34964
35000
  getScope,
34965
35001
  onToolInvoked,
34966
- onToolError
35002
+ onToolError,
35003
+ onToolCompleted
34967
35004
  }) {
34968
35005
  if (!loader.publishReview) return;
34969
35006
  const publishReview = loader.publishReview;
@@ -34976,15 +35013,18 @@ function registerPublishReviewTool({
34976
35013
  outputSchema: publishReviewOutputSchema,
34977
35014
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
34978
35015
  },
34979
- async (params) => {
34980
- onToolInvoked?.("publish_review");
34981
- return handlePublishReviewTool({ params, publishReview, getScope, onToolError });
34982
- }
35016
+ withToolMetrics(
35017
+ "publish_review",
35018
+ async (params) => {
35019
+ return handlePublishReviewTool({ params, publishReview, getScope, onToolError });
35020
+ },
35021
+ { onToolInvoked, onToolError, onToolCompleted }
35022
+ )
34983
35023
  );
34984
35024
  }
34985
35025
  var publishReviewOutputSchema, PUBLISH_REVIEW_DESCRIPTION, publishReviewInputSchema;
34986
35026
  var init_mcpToolPublishReview = __esm({
34987
- "../../libs/shared/src/mcpToolPublishReview.ts"() {
35027
+ "../../libs/shared/src/mcp/mcpToolPublishReview.ts"() {
34988
35028
  "use strict";
34989
35029
  init_zod();
34990
35030
  init_artifactPublishLimits();
@@ -35043,7 +35083,7 @@ var init_mcpToolPublishReview = __esm({
35043
35083
  }
35044
35084
  });
35045
35085
 
35046
- // ../../libs/shared/src/reviewToolHandlers.ts
35086
+ // ../../libs/shared/src/mcp/reviewToolHandlers.ts
35047
35087
  function toSummary(view) {
35048
35088
  const summary = {};
35049
35089
  for (const key of SUMMARY_KEYS) summary[key] = view[key];
@@ -35101,6 +35141,7 @@ function decisionBbox(decision) {
35101
35141
  function decisionNotFoundResult(id) {
35102
35142
  return {
35103
35143
  content: [{ type: "text", text: `Decision "${id}" not found. Use search_comments to find valid IDs.` }],
35144
+ structuredContent: { error: "not_found", id },
35104
35145
  isError: true
35105
35146
  };
35106
35147
  }
@@ -35208,6 +35249,7 @@ async function handleSearchDiscussion(loader, params, scope, onError) {
35208
35249
  if (!canAccess) {
35209
35250
  return {
35210
35251
  content: [{ type: "text", text: "Access denied - discussion sharing is not enabled for this review." }],
35252
+ structuredContent: { error: "sharing_disabled", reviewId: params.reviewId },
35211
35253
  isError: true
35212
35254
  };
35213
35255
  }
@@ -35215,17 +35257,26 @@ async function handleSearchDiscussion(loader, params, scope, onError) {
35215
35257
  if (!threadId2) {
35216
35258
  const links = await loader.loadThreadLinks(params.reviewId, scope);
35217
35259
  if (links.length === 0) {
35218
- return { content: [{ type: "text", text: "No discussion threads linked to this review." }] };
35260
+ return {
35261
+ content: [{ type: "text", text: "No discussion threads linked to this review." }],
35262
+ structuredContent: { error: "no_threads", reviewId: params.reviewId }
35263
+ };
35219
35264
  }
35220
35265
  threadId2 = links[0].thread_id;
35221
35266
  }
35222
35267
  const messages = await loader.loadThreadMessages(threadId2, scope);
35223
35268
  if (messages.length === 0) {
35224
- return { content: [{ type: "text", text: "No messages in this discussion thread." }] };
35269
+ return {
35270
+ content: [{ type: "text", text: "No messages in this discussion thread." }],
35271
+ structuredContent: { error: "no_messages", reviewId: params.reviewId, threadId: threadId2 }
35272
+ };
35225
35273
  }
35226
35274
  const filtered = params.query ? filterDiscussionMessages(messages, params.query) : messages;
35227
35275
  if (!filtered) {
35228
- return { content: [{ type: "text", text: "No messages matching the query in this discussion thread." }] };
35276
+ return {
35277
+ content: [{ type: "text", text: "No messages matching the query in this discussion thread." }],
35278
+ structuredContent: { error: "no_matches", reviewId: params.reviewId, threadId: threadId2, query: params.query }
35279
+ };
35229
35280
  }
35230
35281
  return { content: [{ type: "text", text: formatDiscussionThread(threadId2, filtered) }] };
35231
35282
  } catch (e) {
@@ -35265,11 +35316,11 @@ async function handleApplyComment(loader, id, include, scope) {
35265
35316
  }
35266
35317
  var searchCommentsSchema, SUMMARY_KEYS;
35267
35318
  var init_reviewToolHandlers = __esm({
35268
- "../../libs/shared/src/reviewToolHandlers.ts"() {
35319
+ "../../libs/shared/src/mcp/reviewToolHandlers.ts"() {
35269
35320
  "use strict";
35270
35321
  init_zod();
35271
- init_mcpToolHelpers();
35272
35322
  init_review();
35323
+ init_mcpToolHelpers();
35273
35324
  searchCommentsSchema = {
35274
35325
  query: external_exports.string().optional().describe("Text search across headline, body, and element text"),
35275
35326
  status: external_exports.enum(["resolved", "suggested", "all"]).default("resolved").optional().describe("Filter by comment status (default: resolved)"),
@@ -35328,13 +35379,14 @@ var init_reviewToolHandlers = __esm({
35328
35379
  }
35329
35380
  });
35330
35381
 
35331
- // ../../libs/shared/src/mcpToolRegistration.ts
35382
+ // ../../libs/shared/src/mcp/mcpToolRegistration.ts
35332
35383
  function registerSearchCommentsTool({
35333
35384
  server: server2,
35334
35385
  loader,
35335
35386
  getScope,
35336
35387
  onToolInvoked,
35337
- onToolError
35388
+ onToolError,
35389
+ onToolCompleted
35338
35390
  }) {
35339
35391
  server2.registerTool(
35340
35392
  "search_comments",
@@ -35344,23 +35396,28 @@ function registerSearchCommentsTool({
35344
35396
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
35345
35397
  inputSchema: searchCommentsSchema
35346
35398
  },
35347
- async (params) => {
35348
- onToolInvoked?.("search_comments");
35349
- try {
35399
+ withToolMetrics(
35400
+ "search_comments",
35401
+ async (params) => {
35350
35402
  return await handleSearchComments(
35351
35403
  loader,
35352
35404
  params,
35353
35405
  loaderScope(getScope()),
35354
35406
  (e) => onToolError?.(e, { toolName: "search_comments" })
35355
35407
  );
35356
- } catch (e) {
35357
- onToolError?.(e, { toolName: "search_comments" });
35358
- return loaderErrorResponse(e, "Failed to search comments. Please try again.");
35359
- }
35360
- }
35408
+ },
35409
+ { onToolInvoked, onToolError, onToolCompleted }
35410
+ )
35361
35411
  );
35362
35412
  }
35363
- function registerApplyDecisionTool({ server: server2, loader, getScope, onToolInvoked, onToolError }) {
35413
+ function registerApplyDecisionTool({
35414
+ server: server2,
35415
+ loader,
35416
+ getScope,
35417
+ onToolInvoked,
35418
+ onToolError,
35419
+ onToolCompleted
35420
+ }) {
35364
35421
  server2.registerTool(
35365
35422
  "apply_comment",
35366
35423
  {
@@ -35374,18 +35431,23 @@ function registerApplyDecisionTool({ server: server2, loader, getScope, onToolIn
35374
35431
  },
35375
35432
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
35376
35433
  },
35377
- async ({ id, include = "instructions" }) => {
35378
- onToolInvoked?.("apply_comment");
35379
- try {
35434
+ withToolMetrics(
35435
+ "apply_comment",
35436
+ async ({ id, include = "instructions" }) => {
35380
35437
  return await handleApplyComment(loader, id, include, loaderScope(getScope()));
35381
- } catch (e) {
35382
- onToolError?.(e, { toolName: "apply_comment" });
35383
- return loaderErrorResponse(e, "Failed to apply decision. Please try again.");
35384
- }
35385
- }
35438
+ },
35439
+ { onToolInvoked, onToolError, onToolCompleted }
35440
+ )
35386
35441
  );
35387
35442
  }
35388
- function registerListReviewsTool({ server: server2, loader, getScope, onToolInvoked, onToolError }) {
35443
+ function registerListReviewsTool({
35444
+ server: server2,
35445
+ loader,
35446
+ getScope,
35447
+ onToolInvoked,
35448
+ onToolError,
35449
+ onToolCompleted
35450
+ }) {
35389
35451
  server2.registerTool(
35390
35452
  "list_reviews",
35391
35453
  {
@@ -35400,9 +35462,9 @@ function registerListReviewsTool({ server: server2, loader, getScope, onToolInvo
35400
35462
  },
35401
35463
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
35402
35464
  },
35403
- async (params) => {
35404
- onToolInvoked?.("list_reviews");
35405
- try {
35465
+ withToolMetrics(
35466
+ "list_reviews",
35467
+ async (params) => {
35406
35468
  const result = await loader.listReviews(params, loaderScope(getScope()));
35407
35469
  const text = result.nextCursor ? `Showing ${result.reviews.length} reviews. Pass cursor "${result.nextCursor}" to see more.` : `Showing ${result.reviews.length} reviews.`;
35408
35470
  return {
@@ -35411,14 +35473,19 @@ function registerListReviewsTool({ server: server2, loader, getScope, onToolInvo
35411
35473
  { type: "text", text: JSON.stringify(result) }
35412
35474
  ]
35413
35475
  };
35414
- } catch (e) {
35415
- onToolError?.(e, { toolName: "list_reviews" });
35416
- return loaderErrorResponse(e, "Failed to list reviews. Please try again.");
35417
- }
35418
- }
35476
+ },
35477
+ { onToolInvoked, onToolError, onToolCompleted }
35478
+ )
35419
35479
  );
35420
35480
  }
35421
- function registerListProjectsTool({ server: server2, loader, getScope, onToolInvoked, onToolError }) {
35481
+ function registerListProjectsTool({
35482
+ server: server2,
35483
+ loader,
35484
+ getScope,
35485
+ onToolInvoked,
35486
+ onToolError,
35487
+ onToolCompleted
35488
+ }) {
35422
35489
  server2.registerTool(
35423
35490
  "list_projects",
35424
35491
  {
@@ -35430,9 +35497,9 @@ function registerListProjectsTool({ server: server2, loader, getScope, onToolInv
35430
35497
  },
35431
35498
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
35432
35499
  },
35433
- async (params) => {
35434
- onToolInvoked?.("list_projects");
35435
- try {
35500
+ withToolMetrics(
35501
+ "list_projects",
35502
+ async (params) => {
35436
35503
  const result = await loader.listProjects(params, loaderScope(getScope()));
35437
35504
  const text = result.nextCursor ? `Showing ${result.projects.length} projects. Pass cursor "${result.nextCursor}" to see more.` : `Showing ${result.projects.length} projects.`;
35438
35505
  return {
@@ -35441,11 +35508,9 @@ function registerListProjectsTool({ server: server2, loader, getScope, onToolInv
35441
35508
  { type: "text", text: JSON.stringify(result) }
35442
35509
  ]
35443
35510
  };
35444
- } catch (e) {
35445
- onToolError?.(e, { toolName: "list_projects" });
35446
- return loaderErrorResponse(e, "Failed to list projects. Please try again.");
35447
- }
35448
- }
35511
+ },
35512
+ { onToolInvoked, onToolError, onToolCompleted }
35513
+ )
35449
35514
  );
35450
35515
  }
35451
35516
  function registerSearchDiscussionTool({
@@ -35453,7 +35518,8 @@ function registerSearchDiscussionTool({
35453
35518
  loader,
35454
35519
  getScope,
35455
35520
  onToolInvoked,
35456
- onToolError
35521
+ onToolError,
35522
+ onToolCompleted
35457
35523
  }) {
35458
35524
  server2.registerTool(
35459
35525
  "search_discussion",
@@ -35467,20 +35533,18 @@ function registerSearchDiscussionTool({
35467
35533
  },
35468
35534
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
35469
35535
  },
35470
- async (params) => {
35471
- onToolInvoked?.("search_discussion");
35472
- try {
35536
+ withToolMetrics(
35537
+ "search_discussion",
35538
+ async (params) => {
35473
35539
  return await handleSearchDiscussion(
35474
35540
  loader,
35475
35541
  params,
35476
35542
  loaderScope(getScope()),
35477
35543
  (e) => onToolError?.(e, { toolName: "search_discussion" })
35478
35544
  );
35479
- } catch (e) {
35480
- onToolError?.(e, { toolName: "search_discussion" });
35481
- return loaderErrorResponse(e, "Failed to search discussion. Please try again.");
35482
- }
35483
- }
35545
+ },
35546
+ { onToolInvoked, onToolError, onToolCompleted }
35547
+ )
35484
35548
  );
35485
35549
  }
35486
35550
  function registerMcpTools(opts) {
@@ -35492,7 +35556,7 @@ function registerMcpTools(opts) {
35492
35556
  registerSearchDiscussionTool(opts);
35493
35557
  }
35494
35558
  var init_mcpToolRegistration = __esm({
35495
- "../../libs/shared/src/mcpToolRegistration.ts"() {
35559
+ "../../libs/shared/src/mcp/mcpToolRegistration.ts"() {
35496
35560
  "use strict";
35497
35561
  init_zod();
35498
35562
  init_mcpToolHelpers();
@@ -35501,14 +35565,14 @@ var init_mcpToolRegistration = __esm({
35501
35565
  }
35502
35566
  });
35503
35567
 
35504
- // ../../libs/shared/src/mcpSurface.ts
35568
+ // ../../libs/shared/src/mcp/mcpSurface.ts
35505
35569
  function registerMcpSurface(opts) {
35506
35570
  registerMcpTools(opts);
35507
35571
  registerMcpResources(opts);
35508
35572
  registerMcpPrompts(opts);
35509
35573
  }
35510
35574
  var init_mcpSurface = __esm({
35511
- "../../libs/shared/src/mcpSurface.ts"() {
35575
+ "../../libs/shared/src/mcp/mcpSurface.ts"() {
35512
35576
  "use strict";
35513
35577
  init_mcpPromptRegistration();
35514
35578
  init_mcpResourceRegistration();
@@ -35516,7 +35580,7 @@ var init_mcpSurface = __esm({
35516
35580
  }
35517
35581
  });
35518
35582
 
35519
- // ../../libs/shared/src/observability.ts
35583
+ // ../../libs/shared/src/platform/observability.ts
35520
35584
  function normalizeKey(key) {
35521
35585
  return key.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
35522
35586
  }
@@ -35643,7 +35707,7 @@ function sanitizeObservabilitySentryEvent(event) {
35643
35707
  }
35644
35708
  var OBSERVABILITY_REDACTED, OBSERVABILITY_MAX_ARRAY_ITEMS, DEFAULT_MAX_DEPTH, DATA_URL_PATTERN, LONG_STRING_LIMIT, SECRET_KEYS, CONTENT_KEYS, CONTENT_SUFFIXES, SAFE_METADATA_KEYS, UNSANITIZED;
35645
35709
  var init_observability = __esm({
35646
- "../../libs/shared/src/observability.ts"() {
35710
+ "../../libs/shared/src/platform/observability.ts"() {
35647
35711
  "use strict";
35648
35712
  OBSERVABILITY_REDACTED = "[redacted]";
35649
35713
  OBSERVABILITY_MAX_ARRAY_ITEMS = 20;
@@ -35753,6 +35817,18 @@ function trackToolInvoked(tool) {
35753
35817
  properties
35754
35818
  });
35755
35819
  }
35820
+ function trackToolCompleted(props) {
35821
+ const properties = sanitizeObservabilityProperties({
35822
+ app: "mcp",
35823
+ ...props,
35824
+ identified: distinctId !== null
35825
+ });
35826
+ queue.push({
35827
+ event: MCP_COMPLETED_EVENT,
35828
+ distinct_id: distinctId ?? ANONYMOUS_DISTINCT_ID,
35829
+ properties
35830
+ });
35831
+ }
35756
35832
  async function flushAnalytics() {
35757
35833
  if (!POSTHOG_API_KEY || queue.length === 0) return;
35758
35834
  const batch = queue.splice(0);
@@ -35766,7 +35842,7 @@ async function flushAnalytics() {
35766
35842
  } catch {
35767
35843
  }
35768
35844
  }
35769
- var POSTHOG_API_KEY, POSTHOG_HOST, FLUSH_TIMEOUT_MS, ANONYMOUS_DISTINCT_ID, queue, distinctId, MCP_EVENT;
35845
+ var POSTHOG_API_KEY, POSTHOG_HOST, FLUSH_TIMEOUT_MS, ANONYMOUS_DISTINCT_ID, queue, distinctId, MCP_EVENT, MCP_COMPLETED_EVENT;
35770
35846
  var init_analytics = __esm({
35771
35847
  "src/analytics.ts"() {
35772
35848
  "use strict";
@@ -35778,6 +35854,7 @@ var init_analytics = __esm({
35778
35854
  queue = [];
35779
35855
  distinctId = null;
35780
35856
  MCP_EVENT = "mcp_tool_invoked";
35857
+ MCP_COMPLETED_EVENT = "mcp_tool_completed";
35781
35858
  }
35782
35859
  });
35783
35860
 
@@ -81808,7 +81885,8 @@ function createLaceServer() {
81808
81885
  loader,
81809
81886
  getScope: getLocalScope,
81810
81887
  onToolInvoked: trackToolInvoked,
81811
- onToolError: handleToolError
81888
+ onToolError: handleToolError,
81889
+ onToolCompleted: trackToolCompleted
81812
81890
  });
81813
81891
  return server2;
81814
81892
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lace-mcp",
3
- "version": "0.0.2-alpha.29",
3
+ "version": "0.0.2-alpha.30",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/cli.js",