@clipform/mcp-server 2.4.0 → 2.5.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 +5 -4
- package/dist/auth-context.d.ts +25 -1
- package/dist/auth-context.js +5 -1
- package/dist/{chunk-5LV55T7F.js → chunk-3BYQLPTT.js} +162 -18
- package/dist/chunk-3BYQLPTT.js.map +1 -0
- package/dist/{chunk-NMIUCFJT.js → chunk-3OX54HSJ.js} +509 -169
- package/dist/chunk-3OX54HSJ.js.map +1 -0
- package/dist/{chunk-IDOG3ZTF.js → chunk-6EHOUAPE.js} +3 -3
- package/dist/{chunk-IDOG3ZTF.js.map → chunk-6EHOUAPE.js.map} +1 -1
- package/dist/{chunk-V4L5RQWK.js → chunk-ENHVQDSI.js} +5 -1
- package/dist/chunk-ENHVQDSI.js.map +1 -0
- package/dist/{chunk-4G4MF3ME.js → chunk-NYMC63ZK.js} +7 -7
- package/dist/{chunk-4G4MF3ME.js.map → chunk-NYMC63ZK.js.map} +1 -1
- package/dist/index.js +27 -12
- package/dist/index.js.map +1 -1
- package/dist/prompts.js +3 -3
- package/dist/resources.js +3 -3
- package/dist/server.d.ts +11 -0
- package/dist/server.js +5 -5
- package/package.json +11 -4
- package/dist/chunk-5LV55T7F.js.map +0 -1
- package/dist/chunk-NMIUCFJT.js.map +0 -1
- package/dist/chunk-V4L5RQWK.js.map +0 -1
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getWorkflowText,
|
|
7
7
|
objectType,
|
|
8
8
|
registerPrompts
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-NYMC63ZK.js";
|
|
10
10
|
import {
|
|
11
11
|
GUIDE_TYPES,
|
|
12
12
|
QUIZ_VARIANTS,
|
|
@@ -14,30 +14,32 @@ import {
|
|
|
14
14
|
getGuideUri,
|
|
15
15
|
guideFallbackText,
|
|
16
16
|
registerResources
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-6EHOUAPE.js";
|
|
18
18
|
import {
|
|
19
19
|
ACTIVE_NODE_TYPES,
|
|
20
|
-
BUSINESS,
|
|
21
20
|
CONTACT_FIELDS,
|
|
22
21
|
FORM_TYPE_ALIASES,
|
|
23
22
|
KEN_BURNS_EFFECTS,
|
|
24
23
|
KEN_BURNS_FIT_MODES,
|
|
25
24
|
KEN_BURNS_PRESETS,
|
|
26
25
|
MCP_TOOL_TIERS,
|
|
26
|
+
MCP_TOOL_TOOLSETS,
|
|
27
27
|
NODE_TYPES,
|
|
28
28
|
NON_COUNTABLE_TYPES,
|
|
29
29
|
SLIDESHOW_TRANSITIONS,
|
|
30
|
+
bfsOrder,
|
|
30
31
|
callApi,
|
|
32
|
+
defaultTarget,
|
|
31
33
|
errorResult,
|
|
32
34
|
exposedCompositionIds,
|
|
33
35
|
resolveFormType,
|
|
34
36
|
structuredResult,
|
|
35
37
|
textResult
|
|
36
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-3BYQLPTT.js";
|
|
37
39
|
import {
|
|
38
40
|
getMcpAuth,
|
|
39
41
|
runWithMcpTool
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ENHVQDSI.js";
|
|
41
43
|
import {
|
|
42
44
|
__commonJS,
|
|
43
45
|
__export,
|
|
@@ -10749,7 +10751,7 @@ function object(shape, params) {
|
|
|
10749
10751
|
return new ZodMiniObject(def);
|
|
10750
10752
|
}
|
|
10751
10753
|
|
|
10752
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
10754
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
10753
10755
|
function isZ4Schema(s) {
|
|
10754
10756
|
const schema = s;
|
|
10755
10757
|
return !!schema._zod;
|
|
@@ -10833,17 +10835,33 @@ function normalizeObjectSchema(schema) {
|
|
|
10833
10835
|
}
|
|
10834
10836
|
return void 0;
|
|
10835
10837
|
}
|
|
10838
|
+
function getDotPath(path) {
|
|
10839
|
+
if (path.length === 0) {
|
|
10840
|
+
return "object root";
|
|
10841
|
+
}
|
|
10842
|
+
return path.reduce((acc, seg, index) => {
|
|
10843
|
+
if (index === 0) {
|
|
10844
|
+
return String(seg);
|
|
10845
|
+
}
|
|
10846
|
+
if (typeof seg === "number") {
|
|
10847
|
+
return `${acc}[${seg}]`;
|
|
10848
|
+
}
|
|
10849
|
+
return `${acc}.${seg}`;
|
|
10850
|
+
}, "");
|
|
10851
|
+
}
|
|
10836
10852
|
function getParseErrorMessage(error2) {
|
|
10837
10853
|
if (error2 && typeof error2 === "object") {
|
|
10854
|
+
if ("issues" in error2 && Array.isArray(error2.issues) && error2.issues.length > 0) {
|
|
10855
|
+
return error2.issues.map((i) => {
|
|
10856
|
+
if (!i.path?.length) {
|
|
10857
|
+
return i.message;
|
|
10858
|
+
}
|
|
10859
|
+
return `${i.message} at ${getDotPath(i.path)}`;
|
|
10860
|
+
}).join("\n");
|
|
10861
|
+
}
|
|
10838
10862
|
if ("message" in error2 && typeof error2.message === "string") {
|
|
10839
10863
|
return error2.message;
|
|
10840
10864
|
}
|
|
10841
|
-
if ("issues" in error2 && Array.isArray(error2.issues) && error2.issues.length > 0) {
|
|
10842
|
-
const firstIssue = error2.issues[0];
|
|
10843
|
-
if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
|
|
10844
|
-
return String(firstIssue.message);
|
|
10845
|
-
}
|
|
10846
|
-
}
|
|
10847
10865
|
try {
|
|
10848
10866
|
return JSON.stringify(error2);
|
|
10849
10867
|
} catch {
|
|
@@ -11588,7 +11606,7 @@ function preprocess(fn, schema) {
|
|
|
11588
11606
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/external.js
|
|
11589
11607
|
config(en_default());
|
|
11590
11608
|
|
|
11591
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
11609
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
11592
11610
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
11593
11611
|
var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
|
|
11594
11612
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
@@ -13119,7 +13137,7 @@ var UrlElicitationRequiredError = class extends McpError {
|
|
|
13119
13137
|
}
|
|
13120
13138
|
};
|
|
13121
13139
|
|
|
13122
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
13140
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
13123
13141
|
function isTerminal(status) {
|
|
13124
13142
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
13125
13143
|
}
|
|
@@ -14408,7 +14426,7 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
14408
14426
|
return combined;
|
|
14409
14427
|
};
|
|
14410
14428
|
|
|
14411
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
14429
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
14412
14430
|
function mapMiniTarget(t) {
|
|
14413
14431
|
if (!t)
|
|
14414
14432
|
return "draft-7";
|
|
@@ -14450,7 +14468,7 @@ function parseWithCompat(schema, data) {
|
|
|
14450
14468
|
return result.data;
|
|
14451
14469
|
}
|
|
14452
14470
|
|
|
14453
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
14471
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
14454
14472
|
var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
|
|
14455
14473
|
var Protocol = class {
|
|
14456
14474
|
constructor(_options) {
|
|
@@ -15404,7 +15422,7 @@ function mergeCapabilities(base, additional) {
|
|
|
15404
15422
|
return result;
|
|
15405
15423
|
}
|
|
15406
15424
|
|
|
15407
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
15425
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
|
|
15408
15426
|
var import_ajv = __toESM(require_ajv(), 1);
|
|
15409
15427
|
var import_ajv_formats = __toESM(require_dist(), 1);
|
|
15410
15428
|
function createDefaultAjvInstance() {
|
|
@@ -15472,7 +15490,7 @@ var AjvJsonSchemaValidator = class {
|
|
|
15472
15490
|
}
|
|
15473
15491
|
};
|
|
15474
15492
|
|
|
15475
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
15493
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
15476
15494
|
var ExperimentalServerTasks = class {
|
|
15477
15495
|
constructor(_server) {
|
|
15478
15496
|
this._server = _server;
|
|
@@ -15685,7 +15703,7 @@ var ExperimentalServerTasks = class {
|
|
|
15685
15703
|
}
|
|
15686
15704
|
};
|
|
15687
15705
|
|
|
15688
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
15706
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
|
|
15689
15707
|
function assertToolsCallTaskCapability(requests, method, entityName) {
|
|
15690
15708
|
if (!requests) {
|
|
15691
15709
|
throw new Error(`${entityName} does not support task creation (required for ${method})`);
|
|
@@ -15720,7 +15738,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
|
|
|
15720
15738
|
}
|
|
15721
15739
|
}
|
|
15722
15740
|
|
|
15723
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
15741
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
15724
15742
|
var Server = class extends Protocol {
|
|
15725
15743
|
/**
|
|
15726
15744
|
* Initializes this server with the given name and version information.
|
|
@@ -15786,16 +15804,7 @@ var Server = class extends Protocol {
|
|
|
15786
15804
|
if (!methodSchema) {
|
|
15787
15805
|
throw new Error("Schema is missing a method literal");
|
|
15788
15806
|
}
|
|
15789
|
-
|
|
15790
|
-
if (isZ4Schema(methodSchema)) {
|
|
15791
|
-
const v4Schema = methodSchema;
|
|
15792
|
-
const v4Def = v4Schema._zod?.def;
|
|
15793
|
-
methodValue = v4Def?.value ?? v4Schema.value;
|
|
15794
|
-
} else {
|
|
15795
|
-
const v3Schema = methodSchema;
|
|
15796
|
-
const legacyDef = v3Schema._def;
|
|
15797
|
-
methodValue = legacyDef?.value ?? v3Schema.value;
|
|
15798
|
-
}
|
|
15807
|
+
const methodValue = getLiteralValue(methodSchema);
|
|
15799
15808
|
if (typeof methodValue !== "string") {
|
|
15800
15809
|
throw new Error("Schema method literal must be a string");
|
|
15801
15810
|
}
|
|
@@ -16100,7 +16109,7 @@ var Server = class extends Protocol {
|
|
|
16100
16109
|
}
|
|
16101
16110
|
};
|
|
16102
16111
|
|
|
16103
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
16112
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
16104
16113
|
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
16105
16114
|
function isCompletable(schema) {
|
|
16106
16115
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -16114,7 +16123,7 @@ var McpZodTypeKind;
|
|
|
16114
16123
|
McpZodTypeKind2["Completable"] = "McpCompletable";
|
|
16115
16124
|
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
16116
16125
|
|
|
16117
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
16126
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
16118
16127
|
var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
16119
16128
|
function validateToolName(name) {
|
|
16120
16129
|
const warnings = [];
|
|
@@ -16172,7 +16181,7 @@ function validateAndWarnToolName(name) {
|
|
|
16172
16181
|
return result.isValid;
|
|
16173
16182
|
}
|
|
16174
16183
|
|
|
16175
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
16184
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
16176
16185
|
var ExperimentalMcpServerTasks = class {
|
|
16177
16186
|
constructor(_mcpServer) {
|
|
16178
16187
|
this._mcpServer = _mcpServer;
|
|
@@ -16187,7 +16196,7 @@ var ExperimentalMcpServerTasks = class {
|
|
|
16187
16196
|
}
|
|
16188
16197
|
};
|
|
16189
16198
|
|
|
16190
|
-
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.
|
|
16199
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
16191
16200
|
var McpServer = class {
|
|
16192
16201
|
constructor(serverInfo, options) {
|
|
16193
16202
|
this._registeredResources = {};
|
|
@@ -17158,7 +17167,11 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17158
17167
|
},
|
|
17159
17168
|
outputSchema: {
|
|
17160
17169
|
form_id: external_exports.string().describe("Form UUID - pass to follow-up tools"),
|
|
17161
|
-
|
|
17170
|
+
// #3330: an anonymous session's viewer_url is a scoped preview link
|
|
17171
|
+
// (only the caller can open it) until the account is claimed and the
|
|
17172
|
+
// form is published - only an authenticated/claimed workspace's
|
|
17173
|
+
// create returns a genuinely live URL here.
|
|
17174
|
+
viewer_url: external_exports.string().describe("Form URL for respondents - a live public URL once authenticated/published, or a scoped preview-only link for an anonymous session"),
|
|
17162
17175
|
dashboard_url: external_exports.string().optional().describe("Builder URL (sign in to edit)"),
|
|
17163
17176
|
nodes: external_exports.array(external_exports.object({ id: external_exports.string(), type: external_exports.string(), prompt: external_exports.string() })).describe("Created nodes in order - IDs for attach_node_media / update_node"),
|
|
17164
17177
|
plan: external_exports.object({ name: external_exports.string(), auth_mode: external_exports.string() }).optional().describe("Plan and auth context for this session")
|
|
@@ -17167,7 +17180,11 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17167
17180
|
readOnlyHint: false,
|
|
17168
17181
|
destructiveHint: false,
|
|
17169
17182
|
idempotentHint: false,
|
|
17170
|
-
|
|
17183
|
+
// The form is live at a public (unguessable-share-link) URL from
|
|
17184
|
+
// creation - OpenAI's openWorldHint rubric reads "publishing
|
|
17185
|
+
// content" as true regardless of link guessability (#2976 research
|
|
17186
|
+
// pass 2).
|
|
17187
|
+
openWorldHint: true
|
|
17171
17188
|
},
|
|
17172
17189
|
_meta: { "anthropic/alwaysLoad": true }
|
|
17173
17190
|
},
|
|
@@ -17195,8 +17212,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17195
17212
|
};
|
|
17196
17213
|
const contentCount = nodes.filter((q) => !NON_COUNTABLE_TYPES.includes(q.type)).length;
|
|
17197
17214
|
if (planContext.node_limit !== null && contentCount > planContext.node_limit) {
|
|
17198
|
-
const
|
|
17199
|
-
const message = planContext.is_authenticated ? `Your '${planContext.workspace_name}' workspace is on the ${planContext.plan_name} plan, capped at ${planContext.node_limit} nodes per form. You asked for ${contentCount}. Either rerun with ${planContext.node_limit} nodes or upgrade at ${upgradeUrl}.` : `Anonymous sessions are capped at ${planContext.node_limit} nodes per form (${planContext.plan_name} plan). You asked for ${contentCount}. Either rerun with ${planContext.node_limit} nodes, or sign in to your Clipform account so forms land in your workspace with your real plan limits.`;
|
|
17215
|
+
const message = planContext.is_authenticated ? `Your '${planContext.workspace_name}' workspace is on the ${planContext.plan_name} plan, capped at ${planContext.node_limit} nodes per form. You asked for ${contentCount}. Rerun with ${planContext.node_limit} nodes or fewer.` : `Anonymous sessions are capped at ${planContext.node_limit} nodes per form (${planContext.plan_name} plan). You asked for ${contentCount}. Either rerun with ${planContext.node_limit} nodes, or sign in to your Clipform account so forms land in your workspace with your real plan limits.`;
|
|
17200
17216
|
return errorResult(message);
|
|
17201
17217
|
}
|
|
17202
17218
|
if (planContext.form_limit !== null) {
|
|
@@ -17204,10 +17220,8 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17204
17220
|
if (formsResult.ok) {
|
|
17205
17221
|
const forms = formsResult.data.forms ?? [];
|
|
17206
17222
|
if (forms.length >= planContext.form_limit) {
|
|
17207
|
-
const upgradeUrl = `${BUSINESS.urls.dashboard}/billing`;
|
|
17208
17223
|
const formList = forms.map((f, i) => ` ${i + 1}. "${f.title || "Untitled"}" (id: ${f.id})`).join("\n");
|
|
17209
|
-
const options = planContext.is_authenticated ? ` -
|
|
17210
|
-
- Or delete one of the forms below to free a slot.` : ` - Delete one of the forms below to free a slot.
|
|
17224
|
+
const options = planContext.is_authenticated ? ` - Delete one of the forms below to free a slot.` : ` - Delete one of the forms below to free a slot.
|
|
17211
17225
|
- Or sign in to a Clipform account to keep these forms and get higher limits.`;
|
|
17212
17226
|
return errorResult(
|
|
17213
17227
|
[
|
|
@@ -17219,7 +17233,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17219
17233
|
`Current forms:`,
|
|
17220
17234
|
formList,
|
|
17221
17235
|
``,
|
|
17222
|
-
`Share this list with the user, explain they've hit the ${planContext.form_limit}-form limit, and let them
|
|
17236
|
+
`Share this list with the user, explain they've hit the ${planContext.form_limit}-form limit, and let them pick a form to delete. clipform_delete_form asks the user to confirm before removing anything.`
|
|
17223
17237
|
].join("\n")
|
|
17224
17238
|
);
|
|
17225
17239
|
}
|
|
@@ -17249,6 +17263,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17249
17263
|
const formId = data.form_id;
|
|
17250
17264
|
const formUrl = data.form_url ?? void 0;
|
|
17251
17265
|
const createdNodes = data.nodes ?? [];
|
|
17266
|
+
const isPreview = String(data.viewer_url ?? "").includes("mode=preview");
|
|
17252
17267
|
if (font_family !== void 0) {
|
|
17253
17268
|
await callApi(`/forms/${formId}`, {
|
|
17254
17269
|
method: "PATCH",
|
|
@@ -17263,7 +17278,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17263
17278
|
}
|
|
17264
17279
|
const truncate = (s, n = 60) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
17265
17280
|
const lines = [
|
|
17266
|
-
`Form created and live.`,
|
|
17281
|
+
isPreview ? `Form created as a preview draft.` : `Form created and live.`,
|
|
17267
17282
|
``,
|
|
17268
17283
|
`Title: ${title}`,
|
|
17269
17284
|
`Form ID: ${formId}`,
|
|
@@ -17271,15 +17286,17 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17271
17286
|
`Node IDs (use directly with attach_node_media / update_node - no get_form round-trip needed):`,
|
|
17272
17287
|
...createdNodes.map((n) => `- [${n.type}] "${truncate(n.prompt)}": ${n.id}`),
|
|
17273
17288
|
``,
|
|
17274
|
-
`FORM URL (live - share this with respondents): ${data.viewer_url}`
|
|
17289
|
+
isPreview ? `PREVIEW URL (draft - only you can open this, not yet visible to respondents): ${data.viewer_url}` : `FORM URL (live - share this with respondents): ${data.viewer_url}`
|
|
17275
17290
|
];
|
|
17276
17291
|
if (formUrl) {
|
|
17277
|
-
lines.push(
|
|
17292
|
+
lines.push(
|
|
17293
|
+
isPreview ? `CLAIM URL (one-time - claim this account to publish): ${formUrl}` : `DASHBOARD URL (sign in to edit and manage): ${formUrl}`
|
|
17294
|
+
);
|
|
17278
17295
|
}
|
|
17279
17296
|
lines.push(
|
|
17280
17297
|
``,
|
|
17281
|
-
`The form is live and accessible at the FORM URL above.
|
|
17282
|
-
`The URLs above are exact values; constructed or guessed URLs return 404. Use the FORM URL and DASHBOARD URL as shown.`,
|
|
17298
|
+
isPreview ? `This form is a preview - claim and publish to start collecting responses. Open the CLAIM URL to sign into the account this form already belongs to, then republish with clipform_update_form (is_live: true). The PREVIEW URL never becomes public on its own.` : `The form is live and accessible at the FORM URL above. Edits and media attached AFTER this point are NOT visible to respondents until you re-send is_live: true via clipform_update_form - republishing is the expected final step of a build, never skip it after attaching media/narration.`,
|
|
17299
|
+
`The URLs above are exact values; constructed or guessed URLs return 404. Use the ${isPreview ? "PREVIEW" : "FORM"} URL and ${isPreview ? "CLAIM" : "DASHBOARD"} URL as shown.`,
|
|
17283
17300
|
`Pass form_id on follow-up tools (get_form, add_node, attach_node_media, etc.).`
|
|
17284
17301
|
);
|
|
17285
17302
|
if (planContext) {
|
|
@@ -17322,7 +17339,7 @@ function registerListFormsTool(server) {
|
|
|
17322
17339
|
"clipform_list_forms",
|
|
17323
17340
|
{
|
|
17324
17341
|
title: "List Clipforms",
|
|
17325
|
-
description: `List forms in your workspace with optional filtering. Returns paginated results (cursor-based). next_cursor is included when more results exist.`,
|
|
17342
|
+
description: `List forms in your workspace with optional filtering. Returns paginated results (cursor-based). next_cursor is included when more results exist. To find or open a form by name, pass search with part of its title instead of paging through results.`,
|
|
17326
17343
|
inputSchema: {
|
|
17327
17344
|
limit: external_exports.number().int().min(1).max(100).optional().describe("Number of forms to return (default 25, max 100)"),
|
|
17328
17345
|
cursor: external_exports.string().optional().describe(
|
|
@@ -17332,7 +17349,9 @@ function registerListFormsTool(server) {
|
|
|
17332
17349
|
"Filter by tag name(s), comma-separated. AND logic: only forms with ALL tags are returned."
|
|
17333
17350
|
),
|
|
17334
17351
|
published: external_exports.enum(["true", "false"]).optional().describe("Filter by publish status"),
|
|
17335
|
-
search: external_exports.string().optional().describe(
|
|
17352
|
+
search: external_exports.string().optional().describe(
|
|
17353
|
+
"Filter to forms whose title contains this text (case-insensitive substring). Use this to resolve a form by name in one call instead of paginating."
|
|
17354
|
+
),
|
|
17336
17355
|
sort: external_exports.enum(["created_at", "updated_at"]).optional().describe("Sort field (default: created_at)"),
|
|
17337
17356
|
order: external_exports.enum(["asc", "desc"]).optional().describe("Sort order (default: desc, newest first)")
|
|
17338
17357
|
},
|
|
@@ -17344,8 +17363,6 @@ function registerListFormsTool(server) {
|
|
|
17344
17363
|
share_id: external_exports.string(),
|
|
17345
17364
|
is_live: external_exports.boolean(),
|
|
17346
17365
|
has_unpublished_changes: external_exports.boolean(),
|
|
17347
|
-
created_at: external_exports.string(),
|
|
17348
|
-
updated_at: external_exports.string(),
|
|
17349
17366
|
tags: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), color: external_exports.string().nullable() }))
|
|
17350
17367
|
})
|
|
17351
17368
|
).describe("Matching forms (empty if none)"),
|
|
@@ -17379,8 +17396,6 @@ function registerListFormsTool(server) {
|
|
|
17379
17396
|
share_id: f.share_id,
|
|
17380
17397
|
is_live: f.is_live,
|
|
17381
17398
|
has_unpublished_changes: f.has_unpublished_changes,
|
|
17382
|
-
created_at: f.created_at,
|
|
17383
|
-
updated_at: f.updated_at,
|
|
17384
17399
|
tags: f.tags.map((t) => ({ id: t.id, name: t.name, color: t.color }))
|
|
17385
17400
|
})),
|
|
17386
17401
|
next_cursor: data.next_cursor
|
|
@@ -17406,30 +17421,78 @@ function registerListFormsTool(server) {
|
|
|
17406
17421
|
}
|
|
17407
17422
|
|
|
17408
17423
|
// src/lib/format-form.ts
|
|
17424
|
+
function allowedConfigKeys(nodeType) {
|
|
17425
|
+
const schema = NODE_TYPES[nodeType]?.config_schema;
|
|
17426
|
+
const properties = schema?.properties;
|
|
17427
|
+
if (properties) return new Set(Object.keys(properties));
|
|
17428
|
+
const oneOf = schema?.oneOf;
|
|
17429
|
+
if (Array.isArray(oneOf)) {
|
|
17430
|
+
const keys = /* @__PURE__ */ new Set();
|
|
17431
|
+
for (const branch of oneOf) {
|
|
17432
|
+
for (const k of Object.keys(branch?.properties ?? {})) keys.add(k);
|
|
17433
|
+
}
|
|
17434
|
+
return keys;
|
|
17435
|
+
}
|
|
17436
|
+
return /* @__PURE__ */ new Set();
|
|
17437
|
+
}
|
|
17438
|
+
function projectConfig(nodeType, config2) {
|
|
17439
|
+
if (!config2) return void 0;
|
|
17440
|
+
const allowed = allowedConfigKeys(nodeType);
|
|
17441
|
+
const projected = {};
|
|
17442
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
17443
|
+
if (allowed.has(key)) projected[key] = value;
|
|
17444
|
+
}
|
|
17445
|
+
return projected;
|
|
17446
|
+
}
|
|
17409
17447
|
function getFormNodes(data) {
|
|
17410
17448
|
return data.node ?? data.nodes ?? [];
|
|
17411
17449
|
}
|
|
17450
|
+
function buildLogicByNode(nodes) {
|
|
17451
|
+
const logicByNode = /* @__PURE__ */ new Map();
|
|
17452
|
+
for (const node of nodes) {
|
|
17453
|
+
if (node.node_logic && node.node_logic.length > 0) {
|
|
17454
|
+
logicByNode.set(
|
|
17455
|
+
node.id,
|
|
17456
|
+
node.node_logic.flatMap((entry) => entry.actions ?? [])
|
|
17457
|
+
);
|
|
17458
|
+
}
|
|
17459
|
+
}
|
|
17460
|
+
return logicByNode;
|
|
17461
|
+
}
|
|
17462
|
+
function orderNodesWithLogic(data) {
|
|
17463
|
+
const rawNodes = getFormNodes(data);
|
|
17464
|
+
const logicByNode = buildLogicByNode(rawNodes);
|
|
17465
|
+
return { nodes: bfsOrder(rawNodes, logicByNode), logicByNode };
|
|
17466
|
+
}
|
|
17467
|
+
function projectNext(node, logicByNode) {
|
|
17468
|
+
const target = defaultTarget(logicByNode.get(node.id));
|
|
17469
|
+
return target ? { target_id: target.value, target_type: target.type } : void 0;
|
|
17470
|
+
}
|
|
17412
17471
|
function projectFormState(data) {
|
|
17472
|
+
const { nodes, logicByNode } = orderNodesWithLogic(data);
|
|
17413
17473
|
return {
|
|
17414
17474
|
form_id: String(data.form_id ?? ""),
|
|
17415
17475
|
title: String(data.title ?? ""),
|
|
17416
17476
|
is_live: Boolean(data.is_live),
|
|
17417
17477
|
has_unpublished_changes: Boolean(data.has_unpublished_changes),
|
|
17418
|
-
nodes:
|
|
17478
|
+
nodes: nodes.map((q) => {
|
|
17419
17479
|
const options = q.node_options ?? q.options;
|
|
17480
|
+
const config2 = projectConfig(q.type, q.config);
|
|
17481
|
+
const next = projectNext(q, logicByNode);
|
|
17420
17482
|
return {
|
|
17421
17483
|
id: q.id,
|
|
17422
17484
|
type: q.type,
|
|
17423
17485
|
prompt: q.prompt ?? "",
|
|
17424
17486
|
required: Boolean(q.required),
|
|
17425
|
-
...
|
|
17426
|
-
...options && options.length > 0 ? { options: options.map((o) => ({ id: o.id, content: o.content })) } : {}
|
|
17487
|
+
...config2 && Object.keys(config2).length > 0 ? { config: config2 } : {},
|
|
17488
|
+
...options && options.length > 0 ? { options: options.map((o) => ({ id: o.id, content: o.content })) } : {},
|
|
17489
|
+
...next ? { next } : {}
|
|
17427
17490
|
};
|
|
17428
17491
|
})
|
|
17429
17492
|
};
|
|
17430
17493
|
}
|
|
17431
17494
|
function formatFormState(data) {
|
|
17432
|
-
const nodes =
|
|
17495
|
+
const { nodes, logicByNode } = orderNodesWithLogic(data);
|
|
17433
17496
|
const lines = [
|
|
17434
17497
|
`Form: ${data.title}`,
|
|
17435
17498
|
`Form ID: ${data.form_id}`,
|
|
@@ -17446,8 +17509,9 @@ function formatFormState(data) {
|
|
|
17446
17509
|
lines.push(` ${i + 1}. [${q.type}] ${q.prompt || "(no prompt)"}`);
|
|
17447
17510
|
lines.push(` Node ID: ${q.id}`);
|
|
17448
17511
|
if (q.required) lines.push(` Required: yes`);
|
|
17449
|
-
|
|
17450
|
-
|
|
17512
|
+
const config2 = projectConfig(q.type, q.config);
|
|
17513
|
+
if (config2 && Object.keys(config2).length > 0) {
|
|
17514
|
+
lines.push(` Config: ${JSON.stringify(config2)}`);
|
|
17451
17515
|
}
|
|
17452
17516
|
const options = q.node_options ?? q.options;
|
|
17453
17517
|
if (options && options.length > 0) {
|
|
@@ -17455,6 +17519,10 @@ function formatFormState(data) {
|
|
|
17455
17519
|
` Options: ${options.map((o) => o.content).join(", ")}`
|
|
17456
17520
|
);
|
|
17457
17521
|
}
|
|
17522
|
+
const next = projectNext(q, logicByNode);
|
|
17523
|
+
if (next) {
|
|
17524
|
+
lines.push(` -> next: ${next.target_id} (${next.target_type})`);
|
|
17525
|
+
}
|
|
17458
17526
|
}
|
|
17459
17527
|
return lines.join("\n");
|
|
17460
17528
|
}
|
|
@@ -17465,7 +17533,7 @@ function registerGetFormTool(server) {
|
|
|
17465
17533
|
"clipform_get_form",
|
|
17466
17534
|
{
|
|
17467
17535
|
title: "Get Clipform",
|
|
17468
|
-
description: `Retrieve a form's details including all nodes in sequential order. Returns title, settings, and every node with its options, config, and
|
|
17536
|
+
description: `Retrieve a form's details including all nodes in sequential order and their routing. Returns title, settings, and every node with its options, config, media status, and next node (from clipform_set_logic).`,
|
|
17469
17537
|
inputSchema: {
|
|
17470
17538
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)")
|
|
17471
17539
|
},
|
|
@@ -17481,7 +17549,11 @@ function registerGetFormTool(server) {
|
|
|
17481
17549
|
prompt: external_exports.string(),
|
|
17482
17550
|
required: external_exports.boolean(),
|
|
17483
17551
|
config: external_exports.record(external_exports.unknown()).optional(),
|
|
17484
|
-
options: external_exports.array(external_exports.object({ id: external_exports.string(), content: external_exports.string() })).optional()
|
|
17552
|
+
options: external_exports.array(external_exports.object({ id: external_exports.string(), content: external_exports.string() })).optional(),
|
|
17553
|
+
next: external_exports.object({
|
|
17554
|
+
target_id: external_exports.string().describe("The node/screen ID this node routes to"),
|
|
17555
|
+
target_type: external_exports.enum(["node", "end_screen", "redirect", "link_list"]).describe("What kind of target target_id is")
|
|
17556
|
+
}).optional().describe("This node's outgoing edge, set via clipform_set_logic. Omitted for a chain tail or terminal node.")
|
|
17485
17557
|
})
|
|
17486
17558
|
).describe("Nodes in sequential order - IDs for update_node / attach_node_media")
|
|
17487
17559
|
},
|
|
@@ -17493,7 +17565,7 @@ function registerGetFormTool(server) {
|
|
|
17493
17565
|
}
|
|
17494
17566
|
},
|
|
17495
17567
|
async ({ form_id }) => {
|
|
17496
|
-
const result = await callApi(`/forms/${form_id}`);
|
|
17568
|
+
const result = await callApi(`/forms/${form_id}?include=logic`);
|
|
17497
17569
|
if (!result.ok) {
|
|
17498
17570
|
return errorResult(result.error);
|
|
17499
17571
|
}
|
|
@@ -17526,11 +17598,20 @@ function registerUpdateFormTool(server) {
|
|
|
17526
17598
|
brand_name: external_exports.string().nullable().optional().describe("AI-PROTECTED: Custom brand name shown in footer. Only set when user explicitly provides."),
|
|
17527
17599
|
tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags on this form. Pass the full desired set (e.g. ['quiz', 'trivia', 'slug:elephants']). Omit to leave tags unchanged.")
|
|
17528
17600
|
},
|
|
17601
|
+
outputSchema: {
|
|
17602
|
+
form_id: external_exports.string().describe("Form UUID"),
|
|
17603
|
+
updated_fields: external_exports.array(external_exports.string()).describe("Names of the fields that were changed by this call")
|
|
17604
|
+
},
|
|
17529
17605
|
annotations: {
|
|
17530
17606
|
readOnlyHint: false,
|
|
17531
|
-
|
|
17607
|
+
// is_live: true/false publishes, republishes, or unpublishes the
|
|
17608
|
+
// live form, and other fields (title, theme, logo) overwrite what
|
|
17609
|
+
// respondents already see (#2976 research pass 2).
|
|
17610
|
+
destructiveHint: true,
|
|
17532
17611
|
idempotentHint: true,
|
|
17533
|
-
|
|
17612
|
+
// is_live: true makes the form live at a public URL - openWorldHint
|
|
17613
|
+
// per OpenAI's "publishing content" reading (#2976 research pass 2).
|
|
17614
|
+
openWorldHint: true
|
|
17534
17615
|
}
|
|
17535
17616
|
},
|
|
17536
17617
|
async ({ form_id, title, is_live, show_step_counter, disable_back_navigation, total_steps, primary_color, background_color, font_family, description, author, logo_url, show_branding, brand_name, tags }) => {
|
|
@@ -17567,34 +17648,64 @@ function registerUpdateFormTool(server) {
|
|
|
17567
17648
|
}
|
|
17568
17649
|
}
|
|
17569
17650
|
const updates = [];
|
|
17570
|
-
|
|
17571
|
-
if (
|
|
17651
|
+
const updatedFields = [];
|
|
17652
|
+
if (title !== void 0) {
|
|
17653
|
+
updates.push(`Title \u2192 "${title}"`);
|
|
17654
|
+
updatedFields.push("title");
|
|
17655
|
+
}
|
|
17656
|
+
if (is_live !== void 0) {
|
|
17572
17657
|
updates.push(`Live \u2192 ${is_live}`);
|
|
17573
|
-
|
|
17658
|
+
updatedFields.push("is_live");
|
|
17659
|
+
}
|
|
17660
|
+
if (show_step_counter !== void 0) {
|
|
17574
17661
|
updates.push(`Step counter \u2192 ${show_step_counter}`);
|
|
17575
|
-
|
|
17662
|
+
updatedFields.push("show_step_counter");
|
|
17663
|
+
}
|
|
17664
|
+
if (disable_back_navigation !== void 0) {
|
|
17576
17665
|
updates.push(`Back navigation \u2192 ${disable_back_navigation ? "disabled" : "enabled"}`);
|
|
17577
|
-
|
|
17666
|
+
updatedFields.push("disable_back_navigation");
|
|
17667
|
+
}
|
|
17668
|
+
if (total_steps !== void 0) {
|
|
17578
17669
|
updates.push(`Total steps \u2192 ${total_steps === null ? "auto" : total_steps}`);
|
|
17579
|
-
|
|
17670
|
+
updatedFields.push("total_steps");
|
|
17671
|
+
}
|
|
17672
|
+
if (primary_color !== void 0) {
|
|
17580
17673
|
updates.push(`Primary color \u2192 ${primary_color}`);
|
|
17581
|
-
|
|
17674
|
+
updatedFields.push("primary_color");
|
|
17675
|
+
}
|
|
17676
|
+
if (background_color !== void 0) {
|
|
17582
17677
|
updates.push(`Background color \u2192 ${background_color}`);
|
|
17583
|
-
|
|
17678
|
+
updatedFields.push("background_color");
|
|
17679
|
+
}
|
|
17680
|
+
if (font_family !== void 0) {
|
|
17584
17681
|
updates.push(`Font \u2192 ${font_family}`);
|
|
17585
|
-
|
|
17682
|
+
updatedFields.push("font_family");
|
|
17683
|
+
}
|
|
17684
|
+
if (description !== void 0) {
|
|
17586
17685
|
updates.push(`Description \u2192 ${description === null ? "cleared" : `"${description}"`}`);
|
|
17587
|
-
|
|
17686
|
+
updatedFields.push("description");
|
|
17687
|
+
}
|
|
17688
|
+
if (author !== void 0) {
|
|
17588
17689
|
updates.push(`Author \u2192 ${author === null ? "cleared" : `"${author}"`}`);
|
|
17589
|
-
|
|
17690
|
+
updatedFields.push("author");
|
|
17691
|
+
}
|
|
17692
|
+
if (logo_url !== void 0) {
|
|
17590
17693
|
updates.push(`Logo URL \u2192 ${logo_url === null ? "cleared" : logo_url}`);
|
|
17591
|
-
|
|
17694
|
+
updatedFields.push("logo_url");
|
|
17695
|
+
}
|
|
17696
|
+
if (show_branding !== void 0) {
|
|
17592
17697
|
updates.push(`Show branding \u2192 ${show_branding}`);
|
|
17593
|
-
|
|
17698
|
+
updatedFields.push("show_branding");
|
|
17699
|
+
}
|
|
17700
|
+
if (brand_name !== void 0) {
|
|
17594
17701
|
updates.push(`Brand name \u2192 ${brand_name === null ? "cleared" : `"${brand_name}"`}`);
|
|
17595
|
-
|
|
17702
|
+
updatedFields.push("brand_name");
|
|
17703
|
+
}
|
|
17704
|
+
if (tags) {
|
|
17596
17705
|
updates.push(`Tags \u2192 [${tags.join(", ")}]`);
|
|
17597
|
-
|
|
17706
|
+
updatedFields.push("tags");
|
|
17707
|
+
}
|
|
17708
|
+
return structuredResult(`Form updated: ${updates.join(", ")}`, { form_id, updated_fields: updatedFields });
|
|
17598
17709
|
}
|
|
17599
17710
|
);
|
|
17600
17711
|
}
|
|
@@ -17636,8 +17747,13 @@ function registerDeleteFormTool(server) {
|
|
|
17636
17747
|
annotations: {
|
|
17637
17748
|
readOnlyHint: false,
|
|
17638
17749
|
destructiveHint: true,
|
|
17639
|
-
|
|
17640
|
-
|
|
17750
|
+
// Deleting an already-trashed form has no further effect -
|
|
17751
|
+
// calling this twice with the same form_id is a no-op the second
|
|
17752
|
+
// time (textbook idempotent delete, #2976).
|
|
17753
|
+
idempotentHint: true,
|
|
17754
|
+
// Removes a form's public page - openWorldHint per OpenAI's
|
|
17755
|
+
// "publishing content" reading (#2976 research pass 2).
|
|
17756
|
+
openWorldHint: true
|
|
17641
17757
|
}
|
|
17642
17758
|
},
|
|
17643
17759
|
async ({ form_id, confirm }) => {
|
|
@@ -17660,13 +17776,210 @@ function registerDeleteFormTool(server) {
|
|
|
17660
17776
|
);
|
|
17661
17777
|
}
|
|
17662
17778
|
|
|
17779
|
+
// src/tools/get-results.ts
|
|
17780
|
+
var MAX_TEXT_LENGTH = 500;
|
|
17781
|
+
var MAX_LABEL_LENGTH = 80;
|
|
17782
|
+
var MAX_RECENT_ANSWERS = 20;
|
|
17783
|
+
function coerceToString(value, maxLength) {
|
|
17784
|
+
if (value === null || value === void 0) return "";
|
|
17785
|
+
if (typeof value === "string") {
|
|
17786
|
+
return value.length > maxLength ? `${value.slice(0, maxLength)}\u2026` : value;
|
|
17787
|
+
}
|
|
17788
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
17789
|
+
return String(value);
|
|
17790
|
+
}
|
|
17791
|
+
let json;
|
|
17792
|
+
try {
|
|
17793
|
+
json = JSON.stringify(value);
|
|
17794
|
+
} catch {
|
|
17795
|
+
return "";
|
|
17796
|
+
}
|
|
17797
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}\u2026` : json;
|
|
17798
|
+
}
|
|
17799
|
+
function projectNode(node) {
|
|
17800
|
+
const base = {
|
|
17801
|
+
node_id: node.node_id,
|
|
17802
|
+
node_type: node.node_type,
|
|
17803
|
+
prompt: node.node_text,
|
|
17804
|
+
total_responses: node.total_responses,
|
|
17805
|
+
total_views: node.total_views
|
|
17806
|
+
};
|
|
17807
|
+
if (node.node_type === "choice") {
|
|
17808
|
+
const answers = node.answers;
|
|
17809
|
+
return {
|
|
17810
|
+
...base,
|
|
17811
|
+
options: answers.map((a) => ({
|
|
17812
|
+
label: a.answer_text,
|
|
17813
|
+
count: a.count,
|
|
17814
|
+
percentage: a.percentage
|
|
17815
|
+
}))
|
|
17816
|
+
};
|
|
17817
|
+
}
|
|
17818
|
+
if (node.node_type === "payment") {
|
|
17819
|
+
return base;
|
|
17820
|
+
}
|
|
17821
|
+
if (node.node_category === "action") {
|
|
17822
|
+
const answers = node.answers;
|
|
17823
|
+
return {
|
|
17824
|
+
...base,
|
|
17825
|
+
options: answers.map((a) => ({
|
|
17826
|
+
label: coerceToString(a.answer_value, MAX_LABEL_LENGTH),
|
|
17827
|
+
count: a.count,
|
|
17828
|
+
percentage: a.percentage
|
|
17829
|
+
}))
|
|
17830
|
+
};
|
|
17831
|
+
}
|
|
17832
|
+
if (node.node_type === "open") {
|
|
17833
|
+
const answers = node.answers;
|
|
17834
|
+
return {
|
|
17835
|
+
...base,
|
|
17836
|
+
recent_answers: answers.slice(0, MAX_RECENT_ANSWERS).map((a) => ({
|
|
17837
|
+
text: coerceToString(a.answer_value, MAX_TEXT_LENGTH),
|
|
17838
|
+
submitted_at: a.submitted_at
|
|
17839
|
+
}))
|
|
17840
|
+
};
|
|
17841
|
+
}
|
|
17842
|
+
return base;
|
|
17843
|
+
}
|
|
17844
|
+
function registerGetResultsTool(server) {
|
|
17845
|
+
server.registerTool(
|
|
17846
|
+
"clipform_get_results",
|
|
17847
|
+
{
|
|
17848
|
+
title: "Get Clipform Results",
|
|
17849
|
+
description: `View response counts, choice/action answer breakdowns, and recent open-text answers for a form. Contact, file, draw, camera, and payment answers are counts only - use the dashboard for per-response detail.`,
|
|
17850
|
+
inputSchema: {
|
|
17851
|
+
form_id: external_exports.string().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)")
|
|
17852
|
+
},
|
|
17853
|
+
outputSchema: {
|
|
17854
|
+
total_responses: external_exports.number().describe("Total unique respondents to the form"),
|
|
17855
|
+
nodes: external_exports.array(
|
|
17856
|
+
external_exports.object({
|
|
17857
|
+
node_id: external_exports.string(),
|
|
17858
|
+
node_type: external_exports.string(),
|
|
17859
|
+
prompt: external_exports.string(),
|
|
17860
|
+
total_responses: external_exports.number(),
|
|
17861
|
+
total_views: external_exports.number(),
|
|
17862
|
+
options: external_exports.array(
|
|
17863
|
+
external_exports.object({
|
|
17864
|
+
label: external_exports.string(),
|
|
17865
|
+
count: external_exports.number(),
|
|
17866
|
+
percentage: external_exports.number()
|
|
17867
|
+
})
|
|
17868
|
+
).optional().describe("Answer breakdown - choice and action-category (booking/file_download/shopping) nodes only"),
|
|
17869
|
+
recent_answers: external_exports.array(
|
|
17870
|
+
external_exports.object({
|
|
17871
|
+
text: external_exports.string(),
|
|
17872
|
+
submitted_at: external_exports.string()
|
|
17873
|
+
})
|
|
17874
|
+
).optional().describe("Most recent open-text answers, capped at 20 - open questions only")
|
|
17875
|
+
})
|
|
17876
|
+
).describe("Nodes in form order. Contact/file/draw/camera/payment nodes carry only total_responses/total_views - no per-response detail.")
|
|
17877
|
+
},
|
|
17878
|
+
annotations: {
|
|
17879
|
+
readOnlyHint: true,
|
|
17880
|
+
destructiveHint: false,
|
|
17881
|
+
idempotentHint: true,
|
|
17882
|
+
openWorldHint: false
|
|
17883
|
+
}
|
|
17884
|
+
},
|
|
17885
|
+
async ({ form_id }) => {
|
|
17886
|
+
const result = await callApi(`/forms/${form_id}/results`);
|
|
17887
|
+
if (!result.ok) {
|
|
17888
|
+
return errorResult(result.error);
|
|
17889
|
+
}
|
|
17890
|
+
const data = result.data;
|
|
17891
|
+
const nodes = (data.nodes || []).map(projectNode);
|
|
17892
|
+
const structured = {
|
|
17893
|
+
total_responses: data.total_responses,
|
|
17894
|
+
nodes
|
|
17895
|
+
};
|
|
17896
|
+
if (data.total_responses === 0) {
|
|
17897
|
+
return structuredResult(
|
|
17898
|
+
`This form has no responses yet (${nodes.length} node(s)).`,
|
|
17899
|
+
structured
|
|
17900
|
+
);
|
|
17901
|
+
}
|
|
17902
|
+
const lines = [`${data.total_responses} total response(s) across ${nodes.length} node(s).`];
|
|
17903
|
+
const highlights = nodes.filter((n) => "options" in n || "recent_answers" in n).slice(0, 3);
|
|
17904
|
+
for (const n of highlights) {
|
|
17905
|
+
if ("options" in n && n.options) {
|
|
17906
|
+
const top = n.options.slice(0, 3).map((o) => `${o.label} (${o.percentage}%)`).join(", ");
|
|
17907
|
+
lines.push(`- ${n.prompt}: ${top || "no answers yet"}`);
|
|
17908
|
+
} else if ("recent_answers" in n && n.recent_answers) {
|
|
17909
|
+
lines.push(`- ${n.prompt}: ${n.recent_answers.length} recent open-text answer(s)`);
|
|
17910
|
+
}
|
|
17911
|
+
}
|
|
17912
|
+
return structuredResult(lines.join("\n"), structured);
|
|
17913
|
+
}
|
|
17914
|
+
);
|
|
17915
|
+
}
|
|
17916
|
+
|
|
17917
|
+
// src/tools/whoami.ts
|
|
17918
|
+
function registerWhoamiTool(server) {
|
|
17919
|
+
server.registerTool(
|
|
17920
|
+
"clipform_whoami",
|
|
17921
|
+
{
|
|
17922
|
+
title: "Who Am I",
|
|
17923
|
+
description: `Show the current identity: auth mode (api key, session, or anonymous), active workspace, and plan limits.`,
|
|
17924
|
+
inputSchema: {},
|
|
17925
|
+
outputSchema: {
|
|
17926
|
+
auth_mode: external_exports.enum(["api_key", "session", "anonymous"]).describe("How this session is authenticated"),
|
|
17927
|
+
is_authenticated: external_exports.boolean().describe("True for api_key or session auth; false for anonymous"),
|
|
17928
|
+
workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string() }).nullable().describe("Active workspace, or null when anonymous"),
|
|
17929
|
+
plan: external_exports.object({
|
|
17930
|
+
name: external_exports.string(),
|
|
17931
|
+
tier: external_exports.number(),
|
|
17932
|
+
node_limit: external_exports.number().nullable().describe("Max nodes per form, or null if unlimited"),
|
|
17933
|
+
form_limit: external_exports.number().nullable().describe("Max forms in the workspace, or null if unlimited")
|
|
17934
|
+
}).describe("Plan limits for the active workspace"),
|
|
17935
|
+
workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })).optional().describe("All workspaces in the caller's company - only present for session (dashboard) auth")
|
|
17936
|
+
},
|
|
17937
|
+
annotations: {
|
|
17938
|
+
readOnlyHint: true,
|
|
17939
|
+
destructiveHint: false,
|
|
17940
|
+
idempotentHint: true,
|
|
17941
|
+
openWorldHint: false
|
|
17942
|
+
},
|
|
17943
|
+
_meta: { "anthropic/alwaysLoad": true }
|
|
17944
|
+
},
|
|
17945
|
+
async () => {
|
|
17946
|
+
const result = await callApi("/me", { method: "GET" });
|
|
17947
|
+
if (!result.ok) {
|
|
17948
|
+
return errorResult(result.error);
|
|
17949
|
+
}
|
|
17950
|
+
const me = result.data;
|
|
17951
|
+
const authMode = me.auth_mode ?? "anonymous";
|
|
17952
|
+
const isAuthenticated = me.auth_mode != null && me.auth_mode !== "anonymous";
|
|
17953
|
+
const workspace = me.workspace ? { id: me.workspace.id, name: me.workspace.name } : null;
|
|
17954
|
+
const plan = {
|
|
17955
|
+
name: me.plan?.name ?? "Free",
|
|
17956
|
+
tier: me.plan?.tier ?? 0,
|
|
17957
|
+
node_limit: me.plan?.node_limit ?? null,
|
|
17958
|
+
form_limit: me.plan?.form_limit ?? null
|
|
17959
|
+
};
|
|
17960
|
+
const structured = {
|
|
17961
|
+
auth_mode: authMode,
|
|
17962
|
+
is_authenticated: isAuthenticated,
|
|
17963
|
+
workspace,
|
|
17964
|
+
plan
|
|
17965
|
+
};
|
|
17966
|
+
if (Array.isArray(me.workspaces)) {
|
|
17967
|
+
structured.workspaces = me.workspaces.map((w) => ({ id: w.id, name: w.name }));
|
|
17968
|
+
}
|
|
17969
|
+
const workspaceLine = workspace ? `workspace '${workspace.name}'` : "no workspace (anonymous)";
|
|
17970
|
+
const summary = `Auth: ${authMode}. ${workspaceLine}. Plan: ${plan.name}.`;
|
|
17971
|
+
return structuredResult(summary, structured);
|
|
17972
|
+
}
|
|
17973
|
+
);
|
|
17974
|
+
}
|
|
17975
|
+
|
|
17663
17976
|
// src/tools/add-node.ts
|
|
17664
17977
|
function registerAddNodeTool(server) {
|
|
17665
17978
|
server.registerTool(
|
|
17666
17979
|
"clipform_add_node",
|
|
17667
17980
|
{
|
|
17668
17981
|
title: "Add Node",
|
|
17669
|
-
description: `Add a new node to an existing form. Inserted before the end screen by default; after_node_id controls insertion position. Node types and config schemas match clipform_create_form.`,
|
|
17982
|
+
description: `Add a new node to an existing form. Inserted before the end screen by default; after_node_id controls insertion position. Inserting into a linear flow automatically re-links the chain (the node before the insertion point points to the new node, which points to what followed) - you do not need clipform_set_logic for a simple insert. Branching still needs an explicit logic write. Node types and config schemas match clipform_create_form.`,
|
|
17670
17983
|
inputSchema: {
|
|
17671
17984
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
|
|
17672
17985
|
node: NodeSchema.describe("The node to add"),
|
|
@@ -17697,8 +18010,10 @@ function registerAddNodeTool(server) {
|
|
|
17697
18010
|
return errorResult(result.error);
|
|
17698
18011
|
}
|
|
17699
18012
|
const nodeId = result.data.node_id;
|
|
18013
|
+
const warnings = result.data.warnings ?? [];
|
|
18014
|
+
const relinkNote = warnings.length > 0 ? ` ${warnings.join(" ")}` : " The logic chain was automatically re-linked - no clipform_set_logic call is needed for this insert.";
|
|
17700
18015
|
return structuredResult(
|
|
17701
|
-
`Node added. ID: ${nodeId} | Type: ${node.type}`,
|
|
18016
|
+
`Node added. ID: ${nodeId} | Type: ${node.type}.${relinkNote}`,
|
|
17702
18017
|
{ node_id: nodeId, type: node.type }
|
|
17703
18018
|
);
|
|
17704
18019
|
}
|
|
@@ -17741,7 +18056,10 @@ function registerUpdateNodeTool(server) {
|
|
|
17741
18056
|
},
|
|
17742
18057
|
annotations: {
|
|
17743
18058
|
readOnlyHint: false,
|
|
17744
|
-
|
|
18059
|
+
// Overwrites the node's prompt/config with whatever is sent - a
|
|
18060
|
+
// field explicitly set to null clears it, so this can destroy data,
|
|
18061
|
+
// not just add to it (#2976 research pass 2).
|
|
18062
|
+
destructiveHint: true,
|
|
17745
18063
|
idempotentHint: true,
|
|
17746
18064
|
openWorldHint: false
|
|
17747
18065
|
}
|
|
@@ -17801,7 +18119,9 @@ function registerDeleteNodeTool(server) {
|
|
|
17801
18119
|
annotations: {
|
|
17802
18120
|
readOnlyHint: false,
|
|
17803
18121
|
destructiveHint: true,
|
|
17804
|
-
|
|
18122
|
+
// Deleting an already-deleted node has no further effect -
|
|
18123
|
+
// textbook idempotent delete (#2976).
|
|
18124
|
+
idempotentHint: true,
|
|
17805
18125
|
openWorldHint: false
|
|
17806
18126
|
}
|
|
17807
18127
|
},
|
|
@@ -18034,8 +18354,15 @@ function registerAttachNodeMediaTool(server) {
|
|
|
18034
18354
|
},
|
|
18035
18355
|
annotations: {
|
|
18036
18356
|
readOnlyHint: false,
|
|
18037
|
-
|
|
18038
|
-
|
|
18357
|
+
// Attaching replaces the node's existing media placement (the API
|
|
18358
|
+
// overwrites it, not merges) - the previous placement is gone, even
|
|
18359
|
+
// though the underlying library asset it pointed to is never deleted
|
|
18360
|
+
// (#2976 research pass 2).
|
|
18361
|
+
destructiveHint: true,
|
|
18362
|
+
// Re-attaching the same media_asset_id to the same node converges to
|
|
18363
|
+
// the same placement state - no new resource is created each call
|
|
18364
|
+
// (#2976).
|
|
18365
|
+
idempotentHint: true,
|
|
18039
18366
|
openWorldHint: false
|
|
18040
18367
|
}
|
|
18041
18368
|
},
|
|
@@ -18157,7 +18484,9 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18157
18484
|
annotations: {
|
|
18158
18485
|
readOnlyHint: false,
|
|
18159
18486
|
destructiveHint: true,
|
|
18160
|
-
|
|
18487
|
+
// Deleting already-removed media has no further effect - textbook
|
|
18488
|
+
// idempotent delete (#2976).
|
|
18489
|
+
idempotentHint: true,
|
|
18161
18490
|
openWorldHint: false
|
|
18162
18491
|
}
|
|
18163
18492
|
},
|
|
@@ -18184,23 +18513,20 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18184
18513
|
|
|
18185
18514
|
// src/tools/set-node-logic.ts
|
|
18186
18515
|
var LogicRuleSchema = external_exports.object({
|
|
18187
|
-
|
|
18188
|
-
"The option text to match. Omit for a default rule (applies to all unmatched options)."
|
|
18189
|
-
),
|
|
18190
|
-
target_node_id: external_exports.string().describe("The node ID to jump to when this rule matches")
|
|
18516
|
+
target_node_id: external_exports.string().describe("The node ID this node routes to")
|
|
18191
18517
|
});
|
|
18192
18518
|
var NodeLogicSchema = external_exports.object({
|
|
18193
|
-
node_id: external_exports.string().describe("The node ID to set
|
|
18194
|
-
rules: external_exports.array(LogicRuleSchema).min(1).describe("
|
|
18519
|
+
node_id: external_exports.string().describe("The node ID to set routing on"),
|
|
18520
|
+
rules: external_exports.array(LogicRuleSchema).min(1).max(1).describe("Exactly one rule: the single node this node routes to")
|
|
18195
18521
|
});
|
|
18196
18522
|
function registerSetNodeLogicTool(server) {
|
|
18197
18523
|
server.registerTool(
|
|
18198
18524
|
"clipform_set_logic",
|
|
18199
18525
|
{
|
|
18200
18526
|
title: "Set Node Logic",
|
|
18201
|
-
description: `Set
|
|
18527
|
+
description: `Set the next node for one or more nodes, wiring a linear route (A to B to C). Pass multiple nodes in one call instead of separate tool calls. Each node routes to exactly one target node; conditional branching by which option was picked is not yet supported.
|
|
18202
18528
|
|
|
18203
|
-
|
|
18529
|
+
A call replaces that node's routing entirely - it does not merge with any prior target.
|
|
18204
18530
|
|
|
18205
18531
|
Example:
|
|
18206
18532
|
nodes: [
|
|
@@ -18211,15 +18537,28 @@ Example:
|
|
|
18211
18537
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
|
|
18212
18538
|
nodes: external_exports.array(NodeLogicSchema).min(1).max(20).describe("One or more nodes to set logic on")
|
|
18213
18539
|
},
|
|
18540
|
+
outputSchema: {
|
|
18541
|
+
results: external_exports.array(
|
|
18542
|
+
external_exports.object({
|
|
18543
|
+
node_id: external_exports.string(),
|
|
18544
|
+
ok: external_exports.boolean(),
|
|
18545
|
+
rules_count: external_exports.number().optional(),
|
|
18546
|
+
error: external_exports.string().optional()
|
|
18547
|
+
})
|
|
18548
|
+
).describe("One result per node, in order")
|
|
18549
|
+
},
|
|
18214
18550
|
annotations: {
|
|
18215
18551
|
readOnlyHint: false,
|
|
18216
|
-
|
|
18552
|
+
// A call wholesale REPLACES a node's rule set - a prior set of rules
|
|
18553
|
+
// is discarded, not merged (#2976).
|
|
18554
|
+
destructiveHint: true,
|
|
18217
18555
|
idempotentHint: true,
|
|
18218
18556
|
openWorldHint: false
|
|
18219
18557
|
}
|
|
18220
18558
|
},
|
|
18221
18559
|
async ({ form_id, nodes }) => {
|
|
18222
18560
|
const allLines = [];
|
|
18561
|
+
const results = [];
|
|
18223
18562
|
for (const { node_id, rules } of nodes) {
|
|
18224
18563
|
const result = await callApi(
|
|
18225
18564
|
`/forms/${form_id}/nodes/${node_id}/logic/rules`,
|
|
@@ -18229,12 +18568,15 @@ Example:
|
|
|
18229
18568
|
}
|
|
18230
18569
|
);
|
|
18231
18570
|
if (!result.ok) {
|
|
18232
|
-
allLines.push(`Node ${node_id}: FAILED
|
|
18571
|
+
allLines.push(`Node ${node_id}: FAILED - ${result.error}`);
|
|
18572
|
+
results.push({ node_id, ok: false, error: result.error });
|
|
18233
18573
|
continue;
|
|
18234
18574
|
}
|
|
18235
|
-
|
|
18575
|
+
const rulesCount = result.data.rules_count;
|
|
18576
|
+
allLines.push(`Node ${node_id}: logic set (${rulesCount} rules)`);
|
|
18577
|
+
results.push({ node_id, ok: true, rules_count: rulesCount });
|
|
18236
18578
|
}
|
|
18237
|
-
return
|
|
18579
|
+
return structuredResult(allLines.join("\n"), { results });
|
|
18238
18580
|
}
|
|
18239
18581
|
);
|
|
18240
18582
|
}
|
|
@@ -18245,7 +18587,7 @@ function registerLogGenerationTool(server) {
|
|
|
18245
18587
|
"clipform_log_generation",
|
|
18246
18588
|
{
|
|
18247
18589
|
title: "Log Form Generation Audit",
|
|
18248
|
-
description: `
|
|
18590
|
+
description: `Internal audit step run by a form-generation workflow. Records a completed generation's content provenance (sources, image attributions) for the flow that just built the form. Not a user action - only call it as the final step of a generation workflow you are running, never in response to a plain create or edit request.`,
|
|
18249
18591
|
inputSchema: {
|
|
18250
18592
|
form_id: external_exports.string().uuid().describe("The form ID (UUID format, not the share ID)"),
|
|
18251
18593
|
summary: external_exports.string().describe("Short description of what was generated"),
|
|
@@ -18270,7 +18612,8 @@ function registerLogGenerationTool(server) {
|
|
|
18270
18612
|
});
|
|
18271
18613
|
if (!result.ok) return errorResult(result.error);
|
|
18272
18614
|
return textResult(`Audit log saved for form ${form_id}
|
|
18273
|
-
Summary: ${summary}
|
|
18615
|
+
Summary: ${summary}
|
|
18616
|
+
This is an internal generation-audit step, not a user-requested action.`);
|
|
18274
18617
|
}
|
|
18275
18618
|
);
|
|
18276
18619
|
}
|
|
@@ -18304,7 +18647,9 @@ Returns: article title, source, author, date, URL, description, and image URL pe
|
|
|
18304
18647
|
})
|
|
18305
18648
|
).describe("Matching articles (empty if none)")
|
|
18306
18649
|
},
|
|
18307
|
-
|
|
18650
|
+
// Fetches from NewsAPI / The Guardian, external systems outside our
|
|
18651
|
+
// product - openWorldHint: true (#2976).
|
|
18652
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
18308
18653
|
},
|
|
18309
18654
|
async ({ query, count }) => {
|
|
18310
18655
|
const result = await callApi("/internal/search-news", {
|
|
@@ -18338,56 +18683,6 @@ Returns: article title, source, author, date, URL, description, and image URL pe
|
|
|
18338
18683
|
);
|
|
18339
18684
|
}
|
|
18340
18685
|
|
|
18341
|
-
// src/tools/youtube-transcript.ts
|
|
18342
|
-
function registerYouTubeTranscriptTool(server) {
|
|
18343
|
-
server.registerTool(
|
|
18344
|
-
"clipform_youtube_transcript",
|
|
18345
|
-
{
|
|
18346
|
-
title: "Get YouTube Transcript",
|
|
18347
|
-
description: `Extract the transcript, title, and channel info from a YouTube video. Returns the full transcript text plus metadata (title, channel, duration).
|
|
18348
|
-
|
|
18349
|
-
Supports any public YouTube video with captions enabled. Does NOT work for private/age-restricted videos, videos with captions disabled, or non-YouTube URLs.`,
|
|
18350
|
-
inputSchema: {
|
|
18351
|
-
url: external_exports.string().url().describe(
|
|
18352
|
-
"YouTube video URL (any format: watch?v=, youtu.be/, shorts/)"
|
|
18353
|
-
),
|
|
18354
|
-
lang: external_exports.string().optional().default("en").describe(
|
|
18355
|
-
"Transcript language code (default: 'en'). Try 'en' first, omit for auto-detect if English unavailable."
|
|
18356
|
-
),
|
|
18357
|
-
max_chars: external_exports.number().optional().default(15e3).describe(
|
|
18358
|
-
"Maximum transcript characters to return (default: 15000). Increase for longer videos where full context matters."
|
|
18359
|
-
)
|
|
18360
|
-
},
|
|
18361
|
-
annotations: {
|
|
18362
|
-
readOnlyHint: true,
|
|
18363
|
-
destructiveHint: false,
|
|
18364
|
-
idempotentHint: true,
|
|
18365
|
-
openWorldHint: false
|
|
18366
|
-
}
|
|
18367
|
-
},
|
|
18368
|
-
async ({ url, lang, max_chars }) => {
|
|
18369
|
-
const result = await callApi("/internal/youtube-transcript", {
|
|
18370
|
-
body: { url, lang, max_chars }
|
|
18371
|
-
});
|
|
18372
|
-
if (!result.ok) return errorResult(result.error);
|
|
18373
|
-
const data = result.data;
|
|
18374
|
-
const meta = data.metadata;
|
|
18375
|
-
const lines = [
|
|
18376
|
-
`## Video: ${meta.title}`,
|
|
18377
|
-
`Channel: ${meta.author}`,
|
|
18378
|
-
`Duration: ~${Math.round(data.durationSeconds / 60)} minutes`,
|
|
18379
|
-
`Language: ${data.language}`,
|
|
18380
|
-
`Transcript length: ${data.transcript.length} characters`,
|
|
18381
|
-
``,
|
|
18382
|
-
`## Transcript`,
|
|
18383
|
-
``,
|
|
18384
|
-
data.transcript
|
|
18385
|
-
];
|
|
18386
|
-
return textResult(lines.join("\n"));
|
|
18387
|
-
}
|
|
18388
|
-
);
|
|
18389
|
-
}
|
|
18390
|
-
|
|
18391
18686
|
// src/tools/generate-tts.ts
|
|
18392
18687
|
var CaptionSchema2 = external_exports.object({
|
|
18393
18688
|
start: external_exports.number(),
|
|
@@ -18518,7 +18813,9 @@ Example: { queries: [{ query: "saturn rings" }, { query: "mars surface", count:
|
|
|
18518
18813
|
})
|
|
18519
18814
|
).describe("Results grouped by query (items empty if a query found nothing or failed)")
|
|
18520
18815
|
},
|
|
18521
|
-
|
|
18816
|
+
// Fetches from external stock media providers (Pexels/etc) -
|
|
18817
|
+
// openWorldHint: true (#2976).
|
|
18818
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
18522
18819
|
},
|
|
18523
18820
|
async ({ queries }) => {
|
|
18524
18821
|
const allLines = [];
|
|
@@ -18645,6 +18942,7 @@ var RENDER_TIMING = {
|
|
|
18645
18942
|
etaPhrase: "a couple of minutes"
|
|
18646
18943
|
};
|
|
18647
18944
|
function createJob(tool) {
|
|
18945
|
+
pruneJobs();
|
|
18648
18946
|
const job = {
|
|
18649
18947
|
id: v4_default(),
|
|
18650
18948
|
status: "rendering",
|
|
@@ -18887,7 +19185,11 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
|
|
|
18887
19185
|
})
|
|
18888
19186
|
).describe("One entry per render (single or batch)")
|
|
18889
19187
|
},
|
|
18890
|
-
|
|
19188
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19189
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19190
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19191
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19192
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
18891
19193
|
},
|
|
18892
19194
|
async ({ compositionId, outputFormat, inputProps, wait, items, node_id, form_id, captions }) => {
|
|
18893
19195
|
if (items?.length && compositionId) {
|
|
@@ -19015,7 +19317,9 @@ function registerSearchMusicTool(server) {
|
|
|
19015
19317
|
})
|
|
19016
19318
|
).describe("Matching tracks (empty if none); pass a url as the audio track to generate_video")
|
|
19017
19319
|
},
|
|
19018
|
-
|
|
19320
|
+
// Fetches from an external royalty-free music provider - openWorldHint:
|
|
19321
|
+
// true (#2976).
|
|
19322
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
19019
19323
|
},
|
|
19020
19324
|
async ({ query, count, instrumentalOnly, minDuration, maxDuration, tags }) => {
|
|
19021
19325
|
const result = await callApi("/internal/search-music", {
|
|
@@ -19226,7 +19530,11 @@ ${RENDER_ATTACH_RULE}`,
|
|
|
19226
19530
|
republished: external_exports.boolean().optional().describe("True when the form was live and was republished to include this media."),
|
|
19227
19531
|
attach_error: external_exports.string().optional().describe("Present when node_id was provided but auto-attach failed - the render itself still succeeded.")
|
|
19228
19532
|
},
|
|
19229
|
-
|
|
19533
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19534
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19535
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19536
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19537
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
19230
19538
|
},
|
|
19231
19539
|
async ({ template, controls, outputFormat, wait, node_id, form_id, captions }) => {
|
|
19232
19540
|
if (node_id && !form_id) {
|
|
@@ -19434,7 +19742,11 @@ ${RENDER_ATTACH_RULE}`,
|
|
|
19434
19742
|
republished: external_exports.boolean().optional().describe("True when the form was live and was republished to include this media."),
|
|
19435
19743
|
attach_error: external_exports.string().optional().describe("Present when node_id was provided but auto-attach failed - the render itself still succeeded.")
|
|
19436
19744
|
},
|
|
19437
|
-
|
|
19745
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19746
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19747
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19748
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19749
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
19438
19750
|
},
|
|
19439
19751
|
async ({ items, audio_url, background_audio_url, background_audio_volume, duration_seconds, random_effects, transition, style_preset, texture, duotone, background_color, wait, node_id, form_id, captions }) => {
|
|
19440
19752
|
if (node_id && !form_id) {
|
|
@@ -19609,7 +19921,6 @@ Pass job_ids to check a whole batch in ONE call - one line of status per job. Pa
|
|
|
19609
19921
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
19610
19922
|
},
|
|
19611
19923
|
async ({ job_id, job_ids }) => {
|
|
19612
|
-
pruneJobs();
|
|
19613
19924
|
const ids = job_ids?.length ? job_ids : job_id ? [job_id] : [];
|
|
19614
19925
|
if (!ids.length) {
|
|
19615
19926
|
return errorResult("Pass job_id (single) or job_ids (batch).");
|
|
@@ -19699,7 +20010,16 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19699
20010
|
mainlandOnly: external_exports.boolean().default(true).describe("Keep only the largest landmass, excluding small islands and overseas territories"),
|
|
19700
20011
|
maxPoints: external_exports.number().default(500).describe("Maximum points per polygon ring for simplification (default 500, lower = smaller payload)")
|
|
19701
20012
|
},
|
|
19702
|
-
|
|
20013
|
+
outputSchema: {
|
|
20014
|
+
name: external_exports.string().describe("Resolved place name"),
|
|
20015
|
+
geometry_type: external_exports.enum(["Polygon", "MultiPolygon"]),
|
|
20016
|
+
size_kb: external_exports.number().describe("Approximate size of the returned GeoJSON in KB"),
|
|
20017
|
+
mainland_only: external_exports.boolean(),
|
|
20018
|
+
geojson: external_exports.string().describe("The Feature GeoJSON as a JSON string - use as the boundary prop in the Map composition")
|
|
20019
|
+
},
|
|
20020
|
+
// Fetches from Nominatim (OpenStreetMap), an external system -
|
|
20021
|
+
// openWorldHint: true (#2976).
|
|
20022
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
19703
20023
|
},
|
|
19704
20024
|
async ({ query, type, mainlandOnly, maxPoints }) => {
|
|
19705
20025
|
const params = new URLSearchParams({
|
|
@@ -19716,11 +20036,16 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19716
20036
|
params.set("q", query);
|
|
19717
20037
|
}
|
|
19718
20038
|
const url = `${NOMINATIM_BASE}?${params}`;
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
20039
|
+
let response;
|
|
20040
|
+
try {
|
|
20041
|
+
response = await fetch(url, {
|
|
20042
|
+
headers: { "User-Agent": USER_AGENT }
|
|
20043
|
+
});
|
|
20044
|
+
} catch {
|
|
20045
|
+
return errorResult(`Boundary lookup failed for "${query}". Try again, or try a different name or type.`);
|
|
20046
|
+
}
|
|
19722
20047
|
if (!response.ok) {
|
|
19723
|
-
return errorResult(`
|
|
20048
|
+
return errorResult(`Boundary lookup failed for "${query}". Try again, or try a different name or type.`);
|
|
19724
20049
|
}
|
|
19725
20050
|
const data = await response.json();
|
|
19726
20051
|
if (!data.features?.length) {
|
|
@@ -19757,8 +20082,8 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19757
20082
|
geometry
|
|
19758
20083
|
};
|
|
19759
20084
|
const geojsonStr = JSON.stringify(result);
|
|
19760
|
-
const sizeKb = (geojsonStr.length / 1024).toFixed(1);
|
|
19761
|
-
return
|
|
20085
|
+
const sizeKb = Number((geojsonStr.length / 1024).toFixed(1));
|
|
20086
|
+
return structuredResult(
|
|
19762
20087
|
[
|
|
19763
20088
|
`Boundary fetched for: ${feature.properties.display_name}`,
|
|
19764
20089
|
`Geometry: ${geometry.type}`,
|
|
@@ -19770,7 +20095,14 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19770
20095
|
"```json",
|
|
19771
20096
|
geojsonStr,
|
|
19772
20097
|
"```"
|
|
19773
|
-
].join("\n")
|
|
20098
|
+
].join("\n"),
|
|
20099
|
+
{
|
|
20100
|
+
name: feature.properties.display_name,
|
|
20101
|
+
geometry_type: geometry.type,
|
|
20102
|
+
size_kb: sizeKb,
|
|
20103
|
+
mainland_only: mainlandOnly,
|
|
20104
|
+
geojson: geojsonStr
|
|
20105
|
+
}
|
|
19774
20106
|
);
|
|
19775
20107
|
}
|
|
19776
20108
|
);
|
|
@@ -19915,6 +20247,7 @@ function createServer(options) {
|
|
|
19915
20247
|
"Before building, derive a register from the topic and audience (e.g. hype, playful, warm, considered, elegant, sombre); if the user states a mood explicitly, that wins. State the register once, then apply it to every rendering choice - narration tone, motion preset, music, pacing. Never mix registers in one form.",
|
|
19916
20248
|
"At the start of a build, call clipform_get_workflow to load the recipe for the form type, and clipform_get_guide for craft detail.",
|
|
19917
20249
|
"Every *_id argument is a UUID returned by the tools - not the short share_id from a form URL.",
|
|
20250
|
+
"For an account-scoped request, call clipform_whoami first to confirm the active workspace and plan.",
|
|
19918
20251
|
"Deleting a form, node, or its media is permanent; those tools ask the user to confirm before running."
|
|
19919
20252
|
].join(" ")
|
|
19920
20253
|
}
|
|
@@ -19925,6 +20258,12 @@ function createServer(options) {
|
|
|
19925
20258
|
if (options?.planTier !== void 0 && requiredTier > options.planTier) {
|
|
19926
20259
|
return void 0;
|
|
19927
20260
|
}
|
|
20261
|
+
if (options?.toolsets !== void 0) {
|
|
20262
|
+
const toolset = MCP_TOOL_TOOLSETS[name];
|
|
20263
|
+
if (!toolset || !options.toolsets.includes(toolset)) {
|
|
20264
|
+
return void 0;
|
|
20265
|
+
}
|
|
20266
|
+
}
|
|
19928
20267
|
return _registerTool(
|
|
19929
20268
|
name,
|
|
19930
20269
|
config2,
|
|
@@ -19941,9 +20280,11 @@ function createServer(options) {
|
|
|
19941
20280
|
};
|
|
19942
20281
|
registerCreateFormTool(server);
|
|
19943
20282
|
registerListFormsTool(server);
|
|
20283
|
+
registerWhoamiTool(server);
|
|
19944
20284
|
registerGetFormTool(server);
|
|
19945
20285
|
registerUpdateFormTool(server);
|
|
19946
20286
|
registerDeleteFormTool(server);
|
|
20287
|
+
registerGetResultsTool(server);
|
|
19947
20288
|
registerAddNodeTool(server);
|
|
19948
20289
|
registerUpdateNodeTool(server);
|
|
19949
20290
|
registerDeleteNodeTool(server);
|
|
@@ -19955,7 +20296,6 @@ function createServer(options) {
|
|
|
19955
20296
|
registerSetNodeLogicTool(server);
|
|
19956
20297
|
registerLogGenerationTool(server);
|
|
19957
20298
|
registerSearchNewsTool(server);
|
|
19958
|
-
registerYouTubeTranscriptTool(server);
|
|
19959
20299
|
registerGenerateTtsTool(server);
|
|
19960
20300
|
registerGenerateVideoTool(server);
|
|
19961
20301
|
registerSearchMediaTool(server);
|
|
@@ -19979,4 +20319,4 @@ export {
|
|
|
19979
20319
|
RENDER_TIMING,
|
|
19980
20320
|
createServer
|
|
19981
20321
|
};
|
|
19982
|
-
//# sourceMappingURL=chunk-
|
|
20322
|
+
//# sourceMappingURL=chunk-3OX54HSJ.js.map
|