@kolbo/mcp 1.75.0 → 1.75.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.75.0",
3
+ "version": "1.75.2",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -15,6 +15,8 @@ const { widgetPage } = require('../html');
15
15
  * title: 'Your Projects',
16
16
  * items: [{
17
17
  * id, title, subtitle, // main two lines
18
+ * thumbnail, // optional — real image; falls back to the letter monogram on
19
+ * // missing/failed load, same idiom as catalog.js model icons
18
20
  * badge, // small pill, e.g. role/status ("owner", "default", "shared")
19
21
  * meta, // small trailing text, e.g. a date or count
20
22
  * open_url, // optional — renders an "Open" button (openLink)
@@ -71,8 +73,11 @@ function apply(result) {
71
73
 
72
74
  function itemHTML(item, i) {
73
75
  var clickable = !!item.use_hint;
76
+ var avatar = item.thumbnail
77
+ ? '<img class="k-audio-art" src="' + esc(item.thumbnail) + '" alt="" loading="lazy" onerror="this.outerHTML=monogram(\\'' + esc(item.title || '?').replace(/'/g, '') + '\\')">'
78
+ : monogram(item.title || '?');
74
79
  return '<div class="k-audio-row" data-i="' + i + '"' + (clickable ? ' style="cursor:pointer"' : '') + '>' +
75
- monogram(item.title || '?') +
80
+ avatar +
76
81
  '<div class="k-audio-meta"><div class="k-audio-title">' + esc(item.title || '') + '</div>' +
77
82
  (item.subtitle ? '<div class="k-audio-sub">' + esc(item.subtitle) + '</div>' : '') + '</div>' +
78
83
  (item.badge ? '<span class="k-chip" style="flex:none">' + esc(item.badge) + '</span>' : '') +
@@ -54,6 +54,16 @@ function boot(sc) {
54
54
  if (audioItems.length) {
55
55
  h += audioItems.slice(0, 12).map(audioRowHTML).join('');
56
56
  }
57
+ // Only a fixed page ever renders here (24 visual + 12 audio) — with a library
58
+ // in the thousands there was no way to reach the rest short of asking in
59
+ // chat. A visible "Load more" turns that into one click; it sends a message
60
+ // rather than calling the tool directly, since the widget has no host API
61
+ // to invoke a tool itself — same mechanism every other "Use" action here uses.
62
+ var shown = visualItems.length + audioItems.length;
63
+ if (sc.total != null && sc.total > shown) {
64
+ h += '<button class="k-btn" id="load-more" style="width:100%;margin-top:10px">Load more (' +
65
+ shown + ' of ' + sc.total + ' shown)</button>';
66
+ }
57
67
  el('stage').innerHTML = h;
58
68
  el('stage').classList.remove('k-empty');
59
69
  wire();
@@ -78,6 +88,11 @@ function cellHTML(item, i) {
78
88
  '</div>' +
79
89
  '<div class="k-cell-label">' + esc(item.title || '') + '</div>' +
80
90
  (item.subtitle ? '<div class="k-cell-sub">' + esc(item.subtitle) + '</div>' : '') +
91
+ // Clicking the cell itself already sends the use_hint (see wire()), but that
92
+ // wasn't discoverable — nothing on the tile signalled it was clickable. A
93
+ // visible button makes the affordance obvious, same reasoning as the audio
94
+ // rows' explicit "Use" button.
95
+ '<button class="k-btn" data-use="' + idx + '" style="margin:0 8px 8px;width:calc(100% - 16px)">Use</button>' +
81
96
  '</div>';
82
97
  }
83
98
 
@@ -121,6 +136,13 @@ function wire() {
121
136
  holder.querySelector('video').onclick = function (ev) { ev.stopPropagation(); };
122
137
  };
123
138
  });
139
+ var loadMore = el('load-more');
140
+ if (loadMore) {
141
+ loadMore.onclick = function () {
142
+ window.kolbo.sendMessage('Show me the next page of this same media search (already saw ' +
143
+ (state.items.length) + ' of ' + state.total + ' results).');
144
+ };
145
+ }
124
146
  }
125
147
 
126
148
  function useItem(i) {
@@ -32,6 +32,7 @@ function registerProjectTools(server, client) {
32
32
  role: p.role,
33
33
  is_default: !!p.is_default,
34
34
  is_archived: !!p.is_archived,
35
+ thumbnail_url: p.thumbnail_url || null,
35
36
  open_url: buildProjectUrl(p.id, { is_default: !!p.is_default })
36
37
  }));
37
38
  const text = JSON.stringify({
@@ -48,6 +49,7 @@ function registerProjectTools(server, client) {
48
49
  id: p.id,
49
50
  title: p.name,
50
51
  subtitle: p.role + (p.is_default ? ' · default' : '') + (p.is_archived ? ' · archived' : ''),
52
+ thumbnail: p.thumbnail_url,
51
53
  open_url: p.open_url,
52
54
  use_hint: 'Use my "{TITLE}" project (project_id: {ID}) for what I do next.'
53
55
  })),