@ainyc/canonry 2.10.3 → 2.12.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/assets/{index-VW6VB3cO.js → index-Ckr4V5dK.js} +110 -110
- package/assets/index.html +1 -1
- package/dist/{chunk-FAP76VXF.js → chunk-FCYNFM4B.js} +621 -269
- package/dist/{chunk-Z3BWDCBJ.js → chunk-PLI7EOPM.js} +117 -66
- package/dist/cli.js +116 -10
- package/dist/index.js +2 -2
- package/dist/mcp.js +62 -11
- package/package.json +7 -7
|
@@ -1540,15 +1540,55 @@ var ccCachedReleaseSchema = z14.object({
|
|
|
1540
1540
|
lastUsedAt: z14.string().nullable()
|
|
1541
1541
|
});
|
|
1542
1542
|
|
|
1543
|
-
// ../contracts/src/
|
|
1543
|
+
// ../contracts/src/composites.ts
|
|
1544
1544
|
import { z as z15 } from "zod";
|
|
1545
|
-
var
|
|
1545
|
+
var searchHitKindSchema = z15.enum(["snapshot", "insight"]);
|
|
1546
|
+
var projectSearchSnapshotHitSchema = z15.object({
|
|
1547
|
+
kind: z15.literal("snapshot"),
|
|
1548
|
+
id: z15.string(),
|
|
1549
|
+
runId: z15.string(),
|
|
1550
|
+
keyword: z15.string(),
|
|
1551
|
+
provider: z15.string(),
|
|
1552
|
+
model: z15.string().nullable(),
|
|
1553
|
+
citationState: citationStateSchema,
|
|
1554
|
+
matchedField: z15.enum(["answerText", "citedDomains", "searchQueries", "keyword"]),
|
|
1555
|
+
snippet: z15.string(),
|
|
1556
|
+
createdAt: z15.string()
|
|
1557
|
+
});
|
|
1558
|
+
var projectSearchInsightHitSchema = z15.object({
|
|
1559
|
+
kind: z15.literal("insight"),
|
|
1560
|
+
id: z15.string(),
|
|
1561
|
+
runId: z15.string().nullable(),
|
|
1562
|
+
type: z15.enum(["regression", "gain", "opportunity"]),
|
|
1563
|
+
severity: z15.enum(["critical", "high", "medium", "low"]),
|
|
1564
|
+
title: z15.string(),
|
|
1565
|
+
keyword: z15.string(),
|
|
1566
|
+
provider: z15.string(),
|
|
1567
|
+
matchedField: z15.enum(["title", "keyword", "recommendation", "cause"]),
|
|
1568
|
+
snippet: z15.string(),
|
|
1569
|
+
dismissed: z15.boolean(),
|
|
1570
|
+
createdAt: z15.string()
|
|
1571
|
+
});
|
|
1572
|
+
var projectSearchHitSchema = z15.discriminatedUnion("kind", [
|
|
1573
|
+
projectSearchSnapshotHitSchema,
|
|
1574
|
+
projectSearchInsightHitSchema
|
|
1575
|
+
]);
|
|
1576
|
+
var projectSearchResponseSchema = z15.object({
|
|
1577
|
+
query: z15.string(),
|
|
1578
|
+
totalHits: z15.number().int().nonnegative(),
|
|
1579
|
+
truncated: z15.boolean(),
|
|
1580
|
+
hits: z15.array(projectSearchHitSchema)
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
// ../contracts/src/content.ts
|
|
1584
|
+
import { z as z16 } from "zod";
|
|
1585
|
+
var contentActionSchema = z16.enum(["create", "expand", "refresh", "add-schema"]);
|
|
1546
1586
|
var ContentActions = contentActionSchema.enum;
|
|
1547
|
-
var demandSourceSchema =
|
|
1587
|
+
var demandSourceSchema = z16.enum(["gsc", "competitor-evidence", "both"]);
|
|
1548
1588
|
var DemandSources = demandSourceSchema.enum;
|
|
1549
|
-
var actionConfidenceSchema =
|
|
1589
|
+
var actionConfidenceSchema = z16.enum(["high", "medium", "low"]);
|
|
1550
1590
|
var ActionConfidences = actionConfidenceSchema.enum;
|
|
1551
|
-
var pageTypeSchema =
|
|
1591
|
+
var pageTypeSchema = z16.enum([
|
|
1552
1592
|
"blog-post",
|
|
1553
1593
|
"comparison",
|
|
1554
1594
|
"listicle",
|
|
@@ -1557,7 +1597,7 @@ var pageTypeSchema = z15.enum([
|
|
|
1557
1597
|
"glossary"
|
|
1558
1598
|
]);
|
|
1559
1599
|
var PageTypes = pageTypeSchema.enum;
|
|
1560
|
-
var contentActionStateSchema =
|
|
1600
|
+
var contentActionStateSchema = z16.enum([
|
|
1561
1601
|
"proposed",
|
|
1562
1602
|
"briefed",
|
|
1563
1603
|
"payload-generated",
|
|
@@ -1567,79 +1607,79 @@ var contentActionStateSchema = z15.enum([
|
|
|
1567
1607
|
"dismissed"
|
|
1568
1608
|
]);
|
|
1569
1609
|
var ContentActionStates = contentActionStateSchema.enum;
|
|
1570
|
-
var ourBestPageSchema =
|
|
1571
|
-
url:
|
|
1572
|
-
gscImpressions:
|
|
1573
|
-
gscClicks:
|
|
1610
|
+
var ourBestPageSchema = z16.object({
|
|
1611
|
+
url: z16.string(),
|
|
1612
|
+
gscImpressions: z16.number().nonnegative(),
|
|
1613
|
+
gscClicks: z16.number().nonnegative(),
|
|
1574
1614
|
// Null when the page came from the inventory fallback (no GSC ranking data).
|
|
1575
|
-
gscAvgPosition:
|
|
1576
|
-
organicSessions:
|
|
1577
|
-
});
|
|
1578
|
-
var winningCompetitorSchema =
|
|
1579
|
-
domain:
|
|
1580
|
-
url:
|
|
1581
|
-
title:
|
|
1582
|
-
citationCount:
|
|
1583
|
-
});
|
|
1584
|
-
var scoreBreakdownSchema =
|
|
1585
|
-
demand:
|
|
1586
|
-
competitor:
|
|
1587
|
-
absence:
|
|
1588
|
-
gapSeverity:
|
|
1589
|
-
});
|
|
1590
|
-
var existingActionRefSchema =
|
|
1591
|
-
actionId:
|
|
1615
|
+
gscAvgPosition: z16.number().nonnegative().nullable(),
|
|
1616
|
+
organicSessions: z16.number().nonnegative()
|
|
1617
|
+
});
|
|
1618
|
+
var winningCompetitorSchema = z16.object({
|
|
1619
|
+
domain: z16.string(),
|
|
1620
|
+
url: z16.string(),
|
|
1621
|
+
title: z16.string(),
|
|
1622
|
+
citationCount: z16.number().int().nonnegative()
|
|
1623
|
+
});
|
|
1624
|
+
var scoreBreakdownSchema = z16.object({
|
|
1625
|
+
demand: z16.number(),
|
|
1626
|
+
competitor: z16.number(),
|
|
1627
|
+
absence: z16.number(),
|
|
1628
|
+
gapSeverity: z16.number()
|
|
1629
|
+
});
|
|
1630
|
+
var existingActionRefSchema = z16.object({
|
|
1631
|
+
actionId: z16.string(),
|
|
1592
1632
|
state: contentActionStateSchema,
|
|
1593
|
-
lastUpdated:
|
|
1633
|
+
lastUpdated: z16.string()
|
|
1594
1634
|
});
|
|
1595
|
-
var contentTargetRowDtoSchema =
|
|
1596
|
-
targetRef:
|
|
1597
|
-
query:
|
|
1635
|
+
var contentTargetRowDtoSchema = z16.object({
|
|
1636
|
+
targetRef: z16.string(),
|
|
1637
|
+
query: z16.string(),
|
|
1598
1638
|
action: contentActionSchema,
|
|
1599
1639
|
ourBestPage: ourBestPageSchema.nullable(),
|
|
1600
1640
|
winningCompetitor: winningCompetitorSchema.nullable(),
|
|
1601
|
-
score:
|
|
1641
|
+
score: z16.number(),
|
|
1602
1642
|
scoreBreakdown: scoreBreakdownSchema,
|
|
1603
|
-
drivers:
|
|
1643
|
+
drivers: z16.array(z16.string()),
|
|
1604
1644
|
demandSource: demandSourceSchema,
|
|
1605
1645
|
actionConfidence: actionConfidenceSchema,
|
|
1606
1646
|
existingAction: existingActionRefSchema.nullable()
|
|
1607
1647
|
});
|
|
1608
|
-
var contentTargetsResponseDtoSchema =
|
|
1609
|
-
targets:
|
|
1610
|
-
contextMetrics:
|
|
1611
|
-
totalAiReferralSessions:
|
|
1612
|
-
latestRunId:
|
|
1613
|
-
runTimestamp:
|
|
1648
|
+
var contentTargetsResponseDtoSchema = z16.object({
|
|
1649
|
+
targets: z16.array(contentTargetRowDtoSchema),
|
|
1650
|
+
contextMetrics: z16.object({
|
|
1651
|
+
totalAiReferralSessions: z16.number().int().nonnegative(),
|
|
1652
|
+
latestRunId: z16.string(),
|
|
1653
|
+
runTimestamp: z16.string()
|
|
1614
1654
|
})
|
|
1615
1655
|
});
|
|
1616
|
-
var contentGroundingSourceSchema =
|
|
1617
|
-
uri:
|
|
1618
|
-
title:
|
|
1619
|
-
domain:
|
|
1620
|
-
isOurDomain:
|
|
1621
|
-
isCompetitor:
|
|
1622
|
-
citationCount:
|
|
1623
|
-
providers:
|
|
1624
|
-
});
|
|
1625
|
-
var contentSourceRowDtoSchema =
|
|
1626
|
-
query:
|
|
1627
|
-
groundingSources:
|
|
1628
|
-
});
|
|
1629
|
-
var contentSourcesResponseDtoSchema =
|
|
1630
|
-
sources:
|
|
1631
|
-
latestRunId:
|
|
1632
|
-
});
|
|
1633
|
-
var contentGapRowDtoSchema =
|
|
1634
|
-
query:
|
|
1635
|
-
competitorDomains:
|
|
1636
|
-
competitorCount:
|
|
1637
|
-
missRate:
|
|
1638
|
-
lastSeenInRunId:
|
|
1639
|
-
});
|
|
1640
|
-
var contentGapsResponseDtoSchema =
|
|
1641
|
-
gaps:
|
|
1642
|
-
latestRunId:
|
|
1656
|
+
var contentGroundingSourceSchema = z16.object({
|
|
1657
|
+
uri: z16.string(),
|
|
1658
|
+
title: z16.string(),
|
|
1659
|
+
domain: z16.string(),
|
|
1660
|
+
isOurDomain: z16.boolean(),
|
|
1661
|
+
isCompetitor: z16.boolean(),
|
|
1662
|
+
citationCount: z16.number().int().nonnegative(),
|
|
1663
|
+
providers: z16.array(providerNameSchema)
|
|
1664
|
+
});
|
|
1665
|
+
var contentSourceRowDtoSchema = z16.object({
|
|
1666
|
+
query: z16.string(),
|
|
1667
|
+
groundingSources: z16.array(contentGroundingSourceSchema)
|
|
1668
|
+
});
|
|
1669
|
+
var contentSourcesResponseDtoSchema = z16.object({
|
|
1670
|
+
sources: z16.array(contentSourceRowDtoSchema),
|
|
1671
|
+
latestRunId: z16.string()
|
|
1672
|
+
});
|
|
1673
|
+
var contentGapRowDtoSchema = z16.object({
|
|
1674
|
+
query: z16.string(),
|
|
1675
|
+
competitorDomains: z16.array(z16.string()),
|
|
1676
|
+
competitorCount: z16.number().int().nonnegative(),
|
|
1677
|
+
missRate: z16.number().min(0).max(1),
|
|
1678
|
+
lastSeenInRunId: z16.string()
|
|
1679
|
+
});
|
|
1680
|
+
var contentGapsResponseDtoSchema = z16.object({
|
|
1681
|
+
gaps: z16.array(contentGapRowDtoSchema),
|
|
1682
|
+
latestRunId: z16.string()
|
|
1643
1683
|
});
|
|
1644
1684
|
|
|
1645
1685
|
// src/client.ts
|
|
@@ -2233,6 +2273,17 @@ var ApiClient = class {
|
|
|
2233
2273
|
async getHealth(project) {
|
|
2234
2274
|
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
|
|
2235
2275
|
}
|
|
2276
|
+
async getProjectOverview(project) {
|
|
2277
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/overview`);
|
|
2278
|
+
}
|
|
2279
|
+
async searchProject(project, opts) {
|
|
2280
|
+
const params = new URLSearchParams({ q: opts.q });
|
|
2281
|
+
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
2282
|
+
return this.request(
|
|
2283
|
+
"GET",
|
|
2284
|
+
`/projects/${encodeURIComponent(project)}/search?${params.toString()}`
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2236
2287
|
async getHealthHistory(project, limit) {
|
|
2237
2288
|
const qs = limit ? `?limit=${limit}` : "";
|
|
2238
2289
|
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/history${qs}`);
|
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setGoogleAuthConfig,
|
|
18
18
|
showFirstRunNotice,
|
|
19
19
|
trackEvent
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-FCYNFM4B.js";
|
|
21
21
|
import {
|
|
22
22
|
CcReleaseSyncStatuses,
|
|
23
23
|
CliError,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
saveConfig,
|
|
42
42
|
saveConfigPatch,
|
|
43
43
|
usageError
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-PLI7EOPM.js";
|
|
45
45
|
import {
|
|
46
46
|
apiKeys,
|
|
47
47
|
competitors,
|
|
@@ -1847,7 +1847,7 @@ async function gaSocialReferralSummary(project, opts) {
|
|
|
1847
1847
|
console.log(` Sessions: ${traffic.socialSessions} (${traffic.socialSharePct}% of ${traffic.totalSessions} total)`);
|
|
1848
1848
|
console.log(` Users: ${traffic.socialUsers}`);
|
|
1849
1849
|
console.log();
|
|
1850
|
-
const fmtTrend = (
|
|
1850
|
+
const fmtTrend = (pct2) => pct2 === null ? "n/a" : `${pct2 >= 0 ? "+" : ""}${pct2}%`;
|
|
1851
1851
|
console.log(` 7d trend: ${fmtTrend(trend.trend7dPct)} (${trend.socialSessions7d} vs ${trend.socialSessionsPrev7d})`);
|
|
1852
1852
|
console.log(` 30d trend: ${fmtTrend(trend.trend30dPct)} (${trend.socialSessions30d} vs ${trend.socialSessionsPrev30d})`);
|
|
1853
1853
|
if (trend.biggestMover) {
|
|
@@ -1890,7 +1890,7 @@ async function gaSocialReferralSummary(project, opts) {
|
|
|
1890
1890
|
async function gaAttribution(project, opts) {
|
|
1891
1891
|
const client = getClient4();
|
|
1892
1892
|
const traffic = await client.gaTraffic(project);
|
|
1893
|
-
const fmtTrend = (
|
|
1893
|
+
const fmtTrend = (pct2) => pct2 === null ? "n/a" : `${pct2 >= 0 ? "+" : ""}${pct2}%`;
|
|
1894
1894
|
if (opts?.trend) {
|
|
1895
1895
|
const trend = await client.gaAttributionTrend(project);
|
|
1896
1896
|
if (opts.format === "json") {
|
|
@@ -4047,14 +4047,14 @@ function printMetrics(data) {
|
|
|
4047
4047
|
console.log(`
|
|
4048
4048
|
Citation Rate Trends (${data.window})`);
|
|
4049
4049
|
console.log("\u2500".repeat(50));
|
|
4050
|
-
const
|
|
4051
|
-
console.log(` Overall: ${
|
|
4050
|
+
const pct2 = (n) => `${(n * 100).toFixed(1)}%`;
|
|
4051
|
+
console.log(` Overall: ${pct2(data.overall.citationRate)} (${data.overall.cited}/${data.overall.total})`);
|
|
4052
4052
|
console.log(` Trend: ${data.trend}`);
|
|
4053
4053
|
if (Object.keys(data.byProvider).length > 0) {
|
|
4054
4054
|
console.log(`
|
|
4055
4055
|
By Provider:`);
|
|
4056
4056
|
for (const [provider, metric] of Object.entries(data.byProvider)) {
|
|
4057
|
-
console.log(` ${provider.padEnd(10)} ${
|
|
4057
|
+
console.log(` ${provider.padEnd(10)} ${pct2(metric.citationRate).padStart(6)} (${metric.cited}/${metric.total})`);
|
|
4058
4058
|
}
|
|
4059
4059
|
}
|
|
4060
4060
|
if (data.buckets.length > 0) {
|
|
@@ -4063,7 +4063,7 @@ Citation Rate Trends (${data.window})`);
|
|
|
4063
4063
|
for (const bucket of data.buckets) {
|
|
4064
4064
|
const start = bucket.startDate.slice(0, 10);
|
|
4065
4065
|
const bar = bucket.total > 0 ? "\u2588".repeat(Math.round(bucket.citationRate * 20)) : "";
|
|
4066
|
-
console.log(` ${start} ${
|
|
4066
|
+
console.log(` ${start} ${pct2(bucket.citationRate).padStart(6)} ${bar}`);
|
|
4067
4067
|
}
|
|
4068
4068
|
}
|
|
4069
4069
|
}
|
|
@@ -4100,9 +4100,9 @@ Source Origin Breakdown`);
|
|
|
4100
4100
|
return;
|
|
4101
4101
|
}
|
|
4102
4102
|
for (const cat of data.overall) {
|
|
4103
|
-
const
|
|
4103
|
+
const pct2 = `${(cat.percentage * 100).toFixed(1)}%`;
|
|
4104
4104
|
const domains = cat.topDomains.slice(0, 3).map((d) => d.domain).join(", ");
|
|
4105
|
-
console.log(` ${cat.label.padEnd(20)} ${
|
|
4105
|
+
console.log(` ${cat.label.padEnd(20)} ${pct2.padStart(6)} (${cat.count}) ${domains}`);
|
|
4106
4106
|
}
|
|
4107
4107
|
}
|
|
4108
4108
|
|
|
@@ -6026,6 +6026,84 @@ async function showHealth(project, opts) {
|
|
|
6026
6026
|
}
|
|
6027
6027
|
}
|
|
6028
6028
|
|
|
6029
|
+
// src/commands/overview.ts
|
|
6030
|
+
async function showOverview(project, opts) {
|
|
6031
|
+
const client = createApiClient();
|
|
6032
|
+
const overview = await client.getProjectOverview(project);
|
|
6033
|
+
if (opts.format === "json") {
|
|
6034
|
+
console.log(JSON.stringify(overview, null, 2));
|
|
6035
|
+
return;
|
|
6036
|
+
}
|
|
6037
|
+
const { project: meta, latestRun, health, topInsights, keywordCounts, providers, transitions } = overview;
|
|
6038
|
+
console.log(`Overview: ${meta.displayName ?? meta.name} (${meta.name})
|
|
6039
|
+
`);
|
|
6040
|
+
console.log(` Domain: ${meta.canonicalDomain}`);
|
|
6041
|
+
console.log(` Country: ${meta.country}`);
|
|
6042
|
+
console.log(` Language: ${meta.language}`);
|
|
6043
|
+
if (latestRun.run) {
|
|
6044
|
+
const finished = latestRun.run.finishedAt ?? "\u2014";
|
|
6045
|
+
console.log(`
|
|
6046
|
+
Latest run: ${latestRun.run.id} (${latestRun.run.status}, ${finished})`);
|
|
6047
|
+
console.log(` Total runs: ${latestRun.totalRuns}`);
|
|
6048
|
+
} else {
|
|
6049
|
+
console.log("\n No runs yet.");
|
|
6050
|
+
}
|
|
6051
|
+
console.log(`
|
|
6052
|
+
Keywords cited: ${keywordCounts.citedKeywords}/${keywordCounts.totalKeywords} (${pct(keywordCounts.citedRate)})`);
|
|
6053
|
+
if (providers.length > 0) {
|
|
6054
|
+
console.log(" Providers:");
|
|
6055
|
+
for (const p of providers) {
|
|
6056
|
+
console.log(` ${p.provider.padEnd(10)} ${p.cited}/${p.total} (${pct(p.citedRate)})`);
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
if (transitions.since) {
|
|
6060
|
+
console.log(`
|
|
6061
|
+
vs run at ${transitions.since}: +${transitions.gained} gained, -${transitions.lost} lost, ${transitions.emerging} emerging`);
|
|
6062
|
+
}
|
|
6063
|
+
if (health) {
|
|
6064
|
+
console.log(`
|
|
6065
|
+
Health: ${pct(health.overallCitedRate)} cited (${health.citedPairs}/${health.totalPairs} pairs)`);
|
|
6066
|
+
}
|
|
6067
|
+
if (topInsights.length > 0) {
|
|
6068
|
+
console.log("\n Top insights:");
|
|
6069
|
+
for (const insight of topInsights) {
|
|
6070
|
+
console.log(` [${insight.severity.toUpperCase()}] ${insight.type} \u2014 ${insight.title}`);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
function pct(value) {
|
|
6075
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
// src/commands/search.ts
|
|
6079
|
+
async function searchProject(project, opts) {
|
|
6080
|
+
const client = createApiClient();
|
|
6081
|
+
const result = await client.searchProject(project, { q: opts.query, limit: opts.limit });
|
|
6082
|
+
if (opts.format === "json") {
|
|
6083
|
+
console.log(JSON.stringify(result, null, 2));
|
|
6084
|
+
return;
|
|
6085
|
+
}
|
|
6086
|
+
console.log(`Search: "${result.query}" \u2014 ${result.totalHits} hit${result.totalHits === 1 ? "" : "s"}${result.truncated ? " (truncated)" : ""}
|
|
6087
|
+
`);
|
|
6088
|
+
if (result.hits.length === 0) {
|
|
6089
|
+
console.log(" No matches.");
|
|
6090
|
+
return;
|
|
6091
|
+
}
|
|
6092
|
+
for (const hit of result.hits) {
|
|
6093
|
+
if (hit.kind === "snapshot") {
|
|
6094
|
+
console.log(` [snapshot] ${hit.keyword} (${hit.provider}, ${hit.citationState}) \u2014 ${hit.matchedField}`);
|
|
6095
|
+
console.log(` ${hit.snippet}`);
|
|
6096
|
+
console.log(` run=${hit.runId} at ${hit.createdAt}`);
|
|
6097
|
+
} else {
|
|
6098
|
+
const dismissed = hit.dismissed ? " [dismissed]" : "";
|
|
6099
|
+
console.log(` [insight ${hit.severity.toUpperCase()}] ${hit.type} \u2014 ${hit.title}${dismissed}`);
|
|
6100
|
+
console.log(` ${hit.snippet}`);
|
|
6101
|
+
console.log(` keyword=${hit.keyword} at ${hit.createdAt}`);
|
|
6102
|
+
}
|
|
6103
|
+
console.log("");
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6029
6107
|
// src/cli-commands/intelligence.ts
|
|
6030
6108
|
var INTELLIGENCE_CLI_COMMANDS = [
|
|
6031
6109
|
{
|
|
@@ -6072,6 +6150,34 @@ var INTELLIGENCE_CLI_COMMANDS = [
|
|
|
6072
6150
|
});
|
|
6073
6151
|
await showHealth(project, { history, limit, format: input.format });
|
|
6074
6152
|
}
|
|
6153
|
+
},
|
|
6154
|
+
{
|
|
6155
|
+
path: ["overview"],
|
|
6156
|
+
usage: "canonry overview <project> [--format json]",
|
|
6157
|
+
options: {},
|
|
6158
|
+
run: async (input) => {
|
|
6159
|
+
const usage = "canonry overview <project> [--format json]";
|
|
6160
|
+
const project = requireProject(input, "overview", usage);
|
|
6161
|
+
await showOverview(project, { format: input.format });
|
|
6162
|
+
}
|
|
6163
|
+
},
|
|
6164
|
+
{
|
|
6165
|
+
path: ["search"],
|
|
6166
|
+
usage: "canonry search <project> <query> [--limit <n>] [--format json]",
|
|
6167
|
+
options: {
|
|
6168
|
+
limit: { type: "string" }
|
|
6169
|
+
},
|
|
6170
|
+
run: async (input) => {
|
|
6171
|
+
const usage = "canonry search <project> <query> [--limit <n>] [--format json]";
|
|
6172
|
+
const project = requireProject(input, "search", usage);
|
|
6173
|
+
const query = requirePositional(input, 1, { command: "search", usage, message: "query is required" });
|
|
6174
|
+
const limit = parseIntegerOption(input, "limit", {
|
|
6175
|
+
command: "search",
|
|
6176
|
+
usage,
|
|
6177
|
+
message: "--limit must be an integer"
|
|
6178
|
+
});
|
|
6179
|
+
await searchProject(project, { query, limit, format: input.format });
|
|
6180
|
+
}
|
|
6075
6181
|
}
|
|
6076
6182
|
];
|
|
6077
6183
|
|
package/dist/index.js
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
projectUpsertRequestSchema,
|
|
11
11
|
runTriggerRequestSchema,
|
|
12
12
|
scheduleUpsertRequestSchema
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PLI7EOPM.js";
|
|
14
14
|
import "./chunk-MLKGABMK.js";
|
|
15
15
|
|
|
16
16
|
// src/mcp/cli.ts
|
|
@@ -202,6 +202,32 @@ var canonryMcpTools = [
|
|
|
202
202
|
openApiOperations: ["GET /api/v1/projects/{name}"],
|
|
203
203
|
handler: (client, input) => client.getProject(input.project)
|
|
204
204
|
}),
|
|
205
|
+
defineTool({
|
|
206
|
+
name: "canonry_project_overview",
|
|
207
|
+
title: "Get project overview (composite)",
|
|
208
|
+
description: 'One-call summary for "how is project X doing?" \u2014 bundles project info, latest run, top undismissed insights, latest health snapshot, keyword cited rate, per-provider breakdown, and gained/lost/emerging vs the previous run. Prefer this over fanning out to separate tools.',
|
|
209
|
+
access: "read",
|
|
210
|
+
tier: "core",
|
|
211
|
+
inputSchema: projectInputSchema,
|
|
212
|
+
annotations: readAnnotations(),
|
|
213
|
+
openApiOperations: ["GET /api/v1/projects/{name}/overview"],
|
|
214
|
+
handler: (client, input) => client.getProjectOverview(input.project)
|
|
215
|
+
}),
|
|
216
|
+
defineTool({
|
|
217
|
+
name: "canonry_search",
|
|
218
|
+
title: "Search project (composite)",
|
|
219
|
+
description: "Search query snapshots and intelligence insights for the given text. Looks at snapshot answer text, cited domains, raw provider responses, and insight title/keyword/recommendation/cause. Returns ranked hits with snippets \u2014 use it instead of paginating snapshots when you need to find a competitor mention or term.",
|
|
220
|
+
access: "read",
|
|
221
|
+
tier: "core",
|
|
222
|
+
inputSchema: z2.object({
|
|
223
|
+
project: projectNameSchema,
|
|
224
|
+
q: z2.string().min(2).describe("Search term, at least 2 characters."),
|
|
225
|
+
limit: z2.number().int().positive().max(50).optional().describe("Max combined hits (1-50, default 25).")
|
|
226
|
+
}),
|
|
227
|
+
annotations: readAnnotations(),
|
|
228
|
+
openApiOperations: ["GET /api/v1/projects/{name}/search"],
|
|
229
|
+
handler: (client, input) => client.searchProject(input.project, { q: input.q, limit: input.limit })
|
|
230
|
+
}),
|
|
205
231
|
defineTool({
|
|
206
232
|
name: "canonry_project_export",
|
|
207
233
|
title: "Export project config",
|
|
@@ -885,7 +911,9 @@ var DynamicToolCatalog = class {
|
|
|
885
911
|
loaded = /* @__PURE__ */ new Set();
|
|
886
912
|
eager;
|
|
887
913
|
scope;
|
|
888
|
-
|
|
914
|
+
server;
|
|
915
|
+
constructor(server, entries, scope, options = {}) {
|
|
916
|
+
this.server = server;
|
|
889
917
|
this.entries = entries;
|
|
890
918
|
this.scope = scope;
|
|
891
919
|
this.eager = Boolean(options.eager);
|
|
@@ -899,9 +927,11 @@ var DynamicToolCatalog = class {
|
|
|
899
927
|
}
|
|
900
928
|
applyInitialEnablement() {
|
|
901
929
|
if (this.eager) return;
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
930
|
+
this.batchListChanged(() => {
|
|
931
|
+
for (const entry of this.entries) {
|
|
932
|
+
if (entry.tool.tier !== "core") entry.registered.disable();
|
|
933
|
+
}
|
|
934
|
+
});
|
|
905
935
|
}
|
|
906
936
|
loadToolkit(rawName) {
|
|
907
937
|
if (!isCanonryMcpToolkitName(rawName)) {
|
|
@@ -916,9 +946,11 @@ var DynamicToolCatalog = class {
|
|
|
916
946
|
if (this.loaded.has(name)) {
|
|
917
947
|
return { status: "already-loaded", name, tools: matches.map((entry) => entry.tool.name) };
|
|
918
948
|
}
|
|
919
|
-
|
|
920
|
-
entry
|
|
921
|
-
|
|
949
|
+
this.batchListChanged(() => {
|
|
950
|
+
for (const entry of matches) {
|
|
951
|
+
entry.registered.enable();
|
|
952
|
+
}
|
|
953
|
+
});
|
|
922
954
|
this.loaded.add(name);
|
|
923
955
|
return { status: "loaded", name, tools: matches.map((entry) => entry.tool.name) };
|
|
924
956
|
}
|
|
@@ -929,7 +961,7 @@ var DynamicToolCatalog = class {
|
|
|
929
961
|
loadedToolkits: [...this.loaded].sort(),
|
|
930
962
|
coreTools: this.entries.filter((entry) => entry.tool.tier === "core").map((entry) => entry.tool.name),
|
|
931
963
|
toolkits: CANONRY_MCP_TOOLKITS.map((toolkit) => this.toolkitEntry(toolkit)).filter((entry) => entry.toolCount > 0),
|
|
932
|
-
usage: "Call canonry_load_toolkit with one of the toolkit names listed in `toolkits[].name` to register its tools for the rest of this session."
|
|
964
|
+
usage: "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."
|
|
933
965
|
};
|
|
934
966
|
}
|
|
935
967
|
toolkitEntry(toolkit) {
|
|
@@ -947,6 +979,25 @@ var DynamicToolCatalog = class {
|
|
|
947
979
|
toolsForToolkit(name) {
|
|
948
980
|
return this.entries.filter((entry) => entry.tool.tier === name).map((entry) => entry.tool.name);
|
|
949
981
|
}
|
|
982
|
+
// RegisteredTool.enable/disable each call sendToolListChanged on the McpServer
|
|
983
|
+
// we registered with. Loading an 11-tool toolkit emits 11 notifications under
|
|
984
|
+
// that contract, which a spec-compliant client will treat as 11 catalog
|
|
985
|
+
// refetches. Coalesce them into one notification per batch by intercepting
|
|
986
|
+
// the SDK's sender for the duration of the batch.
|
|
987
|
+
batchListChanged(fn) {
|
|
988
|
+
const host = this.server;
|
|
989
|
+
const original = host.sendToolListChanged;
|
|
990
|
+
let suppressed = false;
|
|
991
|
+
host.sendToolListChanged = () => {
|
|
992
|
+
suppressed = true;
|
|
993
|
+
};
|
|
994
|
+
try {
|
|
995
|
+
fn();
|
|
996
|
+
} finally {
|
|
997
|
+
host.sendToolListChanged = original;
|
|
998
|
+
}
|
|
999
|
+
if (suppressed) original.call(host);
|
|
1000
|
+
}
|
|
950
1001
|
};
|
|
951
1002
|
|
|
952
1003
|
// src/mcp/server.ts
|
|
@@ -981,7 +1032,7 @@ function createCanonryMcpServerWithCatalog(options = {}) {
|
|
|
981
1032
|
);
|
|
982
1033
|
entries.push({ tool, registered });
|
|
983
1034
|
}
|
|
984
|
-
const catalog = new DynamicToolCatalog(entries, scope, { eager: options.eager });
|
|
1035
|
+
const catalog = new DynamicToolCatalog(server, entries, scope, { eager: options.eager });
|
|
985
1036
|
catalog.applyInitialEnablement();
|
|
986
1037
|
registerMetaTools(server, catalog);
|
|
987
1038
|
return { server, catalog };
|
|
@@ -1004,7 +1055,7 @@ function registerMetaTools(server, catalog) {
|
|
|
1004
1055
|
"canonry_load_toolkit",
|
|
1005
1056
|
{
|
|
1006
1057
|
title: "Load a Canonry MCP toolkit",
|
|
1007
|
-
description:
|
|
1058
|
+
description: `Register a toolkit's tools for this session and emit one notifications/tools/list_changed. Idempotent. Loaded toolkits remain loaded for the rest of the session. Wait for this call to return before calling any newly enabled tool \u2014 pipelining the call with a tools/call on the same connection can race the registration and fail with "Tool ... disabled".`,
|
|
1008
1059
|
inputSchema: loadToolkitInputSchema.shape,
|
|
1009
1060
|
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
|
|
1010
1061
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
@@ -60,19 +60,19 @@
|
|
|
60
60
|
"tsup": "^8.5.1",
|
|
61
61
|
"tsx": "^4.19.0",
|
|
62
62
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
63
|
-
"@ainyc/canonry-
|
|
63
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
64
64
|
"@ainyc/canonry-db": "0.0.0",
|
|
65
|
+
"@ainyc/canonry-config": "0.0.0",
|
|
65
66
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
66
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
67
67
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
68
|
+
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
69
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
68
70
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
69
71
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
70
|
-
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
71
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
72
72
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
73
|
-
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
74
|
-
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
75
73
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
74
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
75
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
76
76
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|