@collage-dam/mcp-server 0.1.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/.env.example +56 -0
- package/CHANGELOG.md +90 -0
- package/LICENSE +21 -0
- package/README.md +512 -0
- package/dist/client.d.ts +497 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1162 -0
- package/dist/client.js.map +1 -0
- package/dist/conventions/confirmation.d.ts +89 -0
- package/dist/conventions/confirmation.d.ts.map +1 -0
- package/dist/conventions/confirmation.js +132 -0
- package/dist/conventions/confirmation.js.map +1 -0
- package/dist/conventions/dry-run/batch-executor.d.ts +36 -0
- package/dist/conventions/dry-run/batch-executor.d.ts.map +1 -0
- package/dist/conventions/dry-run/batch-executor.js +89 -0
- package/dist/conventions/dry-run/batch-executor.js.map +1 -0
- package/dist/conventions/dry-run/diff-renderer.d.ts +34 -0
- package/dist/conventions/dry-run/diff-renderer.d.ts.map +1 -0
- package/dist/conventions/dry-run/diff-renderer.js +158 -0
- package/dist/conventions/dry-run/diff-renderer.js.map +1 -0
- package/dist/conventions/dry-run/index.d.ts +13 -0
- package/dist/conventions/dry-run/index.d.ts.map +1 -0
- package/dist/conventions/dry-run/index.js +10 -0
- package/dist/conventions/dry-run/index.js.map +1 -0
- package/dist/conventions/dry-run/mutating-tool.d.ts +64 -0
- package/dist/conventions/dry-run/mutating-tool.d.ts.map +1 -0
- package/dist/conventions/dry-run/mutating-tool.js +88 -0
- package/dist/conventions/dry-run/mutating-tool.js.map +1 -0
- package/dist/conventions/dry-run/summary.d.ts +66 -0
- package/dist/conventions/dry-run/summary.d.ts.map +1 -0
- package/dist/conventions/dry-run/summary.js +185 -0
- package/dist/conventions/dry-run/summary.js.map +1 -0
- package/dist/conventions/dry-run/types.d.ts +597 -0
- package/dist/conventions/dry-run/types.d.ts.map +1 -0
- package/dist/conventions/dry-run/types.js +108 -0
- package/dist/conventions/dry-run/types.js.map +1 -0
- package/dist/conventions/dry-run/with-dry-run.d.ts +66 -0
- package/dist/conventions/dry-run/with-dry-run.d.ts.map +1 -0
- package/dist/conventions/dry-run/with-dry-run.js +219 -0
- package/dist/conventions/dry-run/with-dry-run.js.map +1 -0
- package/dist/conventions/env.d.ts +49 -0
- package/dist/conventions/env.d.ts.map +1 -0
- package/dist/conventions/env.js +84 -0
- package/dist/conventions/env.js.map +1 -0
- package/dist/conventions/errors.d.ts +68 -0
- package/dist/conventions/errors.d.ts.map +1 -0
- package/dist/conventions/errors.js +81 -0
- package/dist/conventions/errors.js.map +1 -0
- package/dist/conventions/logger.d.ts +28 -0
- package/dist/conventions/logger.d.ts.map +1 -0
- package/dist/conventions/logger.js +105 -0
- package/dist/conventions/logger.js.map +1 -0
- package/dist/conventions/pagination.d.ts +37 -0
- package/dist/conventions/pagination.d.ts.map +1 -0
- package/dist/conventions/pagination.js +53 -0
- package/dist/conventions/pagination.js.map +1 -0
- package/dist/conventions/rate-limiter.d.ts +54 -0
- package/dist/conventions/rate-limiter.d.ts.map +1 -0
- package/dist/conventions/rate-limiter.js +143 -0
- package/dist/conventions/rate-limiter.js.map +1 -0
- package/dist/conventions/response-budget.d.ts +66 -0
- package/dist/conventions/response-budget.d.ts.map +1 -0
- package/dist/conventions/response-budget.js +89 -0
- package/dist/conventions/response-budget.js.map +1 -0
- package/dist/conventions/schema-version.d.ts +27 -0
- package/dist/conventions/schema-version.d.ts.map +1 -0
- package/dist/conventions/schema-version.js +29 -0
- package/dist/conventions/schema-version.js.map +1 -0
- package/dist/conventions/state-store-redis.d.ts +32 -0
- package/dist/conventions/state-store-redis.d.ts.map +1 -0
- package/dist/conventions/state-store-redis.js +77 -0
- package/dist/conventions/state-store-redis.js.map +1 -0
- package/dist/conventions/state-store.d.ts +46 -0
- package/dist/conventions/state-store.d.ts.map +1 -0
- package/dist/conventions/state-store.js +105 -0
- package/dist/conventions/state-store.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +421 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/collection-audit.d.ts +13 -0
- package/dist/prompts/collection-audit.d.ts.map +1 -0
- package/dist/prompts/collection-audit.js +168 -0
- package/dist/prompts/collection-audit.js.map +1 -0
- package/dist/prompts/create-distribution.d.ts +15 -0
- package/dist/prompts/create-distribution.d.ts.map +1 -0
- package/dist/prompts/create-distribution.js +111 -0
- package/dist/prompts/create-distribution.js.map +1 -0
- package/dist/prompts/helpers.d.ts +20 -0
- package/dist/prompts/helpers.d.ts.map +1 -0
- package/dist/prompts/helpers.js +53 -0
- package/dist/prompts/helpers.js.map +1 -0
- package/dist/prompts/library-health-audit.d.ts +13 -0
- package/dist/prompts/library-health-audit.d.ts.map +1 -0
- package/dist/prompts/library-health-audit.js +131 -0
- package/dist/prompts/library-health-audit.js.map +1 -0
- package/dist/prompts/usage-insights.d.ts +13 -0
- package/dist/prompts/usage-insights.d.ts.map +1 -0
- package/dist/prompts/usage-insights.js +98 -0
- package/dist/prompts/usage-insights.js.map +1 -0
- package/dist/prompts/wrap-prompt-as-tool.d.ts +48 -0
- package/dist/prompts/wrap-prompt-as-tool.d.ts.map +1 -0
- package/dist/prompts/wrap-prompt-as-tool.js +61 -0
- package/dist/prompts/wrap-prompt-as-tool.js.map +1 -0
- package/dist/resources/asset-by-id.d.ts +4 -0
- package/dist/resources/asset-by-id.d.ts.map +1 -0
- package/dist/resources/asset-by-id.js +27 -0
- package/dist/resources/asset-by-id.js.map +1 -0
- package/dist/resources/collections.d.ts +5 -0
- package/dist/resources/collections.d.ts.map +1 -0
- package/dist/resources/collections.js +48 -0
- package/dist/resources/collections.js.map +1 -0
- package/dist/resources/custom-fields.d.ts +4 -0
- package/dist/resources/custom-fields.d.ts.map +1 -0
- package/dist/resources/custom-fields.js +30 -0
- package/dist/resources/custom-fields.js.map +1 -0
- package/dist/resources/folders.d.ts +5 -0
- package/dist/resources/folders.d.ts.map +1 -0
- package/dist/resources/folders.js +73 -0
- package/dist/resources/folders.js.map +1 -0
- package/dist/resources/helpers.d.ts +17 -0
- package/dist/resources/helpers.d.ts.map +1 -0
- package/dist/resources/helpers.js +59 -0
- package/dist/resources/helpers.js.map +1 -0
- package/dist/resources/portals.d.ts +5 -0
- package/dist/resources/portals.d.ts.map +1 -0
- package/dist/resources/portals.js +81 -0
- package/dist/resources/portals.js.map +1 -0
- package/dist/resources/recent-and-dashboard.d.ts +5 -0
- package/dist/resources/recent-and-dashboard.d.ts.map +1 -0
- package/dist/resources/recent-and-dashboard.js +42 -0
- package/dist/resources/recent-and-dashboard.js.map +1 -0
- package/dist/tools/asset-selection.d.ts +102 -0
- package/dist/tools/asset-selection.d.ts.map +1 -0
- package/dist/tools/asset-selection.js +133 -0
- package/dist/tools/asset-selection.js.map +1 -0
- package/dist/tools/audit/audit-folder-structure.d.ts +108 -0
- package/dist/tools/audit/audit-folder-structure.d.ts.map +1 -0
- package/dist/tools/audit/audit-folder-structure.js +260 -0
- package/dist/tools/audit/audit-folder-structure.js.map +1 -0
- package/dist/tools/audit/audit-naming-conventions.d.ts +83 -0
- package/dist/tools/audit/audit-naming-conventions.d.ts.map +1 -0
- package/dist/tools/audit/audit-naming-conventions.js +238 -0
- package/dist/tools/audit/audit-naming-conventions.js.map +1 -0
- package/dist/tools/audit/audit-tagging-hygiene.d.ts +77 -0
- package/dist/tools/audit/audit-tagging-hygiene.d.ts.map +1 -0
- package/dist/tools/audit/audit-tagging-hygiene.js +402 -0
- package/dist/tools/audit/audit-tagging-hygiene.js.map +1 -0
- package/dist/tools/audit/detect-duplicates.d.ts +62 -0
- package/dist/tools/audit/detect-duplicates.d.ts.map +1 -0
- package/dist/tools/audit/detect-duplicates.js +0 -0
- package/dist/tools/audit/detect-duplicates.js.map +1 -0
- package/dist/tools/audit/types.d.ts +526 -0
- package/dist/tools/audit/types.d.ts.map +1 -0
- package/dist/tools/audit/types.js +188 -0
- package/dist/tools/audit/types.js.map +1 -0
- package/dist/tools/bulk-move-assets.d.ts +78 -0
- package/dist/tools/bulk-move-assets.d.ts.map +1 -0
- package/dist/tools/bulk-move-assets.js +122 -0
- package/dist/tools/bulk-move-assets.js.map +1 -0
- package/dist/tools/bulk-normalize-filenames.d.ts +62 -0
- package/dist/tools/bulk-normalize-filenames.d.ts.map +1 -0
- package/dist/tools/bulk-normalize-filenames.js +237 -0
- package/dist/tools/bulk-normalize-filenames.js.map +1 -0
- package/dist/tools/bulk-rename-assets.d.ts +79 -0
- package/dist/tools/bulk-rename-assets.d.ts.map +1 -0
- package/dist/tools/bulk-rename-assets.js +139 -0
- package/dist/tools/bulk-rename-assets.js.map +1 -0
- package/dist/tools/bulk-tags.d.ts +107 -0
- package/dist/tools/bulk-tags.d.ts.map +1 -0
- package/dist/tools/bulk-tags.js +220 -0
- package/dist/tools/bulk-tags.js.map +1 -0
- package/dist/tools/client-adapters.d.ts +76 -0
- package/dist/tools/client-adapters.d.ts.map +1 -0
- package/dist/tools/client-adapters.js +648 -0
- package/dist/tools/client-adapters.js.map +1 -0
- package/dist/tools/collection-membership.d.ts +90 -0
- package/dist/tools/collection-membership.d.ts.map +1 -0
- package/dist/tools/collection-membership.js +195 -0
- package/dist/tools/collection-membership.js.map +1 -0
- package/dist/tools/create-collection.d.ts +63 -0
- package/dist/tools/create-collection.d.ts.map +1 -0
- package/dist/tools/create-collection.js +151 -0
- package/dist/tools/create-collection.js.map +1 -0
- package/dist/tools/create-folder.d.ts +46 -0
- package/dist/tools/create-folder.d.ts.map +1 -0
- package/dist/tools/create-folder.js +83 -0
- package/dist/tools/create-folder.js.map +1 -0
- package/dist/tools/create-share-link.d.ts +107 -0
- package/dist/tools/create-share-link.d.ts.map +1 -0
- package/dist/tools/create-share-link.js +239 -0
- package/dist/tools/create-share-link.js.map +1 -0
- package/dist/tools/get-asset-details.d.ts +401 -0
- package/dist/tools/get-asset-details.d.ts.map +1 -0
- package/dist/tools/get-asset-details.js +56 -0
- package/dist/tools/get-asset-details.js.map +1 -0
- package/dist/tools/get-collection.d.ts +126 -0
- package/dist/tools/get-collection.d.ts.map +1 -0
- package/dist/tools/get-collection.js +52 -0
- package/dist/tools/get-collection.js.map +1 -0
- package/dist/tools/get-embed-code.d.ts +195 -0
- package/dist/tools/get-embed-code.d.ts.map +1 -0
- package/dist/tools/get-embed-code.js +214 -0
- package/dist/tools/get-embed-code.js.map +1 -0
- package/dist/tools/insights/analyze-share-links.d.ts +159 -0
- package/dist/tools/insights/analyze-share-links.d.ts.map +1 -0
- package/dist/tools/insights/analyze-share-links.js +314 -0
- package/dist/tools/insights/analyze-share-links.js.map +1 -0
- package/dist/tools/insights/insight-cache.d.ts +36 -0
- package/dist/tools/insights/insight-cache.d.ts.map +1 -0
- package/dist/tools/insights/insight-cache.js +98 -0
- package/dist/tools/insights/insight-cache.js.map +1 -0
- package/dist/tools/insights/report-asset-activation.d.ts +149 -0
- package/dist/tools/insights/report-asset-activation.d.ts.map +1 -0
- package/dist/tools/insights/report-asset-activation.js +380 -0
- package/dist/tools/insights/report-asset-activation.js.map +1 -0
- package/dist/tools/insights/report-stale-assets.d.ts +120 -0
- package/dist/tools/insights/report-stale-assets.d.ts.map +1 -0
- package/dist/tools/insights/report-stale-assets.js +281 -0
- package/dist/tools/insights/report-stale-assets.js.map +1 -0
- package/dist/tools/insights/report-top-assets.d.ts +139 -0
- package/dist/tools/insights/report-top-assets.d.ts.map +1 -0
- package/dist/tools/insights/report-top-assets.js +407 -0
- package/dist/tools/insights/report-top-assets.js.map +1 -0
- package/dist/tools/list-categories.d.ts +127 -0
- package/dist/tools/list-categories.d.ts.map +1 -0
- package/dist/tools/list-categories.js +68 -0
- package/dist/tools/list-categories.js.map +1 -0
- package/dist/tools/list-collections.d.ts +127 -0
- package/dist/tools/list-collections.d.ts.map +1 -0
- package/dist/tools/list-collections.js +53 -0
- package/dist/tools/list-collections.js.map +1 -0
- package/dist/tools/list-custom-fields.d.ts +125 -0
- package/dist/tools/list-custom-fields.d.ts.map +1 -0
- package/dist/tools/list-custom-fields.js +51 -0
- package/dist/tools/list-custom-fields.js.map +1 -0
- package/dist/tools/list-share-links.d.ts +192 -0
- package/dist/tools/list-share-links.d.ts.map +1 -0
- package/dist/tools/list-share-links.js +92 -0
- package/dist/tools/list-share-links.js.map +1 -0
- package/dist/tools/list-workspaces.d.ts +88 -0
- package/dist/tools/list-workspaces.d.ts.map +1 -0
- package/dist/tools/list-workspaces.js +71 -0
- package/dist/tools/list-workspaces.js.map +1 -0
- package/dist/tools/move-asset.d.ts +48 -0
- package/dist/tools/move-asset.d.ts.map +1 -0
- package/dist/tools/move-asset.js +85 -0
- package/dist/tools/move-asset.js.map +1 -0
- package/dist/tools/rename-asset.d.ts +88 -0
- package/dist/tools/rename-asset.d.ts.map +1 -0
- package/dist/tools/rename-asset.js +100 -0
- package/dist/tools/rename-asset.js.map +1 -0
- package/dist/tools/rename-folder.d.ts +55 -0
- package/dist/tools/rename-folder.d.ts.map +1 -0
- package/dist/tools/rename-folder.js +101 -0
- package/dist/tools/rename-folder.js.map +1 -0
- package/dist/tools/revoke-share-link.d.ts +55 -0
- package/dist/tools/revoke-share-link.d.ts.map +1 -0
- package/dist/tools/revoke-share-link.js +77 -0
- package/dist/tools/revoke-share-link.js.map +1 -0
- package/dist/tools/search/facets.d.ts +34 -0
- package/dist/tools/search/facets.d.ts.map +1 -0
- package/dist/tools/search/facets.js +147 -0
- package/dist/tools/search/facets.js.map +1 -0
- package/dist/tools/search/filter-builder.d.ts +33 -0
- package/dist/tools/search/filter-builder.d.ts.map +1 -0
- package/dist/tools/search/filter-builder.js +111 -0
- package/dist/tools/search/filter-builder.js.map +1 -0
- package/dist/tools/search/search-assets.d.ts +41 -0
- package/dist/tools/search/search-assets.d.ts.map +1 -0
- package/dist/tools/search/search-assets.js +162 -0
- package/dist/tools/search/search-assets.js.map +1 -0
- package/dist/tools/search/search-collections.d.ts +35 -0
- package/dist/tools/search/search-collections.d.ts.map +1 -0
- package/dist/tools/search/search-collections.js +103 -0
- package/dist/tools/search/search-collections.js.map +1 -0
- package/dist/tools/search/types.d.ts +1047 -0
- package/dist/tools/search/types.d.ts.map +1 -0
- package/dist/tools/search/types.js +216 -0
- package/dist/tools/search/types.js.map +1 -0
- package/dist/tools/update-asset-metadata.d.ts +78 -0
- package/dist/tools/update-asset-metadata.d.ts.map +1 -0
- package/dist/tools/update-asset-metadata.js +203 -0
- package/dist/tools/update-asset-metadata.js.map +1 -0
- package/dist/tools/update-collection.d.ts +69 -0
- package/dist/tools/update-collection.d.ts.map +1 -0
- package/dist/tools/update-collection.js +142 -0
- package/dist/tools/update-collection.js.map +1 -0
- package/dist/tools/view-category-contents.d.ts +231 -0
- package/dist/tools/view-category-contents.d.ts.map +1 -0
- package/dist/tools/view-category-contents.js +97 -0
- package/dist/tools/view-category-contents.js.map +1 -0
- package/dist/types.d.ts +1326 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +288 -0
- package/dist/types.js.map +1 -0
- package/dist/typesense.d.ts +84 -0
- package/dist/typesense.d.ts.map +1 -0
- package/dist/typesense.js +243 -0
- package/dist/typesense.js.map +1 -0
- package/docs/api-field-verification.md +244 -0
- package/docs/deployment-runbook.md +446 -0
- package/docs/security-review.md +195 -0
- package/docs/typesense-filter-schema.md +262 -0
- package/docs/verified-endpoints.md +38 -0
- package/package.json +72 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// ── analyze_share_links (T1, read-only) ──────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Read-only distribution-insights tool. Aggregates share-link engagement
|
|
4
|
+
// metrics across the workspace by walking the existing
|
|
5
|
+
// `dashboard/list-share-assets-url` enumeration surface (already exposed
|
|
6
|
+
// to the MCP layer via `client.listShareLinks`).
|
|
7
|
+
//
|
|
8
|
+
// Output is purely structured metrics (totals, top-N, status
|
|
9
|
+
// distribution, creation time series, cohort breakdowns by creator). The
|
|
10
|
+
// client LLM is responsible for narrative synthesis on top — no prose is
|
|
11
|
+
// generated here. The companion `T6` task in this spec covers the
|
|
12
|
+
// AI-summary scaffolding separately.
|
|
13
|
+
//
|
|
14
|
+
// Caching is the next task (T5). This file is written so a cache wrapper
|
|
15
|
+
// can plug in around `analyzeShareLinks(...)` without touching the
|
|
16
|
+
// detection / aggregation logic.
|
|
17
|
+
//
|
|
18
|
+
// Type discipline: never uses `any`. The upstream `ShareLink` row carries
|
|
19
|
+
// loose / nullable fields (per the live `ShareLinkSchema` in `types.ts`),
|
|
20
|
+
// so this module defines a narrow projection (`ShareLinkRow`) that the
|
|
21
|
+
// enumerator adapter is responsible for producing.
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import { createToolError } from '../../conventions/errors.js';
|
|
24
|
+
import { estimateTokens, HARD_CAP_TOKENS } from '../../conventions/response-budget.js';
|
|
25
|
+
// ── Tool description ────────────────────────────────────────────────
|
|
26
|
+
export const ANALYZE_SHARE_LINKS_DESCRIPTION = 'Read-only analytics over share links in the workspace. Returns ' +
|
|
27
|
+
'structured engagement metrics — totals, top-N most-viewed links, ' +
|
|
28
|
+
'status distribution (active/expired/revoked), creation time series, ' +
|
|
29
|
+
'and cohort breakdowns by creator and (if scoped) by collection. ' +
|
|
30
|
+
'Filters: optional date range, collection scope, creator scope. ' +
|
|
31
|
+
'Per-day view counts are NOT exposed by the upstream surface, so the ' +
|
|
32
|
+
'time series only carries creation cadence; view metrics are reported ' +
|
|
33
|
+
'as totals/aggregates only. No mutation, no AI prose.';
|
|
34
|
+
// ── Input schema ────────────────────────────────────────────────────
|
|
35
|
+
export const DEFAULT_TOP_N = 10;
|
|
36
|
+
export const MAX_TOP_N = 50;
|
|
37
|
+
export const ENUMERATOR_HARD_CAP = 5000;
|
|
38
|
+
/**
|
|
39
|
+
* `date_from` / `date_to` are inclusive ISO-8601 date or datetime strings.
|
|
40
|
+
* If only a date is supplied (e.g. `2026-04-14`), it is interpreted as
|
|
41
|
+
* the start (00:00:00Z) for `date_from` or the end (23:59:59.999Z) for
|
|
42
|
+
* `date_to`. Both bounds are optional — omit to leave that side
|
|
43
|
+
* unbounded.
|
|
44
|
+
*/
|
|
45
|
+
export const AnalyzeShareLinksInputSchema = z
|
|
46
|
+
.object({
|
|
47
|
+
date_from: z
|
|
48
|
+
.string()
|
|
49
|
+
.min(1)
|
|
50
|
+
.refine((v) => !Number.isNaN(Date.parse(v)), {
|
|
51
|
+
message: 'date_from must be a valid ISO-8601 date or datetime string',
|
|
52
|
+
})
|
|
53
|
+
.optional(),
|
|
54
|
+
date_to: z
|
|
55
|
+
.string()
|
|
56
|
+
.min(1)
|
|
57
|
+
.refine((v) => !Number.isNaN(Date.parse(v)), {
|
|
58
|
+
message: 'date_to must be a valid ISO-8601 date or datetime string',
|
|
59
|
+
})
|
|
60
|
+
.optional(),
|
|
61
|
+
collection_id: z.string().min(1).optional(),
|
|
62
|
+
created_by_user_id: z.string().min(1).optional(),
|
|
63
|
+
top_n: z.number().int().min(1).max(MAX_TOP_N).default(DEFAULT_TOP_N),
|
|
64
|
+
})
|
|
65
|
+
.strict();
|
|
66
|
+
export class AnalyzeShareLinksTool {
|
|
67
|
+
deps;
|
|
68
|
+
name = 'analyze_share_links';
|
|
69
|
+
description = ANALYZE_SHARE_LINKS_DESCRIPTION;
|
|
70
|
+
constructor(deps) {
|
|
71
|
+
this.deps = deps;
|
|
72
|
+
}
|
|
73
|
+
async run(rawInput = {}) {
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = AnalyzeShareLinksInputSchema.parse(rawInput);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
error: createToolError('VALIDATION', err instanceof Error ? err.message : 'invalid input'),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Cross-field validation: when both bounds are set, from <= to.
|
|
85
|
+
if (parsed.date_from !== undefined && parsed.date_to !== undefined) {
|
|
86
|
+
const fromMs = Date.parse(parsed.date_from);
|
|
87
|
+
const toMs = Date.parse(parsed.date_to);
|
|
88
|
+
if (fromMs > toMs) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
error: createToolError('VALIDATION', 'date_from must be <= date_to'),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const enumerated = await this.deps.enumerator.enumerate();
|
|
96
|
+
if (!enumerated.ok) {
|
|
97
|
+
return { ok: false, error: enumerated.error };
|
|
98
|
+
}
|
|
99
|
+
const report = buildReport(enumerated.rows, enumerated.truncated, parsed);
|
|
100
|
+
return { ok: true, report };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function buildAnalyzeShareLinksTool(deps) {
|
|
104
|
+
return new AnalyzeShareLinksTool(deps);
|
|
105
|
+
}
|
|
106
|
+
// ── Aggregation (pure) ──────────────────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Build the analytics report from a flat list of rows. Pure function,
|
|
109
|
+
* exported for direct test access.
|
|
110
|
+
*/
|
|
111
|
+
export function buildReport(rows, enumeratorTruncated, input) {
|
|
112
|
+
// ── Filter ──
|
|
113
|
+
const fromMs = parseLowerBound(input.date_from);
|
|
114
|
+
const toMs = parseUpperBound(input.date_to);
|
|
115
|
+
const matching = rows.filter((row) => {
|
|
116
|
+
if (input.collection_id !== undefined) {
|
|
117
|
+
if (row.collection_id !== input.collection_id)
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (input.created_by_user_id !== undefined) {
|
|
121
|
+
if (row.created_by_user_id !== input.created_by_user_id)
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (fromMs !== null || toMs !== null) {
|
|
125
|
+
const created = row.created_at;
|
|
126
|
+
if (created === null)
|
|
127
|
+
return false;
|
|
128
|
+
const createdMs = Date.parse(created);
|
|
129
|
+
if (Number.isNaN(createdMs))
|
|
130
|
+
return false;
|
|
131
|
+
if (fromMs !== null && createdMs < fromMs)
|
|
132
|
+
return false;
|
|
133
|
+
if (toMs !== null && createdMs > toMs)
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
// ── Totals ──
|
|
139
|
+
const viewCounts = matching.map((r) => r.view_count);
|
|
140
|
+
const totalViews = viewCounts.reduce((acc, v) => acc + v, 0);
|
|
141
|
+
const linkCount = matching.length;
|
|
142
|
+
const meanViews = linkCount === 0 ? null : totalViews / linkCount;
|
|
143
|
+
const medianViews = linkCount === 0 ? null : computeMedian(viewCounts);
|
|
144
|
+
// ── Status distribution ──
|
|
145
|
+
const statusDistribution = { active: 0, expired: 0, revoked: 0 };
|
|
146
|
+
for (const row of matching) {
|
|
147
|
+
statusDistribution[row.status] += 1;
|
|
148
|
+
}
|
|
149
|
+
// ── Top N (descending by view count, ties broken by created_at desc) ──
|
|
150
|
+
const sortedByViews = [...matching].sort((a, b) => {
|
|
151
|
+
if (b.view_count !== a.view_count)
|
|
152
|
+
return b.view_count - a.view_count;
|
|
153
|
+
const aMs = a.created_at === null ? 0 : Date.parse(a.created_at);
|
|
154
|
+
const bMs = b.created_at === null ? 0 : Date.parse(b.created_at);
|
|
155
|
+
return bMs - aMs;
|
|
156
|
+
});
|
|
157
|
+
const topLinks = sortedByViews
|
|
158
|
+
.slice(0, input.top_n)
|
|
159
|
+
.map((r) => ({
|
|
160
|
+
id: r.id,
|
|
161
|
+
title: r.title,
|
|
162
|
+
share_url: r.share_url,
|
|
163
|
+
view_count: r.view_count,
|
|
164
|
+
status: r.status,
|
|
165
|
+
created_at: r.created_at,
|
|
166
|
+
expires_at: r.expires_at,
|
|
167
|
+
}));
|
|
168
|
+
// ── Time series (creation cadence, daily) ──
|
|
169
|
+
const dayBuckets = new Map();
|
|
170
|
+
for (const row of matching) {
|
|
171
|
+
if (row.created_at === null)
|
|
172
|
+
continue;
|
|
173
|
+
const day = toIsoDay(row.created_at);
|
|
174
|
+
if (day === null)
|
|
175
|
+
continue;
|
|
176
|
+
dayBuckets.set(day, (dayBuckets.get(day) ?? 0) + 1);
|
|
177
|
+
}
|
|
178
|
+
const linksCreatedPerDay = [...dayBuckets.entries()]
|
|
179
|
+
.map(([date, count]) => ({ date, links_created: count }))
|
|
180
|
+
.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
|
|
181
|
+
// ── Cohorts ──
|
|
182
|
+
const byCreator = aggregateCohort(matching, (r) => r.created_by_user_id);
|
|
183
|
+
const byCollection = input.collection_id !== undefined
|
|
184
|
+
? aggregateCohort(matching, (r) => r.collection_id)
|
|
185
|
+
: [];
|
|
186
|
+
const initial = {
|
|
187
|
+
filters: {
|
|
188
|
+
date_from: input.date_from ?? null,
|
|
189
|
+
date_to: input.date_to ?? null,
|
|
190
|
+
collection_id: input.collection_id ?? null,
|
|
191
|
+
created_by_user_id: input.created_by_user_id ?? null,
|
|
192
|
+
top_n: input.top_n,
|
|
193
|
+
},
|
|
194
|
+
totals: {
|
|
195
|
+
link_count: linkCount,
|
|
196
|
+
total_views: totalViews,
|
|
197
|
+
mean_views: meanViews,
|
|
198
|
+
median_views: medianViews,
|
|
199
|
+
},
|
|
200
|
+
status_distribution: statusDistribution,
|
|
201
|
+
top_links: topLinks,
|
|
202
|
+
time_series: {
|
|
203
|
+
links_created_per_day: linksCreatedPerDay,
|
|
204
|
+
views_per_day: null,
|
|
205
|
+
},
|
|
206
|
+
cohorts: {
|
|
207
|
+
by_creator: byCreator,
|
|
208
|
+
by_collection: byCollection,
|
|
209
|
+
},
|
|
210
|
+
truncated: enumeratorTruncated,
|
|
211
|
+
};
|
|
212
|
+
if (enumeratorTruncated) {
|
|
213
|
+
initial.truncation_reason =
|
|
214
|
+
`Enumerator stopped at the internal cap of ${ENUMERATOR_HARD_CAP} share links; report reflects a partial dataset.`;
|
|
215
|
+
}
|
|
216
|
+
return enforceResponseBudget(initial);
|
|
217
|
+
}
|
|
218
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
219
|
+
function parseLowerBound(value) {
|
|
220
|
+
if (value === undefined)
|
|
221
|
+
return null;
|
|
222
|
+
// Bare YYYY-MM-DD → start of day in UTC.
|
|
223
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
224
|
+
return Date.parse(`${value}T00:00:00.000Z`);
|
|
225
|
+
}
|
|
226
|
+
return Date.parse(value);
|
|
227
|
+
}
|
|
228
|
+
function parseUpperBound(value) {
|
|
229
|
+
if (value === undefined)
|
|
230
|
+
return null;
|
|
231
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
232
|
+
return Date.parse(`${value}T23:59:59.999Z`);
|
|
233
|
+
}
|
|
234
|
+
return Date.parse(value);
|
|
235
|
+
}
|
|
236
|
+
function toIsoDay(value) {
|
|
237
|
+
const ms = Date.parse(value);
|
|
238
|
+
if (Number.isNaN(ms))
|
|
239
|
+
return null;
|
|
240
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
241
|
+
}
|
|
242
|
+
function computeMedian(values) {
|
|
243
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
244
|
+
const n = sorted.length;
|
|
245
|
+
if (n === 0)
|
|
246
|
+
return 0;
|
|
247
|
+
const mid = Math.floor(n / 2);
|
|
248
|
+
if (n % 2 === 1)
|
|
249
|
+
return sorted[mid] ?? 0;
|
|
250
|
+
const left = sorted[mid - 1] ?? 0;
|
|
251
|
+
const right = sorted[mid] ?? 0;
|
|
252
|
+
return (left + right) / 2;
|
|
253
|
+
}
|
|
254
|
+
function aggregateCohort(rows, keyFn) {
|
|
255
|
+
const buckets = new Map();
|
|
256
|
+
for (const row of rows) {
|
|
257
|
+
const key = keyFn(row);
|
|
258
|
+
if (key === null)
|
|
259
|
+
continue;
|
|
260
|
+
let bucket = buckets.get(key);
|
|
261
|
+
if (bucket === undefined) {
|
|
262
|
+
bucket = { link_count: 0, view_count: 0 };
|
|
263
|
+
buckets.set(key, bucket);
|
|
264
|
+
}
|
|
265
|
+
bucket.link_count += 1;
|
|
266
|
+
bucket.view_count += row.view_count;
|
|
267
|
+
}
|
|
268
|
+
return [...buckets.entries()]
|
|
269
|
+
.map(([key, v]) => ({ key, link_count: v.link_count, view_count: v.view_count }))
|
|
270
|
+
.sort((a, b) => {
|
|
271
|
+
if (b.link_count !== a.link_count)
|
|
272
|
+
return b.link_count - a.link_count;
|
|
273
|
+
if (b.view_count !== a.view_count)
|
|
274
|
+
return b.view_count - a.view_count;
|
|
275
|
+
return a.key.localeCompare(b.key);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
// ── Response-budget enforcement ─────────────────────────────────────
|
|
279
|
+
const REDUCED_TOP_LIMIT = 5;
|
|
280
|
+
const REDUCED_COHORT_LIMIT = 10;
|
|
281
|
+
const REDUCED_TIMESERIES_LIMIT = 30;
|
|
282
|
+
/**
|
|
283
|
+
* Aggregate reports tend to be small (totals, distribution, top-N), but a
|
|
284
|
+
* very wide creator cohort or a long date-range time series could push us
|
|
285
|
+
* over budget. If the serialised report exceeds {@link HARD_CAP_TOKENS},
|
|
286
|
+
* trim sample-heavy fields in priority order:
|
|
287
|
+
* 1. cap `top_links`, `cohorts.*`, and `time_series.links_created_per_day`
|
|
288
|
+
* to small limits;
|
|
289
|
+
* 2. if still over, mark `truncated: true` regardless of enumerator
|
|
290
|
+
* truncation and return.
|
|
291
|
+
*/
|
|
292
|
+
function enforceResponseBudget(report) {
|
|
293
|
+
if (estimateTokens(JSON.stringify(report)) <= HARD_CAP_TOKENS) {
|
|
294
|
+
return report;
|
|
295
|
+
}
|
|
296
|
+
const trimmed = {
|
|
297
|
+
...report,
|
|
298
|
+
top_links: report.top_links.slice(0, REDUCED_TOP_LIMIT),
|
|
299
|
+
time_series: {
|
|
300
|
+
...report.time_series,
|
|
301
|
+
links_created_per_day: report.time_series.links_created_per_day.slice(-REDUCED_TIMESERIES_LIMIT),
|
|
302
|
+
},
|
|
303
|
+
cohorts: {
|
|
304
|
+
by_creator: report.cohorts.by_creator.slice(0, REDUCED_COHORT_LIMIT),
|
|
305
|
+
by_collection: report.cohorts.by_collection.slice(0, REDUCED_COHORT_LIMIT),
|
|
306
|
+
},
|
|
307
|
+
truncated: true,
|
|
308
|
+
truncation_reason: report.truncation_reason !== undefined
|
|
309
|
+
? `${report.truncation_reason} Additionally, top/cohort/time-series lists were trimmed to fit the response budget.`
|
|
310
|
+
: 'Top, cohort, and time-series lists were trimmed to fit the response budget.',
|
|
311
|
+
};
|
|
312
|
+
return trimmed;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=analyze-share-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-share-links.js","sourceRoot":"","sources":["../../../src/tools/insights/analyze-share-links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ShareLinkEnumerator } from './analyze-share-links.js';
|
|
2
|
+
/** Default cache lifetime per the spec — 10 minutes. */
|
|
3
|
+
export declare const DEFAULT_INSIGHT_CACHE_TTL_MS: number;
|
|
4
|
+
export interface InsightCacheOptions {
|
|
5
|
+
/** Cache lifetime in milliseconds. Defaults to {@link DEFAULT_INSIGHT_CACHE_TTL_MS}. */
|
|
6
|
+
ttlMs?: number;
|
|
7
|
+
/** Clock source. Defaults to `Date.now`; supply for deterministic tests. */
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Diagnostic shape exposed for tests. Production callers should ignore
|
|
12
|
+
* the extra methods — they exist so unit tests can assert cache hit /
|
|
13
|
+
* miss behaviour without poking the closure.
|
|
14
|
+
*/
|
|
15
|
+
export interface CachedEnumeratorWithStats extends ShareLinkEnumerator {
|
|
16
|
+
/** Returns true when a cached entry exists and is still fresh. */
|
|
17
|
+
hasFreshEntry(): boolean;
|
|
18
|
+
/** Force-expire the cached entry, if any. */
|
|
19
|
+
invalidate(): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a `ShareLinkEnumerator` with a 10-minute (default) in-memory
|
|
23
|
+
* cache. Multiple calls within the TTL window return the same cached
|
|
24
|
+
* result; concurrent calls during a fetch share the same in-flight
|
|
25
|
+
* promise. Errors are surfaced to every concurrent caller but are NOT
|
|
26
|
+
* cached — the next call after a failure will re-fetch.
|
|
27
|
+
*
|
|
28
|
+
* Pure function over the enumerator — does not mutate the input.
|
|
29
|
+
*
|
|
30
|
+
* The return shape exposes test-introspection methods
|
|
31
|
+
* ({@link CachedEnumeratorWithStats}) but assignment to a plain
|
|
32
|
+
* `ShareLinkEnumerator` is the production usage and discards them at
|
|
33
|
+
* the type level.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildCachedShareLinkEnumerator(inner: ShareLinkEnumerator, options?: InsightCacheOptions): CachedEnumeratorWithStats;
|
|
36
|
+
//# sourceMappingURL=insight-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insight-cache.d.ts","sourceRoot":"","sources":["../../../src/tools/insights/insight-cache.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACV,mBAAmB,EAEpB,MAAM,0BAA0B,CAAC;AAGlC,wDAAwD;AACxD,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAaD;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE,kEAAkE;IAClE,aAAa,IAAI,OAAO,CAAC;IACzB,6CAA6C;IAC7C,UAAU,IAAI,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,mBAAwB,GAChC,yBAAyB,CAuD3B"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ── Insight Cache (T5) ──────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// In-memory TTL cache wrapper around the share-link enumerator. The four
|
|
4
|
+
// distribution-insight tools (analyze_share_links, report_asset_activation,
|
|
5
|
+
// report_top_assets, report_stale_assets) all enumerate the workspace's
|
|
6
|
+
// share-link list — three paginated GETs per status bucket per call.
|
|
7
|
+
// Without caching, four insight tool calls in the same conversation hit
|
|
8
|
+
// the upstream API up to 12 times. Caching the enumerator output for ten
|
|
9
|
+
// minutes drops that to one round-trip per ten-minute window, regardless
|
|
10
|
+
// of which (or how many) insight tools are called.
|
|
11
|
+
//
|
|
12
|
+
// Caching policy:
|
|
13
|
+
// * Cache LIFE: 10 minutes by default; configurable.
|
|
14
|
+
// * Cache KEY: implicit — each cache instance is per-workspace because
|
|
15
|
+
// the MCP server is single-workspace and a new server instance is
|
|
16
|
+
// created per stdio client. There is exactly one shared enumerator
|
|
17
|
+
// in `createServer()`.
|
|
18
|
+
// * Cache POLLUTION: failures are NOT cached. A bad upstream response
|
|
19
|
+
// should be retried, not memoised for ten minutes.
|
|
20
|
+
// * SINGLE-FLIGHT: concurrent callers during a fetch share the same
|
|
21
|
+
// in-flight promise. Without this, four insight tools booting at the
|
|
22
|
+
// same time would issue four parallel enumerations even though we
|
|
23
|
+
// intend to serve them all from one round-trip.
|
|
24
|
+
//
|
|
25
|
+
// Type discipline: never uses `any`. The cache only operates on the
|
|
26
|
+
// already-typed `ShareLinkEnumerator` surface — it does not need
|
|
27
|
+
// generics because the enumerator is the only memoisable boundary in
|
|
28
|
+
// the insight surface.
|
|
29
|
+
/** Default cache lifetime per the spec — 10 minutes. */
|
|
30
|
+
export const DEFAULT_INSIGHT_CACHE_TTL_MS = 10 * 60 * 1000;
|
|
31
|
+
/**
|
|
32
|
+
* Wrap a `ShareLinkEnumerator` with a 10-minute (default) in-memory
|
|
33
|
+
* cache. Multiple calls within the TTL window return the same cached
|
|
34
|
+
* result; concurrent calls during a fetch share the same in-flight
|
|
35
|
+
* promise. Errors are surfaced to every concurrent caller but are NOT
|
|
36
|
+
* cached — the next call after a failure will re-fetch.
|
|
37
|
+
*
|
|
38
|
+
* Pure function over the enumerator — does not mutate the input.
|
|
39
|
+
*
|
|
40
|
+
* The return shape exposes test-introspection methods
|
|
41
|
+
* ({@link CachedEnumeratorWithStats}) but assignment to a plain
|
|
42
|
+
* `ShareLinkEnumerator` is the production usage and discards them at
|
|
43
|
+
* the type level.
|
|
44
|
+
*/
|
|
45
|
+
export function buildCachedShareLinkEnumerator(inner, options = {}) {
|
|
46
|
+
const ttlMs = options.ttlMs ?? DEFAULT_INSIGHT_CACHE_TTL_MS;
|
|
47
|
+
const now = options.now ?? Date.now;
|
|
48
|
+
let entry = null;
|
|
49
|
+
let inflight = null;
|
|
50
|
+
const tryRefresh = () => {
|
|
51
|
+
if (inflight !== null)
|
|
52
|
+
return inflight;
|
|
53
|
+
const fetchPromise = (async () => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await inner.enumerate();
|
|
56
|
+
if (result.ok) {
|
|
57
|
+
entry = {
|
|
58
|
+
expires_at: now() + ttlMs,
|
|
59
|
+
// Defensive copy — the cache owns its rows independently of
|
|
60
|
+
// the caller, so external mutation of the returned array
|
|
61
|
+
// does not corrupt subsequent cache hits.
|
|
62
|
+
result: {
|
|
63
|
+
ok: true,
|
|
64
|
+
rows: [...result.rows],
|
|
65
|
+
truncated: result.truncated,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Failures are intentionally NOT cached.
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
inflight = null;
|
|
74
|
+
}
|
|
75
|
+
})();
|
|
76
|
+
inflight = fetchPromise;
|
|
77
|
+
return fetchPromise;
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
enumerate: async () => {
|
|
81
|
+
if (entry !== null && entry.expires_at > now()) {
|
|
82
|
+
// Return a fresh copy of the cached result so callers can
|
|
83
|
+
// mutate their local view without corrupting future cache hits.
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
rows: [...entry.result.rows],
|
|
87
|
+
truncated: entry.result.truncated,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return tryRefresh();
|
|
91
|
+
},
|
|
92
|
+
hasFreshEntry: () => entry !== null && entry.expires_at > now(),
|
|
93
|
+
invalidate: () => {
|
|
94
|
+
entry = null;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=insight-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insight-cache.js","sourceRoot":"","sources":["../../../src/tools/insights/insight-cache.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,yEAAyE;AACzE,4EAA4E;AAC5E,wEAAwE;AACxE,qEAAqE;AACrE,wEAAwE;AACxE,yEAAyE;AACzE,yEAAyE;AACzE,mDAAmD;AACnD,EAAE;AACF,kBAAkB;AAClB,uDAAuD;AACvD,yEAAyE;AACzE,sEAAsE;AACtE,uEAAuE;AACvE,2BAA2B;AAC3B,wEAAwE;AACxE,uDAAuD;AACvD,sEAAsE;AACtE,yEAAyE;AACzE,sEAAsE;AACtE,oDAAoD;AACpD,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,qEAAqE;AACrE,uBAAuB;AAQvB,wDAAwD;AACxD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAgC3D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,8BAA8B,CAC5C,KAA0B,EAC1B,UAA+B,EAAE;IAEjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,4BAA4B,CAAC;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IAEpC,IAAI,KAAK,GAAsB,IAAI,CAAC;IACpC,IAAI,QAAQ,GAAoC,IAAI,CAAC;IAErD,MAAM,UAAU,GAAG,GAA6B,EAAE;QAChD,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC;QAEvC,MAAM,YAAY,GAAG,CAAC,KAAK,IAA8B,EAAE;YACzD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;oBACd,KAAK,GAAG;wBACN,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK;wBACzB,4DAA4D;wBAC5D,yDAAyD;wBACzD,0CAA0C;wBAC1C,MAAM,EAAE;4BACN,EAAE,EAAE,IAAI;4BACR,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;4BACtB,SAAS,EAAE,MAAM,CAAC,SAAS;yBAC5B;qBACF,CAAC;gBACJ,CAAC;gBACD,yCAAyC;gBACzC,OAAO,MAAM,CAAC;YAChB,CAAC;oBAAS,CAAC;gBACT,QAAQ,GAAG,IAAI,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,QAAQ,GAAG,YAAY,CAAC;QACxB,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC;IAEF,OAAO;QACL,SAAS,EAAE,KAAK,IAA8B,EAAE;YAC9C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,EAAE,EAAE,CAAC;gBAC/C,0DAA0D;gBAC1D,gEAAgE;gBAChE,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;oBAC5B,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS;iBAClC,CAAC;YACJ,CAAC;YACD,OAAO,UAAU,EAAE,CAAC;QACtB,CAAC;QACD,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,EAAE;QAC/D,UAAU,EAAE,GAAG,EAAE;YACf,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type McpToolError } from '../../conventions/errors.js';
|
|
3
|
+
import type { ShareLinkEnumerator, ShareLinkRow } from './analyze-share-links.js';
|
|
4
|
+
export declare const REPORT_ASSET_ACTIVATION_DESCRIPTION: string;
|
|
5
|
+
export declare const DEFAULT_TOP_N = 10;
|
|
6
|
+
export declare const MAX_TOP_N = 50;
|
|
7
|
+
/** Default look-back window when neither `date_from` nor `date_to` is set. */
|
|
8
|
+
export declare const DEFAULT_LOOKBACK_DAYS = 30;
|
|
9
|
+
/**
|
|
10
|
+
* `date_from` / `date_to` are inclusive ISO-8601 date or datetime
|
|
11
|
+
* strings. If only a date is supplied (e.g. `2026-04-14`) it is
|
|
12
|
+
* interpreted as the start (00:00:00Z) for `date_from` or the end
|
|
13
|
+
* (23:59:59.999Z) for `date_to`. When BOTH are omitted, the analyzer
|
|
14
|
+
* defaults to the last {@link DEFAULT_LOOKBACK_DAYS} days.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ReportAssetActivationInputSchema: z.ZodObject<{
|
|
17
|
+
date_from: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
18
|
+
date_to: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
19
|
+
collection_id: z.ZodOptional<z.ZodString>;
|
|
20
|
+
top_n: z.ZodDefault<z.ZodNumber>;
|
|
21
|
+
}, "strict", z.ZodTypeAny, {
|
|
22
|
+
top_n: number;
|
|
23
|
+
collection_id?: string | undefined;
|
|
24
|
+
date_from?: string | undefined;
|
|
25
|
+
date_to?: string | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
collection_id?: string | undefined;
|
|
28
|
+
date_from?: string | undefined;
|
|
29
|
+
date_to?: string | undefined;
|
|
30
|
+
top_n?: number | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
export type ReportAssetActivationInput = z.input<typeof ReportAssetActivationInputSchema>;
|
|
33
|
+
export type ReportAssetActivationInputResolved = z.output<typeof ReportAssetActivationInputSchema>;
|
|
34
|
+
/**
|
|
35
|
+
* Narrow shape consumed from `dashboard/common-data`. The upstream
|
|
36
|
+
* surface is opaque (`unknown` at the client layer), so the reader is
|
|
37
|
+
* responsible for parsing it via zod and returning `null` when the shape
|
|
38
|
+
* does not carry a usable `total_assets` count.
|
|
39
|
+
*/
|
|
40
|
+
export interface DashboardSnapshot {
|
|
41
|
+
/** Total number of assets in the workspace. */
|
|
42
|
+
total_assets: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read-only adapter for the workspace dashboard projection. Returns
|
|
46
|
+
* `ok: true` with a parsed snapshot, `ok: true` with `snapshot: null`
|
|
47
|
+
* when the upstream shape did not carry the fields we need (the report
|
|
48
|
+
* surfaces `total_assets: null` and `activation_rate: null` rather than
|
|
49
|
+
* crashing), or `ok: false` with a typed `McpToolError` on a transport
|
|
50
|
+
* failure.
|
|
51
|
+
*/
|
|
52
|
+
export interface DashboardCommonDataReader {
|
|
53
|
+
read(): Promise<{
|
|
54
|
+
ok: true;
|
|
55
|
+
snapshot: DashboardSnapshot | null;
|
|
56
|
+
} | {
|
|
57
|
+
ok: false;
|
|
58
|
+
error: McpToolError;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export interface AssetActivationSummary {
|
|
62
|
+
/** Workspace-wide asset count. `null` when the dashboard reader could not parse the upstream shape. */
|
|
63
|
+
total_assets: number | null;
|
|
64
|
+
/** Number of distinct assets touched by share-link activity in the matching range. */
|
|
65
|
+
assets_with_distribution_activity: number;
|
|
66
|
+
/** Ratio in [0, 1]; `null` when `total_assets` is unknown. */
|
|
67
|
+
activation_rate: number | null;
|
|
68
|
+
/** `total_assets - assets_with_distribution_activity`; `null` when `total_assets` is unknown. */
|
|
69
|
+
dormant_count: number | null;
|
|
70
|
+
}
|
|
71
|
+
export interface AssetFirstSharedPoint {
|
|
72
|
+
/** ISO date (YYYY-MM-DD) bucket. */
|
|
73
|
+
date: string;
|
|
74
|
+
/** Number of assets that were first shared on this date (within the matching range). */
|
|
75
|
+
count: number;
|
|
76
|
+
}
|
|
77
|
+
export interface TopActivatedAsset {
|
|
78
|
+
id: string;
|
|
79
|
+
/** Best-effort title pulled from the most-recent share-link row covering this asset. */
|
|
80
|
+
title: string | null;
|
|
81
|
+
/** Number of share links covering this asset within the matching range. */
|
|
82
|
+
share_link_count: number;
|
|
83
|
+
/** Sum of view counts across those share links. */
|
|
84
|
+
total_views: number;
|
|
85
|
+
}
|
|
86
|
+
export interface ActivationCohortEntry {
|
|
87
|
+
/** User id for `by_user`, collection id for `by_collection`. */
|
|
88
|
+
user_id?: string;
|
|
89
|
+
collection_id?: string;
|
|
90
|
+
/** Number of distinct activated assets attributed to this cohort. */
|
|
91
|
+
activated_assets: number;
|
|
92
|
+
}
|
|
93
|
+
export interface AssetActivationReport {
|
|
94
|
+
/** Inputs as resolved (after defaults / filters applied). */
|
|
95
|
+
filters: {
|
|
96
|
+
date_from: string | null;
|
|
97
|
+
date_to: string | null;
|
|
98
|
+
collection_id: string | null;
|
|
99
|
+
top_n: number;
|
|
100
|
+
};
|
|
101
|
+
summary: AssetActivationSummary;
|
|
102
|
+
time_series: {
|
|
103
|
+
assets_first_shared_per_day: AssetFirstSharedPoint[];
|
|
104
|
+
};
|
|
105
|
+
top_activated_assets: TopActivatedAsset[];
|
|
106
|
+
cohort_breakdown: {
|
|
107
|
+
by_user: Array<{
|
|
108
|
+
user_id: string;
|
|
109
|
+
activated_assets: number;
|
|
110
|
+
}>;
|
|
111
|
+
by_collection: Array<{
|
|
112
|
+
collection_id: string;
|
|
113
|
+
activated_assets: number;
|
|
114
|
+
}>;
|
|
115
|
+
};
|
|
116
|
+
/** True when the share-link enumerator hit its hard cap or the response was trimmed for budget. */
|
|
117
|
+
truncated: boolean;
|
|
118
|
+
truncation_reason?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface ReportAssetActivationDeps {
|
|
121
|
+
enumerator: ShareLinkEnumerator;
|
|
122
|
+
dashboard: DashboardCommonDataReader;
|
|
123
|
+
}
|
|
124
|
+
export type ReportAssetActivationOutcome = {
|
|
125
|
+
ok: true;
|
|
126
|
+
report: AssetActivationReport;
|
|
127
|
+
} | {
|
|
128
|
+
ok: false;
|
|
129
|
+
error: McpToolError;
|
|
130
|
+
};
|
|
131
|
+
export declare class ReportAssetActivationTool {
|
|
132
|
+
private readonly deps;
|
|
133
|
+
readonly name = "report_asset_activation";
|
|
134
|
+
readonly description: string;
|
|
135
|
+
constructor(deps: ReportAssetActivationDeps);
|
|
136
|
+
run(rawInput?: ReportAssetActivationInput): Promise<ReportAssetActivationOutcome>;
|
|
137
|
+
}
|
|
138
|
+
export declare function buildReportAssetActivationTool(deps: ReportAssetActivationDeps): ReportAssetActivationTool;
|
|
139
|
+
/**
|
|
140
|
+
* Build the asset-activation report from a flat list of share-link rows
|
|
141
|
+
* plus an optional workspace-snapshot denominator. Pure function,
|
|
142
|
+
* exported for direct test access.
|
|
143
|
+
*
|
|
144
|
+
* `dashboardSnapshot === null` means the workspace asset-count is
|
|
145
|
+
* unavailable; the report surfaces `total_assets`, `activation_rate`,
|
|
146
|
+
* and `dormant_count` as `null` per the degradation contract.
|
|
147
|
+
*/
|
|
148
|
+
export declare function buildReport(rows: ReadonlyArray<ShareLinkRow>, enumeratorTruncated: boolean, dashboardSnapshot: DashboardSnapshot | null, input: ReportAssetActivationInputResolved): AssetActivationReport;
|
|
149
|
+
//# sourceMappingURL=report-asset-activation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-asset-activation.d.ts","sourceRoot":"","sources":["../../../src/tools/insights/report-asset-activation.ts"],"names":[],"mappings":"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"}
|