@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.
- package/dist/shared/tools.js +14 -44
- package/package.json +1 -1
package/dist/shared/tools.js
CHANGED
|
@@ -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
|
+
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
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
//
|
|
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: [
|
|
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
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
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: [
|
|
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.
|
|
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",
|