@orchyn/mcp 1.14.0 → 1.15.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.
@@ -11,7 +11,7 @@ import { OrchynError } from "./orchyn.js";
11
11
  import { formatPaywallError, runVideoAnalysis, validatePostUrl } from "./video.js";
12
12
  import { ORCHYN_UI_TEMPLATE } from "./ui-template.js";
13
13
  /** Current MCP server version — bumped on every deploy for traceability. */
14
- export const MCP_SERVER_VERSION = "1.14.0";
14
+ export const MCP_SERVER_VERSION = "1.15.0";
15
15
  /** MCP Apps extension identifier */
16
16
  const UI_EXTENSION = "io.modelcontextprotocol/ui";
17
17
  /** MIME type for MCP Apps HTML resources */
@@ -41,24 +41,6 @@ async function computeAppDomain() {
41
41
  return undefined;
42
42
  }
43
43
  }
44
- /** Convert an arbitrary http(s) URL to a base64 MCP `image` data block. */
45
- async function fetchAsBase64Image(url, mimeType) {
46
- try {
47
- const res = await fetch(url, { signal: AbortSignal.timeout(12_000) });
48
- if (!res.ok)
49
- return { type: "image", data: "", mimeType };
50
- const buf = await res.arrayBuffer();
51
- const bytes = new Uint8Array(buf);
52
- let bin = "";
53
- for (let i = 0; i < bytes.length; i += 0x8000) {
54
- bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
55
- }
56
- return { type: "image", data: btoa(bin), mimeType };
57
- }
58
- catch {
59
- return { type: "image", data: "", mimeType };
60
- }
61
- }
62
44
  /**
63
45
  * Rewrite external image URLs to go through the orchyn proxy so they
64
46
  * work inside ChatGPT's sandboxed iframe (CORS + CSP restrictions).
@@ -105,19 +87,12 @@ function proxyUrls(obj) {
105
87
  return obj;
106
88
  }
107
89
  async function toToolResult(proxy) {
108
- // The MCP SDK's ImageContent only accepts `data` (base64). The orchyn
109
- // backend now emits `url` image blocks pointing at permanent, re-hosted
110
- // JPEG thumbnails (HEIC already transcoded), so convert those URLs to
111
- // base64 data here guaranteeing clients render a valid image.
112
- const imgBlocks = proxy.contentBlocks.filter((c) => c.type === "image");
113
- // Skip image blocks whose base64 is empty (fetch failed) — clients reject
114
- // them with "could not be processed: Error processing image".
115
- const images = (await Promise.all(imgBlocks.map((c) => c.url
116
- ? fetchAsBase64Image(String(c.url), String(c.mimeType ?? "image/jpeg"))
117
- : Promise.resolve({ type: "image", data: String(c.data ?? ""), mimeType: String(c.mimeType ?? "image/jpeg") })))).filter((img) => img.type === "image" && img.data.length > 0);
118
- // The Rust backend embeds HTML cards directly in the text block
119
- // (type:"text", text:"<div>...</div>\n\n{json}"). We extract the HTML
120
- // prefix from the first text contentBlock and prepend it.
90
+ // MCP Apps views (ChatGPT & Claude) render the interactive HTML card, which
91
+ // already embeds all thumbnails/videos. Claude's Apps bridge rejects a tool
92
+ // result that mixes raw `image` blocks with an app view ("could not be
93
+ // processed: Error processing image" + blank iframe), so we intentionally
94
+ // drop the standalone base64 image blocks here and keep only the text block
95
+ // carrying the HTML card + structured JSON.
121
96
  const textBlock = proxy.contentBlocks.find((c) => c.type === "text");
122
97
  const rawText = textBlock ? String(textBlock.text ?? "") : "";
123
98
  const htmlPrefix = rawText.startsWith("<")
@@ -131,7 +106,7 @@ async function toToolResult(proxy) {
131
106
  const proxiedHtml = htmlPrefix ? proxyImageUrlsInHtml(htmlPrefix) : "";
132
107
  const text = proxiedHtml ? `${proxiedHtml}\n\n${textJson}` : textJson;
133
108
  return {
134
- content: [...images, { type: "text", text }],
109
+ content: [{ type: "text", text }],
135
110
  structuredContent: proxied ?? {},
136
111
  };
137
112
  }
@@ -330,19 +305,14 @@ export function createMcpServer(makeClient) {
330
305
  const creatorHandle = String(post.creatorHandle ?? "");
331
306
  const title = String(post.title ?? post.caption ?? "");
332
307
  const htmlCard = buildAnalysisHtmlCard(proxied, thumbnailUrl, platform, creatorHandle, title);
333
- // The backend attaches `_inlineImages` as permanent orchyn public URLs
334
- // (re-hosted into storage, HEIC already transcoded to JPEG). The SDK's
335
- // ImageContent only accepts base64 `data`, so fetch each URL and encode it.
336
- // Skip image blocks whose base64 is empty (fetch failed) — clients
337
- // reject them with "could not be processed: Error processing image".
338
- const images = (await Promise.all((result.inlineImages ?? [])
339
- .filter((img) => typeof img === "object" && img !== null)
340
- .map((img) => "url" in img && img.url
341
- ? fetchAsBase64Image(String(img.url), String(img.mimeType ?? "image/jpeg"))
342
- : Promise.resolve({ type: "image", data: String(img.data ?? ""), mimeType: String(img.mimeType ?? "image/jpeg") })))).filter((img) => img.type === "image" && img.data.length > 0);
308
+ // The interactive HTML card above already embeds the thumbnail + full
309
+ // video, so no standalone base64 `image` blocks are emitted. Claude's Apps
310
+ // bridge rejects raw image blocks mixed with an app view ("could not be
311
+ // processed: Error processing image" + blank iframe), so we keep only the
312
+ // text block carrying the card + structured JSON.
343
313
  const textJson = JSON.stringify(proxied, null, 2);
344
314
  return {
345
- content: [...images, { type: "text", text: `${htmlCard}\n\n${textJson}` }],
315
+ content: [{ type: "text", text: `${htmlCard}\n\n${textJson}` }],
346
316
  structuredContent: proxied,
347
317
  };
348
318
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchyn/mcp",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
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",