@clipform/mcp-server 2.4.0 → 2.5.1
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-3GBZFAEM.js} +511 -169
- package/dist/chunk-3GBZFAEM.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,19 @@ 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
|
}
|
|
17296
|
+
const firstUrlLabel = isPreview ? "PREVIEW" : "FORM";
|
|
17297
|
+
const secondUrlLabel = isPreview ? "CLAIM" : "DASHBOARD";
|
|
17279
17298
|
lines.push(
|
|
17280
17299
|
``,
|
|
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
|
|
17300
|
+
isPreview ? `This form is a preview - claim and publish to start collecting responses.${formUrl ? " Open the CLAIM URL to sign into the account this form already belongs to, then republish" : " Sign in to the account this form already belongs to and 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.`,
|
|
17301
|
+
formUrl ? `The URLs above are exact values; constructed or guessed URLs return 404. Use the ${firstUrlLabel} URL and ${secondUrlLabel} URL as shown.` : `The ${firstUrlLabel} URL above is an exact value; constructed or guessed URLs return 404.`,
|
|
17283
17302
|
`Pass form_id on follow-up tools (get_form, add_node, attach_node_media, etc.).`
|
|
17284
17303
|
);
|
|
17285
17304
|
if (planContext) {
|
|
@@ -17322,7 +17341,7 @@ function registerListFormsTool(server) {
|
|
|
17322
17341
|
"clipform_list_forms",
|
|
17323
17342
|
{
|
|
17324
17343
|
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.`,
|
|
17344
|
+
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
17345
|
inputSchema: {
|
|
17327
17346
|
limit: external_exports.number().int().min(1).max(100).optional().describe("Number of forms to return (default 25, max 100)"),
|
|
17328
17347
|
cursor: external_exports.string().optional().describe(
|
|
@@ -17332,7 +17351,9 @@ function registerListFormsTool(server) {
|
|
|
17332
17351
|
"Filter by tag name(s), comma-separated. AND logic: only forms with ALL tags are returned."
|
|
17333
17352
|
),
|
|
17334
17353
|
published: external_exports.enum(["true", "false"]).optional().describe("Filter by publish status"),
|
|
17335
|
-
search: external_exports.string().optional().describe(
|
|
17354
|
+
search: external_exports.string().optional().describe(
|
|
17355
|
+
"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."
|
|
17356
|
+
),
|
|
17336
17357
|
sort: external_exports.enum(["created_at", "updated_at"]).optional().describe("Sort field (default: created_at)"),
|
|
17337
17358
|
order: external_exports.enum(["asc", "desc"]).optional().describe("Sort order (default: desc, newest first)")
|
|
17338
17359
|
},
|
|
@@ -17344,8 +17365,6 @@ function registerListFormsTool(server) {
|
|
|
17344
17365
|
share_id: external_exports.string(),
|
|
17345
17366
|
is_live: external_exports.boolean(),
|
|
17346
17367
|
has_unpublished_changes: external_exports.boolean(),
|
|
17347
|
-
created_at: external_exports.string(),
|
|
17348
|
-
updated_at: external_exports.string(),
|
|
17349
17368
|
tags: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), color: external_exports.string().nullable() }))
|
|
17350
17369
|
})
|
|
17351
17370
|
).describe("Matching forms (empty if none)"),
|
|
@@ -17379,8 +17398,6 @@ function registerListFormsTool(server) {
|
|
|
17379
17398
|
share_id: f.share_id,
|
|
17380
17399
|
is_live: f.is_live,
|
|
17381
17400
|
has_unpublished_changes: f.has_unpublished_changes,
|
|
17382
|
-
created_at: f.created_at,
|
|
17383
|
-
updated_at: f.updated_at,
|
|
17384
17401
|
tags: f.tags.map((t) => ({ id: t.id, name: t.name, color: t.color }))
|
|
17385
17402
|
})),
|
|
17386
17403
|
next_cursor: data.next_cursor
|
|
@@ -17406,30 +17423,78 @@ function registerListFormsTool(server) {
|
|
|
17406
17423
|
}
|
|
17407
17424
|
|
|
17408
17425
|
// src/lib/format-form.ts
|
|
17426
|
+
function allowedConfigKeys(nodeType) {
|
|
17427
|
+
const schema = NODE_TYPES[nodeType]?.config_schema;
|
|
17428
|
+
const properties = schema?.properties;
|
|
17429
|
+
if (properties) return new Set(Object.keys(properties));
|
|
17430
|
+
const oneOf = schema?.oneOf;
|
|
17431
|
+
if (Array.isArray(oneOf)) {
|
|
17432
|
+
const keys = /* @__PURE__ */ new Set();
|
|
17433
|
+
for (const branch of oneOf) {
|
|
17434
|
+
for (const k of Object.keys(branch?.properties ?? {})) keys.add(k);
|
|
17435
|
+
}
|
|
17436
|
+
return keys;
|
|
17437
|
+
}
|
|
17438
|
+
return /* @__PURE__ */ new Set();
|
|
17439
|
+
}
|
|
17440
|
+
function projectConfig(nodeType, config2) {
|
|
17441
|
+
if (!config2) return void 0;
|
|
17442
|
+
const allowed = allowedConfigKeys(nodeType);
|
|
17443
|
+
const projected = {};
|
|
17444
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
17445
|
+
if (allowed.has(key)) projected[key] = value;
|
|
17446
|
+
}
|
|
17447
|
+
return projected;
|
|
17448
|
+
}
|
|
17409
17449
|
function getFormNodes(data) {
|
|
17410
17450
|
return data.node ?? data.nodes ?? [];
|
|
17411
17451
|
}
|
|
17452
|
+
function buildLogicByNode(nodes) {
|
|
17453
|
+
const logicByNode = /* @__PURE__ */ new Map();
|
|
17454
|
+
for (const node of nodes) {
|
|
17455
|
+
if (node.node_logic && node.node_logic.length > 0) {
|
|
17456
|
+
logicByNode.set(
|
|
17457
|
+
node.id,
|
|
17458
|
+
node.node_logic.flatMap((entry) => entry.actions ?? [])
|
|
17459
|
+
);
|
|
17460
|
+
}
|
|
17461
|
+
}
|
|
17462
|
+
return logicByNode;
|
|
17463
|
+
}
|
|
17464
|
+
function orderNodesWithLogic(data) {
|
|
17465
|
+
const rawNodes = getFormNodes(data);
|
|
17466
|
+
const logicByNode = buildLogicByNode(rawNodes);
|
|
17467
|
+
return { nodes: bfsOrder(rawNodes, logicByNode), logicByNode };
|
|
17468
|
+
}
|
|
17469
|
+
function projectNext(node, logicByNode) {
|
|
17470
|
+
const target = defaultTarget(logicByNode.get(node.id));
|
|
17471
|
+
return target ? { target_id: target.value, target_type: target.type } : void 0;
|
|
17472
|
+
}
|
|
17412
17473
|
function projectFormState(data) {
|
|
17474
|
+
const { nodes, logicByNode } = orderNodesWithLogic(data);
|
|
17413
17475
|
return {
|
|
17414
17476
|
form_id: String(data.form_id ?? ""),
|
|
17415
17477
|
title: String(data.title ?? ""),
|
|
17416
17478
|
is_live: Boolean(data.is_live),
|
|
17417
17479
|
has_unpublished_changes: Boolean(data.has_unpublished_changes),
|
|
17418
|
-
nodes:
|
|
17480
|
+
nodes: nodes.map((q) => {
|
|
17419
17481
|
const options = q.node_options ?? q.options;
|
|
17482
|
+
const config2 = projectConfig(q.type, q.config);
|
|
17483
|
+
const next = projectNext(q, logicByNode);
|
|
17420
17484
|
return {
|
|
17421
17485
|
id: q.id,
|
|
17422
17486
|
type: q.type,
|
|
17423
17487
|
prompt: q.prompt ?? "",
|
|
17424
17488
|
required: Boolean(q.required),
|
|
17425
|
-
...
|
|
17426
|
-
...options && options.length > 0 ? { options: options.map((o) => ({ id: o.id, content: o.content })) } : {}
|
|
17489
|
+
...config2 && Object.keys(config2).length > 0 ? { config: config2 } : {},
|
|
17490
|
+
...options && options.length > 0 ? { options: options.map((o) => ({ id: o.id, content: o.content })) } : {},
|
|
17491
|
+
...next ? { next } : {}
|
|
17427
17492
|
};
|
|
17428
17493
|
})
|
|
17429
17494
|
};
|
|
17430
17495
|
}
|
|
17431
17496
|
function formatFormState(data) {
|
|
17432
|
-
const nodes =
|
|
17497
|
+
const { nodes, logicByNode } = orderNodesWithLogic(data);
|
|
17433
17498
|
const lines = [
|
|
17434
17499
|
`Form: ${data.title}`,
|
|
17435
17500
|
`Form ID: ${data.form_id}`,
|
|
@@ -17446,8 +17511,9 @@ function formatFormState(data) {
|
|
|
17446
17511
|
lines.push(` ${i + 1}. [${q.type}] ${q.prompt || "(no prompt)"}`);
|
|
17447
17512
|
lines.push(` Node ID: ${q.id}`);
|
|
17448
17513
|
if (q.required) lines.push(` Required: yes`);
|
|
17449
|
-
|
|
17450
|
-
|
|
17514
|
+
const config2 = projectConfig(q.type, q.config);
|
|
17515
|
+
if (config2 && Object.keys(config2).length > 0) {
|
|
17516
|
+
lines.push(` Config: ${JSON.stringify(config2)}`);
|
|
17451
17517
|
}
|
|
17452
17518
|
const options = q.node_options ?? q.options;
|
|
17453
17519
|
if (options && options.length > 0) {
|
|
@@ -17455,6 +17521,10 @@ function formatFormState(data) {
|
|
|
17455
17521
|
` Options: ${options.map((o) => o.content).join(", ")}`
|
|
17456
17522
|
);
|
|
17457
17523
|
}
|
|
17524
|
+
const next = projectNext(q, logicByNode);
|
|
17525
|
+
if (next) {
|
|
17526
|
+
lines.push(` -> next: ${next.target_id} (${next.target_type})`);
|
|
17527
|
+
}
|
|
17458
17528
|
}
|
|
17459
17529
|
return lines.join("\n");
|
|
17460
17530
|
}
|
|
@@ -17465,7 +17535,7 @@ function registerGetFormTool(server) {
|
|
|
17465
17535
|
"clipform_get_form",
|
|
17466
17536
|
{
|
|
17467
17537
|
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
|
|
17538
|
+
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
17539
|
inputSchema: {
|
|
17470
17540
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)")
|
|
17471
17541
|
},
|
|
@@ -17481,7 +17551,11 @@ function registerGetFormTool(server) {
|
|
|
17481
17551
|
prompt: external_exports.string(),
|
|
17482
17552
|
required: external_exports.boolean(),
|
|
17483
17553
|
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()
|
|
17554
|
+
options: external_exports.array(external_exports.object({ id: external_exports.string(), content: external_exports.string() })).optional(),
|
|
17555
|
+
next: external_exports.object({
|
|
17556
|
+
target_id: external_exports.string().describe("The node/screen ID this node routes to"),
|
|
17557
|
+
target_type: external_exports.enum(["node", "end_screen", "redirect", "link_list"]).describe("What kind of target target_id is")
|
|
17558
|
+
}).optional().describe("This node's outgoing edge, set via clipform_set_logic. Omitted for a chain tail or terminal node.")
|
|
17485
17559
|
})
|
|
17486
17560
|
).describe("Nodes in sequential order - IDs for update_node / attach_node_media")
|
|
17487
17561
|
},
|
|
@@ -17493,7 +17567,7 @@ function registerGetFormTool(server) {
|
|
|
17493
17567
|
}
|
|
17494
17568
|
},
|
|
17495
17569
|
async ({ form_id }) => {
|
|
17496
|
-
const result = await callApi(`/forms/${form_id}`);
|
|
17570
|
+
const result = await callApi(`/forms/${form_id}?include=logic`);
|
|
17497
17571
|
if (!result.ok) {
|
|
17498
17572
|
return errorResult(result.error);
|
|
17499
17573
|
}
|
|
@@ -17526,11 +17600,20 @@ function registerUpdateFormTool(server) {
|
|
|
17526
17600
|
brand_name: external_exports.string().nullable().optional().describe("AI-PROTECTED: Custom brand name shown in footer. Only set when user explicitly provides."),
|
|
17527
17601
|
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
17602
|
},
|
|
17603
|
+
outputSchema: {
|
|
17604
|
+
form_id: external_exports.string().describe("Form UUID"),
|
|
17605
|
+
updated_fields: external_exports.array(external_exports.string()).describe("Names of the fields that were changed by this call")
|
|
17606
|
+
},
|
|
17529
17607
|
annotations: {
|
|
17530
17608
|
readOnlyHint: false,
|
|
17531
|
-
|
|
17609
|
+
// is_live: true/false publishes, republishes, or unpublishes the
|
|
17610
|
+
// live form, and other fields (title, theme, logo) overwrite what
|
|
17611
|
+
// respondents already see (#2976 research pass 2).
|
|
17612
|
+
destructiveHint: true,
|
|
17532
17613
|
idempotentHint: true,
|
|
17533
|
-
|
|
17614
|
+
// is_live: true makes the form live at a public URL - openWorldHint
|
|
17615
|
+
// per OpenAI's "publishing content" reading (#2976 research pass 2).
|
|
17616
|
+
openWorldHint: true
|
|
17534
17617
|
}
|
|
17535
17618
|
},
|
|
17536
17619
|
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 +17650,64 @@ function registerUpdateFormTool(server) {
|
|
|
17567
17650
|
}
|
|
17568
17651
|
}
|
|
17569
17652
|
const updates = [];
|
|
17570
|
-
|
|
17571
|
-
if (
|
|
17653
|
+
const updatedFields = [];
|
|
17654
|
+
if (title !== void 0) {
|
|
17655
|
+
updates.push(`Title \u2192 "${title}"`);
|
|
17656
|
+
updatedFields.push("title");
|
|
17657
|
+
}
|
|
17658
|
+
if (is_live !== void 0) {
|
|
17572
17659
|
updates.push(`Live \u2192 ${is_live}`);
|
|
17573
|
-
|
|
17660
|
+
updatedFields.push("is_live");
|
|
17661
|
+
}
|
|
17662
|
+
if (show_step_counter !== void 0) {
|
|
17574
17663
|
updates.push(`Step counter \u2192 ${show_step_counter}`);
|
|
17575
|
-
|
|
17664
|
+
updatedFields.push("show_step_counter");
|
|
17665
|
+
}
|
|
17666
|
+
if (disable_back_navigation !== void 0) {
|
|
17576
17667
|
updates.push(`Back navigation \u2192 ${disable_back_navigation ? "disabled" : "enabled"}`);
|
|
17577
|
-
|
|
17668
|
+
updatedFields.push("disable_back_navigation");
|
|
17669
|
+
}
|
|
17670
|
+
if (total_steps !== void 0) {
|
|
17578
17671
|
updates.push(`Total steps \u2192 ${total_steps === null ? "auto" : total_steps}`);
|
|
17579
|
-
|
|
17672
|
+
updatedFields.push("total_steps");
|
|
17673
|
+
}
|
|
17674
|
+
if (primary_color !== void 0) {
|
|
17580
17675
|
updates.push(`Primary color \u2192 ${primary_color}`);
|
|
17581
|
-
|
|
17676
|
+
updatedFields.push("primary_color");
|
|
17677
|
+
}
|
|
17678
|
+
if (background_color !== void 0) {
|
|
17582
17679
|
updates.push(`Background color \u2192 ${background_color}`);
|
|
17583
|
-
|
|
17680
|
+
updatedFields.push("background_color");
|
|
17681
|
+
}
|
|
17682
|
+
if (font_family !== void 0) {
|
|
17584
17683
|
updates.push(`Font \u2192 ${font_family}`);
|
|
17585
|
-
|
|
17684
|
+
updatedFields.push("font_family");
|
|
17685
|
+
}
|
|
17686
|
+
if (description !== void 0) {
|
|
17586
17687
|
updates.push(`Description \u2192 ${description === null ? "cleared" : `"${description}"`}`);
|
|
17587
|
-
|
|
17688
|
+
updatedFields.push("description");
|
|
17689
|
+
}
|
|
17690
|
+
if (author !== void 0) {
|
|
17588
17691
|
updates.push(`Author \u2192 ${author === null ? "cleared" : `"${author}"`}`);
|
|
17589
|
-
|
|
17692
|
+
updatedFields.push("author");
|
|
17693
|
+
}
|
|
17694
|
+
if (logo_url !== void 0) {
|
|
17590
17695
|
updates.push(`Logo URL \u2192 ${logo_url === null ? "cleared" : logo_url}`);
|
|
17591
|
-
|
|
17696
|
+
updatedFields.push("logo_url");
|
|
17697
|
+
}
|
|
17698
|
+
if (show_branding !== void 0) {
|
|
17592
17699
|
updates.push(`Show branding \u2192 ${show_branding}`);
|
|
17593
|
-
|
|
17700
|
+
updatedFields.push("show_branding");
|
|
17701
|
+
}
|
|
17702
|
+
if (brand_name !== void 0) {
|
|
17594
17703
|
updates.push(`Brand name \u2192 ${brand_name === null ? "cleared" : `"${brand_name}"`}`);
|
|
17595
|
-
|
|
17704
|
+
updatedFields.push("brand_name");
|
|
17705
|
+
}
|
|
17706
|
+
if (tags) {
|
|
17596
17707
|
updates.push(`Tags \u2192 [${tags.join(", ")}]`);
|
|
17597
|
-
|
|
17708
|
+
updatedFields.push("tags");
|
|
17709
|
+
}
|
|
17710
|
+
return structuredResult(`Form updated: ${updates.join(", ")}`, { form_id, updated_fields: updatedFields });
|
|
17598
17711
|
}
|
|
17599
17712
|
);
|
|
17600
17713
|
}
|
|
@@ -17636,8 +17749,13 @@ function registerDeleteFormTool(server) {
|
|
|
17636
17749
|
annotations: {
|
|
17637
17750
|
readOnlyHint: false,
|
|
17638
17751
|
destructiveHint: true,
|
|
17639
|
-
|
|
17640
|
-
|
|
17752
|
+
// Deleting an already-trashed form has no further effect -
|
|
17753
|
+
// calling this twice with the same form_id is a no-op the second
|
|
17754
|
+
// time (textbook idempotent delete, #2976).
|
|
17755
|
+
idempotentHint: true,
|
|
17756
|
+
// Removes a form's public page - openWorldHint per OpenAI's
|
|
17757
|
+
// "publishing content" reading (#2976 research pass 2).
|
|
17758
|
+
openWorldHint: true
|
|
17641
17759
|
}
|
|
17642
17760
|
},
|
|
17643
17761
|
async ({ form_id, confirm }) => {
|
|
@@ -17660,13 +17778,210 @@ function registerDeleteFormTool(server) {
|
|
|
17660
17778
|
);
|
|
17661
17779
|
}
|
|
17662
17780
|
|
|
17781
|
+
// src/tools/get-results.ts
|
|
17782
|
+
var MAX_TEXT_LENGTH = 500;
|
|
17783
|
+
var MAX_LABEL_LENGTH = 80;
|
|
17784
|
+
var MAX_RECENT_ANSWERS = 20;
|
|
17785
|
+
function coerceToString(value, maxLength) {
|
|
17786
|
+
if (value === null || value === void 0) return "";
|
|
17787
|
+
if (typeof value === "string") {
|
|
17788
|
+
return value.length > maxLength ? `${value.slice(0, maxLength)}\u2026` : value;
|
|
17789
|
+
}
|
|
17790
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
17791
|
+
return String(value);
|
|
17792
|
+
}
|
|
17793
|
+
let json;
|
|
17794
|
+
try {
|
|
17795
|
+
json = JSON.stringify(value);
|
|
17796
|
+
} catch {
|
|
17797
|
+
return "";
|
|
17798
|
+
}
|
|
17799
|
+
return json.length > maxLength ? `${json.slice(0, maxLength)}\u2026` : json;
|
|
17800
|
+
}
|
|
17801
|
+
function projectNode(node) {
|
|
17802
|
+
const base = {
|
|
17803
|
+
node_id: node.node_id,
|
|
17804
|
+
node_type: node.node_type,
|
|
17805
|
+
prompt: node.node_text,
|
|
17806
|
+
total_responses: node.total_responses,
|
|
17807
|
+
total_views: node.total_views
|
|
17808
|
+
};
|
|
17809
|
+
if (node.node_type === "choice") {
|
|
17810
|
+
const answers = node.answers;
|
|
17811
|
+
return {
|
|
17812
|
+
...base,
|
|
17813
|
+
options: answers.map((a) => ({
|
|
17814
|
+
label: a.answer_text,
|
|
17815
|
+
count: a.count,
|
|
17816
|
+
percentage: a.percentage
|
|
17817
|
+
}))
|
|
17818
|
+
};
|
|
17819
|
+
}
|
|
17820
|
+
if (node.node_type === "payment") {
|
|
17821
|
+
return base;
|
|
17822
|
+
}
|
|
17823
|
+
if (node.node_category === "action") {
|
|
17824
|
+
const answers = node.answers;
|
|
17825
|
+
return {
|
|
17826
|
+
...base,
|
|
17827
|
+
options: answers.map((a) => ({
|
|
17828
|
+
label: coerceToString(a.answer_value, MAX_LABEL_LENGTH),
|
|
17829
|
+
count: a.count,
|
|
17830
|
+
percentage: a.percentage
|
|
17831
|
+
}))
|
|
17832
|
+
};
|
|
17833
|
+
}
|
|
17834
|
+
if (node.node_type === "open") {
|
|
17835
|
+
const answers = node.answers;
|
|
17836
|
+
return {
|
|
17837
|
+
...base,
|
|
17838
|
+
recent_answers: answers.slice(0, MAX_RECENT_ANSWERS).map((a) => ({
|
|
17839
|
+
text: coerceToString(a.answer_value, MAX_TEXT_LENGTH),
|
|
17840
|
+
submitted_at: a.submitted_at
|
|
17841
|
+
}))
|
|
17842
|
+
};
|
|
17843
|
+
}
|
|
17844
|
+
return base;
|
|
17845
|
+
}
|
|
17846
|
+
function registerGetResultsTool(server) {
|
|
17847
|
+
server.registerTool(
|
|
17848
|
+
"clipform_get_results",
|
|
17849
|
+
{
|
|
17850
|
+
title: "Get Clipform Results",
|
|
17851
|
+
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.`,
|
|
17852
|
+
inputSchema: {
|
|
17853
|
+
form_id: external_exports.string().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)")
|
|
17854
|
+
},
|
|
17855
|
+
outputSchema: {
|
|
17856
|
+
total_responses: external_exports.number().describe("Total unique respondents to the form"),
|
|
17857
|
+
nodes: external_exports.array(
|
|
17858
|
+
external_exports.object({
|
|
17859
|
+
node_id: external_exports.string(),
|
|
17860
|
+
node_type: external_exports.string(),
|
|
17861
|
+
prompt: external_exports.string(),
|
|
17862
|
+
total_responses: external_exports.number(),
|
|
17863
|
+
total_views: external_exports.number(),
|
|
17864
|
+
options: external_exports.array(
|
|
17865
|
+
external_exports.object({
|
|
17866
|
+
label: external_exports.string(),
|
|
17867
|
+
count: external_exports.number(),
|
|
17868
|
+
percentage: external_exports.number()
|
|
17869
|
+
})
|
|
17870
|
+
).optional().describe("Answer breakdown - choice and action-category (booking/file_download/shopping) nodes only"),
|
|
17871
|
+
recent_answers: external_exports.array(
|
|
17872
|
+
external_exports.object({
|
|
17873
|
+
text: external_exports.string(),
|
|
17874
|
+
submitted_at: external_exports.string()
|
|
17875
|
+
})
|
|
17876
|
+
).optional().describe("Most recent open-text answers, capped at 20 - open questions only")
|
|
17877
|
+
})
|
|
17878
|
+
).describe("Nodes in form order. Contact/file/draw/camera/payment nodes carry only total_responses/total_views - no per-response detail.")
|
|
17879
|
+
},
|
|
17880
|
+
annotations: {
|
|
17881
|
+
readOnlyHint: true,
|
|
17882
|
+
destructiveHint: false,
|
|
17883
|
+
idempotentHint: true,
|
|
17884
|
+
openWorldHint: false
|
|
17885
|
+
}
|
|
17886
|
+
},
|
|
17887
|
+
async ({ form_id }) => {
|
|
17888
|
+
const result = await callApi(`/forms/${form_id}/results`);
|
|
17889
|
+
if (!result.ok) {
|
|
17890
|
+
return errorResult(result.error);
|
|
17891
|
+
}
|
|
17892
|
+
const data = result.data;
|
|
17893
|
+
const nodes = (data.nodes || []).map(projectNode);
|
|
17894
|
+
const structured = {
|
|
17895
|
+
total_responses: data.total_responses,
|
|
17896
|
+
nodes
|
|
17897
|
+
};
|
|
17898
|
+
if (data.total_responses === 0) {
|
|
17899
|
+
return structuredResult(
|
|
17900
|
+
`This form has no responses yet (${nodes.length} node(s)).`,
|
|
17901
|
+
structured
|
|
17902
|
+
);
|
|
17903
|
+
}
|
|
17904
|
+
const lines = [`${data.total_responses} total response(s) across ${nodes.length} node(s).`];
|
|
17905
|
+
const highlights = nodes.filter((n) => "options" in n || "recent_answers" in n).slice(0, 3);
|
|
17906
|
+
for (const n of highlights) {
|
|
17907
|
+
if ("options" in n && n.options) {
|
|
17908
|
+
const top = n.options.slice(0, 3).map((o) => `${o.label} (${o.percentage}%)`).join(", ");
|
|
17909
|
+
lines.push(`- ${n.prompt}: ${top || "no answers yet"}`);
|
|
17910
|
+
} else if ("recent_answers" in n && n.recent_answers) {
|
|
17911
|
+
lines.push(`- ${n.prompt}: ${n.recent_answers.length} recent open-text answer(s)`);
|
|
17912
|
+
}
|
|
17913
|
+
}
|
|
17914
|
+
return structuredResult(lines.join("\n"), structured);
|
|
17915
|
+
}
|
|
17916
|
+
);
|
|
17917
|
+
}
|
|
17918
|
+
|
|
17919
|
+
// src/tools/whoami.ts
|
|
17920
|
+
function registerWhoamiTool(server) {
|
|
17921
|
+
server.registerTool(
|
|
17922
|
+
"clipform_whoami",
|
|
17923
|
+
{
|
|
17924
|
+
title: "Who Am I",
|
|
17925
|
+
description: `Show the current identity: auth mode (api key, session, or anonymous), active workspace, and plan limits.`,
|
|
17926
|
+
inputSchema: {},
|
|
17927
|
+
outputSchema: {
|
|
17928
|
+
auth_mode: external_exports.enum(["api_key", "session", "anonymous"]).describe("How this session is authenticated"),
|
|
17929
|
+
is_authenticated: external_exports.boolean().describe("True for api_key or session auth; false for anonymous"),
|
|
17930
|
+
workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string() }).nullable().describe("Active workspace, or null when anonymous"),
|
|
17931
|
+
plan: external_exports.object({
|
|
17932
|
+
name: external_exports.string(),
|
|
17933
|
+
tier: external_exports.number(),
|
|
17934
|
+
node_limit: external_exports.number().nullable().describe("Max nodes per form, or null if unlimited"),
|
|
17935
|
+
form_limit: external_exports.number().nullable().describe("Max forms in the workspace, or null if unlimited")
|
|
17936
|
+
}).describe("Plan limits for the active workspace"),
|
|
17937
|
+
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")
|
|
17938
|
+
},
|
|
17939
|
+
annotations: {
|
|
17940
|
+
readOnlyHint: true,
|
|
17941
|
+
destructiveHint: false,
|
|
17942
|
+
idempotentHint: true,
|
|
17943
|
+
openWorldHint: false
|
|
17944
|
+
},
|
|
17945
|
+
_meta: { "anthropic/alwaysLoad": true }
|
|
17946
|
+
},
|
|
17947
|
+
async () => {
|
|
17948
|
+
const result = await callApi("/me", { method: "GET" });
|
|
17949
|
+
if (!result.ok) {
|
|
17950
|
+
return errorResult(result.error);
|
|
17951
|
+
}
|
|
17952
|
+
const me = result.data;
|
|
17953
|
+
const authMode = me.auth_mode ?? "anonymous";
|
|
17954
|
+
const isAuthenticated = me.auth_mode != null && me.auth_mode !== "anonymous";
|
|
17955
|
+
const workspace = me.workspace ? { id: me.workspace.id, name: me.workspace.name } : null;
|
|
17956
|
+
const plan = {
|
|
17957
|
+
name: me.plan?.name ?? "Free",
|
|
17958
|
+
tier: me.plan?.tier ?? 0,
|
|
17959
|
+
node_limit: me.plan?.node_limit ?? null,
|
|
17960
|
+
form_limit: me.plan?.form_limit ?? null
|
|
17961
|
+
};
|
|
17962
|
+
const structured = {
|
|
17963
|
+
auth_mode: authMode,
|
|
17964
|
+
is_authenticated: isAuthenticated,
|
|
17965
|
+
workspace,
|
|
17966
|
+
plan
|
|
17967
|
+
};
|
|
17968
|
+
if (Array.isArray(me.workspaces)) {
|
|
17969
|
+
structured.workspaces = me.workspaces.map((w) => ({ id: w.id, name: w.name }));
|
|
17970
|
+
}
|
|
17971
|
+
const workspaceLine = workspace ? `workspace '${workspace.name}'` : "no workspace (anonymous)";
|
|
17972
|
+
const summary = `Auth: ${authMode}. ${workspaceLine}. Plan: ${plan.name}.`;
|
|
17973
|
+
return structuredResult(summary, structured);
|
|
17974
|
+
}
|
|
17975
|
+
);
|
|
17976
|
+
}
|
|
17977
|
+
|
|
17663
17978
|
// src/tools/add-node.ts
|
|
17664
17979
|
function registerAddNodeTool(server) {
|
|
17665
17980
|
server.registerTool(
|
|
17666
17981
|
"clipform_add_node",
|
|
17667
17982
|
{
|
|
17668
17983
|
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.`,
|
|
17984
|
+
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
17985
|
inputSchema: {
|
|
17671
17986
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
|
|
17672
17987
|
node: NodeSchema.describe("The node to add"),
|
|
@@ -17697,8 +18012,10 @@ function registerAddNodeTool(server) {
|
|
|
17697
18012
|
return errorResult(result.error);
|
|
17698
18013
|
}
|
|
17699
18014
|
const nodeId = result.data.node_id;
|
|
18015
|
+
const warnings = result.data.warnings ?? [];
|
|
18016
|
+
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
18017
|
return structuredResult(
|
|
17701
|
-
`Node added. ID: ${nodeId} | Type: ${node.type}`,
|
|
18018
|
+
`Node added. ID: ${nodeId} | Type: ${node.type}.${relinkNote}`,
|
|
17702
18019
|
{ node_id: nodeId, type: node.type }
|
|
17703
18020
|
);
|
|
17704
18021
|
}
|
|
@@ -17741,7 +18058,10 @@ function registerUpdateNodeTool(server) {
|
|
|
17741
18058
|
},
|
|
17742
18059
|
annotations: {
|
|
17743
18060
|
readOnlyHint: false,
|
|
17744
|
-
|
|
18061
|
+
// Overwrites the node's prompt/config with whatever is sent - a
|
|
18062
|
+
// field explicitly set to null clears it, so this can destroy data,
|
|
18063
|
+
// not just add to it (#2976 research pass 2).
|
|
18064
|
+
destructiveHint: true,
|
|
17745
18065
|
idempotentHint: true,
|
|
17746
18066
|
openWorldHint: false
|
|
17747
18067
|
}
|
|
@@ -17801,7 +18121,9 @@ function registerDeleteNodeTool(server) {
|
|
|
17801
18121
|
annotations: {
|
|
17802
18122
|
readOnlyHint: false,
|
|
17803
18123
|
destructiveHint: true,
|
|
17804
|
-
|
|
18124
|
+
// Deleting an already-deleted node has no further effect -
|
|
18125
|
+
// textbook idempotent delete (#2976).
|
|
18126
|
+
idempotentHint: true,
|
|
17805
18127
|
openWorldHint: false
|
|
17806
18128
|
}
|
|
17807
18129
|
},
|
|
@@ -18034,8 +18356,15 @@ function registerAttachNodeMediaTool(server) {
|
|
|
18034
18356
|
},
|
|
18035
18357
|
annotations: {
|
|
18036
18358
|
readOnlyHint: false,
|
|
18037
|
-
|
|
18038
|
-
|
|
18359
|
+
// Attaching replaces the node's existing media placement (the API
|
|
18360
|
+
// overwrites it, not merges) - the previous placement is gone, even
|
|
18361
|
+
// though the underlying library asset it pointed to is never deleted
|
|
18362
|
+
// (#2976 research pass 2).
|
|
18363
|
+
destructiveHint: true,
|
|
18364
|
+
// Re-attaching the same media_asset_id to the same node converges to
|
|
18365
|
+
// the same placement state - no new resource is created each call
|
|
18366
|
+
// (#2976).
|
|
18367
|
+
idempotentHint: true,
|
|
18039
18368
|
openWorldHint: false
|
|
18040
18369
|
}
|
|
18041
18370
|
},
|
|
@@ -18157,7 +18486,9 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18157
18486
|
annotations: {
|
|
18158
18487
|
readOnlyHint: false,
|
|
18159
18488
|
destructiveHint: true,
|
|
18160
|
-
|
|
18489
|
+
// Deleting already-removed media has no further effect - textbook
|
|
18490
|
+
// idempotent delete (#2976).
|
|
18491
|
+
idempotentHint: true,
|
|
18161
18492
|
openWorldHint: false
|
|
18162
18493
|
}
|
|
18163
18494
|
},
|
|
@@ -18184,23 +18515,20 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18184
18515
|
|
|
18185
18516
|
// src/tools/set-node-logic.ts
|
|
18186
18517
|
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")
|
|
18518
|
+
target_node_id: external_exports.string().describe("The node ID this node routes to")
|
|
18191
18519
|
});
|
|
18192
18520
|
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("
|
|
18521
|
+
node_id: external_exports.string().describe("The node ID to set routing on"),
|
|
18522
|
+
rules: external_exports.array(LogicRuleSchema).min(1).max(1).describe("Exactly one rule: the single node this node routes to")
|
|
18195
18523
|
});
|
|
18196
18524
|
function registerSetNodeLogicTool(server) {
|
|
18197
18525
|
server.registerTool(
|
|
18198
18526
|
"clipform_set_logic",
|
|
18199
18527
|
{
|
|
18200
18528
|
title: "Set Node Logic",
|
|
18201
|
-
description: `Set
|
|
18529
|
+
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
18530
|
|
|
18203
|
-
|
|
18531
|
+
A call replaces that node's routing entirely - it does not merge with any prior target.
|
|
18204
18532
|
|
|
18205
18533
|
Example:
|
|
18206
18534
|
nodes: [
|
|
@@ -18211,15 +18539,28 @@ Example:
|
|
|
18211
18539
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
|
|
18212
18540
|
nodes: external_exports.array(NodeLogicSchema).min(1).max(20).describe("One or more nodes to set logic on")
|
|
18213
18541
|
},
|
|
18542
|
+
outputSchema: {
|
|
18543
|
+
results: external_exports.array(
|
|
18544
|
+
external_exports.object({
|
|
18545
|
+
node_id: external_exports.string(),
|
|
18546
|
+
ok: external_exports.boolean(),
|
|
18547
|
+
rules_count: external_exports.number().optional(),
|
|
18548
|
+
error: external_exports.string().optional()
|
|
18549
|
+
})
|
|
18550
|
+
).describe("One result per node, in order")
|
|
18551
|
+
},
|
|
18214
18552
|
annotations: {
|
|
18215
18553
|
readOnlyHint: false,
|
|
18216
|
-
|
|
18554
|
+
// A call wholesale REPLACES a node's rule set - a prior set of rules
|
|
18555
|
+
// is discarded, not merged (#2976).
|
|
18556
|
+
destructiveHint: true,
|
|
18217
18557
|
idempotentHint: true,
|
|
18218
18558
|
openWorldHint: false
|
|
18219
18559
|
}
|
|
18220
18560
|
},
|
|
18221
18561
|
async ({ form_id, nodes }) => {
|
|
18222
18562
|
const allLines = [];
|
|
18563
|
+
const results = [];
|
|
18223
18564
|
for (const { node_id, rules } of nodes) {
|
|
18224
18565
|
const result = await callApi(
|
|
18225
18566
|
`/forms/${form_id}/nodes/${node_id}/logic/rules`,
|
|
@@ -18229,12 +18570,15 @@ Example:
|
|
|
18229
18570
|
}
|
|
18230
18571
|
);
|
|
18231
18572
|
if (!result.ok) {
|
|
18232
|
-
allLines.push(`Node ${node_id}: FAILED
|
|
18573
|
+
allLines.push(`Node ${node_id}: FAILED - ${result.error}`);
|
|
18574
|
+
results.push({ node_id, ok: false, error: result.error });
|
|
18233
18575
|
continue;
|
|
18234
18576
|
}
|
|
18235
|
-
|
|
18577
|
+
const rulesCount = result.data.rules_count;
|
|
18578
|
+
allLines.push(`Node ${node_id}: logic set (${rulesCount} rules)`);
|
|
18579
|
+
results.push({ node_id, ok: true, rules_count: rulesCount });
|
|
18236
18580
|
}
|
|
18237
|
-
return
|
|
18581
|
+
return structuredResult(allLines.join("\n"), { results });
|
|
18238
18582
|
}
|
|
18239
18583
|
);
|
|
18240
18584
|
}
|
|
@@ -18245,7 +18589,7 @@ function registerLogGenerationTool(server) {
|
|
|
18245
18589
|
"clipform_log_generation",
|
|
18246
18590
|
{
|
|
18247
18591
|
title: "Log Form Generation Audit",
|
|
18248
|
-
description: `
|
|
18592
|
+
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
18593
|
inputSchema: {
|
|
18250
18594
|
form_id: external_exports.string().uuid().describe("The form ID (UUID format, not the share ID)"),
|
|
18251
18595
|
summary: external_exports.string().describe("Short description of what was generated"),
|
|
@@ -18270,7 +18614,8 @@ function registerLogGenerationTool(server) {
|
|
|
18270
18614
|
});
|
|
18271
18615
|
if (!result.ok) return errorResult(result.error);
|
|
18272
18616
|
return textResult(`Audit log saved for form ${form_id}
|
|
18273
|
-
Summary: ${summary}
|
|
18617
|
+
Summary: ${summary}
|
|
18618
|
+
This is an internal generation-audit step, not a user-requested action.`);
|
|
18274
18619
|
}
|
|
18275
18620
|
);
|
|
18276
18621
|
}
|
|
@@ -18304,7 +18649,9 @@ Returns: article title, source, author, date, URL, description, and image URL pe
|
|
|
18304
18649
|
})
|
|
18305
18650
|
).describe("Matching articles (empty if none)")
|
|
18306
18651
|
},
|
|
18307
|
-
|
|
18652
|
+
// Fetches from NewsAPI / The Guardian, external systems outside our
|
|
18653
|
+
// product - openWorldHint: true (#2976).
|
|
18654
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
18308
18655
|
},
|
|
18309
18656
|
async ({ query, count }) => {
|
|
18310
18657
|
const result = await callApi("/internal/search-news", {
|
|
@@ -18338,56 +18685,6 @@ Returns: article title, source, author, date, URL, description, and image URL pe
|
|
|
18338
18685
|
);
|
|
18339
18686
|
}
|
|
18340
18687
|
|
|
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
18688
|
// src/tools/generate-tts.ts
|
|
18392
18689
|
var CaptionSchema2 = external_exports.object({
|
|
18393
18690
|
start: external_exports.number(),
|
|
@@ -18518,7 +18815,9 @@ Example: { queries: [{ query: "saturn rings" }, { query: "mars surface", count:
|
|
|
18518
18815
|
})
|
|
18519
18816
|
).describe("Results grouped by query (items empty if a query found nothing or failed)")
|
|
18520
18817
|
},
|
|
18521
|
-
|
|
18818
|
+
// Fetches from external stock media providers (Pexels/etc) -
|
|
18819
|
+
// openWorldHint: true (#2976).
|
|
18820
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
18522
18821
|
},
|
|
18523
18822
|
async ({ queries }) => {
|
|
18524
18823
|
const allLines = [];
|
|
@@ -18645,6 +18944,7 @@ var RENDER_TIMING = {
|
|
|
18645
18944
|
etaPhrase: "a couple of minutes"
|
|
18646
18945
|
};
|
|
18647
18946
|
function createJob(tool) {
|
|
18947
|
+
pruneJobs();
|
|
18648
18948
|
const job = {
|
|
18649
18949
|
id: v4_default(),
|
|
18650
18950
|
status: "rendering",
|
|
@@ -18887,7 +19187,11 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
|
|
|
18887
19187
|
})
|
|
18888
19188
|
).describe("One entry per render (single or batch)")
|
|
18889
19189
|
},
|
|
18890
|
-
|
|
19190
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19191
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19192
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19193
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19194
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
18891
19195
|
},
|
|
18892
19196
|
async ({ compositionId, outputFormat, inputProps, wait, items, node_id, form_id, captions }) => {
|
|
18893
19197
|
if (items?.length && compositionId) {
|
|
@@ -19015,7 +19319,9 @@ function registerSearchMusicTool(server) {
|
|
|
19015
19319
|
})
|
|
19016
19320
|
).describe("Matching tracks (empty if none); pass a url as the audio track to generate_video")
|
|
19017
19321
|
},
|
|
19018
|
-
|
|
19322
|
+
// Fetches from an external royalty-free music provider - openWorldHint:
|
|
19323
|
+
// true (#2976).
|
|
19324
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
19019
19325
|
},
|
|
19020
19326
|
async ({ query, count, instrumentalOnly, minDuration, maxDuration, tags }) => {
|
|
19021
19327
|
const result = await callApi("/internal/search-music", {
|
|
@@ -19226,7 +19532,11 @@ ${RENDER_ATTACH_RULE}`,
|
|
|
19226
19532
|
republished: external_exports.boolean().optional().describe("True when the form was live and was republished to include this media."),
|
|
19227
19533
|
attach_error: external_exports.string().optional().describe("Present when node_id was provided but auto-attach failed - the render itself still succeeded.")
|
|
19228
19534
|
},
|
|
19229
|
-
|
|
19535
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19536
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19537
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19538
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19539
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
19230
19540
|
},
|
|
19231
19541
|
async ({ template, controls, outputFormat, wait, node_id, form_id, captions }) => {
|
|
19232
19542
|
if (node_id && !form_id) {
|
|
@@ -19434,7 +19744,11 @@ ${RENDER_ATTACH_RULE}`,
|
|
|
19434
19744
|
republished: external_exports.boolean().optional().describe("True when the form was live and was republished to include this media."),
|
|
19435
19745
|
attach_error: external_exports.string().optional().describe("Present when node_id was provided but auto-attach failed - the render itself still succeeded.")
|
|
19436
19746
|
},
|
|
19437
|
-
|
|
19747
|
+
// node_id auto-attach replaces the node's existing media placement and
|
|
19748
|
+
// republishes an already-live form - an indirect but real overwrite +
|
|
19749
|
+
// publish (#2976 research pass 2), even though this tool works with no
|
|
19750
|
+
// node_id too (destructive/openWorld apply "even in only select modes").
|
|
19751
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
|
|
19438
19752
|
},
|
|
19439
19753
|
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
19754
|
if (node_id && !form_id) {
|
|
@@ -19609,7 +19923,6 @@ Pass job_ids to check a whole batch in ONE call - one line of status per job. Pa
|
|
|
19609
19923
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
19610
19924
|
},
|
|
19611
19925
|
async ({ job_id, job_ids }) => {
|
|
19612
|
-
pruneJobs();
|
|
19613
19926
|
const ids = job_ids?.length ? job_ids : job_id ? [job_id] : [];
|
|
19614
19927
|
if (!ids.length) {
|
|
19615
19928
|
return errorResult("Pass job_id (single) or job_ids (batch).");
|
|
@@ -19699,7 +20012,16 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19699
20012
|
mainlandOnly: external_exports.boolean().default(true).describe("Keep only the largest landmass, excluding small islands and overseas territories"),
|
|
19700
20013
|
maxPoints: external_exports.number().default(500).describe("Maximum points per polygon ring for simplification (default 500, lower = smaller payload)")
|
|
19701
20014
|
},
|
|
19702
|
-
|
|
20015
|
+
outputSchema: {
|
|
20016
|
+
name: external_exports.string().describe("Resolved place name"),
|
|
20017
|
+
geometry_type: external_exports.enum(["Polygon", "MultiPolygon"]),
|
|
20018
|
+
size_kb: external_exports.number().describe("Approximate size of the returned GeoJSON in KB"),
|
|
20019
|
+
mainland_only: external_exports.boolean(),
|
|
20020
|
+
geojson: external_exports.string().describe("The Feature GeoJSON as a JSON string - use as the boundary prop in the Map composition")
|
|
20021
|
+
},
|
|
20022
|
+
// Fetches from Nominatim (OpenStreetMap), an external system -
|
|
20023
|
+
// openWorldHint: true (#2976).
|
|
20024
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
19703
20025
|
},
|
|
19704
20026
|
async ({ query, type, mainlandOnly, maxPoints }) => {
|
|
19705
20027
|
const params = new URLSearchParams({
|
|
@@ -19716,11 +20038,16 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19716
20038
|
params.set("q", query);
|
|
19717
20039
|
}
|
|
19718
20040
|
const url = `${NOMINATIM_BASE}?${params}`;
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
20041
|
+
let response;
|
|
20042
|
+
try {
|
|
20043
|
+
response = await fetch(url, {
|
|
20044
|
+
headers: { "User-Agent": USER_AGENT }
|
|
20045
|
+
});
|
|
20046
|
+
} catch {
|
|
20047
|
+
return errorResult(`Boundary lookup failed for "${query}". Try again, or try a different name or type.`);
|
|
20048
|
+
}
|
|
19722
20049
|
if (!response.ok) {
|
|
19723
|
-
return errorResult(`
|
|
20050
|
+
return errorResult(`Boundary lookup failed for "${query}". Try again, or try a different name or type.`);
|
|
19724
20051
|
}
|
|
19725
20052
|
const data = await response.json();
|
|
19726
20053
|
if (!data.features?.length) {
|
|
@@ -19757,8 +20084,8 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19757
20084
|
geometry
|
|
19758
20085
|
};
|
|
19759
20086
|
const geojsonStr = JSON.stringify(result);
|
|
19760
|
-
const sizeKb = (geojsonStr.length / 1024).toFixed(1);
|
|
19761
|
-
return
|
|
20087
|
+
const sizeKb = Number((geojsonStr.length / 1024).toFixed(1));
|
|
20088
|
+
return structuredResult(
|
|
19762
20089
|
[
|
|
19763
20090
|
`Boundary fetched for: ${feature.properties.display_name}`,
|
|
19764
20091
|
`Geometry: ${geometry.type}`,
|
|
@@ -19770,7 +20097,14 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
|
|
|
19770
20097
|
"```json",
|
|
19771
20098
|
geojsonStr,
|
|
19772
20099
|
"```"
|
|
19773
|
-
].join("\n")
|
|
20100
|
+
].join("\n"),
|
|
20101
|
+
{
|
|
20102
|
+
name: feature.properties.display_name,
|
|
20103
|
+
geometry_type: geometry.type,
|
|
20104
|
+
size_kb: sizeKb,
|
|
20105
|
+
mainland_only: mainlandOnly,
|
|
20106
|
+
geojson: geojsonStr
|
|
20107
|
+
}
|
|
19774
20108
|
);
|
|
19775
20109
|
}
|
|
19776
20110
|
);
|
|
@@ -19915,6 +20249,7 @@ function createServer(options) {
|
|
|
19915
20249
|
"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
20250
|
"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
20251
|
"Every *_id argument is a UUID returned by the tools - not the short share_id from a form URL.",
|
|
20252
|
+
"For an account-scoped request, call clipform_whoami first to confirm the active workspace and plan.",
|
|
19918
20253
|
"Deleting a form, node, or its media is permanent; those tools ask the user to confirm before running."
|
|
19919
20254
|
].join(" ")
|
|
19920
20255
|
}
|
|
@@ -19925,6 +20260,12 @@ function createServer(options) {
|
|
|
19925
20260
|
if (options?.planTier !== void 0 && requiredTier > options.planTier) {
|
|
19926
20261
|
return void 0;
|
|
19927
20262
|
}
|
|
20263
|
+
if (options?.toolsets !== void 0) {
|
|
20264
|
+
const toolset = MCP_TOOL_TOOLSETS[name];
|
|
20265
|
+
if (!toolset || !options.toolsets.includes(toolset)) {
|
|
20266
|
+
return void 0;
|
|
20267
|
+
}
|
|
20268
|
+
}
|
|
19928
20269
|
return _registerTool(
|
|
19929
20270
|
name,
|
|
19930
20271
|
config2,
|
|
@@ -19941,9 +20282,11 @@ function createServer(options) {
|
|
|
19941
20282
|
};
|
|
19942
20283
|
registerCreateFormTool(server);
|
|
19943
20284
|
registerListFormsTool(server);
|
|
20285
|
+
registerWhoamiTool(server);
|
|
19944
20286
|
registerGetFormTool(server);
|
|
19945
20287
|
registerUpdateFormTool(server);
|
|
19946
20288
|
registerDeleteFormTool(server);
|
|
20289
|
+
registerGetResultsTool(server);
|
|
19947
20290
|
registerAddNodeTool(server);
|
|
19948
20291
|
registerUpdateNodeTool(server);
|
|
19949
20292
|
registerDeleteNodeTool(server);
|
|
@@ -19955,7 +20298,6 @@ function createServer(options) {
|
|
|
19955
20298
|
registerSetNodeLogicTool(server);
|
|
19956
20299
|
registerLogGenerationTool(server);
|
|
19957
20300
|
registerSearchNewsTool(server);
|
|
19958
|
-
registerYouTubeTranscriptTool(server);
|
|
19959
20301
|
registerGenerateTtsTool(server);
|
|
19960
20302
|
registerGenerateVideoTool(server);
|
|
19961
20303
|
registerSearchMediaTool(server);
|
|
@@ -19979,4 +20321,4 @@ export {
|
|
|
19979
20321
|
RENDER_TIMING,
|
|
19980
20322
|
createServer
|
|
19981
20323
|
};
|
|
19982
|
-
//# sourceMappingURL=chunk-
|
|
20324
|
+
//# sourceMappingURL=chunk-3GBZFAEM.js.map
|