@zackbart/connecta 0.14.0 → 0.14.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/CHANGELOG.md +38 -0
- package/dist/providers/cloudflare.d.ts +2 -1
- package/dist/providers/cloudflare.d.ts.map +1 -1
- package/dist/providers/cloudflare.js +1915 -90
- package/dist/providers/cloudflare.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/cloudflare.md +98 -53
- package/ethos.md +1 -0
- package/package.json +1 -1
- package/src/providers/cloudflare.ts +2322 -215
- package/src/version.ts +1 -1
- package/templates/node/package.json +1 -1
|
@@ -57,7 +57,8 @@ export const CLOUDFLARE_DNS_RECORD_TYPES = [
|
|
|
57
57
|
* free-form `data` passthrough — exactly the untyped `{}` this connection
|
|
58
58
|
* exists to avoid — or thirteen more hand-written schemas for record types
|
|
59
59
|
* that are rare in the day-to-day work this surface is for. They remain fully
|
|
60
|
-
* readable and filterable; only
|
|
60
|
+
* readable and filterable; only the named create/update tools omit them. The
|
|
61
|
+
* guarded raw mutation tool remains available for their documented bodies.
|
|
61
62
|
*/
|
|
62
63
|
export const CLOUDFLARE_CONTENT_DNS_RECORD_TYPES = [
|
|
63
64
|
"A",
|
|
@@ -92,7 +93,7 @@ function admissionPolicy(maxConcurrency) {
|
|
|
92
93
|
}
|
|
93
94
|
const DEFAULT_CREDENTIAL = {
|
|
94
95
|
label: "Cloudflare API token",
|
|
95
|
-
description: "A scoped API token (My Profile → API Tokens → Create Token), not a Global API Key. Grant only the permissions the deployment needs: zone-scoped \"Zone Read\"
|
|
96
|
+
description: "A scoped API token (My Profile → API Tokens → Create Token), not a Global API Key. Grant only the permissions the deployment needs: zone-scoped \"Zone Read\", \"Zone Settings Write\", \"DNS Write\", \"Cache Purge\", and the phase-specific Rules product Read permissions as needed; account-scoped \"Workers Scripts Read/Write\", \"Workers KV Storage Read/Write\", \"Workers R2 Storage Read/Write\", or \"Cloudflare Pages Read/Write\" for the platform tools.",
|
|
96
97
|
placeholder: "Paste API token",
|
|
97
98
|
};
|
|
98
99
|
function asRecord(value) {
|
|
@@ -216,8 +217,11 @@ function buildUrl(base, spec) {
|
|
|
216
217
|
}
|
|
217
218
|
return url.toString();
|
|
218
219
|
}
|
|
219
|
-
async function
|
|
220
|
+
async function fetchCloudflare(base, spec, ctx) {
|
|
220
221
|
const token = await readToken(ctx);
|
|
222
|
+
if (spec.body !== undefined && spec.rawBody !== undefined) {
|
|
223
|
+
throw new Error("A Cloudflare request cannot have both JSON and raw bodies.");
|
|
224
|
+
}
|
|
221
225
|
let response;
|
|
222
226
|
try {
|
|
223
227
|
response = await fetch(buildUrl(base, spec), {
|
|
@@ -228,16 +232,23 @@ async function callCloudflare(base, spec, ctx) {
|
|
|
228
232
|
...(spec.body !== undefined
|
|
229
233
|
? { "Content-Type": "application/json" }
|
|
230
234
|
: {}),
|
|
235
|
+
...Object.fromEntries(Object.entries(spec.headers ?? {}).filter((entry) => entry[1] !== undefined)),
|
|
231
236
|
},
|
|
232
237
|
...(spec.body !== undefined
|
|
233
238
|
? { body: JSON.stringify(spec.body) }
|
|
234
|
-
:
|
|
239
|
+
: spec.rawBody !== undefined
|
|
240
|
+
? { body: spec.rawBody }
|
|
241
|
+
: {}),
|
|
235
242
|
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
236
243
|
});
|
|
237
244
|
}
|
|
238
245
|
catch (cause) {
|
|
239
246
|
throw new ConnectorCallError("unavailable", `Could not reach the Cloudflare API: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
240
247
|
}
|
|
248
|
+
return response;
|
|
249
|
+
}
|
|
250
|
+
async function callCloudflare(base, spec, ctx) {
|
|
251
|
+
const response = await fetchCloudflare(base, spec, ctx);
|
|
241
252
|
let envelope;
|
|
242
253
|
try {
|
|
243
254
|
envelope = (await response.json());
|
|
@@ -257,6 +268,41 @@ async function callCloudflare(base, spec, ctx) {
|
|
|
257
268
|
resultInfo: envelope.result_info,
|
|
258
269
|
};
|
|
259
270
|
}
|
|
271
|
+
function base64FromBytes(bytes) {
|
|
272
|
+
let binary = "";
|
|
273
|
+
for (let offset = 0; offset < bytes.length; offset += 0x8000) {
|
|
274
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
|
|
275
|
+
}
|
|
276
|
+
return btoa(binary);
|
|
277
|
+
}
|
|
278
|
+
async function callCloudflareContent(base, spec, ctx, responseType) {
|
|
279
|
+
const response = await fetchCloudflare(base, spec, ctx);
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
let errors = [];
|
|
282
|
+
try {
|
|
283
|
+
const envelope = (await response.clone().json());
|
|
284
|
+
if (Array.isArray(envelope.errors))
|
|
285
|
+
errors = envelope.errors;
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// A raw or gateway error body has no structured detail to preserve.
|
|
289
|
+
}
|
|
290
|
+
throw failureFor(response.status, response.headers, errors);
|
|
291
|
+
}
|
|
292
|
+
const common = {
|
|
293
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
294
|
+
...(response.headers.get("etag")
|
|
295
|
+
? { etag: response.headers.get("etag") }
|
|
296
|
+
: {}),
|
|
297
|
+
};
|
|
298
|
+
if (responseType === "text") {
|
|
299
|
+
return { ...common, text: await response.text() };
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
...common,
|
|
303
|
+
base64: base64FromBytes(new Uint8Array(await response.arrayBuffer())),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
260
306
|
/**
|
|
261
307
|
* Cloudflare's `result_info` reshaped into the one question an agent actually
|
|
262
308
|
* asks — is there another page? — with the raw counters kept alongside it.
|
|
@@ -368,11 +414,111 @@ function projectR2Bucket(value) {
|
|
|
368
414
|
...(bucket["storage_class"] !== undefined
|
|
369
415
|
? { storageClass: bucket["storage_class"] }
|
|
370
416
|
: {}),
|
|
417
|
+
...(bucket["jurisdiction"] !== undefined
|
|
418
|
+
? { jurisdiction: bucket["jurisdiction"] }
|
|
419
|
+
: {}),
|
|
371
420
|
...(bucket["creation_date"] !== undefined
|
|
372
421
|
? { creationDate: bucket["creation_date"] }
|
|
373
422
|
: {}),
|
|
374
423
|
};
|
|
375
424
|
}
|
|
425
|
+
function projectR2Object(value) {
|
|
426
|
+
const object = asRecord(value);
|
|
427
|
+
return {
|
|
428
|
+
key: object["key"],
|
|
429
|
+
size: object["size"],
|
|
430
|
+
etag: object["etag"],
|
|
431
|
+
lastModified: object["last_modified"],
|
|
432
|
+
...(object["storage_class"] !== undefined
|
|
433
|
+
? { storageClass: object["storage_class"] }
|
|
434
|
+
: {}),
|
|
435
|
+
...(object["http_metadata"] !== undefined
|
|
436
|
+
? { httpMetadata: object["http_metadata"] }
|
|
437
|
+
: {}),
|
|
438
|
+
...(object["custom_metadata"] !== undefined
|
|
439
|
+
? { customMetadata: object["custom_metadata"] }
|
|
440
|
+
: {}),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function projectKvKey(value) {
|
|
444
|
+
const key = asRecord(value);
|
|
445
|
+
return {
|
|
446
|
+
name: key["name"],
|
|
447
|
+
...(key["expiration"] !== undefined ? { expiration: key["expiration"] } : {}),
|
|
448
|
+
...(key["metadata"] !== undefined ? { metadata: key["metadata"] } : {}),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function projectWorkerDeployment(value) {
|
|
452
|
+
const deployment = asRecord(value);
|
|
453
|
+
return {
|
|
454
|
+
id: deployment["id"],
|
|
455
|
+
...(deployment["created_on"] !== undefined
|
|
456
|
+
? { createdOn: deployment["created_on"] }
|
|
457
|
+
: {}),
|
|
458
|
+
...(deployment["source"] !== undefined ? { source: deployment["source"] } : {}),
|
|
459
|
+
...(deployment["strategy"] !== undefined
|
|
460
|
+
? { strategy: deployment["strategy"] }
|
|
461
|
+
: {}),
|
|
462
|
+
...(deployment["versions"] !== undefined
|
|
463
|
+
? { versions: deployment["versions"] }
|
|
464
|
+
: {}),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function projectPagesDeployment(value) {
|
|
468
|
+
const deployment = asRecord(value);
|
|
469
|
+
return {
|
|
470
|
+
id: deployment["id"],
|
|
471
|
+
...(deployment["project_name"] !== undefined
|
|
472
|
+
? { projectName: deployment["project_name"] }
|
|
473
|
+
: {}),
|
|
474
|
+
...(deployment["environment"] !== undefined
|
|
475
|
+
? { environment: deployment["environment"] }
|
|
476
|
+
: {}),
|
|
477
|
+
...(deployment["url"] !== undefined ? { url: deployment["url"] } : {}),
|
|
478
|
+
...(deployment["aliases"] !== undefined ? { aliases: deployment["aliases"] } : {}),
|
|
479
|
+
...(deployment["stage"] !== undefined ? { stage: deployment["stage"] } : {}),
|
|
480
|
+
...(deployment["latest_stage"] !== undefined
|
|
481
|
+
? { latestStage: deployment["latest_stage"] }
|
|
482
|
+
: {}),
|
|
483
|
+
...(deployment["created_on"] !== undefined
|
|
484
|
+
? { createdOn: deployment["created_on"] }
|
|
485
|
+
: {}),
|
|
486
|
+
...(deployment["modified_on"] !== undefined
|
|
487
|
+
? { modifiedOn: deployment["modified_on"] }
|
|
488
|
+
: {}),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function projectPagesDomain(value) {
|
|
492
|
+
const domain = asRecord(value);
|
|
493
|
+
return {
|
|
494
|
+
id: domain["id"],
|
|
495
|
+
name: domain["name"],
|
|
496
|
+
...(domain["status"] !== undefined ? { status: domain["status"] } : {}),
|
|
497
|
+
...(domain["verification_data"] !== undefined
|
|
498
|
+
? { verificationData: domain["verification_data"] }
|
|
499
|
+
: {}),
|
|
500
|
+
...(domain["created_on"] !== undefined
|
|
501
|
+
? { createdOn: domain["created_on"] }
|
|
502
|
+
: {}),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function projectRuleset(value) {
|
|
506
|
+
const ruleset = asRecord(value);
|
|
507
|
+
return {
|
|
508
|
+
id: ruleset["id"],
|
|
509
|
+
name: ruleset["name"],
|
|
510
|
+
kind: ruleset["kind"],
|
|
511
|
+
phase: ruleset["phase"],
|
|
512
|
+
...(ruleset["description"] !== undefined
|
|
513
|
+
? { description: ruleset["description"] }
|
|
514
|
+
: {}),
|
|
515
|
+
...(ruleset["version"] !== undefined ? { version: ruleset["version"] } : {}),
|
|
516
|
+
...(ruleset["last_updated"] !== undefined
|
|
517
|
+
? { lastUpdated: ruleset["last_updated"] }
|
|
518
|
+
: {}),
|
|
519
|
+
...(ruleset["rules"] !== undefined ? { rules: ruleset["rules"] } : {}),
|
|
520
|
+
};
|
|
521
|
+
}
|
|
376
522
|
function projectPagesProject(value) {
|
|
377
523
|
const project = asRecord(value);
|
|
378
524
|
const latest = asRecord(project["latest_deployment"]);
|
|
@@ -560,6 +706,224 @@ function optionalNumber(args, key) {
|
|
|
560
706
|
const value = args[key];
|
|
561
707
|
return typeof value === "number" ? value : undefined;
|
|
562
708
|
}
|
|
709
|
+
function optionalBoolean(args, key) {
|
|
710
|
+
const value = args[key];
|
|
711
|
+
return typeof value === "boolean" ? value : undefined;
|
|
712
|
+
}
|
|
713
|
+
function requireString(args, key) {
|
|
714
|
+
const value = optionalString(args, key);
|
|
715
|
+
if (value)
|
|
716
|
+
return value;
|
|
717
|
+
throw new ConnectorCallError("invalid_args", `${key} must not be blank.`);
|
|
718
|
+
}
|
|
719
|
+
function encodePathSegment(value) {
|
|
720
|
+
return encodeURIComponent(value);
|
|
721
|
+
}
|
|
722
|
+
function encodeObjectKey(value) {
|
|
723
|
+
return value
|
|
724
|
+
.split("/")
|
|
725
|
+
.map((segment) => {
|
|
726
|
+
if (segment === "." || segment === "..") {
|
|
727
|
+
throw new ConnectorCallError("invalid_args", "objectKey cannot contain '.' or '..' path segments because URL normalization would change the target resource.");
|
|
728
|
+
}
|
|
729
|
+
return encodeURIComponent(segment);
|
|
730
|
+
})
|
|
731
|
+
.join("/");
|
|
732
|
+
}
|
|
733
|
+
function cloudflareApiPath(value) {
|
|
734
|
+
if (typeof value !== "string") {
|
|
735
|
+
throw new ConnectorCallError("invalid_args", "path must be a string.");
|
|
736
|
+
}
|
|
737
|
+
const path = value.trim();
|
|
738
|
+
if (!path.startsWith("/") || path.startsWith("//") || path.includes("\\")) {
|
|
739
|
+
throw new ConnectorCallError("invalid_args", "path must be a relative Cloudflare v4 path beginning with one slash and containing no backslashes.");
|
|
740
|
+
}
|
|
741
|
+
if (path.includes("?") || path.includes("#")) {
|
|
742
|
+
throw new ConnectorCallError("invalid_args", "Put query parameters in the query array; path cannot contain '?' or '#'.");
|
|
743
|
+
}
|
|
744
|
+
for (const segment of path.split("/")) {
|
|
745
|
+
let decoded = segment;
|
|
746
|
+
let stable = false;
|
|
747
|
+
for (let pass = 0; pass < 20; pass += 1) {
|
|
748
|
+
let next;
|
|
749
|
+
try {
|
|
750
|
+
next = decodeURIComponent(decoded);
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
throw new ConnectorCallError("invalid_args", "path contains invalid percent encoding.");
|
|
754
|
+
}
|
|
755
|
+
if (next === decoded) {
|
|
756
|
+
stable = true;
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
decoded = next;
|
|
760
|
+
}
|
|
761
|
+
if (!stable) {
|
|
762
|
+
throw new ConnectorCallError("invalid_args", "path contains too many layers of percent encoding.");
|
|
763
|
+
}
|
|
764
|
+
if (decoded === "." || decoded === ".." || decoded.includes("/") || decoded.includes("\\")) {
|
|
765
|
+
throw new ConnectorCallError("invalid_args", "path cannot contain encoded or literal traversal, slash, or backslash segments.");
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const normalized = new URL(`https://connecta.invalid/client/v4${path}`);
|
|
769
|
+
if (!normalized.pathname.startsWith("/client/v4/")) {
|
|
770
|
+
throw new ConnectorCallError("invalid_args", "path normalization escaped the Cloudflare v4 API base.");
|
|
771
|
+
}
|
|
772
|
+
return path;
|
|
773
|
+
}
|
|
774
|
+
function queryFromArgs(value) {
|
|
775
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
776
|
+
return undefined;
|
|
777
|
+
const query = {};
|
|
778
|
+
for (const item of value) {
|
|
779
|
+
const entry = asRecord(item);
|
|
780
|
+
query[String(entry["name"])] = String(entry["value"]);
|
|
781
|
+
}
|
|
782
|
+
return query;
|
|
783
|
+
}
|
|
784
|
+
function headersFromArgs(value) {
|
|
785
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
786
|
+
return undefined;
|
|
787
|
+
const headers = {};
|
|
788
|
+
const forbidden = new Set([
|
|
789
|
+
"authorization",
|
|
790
|
+
"cookie",
|
|
791
|
+
"host",
|
|
792
|
+
"content-length",
|
|
793
|
+
"content-type",
|
|
794
|
+
"transfer-encoding",
|
|
795
|
+
]);
|
|
796
|
+
for (const item of value) {
|
|
797
|
+
const entry = asRecord(item);
|
|
798
|
+
const name = String(entry["name"]).trim();
|
|
799
|
+
if (forbidden.has(name.toLowerCase())) {
|
|
800
|
+
throw new ConnectorCallError("invalid_args", `The raw Cloudflare tools do not allow the ${name} header. Authentication and request framing are connector-owned; use contentType for a raw upload body.`);
|
|
801
|
+
}
|
|
802
|
+
headers[name] = String(entry["value"]);
|
|
803
|
+
}
|
|
804
|
+
return headers;
|
|
805
|
+
}
|
|
806
|
+
function r2Headers(args) {
|
|
807
|
+
return { "cf-r2-jurisdiction": optionalString(args, "jurisdiction") };
|
|
808
|
+
}
|
|
809
|
+
function bytesFromBase64(value) {
|
|
810
|
+
try {
|
|
811
|
+
const binary = atob(value);
|
|
812
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
813
|
+
}
|
|
814
|
+
catch (cause) {
|
|
815
|
+
throw new ConnectorCallError("invalid_args", "base64Body and multipart file base64 values must be valid base64.", { cause });
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function uploadBody(args) {
|
|
819
|
+
const fields = asArray(args["fields"]);
|
|
820
|
+
const files = asArray(args["files"]);
|
|
821
|
+
const hasMultipart = fields.length > 0 || files.length > 0;
|
|
822
|
+
const textBody = typeof args["textBody"] === "string" ? args["textBody"] : undefined;
|
|
823
|
+
const base64Body = typeof args["base64Body"] === "string" ? args["base64Body"] : undefined;
|
|
824
|
+
const rawCount = Number(textBody !== undefined) + Number(base64Body !== undefined);
|
|
825
|
+
if ((hasMultipart && rawCount > 0) || (!hasMultipart && rawCount !== 1)) {
|
|
826
|
+
throw new ConnectorCallError("invalid_args", "cloudflare_api_upload needs exactly one body shape: textBody, base64Body, or multipart fields/files.");
|
|
827
|
+
}
|
|
828
|
+
if (hasMultipart) {
|
|
829
|
+
const form = new FormData();
|
|
830
|
+
for (const value of fields) {
|
|
831
|
+
const field = asRecord(value);
|
|
832
|
+
const name = String(field["name"]);
|
|
833
|
+
const contentType = optionalString(field, "contentType");
|
|
834
|
+
if (contentType) {
|
|
835
|
+
form.append(name, new Blob([String(field["value"])], { type: contentType }), optionalString(field, "fileName") ?? name);
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
form.append(name, String(field["value"]));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const value of files) {
|
|
842
|
+
const file = asRecord(value);
|
|
843
|
+
const text = typeof file["text"] === "string" ? file["text"] : undefined;
|
|
844
|
+
const base64 = typeof file["base64"] === "string" ? file["base64"] : undefined;
|
|
845
|
+
if (Number(text !== undefined) + Number(base64 !== undefined) !== 1) {
|
|
846
|
+
throw new ConnectorCallError("invalid_args", "Each multipart file needs exactly one of text or base64.");
|
|
847
|
+
}
|
|
848
|
+
const blob = new Blob([text ?? bytesFromBase64(base64)], { type: String(file["contentType"] ?? "application/octet-stream") });
|
|
849
|
+
form.append(String(file["name"]), blob, String(file["fileName"]));
|
|
850
|
+
}
|
|
851
|
+
return { rawBody: form };
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
rawBody: textBody ?? bytesFromBase64(base64Body),
|
|
855
|
+
headers: {
|
|
856
|
+
"Content-Type": optionalString(args, "contentType") ??
|
|
857
|
+
(textBody !== undefined ? "text/plain; charset=utf-8" : "application/octet-stream"),
|
|
858
|
+
},
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
const OPEN_OBJECT_OUTPUT_SCHEMA = {
|
|
862
|
+
type: "object",
|
|
863
|
+
description: "Cloudflare's result object. Its fields depend on the endpoint.",
|
|
864
|
+
additionalProperties: true,
|
|
865
|
+
};
|
|
866
|
+
const QUERY_INPUT_PROPERTY = {
|
|
867
|
+
type: "array",
|
|
868
|
+
description: "Optional query parameters as name/value pairs. Each parameter name may appear once.",
|
|
869
|
+
items: {
|
|
870
|
+
type: "object",
|
|
871
|
+
properties: {
|
|
872
|
+
name: { type: "string", minLength: 1 },
|
|
873
|
+
value: { type: ["string", "number", "boolean"] },
|
|
874
|
+
},
|
|
875
|
+
required: ["name", "value"],
|
|
876
|
+
additionalProperties: false,
|
|
877
|
+
},
|
|
878
|
+
};
|
|
879
|
+
const HEADERS_INPUT_PROPERTY = {
|
|
880
|
+
type: "array",
|
|
881
|
+
description: "Optional provider headers as name/value pairs, for example cf-r2-jurisdiction, Range, If-None-Match, or Cloudflare product metadata. Authorization, Cookie, Host, Content-Length, Content-Type, and Transfer-Encoding are connector-owned and refused.",
|
|
882
|
+
items: {
|
|
883
|
+
type: "object",
|
|
884
|
+
properties: {
|
|
885
|
+
name: { type: "string", minLength: 1 },
|
|
886
|
+
value: { type: "string" },
|
|
887
|
+
},
|
|
888
|
+
required: ["name", "value"],
|
|
889
|
+
additionalProperties: false,
|
|
890
|
+
},
|
|
891
|
+
};
|
|
892
|
+
const R2_JURISDICTION_PROPERTY = {
|
|
893
|
+
type: "string",
|
|
894
|
+
enum: ["default", "eu", "fedramp"],
|
|
895
|
+
description: "Bucket jurisdiction. Omit for ordinary buckets; set eu or fedramp for jurisdictional buckets.",
|
|
896
|
+
};
|
|
897
|
+
const R2_BUCKET_NAME_PROPERTY = {
|
|
898
|
+
type: "string",
|
|
899
|
+
minLength: 3,
|
|
900
|
+
maxLength: 64,
|
|
901
|
+
description: "R2 bucket name.",
|
|
902
|
+
};
|
|
903
|
+
const R2_BUCKET_SCHEMA = {
|
|
904
|
+
type: "object",
|
|
905
|
+
properties: {
|
|
906
|
+
name: { type: "string" },
|
|
907
|
+
location: { type: "string" },
|
|
908
|
+
storageClass: { type: "string" },
|
|
909
|
+
jurisdiction: { type: "string" },
|
|
910
|
+
creationDate: { type: "string" },
|
|
911
|
+
},
|
|
912
|
+
required: ["name"],
|
|
913
|
+
};
|
|
914
|
+
const R2_OBJECT_SCHEMA = {
|
|
915
|
+
type: "object",
|
|
916
|
+
properties: {
|
|
917
|
+
key: { type: "string" },
|
|
918
|
+
size: { type: "number" },
|
|
919
|
+
etag: { type: "string" },
|
|
920
|
+
lastModified: { type: "string" },
|
|
921
|
+
storageClass: { type: "string" },
|
|
922
|
+
httpMetadata: { type: "object" },
|
|
923
|
+
customMetadata: { type: "object" },
|
|
924
|
+
},
|
|
925
|
+
required: ["key"],
|
|
926
|
+
};
|
|
563
927
|
function buildTools(scope) {
|
|
564
928
|
const { base } = scope;
|
|
565
929
|
const zoneArg = (args) => requireScope(args["zoneId"], scope.zoneId, "zoneId");
|
|
@@ -604,6 +968,215 @@ function buildTools(scope) {
|
|
|
604
968
|
};
|
|
605
969
|
},
|
|
606
970
|
},
|
|
971
|
+
{
|
|
972
|
+
name: "cloudflare_api_get",
|
|
973
|
+
description: "Call any GET endpoint under Cloudflare's v4 API with this connector's token. Use a named tool when one exists; use this read-only escape hatch for Images, Stream, Email Routing, D1, Queues, Access, Tunnels, Analytics, and newer product endpoints the curated surface does not yet name.",
|
|
974
|
+
annotations: readOnly,
|
|
975
|
+
inputSchema: {
|
|
976
|
+
type: "object",
|
|
977
|
+
properties: {
|
|
978
|
+
path: {
|
|
979
|
+
type: "string",
|
|
980
|
+
minLength: 1,
|
|
981
|
+
description: "Relative path below /client/v4, beginning with '/', for example /accounts/<id>/images/v1 or /zones/<id>/email/routing/rules. Do not include a query string.",
|
|
982
|
+
},
|
|
983
|
+
query: QUERY_INPUT_PROPERTY,
|
|
984
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
985
|
+
responseType: {
|
|
986
|
+
type: "string",
|
|
987
|
+
enum: ["json", "text", "base64"],
|
|
988
|
+
description: "How to read a successful response. Defaults to json; use text or base64 for object, log, script, and media downloads.",
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
required: ["path"],
|
|
992
|
+
additionalProperties: false,
|
|
993
|
+
},
|
|
994
|
+
outputSchema: {
|
|
995
|
+
type: "object",
|
|
996
|
+
properties: {
|
|
997
|
+
result: {
|
|
998
|
+
description: "Cloudflare's unprojected result for the endpoint.",
|
|
999
|
+
},
|
|
1000
|
+
resultInfo: {
|
|
1001
|
+
type: "object",
|
|
1002
|
+
description: "Cloudflare's unprojected pagination metadata, when the endpoint returns it.",
|
|
1003
|
+
},
|
|
1004
|
+
text: { type: "string", description: "Text response body when responseType is text." },
|
|
1005
|
+
base64: { type: "string", description: "Base64 response bytes when responseType is base64." },
|
|
1006
|
+
contentType: { type: "string", description: "Response Content-Type for text/base64 reads." },
|
|
1007
|
+
etag: { type: "string", description: "Response ETag when Cloudflare supplies one." },
|
|
1008
|
+
},
|
|
1009
|
+
required: [],
|
|
1010
|
+
},
|
|
1011
|
+
handler: async (args, ctx) => {
|
|
1012
|
+
const query = queryFromArgs(args["query"]);
|
|
1013
|
+
const headers = headersFromArgs(args["headers"]);
|
|
1014
|
+
const responseType = optionalString(args, "responseType") ?? "json";
|
|
1015
|
+
const spec = {
|
|
1016
|
+
method: "GET",
|
|
1017
|
+
path: cloudflareApiPath(args["path"]),
|
|
1018
|
+
...(query !== undefined ? { query } : {}),
|
|
1019
|
+
...(headers !== undefined ? { headers } : {}),
|
|
1020
|
+
};
|
|
1021
|
+
if (responseType === "text" || responseType === "base64") {
|
|
1022
|
+
return await callCloudflareContent(base, spec, ctx, responseType);
|
|
1023
|
+
}
|
|
1024
|
+
const { result, resultInfo } = await callCloudflare(base, spec, ctx);
|
|
1025
|
+
return {
|
|
1026
|
+
result,
|
|
1027
|
+
...(resultInfo !== undefined ? { resultInfo } : {}),
|
|
1028
|
+
};
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: "cloudflare_api_mutate",
|
|
1033
|
+
description: "Call any JSON POST, PUT, PATCH, or DELETE endpoint under Cloudflare's v4 API with this connector's token. This is the approval-gated escape hatch for managing Cloudflare products without waiting for a named tool. It does not support multipart or binary uploads.",
|
|
1034
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1035
|
+
inputSchema: {
|
|
1036
|
+
type: "object",
|
|
1037
|
+
properties: {
|
|
1038
|
+
method: {
|
|
1039
|
+
type: "string",
|
|
1040
|
+
enum: ["POST", "PUT", "PATCH", "DELETE"],
|
|
1041
|
+
description: "HTTP mutation method required by the Cloudflare endpoint.",
|
|
1042
|
+
},
|
|
1043
|
+
path: {
|
|
1044
|
+
type: "string",
|
|
1045
|
+
minLength: 1,
|
|
1046
|
+
description: "Relative path below /client/v4, beginning with '/'. Do not include a query string.",
|
|
1047
|
+
},
|
|
1048
|
+
query: QUERY_INPUT_PROPERTY,
|
|
1049
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
1050
|
+
body: {
|
|
1051
|
+
type: ["object", "array", "string", "number", "boolean", "null"],
|
|
1052
|
+
description: "JSON request body exactly as documented by Cloudflare. Omit for endpoints with no body.",
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
required: ["method", "path"],
|
|
1056
|
+
additionalProperties: false,
|
|
1057
|
+
},
|
|
1058
|
+
outputSchema: {
|
|
1059
|
+
type: "object",
|
|
1060
|
+
properties: {
|
|
1061
|
+
result: {
|
|
1062
|
+
description: "Cloudflare's unprojected result for the endpoint.",
|
|
1063
|
+
},
|
|
1064
|
+
resultInfo: {
|
|
1065
|
+
type: "object",
|
|
1066
|
+
description: "Cloudflare's unprojected pagination metadata, when the endpoint returns it.",
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
required: ["result"],
|
|
1070
|
+
},
|
|
1071
|
+
handler: async (args, ctx) => {
|
|
1072
|
+
const method = String(args["method"]);
|
|
1073
|
+
const query = queryFromArgs(args["query"]);
|
|
1074
|
+
const headers = headersFromArgs(args["headers"]);
|
|
1075
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
1076
|
+
method,
|
|
1077
|
+
path: cloudflareApiPath(args["path"]),
|
|
1078
|
+
...(query !== undefined ? { query } : {}),
|
|
1079
|
+
...(headers !== undefined ? { headers } : {}),
|
|
1080
|
+
...(args["body"] !== undefined ? { body: args["body"] } : {}),
|
|
1081
|
+
}, ctx);
|
|
1082
|
+
return {
|
|
1083
|
+
result,
|
|
1084
|
+
...(resultInfo !== undefined ? { resultInfo } : {}),
|
|
1085
|
+
};
|
|
1086
|
+
},
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
name: "cloudflare_api_upload",
|
|
1090
|
+
description: "Upload raw text, base64 bytes, or multipart form data to a Cloudflare v4 POST or PUT endpoint. Covers Worker modules, R2/KV objects, Images, Stream, and Pages upload endpoints. Reads no local files; content must be supplied explicitly.",
|
|
1091
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1092
|
+
inputSchema: {
|
|
1093
|
+
type: "object",
|
|
1094
|
+
properties: {
|
|
1095
|
+
method: {
|
|
1096
|
+
type: "string",
|
|
1097
|
+
enum: ["POST", "PUT"],
|
|
1098
|
+
description: "HTTP upload method required by the Cloudflare endpoint.",
|
|
1099
|
+
},
|
|
1100
|
+
path: {
|
|
1101
|
+
type: "string",
|
|
1102
|
+
minLength: 1,
|
|
1103
|
+
description: "Relative path below /client/v4, beginning with '/'. Do not include a query string.",
|
|
1104
|
+
},
|
|
1105
|
+
query: QUERY_INPUT_PROPERTY,
|
|
1106
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
1107
|
+
contentType: {
|
|
1108
|
+
type: "string",
|
|
1109
|
+
minLength: 1,
|
|
1110
|
+
description: "Content-Type for a raw text/base64 body. Omit for multipart because fetch supplies the boundary.",
|
|
1111
|
+
},
|
|
1112
|
+
textBody: {
|
|
1113
|
+
type: "string",
|
|
1114
|
+
description: "Raw UTF-8 request body. Mutually exclusive with base64Body and multipart fields/files.",
|
|
1115
|
+
},
|
|
1116
|
+
base64Body: {
|
|
1117
|
+
type: "string",
|
|
1118
|
+
description: "Base64-encoded request bytes. Mutually exclusive with textBody and multipart fields/files.",
|
|
1119
|
+
},
|
|
1120
|
+
fields: {
|
|
1121
|
+
type: "array",
|
|
1122
|
+
description: "String fields for a multipart/form-data request.",
|
|
1123
|
+
items: {
|
|
1124
|
+
type: "object",
|
|
1125
|
+
properties: {
|
|
1126
|
+
name: { type: "string", minLength: 1 },
|
|
1127
|
+
value: { type: "string" },
|
|
1128
|
+
contentType: { type: "string", minLength: 1 },
|
|
1129
|
+
fileName: { type: "string", minLength: 1 },
|
|
1130
|
+
},
|
|
1131
|
+
required: ["name", "value"],
|
|
1132
|
+
additionalProperties: false,
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
files: {
|
|
1136
|
+
type: "array",
|
|
1137
|
+
description: "File parts for multipart/form-data. Each file needs exactly one of text or base64.",
|
|
1138
|
+
items: {
|
|
1139
|
+
type: "object",
|
|
1140
|
+
properties: {
|
|
1141
|
+
name: { type: "string", minLength: 1 },
|
|
1142
|
+
fileName: { type: "string", minLength: 1 },
|
|
1143
|
+
contentType: { type: "string", minLength: 1 },
|
|
1144
|
+
text: { type: "string" },
|
|
1145
|
+
base64: { type: "string" },
|
|
1146
|
+
},
|
|
1147
|
+
required: ["name", "fileName", "contentType"],
|
|
1148
|
+
additionalProperties: false,
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
required: ["method", "path"],
|
|
1153
|
+
additionalProperties: false,
|
|
1154
|
+
},
|
|
1155
|
+
outputSchema: {
|
|
1156
|
+
type: "object",
|
|
1157
|
+
properties: {
|
|
1158
|
+
result: {
|
|
1159
|
+
description: "Cloudflare's unprojected upload result.",
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
required: ["result"],
|
|
1163
|
+
},
|
|
1164
|
+
handler: async (args, ctx) => {
|
|
1165
|
+
const query = queryFromArgs(args["query"]);
|
|
1166
|
+
const headers = headersFromArgs(args["headers"]);
|
|
1167
|
+
const upload = uploadBody(args);
|
|
1168
|
+
const { result } = await callCloudflare(base, {
|
|
1169
|
+
method: String(args["method"]),
|
|
1170
|
+
path: cloudflareApiPath(args["path"]),
|
|
1171
|
+
...(query !== undefined ? { query } : {}),
|
|
1172
|
+
...(headers !== undefined || upload.headers !== undefined
|
|
1173
|
+
? { headers: { ...headers, ...upload.headers } }
|
|
1174
|
+
: {}),
|
|
1175
|
+
rawBody: upload.rawBody,
|
|
1176
|
+
}, ctx);
|
|
1177
|
+
return { result };
|
|
1178
|
+
},
|
|
1179
|
+
},
|
|
607
1180
|
{
|
|
608
1181
|
name: "list_accounts",
|
|
609
1182
|
description: "List Cloudflare accounts this token can see. Supplies the accountId that the Workers, KV, R2, and Pages tools need.",
|
|
@@ -711,54 +1284,207 @@ function buildTools(scope) {
|
|
|
711
1284
|
},
|
|
712
1285
|
},
|
|
713
1286
|
{
|
|
714
|
-
name: "
|
|
715
|
-
description: "List
|
|
1287
|
+
name: "list_zone_settings",
|
|
1288
|
+
description: "List the effective settings for a zone, including each setting's current value and whether the plan allows editing it.",
|
|
716
1289
|
annotations: readOnly,
|
|
717
1290
|
inputSchema: {
|
|
718
1291
|
type: "object",
|
|
719
1292
|
properties: {
|
|
720
1293
|
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
721
|
-
name: {
|
|
722
|
-
type: "string",
|
|
723
|
-
description: "Exact record name, fully qualified, e.g. www.example.com.",
|
|
724
|
-
},
|
|
725
|
-
type: {
|
|
726
|
-
type: "string",
|
|
727
|
-
enum: [...CLOUDFLARE_DNS_RECORD_TYPES],
|
|
728
|
-
description: "Filter by record type.",
|
|
729
|
-
},
|
|
730
|
-
content: {
|
|
731
|
-
type: "string",
|
|
732
|
-
description: "Exact record content, e.g. an IP address.",
|
|
733
|
-
},
|
|
734
|
-
order: {
|
|
735
|
-
type: "string",
|
|
736
|
-
enum: ["type", "name", "content", "ttl", "proxied"],
|
|
737
|
-
description: "Sort field.",
|
|
738
|
-
},
|
|
739
|
-
direction: {
|
|
740
|
-
type: "string",
|
|
741
|
-
enum: ["asc", "desc"],
|
|
742
|
-
description: "Sort direction for `order`. Defaults to asc.",
|
|
743
|
-
},
|
|
744
|
-
// Cloudflare documents 1 to 5,000,000 here with a default of 100; the
|
|
745
|
-
// ceiling is nominal, so this connection caps it at a page size that
|
|
746
|
-
// actually returns.
|
|
747
|
-
...pagingInputProperties(1, 1000, {
|
|
748
|
-
defaultPerPage: 100,
|
|
749
|
-
bounds: "clamped",
|
|
750
|
-
}),
|
|
751
|
-
raw: RAW_INPUT_PROPERTY,
|
|
752
1294
|
},
|
|
753
1295
|
required: scopeRequired("zoneId", scope.zoneId),
|
|
754
1296
|
additionalProperties: false,
|
|
755
1297
|
},
|
|
756
|
-
outputSchema: listOutputSchema("
|
|
1298
|
+
outputSchema: listOutputSchema("settings", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
757
1299
|
handler: async (args, ctx) => {
|
|
758
1300
|
const { result, resultInfo } = await callCloudflare(base, {
|
|
759
1301
|
method: "GET",
|
|
760
|
-
path: `/zones/${
|
|
761
|
-
|
|
1302
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings`,
|
|
1303
|
+
}, ctx);
|
|
1304
|
+
return { settings: asArray(result), page: pageInfo(resultInfo) };
|
|
1305
|
+
},
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
name: "get_zone_setting",
|
|
1309
|
+
description: "Get one zone setting by its Cloudflare setting id, such as ssl, always_use_https, min_tls_version, brotli, or development_mode.",
|
|
1310
|
+
annotations: readOnly,
|
|
1311
|
+
inputSchema: {
|
|
1312
|
+
type: "object",
|
|
1313
|
+
properties: {
|
|
1314
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1315
|
+
settingId: {
|
|
1316
|
+
type: "string",
|
|
1317
|
+
minLength: 1,
|
|
1318
|
+
description: "Cloudflare zone setting id from list_zone_settings.",
|
|
1319
|
+
},
|
|
1320
|
+
},
|
|
1321
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "settingId"],
|
|
1322
|
+
additionalProperties: false,
|
|
1323
|
+
},
|
|
1324
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1325
|
+
handler: async (args, ctx) => {
|
|
1326
|
+
const { result } = await callCloudflare(base, {
|
|
1327
|
+
method: "GET",
|
|
1328
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1329
|
+
}, ctx);
|
|
1330
|
+
return result;
|
|
1331
|
+
},
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
name: "update_zone_setting",
|
|
1335
|
+
description: "Set one editable zone setting. Read it first: allowed value types and plan restrictions differ by setting.",
|
|
1336
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1337
|
+
inputSchema: {
|
|
1338
|
+
type: "object",
|
|
1339
|
+
properties: {
|
|
1340
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1341
|
+
settingId: {
|
|
1342
|
+
type: "string",
|
|
1343
|
+
minLength: 1,
|
|
1344
|
+
description: "Cloudflare zone setting id from list_zone_settings.",
|
|
1345
|
+
},
|
|
1346
|
+
value: {
|
|
1347
|
+
type: ["string", "number", "boolean", "array"],
|
|
1348
|
+
description: "New setting value in the type returned by get_zone_setting. Arrays must contain strings.",
|
|
1349
|
+
items: { type: "string" },
|
|
1350
|
+
},
|
|
1351
|
+
},
|
|
1352
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "settingId", "value"],
|
|
1353
|
+
additionalProperties: false,
|
|
1354
|
+
},
|
|
1355
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1356
|
+
handler: async (args, ctx) => {
|
|
1357
|
+
const { result } = await callCloudflare(base, {
|
|
1358
|
+
method: "PATCH",
|
|
1359
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1360
|
+
body: { value: args["value"] },
|
|
1361
|
+
}, ctx);
|
|
1362
|
+
return result;
|
|
1363
|
+
},
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
name: "list_zone_rulesets",
|
|
1367
|
+
description: "List zone rulesets for WAF, redirects, transforms, cache rules, configuration rules, and other Ruleset Engine phases.",
|
|
1368
|
+
annotations: readOnly,
|
|
1369
|
+
inputSchema: {
|
|
1370
|
+
type: "object",
|
|
1371
|
+
properties: {
|
|
1372
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1373
|
+
perPage: {
|
|
1374
|
+
type: "integer",
|
|
1375
|
+
minimum: 1,
|
|
1376
|
+
maximum: 50,
|
|
1377
|
+
description: "Rulesets per request, 1 to 50.",
|
|
1378
|
+
},
|
|
1379
|
+
cursor: {
|
|
1380
|
+
type: "string",
|
|
1381
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
1382
|
+
},
|
|
1383
|
+
},
|
|
1384
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1385
|
+
additionalProperties: false,
|
|
1386
|
+
},
|
|
1387
|
+
outputSchema: {
|
|
1388
|
+
type: "object",
|
|
1389
|
+
properties: {
|
|
1390
|
+
rulesets: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
1391
|
+
nextCursor: { type: "string" },
|
|
1392
|
+
},
|
|
1393
|
+
required: ["rulesets"],
|
|
1394
|
+
},
|
|
1395
|
+
handler: async (args, ctx) => {
|
|
1396
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
1397
|
+
method: "GET",
|
|
1398
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets`,
|
|
1399
|
+
query: {
|
|
1400
|
+
per_page: optionalNumber(args, "perPage"),
|
|
1401
|
+
cursor: optionalString(args, "cursor"),
|
|
1402
|
+
},
|
|
1403
|
+
}, ctx);
|
|
1404
|
+
const cursor = resultInfo?.cursors?.after;
|
|
1405
|
+
return {
|
|
1406
|
+
rulesets: asArray(result).map(projectRuleset),
|
|
1407
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
1408
|
+
? { nextCursor: cursor }
|
|
1409
|
+
: {}),
|
|
1410
|
+
};
|
|
1411
|
+
},
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
name: "get_zone_ruleset",
|
|
1415
|
+
description: "Get one zone ruleset including its ordered rules, expressions, actions, parameters, and enabled state.",
|
|
1416
|
+
annotations: readOnly,
|
|
1417
|
+
inputSchema: {
|
|
1418
|
+
type: "object",
|
|
1419
|
+
properties: {
|
|
1420
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1421
|
+
rulesetId: {
|
|
1422
|
+
type: "string",
|
|
1423
|
+
minLength: 1,
|
|
1424
|
+
description: "Ruleset id from list_zone_rulesets.",
|
|
1425
|
+
},
|
|
1426
|
+
},
|
|
1427
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "rulesetId"],
|
|
1428
|
+
additionalProperties: false,
|
|
1429
|
+
},
|
|
1430
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1431
|
+
handler: async (args, ctx) => {
|
|
1432
|
+
const { result } = await callCloudflare(base, {
|
|
1433
|
+
method: "GET",
|
|
1434
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets/${encodePathSegment(requireString(args, "rulesetId"))}`,
|
|
1435
|
+
}, ctx);
|
|
1436
|
+
return projectRuleset(result);
|
|
1437
|
+
},
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
name: "list_dns_records",
|
|
1441
|
+
description: "List DNS records in a zone, filtered by name, type, or content. Returns record ids, which update_dns_record and delete_dns_record require.",
|
|
1442
|
+
annotations: readOnly,
|
|
1443
|
+
inputSchema: {
|
|
1444
|
+
type: "object",
|
|
1445
|
+
properties: {
|
|
1446
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1447
|
+
name: {
|
|
1448
|
+
type: "string",
|
|
1449
|
+
description: "Exact record name, fully qualified, e.g. www.example.com.",
|
|
1450
|
+
},
|
|
1451
|
+
type: {
|
|
1452
|
+
type: "string",
|
|
1453
|
+
enum: [...CLOUDFLARE_DNS_RECORD_TYPES],
|
|
1454
|
+
description: "Filter by record type.",
|
|
1455
|
+
},
|
|
1456
|
+
content: {
|
|
1457
|
+
type: "string",
|
|
1458
|
+
description: "Exact record content, e.g. an IP address.",
|
|
1459
|
+
},
|
|
1460
|
+
order: {
|
|
1461
|
+
type: "string",
|
|
1462
|
+
enum: ["type", "name", "content", "ttl", "proxied"],
|
|
1463
|
+
description: "Sort field.",
|
|
1464
|
+
},
|
|
1465
|
+
direction: {
|
|
1466
|
+
type: "string",
|
|
1467
|
+
enum: ["asc", "desc"],
|
|
1468
|
+
description: "Sort direction for `order`. Defaults to asc.",
|
|
1469
|
+
},
|
|
1470
|
+
// Cloudflare documents 1 to 5,000,000 here with a default of 100; the
|
|
1471
|
+
// ceiling is nominal, so this connection caps it at a page size that
|
|
1472
|
+
// actually returns.
|
|
1473
|
+
...pagingInputProperties(1, 1000, {
|
|
1474
|
+
defaultPerPage: 100,
|
|
1475
|
+
bounds: "clamped",
|
|
1476
|
+
}),
|
|
1477
|
+
raw: RAW_INPUT_PROPERTY,
|
|
1478
|
+
},
|
|
1479
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1480
|
+
additionalProperties: false,
|
|
1481
|
+
},
|
|
1482
|
+
outputSchema: listOutputSchema("records", DNS_RECORD_SCHEMA),
|
|
1483
|
+
handler: async (args, ctx) => {
|
|
1484
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
1485
|
+
method: "GET",
|
|
1486
|
+
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records`,
|
|
1487
|
+
query: {
|
|
762
1488
|
name: optionalString(args, "name"),
|
|
763
1489
|
type: optionalString(args, "type"),
|
|
764
1490
|
content: optionalString(args, "content"),
|
|
@@ -838,6 +1564,136 @@ function buildTools(scope) {
|
|
|
838
1564
|
};
|
|
839
1565
|
},
|
|
840
1566
|
},
|
|
1567
|
+
{
|
|
1568
|
+
name: "get_worker_settings",
|
|
1569
|
+
description: "Get a Worker's compatibility date and flags, bindings, limits, observability, placement, usage model, and other script settings.",
|
|
1570
|
+
annotations: readOnly,
|
|
1571
|
+
inputSchema: {
|
|
1572
|
+
type: "object",
|
|
1573
|
+
properties: {
|
|
1574
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1575
|
+
scriptName: {
|
|
1576
|
+
type: "string",
|
|
1577
|
+
minLength: 1,
|
|
1578
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1579
|
+
},
|
|
1580
|
+
},
|
|
1581
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
1582
|
+
additionalProperties: false,
|
|
1583
|
+
},
|
|
1584
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1585
|
+
handler: async (args, ctx) => {
|
|
1586
|
+
const { result } = await callCloudflare(base, {
|
|
1587
|
+
method: "GET",
|
|
1588
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/settings`,
|
|
1589
|
+
}, ctx);
|
|
1590
|
+
return result;
|
|
1591
|
+
},
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
name: "list_worker_deployments",
|
|
1595
|
+
description: "List deployments of a Worker script, including version traffic allocations and deployment strategy.",
|
|
1596
|
+
annotations: readOnly,
|
|
1597
|
+
inputSchema: {
|
|
1598
|
+
type: "object",
|
|
1599
|
+
properties: {
|
|
1600
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1601
|
+
scriptName: {
|
|
1602
|
+
type: "string",
|
|
1603
|
+
minLength: 1,
|
|
1604
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1605
|
+
},
|
|
1606
|
+
},
|
|
1607
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
1608
|
+
additionalProperties: false,
|
|
1609
|
+
},
|
|
1610
|
+
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
1611
|
+
handler: async (args, ctx) => {
|
|
1612
|
+
const { result } = await callCloudflare(base, {
|
|
1613
|
+
method: "GET",
|
|
1614
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments`,
|
|
1615
|
+
}, ctx);
|
|
1616
|
+
const record = asRecord(result);
|
|
1617
|
+
const deployments = Array.isArray(result)
|
|
1618
|
+
? result
|
|
1619
|
+
: asArray(record["deployments"]);
|
|
1620
|
+
return { deployments: deployments.map(projectWorkerDeployment) };
|
|
1621
|
+
},
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
name: "get_worker_deployment",
|
|
1625
|
+
description: "Get one Worker deployment and its version traffic allocations.",
|
|
1626
|
+
annotations: readOnly,
|
|
1627
|
+
inputSchema: {
|
|
1628
|
+
type: "object",
|
|
1629
|
+
properties: {
|
|
1630
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1631
|
+
scriptName: {
|
|
1632
|
+
type: "string",
|
|
1633
|
+
minLength: 1,
|
|
1634
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1635
|
+
},
|
|
1636
|
+
deploymentId: {
|
|
1637
|
+
type: "string",
|
|
1638
|
+
minLength: 1,
|
|
1639
|
+
description: "Deployment id from list_worker_deployments.",
|
|
1640
|
+
},
|
|
1641
|
+
},
|
|
1642
|
+
required: [
|
|
1643
|
+
...scopeRequired("accountId", scope.accountId),
|
|
1644
|
+
"scriptName",
|
|
1645
|
+
"deploymentId",
|
|
1646
|
+
],
|
|
1647
|
+
additionalProperties: false,
|
|
1648
|
+
},
|
|
1649
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1650
|
+
handler: async (args, ctx) => {
|
|
1651
|
+
const { result } = await callCloudflare(base, {
|
|
1652
|
+
method: "GET",
|
|
1653
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
1654
|
+
}, ctx);
|
|
1655
|
+
return projectWorkerDeployment(result);
|
|
1656
|
+
},
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
name: "delete_worker_script",
|
|
1660
|
+
description: "Delete a Worker script and stop traffic served by that script. This cannot be undone from the API.",
|
|
1661
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1662
|
+
inputSchema: {
|
|
1663
|
+
type: "object",
|
|
1664
|
+
properties: {
|
|
1665
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1666
|
+
scriptName: {
|
|
1667
|
+
type: "string",
|
|
1668
|
+
minLength: 1,
|
|
1669
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1670
|
+
},
|
|
1671
|
+
force: {
|
|
1672
|
+
type: "boolean",
|
|
1673
|
+
description: "Pass Cloudflare's force=true option when the script has dependencies that permit forced removal.",
|
|
1674
|
+
},
|
|
1675
|
+
},
|
|
1676
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
1677
|
+
additionalProperties: false,
|
|
1678
|
+
},
|
|
1679
|
+
outputSchema: {
|
|
1680
|
+
type: "object",
|
|
1681
|
+
properties: {
|
|
1682
|
+
deleted: { type: "boolean" },
|
|
1683
|
+
scriptName: { type: "string" },
|
|
1684
|
+
},
|
|
1685
|
+
required: ["deleted", "scriptName"],
|
|
1686
|
+
},
|
|
1687
|
+
handler: async (args, ctx) => {
|
|
1688
|
+
const scriptName = requireString(args, "scriptName");
|
|
1689
|
+
await callCloudflare(base, {
|
|
1690
|
+
method: "DELETE",
|
|
1691
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(scriptName)}`,
|
|
1692
|
+
query: { force: optionalBoolean(args, "force") },
|
|
1693
|
+
}, ctx);
|
|
1694
|
+
return { deleted: true, scriptName };
|
|
1695
|
+
},
|
|
1696
|
+
},
|
|
841
1697
|
{
|
|
842
1698
|
name: "list_kv_namespaces",
|
|
843
1699
|
description: "List Workers KV namespaces in an account, with the namespace ids bindings refer to.",
|
|
@@ -879,45 +1735,371 @@ function buildTools(scope) {
|
|
|
879
1735
|
},
|
|
880
1736
|
},
|
|
881
1737
|
{
|
|
882
|
-
name: "
|
|
883
|
-
description: "
|
|
1738
|
+
name: "get_kv_namespace",
|
|
1739
|
+
description: "Get one Workers KV namespace by id.",
|
|
884
1740
|
annotations: readOnly,
|
|
885
1741
|
inputSchema: {
|
|
886
1742
|
type: "object",
|
|
887
1743
|
properties: {
|
|
888
1744
|
accountId: scopeProperty("accountId", scope.accountId),
|
|
889
|
-
|
|
890
|
-
type: "string",
|
|
891
|
-
description: "Filter to buckets whose name contains this string.",
|
|
892
|
-
},
|
|
893
|
-
perPage: {
|
|
894
|
-
type: "integer",
|
|
895
|
-
minimum: 1,
|
|
896
|
-
maximum: 1000,
|
|
897
|
-
description: "Buckets per request, 1 to 1000. Defaults to 20.",
|
|
898
|
-
},
|
|
899
|
-
cursor: {
|
|
1745
|
+
namespaceId: {
|
|
900
1746
|
type: "string",
|
|
901
|
-
|
|
1747
|
+
minLength: 1,
|
|
1748
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
902
1749
|
},
|
|
903
|
-
raw: RAW_INPUT_PROPERTY,
|
|
904
1750
|
},
|
|
905
|
-
required: scopeRequired("accountId", scope.accountId),
|
|
1751
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
906
1752
|
additionalProperties: false,
|
|
907
1753
|
},
|
|
908
|
-
outputSchema:
|
|
1754
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1755
|
+
handler: async (args, ctx) => {
|
|
1756
|
+
const { result } = await callCloudflare(base, {
|
|
1757
|
+
method: "GET",
|
|
1758
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
1759
|
+
}, ctx);
|
|
1760
|
+
return projectKvNamespace(result);
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: "create_kv_namespace",
|
|
1765
|
+
description: "Create a Workers KV namespace.",
|
|
1766
|
+
annotations: { readOnlyHint: false },
|
|
1767
|
+
inputSchema: {
|
|
909
1768
|
type: "object",
|
|
910
1769
|
properties: {
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1770
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1771
|
+
title: {
|
|
1772
|
+
type: "string",
|
|
1773
|
+
minLength: 1,
|
|
1774
|
+
maxLength: 512,
|
|
1775
|
+
description: "Human-readable namespace title.",
|
|
1776
|
+
},
|
|
1777
|
+
},
|
|
1778
|
+
required: [...scopeRequired("accountId", scope.accountId), "title"],
|
|
1779
|
+
additionalProperties: false,
|
|
1780
|
+
},
|
|
1781
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1782
|
+
handler: async (args, ctx) => {
|
|
1783
|
+
const { result } = await callCloudflare(base, {
|
|
1784
|
+
method: "POST",
|
|
1785
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces`,
|
|
1786
|
+
body: { title: requireString(args, "title") },
|
|
1787
|
+
}, ctx);
|
|
1788
|
+
return projectKvNamespace(result);
|
|
1789
|
+
},
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
name: "rename_kv_namespace",
|
|
1793
|
+
description: "Rename an existing Workers KV namespace without changing its id or keys.",
|
|
1794
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1795
|
+
inputSchema: {
|
|
1796
|
+
type: "object",
|
|
1797
|
+
properties: {
|
|
1798
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1799
|
+
namespaceId: {
|
|
1800
|
+
type: "string",
|
|
1801
|
+
minLength: 1,
|
|
1802
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
1803
|
+
},
|
|
1804
|
+
title: {
|
|
1805
|
+
type: "string",
|
|
1806
|
+
minLength: 1,
|
|
1807
|
+
maxLength: 512,
|
|
1808
|
+
description: "Replacement namespace title.",
|
|
1809
|
+
},
|
|
1810
|
+
},
|
|
1811
|
+
required: [
|
|
1812
|
+
...scopeRequired("accountId", scope.accountId),
|
|
1813
|
+
"namespaceId",
|
|
1814
|
+
"title",
|
|
1815
|
+
],
|
|
1816
|
+
additionalProperties: false,
|
|
1817
|
+
},
|
|
1818
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1819
|
+
handler: async (args, ctx) => {
|
|
1820
|
+
const { result } = await callCloudflare(base, {
|
|
1821
|
+
method: "PUT",
|
|
1822
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
1823
|
+
body: { title: requireString(args, "title") },
|
|
1824
|
+
}, ctx);
|
|
1825
|
+
return result ?? { renamed: true, namespaceId: args["namespaceId"] };
|
|
1826
|
+
},
|
|
1827
|
+
},
|
|
1828
|
+
{
|
|
1829
|
+
name: "delete_kv_namespace",
|
|
1830
|
+
description: "Permanently delete a Workers KV namespace and every key stored in it.",
|
|
1831
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1832
|
+
inputSchema: {
|
|
1833
|
+
type: "object",
|
|
1834
|
+
properties: {
|
|
1835
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1836
|
+
namespaceId: {
|
|
1837
|
+
type: "string",
|
|
1838
|
+
minLength: 1,
|
|
1839
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
1840
|
+
},
|
|
1841
|
+
},
|
|
1842
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
1843
|
+
additionalProperties: false,
|
|
1844
|
+
},
|
|
1845
|
+
outputSchema: {
|
|
1846
|
+
type: "object",
|
|
1847
|
+
properties: {
|
|
1848
|
+
deleted: { type: "boolean" },
|
|
1849
|
+
namespaceId: { type: "string" },
|
|
1850
|
+
},
|
|
1851
|
+
required: ["deleted", "namespaceId"],
|
|
1852
|
+
},
|
|
1853
|
+
handler: async (args, ctx) => {
|
|
1854
|
+
const namespaceId = requireString(args, "namespaceId");
|
|
1855
|
+
await callCloudflare(base, {
|
|
1856
|
+
method: "DELETE",
|
|
1857
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(namespaceId)}`,
|
|
1858
|
+
}, ctx);
|
|
1859
|
+
return { deleted: true, namespaceId };
|
|
1860
|
+
},
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
name: "list_kv_keys",
|
|
1864
|
+
description: "List keys and metadata in a Workers KV namespace by prefix, using cursor pagination.",
|
|
1865
|
+
annotations: readOnly,
|
|
1866
|
+
inputSchema: {
|
|
1867
|
+
type: "object",
|
|
1868
|
+
properties: {
|
|
1869
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1870
|
+
namespaceId: {
|
|
1871
|
+
type: "string",
|
|
1872
|
+
minLength: 1,
|
|
1873
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
1874
|
+
},
|
|
1875
|
+
prefix: {
|
|
1876
|
+
type: "string",
|
|
1877
|
+
description: "Return only keys beginning with this prefix.",
|
|
1878
|
+
},
|
|
1879
|
+
limit: {
|
|
1880
|
+
type: "integer",
|
|
1881
|
+
minimum: 10,
|
|
1882
|
+
maximum: 1000,
|
|
1883
|
+
description: "Keys per request, 10 to 1000. Defaults to 1000.",
|
|
1884
|
+
},
|
|
1885
|
+
cursor: {
|
|
1886
|
+
type: "string",
|
|
1887
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
1888
|
+
},
|
|
1889
|
+
},
|
|
1890
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
1891
|
+
additionalProperties: false,
|
|
1892
|
+
},
|
|
1893
|
+
outputSchema: {
|
|
1894
|
+
type: "object",
|
|
1895
|
+
properties: {
|
|
1896
|
+
keys: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
1897
|
+
nextCursor: { type: "string" },
|
|
1898
|
+
},
|
|
1899
|
+
required: ["keys"],
|
|
1900
|
+
},
|
|
1901
|
+
handler: async (args, ctx) => {
|
|
1902
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
1903
|
+
method: "GET",
|
|
1904
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/keys`,
|
|
1905
|
+
query: {
|
|
1906
|
+
prefix: optionalString(args, "prefix"),
|
|
1907
|
+
limit: optionalNumber(args, "limit"),
|
|
1908
|
+
cursor: optionalString(args, "cursor"),
|
|
1909
|
+
},
|
|
1910
|
+
}, ctx);
|
|
1911
|
+
const cursor = resultInfo?.cursor;
|
|
1912
|
+
return {
|
|
1913
|
+
keys: asArray(result).map(projectKvKey),
|
|
1914
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
1915
|
+
? { nextCursor: cursor }
|
|
1916
|
+
: {}),
|
|
1917
|
+
};
|
|
1918
|
+
},
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
name: "bulk_get_kv_values",
|
|
1922
|
+
description: "Read up to 100 Workers KV values in one request. This JSON endpoint is suitable for text and JSON values; use the raw API for specialized response types.",
|
|
1923
|
+
annotations: readOnly,
|
|
1924
|
+
inputSchema: {
|
|
1925
|
+
type: "object",
|
|
1926
|
+
properties: {
|
|
1927
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1928
|
+
namespaceId: {
|
|
1929
|
+
type: "string",
|
|
1930
|
+
minLength: 1,
|
|
1931
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
1932
|
+
},
|
|
1933
|
+
keys: {
|
|
1934
|
+
type: "array",
|
|
1935
|
+
minItems: 1,
|
|
1936
|
+
maxItems: 100,
|
|
1937
|
+
items: { type: "string", minLength: 1, maxLength: 512 },
|
|
1938
|
+
description: "Key names to retrieve, up to 100.",
|
|
1939
|
+
},
|
|
1940
|
+
withMetadata: {
|
|
1941
|
+
type: "boolean",
|
|
1942
|
+
description: "Include each key's metadata and expiration when true.",
|
|
1943
|
+
},
|
|
1944
|
+
type: {
|
|
1945
|
+
type: "string",
|
|
1946
|
+
enum: ["text", "json"],
|
|
1947
|
+
description: "Return strings as stored, or parse JSON values before returning them.",
|
|
1948
|
+
},
|
|
1949
|
+
},
|
|
1950
|
+
required: [
|
|
1951
|
+
...scopeRequired("accountId", scope.accountId),
|
|
1952
|
+
"namespaceId",
|
|
1953
|
+
"keys",
|
|
1954
|
+
],
|
|
1955
|
+
additionalProperties: false,
|
|
1956
|
+
},
|
|
1957
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1958
|
+
handler: async (args, ctx) => {
|
|
1959
|
+
const { result } = await callCloudflare(base, {
|
|
1960
|
+
method: "POST",
|
|
1961
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/get`,
|
|
1962
|
+
body: {
|
|
1963
|
+
keys: args["keys"],
|
|
1964
|
+
...(args["withMetadata"] !== undefined
|
|
1965
|
+
? { withMetadata: args["withMetadata"] }
|
|
1966
|
+
: {}),
|
|
1967
|
+
...(args["type"] !== undefined ? { type: args["type"] } : {}),
|
|
1968
|
+
},
|
|
1969
|
+
}, ctx);
|
|
1970
|
+
return asRecord(result);
|
|
1971
|
+
},
|
|
1972
|
+
},
|
|
1973
|
+
{
|
|
1974
|
+
name: "bulk_write_kv_values",
|
|
1975
|
+
description: "Create or replace multiple Workers KV values, with optional expirations and JSON metadata.",
|
|
1976
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1977
|
+
inputSchema: {
|
|
1978
|
+
type: "object",
|
|
1979
|
+
properties: {
|
|
1980
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1981
|
+
namespaceId: {
|
|
1982
|
+
type: "string",
|
|
1983
|
+
minLength: 1,
|
|
1984
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
1985
|
+
},
|
|
1986
|
+
entries: {
|
|
1987
|
+
type: "array",
|
|
1988
|
+
minItems: 1,
|
|
1989
|
+
maxItems: 10_000,
|
|
1990
|
+
description: "Key/value entries to write, up to Cloudflare's 10,000-key bulk limit.",
|
|
1991
|
+
items: {
|
|
1992
|
+
type: "object",
|
|
1993
|
+
properties: {
|
|
1994
|
+
key: { type: "string", minLength: 1, maxLength: 512 },
|
|
1995
|
+
value: { type: "string", maxLength: 26_214_400 },
|
|
1996
|
+
expiration: { type: "number" },
|
|
1997
|
+
expiration_ttl: { type: "number", minimum: 60 },
|
|
1998
|
+
metadata: { type: ["object", "array", "string", "number", "boolean", "null"] },
|
|
1999
|
+
base64: { type: "boolean" },
|
|
2000
|
+
},
|
|
2001
|
+
required: ["key", "value"],
|
|
2002
|
+
additionalProperties: false,
|
|
2003
|
+
},
|
|
2004
|
+
},
|
|
2005
|
+
},
|
|
2006
|
+
required: [
|
|
2007
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2008
|
+
"namespaceId",
|
|
2009
|
+
"entries",
|
|
2010
|
+
],
|
|
2011
|
+
additionalProperties: false,
|
|
2012
|
+
},
|
|
2013
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2014
|
+
handler: async (args, ctx) => {
|
|
2015
|
+
const { result } = await callCloudflare(base, {
|
|
2016
|
+
method: "PUT",
|
|
2017
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk`,
|
|
2018
|
+
body: args["entries"],
|
|
2019
|
+
}, ctx);
|
|
2020
|
+
return asRecord(result);
|
|
2021
|
+
},
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
name: "bulk_delete_kv_values",
|
|
2025
|
+
description: "Permanently delete multiple keys from a Workers KV namespace.",
|
|
2026
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2027
|
+
inputSchema: {
|
|
2028
|
+
type: "object",
|
|
2029
|
+
properties: {
|
|
2030
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2031
|
+
namespaceId: {
|
|
2032
|
+
type: "string",
|
|
2033
|
+
minLength: 1,
|
|
2034
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2035
|
+
},
|
|
2036
|
+
keys: {
|
|
2037
|
+
type: "array",
|
|
2038
|
+
minItems: 1,
|
|
2039
|
+
maxItems: 10_000,
|
|
2040
|
+
items: { type: "string", minLength: 1, maxLength: 512 },
|
|
2041
|
+
description: "Key names to delete, up to Cloudflare's 10,000-key bulk limit.",
|
|
2042
|
+
},
|
|
2043
|
+
},
|
|
2044
|
+
required: [
|
|
2045
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2046
|
+
"namespaceId",
|
|
2047
|
+
"keys",
|
|
2048
|
+
],
|
|
2049
|
+
additionalProperties: false,
|
|
2050
|
+
},
|
|
2051
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2052
|
+
handler: async (args, ctx) => {
|
|
2053
|
+
const { result } = await callCloudflare(base, {
|
|
2054
|
+
method: "POST",
|
|
2055
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/delete`,
|
|
2056
|
+
body: args["keys"],
|
|
2057
|
+
}, ctx);
|
|
2058
|
+
return asRecord(result);
|
|
2059
|
+
},
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
name: "list_r2_buckets",
|
|
2063
|
+
description: "List R2 buckets in an account, with location and storage class.",
|
|
2064
|
+
annotations: readOnly,
|
|
2065
|
+
inputSchema: {
|
|
2066
|
+
type: "object",
|
|
2067
|
+
properties: {
|
|
2068
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2069
|
+
nameContains: {
|
|
2070
|
+
type: "string",
|
|
2071
|
+
description: "Filter to buckets whose name contains this string.",
|
|
2072
|
+
},
|
|
2073
|
+
perPage: {
|
|
2074
|
+
type: "integer",
|
|
2075
|
+
minimum: 1,
|
|
2076
|
+
maximum: 1000,
|
|
2077
|
+
description: "Buckets per request, 1 to 1000. Defaults to 20.",
|
|
2078
|
+
},
|
|
2079
|
+
cursor: {
|
|
2080
|
+
type: "string",
|
|
2081
|
+
description: "Opaque cursor from a previous call's nextCursor. R2 paginates by cursor, not page number.",
|
|
2082
|
+
},
|
|
2083
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2084
|
+
raw: RAW_INPUT_PROPERTY,
|
|
2085
|
+
},
|
|
2086
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
2087
|
+
additionalProperties: false,
|
|
2088
|
+
},
|
|
2089
|
+
outputSchema: {
|
|
2090
|
+
type: "object",
|
|
2091
|
+
properties: {
|
|
2092
|
+
buckets: {
|
|
2093
|
+
type: "array",
|
|
2094
|
+
items: {
|
|
2095
|
+
type: "object",
|
|
2096
|
+
properties: {
|
|
2097
|
+
name: { type: "string" },
|
|
2098
|
+
location: { type: "string" },
|
|
2099
|
+
storageClass: { type: "string" },
|
|
2100
|
+
jurisdiction: { type: "string" },
|
|
2101
|
+
creationDate: { type: "string" },
|
|
2102
|
+
},
|
|
921
2103
|
required: ["name"],
|
|
922
2104
|
},
|
|
923
2105
|
},
|
|
@@ -926,30 +2108,383 @@ function buildTools(scope) {
|
|
|
926
2108
|
description: "Pass back as `cursor` to continue. Absent when the listing is complete.",
|
|
927
2109
|
},
|
|
928
2110
|
},
|
|
929
|
-
required: ["buckets"],
|
|
2111
|
+
required: ["buckets"],
|
|
2112
|
+
},
|
|
2113
|
+
handler: async (args, ctx) => {
|
|
2114
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
2115
|
+
method: "GET",
|
|
2116
|
+
path: `/accounts/${encodeURIComponent(accountArg(args))}/r2/buckets`,
|
|
2117
|
+
query: {
|
|
2118
|
+
name_contains: optionalString(args, "nameContains"),
|
|
2119
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2120
|
+
cursor: optionalString(args, "cursor"),
|
|
2121
|
+
},
|
|
2122
|
+
headers: r2Headers(args),
|
|
2123
|
+
}, ctx);
|
|
2124
|
+
// R2 nests its list under `buckets` rather than returning a bare array,
|
|
2125
|
+
// and its result_info carries a cursor instead of page counters.
|
|
2126
|
+
const cursor = resultInfo?.cursor;
|
|
2127
|
+
const next = typeof cursor === "string" && cursor !== ""
|
|
2128
|
+
? { nextCursor: cursor }
|
|
2129
|
+
: {};
|
|
2130
|
+
if (args["raw"] === true)
|
|
2131
|
+
return { buckets: result, ...next };
|
|
2132
|
+
return {
|
|
2133
|
+
buckets: asArray(asRecord(result)["buckets"]).map(projectR2Bucket),
|
|
2134
|
+
...next,
|
|
2135
|
+
};
|
|
2136
|
+
},
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
name: "get_r2_bucket",
|
|
2140
|
+
description: "Get one R2 bucket's location, jurisdiction, storage class, and creation time.",
|
|
2141
|
+
annotations: readOnly,
|
|
2142
|
+
inputSchema: {
|
|
2143
|
+
type: "object",
|
|
2144
|
+
properties: {
|
|
2145
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2146
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2147
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2148
|
+
},
|
|
2149
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2150
|
+
additionalProperties: false,
|
|
2151
|
+
},
|
|
2152
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2153
|
+
handler: async (args, ctx) => {
|
|
2154
|
+
const { result } = await callCloudflare(base, {
|
|
2155
|
+
method: "GET",
|
|
2156
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2157
|
+
headers: r2Headers(args),
|
|
2158
|
+
}, ctx);
|
|
2159
|
+
return projectR2Bucket(result);
|
|
2160
|
+
},
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
name: "create_r2_bucket",
|
|
2164
|
+
description: "Create an R2 bucket with an optional location hint and default storage class.",
|
|
2165
|
+
annotations: { readOnlyHint: false },
|
|
2166
|
+
inputSchema: {
|
|
2167
|
+
type: "object",
|
|
2168
|
+
properties: {
|
|
2169
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2170
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2171
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2172
|
+
locationHint: {
|
|
2173
|
+
type: "string",
|
|
2174
|
+
enum: ["apac", "eeur", "enam", "weur", "wnam", "oc"],
|
|
2175
|
+
description: "Optional placement hint for the new bucket.",
|
|
2176
|
+
},
|
|
2177
|
+
storageClass: {
|
|
2178
|
+
type: "string",
|
|
2179
|
+
enum: ["Standard", "InfrequentAccess"],
|
|
2180
|
+
description: "Default storage class for new objects.",
|
|
2181
|
+
},
|
|
2182
|
+
},
|
|
2183
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2184
|
+
additionalProperties: false,
|
|
2185
|
+
},
|
|
2186
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2187
|
+
handler: async (args, ctx) => {
|
|
2188
|
+
const { result } = await callCloudflare(base, {
|
|
2189
|
+
method: "POST",
|
|
2190
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets`,
|
|
2191
|
+
headers: r2Headers(args),
|
|
2192
|
+
body: {
|
|
2193
|
+
name: requireString(args, "bucketName"),
|
|
2194
|
+
...(args["locationHint"] !== undefined
|
|
2195
|
+
? { locationHint: args["locationHint"] }
|
|
2196
|
+
: {}),
|
|
2197
|
+
...(args["storageClass"] !== undefined
|
|
2198
|
+
? { storageClass: args["storageClass"] }
|
|
2199
|
+
: {}),
|
|
2200
|
+
},
|
|
2201
|
+
}, ctx);
|
|
2202
|
+
return projectR2Bucket(result);
|
|
2203
|
+
},
|
|
2204
|
+
},
|
|
2205
|
+
{
|
|
2206
|
+
name: "update_r2_bucket",
|
|
2207
|
+
description: "Change the default storage class used for newly uploaded objects in an R2 bucket.",
|
|
2208
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2209
|
+
inputSchema: {
|
|
2210
|
+
type: "object",
|
|
2211
|
+
properties: {
|
|
2212
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2213
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2214
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2215
|
+
storageClass: {
|
|
2216
|
+
type: "string",
|
|
2217
|
+
enum: ["Standard", "InfrequentAccess"],
|
|
2218
|
+
description: "New default storage class for future uploads.",
|
|
2219
|
+
},
|
|
2220
|
+
},
|
|
2221
|
+
required: [
|
|
2222
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2223
|
+
"bucketName",
|
|
2224
|
+
"storageClass",
|
|
2225
|
+
],
|
|
2226
|
+
additionalProperties: false,
|
|
2227
|
+
},
|
|
2228
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2229
|
+
handler: async (args, ctx) => {
|
|
2230
|
+
const { result } = await callCloudflare(base, {
|
|
2231
|
+
method: "PATCH",
|
|
2232
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2233
|
+
headers: {
|
|
2234
|
+
...r2Headers(args),
|
|
2235
|
+
"cf-r2-storage-class": String(args["storageClass"]),
|
|
2236
|
+
},
|
|
2237
|
+
}, ctx);
|
|
2238
|
+
return projectR2Bucket(result);
|
|
2239
|
+
},
|
|
2240
|
+
},
|
|
2241
|
+
{
|
|
2242
|
+
name: "delete_r2_bucket",
|
|
2243
|
+
description: "Permanently delete an empty R2 bucket and all of its configuration. Cloudflare refuses non-empty buckets.",
|
|
2244
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2245
|
+
inputSchema: {
|
|
2246
|
+
type: "object",
|
|
2247
|
+
properties: {
|
|
2248
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2249
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2250
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2251
|
+
},
|
|
2252
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2253
|
+
additionalProperties: false,
|
|
2254
|
+
},
|
|
2255
|
+
outputSchema: {
|
|
2256
|
+
type: "object",
|
|
2257
|
+
properties: {
|
|
2258
|
+
deleted: { type: "boolean" },
|
|
2259
|
+
bucketName: { type: "string" },
|
|
2260
|
+
},
|
|
2261
|
+
required: ["deleted", "bucketName"],
|
|
2262
|
+
},
|
|
2263
|
+
handler: async (args, ctx) => {
|
|
2264
|
+
const bucketName = requireString(args, "bucketName");
|
|
2265
|
+
await callCloudflare(base, {
|
|
2266
|
+
method: "DELETE",
|
|
2267
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(bucketName)}`,
|
|
2268
|
+
headers: r2Headers(args),
|
|
2269
|
+
}, ctx);
|
|
2270
|
+
return { deleted: true, bucketName };
|
|
2271
|
+
},
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
name: "list_r2_objects",
|
|
2275
|
+
description: "List object keys and metadata in an R2 bucket by prefix, with delimiter grouping and cursor pagination.",
|
|
2276
|
+
annotations: readOnly,
|
|
2277
|
+
inputSchema: {
|
|
2278
|
+
type: "object",
|
|
2279
|
+
properties: {
|
|
2280
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2281
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2282
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2283
|
+
prefix: {
|
|
2284
|
+
type: "string",
|
|
2285
|
+
description: "Return only object keys beginning with this prefix.",
|
|
2286
|
+
},
|
|
2287
|
+
delimiter: {
|
|
2288
|
+
type: "string",
|
|
2289
|
+
minLength: 1,
|
|
2290
|
+
maxLength: 1,
|
|
2291
|
+
description: "One character used to group path-like keys, usually '/'.",
|
|
2292
|
+
},
|
|
2293
|
+
startAfter: {
|
|
2294
|
+
type: "string",
|
|
2295
|
+
description: "Begin after this key in lexicographic order.",
|
|
2296
|
+
},
|
|
2297
|
+
perPage: {
|
|
2298
|
+
type: "integer",
|
|
2299
|
+
minimum: 1,
|
|
2300
|
+
maximum: 1000,
|
|
2301
|
+
description: "Objects per request, 1 to 1000.",
|
|
2302
|
+
},
|
|
2303
|
+
cursor: {
|
|
2304
|
+
type: "string",
|
|
2305
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
2306
|
+
},
|
|
2307
|
+
},
|
|
2308
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2309
|
+
additionalProperties: false,
|
|
2310
|
+
},
|
|
2311
|
+
outputSchema: {
|
|
2312
|
+
type: "object",
|
|
2313
|
+
properties: {
|
|
2314
|
+
objects: { type: "array", items: R2_OBJECT_SCHEMA },
|
|
2315
|
+
commonPrefixes: { type: "array", items: { type: "string" } },
|
|
2316
|
+
nextCursor: { type: "string" },
|
|
2317
|
+
truncated: { type: "boolean" },
|
|
2318
|
+
},
|
|
2319
|
+
required: ["objects", "truncated"],
|
|
2320
|
+
},
|
|
2321
|
+
handler: async (args, ctx) => {
|
|
2322
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
2323
|
+
method: "GET",
|
|
2324
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects`,
|
|
2325
|
+
headers: r2Headers(args),
|
|
2326
|
+
query: {
|
|
2327
|
+
prefix: optionalString(args, "prefix"),
|
|
2328
|
+
delimiter: optionalString(args, "delimiter"),
|
|
2329
|
+
start_after: optionalString(args, "startAfter"),
|
|
2330
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2331
|
+
cursor: optionalString(args, "cursor"),
|
|
2332
|
+
},
|
|
2333
|
+
}, ctx);
|
|
2334
|
+
const cursor = resultInfo?.cursor;
|
|
2335
|
+
return {
|
|
2336
|
+
objects: asArray(result).map(projectR2Object),
|
|
2337
|
+
...(Array.isArray(resultInfo?.delimited)
|
|
2338
|
+
? { commonPrefixes: resultInfo.delimited }
|
|
2339
|
+
: {}),
|
|
2340
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
2341
|
+
? { nextCursor: cursor }
|
|
2342
|
+
: {}),
|
|
2343
|
+
truncated: resultInfo?.is_truncated === true,
|
|
2344
|
+
};
|
|
2345
|
+
},
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
name: "delete_r2_object",
|
|
2349
|
+
description: "Permanently delete one object from an R2 bucket by key.",
|
|
2350
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2351
|
+
inputSchema: {
|
|
2352
|
+
type: "object",
|
|
2353
|
+
properties: {
|
|
2354
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2355
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2356
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2357
|
+
objectKey: {
|
|
2358
|
+
type: "string",
|
|
2359
|
+
minLength: 1,
|
|
2360
|
+
description: "Exact object key. Slashes are preserved as path separators.",
|
|
2361
|
+
},
|
|
2362
|
+
},
|
|
2363
|
+
required: [
|
|
2364
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2365
|
+
"bucketName",
|
|
2366
|
+
"objectKey",
|
|
2367
|
+
],
|
|
2368
|
+
additionalProperties: false,
|
|
2369
|
+
},
|
|
2370
|
+
outputSchema: {
|
|
2371
|
+
type: "object",
|
|
2372
|
+
properties: {
|
|
2373
|
+
deleted: { type: "boolean" },
|
|
2374
|
+
objectKey: { type: "string" },
|
|
2375
|
+
},
|
|
2376
|
+
required: ["deleted", "objectKey"],
|
|
2377
|
+
},
|
|
2378
|
+
handler: async (args, ctx) => {
|
|
2379
|
+
const objectKey = requireString(args, "objectKey");
|
|
2380
|
+
await callCloudflare(base, {
|
|
2381
|
+
method: "DELETE",
|
|
2382
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects/${encodeObjectKey(objectKey)}`,
|
|
2383
|
+
headers: r2Headers(args),
|
|
2384
|
+
}, ctx);
|
|
2385
|
+
return { deleted: true, objectKey };
|
|
2386
|
+
},
|
|
2387
|
+
},
|
|
2388
|
+
{
|
|
2389
|
+
name: "get_r2_metrics",
|
|
2390
|
+
description: "Get account-level R2 object-count and storage-size metrics split by storage class and publication state.",
|
|
2391
|
+
annotations: readOnly,
|
|
2392
|
+
inputSchema: {
|
|
2393
|
+
type: "object",
|
|
2394
|
+
properties: { accountId: scopeProperty("accountId", scope.accountId) },
|
|
2395
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
2396
|
+
additionalProperties: false,
|
|
2397
|
+
},
|
|
2398
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2399
|
+
handler: async (args, ctx) => {
|
|
2400
|
+
const { result } = await callCloudflare(base, {
|
|
2401
|
+
method: "GET",
|
|
2402
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/metrics`,
|
|
2403
|
+
}, ctx);
|
|
2404
|
+
return asRecord(result);
|
|
2405
|
+
},
|
|
2406
|
+
},
|
|
2407
|
+
{
|
|
2408
|
+
name: "get_r2_cors",
|
|
2409
|
+
description: "Get the browser CORS rules configured on an R2 bucket.",
|
|
2410
|
+
annotations: readOnly,
|
|
2411
|
+
inputSchema: {
|
|
2412
|
+
type: "object",
|
|
2413
|
+
properties: {
|
|
2414
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2415
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2416
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2417
|
+
},
|
|
2418
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2419
|
+
additionalProperties: false,
|
|
930
2420
|
},
|
|
2421
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
931
2422
|
handler: async (args, ctx) => {
|
|
932
|
-
const { result
|
|
2423
|
+
const { result } = await callCloudflare(base, {
|
|
933
2424
|
method: "GET",
|
|
934
|
-
path: `/accounts/${
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
2425
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2426
|
+
headers: r2Headers(args),
|
|
2427
|
+
}, ctx);
|
|
2428
|
+
return asRecord(result);
|
|
2429
|
+
},
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: "set_r2_cors",
|
|
2433
|
+
description: "Replace an R2 bucket's CORS policy. Supply the complete desired rule list; omitted existing rules are removed.",
|
|
2434
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2435
|
+
inputSchema: {
|
|
2436
|
+
type: "object",
|
|
2437
|
+
properties: {
|
|
2438
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2439
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2440
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2441
|
+
rules: {
|
|
2442
|
+
type: "array",
|
|
2443
|
+
maxItems: 100,
|
|
2444
|
+
description: "Complete CORS rule list using Cloudflare fields: allowed.methods, allowed.origins, optional allowed.headers, id, exposeHeaders, and maxAgeSeconds.",
|
|
2445
|
+
items: { type: "object", additionalProperties: true },
|
|
939
2446
|
},
|
|
2447
|
+
},
|
|
2448
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName", "rules"],
|
|
2449
|
+
additionalProperties: false,
|
|
2450
|
+
},
|
|
2451
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2452
|
+
handler: async (args, ctx) => {
|
|
2453
|
+
const { result } = await callCloudflare(base, {
|
|
2454
|
+
method: "PUT",
|
|
2455
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2456
|
+
headers: r2Headers(args),
|
|
2457
|
+
body: { rules: args["rules"] },
|
|
940
2458
|
}, ctx);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
2459
|
+
return asRecord(result);
|
|
2460
|
+
},
|
|
2461
|
+
},
|
|
2462
|
+
{
|
|
2463
|
+
name: "delete_r2_cors",
|
|
2464
|
+
description: "Remove the complete CORS policy from an R2 bucket.",
|
|
2465
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2466
|
+
inputSchema: {
|
|
2467
|
+
type: "object",
|
|
2468
|
+
properties: {
|
|
2469
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2470
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2471
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2472
|
+
},
|
|
2473
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2474
|
+
additionalProperties: false,
|
|
2475
|
+
},
|
|
2476
|
+
outputSchema: {
|
|
2477
|
+
type: "object",
|
|
2478
|
+
properties: { deleted: { type: "boolean" } },
|
|
2479
|
+
required: ["deleted"],
|
|
2480
|
+
},
|
|
2481
|
+
handler: async (args, ctx) => {
|
|
2482
|
+
await callCloudflare(base, {
|
|
2483
|
+
method: "DELETE",
|
|
2484
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2485
|
+
headers: r2Headers(args),
|
|
2486
|
+
}, ctx);
|
|
2487
|
+
return { deleted: true };
|
|
953
2488
|
},
|
|
954
2489
|
},
|
|
955
2490
|
{
|
|
@@ -1003,6 +2538,294 @@ function buildTools(scope) {
|
|
|
1003
2538
|
};
|
|
1004
2539
|
},
|
|
1005
2540
|
},
|
|
2541
|
+
{
|
|
2542
|
+
name: "get_pages_project",
|
|
2543
|
+
description: "Get one Pages project, including build configuration, deployment configuration, domains, and latest deployment.",
|
|
2544
|
+
annotations: readOnly,
|
|
2545
|
+
inputSchema: {
|
|
2546
|
+
type: "object",
|
|
2547
|
+
properties: {
|
|
2548
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2549
|
+
projectName: {
|
|
2550
|
+
type: "string",
|
|
2551
|
+
minLength: 1,
|
|
2552
|
+
description: "Pages project name from list_pages_projects.",
|
|
2553
|
+
},
|
|
2554
|
+
raw: RAW_INPUT_PROPERTY,
|
|
2555
|
+
},
|
|
2556
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
2557
|
+
additionalProperties: false,
|
|
2558
|
+
},
|
|
2559
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2560
|
+
handler: async (args, ctx) => {
|
|
2561
|
+
const { result } = await callCloudflare(base, {
|
|
2562
|
+
method: "GET",
|
|
2563
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}`,
|
|
2564
|
+
}, ctx);
|
|
2565
|
+
return args["raw"] === true ? result : projectPagesProject(result);
|
|
2566
|
+
},
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
name: "list_pages_deployments",
|
|
2570
|
+
description: "List production and preview deployments for a Pages project.",
|
|
2571
|
+
annotations: readOnly,
|
|
2572
|
+
inputSchema: {
|
|
2573
|
+
type: "object",
|
|
2574
|
+
properties: {
|
|
2575
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2576
|
+
projectName: {
|
|
2577
|
+
type: "string",
|
|
2578
|
+
minLength: 1,
|
|
2579
|
+
description: "Pages project name from list_pages_projects.",
|
|
2580
|
+
},
|
|
2581
|
+
env: {
|
|
2582
|
+
type: "string",
|
|
2583
|
+
enum: ["production", "preview"],
|
|
2584
|
+
description: "Optional deployment environment filter.",
|
|
2585
|
+
},
|
|
2586
|
+
...pagingInputProperties(1, 100, { bounds: "undocumented" }),
|
|
2587
|
+
},
|
|
2588
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
2589
|
+
additionalProperties: false,
|
|
2590
|
+
},
|
|
2591
|
+
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
2592
|
+
handler: async (args, ctx) => {
|
|
2593
|
+
const { result, resultInfo } = await callCloudflare(base, {
|
|
2594
|
+
method: "GET",
|
|
2595
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments`,
|
|
2596
|
+
query: {
|
|
2597
|
+
env: optionalString(args, "env"),
|
|
2598
|
+
page: optionalNumber(args, "page"),
|
|
2599
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2600
|
+
},
|
|
2601
|
+
}, ctx);
|
|
2602
|
+
return {
|
|
2603
|
+
deployments: asArray(result).map(projectPagesDeployment),
|
|
2604
|
+
page: pageInfo(resultInfo),
|
|
2605
|
+
};
|
|
2606
|
+
},
|
|
2607
|
+
},
|
|
2608
|
+
{
|
|
2609
|
+
name: "get_pages_deployment",
|
|
2610
|
+
description: "Get one Pages deployment including its environment, URLs, stages, source, and build configuration.",
|
|
2611
|
+
annotations: readOnly,
|
|
2612
|
+
inputSchema: {
|
|
2613
|
+
type: "object",
|
|
2614
|
+
properties: {
|
|
2615
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2616
|
+
projectName: {
|
|
2617
|
+
type: "string",
|
|
2618
|
+
minLength: 1,
|
|
2619
|
+
description: "Pages project name from list_pages_projects.",
|
|
2620
|
+
},
|
|
2621
|
+
deploymentId: {
|
|
2622
|
+
type: "string",
|
|
2623
|
+
minLength: 1,
|
|
2624
|
+
description: "Deployment id from list_pages_deployments.",
|
|
2625
|
+
},
|
|
2626
|
+
raw: RAW_INPUT_PROPERTY,
|
|
2627
|
+
},
|
|
2628
|
+
required: [
|
|
2629
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2630
|
+
"projectName",
|
|
2631
|
+
"deploymentId",
|
|
2632
|
+
],
|
|
2633
|
+
additionalProperties: false,
|
|
2634
|
+
},
|
|
2635
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2636
|
+
handler: async (args, ctx) => {
|
|
2637
|
+
const { result } = await callCloudflare(base, {
|
|
2638
|
+
method: "GET",
|
|
2639
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
2640
|
+
}, ctx);
|
|
2641
|
+
return args["raw"] === true ? result : projectPagesDeployment(result);
|
|
2642
|
+
},
|
|
2643
|
+
},
|
|
2644
|
+
{
|
|
2645
|
+
name: "retry_pages_deployment",
|
|
2646
|
+
description: "Retry a failed or cancelled Pages deployment using its existing source and build configuration.",
|
|
2647
|
+
annotations: { readOnlyHint: false },
|
|
2648
|
+
inputSchema: {
|
|
2649
|
+
type: "object",
|
|
2650
|
+
properties: {
|
|
2651
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2652
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2653
|
+
deploymentId: { type: "string", minLength: 1, description: "Deployment id to retry." },
|
|
2654
|
+
},
|
|
2655
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
2656
|
+
additionalProperties: false,
|
|
2657
|
+
},
|
|
2658
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2659
|
+
handler: async (args, ctx) => {
|
|
2660
|
+
const { result } = await callCloudflare(base, {
|
|
2661
|
+
method: "POST",
|
|
2662
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/retry`,
|
|
2663
|
+
}, ctx);
|
|
2664
|
+
return projectPagesDeployment(result);
|
|
2665
|
+
},
|
|
2666
|
+
},
|
|
2667
|
+
{
|
|
2668
|
+
name: "rollback_pages_deployment",
|
|
2669
|
+
description: "Promote a previous Pages deployment to production, replacing the currently served production deployment.",
|
|
2670
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2671
|
+
inputSchema: {
|
|
2672
|
+
type: "object",
|
|
2673
|
+
properties: {
|
|
2674
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2675
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2676
|
+
deploymentId: { type: "string", minLength: 1, description: "Previous deployment id to promote." },
|
|
2677
|
+
},
|
|
2678
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
2679
|
+
additionalProperties: false,
|
|
2680
|
+
},
|
|
2681
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2682
|
+
handler: async (args, ctx) => {
|
|
2683
|
+
const { result } = await callCloudflare(base, {
|
|
2684
|
+
method: "POST",
|
|
2685
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/rollback`,
|
|
2686
|
+
}, ctx);
|
|
2687
|
+
return projectPagesDeployment(result);
|
|
2688
|
+
},
|
|
2689
|
+
},
|
|
2690
|
+
{
|
|
2691
|
+
name: "delete_pages_deployment",
|
|
2692
|
+
description: "Permanently delete a Pages deployment and its immutable deployment URL.",
|
|
2693
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2694
|
+
inputSchema: {
|
|
2695
|
+
type: "object",
|
|
2696
|
+
properties: {
|
|
2697
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2698
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2699
|
+
deploymentId: { type: "string", minLength: 1, description: "Deployment id to delete." },
|
|
2700
|
+
},
|
|
2701
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
2702
|
+
additionalProperties: false,
|
|
2703
|
+
},
|
|
2704
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, deploymentId: { type: "string" } }, required: ["deleted", "deploymentId"] },
|
|
2705
|
+
handler: async (args, ctx) => {
|
|
2706
|
+
const deploymentId = requireString(args, "deploymentId");
|
|
2707
|
+
await callCloudflare(base, {
|
|
2708
|
+
method: "DELETE",
|
|
2709
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(deploymentId)}`,
|
|
2710
|
+
}, ctx);
|
|
2711
|
+
return { deleted: true, deploymentId };
|
|
2712
|
+
},
|
|
2713
|
+
},
|
|
2714
|
+
{
|
|
2715
|
+
name: "list_pages_domains",
|
|
2716
|
+
description: "List custom domains attached to a Pages project and their validation status.",
|
|
2717
|
+
annotations: readOnly,
|
|
2718
|
+
inputSchema: {
|
|
2719
|
+
type: "object",
|
|
2720
|
+
properties: {
|
|
2721
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2722
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2723
|
+
},
|
|
2724
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
2725
|
+
additionalProperties: false,
|
|
2726
|
+
},
|
|
2727
|
+
outputSchema: listOutputSchema("domains", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
2728
|
+
handler: async (args, ctx) => {
|
|
2729
|
+
const { result } = await callCloudflare(base, {
|
|
2730
|
+
method: "GET",
|
|
2731
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
2732
|
+
}, ctx);
|
|
2733
|
+
return { domains: asArray(result).map(projectPagesDomain) };
|
|
2734
|
+
},
|
|
2735
|
+
},
|
|
2736
|
+
{
|
|
2737
|
+
name: "add_pages_domain",
|
|
2738
|
+
description: "Attach a custom domain to a Pages project. DNS ownership and validation still apply.",
|
|
2739
|
+
annotations: { readOnlyHint: false },
|
|
2740
|
+
inputSchema: {
|
|
2741
|
+
type: "object",
|
|
2742
|
+
properties: {
|
|
2743
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2744
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2745
|
+
domain: { type: "string", minLength: 1, description: "Fully qualified custom domain to attach." },
|
|
2746
|
+
},
|
|
2747
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "domain"],
|
|
2748
|
+
additionalProperties: false,
|
|
2749
|
+
},
|
|
2750
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2751
|
+
handler: async (args, ctx) => {
|
|
2752
|
+
const { result } = await callCloudflare(base, {
|
|
2753
|
+
method: "POST",
|
|
2754
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
2755
|
+
body: { name: requireString(args, "domain") },
|
|
2756
|
+
}, ctx);
|
|
2757
|
+
return projectPagesDomain(result);
|
|
2758
|
+
},
|
|
2759
|
+
},
|
|
2760
|
+
{
|
|
2761
|
+
name: "delete_pages_domain",
|
|
2762
|
+
description: "Detach a custom domain from a Pages project.",
|
|
2763
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2764
|
+
inputSchema: {
|
|
2765
|
+
type: "object",
|
|
2766
|
+
properties: {
|
|
2767
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2768
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2769
|
+
domain: { type: "string", minLength: 1, description: "Custom domain to detach." },
|
|
2770
|
+
},
|
|
2771
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "domain"],
|
|
2772
|
+
additionalProperties: false,
|
|
2773
|
+
},
|
|
2774
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, domain: { type: "string" } }, required: ["deleted", "domain"] },
|
|
2775
|
+
handler: async (args, ctx) => {
|
|
2776
|
+
const domain = requireString(args, "domain");
|
|
2777
|
+
await callCloudflare(base, {
|
|
2778
|
+
method: "DELETE",
|
|
2779
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains/${encodePathSegment(domain)}`,
|
|
2780
|
+
}, ctx);
|
|
2781
|
+
return { deleted: true, domain };
|
|
2782
|
+
},
|
|
2783
|
+
},
|
|
2784
|
+
{
|
|
2785
|
+
name: "purge_pages_build_cache",
|
|
2786
|
+
description: "Clear a Pages project's build cache so its next deployment rebuilds dependencies and artifacts from scratch.",
|
|
2787
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2788
|
+
inputSchema: {
|
|
2789
|
+
type: "object",
|
|
2790
|
+
properties: {
|
|
2791
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2792
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
2793
|
+
},
|
|
2794
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
2795
|
+
additionalProperties: false,
|
|
2796
|
+
},
|
|
2797
|
+
outputSchema: { type: "object", properties: { purged: { type: "boolean" } }, required: ["purged"] },
|
|
2798
|
+
handler: async (args, ctx) => {
|
|
2799
|
+
await callCloudflare(base, {
|
|
2800
|
+
method: "POST",
|
|
2801
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/purge_build_cache`,
|
|
2802
|
+
}, ctx);
|
|
2803
|
+
return { purged: true };
|
|
2804
|
+
},
|
|
2805
|
+
},
|
|
2806
|
+
{
|
|
2807
|
+
name: "delete_pages_project",
|
|
2808
|
+
description: "Permanently delete a Pages project, its deployments, and project configuration.",
|
|
2809
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2810
|
+
inputSchema: {
|
|
2811
|
+
type: "object",
|
|
2812
|
+
properties: {
|
|
2813
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2814
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name to delete." },
|
|
2815
|
+
},
|
|
2816
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
2817
|
+
additionalProperties: false,
|
|
2818
|
+
},
|
|
2819
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, projectName: { type: "string" } }, required: ["deleted", "projectName"] },
|
|
2820
|
+
handler: async (args, ctx) => {
|
|
2821
|
+
const projectName = requireString(args, "projectName");
|
|
2822
|
+
await callCloudflare(base, {
|
|
2823
|
+
method: "DELETE",
|
|
2824
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(projectName)}`,
|
|
2825
|
+
}, ctx);
|
|
2826
|
+
return { deleted: true, projectName };
|
|
2827
|
+
},
|
|
2828
|
+
},
|
|
1006
2829
|
{
|
|
1007
2830
|
// Additive: brings a record into being and destroys nothing, so
|
|
1008
2831
|
// `destructiveHint` stays unset. `readOnlyHint: false` already routes it
|
|
@@ -1324,12 +3147,14 @@ Account purpose: ${purpose}
|
|
|
1324
3147
|
|
|
1325
3148
|
- ${zoneLine}
|
|
1326
3149
|
- ${accountLine}
|
|
1327
|
-
-
|
|
1328
|
-
-
|
|
3150
|
+
- Prefer a named tool: its schema is complete, projected, and enough to call it without provider documentation. For an operation without a named tool, use \`cloudflare_api_get\` for GET, \`cloudflare_api_mutate\` for JSON POST/PUT/PATCH/DELETE, or \`cloudflare_api_upload\` for raw and multipart content. Raw tools take a path below \`/client/v4\`; their argument schemas are complete, but endpoint-specific query, header, and body fields come from Cloudflare's API reference. Use \`headers\` for endpoint-specific controls such as \`cf-r2-jurisdiction\`, ETags, and object metadata; authentication, host, content type, and request framing remain connector-owned.
|
|
3151
|
+
- The raw tools cover the wider control plane without weakening routing: GET is explicitly read-only; every mutation and upload is destructive and must cross the host's approval boundary. The API token remains the hard provider-side permission boundary. Absolute URLs, traversal, and query strings embedded in \`path\` are refused locally.
|
|
3152
|
+
- Useful raw paths include \`/accounts/{accountId}/images/v1\` (Images), \`/accounts/{accountId}/stream\` (Stream), \`/zones/{zoneId}/email/routing/rules\` (Email Routing), \`/accounts/{accountId}/d1/database\` (D1), and \`/accounts/{accountId}/queues\` (Queues). On GET, use \`responseType: "text"\` or \`"base64"\` for non-JSON content. Direct-upload endpoints can issue upload URLs; \`cloudflare_api_upload\` can also send explicit text, base64 bytes, or multipart fields/files.
|
|
3153
|
+
- Lists paginate with \`page\` and \`perPage\` and return a \`page\` object; request the next page only when \`page.hasMore\` is true. \`list_zone_rulesets\`, \`list_r2_buckets\`, \`list_r2_objects\`, and \`list_kv_keys\` instead return \`nextCursor\`; \`list_worker_scripts\` is unpaginated.
|
|
1329
3154
|
- Results are projected to the fields that identify and describe a resource. Pass \`raw: true\` on a read when you genuinely need a field the projection drops.
|
|
1330
3155
|
- The API token is operator-managed and scoped by permission, not by role. An \`auth_required\` failure means the token is missing, invalid, or lacks that call's permission — it is never fixed by retrying. Call \`verify_api_token\` to tell a dead token from a missing permission, then report which permission is needed rather than trying other tools.
|
|
1331
3156
|
- A \`rate_limited\` failure carries the wait window. Cloudflare's limit is 1,200 requests per five minutes per user, counted across the dashboard and every token, so do not fan out speculatively; filter server-side with \`name\`, \`type\`, and \`content\` instead of listing everything and filtering locally.
|
|
1332
|
-
-
|
|
3157
|
+
- Named creates that only add a resource are write-routed without claiming destruction. Updates, overwrites, deletes, rollbacks, cache purges, \`cloudflare_api_mutate\`, and \`cloudflare_api_upload\` are destructive. Read current state before changing it, and prefer a targeted \`purge_cache\` over \`everything\`.
|
|
1333
3158
|
${accountInstructions
|
|
1334
3159
|
? `\n## Account instructions\n\n${accountInstructions}\n`
|
|
1335
3160
|
: ""}`;
|
|
@@ -1351,7 +3176,7 @@ export function cloudflare(id, options) {
|
|
|
1351
3176
|
};
|
|
1352
3177
|
return api(id, {
|
|
1353
3178
|
title: options.title ?? "Cloudflare",
|
|
1354
|
-
description: `Cloudflare zones, DNS,
|
|
3179
|
+
description: `Cloudflare control-plane access for zones, DNS, Workers, KV, R2, Pages, media, email, and other v4 APIs — ${purpose}`,
|
|
1355
3180
|
credential: options.credential ?? DEFAULT_CREDENTIAL,
|
|
1356
3181
|
callAdmission: admissionPolicy(maxConcurrency),
|
|
1357
3182
|
usageGuide: usageGuide(purpose, scope, options.instructions),
|