@canonry/canonry 7.32.0 → 7.44.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.
- package/assets/agent-workspace/skills/aero/references/agent-operations.md +9 -0
- package/assets/agent-workspace/skills/aero/references/site-health.md +12 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +9 -0
- package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +24 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +207 -4
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +14 -2
- package/assets/assets/{AuditHistoryPanel-cQyVA85P.js → AuditHistoryPanel-CvANw3ds.js} +1 -1
- package/assets/assets/{BacklinksPage-UGZ_9N7F.js → BacklinksPage-C6x9ELjk.js} +1 -1
- package/assets/assets/{HistoryPage-UJVFZ5LK.js → HistoryPage-r36HX1cC.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-ChpOxQfx.js → MeasurementPropertyPage-ComoCzR4.js} +3 -3
- package/assets/assets/ProjectPage-CIeU-buk.js +8 -0
- package/assets/assets/RunsPage-DI6eJATZ.js +1 -0
- package/assets/assets/{SettingsPage-DeKxjuTr.js → SettingsPage-Bw7GdXxs.js} +1 -1
- package/assets/assets/{SiteHealthSection-COeVsgop.js → SiteHealthSection-C-JaNKpS.js} +3 -3
- package/assets/assets/{TrafficPage-DmMmeJKK.js → TrafficPage-Cnsp276v.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CUehseZe.js → TrafficSourceDetailPage-CBIaJk7e.js} +1 -1
- package/assets/assets/{extract-error-message-BsxdS7Po.js → extract-error-message-C_Owa0n9.js} +1 -1
- package/assets/assets/index-ZS9sSTMS.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min--MbbBWzJ.js → react-sigma_core.esm.min-ugyh-ooq.js} +1 -1
- package/assets/assets/{v2-overview-adapter-D-IScpdp.js → v2-overview-adapter-BLSrVQ7K.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/chunk-3JVF3ULD.js +550 -0
- package/dist/chunk-45Z5RJZW.js +562 -0
- package/dist/{chunk-QFXD7GR5.js → chunk-5LY7EG4U.js} +238 -20
- package/dist/chunk-EJRV7RBZ.js +23066 -0
- package/dist/{chunk-WRHA54BN.js → chunk-G47XJVAS.js} +5885 -3900
- package/dist/{chunk-42XUY243.js → chunk-GZ2KIONF.js} +11507 -10391
- package/dist/chunk-HGMYEDAQ.js +79 -0
- package/dist/{chunk-2C6NWFN4.js → chunk-JVBI6VBT.js} +2 -2
- package/dist/{chunk-ZIETXJYU.js → chunk-MPWBFKGK.js} +201 -104
- package/dist/{chunk-K5EGSSXC.js → chunk-QQYQDTFZ.js} +142 -47
- package/dist/{chunk-FW4GX2LO.js → chunk-SW54HXSZ.js} +3 -3
- package/dist/cli-catalog-PNJEQV5H.js +15 -0
- package/dist/cli-commands-4UT2CRXX.js +15 -0
- package/dist/cli.js +136 -22525
- package/dist/{demo-server-G5OK7SWE.js → demo-server-WRHR25HF.js} +5 -4
- package/dist/index.js +7 -6
- package/dist/{intelligence-service-3ZUVTIO7.js → intelligence-service-QXXRUB2G.js} +2 -2
- package/dist/mcp.js +30 -15
- package/package.json +10 -10
- package/assets/assets/ProjectPage-CvxuPSiC.js +0 -8
- package/assets/assets/RunsPage-C6U_YcDY.js +0 -1
- package/assets/assets/index-ahEr6Oc-.js +0 -86
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PACKAGE_VERSION,
|
|
3
|
+
canonryMcpTools,
|
|
4
|
+
toolOperationAccess
|
|
5
|
+
} from "./chunk-MPWBFKGK.js";
|
|
1
6
|
import {
|
|
2
7
|
CliError,
|
|
3
8
|
EXIT_SYSTEM_ERROR,
|
|
4
9
|
EXIT_USER_ERROR,
|
|
5
|
-
PACKAGE_VERSION,
|
|
6
|
-
canonryMcpTools,
|
|
7
10
|
isMachineFormat
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-HGMYEDAQ.js";
|
|
9
12
|
import {
|
|
10
13
|
AGENT_PROVIDER_IDS,
|
|
11
14
|
CANONRY_NPM_PACKAGE_URL,
|
|
@@ -13,8 +16,11 @@ import {
|
|
|
13
16
|
DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
|
|
14
17
|
OPERATIONAL_LOG_FIELDS_HEADER,
|
|
15
18
|
OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS,
|
|
19
|
+
OPERATION_ACCESS_HEADER,
|
|
20
|
+
OperationAccesses,
|
|
16
21
|
RESEARCH_RUN_SCOPE,
|
|
17
22
|
SKILL_MANIFEST_FILENAME,
|
|
23
|
+
STORED_ONLY_HEADER,
|
|
18
24
|
SkillsClients,
|
|
19
25
|
USAGE_TELEMETRY_HEADERS,
|
|
20
26
|
agentPluginClientLabel,
|
|
@@ -31,8 +37,10 @@ import {
|
|
|
31
37
|
isRetiredAgentProviderId,
|
|
32
38
|
isSensitiveDiagnosticQueryKey,
|
|
33
39
|
isStrictSemver,
|
|
40
|
+
matchOperationTemplate,
|
|
34
41
|
normalizeAgentSlug,
|
|
35
42
|
normalizeTelemetryStatus,
|
|
43
|
+
operationAccess,
|
|
36
44
|
operationalLogListReadSchema,
|
|
37
45
|
providerBatchConfigSchema,
|
|
38
46
|
providerPricingSchema,
|
|
@@ -49,7 +57,7 @@ import {
|
|
|
49
57
|
skillsClientSchema,
|
|
50
58
|
upgradeCaveatFor,
|
|
51
59
|
upgradeCommandFor
|
|
52
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-GZ2KIONF.js";
|
|
53
61
|
|
|
54
62
|
// src/config.ts
|
|
55
63
|
import fs from "fs";
|
|
@@ -1505,6 +1513,22 @@ var getApiV1ProjectsByName = (options) => {
|
|
|
1505
1513
|
...options
|
|
1506
1514
|
});
|
|
1507
1515
|
};
|
|
1516
|
+
var patchApiV1ProjectsByName = (options) => {
|
|
1517
|
+
return (options.client ?? client).patch({
|
|
1518
|
+
security: [
|
|
1519
|
+
{
|
|
1520
|
+
scheme: "bearer",
|
|
1521
|
+
type: "http"
|
|
1522
|
+
}
|
|
1523
|
+
],
|
|
1524
|
+
url: "/api/v1/projects/{name}",
|
|
1525
|
+
...options,
|
|
1526
|
+
headers: {
|
|
1527
|
+
"Content-Type": "application/json",
|
|
1528
|
+
...options.headers
|
|
1529
|
+
}
|
|
1530
|
+
});
|
|
1531
|
+
};
|
|
1508
1532
|
var putApiV1ProjectsByName = (options) => {
|
|
1509
1533
|
return (options.client ?? client).put({
|
|
1510
1534
|
security: [
|
|
@@ -2949,6 +2973,34 @@ var getApiV1ProjectsByNameAnalyticsCompetitors = (options) => {
|
|
|
2949
2973
|
...options
|
|
2950
2974
|
});
|
|
2951
2975
|
};
|
|
2976
|
+
var getApiV1ProjectsByNameCompetitorSuggestions = (options) => {
|
|
2977
|
+
return (options.client ?? client).get({
|
|
2978
|
+
security: [
|
|
2979
|
+
{
|
|
2980
|
+
scheme: "bearer",
|
|
2981
|
+
type: "http"
|
|
2982
|
+
}
|
|
2983
|
+
],
|
|
2984
|
+
url: "/api/v1/projects/{name}/competitor-suggestions",
|
|
2985
|
+
...options
|
|
2986
|
+
});
|
|
2987
|
+
};
|
|
2988
|
+
var postApiV1ProjectsByNameCompetitorChangesPreview = (options) => {
|
|
2989
|
+
return (options.client ?? client).post({
|
|
2990
|
+
security: [
|
|
2991
|
+
{
|
|
2992
|
+
scheme: "bearer",
|
|
2993
|
+
type: "http"
|
|
2994
|
+
}
|
|
2995
|
+
],
|
|
2996
|
+
url: "/api/v1/projects/{name}/competitor-changes/preview",
|
|
2997
|
+
...options,
|
|
2998
|
+
headers: {
|
|
2999
|
+
"Content-Type": "application/json",
|
|
3000
|
+
...options.headers
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
};
|
|
2952
3004
|
var getApiV1ProjectsByNameRuns = (options) => {
|
|
2953
3005
|
return (options.client ?? client).get({
|
|
2954
3006
|
security: [
|
|
@@ -3057,6 +3109,22 @@ var postApiV1Apply = (options) => {
|
|
|
3057
3109
|
}
|
|
3058
3110
|
});
|
|
3059
3111
|
};
|
|
3112
|
+
var postApiV1ApplyPreview = (options) => {
|
|
3113
|
+
return (options.client ?? client).post({
|
|
3114
|
+
security: [
|
|
3115
|
+
{
|
|
3116
|
+
scheme: "bearer",
|
|
3117
|
+
type: "http"
|
|
3118
|
+
}
|
|
3119
|
+
],
|
|
3120
|
+
url: "/api/v1/apply/preview",
|
|
3121
|
+
...options,
|
|
3122
|
+
headers: {
|
|
3123
|
+
"Content-Type": "application/json",
|
|
3124
|
+
...options.headers
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3127
|
+
};
|
|
3060
3128
|
var getApiV1ProjectsByNameHistory = (options) => {
|
|
3061
3129
|
return (options.client ?? client).get({
|
|
3062
3130
|
security: [
|
|
@@ -5245,6 +5313,18 @@ var getApiV1ProjectsByNameGaProperties = (options) => {
|
|
|
5245
5313
|
...options
|
|
5246
5314
|
});
|
|
5247
5315
|
};
|
|
5316
|
+
var getApiV1ProjectsByNameGaAcquisition = (options) => {
|
|
5317
|
+
return (options.client ?? client).get({
|
|
5318
|
+
security: [
|
|
5319
|
+
{
|
|
5320
|
+
scheme: "bearer",
|
|
5321
|
+
type: "http"
|
|
5322
|
+
}
|
|
5323
|
+
],
|
|
5324
|
+
url: "/api/v1/projects/{name}/ga/acquisition",
|
|
5325
|
+
...options
|
|
5326
|
+
});
|
|
5327
|
+
};
|
|
5248
5328
|
var getApiV1ProjectsByNameGaMeasurementAnalysis = (options) => {
|
|
5249
5329
|
return (options.client ?? client).get({
|
|
5250
5330
|
security: [
|
|
@@ -5629,6 +5709,18 @@ var getApiV1Doctor = (options) => {
|
|
|
5629
5709
|
...options
|
|
5630
5710
|
});
|
|
5631
5711
|
};
|
|
5712
|
+
var getApiV1Status = (options) => {
|
|
5713
|
+
return (options?.client ?? client).get({
|
|
5714
|
+
security: [
|
|
5715
|
+
{
|
|
5716
|
+
scheme: "bearer",
|
|
5717
|
+
type: "http"
|
|
5718
|
+
}
|
|
5719
|
+
],
|
|
5720
|
+
url: "/api/v1/status",
|
|
5721
|
+
...options
|
|
5722
|
+
});
|
|
5723
|
+
};
|
|
5632
5724
|
var getApiV1ProjectsByNameDoctor = (options) => {
|
|
5633
5725
|
return (options.client ?? client).get({
|
|
5634
5726
|
security: [
|
|
@@ -5977,6 +6069,18 @@ var getApiV1ProjectsByNameTrafficReferralAssessment = (options) => {
|
|
|
5977
6069
|
...options
|
|
5978
6070
|
});
|
|
5979
6071
|
};
|
|
6072
|
+
var getApiV1ProjectsByNameTrafficSamples = (options) => {
|
|
6073
|
+
return (options.client ?? client).get({
|
|
6074
|
+
security: [
|
|
6075
|
+
{
|
|
6076
|
+
scheme: "bearer",
|
|
6077
|
+
type: "http"
|
|
6078
|
+
}
|
|
6079
|
+
],
|
|
6080
|
+
url: "/api/v1/projects/{name}/traffic/samples",
|
|
6081
|
+
...options
|
|
6082
|
+
});
|
|
6083
|
+
};
|
|
5980
6084
|
var getApiV1ProjectsByNameTrafficEvents = (options) => {
|
|
5981
6085
|
return (options.client ?? client).get({
|
|
5982
6086
|
security: [
|
|
@@ -6153,6 +6257,30 @@ var getApiV1ProjectsByNameTechnicalAeoChanges = (options) => {
|
|
|
6153
6257
|
...options
|
|
6154
6258
|
});
|
|
6155
6259
|
};
|
|
6260
|
+
var getApiV1ProjectsByNameTechnicalAeoIssues = (options) => {
|
|
6261
|
+
return (options.client ?? client).get({
|
|
6262
|
+
security: [
|
|
6263
|
+
{
|
|
6264
|
+
scheme: "bearer",
|
|
6265
|
+
type: "http"
|
|
6266
|
+
}
|
|
6267
|
+
],
|
|
6268
|
+
url: "/api/v1/projects/{name}/technical-aeo/issues",
|
|
6269
|
+
...options
|
|
6270
|
+
});
|
|
6271
|
+
};
|
|
6272
|
+
var getApiV1ProjectsByNameTechnicalAeoIssuesByIssueIdPages = (options) => {
|
|
6273
|
+
return (options.client ?? client).get({
|
|
6274
|
+
security: [
|
|
6275
|
+
{
|
|
6276
|
+
scheme: "bearer",
|
|
6277
|
+
type: "http"
|
|
6278
|
+
}
|
|
6279
|
+
],
|
|
6280
|
+
url: "/api/v1/projects/{name}/technical-aeo/issues/{issueId}/pages",
|
|
6281
|
+
...options
|
|
6282
|
+
});
|
|
6283
|
+
};
|
|
6156
6284
|
var getApiV1ProjectsByNameTechnicalAeoCrawlPagesAudit = (options) => {
|
|
6157
6285
|
return (options.client ?? client).get({
|
|
6158
6286
|
security: [
|
|
@@ -6477,6 +6605,30 @@ function runWithUsageTags(client2, tags, fn) {
|
|
|
6477
6605
|
if (!(client2 instanceof ApiClient)) return fn();
|
|
6478
6606
|
return usageTagStore.run(tags, fn);
|
|
6479
6607
|
}
|
|
6608
|
+
function classifyStoredOnlyRequest(method, url) {
|
|
6609
|
+
const operation = matchOperationTemplate(method, url);
|
|
6610
|
+
const separator = operation.indexOf(" ");
|
|
6611
|
+
return {
|
|
6612
|
+
operation,
|
|
6613
|
+
access: operationAccess(operation.slice(0, separator), operation.slice(separator + 1), OperationAccesses.live)
|
|
6614
|
+
};
|
|
6615
|
+
}
|
|
6616
|
+
function storedOnlyRequestHeaders(method, url) {
|
|
6617
|
+
if (!["1", "true"].includes((process.env.CANONRY_STORED_ONLY ?? "").toLowerCase())) return {};
|
|
6618
|
+
const { operation, access } = classifyStoredOnlyRequest(method, url);
|
|
6619
|
+
if (access !== OperationAccesses.stored) {
|
|
6620
|
+
throw new CliError({
|
|
6621
|
+
code: "STORED_ONLY_REFUSED",
|
|
6622
|
+
message: `Stored-only mode refuses ${access} operation ${operation}`,
|
|
6623
|
+
details: { operation, access },
|
|
6624
|
+
exitCode: EXIT_USER_ERROR
|
|
6625
|
+
});
|
|
6626
|
+
}
|
|
6627
|
+
return { [STORED_ONLY_HEADER]: "1" };
|
|
6628
|
+
}
|
|
6629
|
+
function storedOnlyProbeHeaders() {
|
|
6630
|
+
return ["1", "true"].includes((process.env.CANONRY_STORED_ONLY ?? "").toLowerCase()) ? { [STORED_ONLY_HEADER]: "1" } : {};
|
|
6631
|
+
}
|
|
6480
6632
|
var SAFE_CLIENT_NAME = /^\w[\w./-]{0,127}$/;
|
|
6481
6633
|
var SAFE_ACTOR_SESSION = /^\w[\w.:-]{0,127}$/;
|
|
6482
6634
|
function safeClientName(value) {
|
|
@@ -6548,6 +6700,7 @@ var ApiClient = class {
|
|
|
6548
6700
|
};
|
|
6549
6701
|
this.heyClient = createClient2({ baseUrl: this.originUrl, apiKey: this.apiKey, headers: this.requestHeaders });
|
|
6550
6702
|
this.heyClient.interceptors.request.use((request) => {
|
|
6703
|
+
for (const [key, value] of Object.entries(storedOnlyRequestHeaders(request.method, request.url.toString()))) request.headers.set(key, value);
|
|
6551
6704
|
const tags = usageTagStore.getStore();
|
|
6552
6705
|
if (tags?.mcpTool) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpTool, tags.mcpTool);
|
|
6553
6706
|
if (tags?.mcpCall) request.headers.set(USAGE_TELEMETRY_HEADERS.mcpCall, tags.mcpCall);
|
|
@@ -6569,6 +6722,7 @@ var ApiClient = class {
|
|
|
6569
6722
|
try {
|
|
6570
6723
|
const origin = new URL(this.originUrl).origin;
|
|
6571
6724
|
const res = await fetch(`${origin}/health`, {
|
|
6725
|
+
headers: storedOnlyProbeHeaders(),
|
|
6572
6726
|
signal: AbortSignal.timeout(2e3)
|
|
6573
6727
|
});
|
|
6574
6728
|
if (res.ok) {
|
|
@@ -6626,7 +6780,7 @@ var ApiClient = class {
|
|
|
6626
6780
|
if (traceEnabled) {
|
|
6627
6781
|
const durMs = Date.now() - traceStart;
|
|
6628
6782
|
process.stderr.write(
|
|
6629
|
-
`[trace] ${result.request.method} ${redactRequestTarget(result.request.url)} \u2192 ${result.response.status} (${durMs}ms)
|
|
6783
|
+
`[trace] ${result.request.method} ${redactRequestTarget(result.request.url)} \u2192 ${result.response.status} (${durMs}ms) access=${result.response.headers.get(OPERATION_ACCESS_HEADER) ?? "unknown"}
|
|
6630
6784
|
`
|
|
6631
6785
|
);
|
|
6632
6786
|
}
|
|
@@ -6729,10 +6883,13 @@ var ApiClient = class {
|
|
|
6729
6883
|
* and releases the response when done.
|
|
6730
6884
|
*/
|
|
6731
6885
|
async streamPost(path3, body, signal) {
|
|
6886
|
+
const candidateUrl = `${this.originUrl}/api/v1${path3}`;
|
|
6887
|
+
const storedOnlyHeaders = storedOnlyRequestHeaders("POST", candidateUrl);
|
|
6732
6888
|
await this.probeBasePath();
|
|
6733
6889
|
const url = `${this.originUrl}/api/v1${path3}`;
|
|
6734
6890
|
const headers = {
|
|
6735
6891
|
...this.requestHeaders,
|
|
6892
|
+
...storedOnlyHeaders,
|
|
6736
6893
|
"Content-Type": "application/json",
|
|
6737
6894
|
Accept: "text/event-stream"
|
|
6738
6895
|
};
|
|
@@ -6772,6 +6929,11 @@ var ApiClient = class {
|
|
|
6772
6929
|
() => putApiV1ProjectsByName({ client: this.heyClient, path: { name }, body })
|
|
6773
6930
|
);
|
|
6774
6931
|
}
|
|
6932
|
+
async patchProject(name, body) {
|
|
6933
|
+
return this.invoke(
|
|
6934
|
+
() => patchApiV1ProjectsByName({ client: this.heyClient, path: { name }, body })
|
|
6935
|
+
);
|
|
6936
|
+
}
|
|
6775
6937
|
async listProjects() {
|
|
6776
6938
|
return this.invoke(() => getApiV1Projects({ client: this.heyClient }));
|
|
6777
6939
|
}
|
|
@@ -6786,19 +6948,26 @@ var ApiClient = class {
|
|
|
6786
6948
|
() => getApiV1ProjectsByNameDeletePreview({ client: this.heyClient, path: { name } })
|
|
6787
6949
|
);
|
|
6788
6950
|
}
|
|
6789
|
-
async getStatus(
|
|
6951
|
+
async getStatus(opts) {
|
|
6790
6952
|
return this.invoke(
|
|
6791
|
-
() =>
|
|
6953
|
+
() => getApiV1Status({ client: this.heyClient, query: { project: opts?.project, problems: opts?.problems } })
|
|
6792
6954
|
);
|
|
6793
6955
|
}
|
|
6794
|
-
async getExport(project) {
|
|
6956
|
+
async getExport(project, opts) {
|
|
6795
6957
|
return this.invoke(
|
|
6796
|
-
() => getApiV1ProjectsByNameExport({
|
|
6958
|
+
() => getApiV1ProjectsByNameExport({
|
|
6959
|
+
client: this.heyClient,
|
|
6960
|
+
path: { name: project },
|
|
6961
|
+
query: { includeSecrets: opts?.includeSecrets }
|
|
6962
|
+
})
|
|
6797
6963
|
);
|
|
6798
6964
|
}
|
|
6799
6965
|
async apply(config) {
|
|
6800
6966
|
return this.invoke(() => postApiV1Apply({ client: this.heyClient, body: config }));
|
|
6801
6967
|
}
|
|
6968
|
+
async previewApply(config) {
|
|
6969
|
+
return this.invoke(() => postApiV1ApplyPreview({ client: this.heyClient, body: config }));
|
|
6970
|
+
}
|
|
6802
6971
|
async getProjectOverview(project, opts) {
|
|
6803
6972
|
return this.invoke(
|
|
6804
6973
|
() => getApiV1ProjectsByNameOverview({
|
|
@@ -6834,10 +7003,11 @@ var ApiClient = class {
|
|
|
6834
7003
|
if (opts.until) params.set("until", opts.until);
|
|
6835
7004
|
if (opts.includeProbes) params.set("includeProbes", "true");
|
|
6836
7005
|
const url = `${this.originUrl}/api/v1/projects/${encodeURIComponent(project)}/results/export?${params.toString()}`;
|
|
7006
|
+
const storedOnlyHeaders = storedOnlyRequestHeaders("GET", url);
|
|
6837
7007
|
let res;
|
|
6838
7008
|
try {
|
|
6839
7009
|
res = await fetch(url, {
|
|
6840
|
-
headers: { ...this.requestHeaders }
|
|
7010
|
+
headers: { ...this.requestHeaders, ...storedOnlyHeaders }
|
|
6841
7011
|
});
|
|
6842
7012
|
} catch (err) {
|
|
6843
7013
|
const message = describeError(err);
|
|
@@ -6941,13 +7111,27 @@ var ApiClient = class {
|
|
|
6941
7111
|
() => getApiV1ProjectsByNameCompetitors({ client: this.heyClient, path: { name: project } })
|
|
6942
7112
|
);
|
|
6943
7113
|
}
|
|
7114
|
+
async suggestCompetitors(project, query = {}) {
|
|
7115
|
+
return this.invoke(
|
|
7116
|
+
() => getApiV1ProjectsByNameCompetitorSuggestions({
|
|
7117
|
+
client: this.heyClient,
|
|
7118
|
+
path: { name: project },
|
|
7119
|
+
query
|
|
7120
|
+
})
|
|
7121
|
+
);
|
|
7122
|
+
}
|
|
7123
|
+
async previewCompetitorChange(project, request) {
|
|
7124
|
+
return this.invoke(
|
|
7125
|
+
() => postApiV1ProjectsByNameCompetitorChangesPreview({ client: this.heyClient, path: { name: project }, body: request })
|
|
7126
|
+
);
|
|
7127
|
+
}
|
|
6944
7128
|
/** Each entry is a domain or `{ domain, aliases }`; stated aliases are added to that competitor's list. */
|
|
6945
|
-
async appendCompetitors(project, competitors) {
|
|
7129
|
+
async appendCompetitors(project, competitors, options) {
|
|
6946
7130
|
return this.invoke(
|
|
6947
7131
|
() => postApiV1ProjectsByNameCompetitors({
|
|
6948
7132
|
client: this.heyClient,
|
|
6949
7133
|
path: { name: project },
|
|
6950
|
-
body: { competitors }
|
|
7134
|
+
body: { competitors, ...options?.acknowledgeBulkAdd !== void 0 ? { acknowledgeBulkAdd: options.acknowledgeBulkAdd } : {} }
|
|
6951
7135
|
})
|
|
6952
7136
|
);
|
|
6953
7137
|
}
|
|
@@ -7519,13 +7703,13 @@ var ApiClient = class {
|
|
|
7519
7703
|
})
|
|
7520
7704
|
);
|
|
7521
7705
|
}
|
|
7522
|
-
async listRuns(project, limit, kind, status, excludeKind, excludeKinds) {
|
|
7706
|
+
async listRuns(project, limit, kind, status, excludeKind, excludeKinds, since) {
|
|
7523
7707
|
return this.invoke(
|
|
7524
7708
|
() => getApiV1ProjectsByNameRuns({
|
|
7525
7709
|
client: this.heyClient,
|
|
7526
7710
|
path: { name: project },
|
|
7527
7711
|
// Filters arrive as free CLI strings; the server validates them against the enums.
|
|
7528
|
-
query: { limit, kind, status, excludeKind, excludeKinds }
|
|
7712
|
+
query: { limit, kind, status, excludeKind, excludeKinds, since }
|
|
7529
7713
|
})
|
|
7530
7714
|
);
|
|
7531
7715
|
}
|
|
@@ -7641,6 +7825,7 @@ var ApiClient = class {
|
|
|
7641
7825
|
* never run the CLI about a newer release.
|
|
7642
7826
|
*/
|
|
7643
7827
|
async getServerUpdateAvailable() {
|
|
7828
|
+
if (storedOnlyProbeHeaders()[STORED_ONLY_HEADER]) return null;
|
|
7644
7829
|
try {
|
|
7645
7830
|
await this.probeBasePath();
|
|
7646
7831
|
const res = await fetch(`${this.originUrl}/health`, { signal: AbortSignal.timeout(2e3) });
|
|
@@ -8653,6 +8838,9 @@ var ApiClient = class {
|
|
|
8653
8838
|
() => getApiV1ProjectsByNameGaProperties({ client: this.heyClient, path: { name: project } })
|
|
8654
8839
|
);
|
|
8655
8840
|
}
|
|
8841
|
+
async gaAcquisition(project, query) {
|
|
8842
|
+
return this.invoke(() => getApiV1ProjectsByNameGaAcquisition({ client: this.heyClient, path: { name: project }, query }));
|
|
8843
|
+
}
|
|
8656
8844
|
async gaMeasurementAnalysis(project, params) {
|
|
8657
8845
|
return this.invoke(
|
|
8658
8846
|
() => getApiV1ProjectsByNameGaMeasurementAnalysis({
|
|
@@ -8852,6 +9040,9 @@ var ApiClient = class {
|
|
|
8852
9040
|
query
|
|
8853
9041
|
}));
|
|
8854
9042
|
}
|
|
9043
|
+
async trafficListSamples(project, query) {
|
|
9044
|
+
return this.invoke(() => getApiV1ProjectsByNameTrafficSamples({ client: this.heyClient, path: { name: project }, query }));
|
|
9045
|
+
}
|
|
8855
9046
|
async trafficListEvents(project, params) {
|
|
8856
9047
|
return this.invoke(
|
|
8857
9048
|
() => getApiV1ProjectsByNameTrafficEvents({
|
|
@@ -9048,6 +9239,26 @@ var ApiClient = class {
|
|
|
9048
9239
|
})
|
|
9049
9240
|
);
|
|
9050
9241
|
}
|
|
9242
|
+
/** Stored findings across audited pages, including score-independent duplicate groups. */
|
|
9243
|
+
async getSiteAuditIssues(project, opts) {
|
|
9244
|
+
return this.invoke(
|
|
9245
|
+
() => getApiV1ProjectsByNameTechnicalAeoIssues({
|
|
9246
|
+
client: this.heyClient,
|
|
9247
|
+
path: { name: project },
|
|
9248
|
+
query: { runId: opts?.runId, cursor: opts?.cursor, limit: opts?.limit }
|
|
9249
|
+
})
|
|
9250
|
+
);
|
|
9251
|
+
}
|
|
9252
|
+
/** Page evidence for one stored audit issue; continuation stays pinned to its crawl. */
|
|
9253
|
+
async getSiteAuditIssuePages(project, issueId, opts) {
|
|
9254
|
+
return this.invoke(
|
|
9255
|
+
() => getApiV1ProjectsByNameTechnicalAeoIssuesByIssueIdPages({
|
|
9256
|
+
client: this.heyClient,
|
|
9257
|
+
path: { name: project, issueId },
|
|
9258
|
+
query: { runId: opts?.runId, cursor: opts?.cursor, limit: opts?.limit }
|
|
9259
|
+
})
|
|
9260
|
+
);
|
|
9261
|
+
}
|
|
9051
9262
|
/** Bounded semantic Site Health neighborhood; this never returns visualization layout. */
|
|
9052
9263
|
async getSiteHealthSubgraph(project, opts) {
|
|
9053
9264
|
return this.invoke(
|
|
@@ -9884,6 +10095,7 @@ var DynamicToolCatalog = class {
|
|
|
9884
10095
|
eager: this.eager,
|
|
9885
10096
|
loadedToolkits: [...this.loaded].sort(),
|
|
9886
10097
|
coreTools: this.entries.filter((entry) => entry.tool.tier === "core").map((entry) => entry.tool.name),
|
|
10098
|
+
liveTools: this.entries.filter((entry) => entry.tool.tier === "core" && toolOperationAccess(entry.tool) === OperationAccesses.live).map((entry) => entry.tool.name),
|
|
9887
10099
|
toolkits: CANONRY_MCP_TOOLKITS.map((toolkit) => this.toolkitEntry(toolkit)).filter((entry) => entry.toolCount > 0),
|
|
9888
10100
|
usage: this.eager ? "All tools available on this connection are already loaded. Use their listed schemas; tool visibility does not grant server permissions." : "Call canonry_load_toolkit with one of the toolkit names listed in `toolkits[].name` to register its tools for the rest of this session. Wait for its response before calling any newly enabled tool."
|
|
9889
10101
|
};
|
|
@@ -9897,6 +10109,7 @@ var DynamicToolCatalog = class {
|
|
|
9897
10109
|
whenToLoad: toolkit.whenToLoad,
|
|
9898
10110
|
toolCount: tools.length,
|
|
9899
10111
|
tools,
|
|
10112
|
+
liveTools: this.entries.filter((entry) => entry.tool.tier === toolkit.name && toolOperationAccess(entry.tool) === OperationAccesses.live).map((entry) => entry.tool.name),
|
|
9900
10113
|
loaded: this.loaded.has(toolkit.name)
|
|
9901
10114
|
};
|
|
9902
10115
|
}
|
|
@@ -9929,7 +10142,7 @@ var OPERATIONS_GUIDE = {
|
|
|
9929
10142
|
"guideVersion": "v1",
|
|
9930
10143
|
"operationsGuideUrl": "https://github.com/Canonry/canonry/blob/main/docs/agent-operations/v1.md",
|
|
9931
10144
|
"resourceUri": "canonry://agent-operations/v1",
|
|
9932
|
-
"initialize": 'Canonry tracks how AI answer engines mention brands and cite domains
|
|
10145
|
+
"initialize": 'Canonry tracks how AI answer engines mention brands and cite domains. Start with canonry_help({intent:"status"}) or a workflow; follow its stored reads and approval boundaries. Connected MCP clients need no skill, resource, plugin, or local CLI. The Canonry skill offers optional additional guidance.\nmentioned = brand in answer TEXT; cited = domain in SOURCE links. Never compute one from the other. A null answerMentioned means not checked, not false. No recorded sweep means no measured figure.\nGet approval for live provider reads, sweeps, probes, research, syncs and writes, scoped to the exact target, action and work. A GET or readOnlyHint does not mean free. canonry_help returns all liveTools for this connection; each needs approval, including read-only tools.\nREST exposes x-canonry-access; CLI help/catalog expose access; MCP help lists liveTools.\nStored-only: CLI --stored-only or CANONRY_STORED_ONLY=1; MCP --stored-only, --scope=stored-only or CANONRY_MCP_SCOPE=stored-only. It refuses live/write work (including doctor) before execution and suppresses housekeeping. CLI refusal is STORED_ONLY_REFUSED. Explicit exports may write files; server accounting remains separate from business mutations.\nHelp and skills grant no permissions; the server enforces authority. Report missing tools or 403; never bypass them with other credentials or endpoints. Hosted catalogs are fixed; use canonry_load_toolkit only when help offers it.\nReport concrete Canonry failures, confusing output, workarounds or improvements to the user and once via canonry_feedback. Exclude secrets and customer data.\n',
|
|
9933
10146
|
"approvalBoundary": [
|
|
9934
10147
|
"provider reads",
|
|
9935
10148
|
"sweeps",
|
|
@@ -9962,10 +10175,11 @@ var OPERATIONS_GUIDE = {
|
|
|
9962
10175
|
"health"
|
|
9963
10176
|
],
|
|
9964
10177
|
"next": [
|
|
10178
|
+
"canonry_status",
|
|
9965
10179
|
"canonry_projects_list",
|
|
9966
10180
|
"canonry_project_overview"
|
|
9967
10181
|
],
|
|
9968
|
-
"guidance": "
|
|
10182
|
+
"guidance": "Read canonry_status first for stored integration health, freshness, and repeated failures across accessible projects, then inspect a project overview for detail. Report missing evidence; do not start a sweep or sync to fill a gap."
|
|
9969
10183
|
},
|
|
9970
10184
|
"diagnose": {
|
|
9971
10185
|
"keywords": [
|
|
@@ -10030,12 +10244,13 @@ var OPERATIONS_GUIDE = {
|
|
|
10030
10244
|
"gtm"
|
|
10031
10245
|
],
|
|
10032
10246
|
"next": [
|
|
10247
|
+
"canonry_status",
|
|
10033
10248
|
"canonry_projects_list",
|
|
10034
10249
|
"canonry_project_get",
|
|
10035
10250
|
"canonry_ga_status",
|
|
10036
10251
|
"canonry_google_ads_status"
|
|
10037
10252
|
],
|
|
10038
|
-
"guidance": "
|
|
10253
|
+
"guidance": "Read canonry_status first for the stored instance or project health view, then use specialist tools for detail. Connecting, selecting resources, syncing, and live provider reads require scoped approval; never request credentials in chat."
|
|
10039
10254
|
},
|
|
10040
10255
|
"reports": {
|
|
10041
10256
|
"keywords": [
|
|
@@ -10052,7 +10267,7 @@ var OPERATIONS_GUIDE = {
|
|
|
10052
10267
|
"guidance": "Build the requested summary from stored project overview, organic evidence, and the relevant visibility, measurement, integration, or traffic reads. Retain evidence dates, scope, sample sizes, missing data, and separate mention/citation signals; do not generate fresh runs implicitly."
|
|
10053
10268
|
}
|
|
10054
10269
|
},
|
|
10055
|
-
"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**Setup:** Page Health needs no answer-engine provider; AI Visibility does.\nAfter a Page Health result on an install with no provider (a `NO_PROVIDER`\nnotice, or doctor's `providers.none-configured`), propose AI Visibility setup\nas the next step. The operator adds the provider key in the dashboard or in\ntheir own terminal; never ask for it in chat.\nRerunning `canonry bootstrap` after a provider change reloads saved provider\nsettings on a matching running local server and reports whether they became\nactive. If the server is offline, start it to apply the saved settings. For an\nexplicit reload of the connected server's own config, use\n`canonry settings reload-providers` or `canonry_providers_reload` with install\nadministrator authority. Remote servers are never reloaded automatically by\nbootstrap, and a local server that belongs to another install is left alone.\nPorts and database settings still need a restart.\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. Compact `nextCursor` completes only `pageList`; use `list` to enumerate\none ranking, markets, observed names or cited domains, preserving filters.\nFirst-page sibling lists are bounded summaries. Keep sample sizes and ties\nvisible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`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.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\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 a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `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"
|
|
10270
|
+
"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**Setup:** Page Health needs no answer-engine provider; AI Visibility does.\nAfter a Page Health result on an install with no provider (a `NO_PROVIDER`\nnotice, or doctor's `providers.none-configured`), propose AI Visibility setup\nas the next step. The operator adds the provider key in the dashboard or in\ntheir own terminal; never ask for it in chat.\nRerunning `canonry bootstrap` after a provider change reloads saved provider\nsettings on a matching running local server and reports whether they became\nactive. If the server is offline, start it to apply the saved settings. For an\nexplicit reload of the connected server's own config, use\n`canonry settings reload-providers` or `canonry_providers_reload` with install\nadministrator authority. Remote servers are never reloaded automatically by\nbootstrap, and a local server that belongs to another install is left alone.\nPorts and database settings still need a restart.\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**Site Health:** read `canonry_site_health_issues` for stored technical findings,\naffected-page counts and example URLs, then `canonry_site_health_issue_pages`\nfor a selected issue's remaining pages. Pin subsequent reads to the returned\nrun. Inspect coverage and omissions: a passing score can still have findings,\nan older scan may lack cross-page evidence, and a partial scan cannot establish\nsite-wide absence. Advisory findings require judgment before proposing a repair.\nAudit evidence informs technical repairs and existing-page recommendations;\nit does not itself establish a new-blog opportunity or authorize any edit.\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. Compact `nextCursor` completes only `pageList`; use `list` to enumerate\none ranking, markets, observed names or cited domains, preserving filters.\nFirst-page sibling lists are bounded summaries. Keep sample sizes and ties\nvisible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`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.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\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 a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `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"
|
|
10056
10271
|
};
|
|
10057
10272
|
|
|
10058
10273
|
// src/mcp/operations-guide.ts
|
|
@@ -10071,6 +10286,8 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
|
10071
10286
|
}
|
|
10072
10287
|
const wanted = [...reads, ...requestedActions];
|
|
10073
10288
|
const loadToolkits = mode === "stdio-progressive" ? catalog.toolkits.filter((t) => !t.loaded && t.tools.some((name) => wanted.includes(name))).map((t) => t.name) : [];
|
|
10289
|
+
const liveTools = [...catalog.liveTools];
|
|
10290
|
+
for (const toolkit of catalog.toolkits) liveTools.push(...toolkit.liveTools);
|
|
10074
10291
|
return {
|
|
10075
10292
|
guideVersion: OPERATIONS_GUIDE.guideVersion,
|
|
10076
10293
|
mode,
|
|
@@ -10085,7 +10302,8 @@ function operationsHelp(catalog, mode, intent = "", includeCatalog = false) {
|
|
|
10085
10302
|
authority: OPERATIONS_GUIDE.authority,
|
|
10086
10303
|
operationsGuideUrl: OPERATIONS_GUIDE.operationsGuideUrl,
|
|
10087
10304
|
...loadToolkits.length ? { loadToolkits } : {},
|
|
10088
|
-
...includeCatalog ? catalog : {}
|
|
10305
|
+
...includeCatalog ? catalog : {},
|
|
10306
|
+
liveTools: [...new Set(liveTools)].sort()
|
|
10089
10307
|
};
|
|
10090
10308
|
}
|
|
10091
10309
|
|
|
@@ -10209,7 +10427,7 @@ function getCanonryMcpTools(scope = "all", tiers, credentialScopes, operator = f
|
|
|
10209
10427
|
const readOnly = scope === "read-only" || credentialScopes !== void 0 && isReadOnlyKey(credentialScopes);
|
|
10210
10428
|
const restricted = credentialScopes && restrictedWriteScopes(credentialScopes);
|
|
10211
10429
|
const authorized = canonryMcpTools.filter((tool) => !tool.requiresOperator || operator);
|
|
10212
|
-
const byScope = readOnly ? authorized.filter((tool) => tool.access === "read") : authorized.filter((tool) => {
|
|
10430
|
+
const byScope = scope === "stored-only" ? authorized.filter((tool) => toolOperationAccess(tool) === OperationAccesses.stored) : readOnly ? authorized.filter((tool) => tool.access === "read") : authorized.filter((tool) => {
|
|
10213
10431
|
if (!restricted || tool.access === "read") return true;
|
|
10214
10432
|
if (tool.requiredScope && restricted.includes(tool.requiredScope)) return true;
|
|
10215
10433
|
return restricted.some((grant) => grant !== RESEARCH_RUN_SCOPE) && tool.tier === "ads";
|