@orchyn/mcp 1.16.1 → 1.17.1

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.
@@ -26,7 +26,7 @@ const UI_RESOURCE_URI = "ui://orchyn/view";
26
26
  */
27
27
  function uiResource(tool) {
28
28
  const slug = tool.replace(/[^a-z0-9_]/gi, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
29
- return `ui://orchyn/${slug || "view"}`;
29
+ return `ui://orchyn/${slug || "view"}?tool=${encodeURIComponent(tool)}`;
30
30
  }
31
31
  /**
32
32
  * claude.ai requires `ui.domain` on the resource == sha256("<MCP endpoint
@@ -2,13 +2,13 @@
2
2
  * MCP Apps HTML UI template for Orchyn tools.
3
3
  *
4
4
  * Features:
5
- * - Skeleton loading shimmer while tool executes
5
+ * - Content renders when a tool result arrives (no loading screen)
6
+ * - Idle/resource-preview state shows a branded ready view
6
7
  * - Smooth fade-in transitions when data arrives
7
8
  * - Expandable analysis sections (click to expand/collapse)
8
9
  * - Click-to-copy on hashtags, hooks, quotes
9
10
  * - Hover micro-interactions on cards
10
11
  * - Dark/light theme auto-detection
11
- * - Progress indicator for multi-step tools
12
12
  * - Responsive layout
13
13
  */
14
14
  export const ORCHYN_UI_TEMPLATE = `<!DOCTYPE html>
@@ -35,29 +35,6 @@ export const ORCHYN_UI_TEMPLATE = `<!DOCTYPE html>
35
35
  *{margin:0;padding:0;box-sizing:border-box;}
36
36
  body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--bg);color:var(--fg);padding:16px;line-height:1.5;}
37
37
 
38
- /* ─── Loading / Skeleton ─── */
39
- .loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;gap:12px;}
40
- .orchyn-logo{animation:logoSpin 3s ease-in-out infinite;}
41
- @keyframes logoSpin{0%,100%{transform:rotate(0deg) scale(1)}50%{transform:rotate(180deg) scale(1.1)}}
42
- .loading-brand{font-size:18px;font-weight:700;letter-spacing:-0.02em;color:var(--fg);}
43
- .loading-spinner{width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite;}
44
- @keyframes spin{to{transform:rotate(360deg)}}
45
- .loading-text{font-size:13px;color:var(--muted);animation:pulse 1.5s ease-in-out infinite;}
46
- @keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
47
-
48
- .skeleton{background:var(--tag);border-radius:var(--radius-sm);overflow:hidden;position:relative;}
49
- .skeleton::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.08),transparent);animation:shimmer 1.5s infinite;}
50
- @keyframes shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
51
- .skeleton-thumb{width:100%;height:180px;border-radius:var(--radius) var(--radius) 0 0;}
52
- .skeleton-line{height:14px;margin:8px 14px;border-radius:4px;}
53
- .skeleton-line.short{width:60%;}
54
- .skeleton-line.medium{width:80%;}
55
- .skeleton-badge{width:80px;height:20px;border-radius:999px;margin:12px 14px 0;}
56
- .skeleton-gallery{display:flex;flex-wrap:wrap;gap:10px;}
57
- .skeleton-card{width:300px;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--card);}
58
- .skeleton-creator{width:220px;border:1px solid var(--border);border-radius:var(--radius);padding:14px;background:var(--card);}
59
- .skeleton-avatar{width:48px;height:48px;border-radius:50%;margin-bottom:8px;}
60
-
61
38
  /* ─── Content entrance ─── */
62
39
  .fade-in{animation:fadeIn .4s ease-out;}
63
40
  @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
@@ -117,10 +94,6 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
117
94
  .section-content.open{max-height:600px;padding:10px 12px;}
118
95
  .section-text{font-size:13px;color:var(--fg);line-height:1.6;}
119
96
 
120
- /* ─── Progress bar ─── */
121
- .progress-bar{height:6px;background:var(--tag);border-radius:999px;overflow:hidden;margin:6px 0;}
122
- .progress-fill{height:100%;border-radius:999px;transition:width .6s cubic-bezier(.4,0,.2,1);background:linear-gradient(90deg,var(--blue),var(--green));}
123
-
124
97
  /* ─── Hook strength ─── */
125
98
  .hook-bar-track{display:flex;align-items:center;gap:10px;margin-top:6px;}
126
99
  .hook-bar{flex:1;height:10px;background:var(--tag);border-radius:999px;overflow:hidden;}
@@ -166,33 +139,69 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
166
139
  </style>
167
140
  </head>
168
141
  <body>
169
- <div id="app">
170
- <div class="loading-container">
171
- <div class="orchyn-logo">
172
- <svg width="40" height="40" viewBox="0 0 48 48" fill="none">
173
- <g fill="var(--accent)" transform="translate(24 24)">
174
- <circle r="4.1"/>
175
- <g id="r"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g>
176
- <use href="#r" transform="rotate(45)"/>
177
- <use href="#r" transform="rotate(90)"/>
178
- <use href="#r" transform="rotate(135)"/>
179
- <use href="#r" transform="rotate(180)"/>
180
- <use href="#r" transform="rotate(225)"/>
181
- <use href="#r" transform="rotate(270)"/>
182
- <use href="#r" transform="rotate(315)"/>
183
- </g>
184
- </svg>
185
- </div>
186
- <div class="loading-brand">Orchyn</div>
187
- <div class="loading-text">Analyzing…</div>
188
- </div>
189
- </div>
142
+ <div id="app"></div>
190
143
  <script>
191
144
  (function(){
192
145
  var nextId=1;var pending=new Map();
193
- var loadingTimer=null;
194
146
  var currentTool="";
195
147
 
148
+ // ─── Tool-specific idle view ───
149
+ // Read ?tool=<name> from the resource URI to show a tailored idle screen.
150
+ var TOOL_NAMES={
151
+ analyze_post:"Analyze Post",
152
+ discover_social_posts:"Discover Social Posts",
153
+ get_user_posts:"Get User Posts",
154
+ get_social_media:"Get Social Media",
155
+ analyze_creator_profile:"Analyze Creator Profile",
156
+ get_post_comments:"Get Post Comments",
157
+ search_creators:"Search Creators",
158
+ get_similar_creators:"Similar Creators",
159
+ discover_sounds:"Discover Sounds",
160
+ understand_social_post:"Understand Social Post",
161
+ check_orchyn_credits:"Check Credits",
162
+ buy_orchyn_credits:"Buy Credits",
163
+ compose_sequence:"Compose Sequence",
164
+ overlay_bake:"Overlay Bake",
165
+ spawn_variants:"Spawn Variants",
166
+ bake_job_status:"Bake Job Status",
167
+ enqueue_publish_job:"Enqueue Publish",
168
+ schedule_post:"Schedule Post"
169
+ };
170
+ var TOOL_DESCS={
171
+ analyze_post:"Deep analysis of any social media post — hook strength, viral triggers, why it works.",
172
+ discover_social_posts:"Trending posts from any platform for a given niche.",
173
+ get_user_posts:"Recent posts from a specific creator handle.",
174
+ get_social_media:"Get post data from any social media URL.",
175
+ analyze_creator_profile:"Full creator profile analysis — engagement, audience, content style.",
176
+ get_post_comments:"Top comments on a post — sentiment, themes, viral threads.",
177
+ search_creators:"Find creators in a niche by engagement, followers, and content.",
178
+ get_similar_creators:"Find creators similar to a given handle.",
179
+ discover_sounds:"Trending sounds and music on TikTok/Instagram.",
180
+ understand_social_post:"Multimodal understanding of video/image content.",
181
+ check_orchyn_credits:"View your Orchyn credit balance and free tool usage.",
182
+ buy_orchyn_credits:"Purchase additional Orchyn credits.",
183
+ compose_sequence:"AI-powered content composition for social posts.",
184
+ overlay_bake:"Bake text/image overlays onto video or image.",
185
+ spawn_variants:"Generate multiple content variants from a single seed.",
186
+ bake_job_status:"Check the status of a rendering/baking job.",
187
+ enqueue_publish_job:"Queue a post for publishing to a connected platform.",
188
+ schedule_post:"Schedule a post for future publishing."
189
+ };
190
+ function renderIdle(){
191
+ var params=new URLSearchParams(window.location.search);
192
+ var tool=params.get("tool")||"";
193
+ var label=TOOL_NAMES[tool]||"Interactive View";
194
+ var desc=TOOL_DESCS[tool]||"Results will appear here as soon as a tool returns.";
195
+ var app=document.getElementById("app");
196
+ if(!app)return;
197
+ app.innerHTML='<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:220px;text-align:center;padding:40px 24px;background:var(--panel, #0d1117);color:var(--fg, #e6edf3);font-family:system-ui,-apple-system,sans-serif">'
198
+ +'<svg width="44" height="44" viewBox="0 0 48 48" fill="none"><g fill="var(--accent, #3fb950)" transform="translate(24 24)"><circle r="4.1"/><g id="ri"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g><use href="#ri" transform="rotate(45)"/><use href="#ri" transform="rotate(90)"/><use href="#ri" transform="rotate(135)"/><use href="#ri" transform="rotate(180)"/><use href="#ri" transform="rotate(225)"/><use href="#ri" transform="rotate(270)"/><use href="#ri" transform="rotate(315)"/></g></svg>'
199
+ +'<div style="margin-top:14px;font-size:17px;font-weight:700">'+label+'</div>'
200
+ +'<div style="margin-top:6px;font-size:13px;color:#8b949e;max-width:340px;line-height:1.5">'+desc+'</div>'
201
+ +'</div>';
202
+ }
203
+ renderIdle();
204
+
196
205
  function send(method,params){
197
206
  var id=nextId++;
198
207
  return new Promise(function(resolve,reject){
@@ -207,69 +216,19 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
207
216
  if(d.id&&pending.has(d.id)){
208
217
  var p=pending.get(d.id);pending.delete(d.id);
209
218
  if(d.error)p.reject(new Error(d.error.message||JSON.stringify(d.error)));
210
- else p.resolve(d.result);return;
219
+ else p.resolve(d.result);
220
+ return;
211
221
  }
212
222
  if(d.method==="ui/notifications/tool-result"){
213
223
  toolResultReceived=true;
214
- clearTimeout(loadingTimer);
215
- clearTimeout(fallbackTimer);
216
224
  render(d.params);
217
225
  setTimeout(reportSize,50);
218
226
  }
219
227
  if(d.method==="ui/notifications/tool-input-partial"){
220
228
  currentTool=d.params&&d.params.name?d.params.name:"";
221
- updateProgress(d.params);
222
229
  }
223
230
  });
224
231
 
225
- // Show loading state after 300ms — branded spinner until tool name arrives,
226
- // then switch to the correct skeleton type.
227
- loadingTimer=setTimeout(function(){
228
- var app=document.getElementById("app");
229
- if(!app)return;
230
- if(currentTool){
231
- // Tool name already known → show correct skeleton
232
- app.innerHTML=renderSkeleton(currentTool);
233
- }else{
234
- // Tool name not yet known → show branded spinner (neutral)
235
- app.innerHTML='<div class="loading-container fade-in">'
236
- +'<div class="orchyn-logo"><svg width="40" height="40" viewBox="0 0 48 48" fill="none"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="rl"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g><use href="#rl" transform="rotate(45)"/><use href="#rl" transform="rotate(90)"/><use href="#rl" transform="rotate(135)"/><use href="#rl" transform="rotate(180)"/><use href="#rl" transform="rotate(225)"/><use href="#rl" transform="rotate(270)"/><use href="#rl" transform="rotate(315)"/></g></svg></div>'
237
- +'<div class="loading-brand">Orchyn</div>'
238
- +'<div class="loading-text">Loading…</div>'
239
- +'</div>';
240
- }
241
- },300);
242
-
243
- // Fallback: if no tool-result arrives within 3s, check if data is embedded
244
- // in the page (some hosts pass structuredContent via URL hash or postMessage)
245
- var fallbackTimer=setTimeout(function(){
246
- if(toolResultReceived)return;
247
- // Try to extract data from URL hash (host may pass structuredContent here)
248
- try{
249
- var hash=window.location.hash.slice(1);
250
- if(hash){
251
- var decoded=JSON.parse(decodeURIComponent(hash));
252
- if(decoded&&typeof decoded==="object"){render(decoded);return;}
253
- }
254
- }catch(e){}
255
- // Try to extract from URL search params
256
- try{
257
- var params=new URLSearchParams(window.location.search);
258
- var dataParam=params.get("data");
259
- if(dataParam){
260
- var decoded=JSON.parse(decodeURIComponent(dataParam));
261
- if(decoded&&typeof decoded==="object"){render(decoded);return;}
262
- }
263
- }catch(e){}
264
- // Last resort: show a branded card indicating results are available
265
- var app=document.getElementById("app");
266
- if(app)app.innerHTML='<div class="card card-wide fade-in"><div class="card-body" style="text-align:center;padding:24px">'
267
- +'<svg width="28" height="28" viewBox="0 0 48 48" fill="none" style="margin-bottom:8px"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="rf"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g><use href="#rf" transform="rotate(45)"/><use href="#rf" transform="rotate(90)"/><use href="#rf" transform="rotate(135)"/><use href="#rf" transform="rotate(180)"/><use href="#rf" transform="rotate(225)"/><use href="#rf" transform="rotate(270)"/><use href="#rf" transform="rotate(315)"/></g></svg>'
268
- +'<div style="font-size:14px;font-weight:600;margin-bottom:4px">✅ Results ready</div>'
269
- +'<div style="font-size:12px;color:var(--muted)">Orchyn analysis complete — see the full response in the chat.</div>'
270
- +'</div></div>';
271
- },3000);
272
-
273
232
  // MCP Apps handshake.
274
233
  // Claude's McpUiInitializeRequestSchema requires appInfo, appCapabilities
275
234
  // and protocolVersion (legacy hosts read capabilities/clientInfo), so send
@@ -332,70 +291,6 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
332
291
  navigator.clipboard.writeText(text).catch(function(){});
333
292
  }
334
293
 
335
- // ─── Skeleton ───
336
- // Tools that analyze/fetch a single URL: show 1 wide card skeleton
337
- var SINGLE_URL_TOOLS={analyze_post:1,get_social_media:1,understand_social_post:1,get_post_comments:1,analyze_creator_profile:1};
338
- // Tools that return galleries: show multiple small card skeletons
339
- var GALLERY_TOOLS={discover_social_posts:1,get_user_posts:1,search_creators:1,get_similar_creators:1,discover_sounds:1};
340
- // Tools that return credits/checkout: show 1 credit card skeleton
341
- var CREDIT_TOOLS={check_orchyn_credits:1,buy_orchyn_credits:1};
342
-
343
- function renderSkeleton(toolName){
344
- // Branded header above skeletons
345
- var brandHeader='<div style="display:flex;align-items:center;gap:8px;margin-bottom:14px;justify-content:center">'
346
- +'<svg width="20" height="20" viewBox="0 0 48 48" fill="none"><g fill="var(--accent)" transform="translate(24 24)"><circle r="4.1"/><g id="sb"><path d="M-2.85 -5.2 L0 -20.6 L2.85 -5.2 L1.15 1.1 L-1.15 1.1 Z"/></g><use href="#sb" transform="rotate(45)"/><use href="#sb" transform="rotate(90)"/><use href="#sb" transform="rotate(135)"/><use href="#sb" transform="rotate(180)"/><use href="#sb" transform="rotate(225)"/><use href="#sb" transform="rotate(270)"/><use href="#sb" transform="rotate(315)"/></g></svg>'
347
- +'<span style="font-size:13px;font-weight:600;color:var(--muted)">Orchyn</span></div>';
348
- // Single URL tools → 1 wide card skeleton
349
- if(SINGLE_URL_TOOLS[toolName]){
350
- return brandHeader+'<div class="fade-in">'
351
- +'<div class="skeleton-card" style="width:100%">'
352
- +'<div class="skeleton skeleton-thumb" style="height:220px"></div>'
353
- +'<div style="padding:14px 16px">'
354
- +'<div class="skeleton skeleton-badge"></div>'
355
- +'<div class="skeleton skeleton-line" style="width:90%"></div>'
356
- +'<div class="skeleton skeleton-line medium"></div>'
357
- +'<div style="display:flex;gap:6px;margin-top:10px">'
358
- +'<div class="skeleton" style="width:70px;height:28px;border-radius:999px"></div>'
359
- +'<div class="skeleton" style="width:60px;height:28px;border-radius:999px"></div>'
360
- +'<div class="skeleton" style="width:65px;height:28px;border-radius:999px"></div>'
361
- +'</div></div></div></div>';
362
- }
363
- // Credit tools → credit card skeleton
364
- if(CREDIT_TOOLS[toolName]){
365
- return brandHeader+'<div class="fade-in" style="max-width:380px">'
366
- +'<div class="credits-card" style="margin:0">'
367
- +'<div class="skeleton" style="width:120px;height:20px;margin-bottom:14px"></div>'
368
- +'<div class="skeleton" style="width:80px;height:40px;margin-bottom:8px"></div>'
369
- +'<div class="skeleton" style="width:140px;height:14px"></div>'
370
- +'</div></div>';
371
- }
372
- // Gallery tools → multiple small card skeletons
373
- // Unknown tool → default to 1 wide card (most common case: single URL tools)
374
- if(!toolName||SINGLE_URL_TOOLS[toolName]||CREDIT_TOOLS[toolName]){
375
- // Already handled above, but catch unknown tools with single card
376
- if(!toolName){
377
- return brandHeader+'<div class="fade-in" style="max-width:520px">'
378
- +'<div class="skeleton-card" style="width:100%">'
379
- +'<div class="skeleton skeleton-thumb" style="height:220px"></div>'
380
- +'<div style="padding:14px 16px">'
381
- +'<div class="skeleton skeleton-badge"></div>'
382
- +'<div class="skeleton skeleton-line" style="width:90%"></div>'
383
- +'<div class="skeleton skeleton-line medium"></div>'
384
- +'</div></div></div>';
385
- }
386
- }
387
- var count=toolName==='discover_sounds'?4:toolName==='search_creators'||toolName==='get_similar_creators'?3:6;
388
- return brandHeader+'<div class="skeleton-gallery stagger">'
389
- +Array(count).fill(0).map(function(){
390
- return '<div class="skeleton-card">'
391
- +'<div class="skeleton skeleton-thumb"></div>'
392
- +'<div class="skeleton skeleton-badge"></div>'
393
- +'<div class="skeleton skeleton-line medium"></div>'
394
- +'<div class="skeleton skeleton-line short"></div>'
395
- +'</div>';
396
- }).join("")+'</div>';
397
- }
398
-
399
294
  // ─── Post Card ───
400
295
  function postCard(p,wide){
401
296
  var platform=p.platform||"unknown",color=pColor(platform),brandSvg=pSvg(platform);
@@ -624,22 +519,11 @@ body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;background:var(--
624
519
  setTimeout(reportSize,50);
625
520
  }
626
521
 
522
+ // tool-input-partial — no loading UI anymore; just retain the tool name in
523
+ // case future logic needs it. Content only appears via tool-result.
627
524
  function updateProgress(params){
628
- var app=document.getElementById("app");
629
- if(!app)return;
630
- // Learn the tool name
631
- var toolName=params&&params.name?params.name:currentTool;
632
- if(toolName&&toolName!==currentTool){
633
- currentTool=toolName;
634
- // Switch from spinner to correct skeleton if still loading
635
- if(app.querySelector(".loading-spinner")||app.querySelector(".loading-container")||app.querySelector(".skeleton")){
636
- app.innerHTML=renderSkeleton(currentTool);
637
- }
638
- }
639
- // Add a progress bar below the existing content if not already present
640
- if(!app.querySelector(".progress-bar")){
641
- app.innerHTML=app.innerHTML+'<div style="text-align:center;margin-top:12px"><div class="progress-bar" style="width:200px;margin:0 auto"><div class="progress-fill" style="width:60%"></div></div></div>';
642
- }
525
+ var toolName=params&&params.name?params.name:null;
526
+ if(toolName)currentTool=toolName;
643
527
  }
644
528
 
645
529
  // Global helpers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchyn/mcp",
3
- "version": "1.16.1",
3
+ "version": "1.17.1",
4
4
  "description": "MCP server for orchyn - fetch, discover and understand TikTok/Instagram/YouTube/X/LinkedIn posts with AI (media metadata, niche discovery, hook & viral analysis)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",