@ainyc/canonry 4.194.0 → 4.195.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/assets/agent-workspace/skills/canonry/SKILL.md +71 -1
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +30 -0
  3. package/assets/assets/{AuditHistoryPanel-C8hUWnaD.js → AuditHistoryPanel-vWUtVXOI.js} +1 -1
  4. package/assets/assets/{BacklinksPage-BcIdghvA.js → BacklinksPage-CSzuMEuv.js} +1 -1
  5. package/assets/assets/{HistoryPage-DF7PLFi1.js → HistoryPage-DtCTQ7CQ.js} +1 -1
  6. package/assets/assets/{MeasurementPropertyPage-BTlXQbYt.js → MeasurementPropertyPage-B7gTMWNs.js} +1 -1
  7. package/assets/assets/{ProjectPage-YiZ_i5eX.js → ProjectPage-Bsm0_jUY.js} +1 -1
  8. package/assets/assets/{RunRow-CfzuyTSM.js → RunRow-D5k4Rdo4.js} +1 -1
  9. package/assets/assets/{RunsPage-Diu_-YWl.js → RunsPage-DIXBB2Om.js} +1 -1
  10. package/assets/assets/{SettingsPage-g93aqkg2.js → SettingsPage-wYhBm6Az.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-DroMQJVF.js → SiteHealthSection-YebzsgDh.js} +3 -3
  12. package/assets/assets/{TrafficPage-BhHjy5Ee.js → TrafficPage-DtnSRFO8.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DI3aZIHo.js → TrafficSourceDetailPage-DYQ6mpYq.js} +1 -1
  14. package/assets/assets/{extract-error-message-DjXzzSl3.js → extract-error-message-BdChrkmo.js} +1 -1
  15. package/assets/assets/{index-33ZEL6A5.js → index-C_E2NRYY.js} +26 -26
  16. package/assets/assets/{react-sigma_core.esm.min-BlfsZb5G.js → react-sigma_core.esm.min-sKWUk_tG.js} +1 -1
  17. package/assets/assets/{v2-overview-adapter-C6cROMod.js → v2-overview-adapter-CkPt-sRK.js} +1 -1
  18. package/assets/index.html +1 -1
  19. package/dist/{chunk-X5AE3GNC.js → chunk-3WODA4JF.js} +276 -5
  20. package/dist/{chunk-7XXXNNE2.js → chunk-B2XM6AQ6.js} +280 -26
  21. package/dist/{chunk-ZA2QVBQO.js → chunk-H3RVNMOO.js} +73 -36
  22. package/dist/{chunk-AO5QJJE6.js → chunk-LJKAB4CX.js} +2 -2
  23. package/dist/{chunk-QVYGI6CL.js → chunk-UMU3IBXM.js} +1225 -474
  24. package/dist/cli.js +233 -154
  25. package/dist/index.js +4 -4
  26. package/dist/{intelligence-service-M4V3YPFZ.js → intelligence-service-MGQRJW4V.js} +2 -2
  27. package/dist/mcp.js +4 -4
  28. package/package.json +11 -11
@@ -21,6 +21,7 @@ import {
21
21
  adsOperationReconcileRequestSchema,
22
22
  adsPauseRequestSchema,
23
23
  adsUnresolvedOperationListQuerySchema,
24
+ apiKeyDtoSchema,
24
25
  backlinkSourceSchema,
25
26
  canonicalizeGtmAccountId,
26
27
  canonicalizeGtmResourceSelection,
@@ -38,8 +39,10 @@ import {
38
39
  googleAdsMetricsWindowSchema,
39
40
  gscPerformanceOrderBySchema,
40
41
  isReadOnlyKey,
42
+ isSensitiveDiagnosticQueryKey,
41
43
  keywordBatchRequestSchema,
42
44
  keywordGenerateRequestSchema,
45
+ logQuerySchema,
43
46
  measurementChangesQuerySchema,
44
47
  measurementDataQualityQuerySchema,
45
48
  measurementDiscoveryRequestSchema,
@@ -79,12 +82,15 @@ import {
79
82
  measurementQueryTemplateApplyRequestSchema,
80
83
  measurementQueryTemplateUpsertRequestSchema,
81
84
  measurementQuestionResultQuerySchema,
85
+ normalizeTelemetryStatus,
82
86
  notificationCreateRequestSchema,
83
87
  notificationEventSchema,
88
+ operationalLogListDtoSchema,
84
89
  organicEvidencePeriodSchema,
85
90
  projectConfigSchema,
86
91
  projectUpsertRequestSchema,
87
92
  providerQuotaPolicySchema,
93
+ providerSummaryEntryDtoSchema,
88
94
  queryBatchRequestSchema,
89
95
  queryGenerateRequestSchema,
90
96
  queryTrackingCommitRequestSchema,
@@ -97,16 +103,19 @@ import {
97
103
  runListFilterQuerySchema,
98
104
  runTriggerRequestSchema,
99
105
  schedulableRunKindSchema,
106
+ scheduleDtoSchema,
100
107
  scheduleUpsertRequestSchema,
108
+ settingsDtoSchema,
101
109
  skillsClientSchema,
102
110
  snapshotRequestInputSchema,
111
+ telemetryStatusDtoSchema,
103
112
  trafficConnectCloudRunRequestSchema,
104
113
  trafficConnectVercelRequestSchema,
105
114
  trafficConnectWordpressRequestSchema,
106
115
  trafficEventKindSchema,
107
116
  trafficSeriesGranularitySchema,
108
117
  visibilityReportRequestSchema
109
- } from "./chunk-X5AE3GNC.js";
118
+ } from "./chunk-3WODA4JF.js";
110
119
 
111
120
  // src/cli-error.ts
112
121
  function isMachineFormat(format) {
@@ -593,6 +602,58 @@ function configExists() {
593
602
  return fs.existsSync(getConfigPath());
594
603
  }
595
604
 
605
+ // src/client-reliability.ts
606
+ function parseRetryAfterMs(header, now = Date.now()) {
607
+ if (!header) return void 0;
608
+ const value = header.trim();
609
+ if (!value) return void 0;
610
+ if (/^\d+(?:\.\d+)?$/.test(value)) {
611
+ const delayMs2 = Number(value) * 1e3;
612
+ return Number.isFinite(delayMs2) && delayMs2 >= 0 && delayMs2 <= Number.MAX_SAFE_INTEGER ? delayMs2 : void 0;
613
+ }
614
+ if (!/^[a-z]{3},\s/i.test(value)) return void 0;
615
+ const target = Date.parse(value);
616
+ if (!Number.isFinite(target)) return void 0;
617
+ const delayMs = Math.max(0, target - now);
618
+ return Number.isSafeInteger(delayMs) ? delayMs : void 0;
619
+ }
620
+ function httpErrorDetails(serverDetails, response) {
621
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
622
+ const requestId = response.headers.get("x-request-id") ?? response.headers.get("request-id");
623
+ return {
624
+ ...serverDetails && typeof serverDetails === "object" && !Array.isArray(serverDetails) ? serverDetails : {},
625
+ httpStatus: response.status,
626
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs },
627
+ ...requestId ? { requestId } : {}
628
+ };
629
+ }
630
+ function isConnectionFailure(message) {
631
+ return message.includes("fetch failed") || message.includes("ECONNREFUSED") || message.includes("connect ECONNREFUSED");
632
+ }
633
+ function connectionFailureMessage(target) {
634
+ return `Could not connect to canonry server at ${redactRequestTarget(target)}. Check that this URL is reachable and the server is running. For a local Canonry instance, start it with "canonry serve" (or "canonry serve &" to run in background).`;
635
+ }
636
+ function redactRequestTarget(target) {
637
+ try {
638
+ const url = new URL(target);
639
+ url.username = "";
640
+ url.password = "";
641
+ for (const key of [...url.searchParams.keys()]) {
642
+ if (isSensitiveDiagnosticQueryKey(key)) {
643
+ url.searchParams.set(key, "<redacted>");
644
+ }
645
+ }
646
+ return url.toString();
647
+ } catch {
648
+ return target.replace(/\/\/[^/@]*@/, "//");
649
+ }
650
+ }
651
+
652
+ // src/package-version.ts
653
+ import { createRequire } from "module";
654
+ var _require = createRequire(import.meta.url);
655
+ var PACKAGE_VERSION = _require("../package.json").version;
656
+
596
657
  // ../api-client-generated/src/generated/core/bodySerializer.gen.ts
597
658
  var jsonBodySerializer = {
598
659
  bodySerializer: (body) => JSON.stringify(
@@ -2930,6 +2991,22 @@ var putApiV1SettingsProvidersByName = (options) => {
2930
2991
  }
2931
2992
  });
2932
2993
  };
2994
+ var putApiV1SettingsGoogle = (options) => {
2995
+ return (options.client ?? client).put({
2996
+ security: [
2997
+ {
2998
+ scheme: "bearer",
2999
+ type: "http"
3000
+ }
3001
+ ],
3002
+ url: "/api/v1/settings/google",
3003
+ ...options,
3004
+ headers: {
3005
+ "Content-Type": "application/json",
3006
+ ...options.headers
3007
+ }
3008
+ });
3009
+ };
2933
3010
  var getApiV1Keys = (options) => {
2934
3011
  return (options?.client ?? client).get({
2935
3012
  security: [
@@ -3078,6 +3155,30 @@ var putApiV1ProjectsByNameSchedule = (options) => {
3078
3155
  }
3079
3156
  });
3080
3157
  };
3158
+ var getApiV1ProjectsByNameSchedules = (options) => {
3159
+ return (options.client ?? client).get({
3160
+ security: [
3161
+ {
3162
+ scheme: "bearer",
3163
+ type: "http"
3164
+ }
3165
+ ],
3166
+ url: "/api/v1/projects/{name}/schedules",
3167
+ ...options
3168
+ });
3169
+ };
3170
+ var getApiV1NotificationsEvents = (options) => {
3171
+ return (options?.client ?? client).get({
3172
+ security: [
3173
+ {
3174
+ scheme: "bearer",
3175
+ type: "http"
3176
+ }
3177
+ ],
3178
+ url: "/api/v1/notifications/events",
3179
+ ...options
3180
+ });
3181
+ };
3081
3182
  var getApiV1ProjectsByNameNotifications = (options) => {
3082
3183
  return (options.client ?? client).get({
3083
3184
  security: [
@@ -3130,6 +3231,18 @@ var postApiV1ProjectsByNameNotificationsByIdTest = (options) => {
3130
3231
  ...options
3131
3232
  });
3132
3233
  };
3234
+ var getApiV1OperationsLogs = (options) => {
3235
+ return (options?.client ?? client).get({
3236
+ security: [
3237
+ {
3238
+ scheme: "bearer",
3239
+ type: "http"
3240
+ }
3241
+ ],
3242
+ url: "/api/v1/operations/logs",
3243
+ ...options
3244
+ });
3245
+ };
3133
3246
  var getApiV1Telemetry = (options) => {
3134
3247
  return (options?.client ?? client).get({
3135
3248
  security: [
@@ -5901,10 +6014,18 @@ function createClient2(opts) {
5901
6014
  }
5902
6015
 
5903
6016
  // src/client.ts
5904
- function createApiClient() {
6017
+ var SAFE_CLIENT_NAME = /^\w[\w./-]{0,127}$/;
6018
+ var SAFE_ACTOR_SESSION = /^\w[\w.:-]{0,127}$/;
6019
+ function safeClientName(value) {
6020
+ return value && SAFE_CLIENT_NAME.test(value) ? value : `canonry-cli/${PACKAGE_VERSION}`;
6021
+ }
6022
+ function safeActorSession(value) {
6023
+ return value && SAFE_ACTOR_SESSION.test(value) ? value : void 0;
6024
+ }
6025
+ function createApiClient(opts = {}) {
5905
6026
  const config = loadConfig();
5906
6027
  const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
5907
- return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
6028
+ return new ApiClient(config.apiUrl, config.apiKey, { ...opts, skipProbe: basePathResolved || opts.skipProbe });
5908
6029
  }
5909
6030
  var ApiClient = class {
5910
6031
  originUrl;
@@ -5912,6 +6033,7 @@ var ApiClient = class {
5912
6033
  probePromise = null;
5913
6034
  probeSkipped;
5914
6035
  heyClient;
6036
+ requestHeaders;
5915
6037
  /** Tracks the base URL most recently applied to `heyClient` so probe-driven updates only re-configure when something actually changed. */
5916
6038
  heyClientBaseUrl;
5917
6039
  constructor(baseUrl, apiKey, opts) {
@@ -5919,7 +6041,13 @@ var ApiClient = class {
5919
6041
  this.apiKey = apiKey;
5920
6042
  this.probeSkipped = opts?.skipProbe ?? false;
5921
6043
  this.heyClientBaseUrl = this.originUrl;
5922
- this.heyClient = createClient2({ baseUrl: this.originUrl, apiKey: this.apiKey });
6044
+ const actorSession = safeActorSession(opts?.actorSession);
6045
+ this.requestHeaders = {
6046
+ authorization: `Bearer ${this.apiKey}`,
6047
+ "user-agent": safeClientName(opts?.clientName),
6048
+ ...actorSession ? { "x-canonry-actor-session": actorSession } : {}
6049
+ };
6050
+ this.heyClient = createClient2({ baseUrl: this.originUrl, apiKey: this.apiKey, headers: this.requestHeaders });
5923
6051
  }
5924
6052
  /**
5925
6053
  * On first API call, probe /health to auto-discover basePath when the user
@@ -5954,7 +6082,7 @@ var ApiClient = class {
5954
6082
  /** Apply the probe's discovered base URL to `heyClient` if it changed since last call. */
5955
6083
  refreshHeyClientBaseUrl() {
5956
6084
  if (this.originUrl !== this.heyClientBaseUrl) {
5957
- this.heyClient.setConfig({ baseUrl: this.originUrl, headers: { authorization: `Bearer ${this.apiKey}` } });
6085
+ this.heyClient.setConfig({ baseUrl: this.originUrl, headers: this.requestHeaders });
5958
6086
  this.heyClientBaseUrl = this.originUrl;
5959
6087
  }
5960
6088
  }
@@ -5978,10 +6106,10 @@ var ApiClient = class {
5978
6106
  process.stderr.write(`[trace] (sdk-call) \u2192 ERROR (${Date.now() - traceStart}ms): ${msg}
5979
6107
  `);
5980
6108
  }
5981
- if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
6109
+ if (isConnectionFailure(msg)) {
5982
6110
  throw new CliError({
5983
6111
  code: "CONNECTION_ERROR",
5984
- message: `Could not connect to canonry server at ${this.originUrl}. Start it with "canonry serve" (or "canonry serve &" to run in background).`,
6112
+ message: connectionFailureMessage(this.originUrl),
5985
6113
  exitCode: EXIT_SYSTEM_ERROR
5986
6114
  });
5987
6115
  }
@@ -5990,7 +6118,7 @@ var ApiClient = class {
5990
6118
  if (traceEnabled) {
5991
6119
  const durMs = Date.now() - traceStart;
5992
6120
  process.stderr.write(
5993
- `[trace] ${result.request.method} ${result.request.url} \u2192 ${result.response.status} (${durMs}ms)
6121
+ `[trace] ${result.request.method} ${redactRequestTarget(result.request.url)} \u2192 ${result.response.status} (${durMs}ms)
5994
6122
  `
5995
6123
  );
5996
6124
  }
@@ -5998,17 +6126,17 @@ var ApiClient = class {
5998
6126
  if (result.response.ok && contentType?.toLowerCase().startsWith("text/html")) {
5999
6127
  throw new CliError({
6000
6128
  code: "UNEXPECTED_RESPONSE_FORMAT",
6001
- message: `Expected a JSON response from the canonry API at ${result.request.url}, but received ${contentType} (HTTP ${result.response.status}). Check the server URL and base path.`,
6129
+ message: `Expected a JSON response from the canonry API at ${redactRequestTarget(result.request.url)}, but received ${contentType} (HTTP ${result.response.status}). Check the server URL and base path.`,
6002
6130
  exitCode: EXIT_SYSTEM_ERROR,
6003
6131
  details: {
6004
- requestUrl: result.request.url,
6132
+ requestUrl: redactRequestTarget(result.request.url),
6005
6133
  contentType,
6006
6134
  httpStatus: result.response.status
6007
6135
  }
6008
6136
  });
6009
6137
  }
6010
6138
  if (result.error !== void 0 && result.error !== null) {
6011
- const errorObj = typeof result.error === "object" && result.error !== null && "error" in result.error && typeof result.error.error === "object" && result.error.error !== null ? result.error.error : null;
6139
+ const errorObj = typeof result.error === "object" && "error" in result.error && typeof result.error.error === "object" && result.error.error !== null ? result.error.error : null;
6012
6140
  const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${result.response.status}: ${result.response.statusText}`;
6013
6141
  const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
6014
6142
  const exitCode = result.response.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
@@ -6016,7 +6144,7 @@ var ApiClient = class {
6016
6144
  code,
6017
6145
  message: msg,
6018
6146
  exitCode,
6019
- details: { ...errorObj?.details ?? {}, httpStatus: result.response.status }
6147
+ details: httpErrorDetails(errorObj?.details, result.response)
6020
6148
  });
6021
6149
  }
6022
6150
  return result.data;
@@ -6081,7 +6209,7 @@ var ApiClient = class {
6081
6209
  await this.probeBasePath();
6082
6210
  const url = `${this.originUrl}/api/v1${path3}`;
6083
6211
  const headers = {
6084
- Authorization: `Bearer ${this.apiKey}`,
6212
+ ...this.requestHeaders,
6085
6213
  "Content-Type": "application/json",
6086
6214
  Accept: "text/event-stream"
6087
6215
  };
@@ -6091,10 +6219,10 @@ var ApiClient = class {
6091
6219
  } catch (err) {
6092
6220
  if (err instanceof CliError) throw err;
6093
6221
  const msg = describeError(err);
6094
- if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
6222
+ if (isConnectionFailure(msg)) {
6095
6223
  throw new CliError({
6096
6224
  code: "CONNECTION_ERROR",
6097
- message: `Could not connect to canonry server at ${this.originUrl}. Start it with "canonry serve" (or "canonry serve &" to run in background).`,
6225
+ message: connectionFailureMessage(this.originUrl),
6098
6226
  exitCode: EXIT_SYSTEM_ERROR
6099
6227
  });
6100
6228
  }
@@ -6111,7 +6239,7 @@ var ApiClient = class {
6111
6239
  const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
6112
6240
  const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
6113
6241
  const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
6114
- throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
6242
+ throw new CliError({ code, message: msg, exitCode, details: httpErrorDetails(errorObj?.details, res) });
6115
6243
  }
6116
6244
  return res;
6117
6245
  }
@@ -6962,16 +7090,32 @@ var ApiClient = class {
6962
7090
  () => putApiV1SettingsProvidersByName({ client: this.heyClient, path: { name }, body })
6963
7091
  );
6964
7092
  }
7093
+ async updateGoogleSettings(body) {
7094
+ return this.invoke(
7095
+ () => putApiV1SettingsGoogle({ client: this.heyClient, body })
7096
+ );
7097
+ }
6965
7098
  async createSnapshot(body) {
6966
7099
  return this.invoke(() => postApiV1Snapshot({ client: this.heyClient, body }));
6967
7100
  }
6968
7101
  async getTelemetry() {
6969
- return this.invoke(() => getApiV1Telemetry({ client: this.heyClient }));
7102
+ return normalizeTelemetryStatus(await this.invoke(() => getApiV1Telemetry({ client: this.heyClient })));
7103
+ }
7104
+ async listOperationalLogs(query = {}) {
7105
+ return this.invoke(() => getApiV1OperationsLogs({ client: this.heyClient, query }));
6970
7106
  }
6971
7107
  async updateTelemetry(enabled) {
6972
- return this.invoke(() => putApiV1Telemetry({ client: this.heyClient, body: { enabled } }));
7108
+ return normalizeTelemetryStatus(await this.invoke(() => putApiV1Telemetry({ client: this.heyClient, body: { enabled } })));
6973
7109
  }
6974
7110
  // ── Schedules / notifications / locations ───────────────────────────────
7111
+ async listSchedules(project) {
7112
+ return this.invoke(
7113
+ () => getApiV1ProjectsByNameSchedules({ client: this.heyClient, path: { name: project } })
7114
+ );
7115
+ }
7116
+ async listNotificationEvents() {
7117
+ return this.invoke(() => getApiV1NotificationsEvents({ client: this.heyClient }));
7118
+ }
6975
7119
  async putSchedule(project, body) {
6976
7120
  return this.invoke(
6977
7121
  () => putApiV1ProjectsByNameSchedule({
@@ -8791,11 +8935,6 @@ var ApiClient = class {
8791
8935
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8792
8936
  import { z as z5 } from "zod";
8793
8937
 
8794
- // src/package-version.ts
8795
- import { createRequire } from "module";
8796
- var _require = createRequire(import.meta.url);
8797
- var PACKAGE_VERSION = _require("../package.json").version;
8798
-
8799
8938
  // src/mcp/tool-registry.ts
8800
8939
  import { z as z4 } from "zod";
8801
8940
 
@@ -9479,6 +9618,8 @@ var scheduleReadInputSchema = z4.object({
9479
9618
  project: projectNameSchema,
9480
9619
  kind: schedulableRunKindSchema.optional().describe('Schedulable run kind. Defaults to "answer-visibility" if omitted.')
9481
9620
  });
9621
+ var scheduleListOutputSchema = z4.object({ items: z4.array(scheduleDtoSchema) });
9622
+ var notificationEventsOutputSchema = z4.object({ items: z4.array(notificationEventSchema) });
9482
9623
  var agentWebhookAttachInputSchema = z4.object({
9483
9624
  project: projectNameSchema,
9484
9625
  url: z4.string().url()
@@ -10275,6 +10416,30 @@ var canonryMcpTools = [
10275
10416
  openApiOperations: ["GET /api/v1/projects/{name}/schedule"],
10276
10417
  handler: (client2, input) => client2.getSchedule(input.project, input.kind)
10277
10418
  }),
10419
+ defineTool({
10420
+ name: "canonry_schedules_list",
10421
+ title: "List project schedules",
10422
+ description: "List every stored schedule for a Canonry project, including each schedulable run kind. This reads configuration only and never starts work.",
10423
+ access: "read",
10424
+ tier: "setup",
10425
+ inputSchema: projectInputSchema,
10426
+ outputSchema: scheduleListOutputSchema,
10427
+ annotations: readAnnotations(),
10428
+ openApiOperations: ["GET /api/v1/projects/{name}/schedules"],
10429
+ handler: (client2, input) => client2.listSchedules(input.project)
10430
+ }),
10431
+ defineTool({
10432
+ name: "canonry_notification_events",
10433
+ title: "List notification event types",
10434
+ description: "List the supported stored notification event names. This is a static capability read; it does not send, test, or change notifications.",
10435
+ access: "read",
10436
+ tier: "setup",
10437
+ inputSchema: emptyInputSchema,
10438
+ outputSchema: notificationEventsOutputSchema,
10439
+ annotations: readAnnotations(),
10440
+ openApiOperations: ["GET /api/v1/notifications/events"],
10441
+ handler: (client2) => client2.listNotificationEvents()
10442
+ }),
10278
10443
  defineTool({
10279
10444
  name: "canonry_backlinks_latest_release",
10280
10445
  title: "Discover latest Common Crawl release",
@@ -10319,10 +10484,73 @@ var canonryMcpTools = [
10319
10484
  access: "read",
10320
10485
  tier: "core",
10321
10486
  inputSchema: emptyInputSchema,
10487
+ outputSchema: settingsDtoSchema,
10322
10488
  annotations: readAnnotations(),
10323
10489
  openApiOperations: ["GET /api/v1/settings"],
10324
10490
  handler: (client2) => client2.getSettings()
10325
10491
  }),
10492
+ defineTool({
10493
+ name: "canonry_key_self",
10494
+ title: "Get current API key identity",
10495
+ description: "Read safe metadata for the credential backing this MCP connection, including its scopes, project reach, and derived read-only status. It never exposes the bearer token or stored hash and does not grant mutation access.",
10496
+ access: "read",
10497
+ tier: "core",
10498
+ inputSchema: emptyInputSchema,
10499
+ outputSchema: apiKeyDtoSchema,
10500
+ annotations: readAnnotations(),
10501
+ openApiOperations: ["GET /api/v1/keys/self"],
10502
+ handler: (client2) => client2.getApiKeySelf()
10503
+ }),
10504
+ defineTool({
10505
+ name: "canonry_logs_list",
10506
+ title: "Read runtime logs",
10507
+ description: "Read bounded, redacted runtime logs, including application and HTTP failures. Filter by actor, request, run, project, level, module, or time. Inspect retention, retentionPolicy, dropped, and captureErrors for coverage; file-backed hosts retain logs across restarts. Separate from audit history. Requires instance-wide logs.read (or wildcard), and an admin role for user sessions; project-scoped keys are refused even with a project filter. Cursors expire on retention eviction; reuse with the same filters. No provider calls.",
10508
+ access: "read",
10509
+ tier: "setup",
10510
+ inputSchema: logQuerySchema,
10511
+ outputSchema: operationalLogListDtoSchema,
10512
+ annotations: readAnnotations(false),
10513
+ openApiOperations: ["GET /api/v1/operations/logs"],
10514
+ handler: async (client2, input) => client2.listOperationalLogs(input)
10515
+ }),
10516
+ defineTool({
10517
+ name: "canonry_telemetry_update",
10518
+ title: "Update server telemetry preference",
10519
+ description: "Set the connected server telemetry preference with explicit approval and settings.write. Returns effective state including environment overrides. Does not change the agent machine configuration.",
10520
+ access: "write",
10521
+ requiredScope: "settings.write",
10522
+ tier: "setup",
10523
+ inputSchema: z4.object({ enabled: z4.boolean() }).strict(),
10524
+ outputSchema: telemetryStatusDtoSchema,
10525
+ annotations: writeAnnotations({ idempotentHint: true }),
10526
+ openApiOperations: ["PUT /api/v1/telemetry"],
10527
+ handler: async (client2, input) => client2.updateTelemetry(input.enabled)
10528
+ }),
10529
+ defineTool({
10530
+ name: "canonry_provider_settings_update",
10531
+ title: "Update non-secret provider settings",
10532
+ description: "Change model or quota for an already-configured provider, with explicit approval and settings.write. Preserves existing credentials. Does not accept API keys, base URLs, or credential rotation; use the secure operator setup flow for those.",
10533
+ access: "write",
10534
+ requiredScope: "settings.write",
10535
+ tier: "setup",
10536
+ inputSchema: z4.object({ provider: z4.string().min(1), model: z4.string().trim().min(1).optional(), quota: providerQuotaPolicySchema.partial().strict().optional() }).strict().refine((input) => input.model !== void 0 || input.quota !== void 0 && Object.keys(input.quota).length > 0, "Provide a model or at least one quota field"),
10537
+ outputSchema: providerSummaryEntryDtoSchema,
10538
+ annotations: writeAnnotations({ idempotentHint: true }),
10539
+ openApiOperations: ["PUT /api/v1/settings/providers/{name}"],
10540
+ handler: async (client2, { provider, ...request }) => client2.updateProvider(provider, request)
10541
+ }),
10542
+ defineTool({
10543
+ name: "canonry_telemetry_get",
10544
+ title: "Get telemetry status",
10545
+ description: "Read the server telemetry status and effective reason. This safe settings read does not change telemetry collection.",
10546
+ access: "read",
10547
+ tier: "setup",
10548
+ inputSchema: emptyInputSchema,
10549
+ outputSchema: telemetryStatusDtoSchema,
10550
+ annotations: readAnnotations(),
10551
+ openApiOperations: ["GET /api/v1/telemetry"],
10552
+ handler: (client2) => client2.getTelemetry()
10553
+ }),
10326
10554
  defineTool({
10327
10555
  name: "canonry_google_connections_list",
10328
10556
  title: "List Google connections",
@@ -12410,6 +12638,7 @@ import { ZodError } from "zod";
12410
12638
  function jsonToolResult(value) {
12411
12639
  const result = value === void 0 ? { ok: true } : value;
12412
12640
  return {
12641
+ structuredContent: toStructuredContent(result),
12413
12642
  content: [
12414
12643
  {
12415
12644
  type: "text",
@@ -12419,16 +12648,23 @@ function jsonToolResult(value) {
12419
12648
  };
12420
12649
  }
12421
12650
  function errorToolResult(error) {
12651
+ const envelope = toCanonryErrorEnvelope(error);
12422
12652
  return {
12423
12653
  isError: true,
12654
+ structuredContent: envelope,
12424
12655
  content: [
12425
12656
  {
12426
12657
  type: "text",
12427
- text: JSON.stringify(toCanonryErrorEnvelope(error), null, 2)
12658
+ text: JSON.stringify(envelope, null, 2)
12428
12659
  }
12429
12660
  ]
12430
12661
  };
12431
12662
  }
12663
+ function toStructuredContent(value) {
12664
+ if (Array.isArray(value)) return { items: value };
12665
+ if (value !== null && typeof value === "object") return value;
12666
+ return { value };
12667
+ }
12432
12668
  async function withToolErrors(handler) {
12433
12669
  try {
12434
12670
  return jsonToolResult(await handler());
@@ -12714,6 +12950,23 @@ var OPERATIONS_GUIDE = {
12714
12950
  "approvalRule": "Get explicit approval for the exact target, action, and bounded provider work before crossing a boundary. Existing approval covers only its stated scope; stop on refusal or ambiguous results.",
12715
12951
  "authority": "Guidance and tool visibility are not permission grants. Server-side roles, scopes, project boundaries, quotas, and approval receipts remain authoritative. Never bypass a refusal with different credentials or endpoints.",
12716
12952
  "workflows": {
12953
+ "operations": {
12954
+ "keywords": [
12955
+ "operations",
12956
+ "permissions",
12957
+ "settings",
12958
+ "telemetry",
12959
+ "logs",
12960
+ "configuration"
12961
+ ],
12962
+ "next": [
12963
+ "canonry_key_self",
12964
+ "canonry_settings_get",
12965
+ "canonry_telemetry_get",
12966
+ "canonry_logs_list"
12967
+ ],
12968
+ "guidance": "Inspect current credential and server state before proposing changes. Runtime logs require instance-wide logs.read; inspect retention, dropped, and captureErrors before drawing conclusions. File-backed hosts retain bounded redacted logs across restarts. Logs are separate from audit history. Respect a 403; never substitute credentials to bypass it. Settings writes require settings.write and explicit approval."
12969
+ },
12717
12970
  "status": {
12718
12971
  "keywords": [
12719
12972
  "status",
@@ -12810,7 +13063,7 @@ var OPERATIONS_GUIDE = {
12810
13063
  "guidance": "Read the stored report for the selected project and period. Retain evidence dates, scope, sample sizes, missing data, and separate mention/citation signals; do not generate fresh runs implicitly."
12811
13064
  }
12812
13065
  },
12813
- "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `prospecting`, `measurement`, `integrations`, `reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Keep sample sizes and ties visible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
13066
+ "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Keep sample sizes and ties visible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` to inspect the current credential's scopes and project\nboundary without exposing its token. `canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard), and\nadmin role for signed-in users. Project-scoped keys cannot use it, even with a\nproject filter. A `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
12814
13067
  };
12815
13068
 
12816
13069
  // src/mcp/operations-guide.ts
@@ -12853,7 +13106,7 @@ function createCanonryMcpServer(options = {}) {
12853
13106
  }
12854
13107
  var SERVER_INSTRUCTIONS = OPERATIONS_GUIDE.initialize;
12855
13108
  function createCanonryMcpServerWithCatalog(options = {}) {
12856
- const clientFactory = options.clientFactory ?? createApiClient;
13109
+ const clientFactory = options.clientFactory ?? (() => createApiClient({ clientName: "canonry-mcp" }));
12857
13110
  const client2 = clientFactory();
12858
13111
  const scope = options.scope ?? "all";
12859
13112
  const server = new McpServer({
@@ -12873,6 +13126,7 @@ function createCanonryMcpServerWithCatalog(options = {}) {
12873
13126
  title: tool.title,
12874
13127
  description: tool.description,
12875
13128
  inputSchema: tool.inputSchema,
13129
+ outputSchema: tool.outputSchema,
12876
13130
  annotations: tool.annotations
12877
13131
  },
12878
13132
  async (input) => withToolErrors(async () => {