@clipform/mcp-server 1.0.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.
Files changed (41) hide show
  1. package/README.md +87 -0
  2. package/dist/__tests__/config-sync.test.d.ts +1 -0
  3. package/dist/__tests__/config-sync.test.js +35 -0
  4. package/dist/__tests__/config-sync.test.js.map +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +8 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/lib/api-client.d.ts +29 -0
  9. package/dist/lib/api-client.js +56 -0
  10. package/dist/lib/api-client.js.map +1 -0
  11. package/dist/lib/config.d.ts +24 -0
  12. package/dist/lib/config.js +266 -0
  13. package/dist/lib/config.js.map +1 -0
  14. package/dist/lib/schemas.d.ts +38 -0
  15. package/dist/lib/schemas.js +118 -0
  16. package/dist/lib/schemas.js.map +1 -0
  17. package/dist/server.d.ts +2 -0
  18. package/dist/server.js +23 -0
  19. package/dist/server.js.map +1 -0
  20. package/dist/tools/add-question.d.ts +2 -0
  21. package/dist/tools/add-question.js +47 -0
  22. package/dist/tools/add-question.js.map +1 -0
  23. package/dist/tools/create-form.d.ts +2 -0
  24. package/dist/tools/create-form.js +101 -0
  25. package/dist/tools/create-form.js.map +1 -0
  26. package/dist/tools/delete-form.d.ts +2 -0
  27. package/dist/tools/delete-form.js +28 -0
  28. package/dist/tools/delete-form.js.map +1 -0
  29. package/dist/tools/delete-question.d.ts +2 -0
  30. package/dist/tools/delete-question.js +29 -0
  31. package/dist/tools/delete-question.js.map +1 -0
  32. package/dist/tools/get-form.d.ts +2 -0
  33. package/dist/tools/get-form.js +52 -0
  34. package/dist/tools/get-form.js.map +1 -0
  35. package/dist/tools/update-form.d.ts +2 -0
  36. package/dist/tools/update-form.js +44 -0
  37. package/dist/tools/update-form.js.map +1 -0
  38. package/dist/tools/update-question.d.ts +2 -0
  39. package/dist/tools/update-question.js +63 -0
  40. package/dist/tools/update-question.js.map +1 -0
  41. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @clipform/mcp-server
2
+
3
+ MCP server for [Clipform](https://clipform.io) - build and manage video-style forms from Claude Code, Claude Desktop, or any MCP client.
4
+
5
+ ## Quick Start
6
+
7
+ Add to your Claude Desktop config (`claude_desktop_config.json`):
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "clipform": {
13
+ "command": "npx",
14
+ "args": ["-y", "@clipform/mcp-server"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ Or for Claude Code, add to `.mcp.json`:
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "clipform": {
26
+ "type": "stdio",
27
+ "command": "npx",
28
+ "args": ["-y", "@clipform/mcp-server"]
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## What you can do
35
+
36
+ | Tool | Description |
37
+ |------|-------------|
38
+ | `clipform_create_form` | Create a new form with questions in one call |
39
+ | `clipform_get_form` | View a form and all its questions |
40
+ | `clipform_update_form` | Change the title or publish status |
41
+ | `clipform_delete_form` | Delete an unclaimed form |
42
+ | `clipform_add_question` | Add a question to an existing form |
43
+ | `clipform_update_question` | Update question text, type, config, or options |
44
+ | `clipform_delete_question` | Remove a question (logic chain auto-relinks) |
45
+
46
+ ## Question types
47
+
48
+ - **choice** - Multiple choice with single or multiple selection
49
+ - **open** - Free-form text, audio, or video responses
50
+ - **contact** - Collect name, email, phone, company, and more
51
+ - **button** - Simple button for acknowledgment or navigation
52
+ - **external_link** - Redirect to an external URL
53
+ - **end_screen** - Customizable completion screen
54
+
55
+ ## Example prompt
56
+
57
+ > Create a Clipform called "Customer Feedback" with a choice question asking "How would you rate our service?" with options Excellent, Good, Fair, Poor, then an open-ended question asking "Any additional comments?", and finish with an end screen saying "Thanks for your feedback!"
58
+
59
+ ## Configuration
60
+
61
+ By default the server connects to `https://api.clipform.io`. To point at a local dev server:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "clipform": {
67
+ "command": "npx",
68
+ "args": ["-y", "@clipform/mcp-server"],
69
+ "env": {
70
+ "SERVER_URL": "http://localhost:3003"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ## How it works
78
+
79
+ When you create a form, you get back an `edit_token` that authorizes all subsequent changes. The token works until the form is claimed by a user via the claim URL.
80
+
81
+ Forms are created with a start node and end screen automatically - you just add the questions in between.
82
+
83
+ ## Links
84
+
85
+ - [Clipform](https://clipform.io) - Create interactive video forms
86
+ - [Documentation](https://clipform.io/docs)
87
+ - [GitHub](https://github.com/andy-cb-smith/vid-master)
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Verifies that the inlined config constants in the MCP server
3
+ * stay in sync with the source of truth in @vid-master/config.
4
+ *
5
+ * This test runs in CI and will fail if the config package changes
6
+ * without the MCP server's inlined copy being updated.
7
+ */
8
+ import { describe, it, expect } from "vitest";
9
+ import { ANSWER_TYPES as SOURCE_ANSWER_TYPES, CONTACT_FIELDS as SOURCE_CONTACT_FIELDS, } from "@vid-master/config";
10
+ import { ANSWER_TYPES as INLINED_ANSWER_TYPES, CONTACT_FIELDS as INLINED_CONTACT_FIELDS, } from "../lib/config.js";
11
+ describe("config-sync", () => {
12
+ it("inlined ANSWER_TYPES match the active types from @vid-master/config", () => {
13
+ const sourceActiveTypes = Object.entries(SOURCE_ANSWER_TYPES)
14
+ .filter(([, def]) => def.is_active && !def.is_system)
15
+ .map(([type]) => type);
16
+ const inlinedTypes = Object.keys(INLINED_ANSWER_TYPES);
17
+ // Same set of type keys
18
+ expect(inlinedTypes.sort()).toEqual(sourceActiveTypes.sort());
19
+ // Each inlined type matches the source on the fields the MCP server uses
20
+ for (const type of inlinedTypes) {
21
+ const source = SOURCE_ANSWER_TYPES[type];
22
+ const inlined = INLINED_ANSWER_TYPES[type];
23
+ expect(inlined.label).toBe(source.label);
24
+ expect(inlined.description).toBe(source.description);
25
+ expect(inlined.is_active).toBe(source.is_active);
26
+ expect(inlined.is_system).toBe(source.is_system);
27
+ expect(inlined.has_options).toBe(source.has_options);
28
+ expect(inlined.config_schema).toEqual(source.config_schema);
29
+ }
30
+ });
31
+ it("inlined CONTACT_FIELDS match @vid-master/config", () => {
32
+ expect(INLINED_CONTACT_FIELDS).toEqual(SOURCE_CONTACT_FIELDS);
33
+ });
34
+ });
35
+ //# sourceMappingURL=config-sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-sync.test.js","sourceRoot":"","sources":["../../src/__tests__/config-sync.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,YAAY,IAAI,mBAAmB,EACnC,cAAc,IAAI,qBAAqB,GACxC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,YAAY,IAAI,oBAAoB,EACpC,cAAc,IAAI,sBAAsB,GACzC,MAAM,kBAAkB,CAAC;AAE1B,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC;aAC1D,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;aACnE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAEzB,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAEvD,wBAAwB;QACxB,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC;QAE9D,yEAAyE;QACzE,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,MAAM,MAAM,GAAI,mBAA2C,CAAC,IAAI,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAE3C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACrD,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACrD,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { createServer } from "./server.js";
4
+ const server = createServer();
5
+ const transport = new StdioServerTransport();
6
+ await server.connect(transport);
7
+ console.error("Clipform MCP Server running on stdio");
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAChC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ interface ApiOptions {
2
+ method?: string;
3
+ body?: Record<string, unknown>;
4
+ params?: Record<string, string>;
5
+ token?: string;
6
+ }
7
+ type ApiResult = {
8
+ ok: true;
9
+ data: Record<string, unknown>;
10
+ } | {
11
+ ok: false;
12
+ status: number;
13
+ error: string;
14
+ };
15
+ export declare function callApi(path: string, options?: ApiOptions): Promise<ApiResult>;
16
+ export declare function errorResult(message: string): {
17
+ content: {
18
+ type: "text";
19
+ text: string;
20
+ }[];
21
+ isError: boolean;
22
+ };
23
+ export declare function textResult(text: string): {
24
+ content: {
25
+ type: "text";
26
+ text: string;
27
+ }[];
28
+ };
29
+ export {};
@@ -0,0 +1,56 @@
1
+ const DASHBOARD_URL = process.env.SERVER_URL || process.env.DASHBOARD_URL || "https://api.clipform.io";
2
+ const API_VERSION = "v1";
3
+ export async function callApi(path, options = {}) {
4
+ const { method = "GET", body, params, token } = options;
5
+ let url = `${DASHBOARD_URL}/${API_VERSION}${path}`;
6
+ if (params) {
7
+ const searchParams = new URLSearchParams(params);
8
+ url += `?${searchParams.toString()}`;
9
+ }
10
+ const headers = {
11
+ "Content-Type": "application/json",
12
+ };
13
+ if (token) {
14
+ headers["Authorization"] = `Bearer ${token}`;
15
+ }
16
+ const fetchOptions = { method, headers };
17
+ if (body && method !== "GET") {
18
+ fetchOptions.body = JSON.stringify(body);
19
+ }
20
+ let response;
21
+ try {
22
+ response = await fetch(url, fetchOptions);
23
+ }
24
+ catch (err) {
25
+ return {
26
+ ok: false,
27
+ status: 0,
28
+ error: `Failed to connect to dashboard API at ${url}. Make sure the dashboard is running.\n\nError: ${err instanceof Error ? err.message : String(err)}`,
29
+ };
30
+ }
31
+ // 204 No Content — successful delete with no body
32
+ if (response.status === 204) {
33
+ return { ok: true, data: {} };
34
+ }
35
+ const data = await response.json();
36
+ if (!response.ok) {
37
+ return {
38
+ ok: false,
39
+ status: response.status,
40
+ error: data.error || `API error (${response.status})`,
41
+ };
42
+ }
43
+ return { ok: true, data };
44
+ }
45
+ export function errorResult(message) {
46
+ return {
47
+ content: [{ type: "text", text: message }],
48
+ isError: true,
49
+ };
50
+ }
51
+ export function textResult(text) {
52
+ return {
53
+ content: [{ type: "text", text }],
54
+ };
55
+ }
56
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CAAC;AACnF,MAAM,WAAW,GAAG,IAAI,CAAC;AAazB,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,UAAsB,EAAE;IAExB,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAExD,IAAI,GAAG,GAAG,GAAG,aAAa,IAAI,WAAW,GAAG,IAAI,EAAE,CAAC;IACnD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QACjD,GAAG,IAAI,IAAI,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,YAAY,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAEtD,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,yCAAyC,GAAG,mDAAmD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SACzJ,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAChC,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEnC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,cAAc,QAAQ,CAAC,MAAM,GAAG;SACtD,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACnD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;KAC3C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Inlined subset of @vid-master/config used by the MCP server.
3
+ * Only includes the 6 active, non-system question types and the fields
4
+ * the MCP server actually references.
5
+ *
6
+ * Keep in sync with packages/config/ — verified by config-sync.test.ts.
7
+ */
8
+ export interface AnswerTypeDefinition {
9
+ label: string;
10
+ description: string;
11
+ is_active: boolean;
12
+ is_system: boolean;
13
+ has_options: boolean;
14
+ config_schema: Record<string, unknown> | null;
15
+ }
16
+ export interface ContactFieldDefinition {
17
+ id: string;
18
+ label: string;
19
+ type: string;
20
+ placeholder: string;
21
+ order: number;
22
+ }
23
+ export declare const ANSWER_TYPES: Record<string, AnswerTypeDefinition>;
24
+ export declare const CONTACT_FIELDS: ContactFieldDefinition[];
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Inlined subset of @vid-master/config used by the MCP server.
3
+ * Only includes the 6 active, non-system question types and the fields
4
+ * the MCP server actually references.
5
+ *
6
+ * Keep in sync with packages/config/ — verified by config-sync.test.ts.
7
+ */
8
+ export const ANSWER_TYPES = {
9
+ choice: {
10
+ label: "Multiple Choice",
11
+ description: "Single or multiple choice questions with predefined options",
12
+ is_active: true,
13
+ is_system: false,
14
+ has_options: true,
15
+ config_schema: {
16
+ type: "object",
17
+ properties: {
18
+ choice: {
19
+ type: "object",
20
+ properties: {
21
+ enable_branching: {
22
+ type: "boolean",
23
+ label: "Enable branching logic",
24
+ default: true,
25
+ description: "Allow each option to have its own logic path. When disabled, all options share a single jump action.",
26
+ },
27
+ },
28
+ },
29
+ selection_mode: {
30
+ enum: ["single", "multiple"],
31
+ type: "string",
32
+ label: "Selection mode",
33
+ default: "single",
34
+ description: "Allow single or multiple selections",
35
+ },
36
+ allow_text_response: {
37
+ type: "boolean",
38
+ label: "Allow text response",
39
+ default: false,
40
+ description: "Allow free-text response in addition to options (single choice only)",
41
+ },
42
+ randomise_options: {
43
+ type: "boolean",
44
+ label: "Randomise options",
45
+ default: false,
46
+ description: "Show options in random order",
47
+ },
48
+ show_option_count: {
49
+ type: "boolean",
50
+ label: "Show option count",
51
+ default: false,
52
+ description: "Display number of options",
53
+ },
54
+ content_media_type: {
55
+ enum: ["upload", "recorded"],
56
+ type: "string",
57
+ label: "Content media type",
58
+ description: "How the question media was provided",
59
+ },
60
+ },
61
+ },
62
+ },
63
+ open: {
64
+ label: "Open Ended",
65
+ description: "Free-form text responses from users",
66
+ is_active: true,
67
+ is_system: false,
68
+ has_options: false,
69
+ config_schema: {
70
+ type: "object",
71
+ properties: {
72
+ formats: {
73
+ type: "array",
74
+ items: {
75
+ type: "object",
76
+ properties: {
77
+ format: {
78
+ enum: ["text", "audio", "video"],
79
+ type: "string",
80
+ },
81
+ order: { type: "number" },
82
+ },
83
+ },
84
+ label: "Allowed response formats",
85
+ description: "Which formats the user can respond with, in display order",
86
+ },
87
+ content_media_type: {
88
+ enum: ["upload", "recorded"],
89
+ type: "string",
90
+ label: "Content media type",
91
+ description: "How the question media was provided",
92
+ },
93
+ },
94
+ },
95
+ },
96
+ contact: {
97
+ label: "Contact Form",
98
+ description: "Collect standardized contact information (name, email, phone, company)",
99
+ is_active: true,
100
+ is_system: false,
101
+ has_options: false,
102
+ config_schema: {
103
+ type: "object",
104
+ properties: {
105
+ title: { type: "string" },
106
+ fields: {
107
+ type: "array",
108
+ items: {
109
+ type: "object",
110
+ required: ["id", "required"],
111
+ properties: {
112
+ id: { type: "string" },
113
+ type: {
114
+ enum: ["text", "textarea", "email", "tel", "url"],
115
+ type: "string",
116
+ },
117
+ label: { type: "string" },
118
+ order: { type: "number" },
119
+ required: { type: "boolean", default: true },
120
+ is_custom: { type: "boolean", default: false },
121
+ },
122
+ },
123
+ },
124
+ description: { type: "string" },
125
+ consent_items: {
126
+ type: "array",
127
+ items: {
128
+ type: "object",
129
+ properties: {
130
+ id: { type: "string" },
131
+ label: { type: "string" },
132
+ order: { type: "number" },
133
+ required: { type: "boolean" },
134
+ },
135
+ },
136
+ },
137
+ },
138
+ },
139
+ },
140
+ button: {
141
+ label: "Button",
142
+ description: "Simple button for acknowledgment or navigation",
143
+ is_active: true,
144
+ is_system: false,
145
+ has_options: true,
146
+ config_schema: {
147
+ type: "object",
148
+ properties: {
149
+ button_text: {
150
+ type: "string",
151
+ label: "Button text",
152
+ default: "Continue",
153
+ },
154
+ button_style: {
155
+ enum: ["primary", "secondary", "outline"],
156
+ type: "string",
157
+ label: "Button style",
158
+ default: "primary",
159
+ },
160
+ },
161
+ },
162
+ },
163
+ external_link: {
164
+ label: "External Link",
165
+ description: "Redirect users to an external URL",
166
+ is_active: true,
167
+ is_system: false,
168
+ has_options: false,
169
+ config_schema: {
170
+ type: "object",
171
+ properties: {
172
+ links: {
173
+ type: "array",
174
+ items: {
175
+ type: "object",
176
+ required: ["id", "url"],
177
+ properties: {
178
+ id: { type: "string" },
179
+ url: {
180
+ type: "string",
181
+ label: "URL",
182
+ placeholder: "https://example.com",
183
+ },
184
+ title: { type: "string", label: "Heading" },
185
+ description: { type: "string", label: "Description" },
186
+ order: { type: "number", label: "Sort order" },
187
+ },
188
+ },
189
+ label: "Links",
190
+ minItems: 1,
191
+ },
192
+ auto_redirect: {
193
+ type: "boolean",
194
+ label: "Auto-redirect",
195
+ default: false,
196
+ description: "Automatically redirect to the URL instead of showing a button",
197
+ },
198
+ },
199
+ },
200
+ },
201
+ end_screen: {
202
+ label: "End Screen",
203
+ description: "Final screen shown when form is completed",
204
+ is_active: true,
205
+ is_system: false,
206
+ has_options: false,
207
+ config_schema: {
208
+ type: "object",
209
+ properties: {
210
+ title: {
211
+ type: "string",
212
+ label: "Title",
213
+ default: "Thank you!",
214
+ description: "Heading shown on completion",
215
+ },
216
+ cta_url: {
217
+ type: "string",
218
+ label: "CTA button URL",
219
+ format: "uri",
220
+ description: "URL for CTA button",
221
+ placeholder: "https://example.com",
222
+ },
223
+ message: {
224
+ type: "string",
225
+ label: "Message",
226
+ default: "Your response has been submitted.",
227
+ description: "Message shown on completion",
228
+ },
229
+ cta_text: {
230
+ type: "string",
231
+ label: "CTA button text",
232
+ description: "Call-to-action button text",
233
+ placeholder: "Visit our website",
234
+ },
235
+ show_social_share: {
236
+ type: "boolean",
237
+ label: "Show social share",
238
+ default: false,
239
+ description: "Show social media share buttons",
240
+ },
241
+ show_restart_button: {
242
+ type: "boolean",
243
+ label: "Show restart button",
244
+ default: false,
245
+ },
246
+ restart_button_text: {
247
+ type: "string",
248
+ label: "Restart button text",
249
+ default: "Start over",
250
+ },
251
+ },
252
+ },
253
+ },
254
+ };
255
+ export const CONTACT_FIELDS = [
256
+ { id: "first_name", label: "First Name", type: "text", placeholder: "Enter first name", order: 1 },
257
+ { id: "last_name", label: "Last Name", type: "text", placeholder: "Enter last name", order: 2 },
258
+ { id: "email", label: "Email Address", type: "email", placeholder: "you@example.com", order: 3 },
259
+ { id: "phone", label: "Phone Number", type: "tel", placeholder: "(555) 123-4567", order: 4 },
260
+ { id: "company", label: "Company", type: "text", placeholder: "Company name", order: 5 },
261
+ { id: "job_title", label: "Job Title", type: "text", placeholder: "Your role", order: 6 },
262
+ { id: "website", label: "Website", type: "url", placeholder: "https://example.com", order: 7 },
263
+ { id: "linkedin", label: "LinkedIn Profile", type: "url", placeholder: "https://linkedin.com/in/username", order: 8 },
264
+ { id: "message", label: "Message", type: "textarea", placeholder: "Your message...", order: 9 },
265
+ ];
266
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ /** Active, non-system question types — the only types the MCP server exposes */
3
+ export declare const QUESTION_TYPES: [string, ...string[]];
4
+ /** Contact field IDs derived from the config package */
5
+ export declare const CONTACT_FIELD_IDS: string[];
6
+ /** Per-type description including config hints, auto-generated from ANSWER_TYPES */
7
+ export declare const QUESTION_TYPES_DESCRIPTION: string;
8
+ /** Aggregate config description for Zod .describe() on the config field */
9
+ export declare const CONFIG_DESCRIPTION: string;
10
+ export declare const QuestionSchema: z.ZodObject<{
11
+ type: z.ZodEnum<[string, ...string[]]>;
12
+ prompt: z.ZodString;
13
+ required: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
14
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
15
+ options: z.ZodOptional<z.ZodArray<z.ZodObject<{
16
+ content: z.ZodString;
17
+ }, "strict", z.ZodTypeAny, {
18
+ content: string;
19
+ }, {
20
+ content: string;
21
+ }>, "many">>;
22
+ }, "strict", z.ZodTypeAny, {
23
+ type: string;
24
+ required: boolean;
25
+ prompt: string;
26
+ options?: {
27
+ content: string;
28
+ }[] | undefined;
29
+ config?: Record<string, unknown> | undefined;
30
+ }, {
31
+ type: string;
32
+ prompt: string;
33
+ required?: boolean | undefined;
34
+ options?: {
35
+ content: string;
36
+ }[] | undefined;
37
+ config?: Record<string, unknown> | undefined;
38
+ }>;
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ import { ANSWER_TYPES, CONTACT_FIELDS } from "./config.js";
3
+ // --- Derived constants (zero hardcoded type lists) ---
4
+ /** Active, non-system question types — the only types the MCP server exposes */
5
+ export const QUESTION_TYPES = Object.entries(ANSWER_TYPES)
6
+ .filter(([, def]) => def.is_active && !def.is_system)
7
+ .map(([type]) => type); // tuple for z.enum
8
+ /** Contact field IDs derived from the config package */
9
+ export const CONTACT_FIELD_IDS = CONTACT_FIELDS.map((f) => f.id);
10
+ // --- Auto-generated config descriptions ---
11
+ /**
12
+ * Walk a JSON-Schema-style config_schema and produce a concise one-line summary.
13
+ * Handles primitives, enums, nested objects (1 level), and arrays with item shapes.
14
+ */
15
+ function generateConfigSummary(configSchema, typeKey) {
16
+ if (!configSchema?.properties)
17
+ return "";
18
+ const parts = [];
19
+ for (const [key, prop] of Object.entries(configSchema.properties)) {
20
+ // Skip internal-only fields the LLM shouldn't set
21
+ if (key === "content_media_type")
22
+ continue;
23
+ if (prop.enum) {
24
+ const vals = prop.enum.map((v) => `"${v}"`).join("|");
25
+ let s = `${key} (${vals}`;
26
+ if (prop.default !== undefined)
27
+ s += `, default: "${prop.default}"`;
28
+ s += ")";
29
+ parts.push(s);
30
+ }
31
+ else if (prop.type === "boolean") {
32
+ let s = `${key} (boolean`;
33
+ if (prop.default !== undefined)
34
+ s += `, default: ${prop.default}`;
35
+ s += ")";
36
+ parts.push(s);
37
+ }
38
+ else if (prop.type === "number") {
39
+ let s = `${key} (number`;
40
+ if (prop.default !== undefined)
41
+ s += `, default: ${prop.default}`;
42
+ s += ")";
43
+ parts.push(s);
44
+ }
45
+ else if (prop.type === "string") {
46
+ let s = `${key} (string`;
47
+ if (prop.default !== undefined)
48
+ s += `, default: "${prop.default}"`;
49
+ s += ")";
50
+ parts.push(s);
51
+ }
52
+ else if (prop.type === "array" && prop.items?.properties) {
53
+ // Array of objects — show item shape
54
+ const itemKeys = Object.keys(prop.items.properties).join(", ");
55
+ parts.push(`${key} (array of {${itemKeys}})`);
56
+ }
57
+ else if (prop.type === "array" && prop.items?.enum) {
58
+ const vals = prop.items.enum.map((v) => `"${v}"`).join("|");
59
+ parts.push(`${key} (array of ${vals})`);
60
+ }
61
+ else if (prop.type === "object" && prop.properties) {
62
+ // Nested object — summarize child keys
63
+ const childKeys = Object.keys(prop.properties).join(", ");
64
+ parts.push(`${key} ({${childKeys}})`);
65
+ }
66
+ }
67
+ // Special case: for contact type, append available field IDs
68
+ if (typeKey === "contact") {
69
+ parts.push(`Available field IDs: ${CONTACT_FIELD_IDS.join(", ")}`);
70
+ }
71
+ return parts.length > 0 ? `Config: ${parts.join(", ")}` : "";
72
+ }
73
+ /** Per-type description including config hints, auto-generated from ANSWER_TYPES */
74
+ export const QUESTION_TYPES_DESCRIPTION = (() => {
75
+ const lines = ["Question types:"];
76
+ for (const type of QUESTION_TYPES) {
77
+ const def = ANSWER_TYPES[type];
78
+ let line = `- ${type}: ${def.description || def.label}`;
79
+ if (def.has_options) {
80
+ line += " (supports options array)";
81
+ }
82
+ const configHint = generateConfigSummary(def.config_schema, type);
83
+ if (configHint) {
84
+ line += `. ${configHint}`;
85
+ }
86
+ lines.push(line);
87
+ }
88
+ return lines.join("\n");
89
+ })();
90
+ /** Aggregate config description for Zod .describe() on the config field */
91
+ export const CONFIG_DESCRIPTION = (() => {
92
+ const lines = ["Type-specific configuration. Per-type keys:"];
93
+ for (const type of QUESTION_TYPES) {
94
+ const def = ANSWER_TYPES[type];
95
+ const summary = generateConfigSummary(def.config_schema, type);
96
+ if (summary) {
97
+ lines.push(` ${type}: ${summary}`);
98
+ }
99
+ }
100
+ return lines.join("\n");
101
+ })();
102
+ // --- Zod schemas ---
103
+ export const QuestionSchema = z
104
+ .object({
105
+ type: z.enum(QUESTION_TYPES),
106
+ prompt: z.string().describe("The question text shown to the respondent"),
107
+ required: z.boolean().optional().default(true),
108
+ config: z
109
+ .record(z.unknown())
110
+ .optional()
111
+ .describe(CONFIG_DESCRIPTION),
112
+ options: z
113
+ .array(z.object({ content: z.string() }).strict())
114
+ .optional()
115
+ .describe("Answer options for choice/button questions"),
116
+ })
117
+ .strict();
118
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function createServer(): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,23 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { registerCreateFormTool } from "./tools/create-form.js";
3
+ import { registerGetFormTool } from "./tools/get-form.js";
4
+ import { registerUpdateFormTool } from "./tools/update-form.js";
5
+ import { registerDeleteFormTool } from "./tools/delete-form.js";
6
+ import { registerAddQuestionTool } from "./tools/add-question.js";
7
+ import { registerUpdateQuestionTool } from "./tools/update-question.js";
8
+ import { registerDeleteQuestionTool } from "./tools/delete-question.js";
9
+ export function createServer() {
10
+ const server = new McpServer({
11
+ name: "clipform-mcp-server",
12
+ version: "1.0.0",
13
+ });
14
+ registerCreateFormTool(server);
15
+ registerGetFormTool(server);
16
+ registerUpdateFormTool(server);
17
+ registerDeleteFormTool(server);
18
+ registerAddQuestionTool(server);
19
+ registerUpdateQuestionTool(server);
20
+ registerDeleteQuestionTool(server);
21
+ return server;
22
+ }
23
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerAddQuestionTool(server: McpServer): void;
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { QuestionSchema, QUESTION_TYPES_DESCRIPTION } from "../lib/schemas.js";
3
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
4
+ export function registerAddQuestionTool(server) {
5
+ server.registerTool("clipform_add_question", {
6
+ title: "Add Question",
7
+ description: `Add a new question to an existing form. By default, the question is inserted before the end screen (appended to the end of the flow). Use after_question_id to insert at a specific position.
8
+
9
+ ${QUESTION_TYPES_DESCRIPTION}
10
+
11
+ All type definitions and config schemas are derived from @vid-master/config (answer-types).`,
12
+ inputSchema: {
13
+ form_id: z.string().describe("The form ID"),
14
+ edit_token: z.string().describe("The edit token"),
15
+ question: QuestionSchema.describe("The question to add"),
16
+ after_question_id: z
17
+ .string()
18
+ .optional()
19
+ .describe("Insert after this question ID. Omit to append before the end screen."),
20
+ },
21
+ annotations: {
22
+ readOnlyHint: false,
23
+ destructiveHint: false,
24
+ idempotentHint: false,
25
+ openWorldHint: true,
26
+ },
27
+ }, async ({ form_id, edit_token, question, after_question_id }) => {
28
+ const body = { question };
29
+ if (after_question_id)
30
+ body.after_question_id = after_question_id;
31
+ const result = await callApi(`/forms/${form_id}/questions`, {
32
+ method: "POST",
33
+ body,
34
+ token: edit_token,
35
+ });
36
+ if (!result.ok) {
37
+ return errorResult(result.error);
38
+ }
39
+ return textResult([
40
+ `Question added successfully!`,
41
+ `Question ID: ${result.data.question_id}`,
42
+ `Type: ${question.type}`,
43
+ `Prompt: ${question.prompt}`,
44
+ ].join("\n"));
45
+ });
46
+ }
47
+ //# sourceMappingURL=add-question.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-question.js","sourceRoot":"","sources":["../../src/tools/add-question.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE;;EAEjB,0BAA0B;;4FAEgE;QACtF,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACjD,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YACxD,iBAAiB,EAAE,CAAC;iBACjB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,sEAAsE,CACvE;SACJ;QACD,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,iBAAiB,EAAE,EAAE,EAAE;QAC7D,MAAM,IAAI,GAA4B,EAAE,QAAQ,EAAE,CAAC;QACnD,IAAI,iBAAiB;YAAE,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAElE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,OAAO,YAAY,EAAE;YAC1D,MAAM,EAAE,MAAM;YACd,IAAI;YACJ,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,UAAU,CACf;YACE,8BAA8B;YAC9B,gBAAgB,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;YACzC,SAAS,QAAQ,CAAC,IAAI,EAAE;YACxB,WAAW,QAAQ,CAAC,MAAM,EAAE;SAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCreateFormTool(server: McpServer): void;
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ import { QuestionSchema, QUESTION_TYPES_DESCRIPTION, } from "../lib/schemas.js";
3
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
4
+ export function registerCreateFormTool(server) {
5
+ server.registerTool("clipform_create_form", {
6
+ title: "Create Clipform",
7
+ description: `Create a new Clipform (interactive video-style form). Returns a claim URL and an edit_token for further modifications.
8
+
9
+ ${QUESTION_TYPES_DESCRIPTION}
10
+
11
+ All type definitions and config schemas are derived from @vid-master/config (answer-types). Refer to the config descriptions above for the correct keys and shapes.
12
+
13
+ Example: A form that asks a question, collects contact info, then finishes:
14
+ {
15
+ title: "Quick Survey",
16
+ questions: [
17
+ { type: "open", prompt: "What's your biggest challenge?" },
18
+ { type: "contact", prompt: "Leave your details", config: { fields: [{ id: "first_name", required: true }, { id: "email", required: true }] } },
19
+ { type: "end_screen", prompt: "Thanks for your response!" }
20
+ ]
21
+ }`,
22
+ inputSchema: {
23
+ title: z.string().describe("Form title"),
24
+ questions: z
25
+ .array(QuestionSchema)
26
+ .min(1)
27
+ .describe("Ordered list of questions/steps"),
28
+ },
29
+ annotations: {
30
+ readOnlyHint: false,
31
+ destructiveHint: false,
32
+ idempotentHint: false,
33
+ openWorldHint: true,
34
+ },
35
+ }, async ({ title, questions }) => {
36
+ // 1. Create bare scaffold
37
+ const createResult = await callApi("/forms", {
38
+ method: "POST",
39
+ body: { title },
40
+ });
41
+ if (!createResult.ok) {
42
+ return errorResult(createResult.error);
43
+ }
44
+ const { data } = createResult;
45
+ const formId = data.form_id;
46
+ const editToken = data.edit_token;
47
+ // 2. Add each question via the add-question endpoint
48
+ for (const q of questions) {
49
+ // If it's an end_screen, update the default one instead of adding another
50
+ if (q.type === "end_screen") {
51
+ // The scaffold already has an end_screen — find it and update its prompt
52
+ const getResult = await callApi(`/forms/${formId}`, {
53
+ method: "GET",
54
+ token: editToken,
55
+ });
56
+ if (getResult.ok) {
57
+ const questions_list = getResult.data.questions;
58
+ const endScreen = questions_list?.find((qq) => qq.type === "end_screen");
59
+ if (endScreen) {
60
+ await callApi(`/forms/${formId}/questions/${endScreen.id}`, {
61
+ method: "PATCH",
62
+ body: { prompt: q.prompt, ...(q.config ? { config: q.config } : {}) },
63
+ token: editToken,
64
+ });
65
+ continue;
66
+ }
67
+ }
68
+ // Fallback: add it normally if we couldn't find the default
69
+ }
70
+ const addResult = await callApi(`/forms/${formId}/questions`, {
71
+ method: "POST",
72
+ body: { question: q },
73
+ token: editToken,
74
+ });
75
+ if (!addResult.ok) {
76
+ return errorResult(`Failed to add question "${q.prompt}": ${addResult.error}`);
77
+ }
78
+ }
79
+ // 3. Publish the form
80
+ await callApi(`/forms/${formId}`, {
81
+ method: "PATCH",
82
+ body: { is_published: true },
83
+ token: editToken,
84
+ });
85
+ return textResult([
86
+ `Form created successfully!`,
87
+ ``,
88
+ `Title: ${title}`,
89
+ `Questions: ${questions.length}`,
90
+ `Form ID: ${formId}`,
91
+ `Share ID: ${data.share_id}`,
92
+ `Edit Token: ${editToken}`,
93
+ ``,
94
+ `Claim URL: ${data.claim_url}`,
95
+ ``,
96
+ `IMPORTANT: Save the edit_token above — you need it for all subsequent operations (clipform_get_form, clipform_update_form, clipform_add_question, clipform_update_question, clipform_delete_question, clipform_delete_form).`,
97
+ `The token expires when the user claims the form via the claim URL.`,
98
+ ].join("\n"));
99
+ });
100
+ }
101
+ //# sourceMappingURL=create-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-form.js","sourceRoot":"","sources":["../../src/tools/create-form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerDeleteFormTool(server: McpServer): void;
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
3
+ export function registerDeleteFormTool(server) {
4
+ server.registerTool("clipform_delete_form", {
5
+ title: "Delete Clipform",
6
+ description: `Permanently delete an unclaimed form and all its questions. This cannot be undone. Only works on forms that haven't been claimed yet.`,
7
+ inputSchema: {
8
+ form_id: z.string().describe("The form ID to delete"),
9
+ edit_token: z.string().describe("The edit token"),
10
+ },
11
+ annotations: {
12
+ readOnlyHint: false,
13
+ destructiveHint: true,
14
+ idempotentHint: false,
15
+ openWorldHint: true,
16
+ },
17
+ }, async ({ form_id, edit_token }) => {
18
+ const result = await callApi(`/forms/${form_id}`, {
19
+ method: "DELETE",
20
+ token: edit_token,
21
+ });
22
+ if (!result.ok) {
23
+ return errorResult(result.error);
24
+ }
25
+ return textResult(`Form ${form_id} has been permanently deleted.`);
26
+ });
27
+ }
28
+ //# sourceMappingURL=delete-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-form.js","sourceRoot":"","sources":["../../src/tools/delete-form.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,uIAAuI;QACpJ,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACrD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;SAClD;QACD,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,OAAO,EAAE,EAAE;YAChD,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,UAAU,CAAC,QAAQ,OAAO,gCAAgC,CAAC,CAAC;IACrE,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerDeleteQuestionTool(server: McpServer): void;
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
3
+ export function registerDeleteQuestionTool(server) {
4
+ server.registerTool("clipform_delete_question", {
5
+ title: "Delete Question",
6
+ description: `Delete a question from a form. The logic chain is automatically re-linked (the previous question will point to the next one). Cannot delete the start node or the last end screen.`,
7
+ inputSchema: {
8
+ form_id: z.string().describe("The form ID"),
9
+ edit_token: z.string().describe("The edit token"),
10
+ question_id: z.string().describe("The question ID to delete"),
11
+ },
12
+ annotations: {
13
+ readOnlyHint: false,
14
+ destructiveHint: true,
15
+ idempotentHint: false,
16
+ openWorldHint: true,
17
+ },
18
+ }, async ({ form_id, edit_token, question_id }) => {
19
+ const result = await callApi(`/forms/${form_id}/questions/${question_id}`, {
20
+ method: "DELETE",
21
+ token: edit_token,
22
+ });
23
+ if (!result.ok) {
24
+ return errorResult(result.error);
25
+ }
26
+ return textResult(`Question ${question_id} deleted. The logic chain has been re-linked.`);
27
+ });
28
+ }
29
+ //# sourceMappingURL=delete-question.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-question.js","sourceRoot":"","sources":["../../src/tools/delete-question.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,oLAAoL;QACjM,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;SAC9D;QACD,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,UAAU,OAAO,cAAc,WAAW,EAAE,EAC5C;YACE,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,UAAU;SAClB,CACF,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,UAAU,CACf,YAAY,WAAW,+CAA+C,CACvE,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetFormTool(server: McpServer): void;
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
3
+ export function registerGetFormTool(server) {
4
+ server.registerTool("clipform_get_form", {
5
+ title: "Get Clipform",
6
+ description: `Retrieve a form's details including all questions in sequential order. Use this to see the current state of a form before making changes.`,
7
+ inputSchema: {
8
+ form_id: z.string().describe("The form ID returned by clipform_create_form"),
9
+ edit_token: z
10
+ .string()
11
+ .describe("The edit token returned by clipform_create_form"),
12
+ },
13
+ annotations: {
14
+ readOnlyHint: true,
15
+ destructiveHint: false,
16
+ idempotentHint: true,
17
+ openWorldHint: true,
18
+ },
19
+ }, async ({ form_id, edit_token }) => {
20
+ const result = await callApi(`/forms/${form_id}`, {
21
+ token: edit_token,
22
+ });
23
+ if (!result.ok) {
24
+ return errorResult(result.error);
25
+ }
26
+ const { data } = result;
27
+ const questions = data.questions;
28
+ const lines = [
29
+ `Form: ${data.title}`,
30
+ `Form ID: ${data.form_id}`,
31
+ `Published: ${data.is_published}`,
32
+ ``,
33
+ `Questions (in order):`,
34
+ ];
35
+ for (let i = 0; i < questions.length; i++) {
36
+ const q = questions[i];
37
+ lines.push(` ${i + 1}. [${q.type}] ${q.prompt || "(no prompt)"}`);
38
+ lines.push(` ID: ${q.id}`);
39
+ if (q.required)
40
+ lines.push(` Required: yes`);
41
+ if (q.config &&
42
+ Object.keys(q.config).length > 0) {
43
+ lines.push(` Config: ${JSON.stringify(q.config)}`);
44
+ }
45
+ if (q.options && q.options.length > 0) {
46
+ lines.push(` Options: ${q.options.map((o) => o.content).join(", ")}`);
47
+ }
48
+ }
49
+ return textResult(lines.join("\n"));
50
+ });
51
+ }
52
+ //# sourceMappingURL=get-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-form.js","sourceRoot":"","sources":["../../src/tools/get-form.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,2IAA2I;QACxJ,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;YAC5E,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,iDAAiD,CAAC;SAC/D;QACD,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,OAAO,EAAE,EAAE;YAChD,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,SAQrB,CAAC;QAEH,MAAM,KAAK,GAAa;YACtB,SAAS,IAAI,CAAC,KAAK,EAAE;YACrB,YAAY,IAAI,CAAC,OAAO,EAAE;YAC1B,cAAc,IAAI,CAAC,YAAY,EAAE;YACjC,EAAE;YACF,uBAAuB;SACxB,CAAC;QAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC;YACnE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,QAAQ;gBAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACjD,IACE,CAAC,CAAC,MAAM;gBACR,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAChC,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CACR,iBAAiB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9D,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerUpdateFormTool(server: McpServer): void;
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
3
+ export function registerUpdateFormTool(server) {
4
+ server.registerTool("clipform_update_form", {
5
+ title: "Update Clipform",
6
+ description: `Update a form's title or publish status. Use clipform_get_form first to see current values.`,
7
+ inputSchema: {
8
+ form_id: z.string().describe("The form ID"),
9
+ edit_token: z.string().describe("The edit token"),
10
+ title: z.string().optional().describe("New form title"),
11
+ is_published: z
12
+ .boolean()
13
+ .optional()
14
+ .describe("Set to true to publish, false to unpublish"),
15
+ },
16
+ annotations: {
17
+ readOnlyHint: false,
18
+ destructiveHint: false,
19
+ idempotentHint: true,
20
+ openWorldHint: true,
21
+ },
22
+ }, async ({ form_id, edit_token, title, is_published }) => {
23
+ const body = {};
24
+ if (title !== undefined)
25
+ body.title = title;
26
+ if (is_published !== undefined)
27
+ body.is_published = is_published;
28
+ const result = await callApi(`/forms/${form_id}`, {
29
+ method: "PATCH",
30
+ body,
31
+ token: edit_token,
32
+ });
33
+ if (!result.ok) {
34
+ return errorResult(result.error);
35
+ }
36
+ const updates = [];
37
+ if (title !== undefined)
38
+ updates.push(`Title → "${title}"`);
39
+ if (is_published !== undefined)
40
+ updates.push(`Published → ${is_published}`);
41
+ return textResult(`Form updated:\n${updates.join("\n")}`);
42
+ });
43
+ }
44
+ //# sourceMappingURL=update-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-form.js","sourceRoot":"","sources":["../../src/tools/update-form.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,6FAA6F;QAC1G,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACjD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACvD,YAAY,EAAE,CAAC;iBACZ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,4CAA4C,CAAC;SAC1D;QACD,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE;QACrD,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAC5C,IAAI,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QAEjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,OAAO,EAAE,EAAE;YAChD,MAAM,EAAE,OAAO;YACf,IAAI;YACJ,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,YAAY,KAAK,GAAG,CAAC,CAAC;QAC5D,IAAI,YAAY,KAAK,SAAS;YAC5B,OAAO,CAAC,IAAI,CAAC,eAAe,YAAY,EAAE,CAAC,CAAC;QAE9C,OAAO,UAAU,CAAC,kBAAkB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerUpdateQuestionTool(server: McpServer): void;
@@ -0,0 +1,63 @@
1
+ import { z } from "zod";
2
+ import { QUESTION_TYPES, CONFIG_DESCRIPTION } from "../lib/schemas.js";
3
+ import { callApi, errorResult, textResult } from "../lib/api-client.js";
4
+ export function registerUpdateQuestionTool(server) {
5
+ server.registerTool("clipform_update_question", {
6
+ title: "Update Question",
7
+ description: `Update an existing question's text, type, config, or options. Use clipform_get_form first to find the question ID. Does not change the question's position in the flow. All type definitions and config schemas are derived from @vid-master/config (answer-types).`,
8
+ inputSchema: {
9
+ form_id: z.string().describe("The form ID"),
10
+ edit_token: z.string().describe("The edit token"),
11
+ question_id: z.string().describe("The question ID to update"),
12
+ prompt: z.string().optional().describe("New question text"),
13
+ type: z
14
+ .enum(QUESTION_TYPES)
15
+ .optional()
16
+ .describe("Change the question type"),
17
+ required: z.boolean().optional().describe("Whether an answer is required"),
18
+ config: z
19
+ .record(z.unknown())
20
+ .optional()
21
+ .describe(CONFIG_DESCRIPTION),
22
+ options: z
23
+ .array(z.object({ content: z.string() }).strict())
24
+ .optional()
25
+ .describe("Replace all options (for choice questions). Omit to keep existing options."),
26
+ },
27
+ annotations: {
28
+ readOnlyHint: false,
29
+ destructiveHint: false,
30
+ idempotentHint: true,
31
+ openWorldHint: true,
32
+ },
33
+ }, async ({ form_id, edit_token, question_id, prompt, type, required, config, options, }) => {
34
+ const body = {};
35
+ if (prompt !== undefined)
36
+ body.prompt = prompt;
37
+ if (type !== undefined)
38
+ body.type = type;
39
+ if (required !== undefined)
40
+ body.required = required;
41
+ if (config !== undefined)
42
+ body.config = config;
43
+ if (options !== undefined)
44
+ body.options = options;
45
+ const result = await callApi(`/forms/${form_id}/questions/${question_id}`, { method: "PATCH", body, token: edit_token });
46
+ if (!result.ok) {
47
+ return errorResult(result.error);
48
+ }
49
+ const updates = [];
50
+ if (prompt !== undefined)
51
+ updates.push(`Prompt → "${prompt}"`);
52
+ if (type !== undefined)
53
+ updates.push(`Type → ${type}`);
54
+ if (required !== undefined)
55
+ updates.push(`Required → ${required}`);
56
+ if (config !== undefined)
57
+ updates.push(`Config updated`);
58
+ if (options !== undefined)
59
+ updates.push(`Options → ${options.map((o) => o.content).join(", ")}`);
60
+ return textResult(`Question ${question_id} updated:\n${updates.join("\n")}`);
61
+ });
62
+ }
63
+ //# sourceMappingURL=update-question.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-question.js","sourceRoot":"","sources":["../../src/tools/update-question.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@clipform/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for building and managing Clipform video forms",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/andy-cb-smith/vid-master",
9
+ "directory": "packages/mcp-server"
10
+ },
11
+ "type": "module",
12
+ "main": "dist/index.js",
13
+ "bin": {
14
+ "mcp-server-clipform": "dist/index.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "start": "node dist/index.js",
21
+ "dev": "tsx watch src/index.ts",
22
+ "build": "tsc",
23
+ "test": "vitest run",
24
+ "prepare": "npm run build"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.12.1",
28
+ "zod": "^3.24.4"
29
+ },
30
+ "devDependencies": {
31
+ "@vid-master/config": "*",
32
+ "tsx": "^4.19.4",
33
+ "typescript": "^5.2.2",
34
+ "vitest": "^3.1.1"
35
+ }
36
+ }