@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,81 @@
|
|
|
1
|
+
// ── collage://portals and collage://portals/{id} ──────────────────
|
|
2
|
+
// Both backed by `instance/get`. The endpoint returns the workspace
|
|
3
|
+
// portal/instance configuration; the response shape is opaque (we keep
|
|
4
|
+
// it as `unknown` at the client layer until a downstream consumer needs
|
|
5
|
+
// specific fields). For `portals/{id}` we filter the portal list
|
|
6
|
+
// client-side, mirroring how `getCollection` filters `collection/get-all`.
|
|
7
|
+
import { createToolError } from '../conventions/errors.js';
|
|
8
|
+
import { errorResource, jsonResource } from './helpers.js';
|
|
9
|
+
export function buildPortalsListReader(client) {
|
|
10
|
+
return async (uri) => {
|
|
11
|
+
const result = await client.getInstance();
|
|
12
|
+
if (!result.ok) {
|
|
13
|
+
return errorResource(uri, result.error, result.request_id);
|
|
14
|
+
}
|
|
15
|
+
return jsonResource(uri, {
|
|
16
|
+
data: result.data,
|
|
17
|
+
request_id: result.request_id,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function buildPortalByIdReader(client) {
|
|
22
|
+
return async (uri, variables) => {
|
|
23
|
+
const idVar = variables['id'];
|
|
24
|
+
const id = Array.isArray(idVar) ? idVar[0] : idVar;
|
|
25
|
+
if (id === undefined || id === '') {
|
|
26
|
+
return errorResource(uri, createToolError('VALIDATION', 'collage://portals/{id} requires an id'), 'no-request');
|
|
27
|
+
}
|
|
28
|
+
const result = await client.getInstance();
|
|
29
|
+
if (!result.ok) {
|
|
30
|
+
return errorResource(uri, result.error, result.request_id);
|
|
31
|
+
}
|
|
32
|
+
const match = findPortalById(result.data, id);
|
|
33
|
+
if (match === null) {
|
|
34
|
+
return errorResource(uri, createToolError('NOT_FOUND', `portal ${id} not found in workspace`), result.request_id);
|
|
35
|
+
}
|
|
36
|
+
return jsonResource(uri, {
|
|
37
|
+
portal: match,
|
|
38
|
+
request_id: result.request_id,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The instance/get response shape is opaque at the client layer. We probe
|
|
44
|
+
* a few likely structures to find a portal with a matching id without
|
|
45
|
+
* locking the resource to a specific schema yet.
|
|
46
|
+
*/
|
|
47
|
+
function findPortalById(raw, id) {
|
|
48
|
+
const candidates = collectPortalCandidates(raw);
|
|
49
|
+
for (const candidate of candidates) {
|
|
50
|
+
if (candidate === null || typeof candidate !== 'object')
|
|
51
|
+
continue;
|
|
52
|
+
const obj = candidate;
|
|
53
|
+
const candidateId = obj['id'];
|
|
54
|
+
if (candidateId !== undefined && String(candidateId) === id) {
|
|
55
|
+
return candidate;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function collectPortalCandidates(raw) {
|
|
61
|
+
if (raw === null || typeof raw !== 'object')
|
|
62
|
+
return [];
|
|
63
|
+
const obj = raw;
|
|
64
|
+
const out = [];
|
|
65
|
+
// Top-level array
|
|
66
|
+
if (Array.isArray(raw)) {
|
|
67
|
+
out.push(...raw);
|
|
68
|
+
}
|
|
69
|
+
// Common envelope shapes: `{ data: [...] }` or `{ data: { instances: [...] } }`
|
|
70
|
+
for (const key of ['data', 'instances', 'portals']) {
|
|
71
|
+
const v = obj[key];
|
|
72
|
+
if (Array.isArray(v)) {
|
|
73
|
+
out.push(...v);
|
|
74
|
+
}
|
|
75
|
+
else if (v !== null && typeof v === 'object') {
|
|
76
|
+
out.push(...collectPortalCandidates(v));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=portals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portals.js","sourceRoot":"","sources":["../../src/resources/portals.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,oEAAoE;AACpE,uEAAuE;AACvE,wEAAwE;AACxE,iEAAiE;AACjE,2EAA2E;AAI3E,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,sBAAsB,CAAC,MAAqB;IAC1D,OAAO,KAAK,EAAE,GAAQ,EAA+B,EAAE;QACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,YAAY,CAAC,GAAG,EAAE;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,OAAO,KAAK,EACV,GAAQ,EACR,SAA4C,EACf,EAAE;QAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACnD,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAClC,OAAO,aAAa,CAClB,GAAG,EACH,eAAe,CAAC,YAAY,EAAE,uCAAuC,CAAC,EACtE,YAAY,CACb,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,aAAa,CAClB,GAAG,EACH,eAAe,CAAC,WAAW,EAAE,UAAU,EAAE,yBAAyB,CAAC,EACnE,MAAM,CAAC,UAAU,CAClB,CAAC;QACJ,CAAC;QAED,OAAO,YAAY,CAAC,GAAG,EAAE;YACvB,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,GAAY,EAAE,EAAU;IAC9C,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAChD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ;YAAE,SAAS;QAClE,MAAM,GAAG,GAAG,SAAoC,CAAC;QACjD,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,WAAW,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;YAC5D,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAY;IAC3C,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACvD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,GAAG,GAAc,EAAE,CAAC;IAE1B,kBAAkB;IAClB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,gFAAgF;IAChF,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,CAAU,EAAE,CAAC;QAC5D,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACjB,CAAC;aAAM,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/C,GAAG,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { CollageClient } from '../client.js';
|
|
3
|
+
export declare function buildRecentAssetsReader(client: CollageClient): (uri: URL) => Promise<ReadResourceResult>;
|
|
4
|
+
export declare function buildDashboardReader(client: CollageClient): (uri: URL) => Promise<ReadResourceResult>;
|
|
5
|
+
//# sourceMappingURL=recent-and-dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-and-dashboard.d.ts","sourceRoot":"","sources":["../../src/resources/recent-and-dashboard.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,IAC7C,KAAK,GAAG,KAAG,OAAO,CAAC,kBAAkB,CAAC,CAqBrD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,IAC1C,KAAK,GAAG,KAAG,OAAO,CAAC,kBAAkB,CAAC,CAUrD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// ── collage://assets/recent and collage://dashboard ────────────────
|
|
2
|
+
// Backed by `get-recent-uploaded` and `dashboard/common-data`.
|
|
3
|
+
//
|
|
4
|
+
// Both endpoints have opaque (`z.unknown()`) response shapes at the
|
|
5
|
+
// client layer. The resource handlers pass them through as-is so the
|
|
6
|
+
// LLM can reason over Collage's native projection until we have a
|
|
7
|
+
// downstream consumer that needs a narrower schema.
|
|
8
|
+
import { clampPageSize } from '../conventions/pagination.js';
|
|
9
|
+
import { decodeOffsetCursor, errorResource, jsonResource } from './helpers.js';
|
|
10
|
+
export function buildRecentAssetsReader(client) {
|
|
11
|
+
return async (uri) => {
|
|
12
|
+
const pageSize = clampPageSize(Number(uri.searchParams.get('page_size') ?? undefined) || undefined);
|
|
13
|
+
const offset = decodeOffsetCursor(uri);
|
|
14
|
+
const page = Math.floor(offset / pageSize) + 1;
|
|
15
|
+
const result = await client.listRecentUploaded({
|
|
16
|
+
page,
|
|
17
|
+
totalRecord: pageSize,
|
|
18
|
+
});
|
|
19
|
+
if (!result.ok) {
|
|
20
|
+
return errorResource(uri, result.error, result.request_id);
|
|
21
|
+
}
|
|
22
|
+
return jsonResource(uri, {
|
|
23
|
+
data: result.data,
|
|
24
|
+
page,
|
|
25
|
+
page_size: pageSize,
|
|
26
|
+
request_id: result.request_id,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function buildDashboardReader(client) {
|
|
31
|
+
return async (uri) => {
|
|
32
|
+
const result = await client.getDashboardCommonData();
|
|
33
|
+
if (!result.ok) {
|
|
34
|
+
return errorResource(uri, result.error, result.request_id);
|
|
35
|
+
}
|
|
36
|
+
return jsonResource(uri, {
|
|
37
|
+
data: result.data,
|
|
38
|
+
request_id: result.request_id,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=recent-and-dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-and-dashboard.js","sourceRoot":"","sources":["../../src/resources/recent-and-dashboard.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,+DAA+D;AAC/D,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,kEAAkE;AAClE,oDAAoD;AAIpD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE/E,MAAM,UAAU,uBAAuB,CAAC,MAAqB;IAC3D,OAAO,KAAK,EAAE,GAAQ,EAA+B,EAAE;QACrD,MAAM,QAAQ,GAAG,aAAa,CAC5B,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CACpE,CAAC;QACF,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC;YAC7C,IAAI;YACJ,WAAW,EAAE,QAAQ;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,YAAY,CAAC,GAAG,EAAE;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI;YACJ,SAAS,EAAE,QAAQ;YACnB,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAqB;IACxD,OAAO,KAAK,EAAE,GAAQ,EAA+B,EAAE;QACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,sBAAsB,EAAE,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,YAAY,CAAC,GAAG,EAAE;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU,EAAE,MAAM,CAAC,UAAU;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { CollageClient } from '../client.js';
|
|
3
|
+
import { type McpToolError } from '../conventions/errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* Selection input. Exactly one of `query`, `ids`, or `collection_id` must be
|
|
6
|
+
* provided. The schema accepts all three and the resolver enforces the
|
|
7
|
+
* exclusivity at runtime so callers get a structured `VALIDATION` error
|
|
8
|
+
* instead of a Zod parse failure.
|
|
9
|
+
*/
|
|
10
|
+
export declare const AssetSelectionSchema: z.ZodEffects<z.ZodObject<{
|
|
11
|
+
query: z.ZodOptional<z.ZodString>;
|
|
12
|
+
ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodNumber, z.ZodString]>, "many">>;
|
|
13
|
+
collection_id: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodString]>>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
query?: string | undefined;
|
|
16
|
+
collection_id?: string | number | undefined;
|
|
17
|
+
ids?: (string | number)[] | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
query?: string | undefined;
|
|
20
|
+
collection_id?: string | number | undefined;
|
|
21
|
+
ids?: (string | number)[] | undefined;
|
|
22
|
+
}>, {
|
|
23
|
+
query?: string | undefined;
|
|
24
|
+
collection_id?: string | number | undefined;
|
|
25
|
+
ids?: (string | number)[] | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
query?: string | undefined;
|
|
28
|
+
collection_id?: string | number | undefined;
|
|
29
|
+
ids?: (string | number)[] | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
export type AssetSelection = z.infer<typeof AssetSelectionSchema>;
|
|
32
|
+
/**
|
|
33
|
+
* Minimal per-asset projection used by Phase 1 tools to render diffs.
|
|
34
|
+
*
|
|
35
|
+
* Tools that need fuller detail (e.g. `update_asset_metadata` reading the
|
|
36
|
+
* description) call back into the client themselves keyed by `id`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ResolvedAsset {
|
|
39
|
+
id: string;
|
|
40
|
+
display_file_name?: string;
|
|
41
|
+
/** Current tag names — used by `bulk_*_tags` to compute set differences. */
|
|
42
|
+
tags?: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface ResolveSelectionResult {
|
|
45
|
+
ok: true;
|
|
46
|
+
asset_ids: string[];
|
|
47
|
+
/** Optional per-asset snapshot keyed by id. May be empty (e.g. `ids` path). */
|
|
48
|
+
snapshots: Map<string, ResolvedAsset>;
|
|
49
|
+
}
|
|
50
|
+
export type ResolveSelectionOutcome = ResolveSelectionResult | {
|
|
51
|
+
ok: false;
|
|
52
|
+
error: McpToolError;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Narrow surface the resolver depends on for the `query` path. Implementations
|
|
56
|
+
* can wrap Typesense, the Collage search REST endpoint, or — until the public
|
|
57
|
+
* Typesense host is provisioned — throw a typed `SEARCH_UNAVAILABLE` error.
|
|
58
|
+
*/
|
|
59
|
+
export interface AssetSearchAdapter {
|
|
60
|
+
searchAssets(query: string): Promise<{
|
|
61
|
+
ok: true;
|
|
62
|
+
assets: ResolvedAsset[];
|
|
63
|
+
} | {
|
|
64
|
+
ok: false;
|
|
65
|
+
error: McpToolError;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Default search adapter used when no Typesense host is configured. Always
|
|
70
|
+
* returns a typed `UPSTREAM` error (`SEARCH_UNAVAILABLE`) so callers can
|
|
71
|
+
* surface a clear, actionable failure message instead of crashing.
|
|
72
|
+
*/
|
|
73
|
+
export declare const unavailableSearchAdapter: AssetSearchAdapter;
|
|
74
|
+
/**
|
|
75
|
+
* Narrow surface for the `collection_id` path. The default implementation
|
|
76
|
+
* is provided below and wraps `CollageClient.listCollections()` — but tests
|
|
77
|
+
* can substitute a fake without standing up the full client.
|
|
78
|
+
*/
|
|
79
|
+
export interface CollectionLookup {
|
|
80
|
+
getCollectionAssetIds(collectionId: string): Promise<{
|
|
81
|
+
ok: true;
|
|
82
|
+
asset_ids: string[];
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
error: McpToolError;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
export declare function buildCollectionLookup(client: CollageClient): CollectionLookup;
|
|
89
|
+
export interface ResolveSelectionDeps {
|
|
90
|
+
search: AssetSearchAdapter;
|
|
91
|
+
collections: CollectionLookup;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a selection envelope into a concrete list of asset IDs.
|
|
95
|
+
*
|
|
96
|
+
* - `ids` — returned verbatim, deduplicated, coerced to string.
|
|
97
|
+
* - `collection_id` — looked up via the live Collage REST API.
|
|
98
|
+
* - `query` — delegated to the search adapter; may return
|
|
99
|
+
* `SEARCH_UNAVAILABLE` until Typesense is provisioned.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveAssetSelection(selection: AssetSelection, deps: ResolveSelectionDeps): Promise<ResolveSelectionOutcome>;
|
|
102
|
+
//# sourceMappingURL=asset-selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-selection.d.ts","sourceRoot":"","sources":["../../src/tools/asset-selection.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAK9E;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;EAmB9B,CAAC;AAEJ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,+EAA+E;IAC/E,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,uBAAuB,GAC/B,sBAAsB,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAIvC;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,CACV,KAAK,EAAE,MAAM,GACZ,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,aAAa,EAAE,CAAA;KAAE,GACrC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;CACH;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,kBAQtC,CAAC;AAIF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB,CACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GACjC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;CACH;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAqB7E;AAID,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,cAAc,EACzB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CA+ClC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// ── Shared Asset Selection Resolver ──────────────────────────────────
|
|
2
|
+
// Phase 1 metadata/tagging tools all share the same way to identify which
|
|
3
|
+
// assets to operate on: a free-text search query, an explicit ID list, or
|
|
4
|
+
// a collection ID whose member assets are resolved via the live API.
|
|
5
|
+
//
|
|
6
|
+
// The resolver is intentionally narrow: it returns a flat list of asset IDs
|
|
7
|
+
// (as strings) plus a small per-id metadata map for diff display. Tools layer
|
|
8
|
+
// their domain logic (tag/metadata math) on top.
|
|
9
|
+
//
|
|
10
|
+
// IMPORTANT: the public Typesense host is currently owed by Collage; the
|
|
11
|
+
// `query` path therefore wires through to a search adapter that may throw a
|
|
12
|
+
// typed `UPSTREAM` error (`SEARCH_UNAVAILABLE`) explaining the dependency
|
|
13
|
+
// is not reachable yet. The `ids` and `collection_id` paths are end-to-end
|
|
14
|
+
// against the live Collage REST API and are the recommended Phase 1 paths.
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { createToolError } from '../conventions/errors.js';
|
|
17
|
+
import { IdSchema } from '../types.js';
|
|
18
|
+
// ── Selection Input Schema ───────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Selection input. Exactly one of `query`, `ids`, or `collection_id` must be
|
|
21
|
+
* provided. The schema accepts all three and the resolver enforces the
|
|
22
|
+
* exclusivity at runtime so callers get a structured `VALIDATION` error
|
|
23
|
+
* instead of a Zod parse failure.
|
|
24
|
+
*/
|
|
25
|
+
export const AssetSelectionSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
query: z.string().min(1).optional(),
|
|
28
|
+
ids: z.array(IdSchema).min(1).max(5000).optional(),
|
|
29
|
+
collection_id: IdSchema.optional(),
|
|
30
|
+
})
|
|
31
|
+
.refine((sel) => {
|
|
32
|
+
const present = [
|
|
33
|
+
sel.query !== undefined,
|
|
34
|
+
sel.ids !== undefined,
|
|
35
|
+
sel.collection_id !== undefined,
|
|
36
|
+
].filter(Boolean).length;
|
|
37
|
+
return present === 1;
|
|
38
|
+
}, {
|
|
39
|
+
message: 'Selection must specify exactly one of: query, ids, or collection_id.',
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Default search adapter used when no Typesense host is configured. Always
|
|
43
|
+
* returns a typed `UPSTREAM` error (`SEARCH_UNAVAILABLE`) so callers can
|
|
44
|
+
* surface a clear, actionable failure message instead of crashing.
|
|
45
|
+
*/
|
|
46
|
+
export const unavailableSearchAdapter = {
|
|
47
|
+
searchAssets: async () => ({
|
|
48
|
+
ok: false,
|
|
49
|
+
error: createToolError('UPSTREAM', 'Asset search is not available: the public Typesense host has not been provisioned yet. Use `ids` or `collection_id` selection until search is wired up.'),
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
export function buildCollectionLookup(client) {
|
|
53
|
+
return {
|
|
54
|
+
getCollectionAssetIds: async (collectionId) => {
|
|
55
|
+
const res = await client.listCollections();
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
return { ok: false, error: res.error };
|
|
58
|
+
}
|
|
59
|
+
const match = res.data.find((c) => String(c.id) === collectionId);
|
|
60
|
+
if (match === undefined) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
error: createToolError('NOT_FOUND', `Collection ${collectionId} not found in workspace.`),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const ids = (match.assets_id ?? []).map((id) => String(id));
|
|
67
|
+
return { ok: true, asset_ids: ids };
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a selection envelope into a concrete list of asset IDs.
|
|
73
|
+
*
|
|
74
|
+
* - `ids` — returned verbatim, deduplicated, coerced to string.
|
|
75
|
+
* - `collection_id` — looked up via the live Collage REST API.
|
|
76
|
+
* - `query` — delegated to the search adapter; may return
|
|
77
|
+
* `SEARCH_UNAVAILABLE` until Typesense is provisioned.
|
|
78
|
+
*/
|
|
79
|
+
export async function resolveAssetSelection(selection, deps) {
|
|
80
|
+
if (selection.ids !== undefined) {
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const raw of selection.ids) {
|
|
84
|
+
const id = String(raw);
|
|
85
|
+
if (!seen.has(id)) {
|
|
86
|
+
seen.add(id);
|
|
87
|
+
out.push(id);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, asset_ids: out, snapshots: new Map() };
|
|
91
|
+
}
|
|
92
|
+
if (selection.collection_id !== undefined) {
|
|
93
|
+
const res = await deps.collections.getCollectionAssetIds(String(selection.collection_id));
|
|
94
|
+
if (!res.ok)
|
|
95
|
+
return res;
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
asset_ids: dedupe(res.asset_ids),
|
|
99
|
+
snapshots: new Map(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (selection.query !== undefined) {
|
|
103
|
+
const res = await deps.search.searchAssets(selection.query);
|
|
104
|
+
if (!res.ok)
|
|
105
|
+
return res;
|
|
106
|
+
const snapshots = new Map();
|
|
107
|
+
const ids = [];
|
|
108
|
+
for (const asset of res.assets) {
|
|
109
|
+
if (snapshots.has(asset.id))
|
|
110
|
+
continue;
|
|
111
|
+
snapshots.set(asset.id, asset);
|
|
112
|
+
ids.push(asset.id);
|
|
113
|
+
}
|
|
114
|
+
return { ok: true, asset_ids: ids, snapshots };
|
|
115
|
+
}
|
|
116
|
+
// Defensive — the schema's refine() should prevent us reaching here.
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
error: createToolError('VALIDATION', 'Selection must specify exactly one of: query, ids, or collection_id.'),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function dedupe(input) {
|
|
123
|
+
const seen = new Set();
|
|
124
|
+
const out = [];
|
|
125
|
+
for (const id of input) {
|
|
126
|
+
if (!seen.has(id)) {
|
|
127
|
+
seen.add(id);
|
|
128
|
+
out.push(id);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=asset-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-selection.js","sourceRoot":"","sources":["../../src/tools/asset-selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type McpToolError } from '../../conventions/errors.js';
|
|
3
|
+
import { type AuditReport } from './types.js';
|
|
4
|
+
export declare const AUDIT_FOLDER_STRUCTURE_DESCRIPTION: string;
|
|
5
|
+
export declare const AuditFolderStructureInputSchema: z.ZodObject<{
|
|
6
|
+
/**
|
|
7
|
+
* Folders deeper than this value (root = depth 0) are flagged as a
|
|
8
|
+
* `folder_depth_anomaly` finding. Must be >= 0. Defaults to 5.
|
|
9
|
+
*/
|
|
10
|
+
max_depth: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
/**
|
|
12
|
+
* Collections holding fewer than this many assets are flagged as
|
|
13
|
+
* `low_asset_collections`. Required from caller — exposed as a tunable
|
|
14
|
+
* because what counts as "low" varies widely across libraries. Must be
|
|
15
|
+
* >= 1; defaults to 3.
|
|
16
|
+
*/
|
|
17
|
+
min_assets: z.ZodDefault<z.ZodNumber>;
|
|
18
|
+
/** Per-finding asset / id sample cap. Defaults to 10. */
|
|
19
|
+
sample_limit: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
sample_limit: number;
|
|
22
|
+
max_depth: number;
|
|
23
|
+
min_assets: number;
|
|
24
|
+
}, {
|
|
25
|
+
sample_limit?: number | undefined;
|
|
26
|
+
max_depth?: number | undefined;
|
|
27
|
+
min_assets?: number | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export type AuditFolderStructureInput = z.input<typeof AuditFolderStructureInputSchema>;
|
|
30
|
+
export type AuditFolderStructureInputResolved = z.output<typeof AuditFolderStructureInputSchema>;
|
|
31
|
+
/** Minimal folder / category projection used by the audit. */
|
|
32
|
+
export interface AuditFolder {
|
|
33
|
+
id: string;
|
|
34
|
+
/** Parent folder id, or null for a root. */
|
|
35
|
+
parent_id: string | null;
|
|
36
|
+
/** Human label for diagnostic messaging. */
|
|
37
|
+
name?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Minimal asset projection — only `id` plus parent folder id (nullable so
|
|
41
|
+
* orphans surface naturally).
|
|
42
|
+
*/
|
|
43
|
+
export interface AuditAssetForStructure {
|
|
44
|
+
id: string;
|
|
45
|
+
category_id: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Minimal collection projection. */
|
|
48
|
+
export interface AuditCollection {
|
|
49
|
+
id: string;
|
|
50
|
+
name?: string;
|
|
51
|
+
asset_count: number;
|
|
52
|
+
}
|
|
53
|
+
export interface FolderStructureEnumerator {
|
|
54
|
+
enumerateFolders(): Promise<{
|
|
55
|
+
ok: true;
|
|
56
|
+
folders: AuditFolder[];
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
error: McpToolError;
|
|
60
|
+
}>;
|
|
61
|
+
enumerateAssets(): Promise<{
|
|
62
|
+
ok: true;
|
|
63
|
+
assets: AuditAssetForStructure[];
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
error: McpToolError;
|
|
67
|
+
}>;
|
|
68
|
+
enumerateCollections(): Promise<{
|
|
69
|
+
ok: true;
|
|
70
|
+
collections: AuditCollection[];
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
error: McpToolError;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
export interface AuditFolderStructureDeps {
|
|
77
|
+
enumerator: FolderStructureEnumerator;
|
|
78
|
+
}
|
|
79
|
+
export type AuditFolderStructureOutcome = {
|
|
80
|
+
ok: true;
|
|
81
|
+
report: AuditReport;
|
|
82
|
+
} | {
|
|
83
|
+
ok: false;
|
|
84
|
+
error: McpToolError;
|
|
85
|
+
};
|
|
86
|
+
export declare class AuditFolderStructureTool {
|
|
87
|
+
private readonly deps;
|
|
88
|
+
readonly name = "audit_folder_structure";
|
|
89
|
+
readonly description: string;
|
|
90
|
+
constructor(deps: AuditFolderStructureDeps);
|
|
91
|
+
run(rawInput?: AuditFolderStructureInput): Promise<AuditFolderStructureOutcome>;
|
|
92
|
+
}
|
|
93
|
+
export declare function buildAuditFolderStructureTool(deps: AuditFolderStructureDeps): AuditFolderStructureTool;
|
|
94
|
+
/**
|
|
95
|
+
* Compute the depth of every folder, indexed by id.
|
|
96
|
+
*
|
|
97
|
+
* Roots (parent_id === null) sit at depth 0. Folders whose parent does not
|
|
98
|
+
* appear in the supplied list are still rooted at depth 0 — a missing
|
|
99
|
+
* parent is treated as a graph oddity, not a fatal error, so audit runs
|
|
100
|
+
* never crash on partial workspace snapshots.
|
|
101
|
+
*
|
|
102
|
+
* Cycles (a folder whose parent chain returns to itself) are detected and
|
|
103
|
+
* the offending folder is clamped at depth `maxDepth + 1` so it still
|
|
104
|
+
* surfaces in the anomaly finding without blowing the stack.
|
|
105
|
+
*/
|
|
106
|
+
export declare function computeFolderDepths(folders: ReadonlyArray<AuditFolder>, maxDepth: number): Map<string, number>;
|
|
107
|
+
export declare function buildReport(folders: ReadonlyArray<AuditFolder>, assets: ReadonlyArray<AuditAssetForStructure>, collections: ReadonlyArray<AuditCollection>, input: AuditFolderStructureInputResolved): AuditReport;
|
|
108
|
+
//# sourceMappingURL=audit-folder-structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-folder-structure.d.ts","sourceRoot":"","sources":["../../../src/tools/audit/audit-folder-structure.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEjF,OAAO,EAKL,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAIpB,eAAO,MAAM,kCAAkC,QAK3B,CAAC;AASrB,eAAO,MAAM,+BAA+B;IAC1C;;;OAGG;;IAEH;;;;;OAKG;;IAEH,yDAAyD;;;;;;;;;;EAEzD,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,+BAA+B,CACvC,CAAC;AACF,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CACtD,OAAO,+BAA+B,CACvC,CAAC;AAIF,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,yBAAyB;IACxC,gBAAgB,IAAI,OAAO,CACvB;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,GACpC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;IACF,eAAe,IAAI,OAAO,CACtB;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,sBAAsB,EAAE,CAAA;KAAE,GAC9C;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;IACF,oBAAoB,IAAI,OAAO,CAC3B;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,eAAe,EAAE,CAAA;KAAE,GAC5C;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;CACH;AAID,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,yBAAyB,CAAC;CACvC;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAEvC,qBAAa,wBAAwB;IAIvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,QAAQ,CAAC,IAAI,4BAA4B;IACzC,QAAQ,CAAC,WAAW,SAAsC;gBAE7B,IAAI,EAAE,wBAAwB;IAErD,GAAG,CACP,QAAQ,GAAE,yBAA8B,GACvC,OAAO,CAAC,2BAA2B,CAAC;CA+BxC;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,wBAAwB,GAC7B,wBAAwB,CAE1B;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,EACnC,QAAQ,EAAE,MAAM,GACf,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBrB;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,EACnC,MAAM,EAAE,aAAa,CAAC,sBAAsB,CAAC,EAC7C,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,EAC3C,KAAK,EAAE,iCAAiC,GACvC,WAAW,CA2Fb"}
|