corent-mcp 0.3.1 → 0.4.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/dist/server.js CHANGED
@@ -15,8 +15,26 @@
15
15
  */
16
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
17
  import { z } from "zod";
18
+ import { MEDIA_WIDGET_HTML } from "./widget-html.js";
18
19
  export const DEFAULT_API_URL = "https://api.corent.tech";
19
- export const SERVER_VERSION = "0.3.1";
20
+ export const SERVER_VERSION = "0.4.0";
21
+ // --- MCP Apps (SEP-1865): the media tools render an inline widget in hosts
22
+ // that support it (claude.ai web/desktop, others). Hosts without the extension
23
+ // ignore the _meta and fall back to plain text results, so this is additive.
24
+ const WIDGET_URI = "ui://corent/media";
25
+ const WIDGET_MIME = "text/html;profile=mcp-app";
26
+ // Both the canonical nested key and the legacy flat key — the spec tells hosts
27
+ // to check both, and older host builds only know the flat one.
28
+ const WIDGET_TOOL_META = {
29
+ ui: { resourceUri: WIDGET_URI },
30
+ "ui/resourceUri": WIDGET_URI,
31
+ };
32
+ // Generated media lives on Supabase storage; the logo and any brand assets on
33
+ // corent.tech. Everything else stays blocked by the host's CSP.
34
+ const WIDGET_CSP = {
35
+ connectDomains: [],
36
+ resourceDomains: ["https://hzokyylcctfybulkgmcb.supabase.co", "https://corent.tech"],
37
+ };
20
38
  class CorentApiError extends Error {
21
39
  status;
22
40
  detail;
@@ -62,7 +80,7 @@ export function createCorentServer(config = {}) {
62
80
  // can advertise its tools to catalogs/agents without a key configured.
63
81
  if (!apiKey) {
64
82
  throw new CorentApiError(401, {
65
- message: "CORENT_API_KEY is not set. Get a key at https://corent.tech and add it to your MCP client config.",
83
+ message: "No Corent credentials. On claude.ai or other remote MCP clients, reconnect the Corent connector and approve access (OAuth). For local/stdio use, set CORENT_API_KEY in your MCP client config. Keys: https://corent.tech/dashboard/api-keys",
66
84
  });
67
85
  }
68
86
  const res = await fetch(`${apiUrl}${path}`, {
@@ -106,7 +124,30 @@ export function createCorentServer(config = {}) {
106
124
  }
107
125
  };
108
126
  }
109
- const server = new McpServer({ name: "corent", version: SERVER_VERSION });
127
+ // icons/websiteUrl: spec-standard server identity MCP clients that support
128
+ // it (claude.ai included) show the square Corent mark instead of a letter tile.
129
+ const server = new McpServer({
130
+ name: "corent",
131
+ title: "Corent",
132
+ version: SERVER_VERSION,
133
+ icons: [{ src: "https://corent.tech/brand/intro-mark.png", mimeType: "image/png", sizes: ["512x512"] }],
134
+ websiteUrl: "https://corent.tech",
135
+ });
136
+ server.registerResource("corent-media-widget", WIDGET_URI, {
137
+ title: "Corent media widget",
138
+ description: "Inline card that tracks a generation and plays the result.",
139
+ mimeType: WIDGET_MIME,
140
+ _meta: { ui: { csp: WIDGET_CSP } },
141
+ }, async () => ({
142
+ contents: [
143
+ {
144
+ uri: WIDGET_URI,
145
+ mimeType: WIDGET_MIME,
146
+ text: MEDIA_WIDGET_HTML,
147
+ _meta: { ui: { csp: WIDGET_CSP } },
148
+ },
149
+ ],
150
+ }));
110
151
  // Loose output shapes: every field optional so an additive API change can
111
152
  // never break a client's schema validation.
112
153
  // model is null while a video job is still processing (no provider chosen
@@ -131,9 +172,14 @@ export function createCorentServer(config = {}) {
131
172
  };
132
173
  server.registerTool("generate_image", {
133
174
  title: "Generate image",
175
+ _meta: WIDGET_TOOL_META,
134
176
  description: "Generate an image from a text prompt. Returns a permanent public URL. Synchronous: typically completes in 2-20 seconds. Costs a few cents, billed to the Corent account. Failed generations are never billed.",
135
177
  inputSchema: {
136
178
  prompt: z.string().describe("What to generate, in plain language"),
179
+ tier: z
180
+ .enum(["air", "lite", "premium", "pro", "max_pro"])
181
+ .optional()
182
+ .describe("Quality tier (preferred): air=cheapest/fastest, lite=everyday, premium=production, pro=advanced, max_pro=maximum fidelity (flagship models). Overrides preference."),
137
183
  preference: z
138
184
  .enum(["fast", "cheap", "quality", "balanced"])
139
185
  .default("balanced")
@@ -146,28 +192,38 @@ export function createCorentServer(config = {}) {
146
192
  },
147
193
  outputSchema: imageOutput,
148
194
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
149
- }, wrap(async ({ prompt, preference, style, aspect_ratio }) => corent("/v1/images/generate", {
195
+ }, wrap(async ({ prompt, tier, preference, style, aspect_ratio }) => corent("/v1/images/generate", {
150
196
  method: "POST",
151
- body: JSON.stringify({ prompt, preference, style, aspect_ratio }),
197
+ body: JSON.stringify({ prompt, tier, preference, style, aspect_ratio }),
152
198
  })));
153
199
  server.registerTool("generate_video", {
154
200
  title: "Generate video",
201
+ _meta: WIDGET_TOOL_META,
155
202
  description: "Start generating a video from a text prompt (optionally animating a source image). Asynchronous: returns a job id immediately; poll get_job until status is 'completed'. Video costs more than images (tens of cents to a few dollars depending on tier). Failed generations are never billed.",
156
203
  inputSchema: {
157
204
  prompt: z.string().describe("What to generate, in plain language"),
205
+ tier: z
206
+ .enum(["air", "lite", "premium", "pro", "max_pro"])
207
+ .optional()
208
+ .describe("Quality tier (preferred): air=cheapest/fastest, lite=everyday, premium=production, pro=advanced, max_pro=maximum fidelity (flagship models). Overrides preference."),
158
209
  preference: z.enum(["fast", "cheap", "quality", "balanced"]).default("balanced"),
159
210
  aspect_ratio: z.enum(["16:9", "9:16", "1:1"]).default("16:9"),
160
211
  duration_s: z.number().int().min(1).max(30).optional().describe("Requested duration in seconds"),
212
+ resolution: z
213
+ .enum(["720p", "1080p", "4k"])
214
+ .optional()
215
+ .describe("Pixel resolution (default 720p). Tier-capped: premium/pro allow up to 1080p, max_pro up to 4k; above the tier's cap it is clamped down, never rejected. Higher resolutions cost more (1080p ~2.5x, 4k ~5.5x). GET /v1/tiers lists each tier's menu with prices."),
161
216
  image_url: z.string().url().optional().describe("If set, animates this image instead of pure text-to-video"),
162
217
  },
163
218
  outputSchema: jobOutput,
164
219
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
165
- }, wrap(async ({ prompt, preference, aspect_ratio, duration_s, image_url }) => corent("/v1/videos/generate", {
220
+ }, wrap(async ({ prompt, tier, preference, aspect_ratio, duration_s, resolution, image_url }) => corent("/v1/videos/generate", {
166
221
  method: "POST",
167
- body: JSON.stringify({ prompt, preference, aspect_ratio, duration_s, image_url }),
222
+ body: JSON.stringify({ prompt, tier, preference, aspect_ratio, duration_s, resolution, image_url }),
168
223
  })));
169
224
  server.registerTool("get_job", {
170
225
  title: "Check job status",
226
+ _meta: WIDGET_TOOL_META,
171
227
  description: "Check the status of a generation job (mainly videos). When completed, the response includes the permanent media URL and the cost. Read-only and free.",
172
228
  inputSchema: { job_id: z.string().describe("The job id returned by generate_video or generate_image") },
173
229
  outputSchema: jobOutput,
@@ -206,6 +262,7 @@ export function createCorentServer(config = {}) {
206
262
  }, wrap(async ({ intent }) => corent("/v1/intent", { method: "POST", body: JSON.stringify({ intent }) })));
207
263
  server.registerTool("create", {
208
264
  title: "Create (plan + generate, budget-capped)",
265
+ _meta: WIDGET_TOOL_META,
209
266
  description: "Describe what you want in plain language and Corent plans AND generates it — choosing image vs video, tier, aspect ratio, and duration for you (the 'zero decisions' path). You MUST pass max_cost_cents as a spend ceiling; if the estimated cost exceeds it, nothing is generated and you're told the estimate so you can raise the ceiling. Image results come back with a URL; video results come back as a job id to poll with get_job. Failed generations are never billed.",
210
267
  inputSchema: {
211
268
  intent: z.string().describe("What you want, in natural language"),