@clipform/mcp-server 3.0.0 → 3.3.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 +2 -2
- package/README.template.md +1 -1
- package/dist/auth-context.d.ts +13 -0
- package/dist/auth-context.js +1 -1
- package/dist/{chunk-KCF7ZVDI.js → chunk-22BV4NSB.js} +2 -2
- package/dist/{chunk-QBPE32AV.js → chunk-4XCXDH3I.js} +359 -83
- package/dist/chunk-4XCXDH3I.js.map +1 -0
- package/dist/{chunk-HZMMOSEM.js → chunk-A5CFGL27.js} +1 -1
- package/dist/chunk-A5CFGL27.js.map +1 -0
- package/dist/{chunk-EXV5M7YV.js → chunk-ELK26KWM.js} +2 -2
- package/dist/{chunk-KNOCYZHG.js → chunk-SEZ37YSH.js} +105 -22
- package/dist/chunk-SEZ37YSH.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/prompts.js +3 -3
- package/dist/resources.js +3 -3
- package/dist/server.js +5 -5
- package/package.json +3 -2
- package/dist/chunk-HZMMOSEM.js.map +0 -1
- package/dist/chunk-KNOCYZHG.js.map +0 -1
- package/dist/chunk-QBPE32AV.js.map +0 -1
- /package/dist/{chunk-KCF7ZVDI.js.map → chunk-22BV4NSB.js.map} +0 -0
- /package/dist/{chunk-EXV5M7YV.js.map → chunk-ELK26KWM.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getWorkflowText,
|
|
7
7
|
objectType,
|
|
8
8
|
registerPrompts
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-22BV4NSB.js";
|
|
10
10
|
import {
|
|
11
11
|
GUIDE_TYPES,
|
|
12
12
|
QUIZ_VARIANTS,
|
|
@@ -15,31 +15,36 @@ import {
|
|
|
15
15
|
getGuideUri,
|
|
16
16
|
guideFallbackText,
|
|
17
17
|
registerResources
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ELK26KWM.js";
|
|
19
19
|
import {
|
|
20
20
|
ACTIVE_NODE_TYPES,
|
|
21
21
|
CONTACT_FIELDS,
|
|
22
22
|
FEATURE_FLAGS,
|
|
23
23
|
FORM_TYPE_ALIASES,
|
|
24
24
|
FULL_CAPS,
|
|
25
|
+
MCP_ANON_PUBLISH_GATE,
|
|
26
|
+
MCP_TOOL_SECURITY,
|
|
25
27
|
MCP_TOOL_TIERS,
|
|
26
28
|
MCP_TOOL_TOOLSETS,
|
|
27
29
|
NODE_TYPES,
|
|
28
30
|
NON_COUNTABLE_TYPES,
|
|
31
|
+
authChallengeMeta,
|
|
29
32
|
bfsOrder,
|
|
30
33
|
callApi,
|
|
31
34
|
capsFromToolsets,
|
|
32
35
|
defaultTarget,
|
|
33
36
|
errorResult,
|
|
34
37
|
exposedCompositionIds,
|
|
38
|
+
isAnonymousAccountRequired,
|
|
35
39
|
resolveFormType,
|
|
36
40
|
structuredResult,
|
|
37
41
|
textResult
|
|
38
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-SEZ37YSH.js";
|
|
39
43
|
import {
|
|
40
44
|
getMcpAuth,
|
|
45
|
+
runWithMcpAuth,
|
|
41
46
|
runWithMcpTool
|
|
42
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-A5CFGL27.js";
|
|
43
48
|
import {
|
|
44
49
|
__commonJS,
|
|
45
50
|
__export,
|
|
@@ -114,7 +119,7 @@ var require_code = __commonJS({
|
|
|
114
119
|
}
|
|
115
120
|
exports._ = _;
|
|
116
121
|
var plus = new _Code("+");
|
|
117
|
-
function
|
|
122
|
+
function str2(strs, ...args) {
|
|
118
123
|
const expr = [safeStringify(strs[0])];
|
|
119
124
|
let i = 0;
|
|
120
125
|
while (i < args.length) {
|
|
@@ -125,7 +130,7 @@ var require_code = __commonJS({
|
|
|
125
130
|
optimize(expr);
|
|
126
131
|
return new _Code(expr);
|
|
127
132
|
}
|
|
128
|
-
exports.str =
|
|
133
|
+
exports.str = str2;
|
|
129
134
|
function addCodeArg(code, arg) {
|
|
130
135
|
if (arg instanceof _Code)
|
|
131
136
|
code.push(...arg._items);
|
|
@@ -168,7 +173,7 @@ var require_code = __commonJS({
|
|
|
168
173
|
return;
|
|
169
174
|
}
|
|
170
175
|
function strConcat(c1, c2) {
|
|
171
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
176
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str2`${c1}${c2}`;
|
|
172
177
|
}
|
|
173
178
|
exports.strConcat = strConcat;
|
|
174
179
|
function interpolate(x) {
|
|
@@ -1130,22 +1135,22 @@ var require_util = __commonJS({
|
|
|
1130
1135
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
1131
1136
|
}
|
|
1132
1137
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
1133
|
-
function unescapeFragment(
|
|
1134
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
1138
|
+
function unescapeFragment(str2) {
|
|
1139
|
+
return unescapeJsonPointer(decodeURIComponent(str2));
|
|
1135
1140
|
}
|
|
1136
1141
|
exports.unescapeFragment = unescapeFragment;
|
|
1137
|
-
function escapeFragment(
|
|
1138
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
1142
|
+
function escapeFragment(str2) {
|
|
1143
|
+
return encodeURIComponent(escapeJsonPointer(str2));
|
|
1139
1144
|
}
|
|
1140
1145
|
exports.escapeFragment = escapeFragment;
|
|
1141
|
-
function escapeJsonPointer(
|
|
1142
|
-
if (typeof
|
|
1143
|
-
return `${
|
|
1144
|
-
return
|
|
1146
|
+
function escapeJsonPointer(str2) {
|
|
1147
|
+
if (typeof str2 == "number")
|
|
1148
|
+
return `${str2}`;
|
|
1149
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1145
1150
|
}
|
|
1146
1151
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
1147
|
-
function unescapeJsonPointer(
|
|
1148
|
-
return
|
|
1152
|
+
function unescapeJsonPointer(str2) {
|
|
1153
|
+
return str2.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1149
1154
|
}
|
|
1150
1155
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
1151
1156
|
function eachItem(xs, f) {
|
|
@@ -2170,8 +2175,8 @@ var require_json_schema_traverse = __commonJS({
|
|
|
2170
2175
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
2171
2176
|
}
|
|
2172
2177
|
}
|
|
2173
|
-
function escapeJsonPtr(
|
|
2174
|
-
return
|
|
2178
|
+
function escapeJsonPtr(str2) {
|
|
2179
|
+
return str2.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2175
2180
|
}
|
|
2176
2181
|
}
|
|
2177
2182
|
});
|
|
@@ -3235,10 +3240,10 @@ var require_utils = __commonJS({
|
|
|
3235
3240
|
return { host, isIPV6: false };
|
|
3236
3241
|
}
|
|
3237
3242
|
}
|
|
3238
|
-
function findToken(
|
|
3243
|
+
function findToken(str2, token) {
|
|
3239
3244
|
let ind = 0;
|
|
3240
|
-
for (let i = 0; i <
|
|
3241
|
-
if (
|
|
3245
|
+
for (let i = 0; i < str2.length; i++) {
|
|
3246
|
+
if (str2[i] === token) ind++;
|
|
3242
3247
|
}
|
|
3243
3248
|
return ind;
|
|
3244
3249
|
}
|
|
@@ -3975,7 +3980,7 @@ var require_core = __commonJS({
|
|
|
3975
3980
|
var util_1 = require_util();
|
|
3976
3981
|
var $dataRefSchema = require_data();
|
|
3977
3982
|
var uri_1 = require_uri();
|
|
3978
|
-
var defaultRegExp = (
|
|
3983
|
+
var defaultRegExp = (str2, flags) => new RegExp(str2, flags);
|
|
3979
3984
|
defaultRegExp.code = "new RegExp";
|
|
3980
3985
|
var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"];
|
|
3981
3986
|
var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -4770,16 +4775,16 @@ var require_ucs2length = __commonJS({
|
|
|
4770
4775
|
"../../node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/runtime/ucs2length.js"(exports) {
|
|
4771
4776
|
"use strict";
|
|
4772
4777
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4773
|
-
function ucs2length(
|
|
4774
|
-
const len =
|
|
4778
|
+
function ucs2length(str2) {
|
|
4779
|
+
const len = str2.length;
|
|
4775
4780
|
let length = 0;
|
|
4776
4781
|
let pos = 0;
|
|
4777
4782
|
let value;
|
|
4778
4783
|
while (pos < len) {
|
|
4779
4784
|
length++;
|
|
4780
|
-
value =
|
|
4785
|
+
value = str2.charCodeAt(pos++);
|
|
4781
4786
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
4782
|
-
value =
|
|
4787
|
+
value = str2.charCodeAt(pos);
|
|
4783
4788
|
if ((value & 64512) === 56320)
|
|
4784
4789
|
pos++;
|
|
4785
4790
|
}
|
|
@@ -6662,8 +6667,8 @@ var require_formats = __commonJS({
|
|
|
6662
6667
|
}
|
|
6663
6668
|
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
6664
6669
|
var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
6665
|
-
function date3(
|
|
6666
|
-
const matches = DATE.exec(
|
|
6670
|
+
function date3(str2) {
|
|
6671
|
+
const matches = DATE.exec(str2);
|
|
6667
6672
|
if (!matches)
|
|
6668
6673
|
return false;
|
|
6669
6674
|
const year = +matches[1];
|
|
@@ -6682,8 +6687,8 @@ var require_formats = __commonJS({
|
|
|
6682
6687
|
}
|
|
6683
6688
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
6684
6689
|
function getTime(strictTimeZone) {
|
|
6685
|
-
return function time3(
|
|
6686
|
-
const matches = TIME.exec(
|
|
6690
|
+
return function time3(str2) {
|
|
6691
|
+
const matches = TIME.exec(str2);
|
|
6687
6692
|
if (!matches)
|
|
6688
6693
|
return false;
|
|
6689
6694
|
const hr = +matches[1];
|
|
@@ -6729,8 +6734,8 @@ var require_formats = __commonJS({
|
|
|
6729
6734
|
var DATE_TIME_SEPARATOR = /t|\s/i;
|
|
6730
6735
|
function getDateTime(strictTimeZone) {
|
|
6731
6736
|
const time3 = getTime(strictTimeZone);
|
|
6732
|
-
return function date_time(
|
|
6733
|
-
const dateTime =
|
|
6737
|
+
return function date_time(str2) {
|
|
6738
|
+
const dateTime = str2.split(DATE_TIME_SEPARATOR);
|
|
6734
6739
|
return dateTime.length === 2 && date3(dateTime[0]) && time3(dateTime[1]);
|
|
6735
6740
|
};
|
|
6736
6741
|
}
|
|
@@ -6755,13 +6760,13 @@ var require_formats = __commonJS({
|
|
|
6755
6760
|
}
|
|
6756
6761
|
var NOT_URI_FRAGMENT = /\/|:/;
|
|
6757
6762
|
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
6758
|
-
function uri(
|
|
6759
|
-
return NOT_URI_FRAGMENT.test(
|
|
6763
|
+
function uri(str2) {
|
|
6764
|
+
return NOT_URI_FRAGMENT.test(str2) && URI.test(str2);
|
|
6760
6765
|
}
|
|
6761
6766
|
var BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
6762
|
-
function byte(
|
|
6767
|
+
function byte(str2) {
|
|
6763
6768
|
BYTE.lastIndex = 0;
|
|
6764
|
-
return BYTE.test(
|
|
6769
|
+
return BYTE.test(str2);
|
|
6765
6770
|
}
|
|
6766
6771
|
var MIN_INT32 = -(2 ** 31);
|
|
6767
6772
|
var MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -6775,11 +6780,11 @@ var require_formats = __commonJS({
|
|
|
6775
6780
|
return true;
|
|
6776
6781
|
}
|
|
6777
6782
|
var Z_ANCHOR = /[^\\]\\Z/;
|
|
6778
|
-
function regex(
|
|
6779
|
-
if (Z_ANCHOR.test(
|
|
6783
|
+
function regex(str2) {
|
|
6784
|
+
if (Z_ANCHOR.test(str2))
|
|
6780
6785
|
return false;
|
|
6781
6786
|
try {
|
|
6782
|
-
new RegExp(
|
|
6787
|
+
new RegExp(str2);
|
|
6783
6788
|
return true;
|
|
6784
6789
|
} catch (e) {
|
|
6785
6790
|
return false;
|
|
@@ -7115,14 +7120,14 @@ function promiseAllObject(promisesObj) {
|
|
|
7115
7120
|
}
|
|
7116
7121
|
function randomString(length = 10) {
|
|
7117
7122
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
7118
|
-
let
|
|
7123
|
+
let str2 = "";
|
|
7119
7124
|
for (let i = 0; i < length; i++) {
|
|
7120
|
-
|
|
7125
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
7121
7126
|
}
|
|
7122
|
-
return
|
|
7127
|
+
return str2;
|
|
7123
7128
|
}
|
|
7124
|
-
function esc(
|
|
7125
|
-
return JSON.stringify(
|
|
7129
|
+
function esc(str2) {
|
|
7130
|
+
return JSON.stringify(str2);
|
|
7126
7131
|
}
|
|
7127
7132
|
var captureStackTrace = Error.captureStackTrace ? Error.captureStackTrace : (..._args) => {
|
|
7128
7133
|
};
|
|
@@ -7210,8 +7215,8 @@ var getParsedType = (data) => {
|
|
|
7210
7215
|
};
|
|
7211
7216
|
var propertyKeyTypes = /* @__PURE__ */ new Set(["string", "number", "symbol"]);
|
|
7212
7217
|
var primitiveTypes = /* @__PURE__ */ new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]);
|
|
7213
|
-
function escapeRegex(
|
|
7214
|
-
return
|
|
7218
|
+
function escapeRegex(str2) {
|
|
7219
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7215
7220
|
}
|
|
7216
7221
|
function clone(inst, def, params) {
|
|
7217
7222
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -17187,6 +17192,48 @@ function applyNodeDefaults(node) {
|
|
|
17187
17192
|
}
|
|
17188
17193
|
}
|
|
17189
17194
|
|
|
17195
|
+
// src/lib/workspace-target.ts
|
|
17196
|
+
var PINNED_WORKSPACE_ERROR = "This connection is pinned to one workspace, so a workspace argument can't be used. Omit it to act in the connection's workspace.";
|
|
17197
|
+
async function runWithWorkspaceTarget(args, run) {
|
|
17198
|
+
const input = args && typeof args === "object" ? args : {};
|
|
17199
|
+
const workspaceId = typeof input.workspace_id === "string" && input.workspace_id ? input.workspace_id : void 0;
|
|
17200
|
+
const formId = typeof input.form_id === "string" && input.form_id ? input.form_id : void 0;
|
|
17201
|
+
if (!workspaceId && !formId) return run();
|
|
17202
|
+
const ctx = getMcpAuth();
|
|
17203
|
+
const resolve = ctx?.resolveWorkspace;
|
|
17204
|
+
if (!resolve) {
|
|
17205
|
+
if (workspaceId) return errorResult(PINNED_WORKSPACE_ERROR);
|
|
17206
|
+
return run();
|
|
17207
|
+
}
|
|
17208
|
+
if (workspaceId && !formId && workspaceId === ctx.default_workspace_id) {
|
|
17209
|
+
return run();
|
|
17210
|
+
}
|
|
17211
|
+
let resolved;
|
|
17212
|
+
try {
|
|
17213
|
+
resolved = await resolve({
|
|
17214
|
+
...workspaceId ? { workspace_id: workspaceId } : {},
|
|
17215
|
+
...formId ? { form_id: formId } : {}
|
|
17216
|
+
});
|
|
17217
|
+
} catch (err) {
|
|
17218
|
+
return errorResult(err instanceof Error ? err.message : "Could not resolve that workspace.");
|
|
17219
|
+
}
|
|
17220
|
+
if (!resolved) return run();
|
|
17221
|
+
return runWithMcpAuth(
|
|
17222
|
+
{
|
|
17223
|
+
...ctx,
|
|
17224
|
+
workspace_id: resolved.workspace_id,
|
|
17225
|
+
api_key: resolved.api_key
|
|
17226
|
+
},
|
|
17227
|
+
run
|
|
17228
|
+
);
|
|
17229
|
+
}
|
|
17230
|
+
var workspaceIdInput = external_exports.string().uuid().optional().describe(
|
|
17231
|
+
"Workspace to act in (id from clipform_whoami workspaces). Omit to use the connection's default workspace."
|
|
17232
|
+
);
|
|
17233
|
+
var formIdTargetInput = external_exports.string().uuid().optional().describe(
|
|
17234
|
+
"The form this is for (from clipform_create_form) - a routing hint only, to pick the right workspace. The result still lands in the workspace library, not on a node. Required on an anonymous session, or this lands in a different workspace and clipform_attach_node_media will not find it."
|
|
17235
|
+
);
|
|
17236
|
+
|
|
17190
17237
|
// src/tools/create-form.ts
|
|
17191
17238
|
function registerCreateFormTool(server) {
|
|
17192
17239
|
server.registerTool(
|
|
@@ -17211,6 +17258,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17211
17258
|
]
|
|
17212
17259
|
}`,
|
|
17213
17260
|
inputSchema: {
|
|
17261
|
+
workspace_id: workspaceIdInput,
|
|
17214
17262
|
title: external_exports.string().describe("Form title"),
|
|
17215
17263
|
nodes: external_exports.array(NodeSchema).min(1).describe("Ordered list of nodes/steps for the form"),
|
|
17216
17264
|
show_step_counter: external_exports.boolean().optional().describe("Show step counter (e.g. '1/5'). Set true for quizzes."),
|
|
@@ -17229,7 +17277,8 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17229
17277
|
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"),
|
|
17230
17278
|
dashboard_url: external_exports.string().optional().describe("Builder URL (sign in to edit)"),
|
|
17231
17279
|
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"),
|
|
17232
|
-
plan: external_exports.object({ name: external_exports.string(), auth_mode: external_exports.string() }).optional().describe("Plan and auth context for this session")
|
|
17280
|
+
plan: external_exports.object({ name: external_exports.string(), auth_mode: external_exports.string() }).optional().describe("Plan and auth context for this session"),
|
|
17281
|
+
workspace_name: external_exports.string().optional().describe("Name of the workspace the form was created in")
|
|
17233
17282
|
},
|
|
17234
17283
|
annotations: {
|
|
17235
17284
|
readOnlyHint: false,
|
|
@@ -17318,7 +17367,8 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17318
17367
|
timeoutMs: 12e4
|
|
17319
17368
|
});
|
|
17320
17369
|
if (!createResult.ok) {
|
|
17321
|
-
|
|
17370
|
+
const message = `Form NOT created (nothing was saved): ${createResult.error}`;
|
|
17371
|
+
return isAnonymousAccountRequired(createResult) ? errorResult(message, authChallengeMeta("invalid_token", message)) : errorResult(message);
|
|
17322
17372
|
}
|
|
17323
17373
|
const { data } = createResult;
|
|
17324
17374
|
const formId = data.form_id;
|
|
@@ -17342,6 +17392,7 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17342
17392
|
isPreview ? `Form created as a preview draft.` : `Form created and live.`,
|
|
17343
17393
|
``,
|
|
17344
17394
|
`Title: ${title}`,
|
|
17395
|
+
...planContext?.is_authenticated && planContext.workspace_name ? [`Workspace: ${planContext.workspace_name}`] : [],
|
|
17345
17396
|
`Form ID: ${formId}`,
|
|
17346
17397
|
``,
|
|
17347
17398
|
`Node IDs (use directly with attach_node_media / update_node - no get_form round-trip needed):`,
|
|
@@ -17389,15 +17440,111 @@ Example: A form that asks a question, collects contact info, then finishes:
|
|
|
17389
17440
|
// runtime. structuredContent should return only relevant data (ChatGPT
|
|
17390
17441
|
// App Directory response-hygiene policy, #797).
|
|
17391
17442
|
nodes: createdNodes.map((n) => ({ id: n.id, type: n.type, prompt: n.prompt })),
|
|
17392
|
-
...planContext ? { plan: { name: planContext.plan_name, auth_mode: planContext.auth_mode } } : {}
|
|
17443
|
+
...planContext ? { plan: { name: planContext.plan_name, auth_mode: planContext.auth_mode } } : {},
|
|
17444
|
+
...planContext?.is_authenticated && planContext.workspace_name ? { workspace_name: planContext.workspace_name } : {}
|
|
17393
17445
|
}
|
|
17394
17446
|
};
|
|
17395
17447
|
}
|
|
17396
17448
|
);
|
|
17397
17449
|
}
|
|
17398
17450
|
|
|
17399
|
-
//
|
|
17400
|
-
|
|
17451
|
+
// ../form-sources/dist/types.js
|
|
17452
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
17453
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
17454
|
+
var DEFAULT_QUESTION_CAP = 50;
|
|
17455
|
+
var MAX_OPTIONS = 10;
|
|
17456
|
+
|
|
17457
|
+
// ../form-sources/dist/patterns.js
|
|
17458
|
+
var firstMatch = (url, patterns) => {
|
|
17459
|
+
for (const re of patterns) {
|
|
17460
|
+
const id = re.exec(url)?.[1];
|
|
17461
|
+
if (id)
|
|
17462
|
+
return id;
|
|
17463
|
+
}
|
|
17464
|
+
return null;
|
|
17465
|
+
};
|
|
17466
|
+
var VIDEOASK_PATTERNS = [
|
|
17467
|
+
/^https?:\/\/api\.videoask\.com\/forms\/sharing\/(f[a-z0-9]{8})(?:[/?#]|$)/i,
|
|
17468
|
+
/^https?:\/\/(?:www\.)?videoask\.com\/(f[a-z0-9]{8})(?:[/?#]|$)/i
|
|
17469
|
+
];
|
|
17470
|
+
var detectVideoAskId = (url) => firstMatch(url, VIDEOASK_PATTERNS);
|
|
17471
|
+
var TYPEFORM_ATTR_KINDS = {
|
|
17472
|
+
"data-tf-widget": "inline",
|
|
17473
|
+
"data-tf-popup": "popup",
|
|
17474
|
+
"data-tf-slider": "slider",
|
|
17475
|
+
"data-tf-sidetab": "slider",
|
|
17476
|
+
"data-tf-popover": "popover"
|
|
17477
|
+
};
|
|
17478
|
+
var TALLY_ATTR_KINDS = {
|
|
17479
|
+
"data-tally-src": "inline",
|
|
17480
|
+
"data-tally-open": "popup"
|
|
17481
|
+
};
|
|
17482
|
+
var EMBED_ATTR_NAMES = [...Object.keys(TYPEFORM_ATTR_KINDS), ...Object.keys(TALLY_ATTR_KINDS)];
|
|
17483
|
+
function pushUnique(out, ref) {
|
|
17484
|
+
if (!out.some((r) => r.id === ref.id))
|
|
17485
|
+
out.push(ref);
|
|
17486
|
+
}
|
|
17487
|
+
function detectVideoAskScript(text) {
|
|
17488
|
+
const out = [];
|
|
17489
|
+
if (!/VIDEOASK_EMBED_CONFIG/.test(text))
|
|
17490
|
+
return out;
|
|
17491
|
+
for (const m of text.matchAll(/videoask\.com\/(f[a-z0-9]{8})(?![a-z0-9])/gi))
|
|
17492
|
+
pushUnique(out, { id: m[1].toLowerCase() });
|
|
17493
|
+
return out;
|
|
17494
|
+
}
|
|
17495
|
+
|
|
17496
|
+
// ../form-sources/dist/providers/videoask.js
|
|
17497
|
+
function optionText(o) {
|
|
17498
|
+
if (typeof o === "string")
|
|
17499
|
+
return o;
|
|
17500
|
+
if (isObj(o))
|
|
17501
|
+
return str(o.label) || str(o.text) || str(o.value) || str(o.title);
|
|
17502
|
+
return "";
|
|
17503
|
+
}
|
|
17504
|
+
var videoask = {
|
|
17505
|
+
id: "videoask",
|
|
17506
|
+
detect: detectVideoAskId,
|
|
17507
|
+
detectScript: detectVideoAskScript,
|
|
17508
|
+
fetchSpec: (id) => ({ url: `https://api.videoask.com/forms/sharing/${id}`, responseType: "json" }),
|
|
17509
|
+
/**
|
|
17510
|
+
* `GET api.videoask.com/forms/sharing/<id>`. Personal/account fields
|
|
17511
|
+
* (`author_id`, `organization_id`, ...) are dropped by construction: only the
|
|
17512
|
+
* fields below are copied.
|
|
17513
|
+
*/
|
|
17514
|
+
summarize(id, json, opts) {
|
|
17515
|
+
if (!isObj(json) || !Array.isArray(json.questions))
|
|
17516
|
+
throw new Error("not a VideoAsk form payload");
|
|
17517
|
+
const transcriptCap = opts?.transcriptCap;
|
|
17518
|
+
const questions = json.questions.slice(0, opts?.questionCap ?? DEFAULT_QUESTION_CAP).map((q) => {
|
|
17519
|
+
const o = isObj(q) ? q : {};
|
|
17520
|
+
const transcript = str(o.transcription);
|
|
17521
|
+
return {
|
|
17522
|
+
label: str(o.label),
|
|
17523
|
+
type: str(o.type),
|
|
17524
|
+
options: (Array.isArray(o.options) ? o.options : []).map(optionText).filter(Boolean).slice(0, MAX_OPTIONS),
|
|
17525
|
+
transcript: transcriptCap === void 0 ? transcript : transcript.slice(0, transcriptCap)
|
|
17526
|
+
};
|
|
17527
|
+
});
|
|
17528
|
+
return {
|
|
17529
|
+
provider: "videoask",
|
|
17530
|
+
id,
|
|
17531
|
+
title: str(json.title),
|
|
17532
|
+
questions,
|
|
17533
|
+
meta: {
|
|
17534
|
+
...str(json.created_at) ? { created_at: str(json.created_at) } : {},
|
|
17535
|
+
...str(json.updated_at) ? { last_edited_at: str(json.updated_at) } : {},
|
|
17536
|
+
// hide_branding is a paid-plan feature.
|
|
17537
|
+
paid_plan: json.hide_branding === true,
|
|
17538
|
+
asks_contact: ["requires_contact_email", "requires_contact_info", "requires_contact_name", "requires_contact_phone_number"].some((k) => json[k] === true),
|
|
17539
|
+
...str(json.status) ? { status: str(json.status) } : {}
|
|
17540
|
+
}
|
|
17541
|
+
};
|
|
17542
|
+
}
|
|
17543
|
+
};
|
|
17544
|
+
|
|
17545
|
+
// ../form-sources/dist/index.js
|
|
17546
|
+
var IMPORTABLE_PROVIDERS = ["google", "typeform", "tally"];
|
|
17547
|
+
function detectProvider(url) {
|
|
17401
17548
|
let parsed;
|
|
17402
17549
|
try {
|
|
17403
17550
|
parsed = new URL(url);
|
|
@@ -17405,17 +17552,22 @@ function detectImportPlatform(url) {
|
|
|
17405
17552
|
return null;
|
|
17406
17553
|
}
|
|
17407
17554
|
const hostname2 = parsed.hostname.toLowerCase();
|
|
17408
|
-
if (hostname2 === "docs.google.com" || hostname2 === "forms.gle")
|
|
17555
|
+
if (hostname2 === "docs.google.com" || hostname2 === "forms.gle")
|
|
17409
17556
|
return "google";
|
|
17410
|
-
|
|
17411
|
-
if (hostname2.includes("typeform.com") && parsed.pathname.includes("/to/")) {
|
|
17557
|
+
if (hostname2.includes("typeform.com") && parsed.pathname.includes("/to/"))
|
|
17412
17558
|
return "typeform";
|
|
17413
|
-
|
|
17414
|
-
if (hostname2.includes("tally.so")) {
|
|
17559
|
+
if (hostname2.includes("tally.so"))
|
|
17415
17560
|
return "tally";
|
|
17416
|
-
|
|
17561
|
+
if (videoask.detect(url))
|
|
17562
|
+
return "videoask";
|
|
17417
17563
|
return null;
|
|
17418
17564
|
}
|
|
17565
|
+
|
|
17566
|
+
// src/tools/import-form.ts
|
|
17567
|
+
function detectImportPlatform(url) {
|
|
17568
|
+
const provider = detectProvider(url);
|
|
17569
|
+
return provider && IMPORTABLE_PROVIDERS.includes(provider) ? provider : null;
|
|
17570
|
+
}
|
|
17419
17571
|
function pluralQuestions(n) {
|
|
17420
17572
|
return `${n} question${n === 1 ? "" : "s"}`;
|
|
17421
17573
|
}
|
|
@@ -17426,6 +17578,7 @@ function registerImportFormTool(server) {
|
|
|
17426
17578
|
title: "Import Form",
|
|
17427
17579
|
description: `Convert a public Google Form, Typeform, or Tally form into a new Clipform. Supported URLs: Google Forms (docs.google.com or forms.gle - must be shared as "Anyone with the link"), Typeform (form.typeform.com/to/...), and Tally (tally.so - must be public). The source form must be publicly accessible; a private form's questions cannot be read. The new Clipform is created as a DRAFT - review it, then republish with clipform_update_form (is_live: true) once it's ready. Some question types don't map cleanly to a Clipform node and are skipped rather than guessed at; the result always reports exactly what was imported vs skipped, and why.`,
|
|
17428
17580
|
inputSchema: {
|
|
17581
|
+
workspace_id: workspaceIdInput,
|
|
17429
17582
|
url: external_exports.string().url().describe("Public URL of the Google Form, Typeform, or Tally form to import")
|
|
17430
17583
|
},
|
|
17431
17584
|
outputSchema: {
|
|
@@ -17438,7 +17591,8 @@ function registerImportFormTool(server) {
|
|
|
17438
17591
|
skipped: external_exports.number().describe("Questions that could not be converted"),
|
|
17439
17592
|
skipped_details: external_exports.array(external_exports.record(external_exports.string(), external_exports.string())).describe("Per-skipped-question title, reason, and source question type"),
|
|
17440
17593
|
warnings: external_exports.array(external_exports.string()).describe("Non-fatal issues surfaced during the import")
|
|
17441
|
-
})
|
|
17594
|
+
}),
|
|
17595
|
+
workspace_name: external_exports.string().optional().describe("Name of the workspace the form was created in")
|
|
17442
17596
|
},
|
|
17443
17597
|
annotations: {
|
|
17444
17598
|
readOnlyHint: false,
|
|
@@ -17462,6 +17616,7 @@ function registerImportFormTool(server) {
|
|
|
17462
17616
|
}
|
|
17463
17617
|
const me = meResult.data;
|
|
17464
17618
|
const workspaceId = me.workspace?.id ?? null;
|
|
17619
|
+
const workspaceName = me.auth_mode && me.auth_mode !== "anonymous" ? me.workspace?.name ?? void 0 : void 0;
|
|
17465
17620
|
if (!workspaceId) {
|
|
17466
17621
|
return errorResult(
|
|
17467
17622
|
"No workspace available. This usually means authentication is misconfigured - check your API key or OAuth connection."
|
|
@@ -17504,7 +17659,9 @@ function registerImportFormTool(server) {
|
|
|
17504
17659
|
`Pass form_id (the UUID above, not a share_id) to follow-up tools (get_form, add_node, update_node, etc.).`
|
|
17505
17660
|
);
|
|
17506
17661
|
}
|
|
17662
|
+
if (formId && workspaceName) lines.push(``, `Created in workspace '${workspaceName}'.`);
|
|
17507
17663
|
return structuredResult(lines.join("\n"), {
|
|
17664
|
+
...workspaceName ? { workspace_name: workspaceName } : {},
|
|
17508
17665
|
form_id: formId,
|
|
17509
17666
|
viewer_url: viewerUrl,
|
|
17510
17667
|
import_summary: {
|
|
@@ -17528,6 +17685,7 @@ function registerListFormsTool(server) {
|
|
|
17528
17685
|
title: "List Clipforms",
|
|
17529
17686
|
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.`,
|
|
17530
17687
|
inputSchema: {
|
|
17688
|
+
workspace_id: workspaceIdInput,
|
|
17531
17689
|
limit: external_exports.number().int().min(1).max(100).optional().describe("Number of forms to return (default 25, max 100)"),
|
|
17532
17690
|
cursor: external_exports.string().optional().describe(
|
|
17533
17691
|
"Pagination cursor from previous response's next_cursor"
|
|
@@ -17811,6 +17969,11 @@ function registerGetFormTool(server) {
|
|
|
17811
17969
|
}
|
|
17812
17970
|
|
|
17813
17971
|
// src/tools/update-form.ts
|
|
17972
|
+
if (MCP_ANON_PUBLISH_GATE.tool !== "clipform_update_form") {
|
|
17973
|
+
throw new Error(
|
|
17974
|
+
`update-form.ts registers "clipform_update_form" but MCP_ANON_PUBLISH_GATE.tool is "${MCP_ANON_PUBLISH_GATE.tool}" - update both sides together (packages/config/index.js and this file's registerTool call) or the Claude lazy-auth HTTP gate silently stops matching this tool.`
|
|
17975
|
+
);
|
|
17976
|
+
}
|
|
17814
17977
|
function registerUpdateFormTool(server) {
|
|
17815
17978
|
server.registerTool(
|
|
17816
17979
|
"clipform_update_form",
|
|
@@ -17871,7 +18034,7 @@ function registerUpdateFormTool(server) {
|
|
|
17871
18034
|
body
|
|
17872
18035
|
});
|
|
17873
18036
|
if (!result.ok) {
|
|
17874
|
-
return errorResult(result.error);
|
|
18037
|
+
return isAnonymousAccountRequired(result) ? errorResult(result.error, authChallengeMeta("invalid_token", result.error)) : errorResult(result.error);
|
|
17875
18038
|
}
|
|
17876
18039
|
}
|
|
17877
18040
|
if (tags) {
|
|
@@ -17880,7 +18043,7 @@ function registerUpdateFormTool(server) {
|
|
|
17880
18043
|
body: { tags }
|
|
17881
18044
|
});
|
|
17882
18045
|
if (!tagResult.ok) {
|
|
17883
|
-
return errorResult(tagResult.error);
|
|
18046
|
+
return isAnonymousAccountRequired(tagResult) ? errorResult(tagResult.error, authChallengeMeta("invalid_token", tagResult.error)) : errorResult(tagResult.error);
|
|
17884
18047
|
}
|
|
17885
18048
|
}
|
|
17886
18049
|
const updates = [];
|
|
@@ -18347,7 +18510,8 @@ function registerWhoamiTool(server) {
|
|
|
18347
18510
|
node_limit: external_exports.number().nullable().describe("Max nodes per form, or null if unlimited"),
|
|
18348
18511
|
form_limit: external_exports.number().nullable().describe("Max forms in the workspace, or null if unlimited")
|
|
18349
18512
|
}).describe("Plan limits for the active workspace"),
|
|
18350
|
-
workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })).optional().describe("
|
|
18513
|
+
workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })).optional().describe("Workspaces the caller can target with workspace_id - present for session (dashboard) auth and the OAuth connector"),
|
|
18514
|
+
default_workspace_id: external_exports.string().optional().describe("OAuth connector only: the workspace used when a tool call omits workspace_id"),
|
|
18351
18515
|
links: external_exports.object({
|
|
18352
18516
|
dashboard: external_exports.string().describe("Dashboard home for the active workspace."),
|
|
18353
18517
|
forms: external_exports.string().describe("Forms list for the active workspace.")
|
|
@@ -18385,11 +18549,26 @@ function registerWhoamiTool(server) {
|
|
|
18385
18549
|
if (Array.isArray(me.workspaces)) {
|
|
18386
18550
|
structured.workspaces = me.workspaces.map((w) => ({ id: w.id, name: w.name }));
|
|
18387
18551
|
}
|
|
18552
|
+
const ctx = getMcpAuth();
|
|
18553
|
+
if (!structured.workspaces && ctx?.listWorkspaces) {
|
|
18554
|
+
try {
|
|
18555
|
+
const list = await ctx.listWorkspaces();
|
|
18556
|
+
structured.workspaces = list.map((w) => ({ id: w.id, name: w.name }));
|
|
18557
|
+
if (ctx.default_workspace_id) structured.default_workspace_id = ctx.default_workspace_id;
|
|
18558
|
+
} catch {
|
|
18559
|
+
}
|
|
18560
|
+
}
|
|
18388
18561
|
if (me.links && typeof me.links.dashboard === "string" && typeof me.links.forms === "string") {
|
|
18389
18562
|
structured.links = { dashboard: me.links.dashboard, forms: me.links.forms };
|
|
18390
18563
|
}
|
|
18391
18564
|
const workspaceLine = workspace ? `workspace '${workspace.name}'` : "no workspace (anonymous)";
|
|
18392
18565
|
const lines = [`Auth: ${authMode}. ${workspaceLine}. Plan: ${plan.name}.`];
|
|
18566
|
+
if (Array.isArray(structured.workspaces) && ctx?.listWorkspaces) {
|
|
18567
|
+
const list = structured.workspaces;
|
|
18568
|
+
lines.push(
|
|
18569
|
+
`Workspaces you can act in (pass one of these ids as the workspace argument to target it; omit it to use the default): ` + list.map((w) => `${w.name} (${w.id})`).join(", ")
|
|
18570
|
+
);
|
|
18571
|
+
}
|
|
18393
18572
|
if (structured.links) {
|
|
18394
18573
|
const links = structured.links;
|
|
18395
18574
|
lines.push(`Dashboard: ${links.dashboard}`, `Forms list: ${links.forms}`);
|
|
@@ -18578,7 +18757,8 @@ function registerDeleteNodeTool(server) {
|
|
|
18578
18757
|
|
|
18579
18758
|
// src/tools/upload-media-asset.ts
|
|
18580
18759
|
var MediaAssetItemSchema = external_exports.object({
|
|
18581
|
-
media_type: external_exports.enum(["video", "still"]).describe("Type of media"),
|
|
18760
|
+
media_type: external_exports.enum(["video", "still", "document"]).describe("Type of media. Use document for a consent-agreement PDF (url required, PDF only)."),
|
|
18761
|
+
filename: external_exports.string().max(200).optional().describe("Display filename for a document (media_type document). Defaults to the URL's file name."),
|
|
18582
18762
|
media_source: external_exports.enum(["uploaded", "recorded"]).default("uploaded").describe("How the media was created"),
|
|
18583
18763
|
url: external_exports.string().url().optional().describe("Public URL that links directly to the media file itself. When provided, the server fetches and stores it directly."),
|
|
18584
18764
|
captions: external_exports.array(
|
|
@@ -18624,9 +18804,16 @@ function mapFileEntryToItem(file) {
|
|
|
18624
18804
|
if (category === "video") {
|
|
18625
18805
|
return { ok: true, item: { media_type: "video", media_source: "uploaded", url: file.download_url } };
|
|
18626
18806
|
}
|
|
18807
|
+
if (mimeType === "application/pdf") {
|
|
18808
|
+
const filename = file.name ?? file.file_name;
|
|
18809
|
+
return {
|
|
18810
|
+
ok: true,
|
|
18811
|
+
item: { media_type: "document", media_source: "uploaded", url: file.download_url, ...filename ? { filename } : {} }
|
|
18812
|
+
};
|
|
18813
|
+
}
|
|
18627
18814
|
return {
|
|
18628
18815
|
ok: false,
|
|
18629
|
-
error: `Attached file type "${mimeType || "unknown"}" is not supported. Attach an image or
|
|
18816
|
+
error: `Attached file type "${mimeType || "unknown"}" is not supported. Attach an image, video or PDF file, or provide a direct URL instead.`
|
|
18630
18817
|
};
|
|
18631
18818
|
}
|
|
18632
18819
|
function registerUploadMediaAssetTool(server) {
|
|
@@ -18636,38 +18823,55 @@ function registerUploadMediaAssetTool(server) {
|
|
|
18636
18823
|
title: "Upload Media Asset",
|
|
18637
18824
|
description: `Put one or more media files into your workspace media library (max 10, uploaded sequentially). This is step one of attaching media to a node - follow up with clipform_attach_node_media to place the returned media_asset_id on a node.
|
|
18638
18825
|
|
|
18639
|
-
When a public URL is provided, the media is fetched and stored automatically.
|
|
18826
|
+
When a public URL is provided, the media is fetched and stored automatically. The API picks where each asset is stored based on its size and type. Word-level captions from clipform_generate_tts enable per-word highlighting in the viewer once attached - pass the caption_ref it returned instead of hand-copying the captions array.
|
|
18827
|
+
|
|
18828
|
+
PDFs (media_type document) are for consent agreements: they return a document descriptor, not a media_asset_id.
|
|
18640
18829
|
|
|
18641
18830
|
Provide media via \`items\` (explicit type + URL), \`files\` (files attached by the host client), or both. At least one is required.`,
|
|
18642
18831
|
inputSchema: {
|
|
18832
|
+
workspace_id: workspaceIdInput,
|
|
18833
|
+
form_id: formIdTargetInput,
|
|
18643
18834
|
items: external_exports.array(MediaAssetItemSchema).min(1).max(10).optional().describe("One or more media items to create as workspace library assets"),
|
|
18644
|
-
files: external_exports.array(FileEntrySchema).min(1).max(10).optional().describe("Files attached by the host client. Each becomes a video or still library asset based on its declared mime_type.")
|
|
18835
|
+
files: external_exports.array(FileEntrySchema).min(1).max(10).optional().describe("Files attached by the host client. Each becomes a video or still library asset (or a consent-agreement document for application/pdf) based on its declared mime_type.")
|
|
18645
18836
|
},
|
|
18646
18837
|
outputSchema: {
|
|
18647
18838
|
results: external_exports.array(
|
|
18648
18839
|
external_exports.object({
|
|
18649
|
-
media_asset_id: external_exports.string().optional(),
|
|
18840
|
+
media_asset_id: external_exports.string().optional().describe("Absent for documents"),
|
|
18650
18841
|
media_type: external_exports.string(),
|
|
18842
|
+
document: external_exports.object({
|
|
18843
|
+
storage_path: external_exports.string(),
|
|
18844
|
+
filename: external_exports.string(),
|
|
18845
|
+
url: external_exports.string(),
|
|
18846
|
+
version: external_exports.string()
|
|
18847
|
+
}).optional().describe("Consent-agreement descriptor (documents only) - set as consent_items[i].document via clipform_update_node"),
|
|
18651
18848
|
stored: external_exports.boolean().describe("Bytes already stored (URL-based); false when an upload_url is pending"),
|
|
18652
18849
|
upload_url: external_exports.string().optional(),
|
|
18653
18850
|
upload_method: external_exports.string().optional(),
|
|
18654
18851
|
ok: external_exports.boolean(),
|
|
18655
18852
|
error: external_exports.string().optional()
|
|
18656
18853
|
})
|
|
18657
|
-
).describe("One result per item, in order")
|
|
18854
|
+
).describe("One result per item, in order"),
|
|
18855
|
+
workspace_name: external_exports.string().optional().describe("Name of the workspace the media library entries were created in")
|
|
18658
18856
|
},
|
|
18659
18857
|
annotations: {
|
|
18660
18858
|
readOnlyHint: false,
|
|
18661
18859
|
destructiveHint: false,
|
|
18662
18860
|
idempotentHint: false,
|
|
18663
|
-
|
|
18861
|
+
// Open-world: fetches an arbitrary caller-supplied URL (and may hand
|
|
18862
|
+
// larger video to a third-party streaming provider) (#4434).
|
|
18863
|
+
openWorldHint: true
|
|
18664
18864
|
},
|
|
18665
18865
|
// Apps SDK convention (openai-apps-sdk-examples#185): a host client only
|
|
18666
18866
|
// hydrates a file attachment into a TOP-LEVEL tool argument named here -
|
|
18667
18867
|
// same mechanism create-form.ts uses for anthropic/alwaysLoad.
|
|
18668
18868
|
_meta: { "openai/fileParams": ["files"] }
|
|
18669
18869
|
},
|
|
18670
|
-
|
|
18870
|
+
// `form_id` is consumed centrally by `runWithWorkspaceTarget` (server.ts)
|
|
18871
|
+
// BEFORE this handler runs - it's a routing hint only (#4621), never
|
|
18872
|
+
// forwarded to the API: this route takes no form_id and /me below still
|
|
18873
|
+
// resolves the (now-targeted) workspace.
|
|
18874
|
+
async ({ items, files, form_id: _form_id }) => {
|
|
18671
18875
|
if ((!items || items.length === 0) && (!files || files.length === 0)) {
|
|
18672
18876
|
return errorResult("Provide at least one media item via `items` or `files`.");
|
|
18673
18877
|
}
|
|
@@ -18681,6 +18885,7 @@ Provide media via \`items\` (explicit type + URL), \`files\` (files attached by
|
|
|
18681
18885
|
}
|
|
18682
18886
|
const me = meResult.data;
|
|
18683
18887
|
const workspaceId = me.workspace?.id ?? null;
|
|
18888
|
+
const workspaceName = me.auth_mode && me.auth_mode !== "anonymous" ? me.workspace?.name ?? void 0 : void 0;
|
|
18684
18889
|
if (!workspaceId) {
|
|
18685
18890
|
return errorResult(
|
|
18686
18891
|
"No workspace available. This usually means authentication is misconfigured - check your API key or OAuth connection."
|
|
@@ -18712,6 +18917,7 @@ Provide media via \`items\` (explicit type + URL), \`files\` (files attached by
|
|
|
18712
18917
|
media_source: item.media_source
|
|
18713
18918
|
};
|
|
18714
18919
|
if (item.url) createBody.url = item.url;
|
|
18920
|
+
if (item.filename) createBody.filename = item.filename;
|
|
18715
18921
|
if (item.captions) createBody.captions = item.captions;
|
|
18716
18922
|
if (item.caption_ref) createBody.caption_ref = item.caption_ref;
|
|
18717
18923
|
if (item.ai_generated !== void 0) createBody.ai_generated = item.ai_generated;
|
|
@@ -18722,6 +18928,33 @@ Provide media via \`items\` (explicit type + URL), \`files\` (files attached by
|
|
|
18722
18928
|
results.push({ media_type: item.media_type, stored: false, ok: false, error: created.error });
|
|
18723
18929
|
continue;
|
|
18724
18930
|
}
|
|
18931
|
+
if (item.media_type === "document") {
|
|
18932
|
+
const d = created.data;
|
|
18933
|
+
if (typeof d.storage_path !== "string" || typeof d.url !== "string" || typeof d.version !== "string" || !d.version) {
|
|
18934
|
+
const error2 = "document upload did not return a document descriptor.";
|
|
18935
|
+
lines.push(`FAILED: ${error2}`);
|
|
18936
|
+
lines.push("");
|
|
18937
|
+
results.push({ media_type: "document", stored: false, ok: false, error: error2 });
|
|
18938
|
+
continue;
|
|
18939
|
+
}
|
|
18940
|
+
const document = {
|
|
18941
|
+
storage_path: d.storage_path,
|
|
18942
|
+
filename: String(d.filename ?? item.filename ?? "agreement.pdf"),
|
|
18943
|
+
url: d.url,
|
|
18944
|
+
version: d.version
|
|
18945
|
+
};
|
|
18946
|
+
successCount++;
|
|
18947
|
+
results.push({ media_type: "document", stored: true, ok: true, document });
|
|
18948
|
+
lines.push(
|
|
18949
|
+
[
|
|
18950
|
+
`Document stored: ${document.filename}`,
|
|
18951
|
+
`Descriptor: ${JSON.stringify(document)}`,
|
|
18952
|
+
`Set this on a Details node consent item via clipform_update_node: consent_items[i].document = <descriptor>, require_scroll_to_accept: true. Do not use clipform_attach_node_media.`
|
|
18953
|
+
].join("\n")
|
|
18954
|
+
);
|
|
18955
|
+
lines.push("");
|
|
18956
|
+
continue;
|
|
18957
|
+
}
|
|
18725
18958
|
const mediaAssetId = created.data.media_asset_id;
|
|
18726
18959
|
if (!mediaAssetId) {
|
|
18727
18960
|
const error2 = `upload did not return a media asset id (route ${String(created.data.route ?? "unknown")}).`;
|
|
@@ -18763,9 +18996,12 @@ Provide media via \`items\` (explicit type + URL), \`files\` (files attached by
|
|
|
18763
18996
|
lines.unshift(`Media asset upload: ${successCount}/${entries.length} succeeded
|
|
18764
18997
|
`);
|
|
18765
18998
|
}
|
|
18766
|
-
|
|
18999
|
+
if (results.some((r) => r.ok && r.media_asset_id)) {
|
|
19000
|
+
lines.push(`Attach these to nodes with clipform_attach_node_media.`);
|
|
19001
|
+
}
|
|
18767
19002
|
if (successCount === 0) return errorResult(lines.join("\n"));
|
|
18768
|
-
|
|
19003
|
+
if (workspaceName) lines.push(`Workspace: ${workspaceName}`);
|
|
19004
|
+
return structuredResult(lines.join("\n"), { results, ...workspaceName ? { workspace_name: workspaceName } : {} });
|
|
18769
19005
|
}
|
|
18770
19006
|
);
|
|
18771
19007
|
}
|
|
@@ -18776,9 +19012,10 @@ function registerCompleteMediaUploadTool(server) {
|
|
|
18776
19012
|
"clipform_complete_media_upload",
|
|
18777
19013
|
{
|
|
18778
19014
|
title: "Complete Media Upload",
|
|
18779
|
-
description: `Confirm a signed-PUT still image upload finished, after PUTting the bytes to the upload_url returned by clipform_upload_media_asset. The API verifies the object actually landed in storage before flipping the asset from processing to ready - call this right after the PUT succeeds, or the asset stays invisible in the library. Not needed for video uploads (
|
|
19015
|
+
description: `Confirm a signed-PUT still image upload finished, after PUTting the bytes to the upload_url returned by clipform_upload_media_asset. The API verifies the object actually landed in storage before flipping the asset from processing to ready - call this right after the PUT succeeds, or the asset stays invisible in the library. Not needed for video uploads (they settle automatically).`,
|
|
18780
19016
|
inputSchema: {
|
|
18781
|
-
media_asset_id: external_exports.string().uuid().describe("The media_asset_id returned by clipform_upload_media_asset for the still image")
|
|
19017
|
+
media_asset_id: external_exports.string().uuid().describe("The media_asset_id returned by clipform_upload_media_asset for the still image"),
|
|
19018
|
+
form_id: formIdTargetInput
|
|
18782
19019
|
},
|
|
18783
19020
|
outputSchema: {
|
|
18784
19021
|
media_asset_id: external_exports.string(),
|
|
@@ -18791,7 +19028,10 @@ function registerCompleteMediaUploadTool(server) {
|
|
|
18791
19028
|
openWorldHint: false
|
|
18792
19029
|
}
|
|
18793
19030
|
},
|
|
18794
|
-
|
|
19031
|
+
// `form_id` (#4621) is a routing hint only, consumed centrally by
|
|
19032
|
+
// `runWithWorkspaceTarget` (server.ts) before this handler runs - never
|
|
19033
|
+
// forwarded to the API below (this route takes no form_id).
|
|
19034
|
+
async ({ media_asset_id, form_id: _form_id }) => {
|
|
18795
19035
|
const meResult = await callApi("/me", { method: "GET" });
|
|
18796
19036
|
if (!meResult.ok) {
|
|
18797
19037
|
return errorResult(`Unable to determine workspace: ${meResult.error}`);
|
|
@@ -18970,7 +19210,7 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18970
19210
|
"clipform_delete_node_media",
|
|
18971
19211
|
{
|
|
18972
19212
|
title: "Delete Node Media",
|
|
18973
|
-
description: `Remove media from a node. Deletes the media record and
|
|
19213
|
+
description: `Remove media from a node. Deletes the media record and the stored media asset (including any copy held by a third-party video provider). Requires user confirmation before it runs.`,
|
|
18974
19214
|
inputSchema: {
|
|
18975
19215
|
form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
|
|
18976
19216
|
node_id: external_exports.string().describe("The node ID"),
|
|
@@ -18982,7 +19222,9 @@ function registerDeleteNodeMediaTool(server) {
|
|
|
18982
19222
|
// Deleting already-removed media has no further effect - textbook
|
|
18983
19223
|
// idempotent delete (#2976).
|
|
18984
19224
|
idempotentHint: true,
|
|
18985
|
-
|
|
19225
|
+
// Open-world: also deletes any copy held by a third-party video
|
|
19226
|
+
// provider (#4434).
|
|
19227
|
+
openWorldHint: true
|
|
18986
19228
|
}
|
|
18987
19229
|
},
|
|
18988
19230
|
async ({ form_id, node_id, confirm }) => {
|
|
@@ -19124,10 +19366,10 @@ function registerSearchNewsTool(server) {
|
|
|
19124
19366
|
server.registerTool(
|
|
19125
19367
|
"clipform_search_news",
|
|
19126
19368
|
{
|
|
19127
|
-
title: "Search News
|
|
19128
|
-
description: `
|
|
19369
|
+
title: "Search Recent News",
|
|
19370
|
+
description: `Search recent news articles from NewsAPI and The Guardian and return them as structured results.
|
|
19129
19371
|
|
|
19130
|
-
Coverage: recent events, people, and topics (post-May-2025). Does NOT cover timeless topics (history, geography, science).
|
|
19372
|
+
Coverage: recent events, people, and topics (post-May-2025). Does NOT cover timeless topics (history, geography, science).
|
|
19131
19373
|
|
|
19132
19374
|
Returns: article title, source, author, date, URL, description, and image URL per result.`,
|
|
19133
19375
|
inputSchema: {
|
|
@@ -19212,6 +19454,8 @@ Use the tone parameter to direct HOW the voice speaks. Always set a tone that ma
|
|
|
19212
19454
|
|
|
19213
19455
|
Pass one item or many (max 10) - multiple items run in parallel. Returns audio_url and caption_ref per item - pass caption_ref downstream to clipform_render_composition / clipform_render_video_template / clipform_upload_media_asset to attach this run's word-level captions instead of hand-copying the captions array.`,
|
|
19214
19456
|
inputSchema: {
|
|
19457
|
+
workspace_id: workspaceIdInput,
|
|
19458
|
+
form_id: formIdTargetInput,
|
|
19215
19459
|
items: external_exports.array(TtsItemSchema).min(1).max(10).describe("One or more TTS items to generate")
|
|
19216
19460
|
},
|
|
19217
19461
|
outputSchema: {
|
|
@@ -19227,7 +19471,11 @@ Pass one item or many (max 10) - multiple items run in parallel. Returns audio_u
|
|
|
19227
19471
|
},
|
|
19228
19472
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
19229
19473
|
},
|
|
19230
|
-
|
|
19474
|
+
// `form_id` (#4621) is a routing hint only, consumed centrally by
|
|
19475
|
+
// `runWithWorkspaceTarget` (server.ts) before this handler runs - it
|
|
19476
|
+
// swaps the auth context's workspace_id read below, but is never
|
|
19477
|
+
// forwarded to the API itself (the /internal/tts route takes no form_id).
|
|
19478
|
+
async ({ items, form_id: _form_id }) => {
|
|
19231
19479
|
const workspace_id = getMcpAuth()?.workspace_id;
|
|
19232
19480
|
const results = await Promise.allSettled(
|
|
19233
19481
|
items.map(
|
|
@@ -20609,6 +20857,15 @@ function createServer(options) {
|
|
|
20609
20857
|
{ ...MCP_SERVER_IDENTITY },
|
|
20610
20858
|
{ instructions: buildInstructions(caps) }
|
|
20611
20859
|
);
|
|
20860
|
+
const rawServer = server.server;
|
|
20861
|
+
const nativeSetRequestHandler = rawServer.setRequestHandler.bind(rawServer);
|
|
20862
|
+
let baseListToolsHandler;
|
|
20863
|
+
rawServer.setRequestHandler = (schema, handler) => {
|
|
20864
|
+
if (schema === ListToolsRequestSchema && baseListToolsHandler === void 0) {
|
|
20865
|
+
baseListToolsHandler = handler;
|
|
20866
|
+
}
|
|
20867
|
+
return nativeSetRequestHandler(schema, handler);
|
|
20868
|
+
};
|
|
20612
20869
|
const _registerTool = server.registerTool.bind(server);
|
|
20613
20870
|
server.registerTool = (name, config2, handler) => {
|
|
20614
20871
|
const requiredTier = MCP_TOOL_TIERS[name] ?? 0;
|
|
@@ -20632,7 +20889,7 @@ function createServer(options) {
|
|
|
20632
20889
|
config2,
|
|
20633
20890
|
(...args) => runWithMcpTool(name, async () => {
|
|
20634
20891
|
try {
|
|
20635
|
-
return await handler(...args);
|
|
20892
|
+
return await runWithWorkspaceTarget(args[0], () => handler(...args));
|
|
20636
20893
|
} catch (err) {
|
|
20637
20894
|
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
20638
20895
|
options?.onToolError?.(name, error2);
|
|
@@ -20675,6 +20932,25 @@ function createServer(options) {
|
|
|
20675
20932
|
registerGetWorkflowTool(server, caps);
|
|
20676
20933
|
registerPrompts(server, caps);
|
|
20677
20934
|
registerResources(server, caps);
|
|
20935
|
+
rawServer.setRequestHandler = nativeSetRequestHandler;
|
|
20936
|
+
if (baseListToolsHandler) {
|
|
20937
|
+
nativeSetRequestHandler(ListToolsRequestSchema, async (request, extra) => {
|
|
20938
|
+
const base = await baseListToolsHandler(request, extra);
|
|
20939
|
+
return {
|
|
20940
|
+
...base,
|
|
20941
|
+
tools: base.tools.map((tool) => {
|
|
20942
|
+
const security = MCP_TOOL_SECURITY[tool.name];
|
|
20943
|
+
if (security === void 0) return tool;
|
|
20944
|
+
const existingMeta = tool._meta ?? {};
|
|
20945
|
+
return {
|
|
20946
|
+
...tool,
|
|
20947
|
+
securitySchemes: security,
|
|
20948
|
+
_meta: { ...existingMeta, securitySchemes: security }
|
|
20949
|
+
};
|
|
20950
|
+
})
|
|
20951
|
+
};
|
|
20952
|
+
});
|
|
20953
|
+
}
|
|
20678
20954
|
return server;
|
|
20679
20955
|
}
|
|
20680
20956
|
|
|
@@ -20685,4 +20961,4 @@ export {
|
|
|
20685
20961
|
buildInstructions,
|
|
20686
20962
|
createServer
|
|
20687
20963
|
};
|
|
20688
|
-
//# sourceMappingURL=chunk-
|
|
20964
|
+
//# sourceMappingURL=chunk-4XCXDH3I.js.map
|