@clipform/mcp-server 2.1.1 → 2.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/README.md CHANGED
@@ -72,6 +72,7 @@ Your MCP client lists these automatically on connect (via `tools/list`). Full re
72
72
  | `clipform_update_node` | Update one or more existing nodes' text, type, config, or options. |
73
73
  | `clipform_delete_node` | Delete a node from a form. |
74
74
  | `clipform_upload_media_asset` | Put one or more media files into your workspace media library (max 10, uploaded sequentially). |
75
+ | `clipform_complete_media_upload` | Confirm a signed-PUT still image upload finished, after PUTting the bytes to the upload_url returned by clipform_upload_media_asset. |
75
76
  | `clipform_attach_node_media` | Attach an existing workspace media asset (from clipform_upload_media_asset) to one or more nodes (max 10). |
76
77
  | `clipform_get_node_media` | Get the media attached to a node, including processing status. |
77
78
  | `clipform_delete_node_media` | Remove media from a node. |
@@ -3,7 +3,7 @@ import {
3
3
  FORM_TYPE_KEYS,
4
4
  callApi,
5
5
  getSessionContext
6
- } from "./chunk-CNEUXDNC.js";
6
+ } from "./chunk-VY52B6DR.js";
7
7
 
8
8
  // src/lib/guides.ts
9
9
  var guidesPromise = null;
@@ -136,4 +136,4 @@ export {
136
136
  getGuideUri,
137
137
  registerResources
138
138
  };
139
- //# sourceMappingURL=chunk-HG3FYVTR.js.map
139
+ //# sourceMappingURL=chunk-AHJXNS4R.js.map
@@ -2,7 +2,7 @@ import {
2
2
  WORKFLOW_TYPES,
3
3
  getDiscoveryParams,
4
4
  getSessionContextWithAuth
5
- } from "./chunk-CNEUXDNC.js";
5
+ } from "./chunk-VY52B6DR.js";
6
6
  import {
7
7
  __export
8
8
  } from "./chunk-HCZI2UJ5.js";
@@ -4708,4 +4708,4 @@ export {
4708
4708
  getWorkflowText,
4709
4709
  registerPrompts
4710
4710
  };
4711
- //# sourceMappingURL=chunk-TTSO4ZPG.js.map
4711
+ //# sourceMappingURL=chunk-HWGDTUKS.js.map
@@ -6,7 +6,7 @@ import {
6
6
  getWorkflowText,
7
7
  objectType,
8
8
  registerPrompts
9
- } from "./chunk-TTSO4ZPG.js";
9
+ } from "./chunk-HWGDTUKS.js";
10
10
  import {
11
11
  GUIDE_TYPES,
12
12
  QUIZ_VARIANTS,
@@ -14,7 +14,7 @@ import {
14
14
  getGuideUri,
15
15
  guideFallbackText,
16
16
  registerResources
17
- } from "./chunk-HG3FYVTR.js";
17
+ } from "./chunk-AHJXNS4R.js";
18
18
  import {
19
19
  ACTIVE_NODE_TYPES,
20
20
  BUSINESS,
@@ -33,7 +33,7 @@ import {
33
33
  resolveFormType,
34
34
  structuredResult,
35
35
  textResult
36
- } from "./chunk-CNEUXDNC.js";
36
+ } from "./chunk-VY52B6DR.js";
37
37
  import {
38
38
  __commonJS,
39
39
  __export,
@@ -17923,7 +17923,7 @@ When a public URL is provided, the media is fetched and stored automatically. Fo
17923
17923
  resultLines.push(
17924
17924
  `Upload URL: ${uploadUrl}`,
17925
17925
  `Upload method: ${uploadMethod}`,
17926
- `Upload the file directly to the upload URL using ${uploadMethod === "tus" ? "TUS resumable upload" : "HTTP PUT"}.`
17926
+ uploadMethod === "put" ? `PUT the file's bytes directly to the upload URL, then call clipform_complete_media_upload with media_asset_id ${mediaAssetId} to confirm the upload and mark it ready - it stays invisible in the library until you do.` : `Upload the file directly to the upload URL using TUS resumable upload.`
17927
17927
  );
17928
17928
  } else {
17929
17929
  resultLines.push(`Media stored in the workspace library.`);
@@ -17945,6 +17945,54 @@ When a public URL is provided, the media is fetched and stored automatically. Fo
17945
17945
  );
17946
17946
  }
17947
17947
 
17948
+ // src/tools/complete-media-upload.ts
17949
+ function registerCompleteMediaUploadTool(server) {
17950
+ server.registerTool(
17951
+ "clipform_complete_media_upload",
17952
+ {
17953
+ title: "Complete Media Upload",
17954
+ description: `Confirm a signed-PUT still image upload finished, after PUTting the bytes to the upload_url returned by clipform_upload_media_asset. The API verifies the object actually landed in storage before flipping the asset from processing to ready - call this right after the PUT succeeds, or the asset stays invisible in the library. Not needed for video uploads (TUS/Mux settle automatically).`,
17955
+ inputSchema: {
17956
+ media_asset_id: external_exports.string().uuid().describe("The media_asset_id returned by clipform_upload_media_asset for the still image")
17957
+ },
17958
+ outputSchema: {
17959
+ media_asset_id: external_exports.string(),
17960
+ status: external_exports.string().describe("The asset's status after this call - ready on success, or its already-settled status when called again (noop)")
17961
+ },
17962
+ annotations: {
17963
+ readOnlyHint: false,
17964
+ destructiveHint: false,
17965
+ idempotentHint: true,
17966
+ openWorldHint: false
17967
+ }
17968
+ },
17969
+ async ({ media_asset_id }) => {
17970
+ const meResult = await callApi("/me", { method: "GET" });
17971
+ if (!meResult.ok) {
17972
+ return errorResult(`Unable to determine workspace: ${meResult.error}`);
17973
+ }
17974
+ const me = meResult.data;
17975
+ const workspaceId = me.workspace?.id ?? null;
17976
+ if (!workspaceId) {
17977
+ return errorResult(
17978
+ "No workspace available. This usually means authentication is misconfigured - check your API key or OAuth connection."
17979
+ );
17980
+ }
17981
+ const result = await callApi(`/workspaces/${workspaceId}/media/${media_asset_id}/complete`, {
17982
+ method: "POST",
17983
+ body: { uploaded: true }
17984
+ });
17985
+ if (!result.ok) {
17986
+ return errorResult(result.error);
17987
+ }
17988
+ const status = result.data.status ?? "ready";
17989
+ const noop = Boolean(result.data.noop);
17990
+ const text = noop ? `Media asset ${media_asset_id} was already ${status} - no change made.` : `Media asset ${media_asset_id} is now ${status}.`;
17991
+ return structuredResult(text, { media_asset_id, status });
17992
+ }
17993
+ );
17994
+ }
17995
+
17948
17996
  // src/tools/attach-node-media.ts
17949
17997
  var MEDIA_SUPPORTED_TYPES = Object.entries(NODE_TYPES).filter(([, def]) => def.supports_media).map(([type]) => type);
17950
17998
  var AttachMediaItemSchema = external_exports.object({
@@ -18338,7 +18386,7 @@ var CaptionSchema2 = external_exports.object({
18338
18386
  });
18339
18387
  var TtsItemSchema = external_exports.object({
18340
18388
  text: external_exports.string().min(1).max(5e3).describe("Narration text"),
18341
- voice: external_exports.enum(["ryan", "sonia", "andrew", "ava", "guy"]).optional().default("ryan").describe("Voice: ryan (British male, clear/articulate), sonia (British female, warm/bright), andrew (American male, smooth/neutral), ava (American female, vibrant/friendly), guy (American male, deep/authoritative). Pick based on the form's topic and audience."),
18389
+ voice: external_exports.enum(["ryan", "sonia", "andrew", "ava", "guy"]).optional().default("ryan").describe("Voice: ryan (British male, clear/articulate), sonia (British female, warm/bright), andrew (American male, smooth/neutral), ava (American female, vibrant/friendly), guy (American male, deep/authoritative). Pick ONE voice for the whole form based on its topic and audience, then reuse that same voice on every item and every call - never mix voices within a form unless the user explicitly asks for more than one."),
18342
18390
  tone: external_exports.string().max(500).optional().describe("Style instructions for how the voice should sound. E.g. 'Speak like an energetic quiz show host, playful and teasing' or 'Warm and conversational, like chatting with a friend'. Infer from context if not specified by the user.")
18343
18391
  });
18344
18392
  function registerGenerateTtsTool(server) {
@@ -18348,7 +18396,7 @@ function registerGenerateTtsTool(server) {
18348
18396
  title: "Generate Text-to-Speech",
18349
18397
  description: `Generate narration audio from text with word-level captions. Use this for quiz question narration, survey introductions, form instructions, or any node that benefits from a human voice. Proactively suggest narration for quizzes and content-rich forms - it significantly improves engagement.
18350
18398
 
18351
- Available voices: ryan (British male, clear), sonia (British female, warm), andrew (American male, smooth), ava (American female, vibrant), guy (American male, deep). Pick the voice that fits the topic - e.g. a London quiz gets ryan or sonia, a US sports quiz gets andrew or guy.
18399
+ Available voices: ryan (British male, clear), sonia (British female, warm), andrew (American male, smooth), ava (American female, vibrant), guy (American male, deep). Pick ONE voice that fits the topic - e.g. a London quiz gets ryan or sonia, a US sports quiz gets andrew or guy - and reuse that SAME voice for every item and every call across the whole form. Never mix voices within one form unless the user explicitly asks for multiple voices.
18352
18400
 
18353
18401
  Use the tone parameter to direct HOW the voice speaks. Always set a tone that matches the form's mood - e.g. quizzes: "Energetic and playful, like a quiz show host teasing the audience", surveys: "Professional but warm, encouraging honest answers", personality quizzes: "Curious and reflective". This dramatically improves the narration quality.
18354
18402
 
@@ -19889,6 +19937,7 @@ function createServer(options) {
19889
19937
  registerUpdateNodeTool(server);
19890
19938
  registerDeleteNodeTool(server);
19891
19939
  registerUploadMediaAssetTool(server);
19940
+ registerCompleteMediaUploadTool(server);
19892
19941
  registerAttachNodeMediaTool(server);
19893
19942
  registerGetNodeMediaTool(server);
19894
19943
  registerDeleteNodeMediaTool(server);
@@ -19919,4 +19968,4 @@ export {
19919
19968
  RENDER_TIMING,
19920
19969
  createServer
19921
19970
  };
19922
- //# sourceMappingURL=chunk-RQKRLXRN.js.map
19971
+ //# sourceMappingURL=chunk-PI3KAC3M.js.map