@orchyn/mcp 1.1.2 → 1.2.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/index.js CHANGED
@@ -18,18 +18,7 @@ import { getBaseUrl, getPublicUrl, getPort, getCredentialsFile, getTransportMode
18
18
  import { AuthManager, OrchynAuthError, createHttpTokenProvider, createStdioTokenProvider } from "./auth.js";
19
19
  import { OrchynClient, OrchynError } from "./orchyn.js";
20
20
  import { OAuthManager } from "./oauth.js";
21
- import { formatPaywallError, runVideoAnalysis, validateVideoUrl } from "./video.js";
22
- const TOOL_NAME = "analyze_video";
23
- const TOOL_DESCRIPTION = "Start an AI analysis of a TikTok, Instagram, or YouTube video from its link. " +
24
- "Requires a connected orchyn account; consumes orchyn credits (first analysis free). " +
25
- "Returns the analysis result once finished.";
26
- const TOOL_INPUT_SCHEMA = z
27
- .object({
28
- url: z
29
- .string()
30
- .describe("Public video URL (tiktok.com, instagram.com, youtube.com, youtu.be, or common shortlinks)."),
31
- })
32
- .strict();
21
+ import { formatPaywallError, runVideoAnalysis, validatePostUrl } from "./video.js";
33
22
  function toToolResult(proxy) {
34
23
  const images = proxy.contentBlocks
35
24
  .filter((c) => c.type === "image")
@@ -48,19 +37,25 @@ function toolError(prefix, err) {
48
37
  export function createServer(opts) {
49
38
  const server = new McpServer({
50
39
  name: "orchyn-mcp",
51
- version: "1.0.0",
40
+ version: "1.1.0",
52
41
  });
53
- server.registerTool(TOOL_NAME, {
54
- title: "Analyze Video",
55
- description: TOOL_DESCRIPTION,
56
- inputSchema: TOOL_INPUT_SCHEMA,
42
+ server.registerTool("analyze_post", {
43
+ title: "Analyze Post",
44
+ description: "Analyze a social post (video, image, carousel/slideshow) from its link — " +
45
+ "imports the media and runs AI analysis over the actual content (video frames, carousel images, caption). " +
46
+ "Supports TikTok, Instagram, YouTube and X/Twitter. Returns the full analysis once finished.",
47
+ inputSchema: z
48
+ .object({
49
+ url: z.string().describe("Public post URL (TikTok/Instagram/YouTube/X or shortlinks)."),
50
+ })
51
+ .strict(),
57
52
  }, async (args, extra) => {
58
53
  let session;
59
54
  if (extra.authInfo?.token && opts.resolveSession) {
60
55
  session = opts.resolveSession(extra.authInfo.token);
61
56
  }
62
57
  const client = opts.makeClient(session);
63
- const validation = validateVideoUrl(args.url);
58
+ const validation = validatePostUrl(args.url);
64
59
  if (!validation.ok) {
65
60
  return {
66
61
  content: [{ type: "text", text: `Invalid url: ${validation.error}` }],
@@ -69,33 +64,54 @@ export function createServer(opts) {
69
64
  }
70
65
  try {
71
66
  const result = await runVideoAnalysis(client, validation.url);
72
- return {
73
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
74
- };
67
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
75
68
  }
76
69
  catch (err) {
77
70
  if (err instanceof OrchynError && err.paywall) {
78
71
  return {
79
- content: [
80
- {
81
- type: "text",
82
- text: `Analysis blocked: ${formatPaywallError(err)}\n\nHTTP ${err.status}: ${err.message}`,
83
- },
84
- ],
72
+ content: [{ type: "text", text: `Analysis blocked: ${formatPaywallError(err)}\n\nHTTP ${err.status}: ${err.message}` }],
85
73
  isError: true,
86
74
  };
87
75
  }
88
76
  const msg = err instanceof Error ? err.message : String(err);
77
+ return { content: [{ type: "text", text: `Analysis failed: ${msg}` }], isError: true };
78
+ }
79
+ });
80
+ server.registerTool("analyze_video", {
81
+ title: "Analyze Video",
82
+ description: "[Deprecated — use analyze_post] Alias of analyze_post — analyzes any post type.",
83
+ inputSchema: z
84
+ .object({
85
+ url: z.string().describe("Public post URL (TikTok/Instagram/YouTube/X or shortlinks)."),
86
+ })
87
+ .strict(),
88
+ }, async (args, extra) => {
89
+ let session;
90
+ if (extra.authInfo?.token && opts.resolveSession) {
91
+ session = opts.resolveSession(extra.authInfo.token);
92
+ }
93
+ const client = opts.makeClient(session);
94
+ const validation = validatePostUrl(args.url);
95
+ if (!validation.ok) {
89
96
  return {
90
- content: [
91
- {
92
- type: "text",
93
- text: `Video analysis failed: ${msg}`,
94
- },
95
- ],
97
+ content: [{ type: "text", text: `Invalid url: ${validation.error}` }],
96
98
  isError: true,
97
99
  };
98
100
  }
101
+ try {
102
+ const result = await runVideoAnalysis(client, validation.url);
103
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
104
+ }
105
+ catch (err) {
106
+ if (err instanceof OrchynError && err.paywall) {
107
+ return {
108
+ content: [{ type: "text", text: `Analysis blocked: ${formatPaywallError(err)}\n\nHTTP ${err.status}: ${err.message}` }],
109
+ isError: true,
110
+ };
111
+ }
112
+ const msg = err instanceof Error ? err.message : String(err);
113
+ return { content: [{ type: "text", text: `Analysis failed: ${msg}` }], isError: true };
114
+ }
99
115
  });
100
116
  server.registerTool("get_social_media", {
101
117
  title: "Get Social Media",
package/dist/video.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export const POLL_INTERVAL_MS = 2000;
6
6
  export const POLL_TIMEOUT_MS = 300_000;
7
- const SUPPORTED_HOSTS = new Set([
7
+ const SUPPORTED_HOSTS_VIDEO = new Set([
8
8
  "tiktok.com",
9
9
  "vm.tiktok.com",
10
10
  "instagram.com",
@@ -15,7 +15,13 @@ const SUPPORTED_HOSTS = new Set([
15
15
  "youtube-nocookie.com",
16
16
  "m.tiktok.com",
17
17
  ]);
18
- export function validateVideoUrl(rawUrl) {
18
+ const SUPPORTED_HOSTS_POST = new Set([
19
+ ...SUPPORTED_HOSTS_VIDEO,
20
+ "x.com",
21
+ "twitter.com",
22
+ "mobile.twitter.com",
23
+ ]);
24
+ function validateUrl(rawUrl, allowed) {
19
25
  if (typeof rawUrl !== "string" || rawUrl.trim() === "") {
20
26
  return { ok: false, error: "url must be a non-empty string." };
21
27
  }
@@ -36,7 +42,7 @@ export function validateVideoUrl(rawUrl) {
36
42
  // accept all youtube.com paths, incl. /shorts/<id>
37
43
  return { ok: true, url: parsed.toString() };
38
44
  }
39
- if (SUPPORTED_HOSTS.has(host)) {
45
+ if (allowed.has(host)) {
40
46
  return { ok: true, url: parsed.toString() };
41
47
  }
42
48
  return {
@@ -44,6 +50,12 @@ export function validateVideoUrl(rawUrl) {
44
50
  error: "url host is not supported. Supported: tiktok.com, vm.tiktok.com, instagram.com, instagr.am, youtube.com, youtu.be, m.youtube.com (and /shorts).",
45
51
  };
46
52
  }
53
+ export function validateVideoUrl(rawUrl) {
54
+ return validateUrl(rawUrl, SUPPORTED_HOSTS_VIDEO);
55
+ }
56
+ export function validatePostUrl(rawUrl) {
57
+ return validateUrl(rawUrl, SUPPORTED_HOSTS_POST);
58
+ }
47
59
  export class JobTimeoutError extends Error {
48
60
  constructor(jobId, elapsedMs, lastStatus) {
49
61
  super(`Timed out after ${Math.round(elapsedMs / 1000)}s waiting for analysis job ${jobId} to finish.` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchyn/mcp",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for orchyn - fetch, discover and understand TikTok/Instagram/YouTube/X posts with AI (media metadata, niche discovery, hook & viral analysis)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",