@cloudinary/asset-management-mcp 0.8.1 → 0.9.0-rc.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/README.md +2 -2
- package/bin/mcp-server.js +4849 -305
- package/bin/mcp-server.js.map +25 -21
- package/esm/landing-page.js +1 -1
- package/esm/lib/config.d.ts +3 -3
- package/esm/lib/config.js +3 -3
- package/esm/lib/config.js.map +1 -1
- package/esm/lib/encodings.d.ts +1 -0
- package/esm/lib/encodings.d.ts.map +1 -1
- package/esm/lib/encodings.js +26 -5
- package/esm/lib/encodings.js.map +1 -1
- package/esm/lib/security.d.ts +1 -1
- package/esm/lib/security.d.ts.map +1 -1
- package/esm/lib/security.js +29 -17
- package/esm/lib/security.js.map +1 -1
- package/esm/mcp-server/mcp-server.js +1 -1
- package/esm/mcp-server/mcp-server.js.map +1 -1
- package/esm/mcp-server/server.extensions.d.ts.map +1 -1
- package/esm/mcp-server/server.extensions.js +84 -0
- package/esm/mcp-server/server.extensions.js.map +1 -1
- package/esm/mcp-server/server.js +1 -1
- package/esm/mcp-server/server.js.map +1 -1
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.js +4 -0
- package/esm/mcp-server/tools/assetsGetResourceByAssetId.js.map +1 -1
- package/esm/mcp-server/tools/assetsListImages.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListImages.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListImages.js +4 -0
- package/esm/mcp-server/tools/assetsListImages.js.map +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListRawFiles.js +4 -0
- package/esm/mcp-server/tools/assetsListRawFiles.js.map +1 -1
- package/esm/mcp-server/tools/assetsListVideos.d.ts +1 -1
- package/esm/mcp-server/tools/assetsListVideos.d.ts.map +1 -1
- package/esm/mcp-server/tools/assetsListVideos.js +4 -0
- package/esm/mcp-server/tools/assetsListVideos.js.map +1 -1
- package/esm/mcp-server/tools/searchSearchAssets.d.ts.map +1 -1
- package/esm/mcp-server/tools/searchSearchAssets.js +4 -0
- package/esm/mcp-server/tools/searchSearchAssets.js.map +1 -1
- package/esm/mcp-server/tools/uploadUpload.d.ts.map +1 -1
- package/esm/mcp-server/tools/uploadUpload.js +4 -0
- package/esm/mcp-server/tools/uploadUpload.js.map +1 -1
- package/esm/mcp-server/tools.d.ts +2 -0
- package/esm/mcp-server/tools.d.ts.map +1 -1
- package/esm/mcp-server/tools.js +2 -0
- package/esm/mcp-server/tools.js.map +1 -1
- package/esm/mcp-server/widgets/asset-details-widget.d.ts +3 -0
- package/esm/mcp-server/widgets/asset-details-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-details-widget.js +299 -0
- package/esm/mcp-server/widgets/asset-details-widget.js.map +1 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.d.ts +4 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.js +1063 -0
- package/esm/mcp-server/widgets/asset-gallery-widget.js.map +1 -0
- package/esm/mcp-server/widgets/asset-upload-widget.d.ts +3 -0
- package/esm/mcp-server/widgets/asset-upload-widget.d.ts.map +1 -0
- package/esm/mcp-server/widgets/asset-upload-widget.js +1093 -0
- package/esm/mcp-server/widgets/asset-upload-widget.js.map +1 -0
- package/esm/mcp-server/widgets/widget-shared.d.ts +9 -0
- package/esm/mcp-server/widgets/widget-shared.d.ts.map +1 -0
- package/esm/mcp-server/widgets/widget-shared.js +2019 -0
- package/esm/mcp-server/widgets/widget-shared.js.map +1 -0
- package/esm/models/fieldsspec.d.ts +1 -1
- package/package.json +1 -1
- package/src/landing-page.ts +1 -1
- package/src/lib/config.ts +3 -3
- package/src/lib/encodings.ts +32 -4
- package/src/lib/security.ts +14 -2
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.extensions.ts +97 -0
- package/src/mcp-server/server.ts +1 -1
- package/src/mcp-server/tools/assetsGetResourceByAssetId.ts +4 -0
- package/src/mcp-server/tools/assetsListImages.ts +4 -0
- package/src/mcp-server/tools/assetsListRawFiles.ts +4 -0
- package/src/mcp-server/tools/assetsListVideos.ts +4 -0
- package/src/mcp-server/tools/searchSearchAssets.ts +4 -0
- package/src/mcp-server/tools/uploadUpload.ts +4 -0
- package/src/mcp-server/tools.ts +4 -0
- package/src/mcp-server/widgets/asset-details-widget.ts +313 -0
- package/src/mcp-server/widgets/asset-gallery-widget.ts +1077 -0
- package/src/mcp-server/widgets/asset-upload-widget.ts +1115 -0
- package/src/mcp-server/widgets/widget-shared.ts +2030 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standalone MCP App widget for displaying a single Cloudinary asset
|
|
3
|
+
* in a rich detail view. Attached to the get-asset-details tool.
|
|
4
|
+
*
|
|
5
|
+
* Shares CLDS tokens, MCPApp client, helpers, and detail renderers
|
|
6
|
+
* with the gallery widget via widget-shared.ts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
SHARED_CSS_TOKENS,
|
|
11
|
+
SHARED_CSS_COMPONENTS,
|
|
12
|
+
SHARED_JS_MCP_CLIENT,
|
|
13
|
+
SHARED_JS_HELPERS,
|
|
14
|
+
SHARED_JS_TOOLTIPS,
|
|
15
|
+
SHARED_JS_MODAL,
|
|
16
|
+
SHARED_JS_DETAIL_RENDERERS,
|
|
17
|
+
SHARED_JS_HOST_CONTEXT,
|
|
18
|
+
} from "./widget-shared.js";
|
|
19
|
+
|
|
20
|
+
export const ASSET_DETAILS_RESOURCE_URI = "ui://cloudinary/asset-details.html";
|
|
21
|
+
|
|
22
|
+
export function getAssetDetailsHtml(): string {
|
|
23
|
+
return ASSET_DETAILS_HTML;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ASSET_DETAILS_CSS = /* css */ `
|
|
27
|
+
.details-header {
|
|
28
|
+
display: flex; align-items: center; gap: 14px;
|
|
29
|
+
padding-bottom: var(--cld-sp-md);
|
|
30
|
+
margin-bottom: var(--cld-sp-md);
|
|
31
|
+
border-bottom: 1px solid var(--cld-border);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.details-header-thumb {
|
|
35
|
+
width: 56px; height: 56px; border-radius: var(--cld-radius);
|
|
36
|
+
object-fit: cover; background: var(--cld-bg3); flex-shrink: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.details-header-icon {
|
|
40
|
+
width: 56px; height: 56px; border-radius: var(--cld-radius);
|
|
41
|
+
background: var(--cld-bg3); flex-shrink: 0;
|
|
42
|
+
display: flex; align-items: center; justify-content: center;
|
|
43
|
+
}
|
|
44
|
+
.details-header-icon svg { width: 28px; height: 28px; }
|
|
45
|
+
|
|
46
|
+
.details-header-info { flex: 1; min-width: 0; }
|
|
47
|
+
|
|
48
|
+
.details-header-name {
|
|
49
|
+
font-size: 16px; font-weight: 600; color: var(--cld-text);
|
|
50
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.details-header-sub {
|
|
54
|
+
font-size: 12px; color: var(--cld-text3); margin-top: 2px;
|
|
55
|
+
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.details-header-sub .pill {
|
|
59
|
+
font-size: 10px; color: var(--cld-text2); background: var(--cld-bg3);
|
|
60
|
+
padding: 2px 7px; border-radius: 4px; border: 1px solid var(--cld-border);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.open-link {
|
|
64
|
+
padding: 6px 14px; border-radius: var(--cld-radius-sm);
|
|
65
|
+
font-size: 12px; font-weight: 500; cursor: pointer;
|
|
66
|
+
border: 1px solid var(--cld-accent); background: transparent;
|
|
67
|
+
color: var(--cld-accent); font-family: inherit;
|
|
68
|
+
transition: background 0.15s;
|
|
69
|
+
white-space: nowrap; flex-shrink: 0;
|
|
70
|
+
}
|
|
71
|
+
.open-link:hover { background: var(--cld-accent-bg); }
|
|
72
|
+
|
|
73
|
+
.hero-container {
|
|
74
|
+
position: relative; margin-bottom: var(--cld-sp-md);
|
|
75
|
+
border-radius: var(--cld-radius); overflow: hidden;
|
|
76
|
+
background: var(--cld-bg3);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.hero-container img {
|
|
80
|
+
width: 100%; max-height: 300px; object-fit: contain; display: block;
|
|
81
|
+
}
|
|
82
|
+
.hero-container video {
|
|
83
|
+
width: 100%; max-height: 300px; display: block;
|
|
84
|
+
}
|
|
85
|
+
.hero-container .hero-audio-wrap {
|
|
86
|
+
border-radius: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.hero-container .file-icon {
|
|
90
|
+
padding: 40px 20px;
|
|
91
|
+
}
|
|
92
|
+
.hero-container .file-icon svg { width: 48px; height: 48px; }
|
|
93
|
+
|
|
94
|
+
.details-content .detail-section { padding: 14px 0; }
|
|
95
|
+
.details-content .detail-section:first-child { padding-top: 0; }
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
const ASSET_DETAILS_JS = /* js */ `
|
|
99
|
+
var LOG_PREFIX = "[details]";
|
|
100
|
+
var MIN_HEIGHT = 120;
|
|
101
|
+
var pendingCall = { name: null, args: null };
|
|
102
|
+
|
|
103
|
+
var app = new MCPApp({ name: "Cloudinary Asset Details", version: "1.0.0" });
|
|
104
|
+
setupHostContext(app);
|
|
105
|
+
|
|
106
|
+
function renderPage(r) {
|
|
107
|
+
var root = document.getElementById("app");
|
|
108
|
+
var url = r.secure_url || r.url || "";
|
|
109
|
+
var name = r.display_name || r.public_id || r.filename || "Asset";
|
|
110
|
+
var fmt = (r.format || "").toUpperCase();
|
|
111
|
+
var rt = r.resource_type || "";
|
|
112
|
+
var dims = (r.width && r.height) ? r.width + "\\u00d7" + r.height : "";
|
|
113
|
+
var size = r.bytes ? fmtBytes(r.bytes) : "";
|
|
114
|
+
var dur = r.duration ? fmtDuration(r.duration) : "";
|
|
115
|
+
|
|
116
|
+
var h = "";
|
|
117
|
+
|
|
118
|
+
// Header
|
|
119
|
+
h += '<div class="details-header">';
|
|
120
|
+
var headerThumb = thumbUrl(url, 56, 56, r);
|
|
121
|
+
if (headerThumb) {
|
|
122
|
+
h += '<img class="details-header-thumb" src="' + esc(headerThumb) + '">';
|
|
123
|
+
} else if (rt === "raw") {
|
|
124
|
+
h += '<div class="details-header-icon">' + fileTypeIcon(r.format) + "</div>";
|
|
125
|
+
}
|
|
126
|
+
h += '<div class="details-header-info">';
|
|
127
|
+
h += '<div class="details-header-name" title="' + esc(name) + '">' + esc(name) + "</div>";
|
|
128
|
+
h += '<div class="details-header-sub">';
|
|
129
|
+
if (fmt) h += '<span class="pill">' + esc(fmt) + "</span>";
|
|
130
|
+
if (rt) h += '<span class="pill">' + esc(rt) + (r.is_audio ? " (audio)" : "") + "</span>";
|
|
131
|
+
if (dims) h += '<span class="pill">' + dims + "</span>";
|
|
132
|
+
if (dur) h += '<span class="pill">' + dur + "</span>";
|
|
133
|
+
if (size) h += '<span class="pill">' + size + "</span>";
|
|
134
|
+
h += "</div></div>";
|
|
135
|
+
h += '<div style="display:flex;gap:6px;flex-shrink:0">';
|
|
136
|
+
h += '<button class="open-link" id="refresh-asset" title="Refresh">\\u21BB</button>';
|
|
137
|
+
if (url) h += '<button class="open-link" id="open-asset">Open</button>';
|
|
138
|
+
h += "</div>";
|
|
139
|
+
h += "</div>";
|
|
140
|
+
|
|
141
|
+
// Hero
|
|
142
|
+
h += '<div class="hero-container">';
|
|
143
|
+
h += renderHeroPreview(r);
|
|
144
|
+
h += "</div>";
|
|
145
|
+
|
|
146
|
+
// Details content
|
|
147
|
+
h += '<div class="details-content">';
|
|
148
|
+
|
|
149
|
+
h += sectionStart("asset_info");
|
|
150
|
+
h += '<summary class="detail-section-title">Asset Info</summary>';
|
|
151
|
+
h += renderAssetGrid(r);
|
|
152
|
+
h += "</details>";
|
|
153
|
+
|
|
154
|
+
h += renderAudioInfo(r);
|
|
155
|
+
h += renderVideoInfo(r);
|
|
156
|
+
h += renderTags(r.tags);
|
|
157
|
+
h += renderContext(r.context);
|
|
158
|
+
h += renderImageMetadata(r.image_metadata || r.media_metadata);
|
|
159
|
+
h += renderColors(r.colors, r.predominant);
|
|
160
|
+
h += renderModerationSection(r.moderation, r.moderation_kind, r.moderation_status);
|
|
161
|
+
h += renderAccessControl(r.access_control);
|
|
162
|
+
h += renderCoordinates(r.faces, r.coordinates);
|
|
163
|
+
h += renderLastUpdated(r.last_updated);
|
|
164
|
+
h += renderMetadata(r.metadata);
|
|
165
|
+
h += renderInfo(r.info);
|
|
166
|
+
h += renderDerived(r.derived, r.derived_next_cursor, r.asset_id);
|
|
167
|
+
h += renderDerivatives(r.derivatives);
|
|
168
|
+
h += renderRelatedAssets(r.related_assets);
|
|
169
|
+
h += renderVersions(r.versions);
|
|
170
|
+
h += renderEager(r.eager);
|
|
171
|
+
h += renderQualityAnalysis(r.quality_analysis, r.quality_score);
|
|
172
|
+
h += renderAccessibilityAnalysis(r.accessibility_analysis);
|
|
173
|
+
h += renderExtraFields(r);
|
|
174
|
+
h += renderRawResponse(r);
|
|
175
|
+
|
|
176
|
+
h += "</div>";
|
|
177
|
+
|
|
178
|
+
root.innerHTML = h;
|
|
179
|
+
|
|
180
|
+
// Event delegation
|
|
181
|
+
root.addEventListener("click", function handler(e) {
|
|
182
|
+
var el = e.target;
|
|
183
|
+
while (el && el !== root) {
|
|
184
|
+
if (el.id === "open-asset") {
|
|
185
|
+
app._rpc("ui/open-link", { url: url });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (el.id === "refresh-asset") {
|
|
189
|
+
fetchDirect();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (el.id === "load-more-derived-btn") {
|
|
193
|
+
loadMoreDerived(el);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (el.classList && el.classList.contains("link-val") && el.dataset.url) {
|
|
197
|
+
app._rpc("ui/open-link", { url: el.dataset.url });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (el.classList && el.classList.contains("derived-open") && el.dataset.url) {
|
|
201
|
+
app._rpc("ui/open-link", { url: el.dataset.url });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
el = el.parentElement;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
function showFetchPrompt() {
|
|
211
|
+
var name = pendingCall.name || "get-asset-details";
|
|
212
|
+
var root = document.getElementById("app");
|
|
213
|
+
var h = '<div class="prompt">';
|
|
214
|
+
h += '<div class="prompt-icon">\\u{1F4E6}</div>';
|
|
215
|
+
h += '<div class="prompt-title">Could Not Display Results</div>';
|
|
216
|
+
h += '<div class="prompt-desc">';
|
|
217
|
+
h += "The response from <strong>" + esc(name) + "</strong> could not be rendered. ";
|
|
218
|
+
h += "You can try fetching the data directly from the server.";
|
|
219
|
+
h += "</div>";
|
|
220
|
+
h += '<div class="prompt-actions">';
|
|
221
|
+
h += '<button class="prompt-btn prompt-btn-primary" id="fetch-direct-btn">Fetch Directly</button>';
|
|
222
|
+
h += "</div></div>";
|
|
223
|
+
root.innerHTML = h;
|
|
224
|
+
document.getElementById("fetch-direct-btn").addEventListener("click", function() { fetchDirect(); });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function fetchDirect() {
|
|
228
|
+
var name = pendingCall.name || "get-asset-details";
|
|
229
|
+
var args = pendingCall.args || {};
|
|
230
|
+
console.log(LOG_PREFIX, "tools/call ->", name, JSON.stringify(args));
|
|
231
|
+
|
|
232
|
+
document.getElementById("app").innerHTML = '<div class="status">Fetching asset details\\u2026</div>';
|
|
233
|
+
try {
|
|
234
|
+
var res = await app.callServerTool({ name: name, arguments: args });
|
|
235
|
+
var data = ingestResult(res);
|
|
236
|
+
if (data && !data._error && !data._truncated && !data._parseError) {
|
|
237
|
+
renderPage(data);
|
|
238
|
+
} else if (data && data._error) {
|
|
239
|
+
showPersistentError("Server Error", data._message || JSON.stringify(data));
|
|
240
|
+
} else if (data && data._parseError) {
|
|
241
|
+
showPersistentError("Parse Error", data._message || "Could not parse response.");
|
|
242
|
+
} else if (data && data._truncated) {
|
|
243
|
+
showPersistentError("Truncated", "Response was truncated: " + (data._message || "").substring(0, 200));
|
|
244
|
+
} else {
|
|
245
|
+
showPersistentError("No Data", "Server returned no asset details.");
|
|
246
|
+
}
|
|
247
|
+
} catch (e) {
|
|
248
|
+
showPersistentError("Fetch Failed", e && e.message ? e.message : String(e));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
app.ontoolinput = function(params) {
|
|
253
|
+
pendingCall.name = "get-asset-details";
|
|
254
|
+
if (params.arguments) pendingCall.args = params.arguments;
|
|
255
|
+
showReadyPrompt(pendingCall, fetchDirect);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
app.ontoolcancelled = function(params) {
|
|
259
|
+
console.log(LOG_PREFIX, "tool cancelled:", params && params.reason);
|
|
260
|
+
showCancelledPrompt(pendingCall, fetchDirect);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
app.ontoolresult = function(result) {
|
|
264
|
+
var data = ingestResult(result);
|
|
265
|
+
if (data && !data._error && !data._truncated && !data._parseError) {
|
|
266
|
+
console.log(LOG_PREFIX, "host result received for", data.asset_id || data.public_id);
|
|
267
|
+
renderPage(data);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (data && data._error) {
|
|
272
|
+
showPersistentError("Error", data._message || JSON.stringify(data));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.warn(LOG_PREFIX, "host result unusable:", data && data._message ? data._message.substring(0, 200) : "null");
|
|
277
|
+
showFetchPrompt();
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
app.connect().then(function() {
|
|
281
|
+
console.log(LOG_PREFIX, "ready");
|
|
282
|
+
setupResize(app, MIN_HEIGHT);
|
|
283
|
+
}).catch(function(err) {
|
|
284
|
+
showError("Connection Failed", err && err.message ? err.message : String(err));
|
|
285
|
+
});
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
const ASSET_DETAILS_HTML = /* html */ `<!DOCTYPE html>
|
|
289
|
+
<html lang="en">
|
|
290
|
+
<head>
|
|
291
|
+
<meta charset="UTF-8">
|
|
292
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
293
|
+
<title>Cloudinary Asset Details</title>
|
|
294
|
+
<style>
|
|
295
|
+
${SHARED_CSS_TOKENS}
|
|
296
|
+
${SHARED_CSS_COMPONENTS}
|
|
297
|
+
${ASSET_DETAILS_CSS}
|
|
298
|
+
</style>
|
|
299
|
+
</head>
|
|
300
|
+
<body>
|
|
301
|
+
<div id="app"><div class="status">Loading asset details…</div></div>
|
|
302
|
+
|
|
303
|
+
<script>
|
|
304
|
+
${SHARED_JS_MCP_CLIENT}
|
|
305
|
+
${SHARED_JS_HELPERS}
|
|
306
|
+
${SHARED_JS_TOOLTIPS}
|
|
307
|
+
${SHARED_JS_MODAL}
|
|
308
|
+
${SHARED_JS_DETAIL_RENDERERS}
|
|
309
|
+
${SHARED_JS_HOST_CONTEXT}
|
|
310
|
+
${ASSET_DETAILS_JS}
|
|
311
|
+
</script>
|
|
312
|
+
</body>
|
|
313
|
+
</html>`;
|