@cloudinary/asset-management-mcp 0.9.2 → 0.9.3
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 +86 -17
- package/bin/mcp-server.js.map +8 -8
- package/esm/landing-page.js +1 -1
- package/esm/lib/config.d.ts +2 -2
- package/esm/lib/config.js +2 -2
- package/esm/mcp-server/apps/app-shared.d.ts +3 -3
- package/esm/mcp-server/apps/app-shared.d.ts.map +1 -1
- package/esm/mcp-server/apps/app-shared.js +56 -3
- package/esm/mcp-server/apps/app-shared.js.map +1 -1
- package/esm/mcp-server/apps/asset-gallery-app.js +24 -8
- package/esm/mcp-server/apps/asset-gallery-app.js.map +1 -1
- package/esm/mcp-server/mcp-server.js +1 -1
- package/esm/mcp-server/server.js +1 -1
- package/package.json +1 -1
- package/src/landing-page.ts +1 -1
- package/src/lib/config.ts +2 -2
- package/src/mcp-server/apps/app-shared.ts +56 -3
- package/src/mcp-server/apps/asset-gallery-app.ts +24 -8
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.ts +1 -1
package/package.json
CHANGED
package/src/landing-page.ts
CHANGED
|
@@ -939,7 +939,7 @@ http_headers = { "api-key" = "YOUR_API_KEY", "api-secret" = "YOUR_API_SECRET", "
|
|
|
939
939
|
<h1>Instructions</h1>
|
|
940
940
|
<p>One-click installation for Claude Desktop users</p>
|
|
941
941
|
<div class="instruction-item">
|
|
942
|
-
<a href="https://github.com/cloudinary/asset-management-mcp/releases/download/v0.9.
|
|
942
|
+
<a href="https://github.com/cloudinary/asset-management-mcp/releases/download/v0.9.3/mcp-server.mcpb" download="mcp-server.mcpb" class="action-button header-action" style="display: inline-flex; margin-bottom: 16px;">
|
|
943
943
|
📥 Download MCP Bundle
|
|
944
944
|
</a>
|
|
945
945
|
</div>
|
package/src/lib/config.ts
CHANGED
|
@@ -113,8 +113,8 @@ export function serverURLFromOptions(options: SDKOptions): URL | null {
|
|
|
113
113
|
export const SDK_METADATA = {
|
|
114
114
|
language: "typescript",
|
|
115
115
|
openapiDocVersion: "0.5.1",
|
|
116
|
-
sdkVersion: "0.9.
|
|
116
|
+
sdkVersion: "0.9.3",
|
|
117
117
|
genVersion: "2.885.1",
|
|
118
118
|
userAgent:
|
|
119
|
-
"speakeasy-sdk/mcp-typescript 0.9.
|
|
119
|
+
"speakeasy-sdk/mcp-typescript 0.9.3 2.885.1 0.5.1 @cloudinary/asset-management-mcp",
|
|
120
120
|
} as const;
|
|
@@ -116,7 +116,7 @@ export const SHARED_CSS_TOKENS = /* css */ `
|
|
|
116
116
|
[data-theme="dark"] .status-warn, .dark .status-warn { background: #854d0e; color: #fef08a; }
|
|
117
117
|
[data-theme="dark"] .status-err, .dark .status-err { background: #991b1b; color: #fecaca; }
|
|
118
118
|
|
|
119
|
-
html {
|
|
119
|
+
html { scrollbar-gutter: auto; }
|
|
120
120
|
body {
|
|
121
121
|
font-family: var(--cld-font);
|
|
122
122
|
background: var(--cld-bg);
|
|
@@ -125,6 +125,7 @@ body {
|
|
|
125
125
|
line-height: 1.5;
|
|
126
126
|
font-size: var(--cld-font-xs);
|
|
127
127
|
position: relative;
|
|
128
|
+
overflow-x: hidden;
|
|
128
129
|
}
|
|
129
130
|
.theme-btn {
|
|
130
131
|
width: 22px; height: 22px; border-radius: 50%;
|
|
@@ -163,9 +164,11 @@ export const SHARED_CSS_COMPONENTS = /* css */ `
|
|
|
163
164
|
.link { cursor: pointer; }
|
|
164
165
|
.link:hover { color: var(--cld-accent); text-decoration: underline; }
|
|
165
166
|
|
|
166
|
-
/* Modal
|
|
167
|
+
/* Modal — positioned absolutely so it can be placed in the part of the
|
|
168
|
+
* iframe that is currently visible in the host's viewport (since position:fixed
|
|
169
|
+
* inside a tall iframe anchors to iframe-center, not the user's visible area). */
|
|
167
170
|
.modal-overlay {
|
|
168
|
-
position:
|
|
171
|
+
position: absolute; left: 0; right: 0;
|
|
169
172
|
background: rgba(0,0,0,0.45);
|
|
170
173
|
display: flex; align-items: center; justify-content: center;
|
|
171
174
|
z-index: 1000; backdrop-filter: blur(3px); padding: 24px;
|
|
@@ -1013,6 +1016,55 @@ function closeModal() {
|
|
|
1013
1016
|
if (ov) ov.remove();
|
|
1014
1017
|
}
|
|
1015
1018
|
|
|
1019
|
+
/* Position the modal overlay so it's centered on the part of the iframe
|
|
1020
|
+
* the user is currently looking at. Inside an iframe that the host has
|
|
1021
|
+
* sized to scrollHeight, position: fixed anchors to iframe-center, not
|
|
1022
|
+
* the user's visible band — so we compute the visible band ourselves
|
|
1023
|
+
* via IntersectionObserver and place the overlay absolutely there. */
|
|
1024
|
+
function positionModalInVisibleArea(overlay) {
|
|
1025
|
+
if (!overlay) return;
|
|
1026
|
+
var docH = Math.max(document.documentElement.scrollHeight, window.innerHeight);
|
|
1027
|
+
|
|
1028
|
+
// Try IntersectionObserver first (works cross-origin too)
|
|
1029
|
+
if (typeof IntersectionObserver !== "undefined") {
|
|
1030
|
+
var probe = document.createElement("div");
|
|
1031
|
+
probe.style.cssText = "position:absolute;left:0;top:0;width:1px;height:" + docH + "px;pointer-events:none;visibility:hidden;";
|
|
1032
|
+
document.body.appendChild(probe);
|
|
1033
|
+
var io = new IntersectionObserver(function(entries) {
|
|
1034
|
+
try {
|
|
1035
|
+
var entry = entries[0];
|
|
1036
|
+
var rect = entry.intersectionRect;
|
|
1037
|
+
var rootRect = entry.rootBounds || entry.boundingClientRect;
|
|
1038
|
+
// intersectionRect.top is in viewport coords; visible band of the
|
|
1039
|
+
// document is from (probe.top + rect.top) to (probe.top + rect.bottom).
|
|
1040
|
+
var probeTop = entry.boundingClientRect.top; // viewport-relative
|
|
1041
|
+
var visibleTop = rect.top - probeTop; // document-relative
|
|
1042
|
+
var visibleHeight = rect.height;
|
|
1043
|
+
if (visibleHeight > 0) {
|
|
1044
|
+
overlay.style.top = visibleTop + "px";
|
|
1045
|
+
overlay.style.height = visibleHeight + "px";
|
|
1046
|
+
} else {
|
|
1047
|
+
// Fallback: probe not intersecting (shouldn't happen with full-height probe)
|
|
1048
|
+
overlay.style.top = (window.scrollY || 0) + "px";
|
|
1049
|
+
overlay.style.height = window.innerHeight + "px";
|
|
1050
|
+
}
|
|
1051
|
+
} finally {
|
|
1052
|
+
io.disconnect();
|
|
1053
|
+
if (probe.parentNode) probe.parentNode.removeChild(probe);
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
io.observe(probe);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Fallback: same-origin only — read iframe's bounding rect
|
|
1061
|
+
var rect = document.documentElement.getBoundingClientRect();
|
|
1062
|
+
var visibleTop = rect.top < 0 ? -rect.top : 0;
|
|
1063
|
+
var visibleHeight = Math.min(window.innerHeight, rect.bottom) - Math.max(0, rect.top);
|
|
1064
|
+
overlay.style.top = visibleTop + "px";
|
|
1065
|
+
overlay.style.height = Math.max(visibleHeight, 200) + "px";
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1016
1068
|
function openModal(headerHtml, bodyHtml) {
|
|
1017
1069
|
closeModal();
|
|
1018
1070
|
var h = '<div class="modal-overlay"><div class="modal">';
|
|
@@ -1022,6 +1074,7 @@ function openModal(headerHtml, bodyHtml) {
|
|
|
1022
1074
|
document.body.insertAdjacentHTML("beforeend", h);
|
|
1023
1075
|
|
|
1024
1076
|
var overlay = document.querySelector(".modal-overlay");
|
|
1077
|
+
positionModalInVisibleArea(overlay);
|
|
1025
1078
|
overlay.addEventListener("click", function(e) {
|
|
1026
1079
|
if (e.target === overlay || e.target.classList.contains("modal-close")) {
|
|
1027
1080
|
closeModal();
|
|
@@ -168,17 +168,27 @@ const GALLERY_CSS = /* css */ `
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/* ── Multi-select bar ── */
|
|
171
|
+
/* Wrapper takes layout space ONLY when a selection is active. When idle
|
|
172
|
+
* it's display:none so it contributes 0 to scrollHeight (no phantom
|
|
173
|
+
* scrollbar gutter / no extra iframe height). */
|
|
174
|
+
.select-bar-wrap {
|
|
175
|
+
display: none;
|
|
176
|
+
position: sticky; bottom: 0; left: 0; right: 0;
|
|
177
|
+
justify-content: center; pointer-events: none;
|
|
178
|
+
z-index: 100; height: 64px;
|
|
179
|
+
}
|
|
180
|
+
.select-bar-wrap.visible { display: flex; }
|
|
171
181
|
.select-bar {
|
|
172
|
-
position:
|
|
173
|
-
transform:
|
|
182
|
+
position: absolute; bottom: 8px;
|
|
183
|
+
transform: translateY(80px);
|
|
174
184
|
background: #1a1d24; color: white; border-radius: 40px;
|
|
175
185
|
padding: 0 6px 0 16px; height: 48px;
|
|
176
186
|
display: flex; align-items: center; gap: 4px;
|
|
177
187
|
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
178
188
|
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s;
|
|
179
|
-
opacity: 0; pointer-events: none;
|
|
189
|
+
opacity: 0; pointer-events: none; white-space: nowrap;
|
|
180
190
|
}
|
|
181
|
-
.select-bar.visible { transform:
|
|
191
|
+
.select-bar-wrap.visible .select-bar { transform: translateY(0); opacity: 1; pointer-events: all; }
|
|
182
192
|
.select-count { font-size: 13px; font-weight: 600; margin-right: 8px; }
|
|
183
193
|
.bar-btn {
|
|
184
194
|
height: 36px; padding: 0 14px; border: none; border-radius: 30px;
|
|
@@ -243,12 +253,12 @@ function showToast(msg) {
|
|
|
243
253
|
}
|
|
244
254
|
|
|
245
255
|
function updateSelectBar() {
|
|
246
|
-
var
|
|
256
|
+
var wrap = document.getElementById("select-bar-wrap");
|
|
247
257
|
var countEl = document.getElementById("select-count");
|
|
248
|
-
if (!
|
|
258
|
+
if (!wrap || !countEl) return;
|
|
249
259
|
var n = selected.size;
|
|
250
260
|
countEl.textContent = n + " selected";
|
|
251
|
-
|
|
261
|
+
wrap.classList.toggle("visible", n > 0);
|
|
252
262
|
var btn = document.getElementById("select-all-btn");
|
|
253
263
|
if (btn) {
|
|
254
264
|
var visible = getVisibleIndices();
|
|
@@ -528,7 +538,8 @@ function render() {
|
|
|
528
538
|
h += "</div>";
|
|
529
539
|
}
|
|
530
540
|
|
|
531
|
-
// Multi-select bar
|
|
541
|
+
// Multi-select bar (sticky, in-flow wrapper so iframe sizing stays truthful)
|
|
542
|
+
h += '<div class="select-bar-wrap" id="select-bar-wrap">';
|
|
532
543
|
h += '<div class="select-bar" id="select-bar">';
|
|
533
544
|
h += '<span class="select-count" id="select-count">0 selected</span>';
|
|
534
545
|
h += '<div class="bar-divider"></div>';
|
|
@@ -538,6 +549,7 @@ function render() {
|
|
|
538
549
|
h += '<div class="bar-divider"></div>';
|
|
539
550
|
h += '<button class="bar-btn bar-ghost" id="bar-clear">' + IC.x + '</button>';
|
|
540
551
|
h += '</div>';
|
|
552
|
+
h += '</div>';
|
|
541
553
|
|
|
542
554
|
// Toast
|
|
543
555
|
h += '<div class="gallery-toast" id="gallery-toast"></div>';
|
|
@@ -767,6 +779,9 @@ async function fetchDirect() {
|
|
|
767
779
|
console.log(LOG_PREFIX, "fetchDirect ->", name);
|
|
768
780
|
|
|
769
781
|
document.getElementById("app").innerHTML = '<div class="status">Fetching assets\\u2026</div>';
|
|
782
|
+
requestAnimationFrame(function() {
|
|
783
|
+
app.reportSize(Math.max(document.documentElement.scrollHeight, MIN_HEIGHT));
|
|
784
|
+
});
|
|
770
785
|
try {
|
|
771
786
|
var res = await app.callServerTool({ name: name, arguments: args });
|
|
772
787
|
var data = ingestResult(res);
|
|
@@ -818,6 +833,7 @@ async function loadMore() {
|
|
|
818
833
|
function refreshGallery() {
|
|
819
834
|
allResources = [];
|
|
820
835
|
lastCursor = null;
|
|
836
|
+
selected.clear();
|
|
821
837
|
fetchDirect();
|
|
822
838
|
}
|
|
823
839
|
|