@nooticr/mcp 1.26.62 → 1.26.64

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/README.md CHANGED
@@ -52,7 +52,7 @@ npx @nooticr/mcp login # one-time sign-in (Google)
52
52
 
53
53
  ## Tools
54
54
 
55
- 106 tools, grouped by what you are trying to do. Prices are in nooticr credits and
55
+ 108 tools, grouped by what you are trying to do. Prices are in nooticr credits and
56
56
  match what the server actually charges.
57
57
 
58
58
  The job tools under **Answer a question you actually have** are not
@@ -108,6 +108,8 @@ them caps that fan-out with an argument.
108
108
  | `creative_voiceover` | quoted first | A voice-over of up to 2500 characters with word timings for burned-in captions, in a voice from `creative_voices_search`. The first call returns a quote in credits and charges nothing; call again with `confirm: true` and the `confirmationToken` to generate. Settled to actual usage, never above the quote. |
109
109
  | `creative_sfx` | quoted first | A short sound effect (0.5-10 s) from a description. Same quote-then-confirm handshake as `creative_voiceover`. |
110
110
  | `creative_music` | quoted first | An instrumental music bed of 5-60 seconds, generated as a job: confirm the quote, then poll `creative_job_status`. May be switched off on a deployment, in which case it says so. Describe mood, tempo and instruments, not artists or songs. |
111
+ | `creative_image` | quoted first | 1-4 still images from a `prompt` with GPT Image (low quality), at a `size` (default portrait 1024x1536), optionally `transparent`. The first call returns a quote in credits and charges nothing; call again with `confirm: true` and the `confirmationToken` to generate. Settled to actual usage, never above the quote. Returns the new `assets` and `creativeCredits`. Keep long text out of the prompt and ask for invented people, not real ones. |
112
+ | `creative_image_edit` | quoted first | Edit or combine 1-4 existing image assets (`imageAssetIds`) with a `prompt`, with an optional `maskAssetId`. Same quote-then-confirm handshake as `creative_image`; returns a new asset and `creativeCredits`. |
111
113
  | `creative_job_status` | free | Where an asynchronous creative job stands by `jobId`: queued, running, done, failed or canceled, with the finished assets and the credits quoted, charged and refunded. Poll after `pollAfterSeconds`. |
112
114
  | `mention_trend` | free | **What a watch has seen over time**, which nothing else here answers — every other read is about now. One point per run: when it ran, how many mentions it found, how many were new, and both counts per network, so "which network is growing" and "did it go quiet or did we stop looking" come apart. On a competitor watch each point carries the median that run measured, which turns "they beat their own median" into "and that median has doubled". Plus the mentions more than one run has seen. Free because the sweeps were billed when they ran; charging again to read what was already paid for is the thing it fixes. |
113
115
  | `update_watch_portfolio` | free | Add or remove a portfolio watch's terms by `watchId`. No new confirmation, because the per-run budget you agreed to does not move — more terms split it more ways, and the reply names any network a run no longer reaches. |
@@ -4,10 +4,10 @@
4
4
  * Both are passthroughs to the server's `POST /mcp` dispatch, which owns the
5
5
  * tenancy check (another workspace's id answers "not found").
6
6
  *
7
- * The ElevenLabs half (CR2): `creative_voices_search` is free; `creative_voiceover`,
8
- * `creative_sfx` and `creative_music` are paid and use the server's quote-then-confirm
9
- * handshake. The remaining paid creative tools (image, video, ...) register here when
10
- * they ship; `creative_test_render` is a server test fixture and is never registered.
7
+ * The paid creative tools register here when they ship. The ElevenLabs half (CR2):
8
+ * `creative_voices_search` is free; `creative_voiceover`, `creative_sfx` and `creative_music` are paid.
9
+ * `creative_image` and `creative_image_edit` (CR3) are paid. All paid tools use the server's
10
+ * quote-then-confirm handshake. `creative_test_render` is a server test fixture and is never registered.
11
11
  * Neither tool has a view yet: the asset list would be a gallery, which the
12
12
  * design system has no card for.
13
13
  */
@@ -66,6 +66,10 @@ export function registerCreativeTools(server, makeClient) {
66
66
  inputSchema: z.object({ jobId: z.string().uuid().describe("jobId returned when the job was started.") }).strict(),
67
67
  outputSchema: OUTPUT_SCHEMAS.creative_job_status,
68
68
  }, passthrough("creative_job_status"));
69
+ const imageSize = z
70
+ .enum(["1024x1536", "1024x1024", "1536x1024", "1088x1920", "1088x1360"])
71
+ .optional()
72
+ .describe("Default 1024x1536 (portrait).");
69
73
  const voiceRef = z
70
74
  .object({ publicOwnerId: z.string(), voiceId: z.string() })
71
75
  .strict()
@@ -77,6 +81,48 @@ export function registerCreativeTools(server, makeClient) {
77
81
  };
78
82
  const paidAnnotations = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
79
83
  const NO_LIKENESS = " No real people's likeness or voice, no copyrighted music, logos or characters.";
84
+ server.registerTool("creative_image", {
85
+ title: "Create Images",
86
+ description: "Generate 1-4 still images with GPT Image (low quality, fast and cheap) and store them as " +
87
+ "creative assets. The prompt is up to 4000 characters; do not put long text in it (put " +
88
+ "long text in a caption overlay) and do not ask for real named people. The first call is a free " +
89
+ "price quote in credits; nothing is charged until you confirm. Show the user the credits and " +
90
+ "ask before calling again with the same arguments plus confirm: true and the " +
91
+ "confirmationToken. Settles to the real usage, never above the quote. Returns assets and " +
92
+ "creativeCredits.",
93
+ annotations: paidAnnotations,
94
+ inputSchema: z
95
+ .object({
96
+ appId: z.number().int(),
97
+ prompt: z.string().min(1).max(4000),
98
+ size: imageSize,
99
+ n: z.number().int().min(1).max(4).optional(),
100
+ transparent: z.boolean().optional(),
101
+ ...handshake,
102
+ })
103
+ .strict(),
104
+ outputSchema: OUTPUT_SCHEMAS.creative_image,
105
+ }, passthrough("creative_image"));
106
+ server.registerTool("creative_image_edit", {
107
+ title: "Edit Images",
108
+ description: "Edit or combine 1-4 existing creative image assets (imageAssetIds, from creative_assets_list " +
109
+ "or a previous creative_image) with GPT Image (low quality), with an optional maskAssetId, and " +
110
+ "store the result as a new asset. The first call is a free price quote in credits; nothing is " +
111
+ "charged until you confirm. Show the user the credits and ask before calling again with " +
112
+ "confirm: true and the confirmationToken. Returns assets and creativeCredits.",
113
+ annotations: paidAnnotations,
114
+ inputSchema: z
115
+ .object({
116
+ appId: z.number().int(),
117
+ prompt: z.string().min(1).max(4000),
118
+ imageAssetIds: z.array(z.string()).min(1).max(4),
119
+ maskAssetId: z.string().optional(),
120
+ size: imageSize,
121
+ ...handshake,
122
+ })
123
+ .strict(),
124
+ outputSchema: OUTPUT_SCHEMAS.creative_image_edit,
125
+ }, passthrough("creative_image_edit"));
80
126
  server.registerTool("creative_voices_search", {
81
127
  title: "Search Creative Voices",
82
128
  description: "Search ElevenLabs' voice library for a voice to use in creative_voiceover. Library voices " +
@@ -484,6 +484,9 @@ export const LOADING_PLANS = {
484
484
  creative_voiceover: { label: "Quoting the voice-over", kind: "text", n: 1, steps: [] },
485
485
  creative_sfx: { label: "Quoting the sound effect", kind: "text", n: 1, steps: [] },
486
486
  creative_music: { label: "Quoting the music bed", kind: "text", n: 1, steps: [] },
487
+ // Paid, but the first call only quotes; the charge waits for the confirmed second call.
488
+ creative_image: { label: "Quoting the images", kind: "text", n: 1, steps: [] },
489
+ creative_image_edit: { label: "Quoting the image edit", kind: "text", n: 1, steps: [] },
487
490
  list_brand_watches: { label: "Reading your watches", kind: "list", n: 3, steps: [], free: true },
488
491
  stop_brand_watch: { label: "Stopping the watch", kind: "text", n: 1, steps: [], free: true },
489
492
  update_watch_portfolio: { label: "Updating the portfolio", kind: "text", n: 1, steps: [], free: true },
@@ -1265,23 +1265,55 @@ export const OUTPUT_SCHEMAS = {
1265
1265
  }),
1266
1266
  // The paid tools answer a quote first, then the generated assets (or a job for music).
1267
1267
  creative_voiceover: open({
1268
- requiresConfirmation: scalar().describe("true on the first call: nothing was generated or charged yet."),
1268
+ quote: open({ credits: scalar() }).nullish().describe("First call: what generating will cost. Nothing was generated or charged yet."),
1269
1269
  confirmationToken: scalar().describe("Pass this back with confirm: true to generate."),
1270
- creditsQuoted: scalar(),
1271
- assets: listOf(open({ id: scalar(), kind: scalar(), url: scalar() })),
1270
+ expiresAt: scalar(),
1271
+ next: scalar(),
1272
+ assetId: scalar(),
1273
+ url: scalar(),
1274
+ key: scalar(),
1275
+ durationMs: scalar(),
1276
+ words: listOf(open({ text: scalar(), start: scalar(), end: scalar() })),
1277
+ wordsTruncated: scalar(),
1278
+ creativeCredits: open({}).nullish(),
1272
1279
  }),
1273
1280
  creative_sfx: open({
1274
- requiresConfirmation: scalar().describe("true on the first call: nothing was generated or charged yet."),
1281
+ quote: open({ credits: scalar() }).nullish().describe("First call: what generating will cost. Nothing was generated or charged yet."),
1275
1282
  confirmationToken: scalar().describe("Pass this back with confirm: true to generate."),
1276
- creditsQuoted: scalar(),
1277
- assets: listOf(open({ id: scalar(), kind: scalar(), url: scalar() })),
1283
+ expiresAt: scalar(),
1284
+ next: scalar(),
1285
+ assetId: scalar(),
1286
+ url: scalar(),
1287
+ key: scalar(),
1288
+ durationMs: scalar(),
1289
+ creativeCredits: open({}).nullish(),
1278
1290
  }),
1279
1291
  creative_music: open({
1280
- requiresConfirmation: scalar().describe("true on the first call: nothing was generated or charged yet."),
1292
+ quote: open({ credits: scalar() }).nullish().describe("First call: what generating will cost. Nothing was generated or charged yet."),
1281
1293
  confirmationToken: scalar().describe("Pass this back with confirm: true to start the job."),
1282
- creditsQuoted: scalar(),
1294
+ expiresAt: scalar(),
1295
+ next: scalar(),
1283
1296
  jobId: scalar().describe("Poll creative_job_status with this."),
1284
- pollAfterSeconds: scalar(),
1297
+ status: scalar(),
1298
+ creativeCredits: open({}).nullish(),
1299
+ refused: scalar().describe("not_enabled when music generation is switched off for this account."),
1300
+ alternative: scalar(),
1301
+ }),
1302
+ // The paid image tools answer a quote first (`quote`, a token, an expiry), then the generated assets
1303
+ // with the credits settled. Keys are the server's (crates/server/src/creative/billing.rs, tools.rs).
1304
+ creative_image: open({
1305
+ quote: open({ credits: scalar() }).nullish(),
1306
+ confirmationToken: scalar().describe("Pass this back with confirm: true to generate."),
1307
+ expiresAt: scalar(),
1308
+ assets: listOf(open({ assetId: scalar(), url: scalar(), key: scalar(), width: scalar(), height: scalar() })),
1309
+ creativeCredits: open({ charged: scalar(), refunded: scalar(), quoted: scalar(), balance: scalar() }).nullish(),
1310
+ }),
1311
+ creative_image_edit: open({
1312
+ quote: open({ credits: scalar() }).nullish(),
1313
+ confirmationToken: scalar().describe("Pass this back with confirm: true to generate."),
1314
+ expiresAt: scalar(),
1315
+ assets: listOf(open({ assetId: scalar(), url: scalar(), key: scalar(), width: scalar(), height: scalar() })),
1316
+ creativeCredits: open({ charged: scalar(), refunded: scalar(), quoted: scalar(), balance: scalar() }).nullish(),
1285
1317
  }),
1286
1318
  creative_job_status: open({
1287
1319
  jobId: scalar(),
@@ -294,6 +294,18 @@ export const TOOL_DEFINITIONS = [
294
294
  description: "Instrumental music bed, 5-60 seconds, as an asynchronous job. Describe mood, tempo and instruments, not artists or songs. May be switched off on this deployment (it then says so and offers stock music). The first call is a free price quote in credits; nothing is charged until you confirm. Show the user the credits and ask before calling again with confirm: true and the confirmationToken, then poll creative_job_status every 10-20 s. No real people's likeness or voice, no copyrighted music, logos or characters.",
295
295
  inputSchema: z.object({ appId: z.number().int(), prompt: z.string().min(1).max(500), durationSeconds: z.number().int().min(5).max(60), briefId: z.string().optional(), confirm: z.boolean().optional().describe("Set true only once the user has agreed to the quoted credits."), confirmationToken: z.string().optional().describe("The token the quote returned.") }).strict(),
296
296
  },
297
+ {
298
+ name: "creative_image",
299
+ title: "Create Images",
300
+ description: "Generate 1-4 still images with GPT Image (low quality, fast and cheap) and store them as creative assets. The prompt is up to 4000 characters; do not put long text in it (put long text in a caption overlay) and do not ask for real named people. The first call is a free price quote in credits; nothing is charged until you confirm. Show the user the credits and ask before calling again with the same arguments plus confirm: true and the confirmationToken. Settles to the real usage, never above the quote. Returns assets and creativeCredits.",
301
+ inputSchema: z.object({ appId: z.number().int(), prompt: z.string().min(1).max(4000), size: z.enum(["1024x1536", "1024x1024", "1536x1024", "1088x1920", "1088x1360"]).optional().describe("Default 1024x1536 (portrait)."), n: z.number().int().min(1).max(4).optional(), transparent: z.boolean().optional(), briefId: z.string().optional(), confirm: z.boolean().optional().describe("Set true only once the user has agreed to the quoted credits."), confirmationToken: z.string().optional().describe("The token the quote returned.") }).strict(),
302
+ },
303
+ {
304
+ name: "creative_image_edit",
305
+ title: "Edit Images",
306
+ description: "Edit or combine 1-4 existing creative image assets (imageAssetIds, from creative_assets_list or a previous creative_image) with GPT Image (low quality), with an optional maskAssetId, and store the result as a new asset. The first call is a free price quote in credits; nothing is charged until you confirm. Show the user the credits and ask before calling again with confirm: true and the confirmationToken. Returns assets and creativeCredits.",
307
+ inputSchema: z.object({ appId: z.number().int(), prompt: z.string().min(1).max(4000), imageAssetIds: z.array(z.string()).min(1).max(4), maskAssetId: z.string().optional(), size: z.enum(["1024x1536", "1024x1024", "1536x1024", "1088x1920", "1088x1360"]).optional().describe("Default 1024x1536 (portrait)."), briefId: z.string().optional(), confirm: z.boolean().optional().describe("Set true only once the user has agreed to the quoted credits."), confirmationToken: z.string().optional().describe("The token the quote returned.") }).strict(),
308
+ },
297
309
  {
298
310
  name: "creative_job_status",
299
311
  title: "Creative Job Status",
@@ -34,7 +34,7 @@ import { registerAmazonTools } from "./amazon.js";
34
34
  import { registerMarketplaceTools } from "./marketplace.js";
35
35
  import { DEFAULT_NOOTICR_SETTINGS, DEFAULT_SOCIAL_PLATFORMS, MemorySettingsStore, SETTINGS_READ_TOOL, SETTINGS_UPDATE_TOOL, settingsOwnerFromAuth, } from "./settings.js";
36
36
  /** Current MCP server version — bumped on every deploy for traceability. */
37
- export const MCP_SERVER_VERSION = "1.26.62";
37
+ export const MCP_SERVER_VERSION = "1.26.64";
38
38
  /** MCP Apps extension identifier */
39
39
  const UI_EXTENSION = "io.modelcontextprotocol/ui";
40
40
  /** MIME type for MCP Apps HTML resources */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nooticr/mcp",
3
3
  "mcpName": "com.nooticr/mcp",
4
- "version": "1.26.62",
4
+ "version": "1.26.64",
5
5
  "description": "MCP server for nooticr - fetch, discover and understand TikTok/Instagram/YouTube/X/Reddit/LinkedIn/Weibo posts with AI (media metadata, niche discovery, brand monitoring, hook & viral analysis)",
6
6
  "homepage": "https://mcp.nooticr.com",
7
7
  "repository": {