@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
|
@@ -67,7 +67,8 @@ export const CLOUDFLARE_DNS_RECORD_TYPES = [
|
|
|
67
67
|
* free-form `data` passthrough — exactly the untyped `{}` this connection
|
|
68
68
|
* exists to avoid — or thirteen more hand-written schemas for record types
|
|
69
69
|
* that are rare in the day-to-day work this surface is for. They remain fully
|
|
70
|
-
* readable and filterable; only
|
|
70
|
+
* readable and filterable; only the named create/update tools omit them. The
|
|
71
|
+
* guarded raw mutation tool remains available for their documented bodies.
|
|
71
72
|
*/
|
|
72
73
|
export const CLOUDFLARE_CONTENT_DNS_RECORD_TYPES = [
|
|
73
74
|
"A",
|
|
@@ -134,7 +135,7 @@ function admissionPolicy(maxConcurrency: number): ConnectorCallAdmissionPolicy {
|
|
|
134
135
|
const DEFAULT_CREDENTIAL: ConnectorCredentialConfig = {
|
|
135
136
|
label: "Cloudflare API token",
|
|
136
137
|
description:
|
|
137
|
-
"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\"
|
|
138
|
+
"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.",
|
|
138
139
|
placeholder: "Paste API token",
|
|
139
140
|
};
|
|
140
141
|
|
|
@@ -154,6 +155,9 @@ interface CloudflareResultInfo {
|
|
|
154
155
|
total_pages?: number;
|
|
155
156
|
/** Cursor-paginated endpoints (R2 buckets, KV keys) report this instead. */
|
|
156
157
|
cursor?: string;
|
|
158
|
+
is_truncated?: boolean;
|
|
159
|
+
delimited?: string[];
|
|
160
|
+
cursors?: { after?: string; before?: string };
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
interface CloudflareEnvelope {
|
|
@@ -299,10 +303,12 @@ function failureFor(
|
|
|
299
303
|
// --- The request path --------------------------------------------------------
|
|
300
304
|
|
|
301
305
|
interface RequestSpec {
|
|
302
|
-
method: "GET" | "POST" | "PATCH" | "DELETE";
|
|
306
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
303
307
|
path: string;
|
|
304
308
|
query?: Record<string, string | number | boolean | undefined>;
|
|
309
|
+
headers?: Record<string, string | undefined>;
|
|
305
310
|
body?: unknown;
|
|
311
|
+
rawBody?: BodyInit;
|
|
306
312
|
}
|
|
307
313
|
|
|
308
314
|
interface CloudflareResponse {
|
|
@@ -330,12 +336,15 @@ function buildUrl(base: string, spec: RequestSpec): string {
|
|
|
330
336
|
return url.toString();
|
|
331
337
|
}
|
|
332
338
|
|
|
333
|
-
async function
|
|
339
|
+
async function fetchCloudflare(
|
|
334
340
|
base: string,
|
|
335
341
|
spec: RequestSpec,
|
|
336
342
|
ctx: ConnectorContext,
|
|
337
|
-
): Promise<
|
|
343
|
+
): Promise<Response> {
|
|
338
344
|
const token = await readToken(ctx);
|
|
345
|
+
if (spec.body !== undefined && spec.rawBody !== undefined) {
|
|
346
|
+
throw new Error("A Cloudflare request cannot have both JSON and raw bodies.");
|
|
347
|
+
}
|
|
339
348
|
let response: Response;
|
|
340
349
|
try {
|
|
341
350
|
response = await fetch(buildUrl(base, spec), {
|
|
@@ -346,10 +355,17 @@ async function callCloudflare(
|
|
|
346
355
|
...(spec.body !== undefined
|
|
347
356
|
? { "Content-Type": "application/json" }
|
|
348
357
|
: {}),
|
|
358
|
+
...Object.fromEntries(
|
|
359
|
+
Object.entries(spec.headers ?? {}).filter(
|
|
360
|
+
(entry): entry is [string, string] => entry[1] !== undefined,
|
|
361
|
+
),
|
|
362
|
+
),
|
|
349
363
|
},
|
|
350
364
|
...(spec.body !== undefined
|
|
351
365
|
? { body: JSON.stringify(spec.body) }
|
|
352
|
-
:
|
|
366
|
+
: spec.rawBody !== undefined
|
|
367
|
+
? { body: spec.rawBody }
|
|
368
|
+
: {}),
|
|
353
369
|
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
354
370
|
});
|
|
355
371
|
} catch (cause) {
|
|
@@ -361,6 +377,15 @@ async function callCloudflare(
|
|
|
361
377
|
{ cause },
|
|
362
378
|
);
|
|
363
379
|
}
|
|
380
|
+
return response;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function callCloudflare(
|
|
384
|
+
base: string,
|
|
385
|
+
spec: RequestSpec,
|
|
386
|
+
ctx: ConnectorContext,
|
|
387
|
+
): Promise<CloudflareResponse> {
|
|
388
|
+
const response = await fetchCloudflare(base, spec, ctx);
|
|
364
389
|
|
|
365
390
|
let envelope: CloudflareEnvelope;
|
|
366
391
|
try {
|
|
@@ -386,6 +411,46 @@ async function callCloudflare(
|
|
|
386
411
|
};
|
|
387
412
|
}
|
|
388
413
|
|
|
414
|
+
function base64FromBytes(bytes: Uint8Array): string {
|
|
415
|
+
let binary = "";
|
|
416
|
+
for (let offset = 0; offset < bytes.length; offset += 0x8000) {
|
|
417
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
|
|
418
|
+
}
|
|
419
|
+
return btoa(binary);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function callCloudflareContent(
|
|
423
|
+
base: string,
|
|
424
|
+
spec: RequestSpec,
|
|
425
|
+
ctx: ConnectorContext,
|
|
426
|
+
responseType: "text" | "base64",
|
|
427
|
+
): Promise<JsonRecord> {
|
|
428
|
+
const response = await fetchCloudflare(base, spec, ctx);
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
let errors: CloudflareEnvelopeError[] = [];
|
|
431
|
+
try {
|
|
432
|
+
const envelope = (await response.clone().json()) as CloudflareEnvelope;
|
|
433
|
+
if (Array.isArray(envelope.errors)) errors = envelope.errors;
|
|
434
|
+
} catch {
|
|
435
|
+
// A raw or gateway error body has no structured detail to preserve.
|
|
436
|
+
}
|
|
437
|
+
throw failureFor(response.status, response.headers, errors);
|
|
438
|
+
}
|
|
439
|
+
const common = {
|
|
440
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
441
|
+
...(response.headers.get("etag")
|
|
442
|
+
? { etag: response.headers.get("etag")! }
|
|
443
|
+
: {}),
|
|
444
|
+
};
|
|
445
|
+
if (responseType === "text") {
|
|
446
|
+
return { ...common, text: await response.text() };
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
...common,
|
|
450
|
+
base64: base64FromBytes(new Uint8Array(await response.arrayBuffer())),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
389
454
|
// --- Projections -------------------------------------------------------------
|
|
390
455
|
|
|
391
456
|
interface PageInfo {
|
|
@@ -516,12 +581,118 @@ function projectR2Bucket(value: unknown): JsonRecord {
|
|
|
516
581
|
...(bucket["storage_class"] !== undefined
|
|
517
582
|
? { storageClass: bucket["storage_class"] }
|
|
518
583
|
: {}),
|
|
584
|
+
...(bucket["jurisdiction"] !== undefined
|
|
585
|
+
? { jurisdiction: bucket["jurisdiction"] }
|
|
586
|
+
: {}),
|
|
519
587
|
...(bucket["creation_date"] !== undefined
|
|
520
588
|
? { creationDate: bucket["creation_date"] }
|
|
521
589
|
: {}),
|
|
522
590
|
};
|
|
523
591
|
}
|
|
524
592
|
|
|
593
|
+
function projectR2Object(value: unknown): JsonRecord {
|
|
594
|
+
const object = asRecord(value);
|
|
595
|
+
return {
|
|
596
|
+
key: object["key"],
|
|
597
|
+
size: object["size"],
|
|
598
|
+
etag: object["etag"],
|
|
599
|
+
lastModified: object["last_modified"],
|
|
600
|
+
...(object["storage_class"] !== undefined
|
|
601
|
+
? { storageClass: object["storage_class"] }
|
|
602
|
+
: {}),
|
|
603
|
+
...(object["http_metadata"] !== undefined
|
|
604
|
+
? { httpMetadata: object["http_metadata"] }
|
|
605
|
+
: {}),
|
|
606
|
+
...(object["custom_metadata"] !== undefined
|
|
607
|
+
? { customMetadata: object["custom_metadata"] }
|
|
608
|
+
: {}),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function projectKvKey(value: unknown): JsonRecord {
|
|
613
|
+
const key = asRecord(value);
|
|
614
|
+
return {
|
|
615
|
+
name: key["name"],
|
|
616
|
+
...(key["expiration"] !== undefined ? { expiration: key["expiration"] } : {}),
|
|
617
|
+
...(key["metadata"] !== undefined ? { metadata: key["metadata"] } : {}),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function projectWorkerDeployment(value: unknown): JsonRecord {
|
|
622
|
+
const deployment = asRecord(value);
|
|
623
|
+
return {
|
|
624
|
+
id: deployment["id"],
|
|
625
|
+
...(deployment["created_on"] !== undefined
|
|
626
|
+
? { createdOn: deployment["created_on"] }
|
|
627
|
+
: {}),
|
|
628
|
+
...(deployment["source"] !== undefined ? { source: deployment["source"] } : {}),
|
|
629
|
+
...(deployment["strategy"] !== undefined
|
|
630
|
+
? { strategy: deployment["strategy"] }
|
|
631
|
+
: {}),
|
|
632
|
+
...(deployment["versions"] !== undefined
|
|
633
|
+
? { versions: deployment["versions"] }
|
|
634
|
+
: {}),
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function projectPagesDeployment(value: unknown): JsonRecord {
|
|
639
|
+
const deployment = asRecord(value);
|
|
640
|
+
return {
|
|
641
|
+
id: deployment["id"],
|
|
642
|
+
...(deployment["project_name"] !== undefined
|
|
643
|
+
? { projectName: deployment["project_name"] }
|
|
644
|
+
: {}),
|
|
645
|
+
...(deployment["environment"] !== undefined
|
|
646
|
+
? { environment: deployment["environment"] }
|
|
647
|
+
: {}),
|
|
648
|
+
...(deployment["url"] !== undefined ? { url: deployment["url"] } : {}),
|
|
649
|
+
...(deployment["aliases"] !== undefined ? { aliases: deployment["aliases"] } : {}),
|
|
650
|
+
...(deployment["stage"] !== undefined ? { stage: deployment["stage"] } : {}),
|
|
651
|
+
...(deployment["latest_stage"] !== undefined
|
|
652
|
+
? { latestStage: deployment["latest_stage"] }
|
|
653
|
+
: {}),
|
|
654
|
+
...(deployment["created_on"] !== undefined
|
|
655
|
+
? { createdOn: deployment["created_on"] }
|
|
656
|
+
: {}),
|
|
657
|
+
...(deployment["modified_on"] !== undefined
|
|
658
|
+
? { modifiedOn: deployment["modified_on"] }
|
|
659
|
+
: {}),
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function projectPagesDomain(value: unknown): JsonRecord {
|
|
664
|
+
const domain = asRecord(value);
|
|
665
|
+
return {
|
|
666
|
+
id: domain["id"],
|
|
667
|
+
name: domain["name"],
|
|
668
|
+
...(domain["status"] !== undefined ? { status: domain["status"] } : {}),
|
|
669
|
+
...(domain["verification_data"] !== undefined
|
|
670
|
+
? { verificationData: domain["verification_data"] }
|
|
671
|
+
: {}),
|
|
672
|
+
...(domain["created_on"] !== undefined
|
|
673
|
+
? { createdOn: domain["created_on"] }
|
|
674
|
+
: {}),
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function projectRuleset(value: unknown): JsonRecord {
|
|
679
|
+
const ruleset = asRecord(value);
|
|
680
|
+
return {
|
|
681
|
+
id: ruleset["id"],
|
|
682
|
+
name: ruleset["name"],
|
|
683
|
+
kind: ruleset["kind"],
|
|
684
|
+
phase: ruleset["phase"],
|
|
685
|
+
...(ruleset["description"] !== undefined
|
|
686
|
+
? { description: ruleset["description"] }
|
|
687
|
+
: {}),
|
|
688
|
+
...(ruleset["version"] !== undefined ? { version: ruleset["version"] } : {}),
|
|
689
|
+
...(ruleset["last_updated"] !== undefined
|
|
690
|
+
? { lastUpdated: ruleset["last_updated"] }
|
|
691
|
+
: {}),
|
|
692
|
+
...(ruleset["rules"] !== undefined ? { rules: ruleset["rules"] } : {}),
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
525
696
|
function projectPagesProject(value: unknown): JsonRecord {
|
|
526
697
|
const project = asRecord(value);
|
|
527
698
|
const latest = asRecord(project["latest_deployment"]);
|
|
@@ -754,6 +925,287 @@ function optionalNumber(args: JsonRecord, key: string): number | undefined {
|
|
|
754
925
|
return typeof value === "number" ? value : undefined;
|
|
755
926
|
}
|
|
756
927
|
|
|
928
|
+
function optionalBoolean(args: JsonRecord, key: string): boolean | undefined {
|
|
929
|
+
const value = args[key];
|
|
930
|
+
return typeof value === "boolean" ? value : undefined;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function requireString(args: JsonRecord, key: string): string {
|
|
934
|
+
const value = optionalString(args, key);
|
|
935
|
+
if (value) return value;
|
|
936
|
+
throw new ConnectorCallError("invalid_args", `${key} must not be blank.`);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function encodePathSegment(value: string): string {
|
|
940
|
+
return encodeURIComponent(value);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function encodeObjectKey(value: string): string {
|
|
944
|
+
return value
|
|
945
|
+
.split("/")
|
|
946
|
+
.map((segment) => {
|
|
947
|
+
if (segment === "." || segment === "..") {
|
|
948
|
+
throw new ConnectorCallError(
|
|
949
|
+
"invalid_args",
|
|
950
|
+
"objectKey cannot contain '.' or '..' path segments because URL normalization would change the target resource.",
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
return encodeURIComponent(segment);
|
|
954
|
+
})
|
|
955
|
+
.join("/");
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function cloudflareApiPath(value: unknown): string {
|
|
959
|
+
if (typeof value !== "string") {
|
|
960
|
+
throw new ConnectorCallError("invalid_args", "path must be a string.");
|
|
961
|
+
}
|
|
962
|
+
const path = value.trim();
|
|
963
|
+
if (!path.startsWith("/") || path.startsWith("//") || path.includes("\\")) {
|
|
964
|
+
throw new ConnectorCallError(
|
|
965
|
+
"invalid_args",
|
|
966
|
+
"path must be a relative Cloudflare v4 path beginning with one slash and containing no backslashes.",
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
if (path.includes("?") || path.includes("#")) {
|
|
970
|
+
throw new ConnectorCallError(
|
|
971
|
+
"invalid_args",
|
|
972
|
+
"Put query parameters in the query array; path cannot contain '?' or '#'.",
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
for (const segment of path.split("/")) {
|
|
976
|
+
let decoded = segment;
|
|
977
|
+
let stable = false;
|
|
978
|
+
for (let pass = 0; pass < 20; pass += 1) {
|
|
979
|
+
let next: string;
|
|
980
|
+
try {
|
|
981
|
+
next = decodeURIComponent(decoded);
|
|
982
|
+
} catch {
|
|
983
|
+
throw new ConnectorCallError(
|
|
984
|
+
"invalid_args",
|
|
985
|
+
"path contains invalid percent encoding.",
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
if (next === decoded) {
|
|
989
|
+
stable = true;
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
decoded = next;
|
|
993
|
+
}
|
|
994
|
+
if (!stable) {
|
|
995
|
+
throw new ConnectorCallError(
|
|
996
|
+
"invalid_args",
|
|
997
|
+
"path contains too many layers of percent encoding.",
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
if (decoded === "." || decoded === ".." || decoded.includes("/") || decoded.includes("\\")) {
|
|
1001
|
+
throw new ConnectorCallError(
|
|
1002
|
+
"invalid_args",
|
|
1003
|
+
"path cannot contain encoded or literal traversal, slash, or backslash segments.",
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
const normalized = new URL(`https://connecta.invalid/client/v4${path}`);
|
|
1008
|
+
if (!normalized.pathname.startsWith("/client/v4/")) {
|
|
1009
|
+
throw new ConnectorCallError(
|
|
1010
|
+
"invalid_args",
|
|
1011
|
+
"path normalization escaped the Cloudflare v4 API base.",
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
return path;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function queryFromArgs(
|
|
1018
|
+
value: unknown,
|
|
1019
|
+
): Record<string, string | number | boolean | undefined> | undefined {
|
|
1020
|
+
if (!Array.isArray(value) || value.length === 0) return undefined;
|
|
1021
|
+
const query: Record<string, string> = {};
|
|
1022
|
+
for (const item of value) {
|
|
1023
|
+
const entry = asRecord(item);
|
|
1024
|
+
query[String(entry["name"])] = String(entry["value"]);
|
|
1025
|
+
}
|
|
1026
|
+
return query;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function headersFromArgs(value: unknown): Record<string, string> | undefined {
|
|
1030
|
+
if (!Array.isArray(value) || value.length === 0) return undefined;
|
|
1031
|
+
const headers: Record<string, string> = {};
|
|
1032
|
+
const forbidden = new Set([
|
|
1033
|
+
"authorization",
|
|
1034
|
+
"cookie",
|
|
1035
|
+
"host",
|
|
1036
|
+
"content-length",
|
|
1037
|
+
"content-type",
|
|
1038
|
+
"transfer-encoding",
|
|
1039
|
+
]);
|
|
1040
|
+
for (const item of value) {
|
|
1041
|
+
const entry = asRecord(item);
|
|
1042
|
+
const name = String(entry["name"]).trim();
|
|
1043
|
+
if (forbidden.has(name.toLowerCase())) {
|
|
1044
|
+
throw new ConnectorCallError(
|
|
1045
|
+
"invalid_args",
|
|
1046
|
+
`The raw Cloudflare tools do not allow the ${name} header. Authentication and request framing are connector-owned; use contentType for a raw upload body.`,
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
headers[name] = String(entry["value"]);
|
|
1050
|
+
}
|
|
1051
|
+
return headers;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function r2Headers(args: JsonRecord): Record<string, string | undefined> {
|
|
1055
|
+
return { "cf-r2-jurisdiction": optionalString(args, "jurisdiction") };
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function bytesFromBase64(value: string): Uint8Array<ArrayBuffer> {
|
|
1059
|
+
try {
|
|
1060
|
+
const binary = atob(value);
|
|
1061
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
1062
|
+
} catch (cause) {
|
|
1063
|
+
throw new ConnectorCallError(
|
|
1064
|
+
"invalid_args",
|
|
1065
|
+
"base64Body and multipart file base64 values must be valid base64.",
|
|
1066
|
+
{ cause },
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function uploadBody(args: JsonRecord): {
|
|
1072
|
+
rawBody: BodyInit;
|
|
1073
|
+
headers?: Record<string, string | undefined>;
|
|
1074
|
+
} {
|
|
1075
|
+
const fields = asArray(args["fields"]);
|
|
1076
|
+
const files = asArray(args["files"]);
|
|
1077
|
+
const hasMultipart = fields.length > 0 || files.length > 0;
|
|
1078
|
+
const textBody = typeof args["textBody"] === "string" ? args["textBody"] : undefined;
|
|
1079
|
+
const base64Body =
|
|
1080
|
+
typeof args["base64Body"] === "string" ? args["base64Body"] : undefined;
|
|
1081
|
+
const rawCount = Number(textBody !== undefined) + Number(base64Body !== undefined);
|
|
1082
|
+
if ((hasMultipart && rawCount > 0) || (!hasMultipart && rawCount !== 1)) {
|
|
1083
|
+
throw new ConnectorCallError(
|
|
1084
|
+
"invalid_args",
|
|
1085
|
+
"cloudflare_api_upload needs exactly one body shape: textBody, base64Body, or multipart fields/files.",
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
if (hasMultipart) {
|
|
1089
|
+
const form = new FormData();
|
|
1090
|
+
for (const value of fields) {
|
|
1091
|
+
const field = asRecord(value);
|
|
1092
|
+
const name = String(field["name"]);
|
|
1093
|
+
const contentType = optionalString(field, "contentType");
|
|
1094
|
+
if (contentType) {
|
|
1095
|
+
form.append(
|
|
1096
|
+
name,
|
|
1097
|
+
new Blob([String(field["value"])], { type: contentType }),
|
|
1098
|
+
optionalString(field, "fileName") ?? name,
|
|
1099
|
+
);
|
|
1100
|
+
} else {
|
|
1101
|
+
form.append(name, String(field["value"]));
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
for (const value of files) {
|
|
1105
|
+
const file = asRecord(value);
|
|
1106
|
+
const text = typeof file["text"] === "string" ? file["text"] : undefined;
|
|
1107
|
+
const base64 =
|
|
1108
|
+
typeof file["base64"] === "string" ? file["base64"] : undefined;
|
|
1109
|
+
if (Number(text !== undefined) + Number(base64 !== undefined) !== 1) {
|
|
1110
|
+
throw new ConnectorCallError(
|
|
1111
|
+
"invalid_args",
|
|
1112
|
+
"Each multipart file needs exactly one of text or base64.",
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
const blob = new Blob(
|
|
1116
|
+
[text ?? bytesFromBase64(base64!)],
|
|
1117
|
+
{ type: String(file["contentType"] ?? "application/octet-stream") },
|
|
1118
|
+
);
|
|
1119
|
+
form.append(String(file["name"]), blob, String(file["fileName"]));
|
|
1120
|
+
}
|
|
1121
|
+
return { rawBody: form };
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
rawBody: textBody ?? bytesFromBase64(base64Body!),
|
|
1125
|
+
headers: {
|
|
1126
|
+
"Content-Type":
|
|
1127
|
+
optionalString(args, "contentType") ??
|
|
1128
|
+
(textBody !== undefined ? "text/plain; charset=utf-8" : "application/octet-stream"),
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const OPEN_OBJECT_OUTPUT_SCHEMA: JsonSchema = {
|
|
1134
|
+
type: "object",
|
|
1135
|
+
description: "Cloudflare's result object. Its fields depend on the endpoint.",
|
|
1136
|
+
additionalProperties: true,
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
const QUERY_INPUT_PROPERTY: JsonSchema = {
|
|
1140
|
+
type: "array",
|
|
1141
|
+
description:
|
|
1142
|
+
"Optional query parameters as name/value pairs. Each parameter name may appear once.",
|
|
1143
|
+
items: {
|
|
1144
|
+
type: "object",
|
|
1145
|
+
properties: {
|
|
1146
|
+
name: { type: "string", minLength: 1 },
|
|
1147
|
+
value: { type: ["string", "number", "boolean"] },
|
|
1148
|
+
},
|
|
1149
|
+
required: ["name", "value"],
|
|
1150
|
+
additionalProperties: false,
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
const HEADERS_INPUT_PROPERTY: JsonSchema = {
|
|
1155
|
+
type: "array",
|
|
1156
|
+
description:
|
|
1157
|
+
"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.",
|
|
1158
|
+
items: {
|
|
1159
|
+
type: "object",
|
|
1160
|
+
properties: {
|
|
1161
|
+
name: { type: "string", minLength: 1 },
|
|
1162
|
+
value: { type: "string" },
|
|
1163
|
+
},
|
|
1164
|
+
required: ["name", "value"],
|
|
1165
|
+
additionalProperties: false,
|
|
1166
|
+
},
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
const R2_JURISDICTION_PROPERTY: JsonSchema = {
|
|
1170
|
+
type: "string",
|
|
1171
|
+
enum: ["default", "eu", "fedramp"],
|
|
1172
|
+
description:
|
|
1173
|
+
"Bucket jurisdiction. Omit for ordinary buckets; set eu or fedramp for jurisdictional buckets.",
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
const R2_BUCKET_NAME_PROPERTY: JsonSchema = {
|
|
1177
|
+
type: "string",
|
|
1178
|
+
minLength: 3,
|
|
1179
|
+
maxLength: 64,
|
|
1180
|
+
description: "R2 bucket name.",
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
const R2_BUCKET_SCHEMA: JsonSchema = {
|
|
1184
|
+
type: "object",
|
|
1185
|
+
properties: {
|
|
1186
|
+
name: { type: "string" },
|
|
1187
|
+
location: { type: "string" },
|
|
1188
|
+
storageClass: { type: "string" },
|
|
1189
|
+
jurisdiction: { type: "string" },
|
|
1190
|
+
creationDate: { type: "string" },
|
|
1191
|
+
},
|
|
1192
|
+
required: ["name"],
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
const R2_OBJECT_SCHEMA: JsonSchema = {
|
|
1196
|
+
type: "object",
|
|
1197
|
+
properties: {
|
|
1198
|
+
key: { type: "string" },
|
|
1199
|
+
size: { type: "number" },
|
|
1200
|
+
etag: { type: "string" },
|
|
1201
|
+
lastModified: { type: "string" },
|
|
1202
|
+
storageClass: { type: "string" },
|
|
1203
|
+
httpMetadata: { type: "object" },
|
|
1204
|
+
customMetadata: { type: "object" },
|
|
1205
|
+
},
|
|
1206
|
+
required: ["key"],
|
|
1207
|
+
};
|
|
1208
|
+
|
|
757
1209
|
function buildTools(scope: Scoping): ApiTool[] {
|
|
758
1210
|
const { base } = scope;
|
|
759
1211
|
const zoneArg = (args: JsonRecord): string =>
|
|
@@ -808,394 +1260,1715 @@ function buildTools(scope: Scoping): ApiTool[] {
|
|
|
808
1260
|
},
|
|
809
1261
|
},
|
|
810
1262
|
{
|
|
811
|
-
name: "
|
|
1263
|
+
name: "cloudflare_api_get",
|
|
812
1264
|
description:
|
|
813
|
-
"
|
|
1265
|
+
"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.",
|
|
814
1266
|
annotations: readOnly,
|
|
815
1267
|
inputSchema: {
|
|
816
1268
|
type: "object",
|
|
817
1269
|
properties: {
|
|
818
|
-
|
|
1270
|
+
path: {
|
|
819
1271
|
type: "string",
|
|
820
|
-
|
|
1272
|
+
minLength: 1,
|
|
1273
|
+
description:
|
|
1274
|
+
"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.",
|
|
1275
|
+
},
|
|
1276
|
+
query: QUERY_INPUT_PROPERTY,
|
|
1277
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
1278
|
+
responseType: {
|
|
1279
|
+
type: "string",
|
|
1280
|
+
enum: ["json", "text", "base64"],
|
|
1281
|
+
description:
|
|
1282
|
+
"How to read a successful response. Defaults to json; use text or base64 for object, log, script, and media downloads.",
|
|
821
1283
|
},
|
|
822
|
-
...pagingInputProperties(5, 50, { defaultPerPage: 20 }),
|
|
823
|
-
raw: RAW_INPUT_PROPERTY,
|
|
824
1284
|
},
|
|
825
|
-
required: [],
|
|
1285
|
+
required: ["path"],
|
|
826
1286
|
additionalProperties: false,
|
|
827
1287
|
},
|
|
828
|
-
outputSchema:
|
|
1288
|
+
outputSchema: {
|
|
1289
|
+
type: "object",
|
|
1290
|
+
properties: {
|
|
1291
|
+
result: {
|
|
1292
|
+
description: "Cloudflare's unprojected result for the endpoint.",
|
|
1293
|
+
},
|
|
1294
|
+
resultInfo: {
|
|
1295
|
+
type: "object",
|
|
1296
|
+
description:
|
|
1297
|
+
"Cloudflare's unprojected pagination metadata, when the endpoint returns it.",
|
|
1298
|
+
},
|
|
1299
|
+
text: { type: "string", description: "Text response body when responseType is text." },
|
|
1300
|
+
base64: { type: "string", description: "Base64 response bytes when responseType is base64." },
|
|
1301
|
+
contentType: { type: "string", description: "Response Content-Type for text/base64 reads." },
|
|
1302
|
+
etag: { type: "string", description: "Response ETag when Cloudflare supplies one." },
|
|
1303
|
+
},
|
|
1304
|
+
required: [],
|
|
1305
|
+
},
|
|
829
1306
|
handler: async (args: JsonRecord, ctx) => {
|
|
1307
|
+
const query = queryFromArgs(args["query"]);
|
|
1308
|
+
const headers = headersFromArgs(args["headers"]);
|
|
1309
|
+
const responseType = optionalString(args, "responseType") ?? "json";
|
|
1310
|
+
const spec: RequestSpec = {
|
|
1311
|
+
method: "GET",
|
|
1312
|
+
path: cloudflareApiPath(args["path"]),
|
|
1313
|
+
...(query !== undefined ? { query } : {}),
|
|
1314
|
+
...(headers !== undefined ? { headers } : {}),
|
|
1315
|
+
};
|
|
1316
|
+
if (responseType === "text" || responseType === "base64") {
|
|
1317
|
+
return await callCloudflareContent(base, spec, ctx, responseType);
|
|
1318
|
+
}
|
|
830
1319
|
const { result, resultInfo } = await callCloudflare(
|
|
831
1320
|
base,
|
|
832
|
-
|
|
833
|
-
method: "GET",
|
|
834
|
-
path: "/accounts",
|
|
835
|
-
query: {
|
|
836
|
-
name: optionalString(args, "name"),
|
|
837
|
-
page: optionalNumber(args, "page"),
|
|
838
|
-
per_page: optionalNumber(args, "perPage"),
|
|
839
|
-
},
|
|
840
|
-
},
|
|
1321
|
+
spec,
|
|
841
1322
|
ctx,
|
|
842
1323
|
);
|
|
843
|
-
if (args["raw"] === true) return { accounts: result, page: pageInfo(resultInfo) };
|
|
844
1324
|
return {
|
|
845
|
-
|
|
846
|
-
|
|
1325
|
+
result,
|
|
1326
|
+
...(resultInfo !== undefined ? { resultInfo } : {}),
|
|
847
1327
|
};
|
|
848
1328
|
},
|
|
849
1329
|
},
|
|
850
1330
|
{
|
|
851
|
-
name: "
|
|
1331
|
+
name: "cloudflare_api_mutate",
|
|
852
1332
|
description:
|
|
853
|
-
"
|
|
854
|
-
annotations:
|
|
1333
|
+
"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.",
|
|
1334
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
855
1335
|
inputSchema: {
|
|
856
1336
|
type: "object",
|
|
857
1337
|
properties: {
|
|
858
|
-
|
|
1338
|
+
method: {
|
|
859
1339
|
type: "string",
|
|
860
|
-
|
|
1340
|
+
enum: ["POST", "PUT", "PATCH", "DELETE"],
|
|
1341
|
+
description: "HTTP mutation method required by the Cloudflare endpoint.",
|
|
861
1342
|
},
|
|
862
|
-
|
|
1343
|
+
path: {
|
|
863
1344
|
type: "string",
|
|
1345
|
+
minLength: 1,
|
|
864
1346
|
description:
|
|
865
|
-
"
|
|
1347
|
+
"Relative path below /client/v4, beginning with '/'. Do not include a query string.",
|
|
866
1348
|
},
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1349
|
+
query: QUERY_INPUT_PROPERTY,
|
|
1350
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
1351
|
+
body: {
|
|
1352
|
+
type: ["object", "array", "string", "number", "boolean", "null"],
|
|
1353
|
+
description:
|
|
1354
|
+
"JSON request body exactly as documented by Cloudflare. Omit for endpoints with no body.",
|
|
871
1355
|
},
|
|
872
|
-
...pagingInputProperties(5, 50, { defaultPerPage: 20 }),
|
|
873
|
-
raw: RAW_INPUT_PROPERTY,
|
|
874
1356
|
},
|
|
875
|
-
required: [],
|
|
1357
|
+
required: ["method", "path"],
|
|
876
1358
|
additionalProperties: false,
|
|
877
1359
|
},
|
|
878
|
-
outputSchema:
|
|
1360
|
+
outputSchema: {
|
|
1361
|
+
type: "object",
|
|
1362
|
+
properties: {
|
|
1363
|
+
result: {
|
|
1364
|
+
description: "Cloudflare's unprojected result for the endpoint.",
|
|
1365
|
+
},
|
|
1366
|
+
resultInfo: {
|
|
1367
|
+
type: "object",
|
|
1368
|
+
description:
|
|
1369
|
+
"Cloudflare's unprojected pagination metadata, when the endpoint returns it.",
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1372
|
+
required: ["result"],
|
|
1373
|
+
},
|
|
879
1374
|
handler: async (args: JsonRecord, ctx) => {
|
|
1375
|
+
const method = String(args["method"]) as RequestSpec["method"];
|
|
1376
|
+
const query = queryFromArgs(args["query"]);
|
|
1377
|
+
const headers = headersFromArgs(args["headers"]);
|
|
880
1378
|
const { result, resultInfo } = await callCloudflare(
|
|
881
1379
|
base,
|
|
882
1380
|
{
|
|
883
|
-
method
|
|
884
|
-
path: "
|
|
885
|
-
query: {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
// discovery tool: a deployment default that silently narrowed
|
|
889
|
-
// what an agent can see would contradict the property's own
|
|
890
|
-
// description, and there would be no argument that escapes it.
|
|
891
|
-
"account.id": optionalString(args, "accountId"),
|
|
892
|
-
status: optionalString(args, "status"),
|
|
893
|
-
page: optionalNumber(args, "page"),
|
|
894
|
-
per_page: optionalNumber(args, "perPage"),
|
|
895
|
-
},
|
|
1381
|
+
method,
|
|
1382
|
+
path: cloudflareApiPath(args["path"]),
|
|
1383
|
+
...(query !== undefined ? { query } : {}),
|
|
1384
|
+
...(headers !== undefined ? { headers } : {}),
|
|
1385
|
+
...(args["body"] !== undefined ? { body: args["body"] } : {}),
|
|
896
1386
|
},
|
|
897
1387
|
ctx,
|
|
898
1388
|
);
|
|
899
|
-
if (args["raw"] === true) return { zones: result, page: pageInfo(resultInfo) };
|
|
900
1389
|
return {
|
|
901
|
-
|
|
902
|
-
|
|
1390
|
+
result,
|
|
1391
|
+
...(resultInfo !== undefined ? { resultInfo } : {}),
|
|
903
1392
|
};
|
|
904
1393
|
},
|
|
905
1394
|
},
|
|
906
1395
|
{
|
|
907
|
-
name: "
|
|
1396
|
+
name: "cloudflare_api_upload",
|
|
908
1397
|
description:
|
|
909
|
-
"
|
|
910
|
-
annotations:
|
|
1398
|
+
"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.",
|
|
1399
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
911
1400
|
inputSchema: {
|
|
912
1401
|
type: "object",
|
|
913
1402
|
properties: {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1403
|
+
method: {
|
|
1404
|
+
type: "string",
|
|
1405
|
+
enum: ["POST", "PUT"],
|
|
1406
|
+
description: "HTTP upload method required by the Cloudflare endpoint.",
|
|
1407
|
+
},
|
|
1408
|
+
path: {
|
|
1409
|
+
type: "string",
|
|
1410
|
+
minLength: 1,
|
|
1411
|
+
description:
|
|
1412
|
+
"Relative path below /client/v4, beginning with '/'. Do not include a query string.",
|
|
1413
|
+
},
|
|
1414
|
+
query: QUERY_INPUT_PROPERTY,
|
|
1415
|
+
headers: HEADERS_INPUT_PROPERTY,
|
|
1416
|
+
contentType: {
|
|
1417
|
+
type: "string",
|
|
1418
|
+
minLength: 1,
|
|
1419
|
+
description:
|
|
1420
|
+
"Content-Type for a raw text/base64 body. Omit for multipart because fetch supplies the boundary.",
|
|
1421
|
+
},
|
|
1422
|
+
textBody: {
|
|
1423
|
+
type: "string",
|
|
1424
|
+
description: "Raw UTF-8 request body. Mutually exclusive with base64Body and multipart fields/files.",
|
|
1425
|
+
},
|
|
1426
|
+
base64Body: {
|
|
1427
|
+
type: "string",
|
|
1428
|
+
description: "Base64-encoded request bytes. Mutually exclusive with textBody and multipart fields/files.",
|
|
1429
|
+
},
|
|
1430
|
+
fields: {
|
|
1431
|
+
type: "array",
|
|
1432
|
+
description: "String fields for a multipart/form-data request.",
|
|
1433
|
+
items: {
|
|
1434
|
+
type: "object",
|
|
1435
|
+
properties: {
|
|
1436
|
+
name: { type: "string", minLength: 1 },
|
|
1437
|
+
value: { type: "string" },
|
|
1438
|
+
contentType: { type: "string", minLength: 1 },
|
|
1439
|
+
fileName: { type: "string", minLength: 1 },
|
|
1440
|
+
},
|
|
1441
|
+
required: ["name", "value"],
|
|
1442
|
+
additionalProperties: false,
|
|
1443
|
+
},
|
|
1444
|
+
},
|
|
1445
|
+
files: {
|
|
1446
|
+
type: "array",
|
|
1447
|
+
description:
|
|
1448
|
+
"File parts for multipart/form-data. Each file needs exactly one of text or base64.",
|
|
1449
|
+
items: {
|
|
1450
|
+
type: "object",
|
|
1451
|
+
properties: {
|
|
1452
|
+
name: { type: "string", minLength: 1 },
|
|
1453
|
+
fileName: { type: "string", minLength: 1 },
|
|
1454
|
+
contentType: { type: "string", minLength: 1 },
|
|
1455
|
+
text: { type: "string" },
|
|
1456
|
+
base64: { type: "string" },
|
|
1457
|
+
},
|
|
1458
|
+
required: ["name", "fileName", "contentType"],
|
|
1459
|
+
additionalProperties: false,
|
|
1460
|
+
},
|
|
1461
|
+
},
|
|
1462
|
+
},
|
|
1463
|
+
required: ["method", "path"],
|
|
1464
|
+
additionalProperties: false,
|
|
1465
|
+
},
|
|
1466
|
+
outputSchema: {
|
|
1467
|
+
type: "object",
|
|
1468
|
+
properties: {
|
|
1469
|
+
result: {
|
|
1470
|
+
description: "Cloudflare's unprojected upload result.",
|
|
1471
|
+
},
|
|
1472
|
+
},
|
|
1473
|
+
required: ["result"],
|
|
1474
|
+
},
|
|
921
1475
|
handler: async (args: JsonRecord, ctx) => {
|
|
1476
|
+
const query = queryFromArgs(args["query"]);
|
|
1477
|
+
const headers = headersFromArgs(args["headers"]);
|
|
1478
|
+
const upload = uploadBody(args);
|
|
922
1479
|
const { result } = await callCloudflare(
|
|
923
1480
|
base,
|
|
924
|
-
{
|
|
1481
|
+
{
|
|
1482
|
+
method: String(args["method"]) as "POST" | "PUT",
|
|
1483
|
+
path: cloudflareApiPath(args["path"]),
|
|
1484
|
+
...(query !== undefined ? { query } : {}),
|
|
1485
|
+
...(headers !== undefined || upload.headers !== undefined
|
|
1486
|
+
? { headers: { ...headers, ...upload.headers } }
|
|
1487
|
+
: {}),
|
|
1488
|
+
rawBody: upload.rawBody,
|
|
1489
|
+
},
|
|
925
1490
|
ctx,
|
|
926
1491
|
);
|
|
927
|
-
return
|
|
1492
|
+
return { result };
|
|
928
1493
|
},
|
|
929
1494
|
},
|
|
930
1495
|
{
|
|
931
|
-
name: "
|
|
1496
|
+
name: "list_accounts",
|
|
932
1497
|
description:
|
|
933
|
-
"List
|
|
1498
|
+
"List Cloudflare accounts this token can see. Supplies the accountId that the Workers, KV, R2, and Pages tools need.",
|
|
934
1499
|
annotations: readOnly,
|
|
935
1500
|
inputSchema: {
|
|
936
1501
|
type: "object",
|
|
937
1502
|
properties: {
|
|
938
|
-
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
939
1503
|
name: {
|
|
940
1504
|
type: "string",
|
|
941
|
-
description:
|
|
942
|
-
"Exact record name, fully qualified, e.g. www.example.com.",
|
|
943
|
-
},
|
|
944
|
-
type: {
|
|
945
|
-
type: "string",
|
|
946
|
-
enum: [...CLOUDFLARE_DNS_RECORD_TYPES],
|
|
947
|
-
description: "Filter by record type.",
|
|
948
|
-
},
|
|
949
|
-
content: {
|
|
950
|
-
type: "string",
|
|
951
|
-
description: "Exact record content, e.g. an IP address.",
|
|
952
|
-
},
|
|
953
|
-
order: {
|
|
954
|
-
type: "string",
|
|
955
|
-
enum: ["type", "name", "content", "ttl", "proxied"],
|
|
956
|
-
description: "Sort field.",
|
|
957
|
-
},
|
|
958
|
-
direction: {
|
|
959
|
-
type: "string",
|
|
960
|
-
enum: ["asc", "desc"],
|
|
961
|
-
description: "Sort direction for `order`. Defaults to asc.",
|
|
1505
|
+
description: "Filter by exact account name.",
|
|
962
1506
|
},
|
|
963
|
-
|
|
964
|
-
// ceiling is nominal, so this connection caps it at a page size that
|
|
965
|
-
// actually returns.
|
|
966
|
-
...pagingInputProperties(1, 1000, {
|
|
967
|
-
defaultPerPage: 100,
|
|
968
|
-
bounds: "clamped",
|
|
969
|
-
}),
|
|
1507
|
+
...pagingInputProperties(5, 50, { defaultPerPage: 20 }),
|
|
970
1508
|
raw: RAW_INPUT_PROPERTY,
|
|
971
1509
|
},
|
|
972
|
-
required:
|
|
1510
|
+
required: [],
|
|
973
1511
|
additionalProperties: false,
|
|
974
1512
|
},
|
|
975
|
-
outputSchema: listOutputSchema("
|
|
1513
|
+
outputSchema: listOutputSchema("accounts", ACCOUNT_SCHEMA),
|
|
976
1514
|
handler: async (args: JsonRecord, ctx) => {
|
|
977
1515
|
const { result, resultInfo } = await callCloudflare(
|
|
978
1516
|
base,
|
|
979
1517
|
{
|
|
980
1518
|
method: "GET",
|
|
981
|
-
path:
|
|
1519
|
+
path: "/accounts",
|
|
982
1520
|
query: {
|
|
983
1521
|
name: optionalString(args, "name"),
|
|
984
|
-
type: optionalString(args, "type"),
|
|
985
|
-
content: optionalString(args, "content"),
|
|
986
|
-
order: optionalString(args, "order"),
|
|
987
|
-
direction: optionalString(args, "direction"),
|
|
988
1522
|
page: optionalNumber(args, "page"),
|
|
989
1523
|
per_page: optionalNumber(args, "perPage"),
|
|
990
1524
|
},
|
|
991
1525
|
},
|
|
992
1526
|
ctx,
|
|
993
1527
|
);
|
|
994
|
-
if (args["raw"] === true)
|
|
995
|
-
return { records: result, page: pageInfo(resultInfo) };
|
|
1528
|
+
if (args["raw"] === true) return { accounts: result, page: pageInfo(resultInfo) };
|
|
996
1529
|
return {
|
|
997
|
-
|
|
1530
|
+
accounts: asArray(result).map(projectAccount),
|
|
998
1531
|
page: pageInfo(resultInfo),
|
|
999
1532
|
};
|
|
1000
1533
|
},
|
|
1001
1534
|
},
|
|
1002
1535
|
{
|
|
1003
|
-
name: "
|
|
1004
|
-
description:
|
|
1536
|
+
name: "list_zones",
|
|
1537
|
+
description:
|
|
1538
|
+
"List zones (domains) this token can see, with their ids and status. This is the zoneId discovery step for every DNS and cache tool.",
|
|
1005
1539
|
annotations: readOnly,
|
|
1006
1540
|
inputSchema: {
|
|
1007
1541
|
type: "object",
|
|
1008
1542
|
properties: {
|
|
1009
|
-
|
|
1010
|
-
recordId: {
|
|
1543
|
+
name: {
|
|
1011
1544
|
type: "string",
|
|
1012
|
-
description: "
|
|
1545
|
+
description: "Filter by zone name, e.g. example.com.",
|
|
1546
|
+
},
|
|
1547
|
+
accountId: {
|
|
1548
|
+
type: "string",
|
|
1549
|
+
description:
|
|
1550
|
+
"Restrict to one account. Defaults to every account the token can see.",
|
|
1551
|
+
},
|
|
1552
|
+
status: {
|
|
1553
|
+
type: "string",
|
|
1554
|
+
enum: ["initializing", "pending", "active", "moved"],
|
|
1555
|
+
description: "Filter by zone status.",
|
|
1013
1556
|
},
|
|
1557
|
+
...pagingInputProperties(5, 50, { defaultPerPage: 20 }),
|
|
1014
1558
|
raw: RAW_INPUT_PROPERTY,
|
|
1015
1559
|
},
|
|
1016
|
-
required: [
|
|
1560
|
+
required: [],
|
|
1017
1561
|
additionalProperties: false,
|
|
1018
1562
|
},
|
|
1019
|
-
outputSchema:
|
|
1563
|
+
outputSchema: listOutputSchema("zones", ZONE_SCHEMA),
|
|
1020
1564
|
handler: async (args: JsonRecord, ctx) => {
|
|
1021
|
-
const { result } = await callCloudflare(
|
|
1565
|
+
const { result, resultInfo } = await callCloudflare(
|
|
1022
1566
|
base,
|
|
1023
1567
|
{
|
|
1024
1568
|
method: "GET",
|
|
1025
|
-
path:
|
|
1026
|
-
|
|
1027
|
-
|
|
1569
|
+
path: "/zones",
|
|
1570
|
+
query: {
|
|
1571
|
+
name: optionalString(args, "name"),
|
|
1572
|
+
// Deliberately not defaulted to `scope.accountId`. This is the
|
|
1573
|
+
// discovery tool: a deployment default that silently narrowed
|
|
1574
|
+
// what an agent can see would contradict the property's own
|
|
1575
|
+
// description, and there would be no argument that escapes it.
|
|
1576
|
+
"account.id": optionalString(args, "accountId"),
|
|
1577
|
+
status: optionalString(args, "status"),
|
|
1578
|
+
page: optionalNumber(args, "page"),
|
|
1579
|
+
per_page: optionalNumber(args, "perPage"),
|
|
1580
|
+
},
|
|
1028
1581
|
},
|
|
1029
1582
|
ctx,
|
|
1030
1583
|
);
|
|
1031
|
-
|
|
1584
|
+
if (args["raw"] === true) return { zones: result, page: pageInfo(resultInfo) };
|
|
1585
|
+
return {
|
|
1586
|
+
zones: asArray(result).map(projectZone),
|
|
1587
|
+
page: pageInfo(resultInfo),
|
|
1588
|
+
};
|
|
1032
1589
|
},
|
|
1033
1590
|
},
|
|
1034
1591
|
{
|
|
1035
|
-
name: "
|
|
1592
|
+
name: "get_zone",
|
|
1036
1593
|
description:
|
|
1037
|
-
"
|
|
1594
|
+
"Fetch one zone's settings summary by id: status, plan, name servers, and owning account.",
|
|
1038
1595
|
annotations: readOnly,
|
|
1039
1596
|
inputSchema: {
|
|
1040
1597
|
type: "object",
|
|
1041
1598
|
properties: {
|
|
1042
|
-
|
|
1599
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1043
1600
|
raw: RAW_INPUT_PROPERTY,
|
|
1044
1601
|
},
|
|
1045
|
-
required: scopeRequired("
|
|
1602
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1046
1603
|
additionalProperties: false,
|
|
1047
1604
|
},
|
|
1048
|
-
outputSchema:
|
|
1605
|
+
outputSchema: ZONE_SCHEMA,
|
|
1606
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1607
|
+
const { result } = await callCloudflare(
|
|
1608
|
+
base,
|
|
1609
|
+
{ method: "GET", path: `/zones/${encodeURIComponent(zoneArg(args))}` },
|
|
1610
|
+
ctx,
|
|
1611
|
+
);
|
|
1612
|
+
return args["raw"] === true ? result : projectZone(result);
|
|
1613
|
+
},
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
name: "list_zone_settings",
|
|
1617
|
+
description:
|
|
1618
|
+
"List the effective settings for a zone, including each setting's current value and whether the plan allows editing it.",
|
|
1619
|
+
annotations: readOnly,
|
|
1620
|
+
inputSchema: {
|
|
1049
1621
|
type: "object",
|
|
1050
1622
|
properties: {
|
|
1051
|
-
|
|
1052
|
-
createdOn: { type: "string" },
|
|
1053
|
-
modifiedOn: { type: "string" },
|
|
1054
|
-
usageModel: { type: "string" },
|
|
1623
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1055
1624
|
},
|
|
1056
|
-
required:
|
|
1057
|
-
|
|
1625
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1626
|
+
additionalProperties: false,
|
|
1627
|
+
},
|
|
1628
|
+
outputSchema: listOutputSchema("settings", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
1058
1629
|
handler: async (args: JsonRecord, ctx) => {
|
|
1059
1630
|
const { result, resultInfo } = await callCloudflare(
|
|
1060
1631
|
base,
|
|
1061
1632
|
{
|
|
1062
1633
|
method: "GET",
|
|
1063
|
-
path: `/
|
|
1634
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings`,
|
|
1064
1635
|
},
|
|
1065
1636
|
ctx,
|
|
1066
1637
|
);
|
|
1067
|
-
|
|
1068
|
-
return { scripts: result, page: pageInfo(resultInfo) };
|
|
1069
|
-
return {
|
|
1070
|
-
scripts: asArray(result).map(projectWorkerScript),
|
|
1071
|
-
page: pageInfo(resultInfo),
|
|
1072
|
-
};
|
|
1638
|
+
return { settings: asArray(result), page: pageInfo(resultInfo) };
|
|
1073
1639
|
},
|
|
1074
1640
|
},
|
|
1075
1641
|
{
|
|
1076
|
-
name: "
|
|
1642
|
+
name: "get_zone_setting",
|
|
1077
1643
|
description:
|
|
1078
|
-
"
|
|
1644
|
+
"Get one zone setting by its Cloudflare setting id, such as ssl, always_use_https, min_tls_version, brotli, or development_mode.",
|
|
1079
1645
|
annotations: readOnly,
|
|
1080
1646
|
inputSchema: {
|
|
1081
1647
|
type: "object",
|
|
1082
1648
|
properties: {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1649
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1650
|
+
settingId: {
|
|
1651
|
+
type: "string",
|
|
1652
|
+
minLength: 1,
|
|
1653
|
+
description: "Cloudflare zone setting id from list_zone_settings.",
|
|
1654
|
+
},
|
|
1086
1655
|
},
|
|
1087
|
-
required: scopeRequired("
|
|
1656
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "settingId"],
|
|
1088
1657
|
additionalProperties: false,
|
|
1089
1658
|
},
|
|
1090
|
-
outputSchema:
|
|
1659
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1660
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1661
|
+
const { result } = await callCloudflare(
|
|
1662
|
+
base,
|
|
1663
|
+
{
|
|
1664
|
+
method: "GET",
|
|
1665
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1666
|
+
},
|
|
1667
|
+
ctx,
|
|
1668
|
+
);
|
|
1669
|
+
return result;
|
|
1670
|
+
},
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
name: "update_zone_setting",
|
|
1674
|
+
description:
|
|
1675
|
+
"Set one editable zone setting. Read it first: allowed value types and plan restrictions differ by setting.",
|
|
1676
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1677
|
+
inputSchema: {
|
|
1091
1678
|
type: "object",
|
|
1092
1679
|
properties: {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1680
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1681
|
+
settingId: {
|
|
1682
|
+
type: "string",
|
|
1683
|
+
minLength: 1,
|
|
1684
|
+
description: "Cloudflare zone setting id from list_zone_settings.",
|
|
1685
|
+
},
|
|
1686
|
+
value: {
|
|
1687
|
+
type: ["string", "number", "boolean", "array"],
|
|
1688
|
+
description:
|
|
1689
|
+
"New setting value in the type returned by get_zone_setting. Arrays must contain strings.",
|
|
1690
|
+
items: { type: "string" },
|
|
1691
|
+
},
|
|
1096
1692
|
},
|
|
1097
|
-
required: ["
|
|
1098
|
-
|
|
1693
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "settingId", "value"],
|
|
1694
|
+
additionalProperties: false,
|
|
1695
|
+
},
|
|
1696
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1099
1697
|
handler: async (args: JsonRecord, ctx) => {
|
|
1100
|
-
const { result
|
|
1698
|
+
const { result } = await callCloudflare(
|
|
1101
1699
|
base,
|
|
1102
1700
|
{
|
|
1103
|
-
method: "
|
|
1104
|
-
path: `/
|
|
1105
|
-
|
|
1106
|
-
page: optionalNumber(args, "page"),
|
|
1107
|
-
per_page: optionalNumber(args, "perPage"),
|
|
1108
|
-
},
|
|
1701
|
+
method: "PATCH",
|
|
1702
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1703
|
+
body: { value: args["value"] },
|
|
1109
1704
|
},
|
|
1110
1705
|
ctx,
|
|
1111
1706
|
);
|
|
1112
|
-
|
|
1113
|
-
return { namespaces: result, page: pageInfo(resultInfo) };
|
|
1114
|
-
return {
|
|
1115
|
-
namespaces: asArray(result).map(projectKvNamespace),
|
|
1116
|
-
page: pageInfo(resultInfo),
|
|
1117
|
-
};
|
|
1707
|
+
return result;
|
|
1118
1708
|
},
|
|
1119
1709
|
},
|
|
1120
1710
|
{
|
|
1121
|
-
name: "
|
|
1711
|
+
name: "list_zone_rulesets",
|
|
1122
1712
|
description:
|
|
1123
|
-
"List
|
|
1713
|
+
"List zone rulesets for WAF, redirects, transforms, cache rules, configuration rules, and other Ruleset Engine phases.",
|
|
1124
1714
|
annotations: readOnly,
|
|
1125
1715
|
inputSchema: {
|
|
1126
1716
|
type: "object",
|
|
1127
1717
|
properties: {
|
|
1128
|
-
|
|
1129
|
-
nameContains: {
|
|
1130
|
-
type: "string",
|
|
1131
|
-
description: "Filter to buckets whose name contains this string.",
|
|
1132
|
-
},
|
|
1718
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1133
1719
|
perPage: {
|
|
1134
1720
|
type: "integer",
|
|
1135
1721
|
minimum: 1,
|
|
1136
|
-
maximum:
|
|
1137
|
-
description: "
|
|
1722
|
+
maximum: 50,
|
|
1723
|
+
description: "Rulesets per request, 1 to 50.",
|
|
1138
1724
|
},
|
|
1139
1725
|
cursor: {
|
|
1140
1726
|
type: "string",
|
|
1141
|
-
description:
|
|
1142
|
-
"Opaque cursor from a previous call's nextCursor. R2 paginates by cursor, not page number.",
|
|
1727
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
1143
1728
|
},
|
|
1144
|
-
raw: RAW_INPUT_PROPERTY,
|
|
1145
1729
|
},
|
|
1146
|
-
required: scopeRequired("
|
|
1730
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1147
1731
|
additionalProperties: false,
|
|
1148
1732
|
},
|
|
1149
1733
|
outputSchema: {
|
|
1150
1734
|
type: "object",
|
|
1151
1735
|
properties: {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1736
|
+
rulesets: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
1737
|
+
nextCursor: { type: "string" },
|
|
1738
|
+
},
|
|
1739
|
+
required: ["rulesets"],
|
|
1740
|
+
},
|
|
1741
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1742
|
+
const { result, resultInfo } = await callCloudflare(
|
|
1743
|
+
base,
|
|
1744
|
+
{
|
|
1745
|
+
method: "GET",
|
|
1746
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets`,
|
|
1747
|
+
query: {
|
|
1748
|
+
per_page: optionalNumber(args, "perPage"),
|
|
1749
|
+
cursor: optionalString(args, "cursor"),
|
|
1750
|
+
},
|
|
1751
|
+
},
|
|
1752
|
+
ctx,
|
|
1753
|
+
);
|
|
1754
|
+
const cursor = resultInfo?.cursors?.after;
|
|
1755
|
+
return {
|
|
1756
|
+
rulesets: asArray(result).map(projectRuleset),
|
|
1757
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
1758
|
+
? { nextCursor: cursor }
|
|
1759
|
+
: {}),
|
|
1760
|
+
};
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: "get_zone_ruleset",
|
|
1765
|
+
description:
|
|
1766
|
+
"Get one zone ruleset including its ordered rules, expressions, actions, parameters, and enabled state.",
|
|
1767
|
+
annotations: readOnly,
|
|
1768
|
+
inputSchema: {
|
|
1769
|
+
type: "object",
|
|
1770
|
+
properties: {
|
|
1771
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1772
|
+
rulesetId: {
|
|
1773
|
+
type: "string",
|
|
1774
|
+
minLength: 1,
|
|
1775
|
+
description: "Ruleset id from list_zone_rulesets.",
|
|
1776
|
+
},
|
|
1777
|
+
},
|
|
1778
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "rulesetId"],
|
|
1779
|
+
additionalProperties: false,
|
|
1780
|
+
},
|
|
1781
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1782
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1783
|
+
const { result } = await callCloudflare(
|
|
1784
|
+
base,
|
|
1785
|
+
{
|
|
1786
|
+
method: "GET",
|
|
1787
|
+
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets/${encodePathSegment(requireString(args, "rulesetId"))}`,
|
|
1788
|
+
},
|
|
1789
|
+
ctx,
|
|
1790
|
+
);
|
|
1791
|
+
return projectRuleset(result);
|
|
1792
|
+
},
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
name: "list_dns_records",
|
|
1796
|
+
description:
|
|
1797
|
+
"List DNS records in a zone, filtered by name, type, or content. Returns record ids, which update_dns_record and delete_dns_record require.",
|
|
1798
|
+
annotations: readOnly,
|
|
1799
|
+
inputSchema: {
|
|
1800
|
+
type: "object",
|
|
1801
|
+
properties: {
|
|
1802
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1803
|
+
name: {
|
|
1804
|
+
type: "string",
|
|
1805
|
+
description:
|
|
1806
|
+
"Exact record name, fully qualified, e.g. www.example.com.",
|
|
1807
|
+
},
|
|
1808
|
+
type: {
|
|
1809
|
+
type: "string",
|
|
1810
|
+
enum: [...CLOUDFLARE_DNS_RECORD_TYPES],
|
|
1811
|
+
description: "Filter by record type.",
|
|
1812
|
+
},
|
|
1813
|
+
content: {
|
|
1814
|
+
type: "string",
|
|
1815
|
+
description: "Exact record content, e.g. an IP address.",
|
|
1816
|
+
},
|
|
1817
|
+
order: {
|
|
1818
|
+
type: "string",
|
|
1819
|
+
enum: ["type", "name", "content", "ttl", "proxied"],
|
|
1820
|
+
description: "Sort field.",
|
|
1821
|
+
},
|
|
1822
|
+
direction: {
|
|
1823
|
+
type: "string",
|
|
1824
|
+
enum: ["asc", "desc"],
|
|
1825
|
+
description: "Sort direction for `order`. Defaults to asc.",
|
|
1826
|
+
},
|
|
1827
|
+
// Cloudflare documents 1 to 5,000,000 here with a default of 100; the
|
|
1828
|
+
// ceiling is nominal, so this connection caps it at a page size that
|
|
1829
|
+
// actually returns.
|
|
1830
|
+
...pagingInputProperties(1, 1000, {
|
|
1831
|
+
defaultPerPage: 100,
|
|
1832
|
+
bounds: "clamped",
|
|
1833
|
+
}),
|
|
1834
|
+
raw: RAW_INPUT_PROPERTY,
|
|
1835
|
+
},
|
|
1836
|
+
required: scopeRequired("zoneId", scope.zoneId),
|
|
1837
|
+
additionalProperties: false,
|
|
1838
|
+
},
|
|
1839
|
+
outputSchema: listOutputSchema("records", DNS_RECORD_SCHEMA),
|
|
1840
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1841
|
+
const { result, resultInfo } = await callCloudflare(
|
|
1842
|
+
base,
|
|
1843
|
+
{
|
|
1844
|
+
method: "GET",
|
|
1845
|
+
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records`,
|
|
1846
|
+
query: {
|
|
1847
|
+
name: optionalString(args, "name"),
|
|
1848
|
+
type: optionalString(args, "type"),
|
|
1849
|
+
content: optionalString(args, "content"),
|
|
1850
|
+
order: optionalString(args, "order"),
|
|
1851
|
+
direction: optionalString(args, "direction"),
|
|
1852
|
+
page: optionalNumber(args, "page"),
|
|
1853
|
+
per_page: optionalNumber(args, "perPage"),
|
|
1854
|
+
},
|
|
1855
|
+
},
|
|
1856
|
+
ctx,
|
|
1857
|
+
);
|
|
1858
|
+
if (args["raw"] === true)
|
|
1859
|
+
return { records: result, page: pageInfo(resultInfo) };
|
|
1860
|
+
return {
|
|
1861
|
+
records: asArray(result).map(projectDnsRecord),
|
|
1862
|
+
page: pageInfo(resultInfo),
|
|
1863
|
+
};
|
|
1864
|
+
},
|
|
1865
|
+
},
|
|
1866
|
+
{
|
|
1867
|
+
name: "get_dns_record",
|
|
1868
|
+
description: "Fetch one DNS record by its record id.",
|
|
1869
|
+
annotations: readOnly,
|
|
1870
|
+
inputSchema: {
|
|
1871
|
+
type: "object",
|
|
1872
|
+
properties: {
|
|
1873
|
+
zoneId: scopeProperty("zoneId", scope.zoneId),
|
|
1874
|
+
recordId: {
|
|
1875
|
+
type: "string",
|
|
1876
|
+
description: "DNS record id, from list_dns_records.",
|
|
1877
|
+
},
|
|
1878
|
+
raw: RAW_INPUT_PROPERTY,
|
|
1879
|
+
},
|
|
1880
|
+
required: [...scopeRequired("zoneId", scope.zoneId), "recordId"],
|
|
1881
|
+
additionalProperties: false,
|
|
1882
|
+
},
|
|
1883
|
+
outputSchema: DNS_RECORD_SCHEMA,
|
|
1884
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1885
|
+
const { result } = await callCloudflare(
|
|
1886
|
+
base,
|
|
1887
|
+
{
|
|
1888
|
+
method: "GET",
|
|
1889
|
+
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records/${encodeURIComponent(
|
|
1890
|
+
String(args["recordId"]),
|
|
1891
|
+
)}`,
|
|
1892
|
+
},
|
|
1893
|
+
ctx,
|
|
1894
|
+
);
|
|
1895
|
+
return args["raw"] === true ? result : projectDnsRecord(result);
|
|
1896
|
+
},
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
name: "list_worker_scripts",
|
|
1900
|
+
description:
|
|
1901
|
+
"List Workers scripts deployed in an account, with their last-modified times.",
|
|
1902
|
+
annotations: readOnly,
|
|
1903
|
+
inputSchema: {
|
|
1904
|
+
type: "object",
|
|
1905
|
+
properties: {
|
|
1906
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1907
|
+
raw: RAW_INPUT_PROPERTY,
|
|
1908
|
+
},
|
|
1909
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
1910
|
+
additionalProperties: false,
|
|
1911
|
+
},
|
|
1912
|
+
outputSchema: listOutputSchema("scripts", {
|
|
1913
|
+
type: "object",
|
|
1914
|
+
properties: {
|
|
1915
|
+
id: { type: "string", description: "Script name." },
|
|
1916
|
+
createdOn: { type: "string" },
|
|
1917
|
+
modifiedOn: { type: "string" },
|
|
1918
|
+
usageModel: { type: "string" },
|
|
1919
|
+
},
|
|
1920
|
+
required: ["id"],
|
|
1921
|
+
}),
|
|
1922
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1923
|
+
const { result, resultInfo } = await callCloudflare(
|
|
1924
|
+
base,
|
|
1925
|
+
{
|
|
1926
|
+
method: "GET",
|
|
1927
|
+
path: `/accounts/${encodeURIComponent(accountArg(args))}/workers/scripts`,
|
|
1928
|
+
},
|
|
1929
|
+
ctx,
|
|
1930
|
+
);
|
|
1931
|
+
if (args["raw"] === true)
|
|
1932
|
+
return { scripts: result, page: pageInfo(resultInfo) };
|
|
1933
|
+
return {
|
|
1934
|
+
scripts: asArray(result).map(projectWorkerScript),
|
|
1935
|
+
page: pageInfo(resultInfo),
|
|
1936
|
+
};
|
|
1937
|
+
},
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
name: "get_worker_settings",
|
|
1941
|
+
description:
|
|
1942
|
+
"Get a Worker's compatibility date and flags, bindings, limits, observability, placement, usage model, and other script settings.",
|
|
1943
|
+
annotations: readOnly,
|
|
1944
|
+
inputSchema: {
|
|
1945
|
+
type: "object",
|
|
1946
|
+
properties: {
|
|
1947
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1948
|
+
scriptName: {
|
|
1949
|
+
type: "string",
|
|
1950
|
+
minLength: 1,
|
|
1951
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1952
|
+
},
|
|
1953
|
+
},
|
|
1954
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
1955
|
+
additionalProperties: false,
|
|
1956
|
+
},
|
|
1957
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1958
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1959
|
+
const { result } = await callCloudflare(
|
|
1960
|
+
base,
|
|
1961
|
+
{
|
|
1962
|
+
method: "GET",
|
|
1963
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/settings`,
|
|
1964
|
+
},
|
|
1965
|
+
ctx,
|
|
1966
|
+
);
|
|
1967
|
+
return result;
|
|
1968
|
+
},
|
|
1969
|
+
},
|
|
1970
|
+
{
|
|
1971
|
+
name: "list_worker_deployments",
|
|
1972
|
+
description:
|
|
1973
|
+
"List deployments of a Worker script, including version traffic allocations and deployment strategy.",
|
|
1974
|
+
annotations: readOnly,
|
|
1975
|
+
inputSchema: {
|
|
1976
|
+
type: "object",
|
|
1977
|
+
properties: {
|
|
1978
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
1979
|
+
scriptName: {
|
|
1980
|
+
type: "string",
|
|
1981
|
+
minLength: 1,
|
|
1982
|
+
description: "Worker script name from list_worker_scripts.",
|
|
1983
|
+
},
|
|
1984
|
+
},
|
|
1985
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
1986
|
+
additionalProperties: false,
|
|
1987
|
+
},
|
|
1988
|
+
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
1989
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
1990
|
+
const { result } = await callCloudflare(
|
|
1991
|
+
base,
|
|
1992
|
+
{
|
|
1993
|
+
method: "GET",
|
|
1994
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments`,
|
|
1995
|
+
},
|
|
1996
|
+
ctx,
|
|
1997
|
+
);
|
|
1998
|
+
const record = asRecord(result);
|
|
1999
|
+
const deployments = Array.isArray(result)
|
|
2000
|
+
? result
|
|
2001
|
+
: asArray(record["deployments"]);
|
|
2002
|
+
return { deployments: deployments.map(projectWorkerDeployment) };
|
|
2003
|
+
},
|
|
2004
|
+
},
|
|
2005
|
+
{
|
|
2006
|
+
name: "get_worker_deployment",
|
|
2007
|
+
description: "Get one Worker deployment and its version traffic allocations.",
|
|
2008
|
+
annotations: readOnly,
|
|
2009
|
+
inputSchema: {
|
|
2010
|
+
type: "object",
|
|
2011
|
+
properties: {
|
|
2012
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2013
|
+
scriptName: {
|
|
2014
|
+
type: "string",
|
|
2015
|
+
minLength: 1,
|
|
2016
|
+
description: "Worker script name from list_worker_scripts.",
|
|
2017
|
+
},
|
|
2018
|
+
deploymentId: {
|
|
2019
|
+
type: "string",
|
|
2020
|
+
minLength: 1,
|
|
2021
|
+
description: "Deployment id from list_worker_deployments.",
|
|
2022
|
+
},
|
|
2023
|
+
},
|
|
2024
|
+
required: [
|
|
2025
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2026
|
+
"scriptName",
|
|
2027
|
+
"deploymentId",
|
|
2028
|
+
],
|
|
2029
|
+
additionalProperties: false,
|
|
2030
|
+
},
|
|
2031
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2032
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2033
|
+
const { result } = await callCloudflare(
|
|
2034
|
+
base,
|
|
2035
|
+
{
|
|
2036
|
+
method: "GET",
|
|
2037
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
2038
|
+
},
|
|
2039
|
+
ctx,
|
|
2040
|
+
);
|
|
2041
|
+
return projectWorkerDeployment(result);
|
|
2042
|
+
},
|
|
2043
|
+
},
|
|
2044
|
+
{
|
|
2045
|
+
name: "delete_worker_script",
|
|
2046
|
+
description:
|
|
2047
|
+
"Delete a Worker script and stop traffic served by that script. This cannot be undone from the API.",
|
|
2048
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2049
|
+
inputSchema: {
|
|
2050
|
+
type: "object",
|
|
2051
|
+
properties: {
|
|
2052
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2053
|
+
scriptName: {
|
|
2054
|
+
type: "string",
|
|
2055
|
+
minLength: 1,
|
|
2056
|
+
description: "Worker script name from list_worker_scripts.",
|
|
2057
|
+
},
|
|
2058
|
+
force: {
|
|
2059
|
+
type: "boolean",
|
|
2060
|
+
description:
|
|
2061
|
+
"Pass Cloudflare's force=true option when the script has dependencies that permit forced removal.",
|
|
2062
|
+
},
|
|
2063
|
+
},
|
|
2064
|
+
required: [...scopeRequired("accountId", scope.accountId), "scriptName"],
|
|
2065
|
+
additionalProperties: false,
|
|
2066
|
+
},
|
|
2067
|
+
outputSchema: {
|
|
2068
|
+
type: "object",
|
|
2069
|
+
properties: {
|
|
2070
|
+
deleted: { type: "boolean" },
|
|
2071
|
+
scriptName: { type: "string" },
|
|
2072
|
+
},
|
|
2073
|
+
required: ["deleted", "scriptName"],
|
|
2074
|
+
},
|
|
2075
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2076
|
+
const scriptName = requireString(args, "scriptName");
|
|
2077
|
+
await callCloudflare(
|
|
2078
|
+
base,
|
|
2079
|
+
{
|
|
2080
|
+
method: "DELETE",
|
|
2081
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(scriptName)}`,
|
|
2082
|
+
query: { force: optionalBoolean(args, "force") },
|
|
2083
|
+
},
|
|
2084
|
+
ctx,
|
|
2085
|
+
);
|
|
2086
|
+
return { deleted: true, scriptName };
|
|
2087
|
+
},
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
name: "list_kv_namespaces",
|
|
2091
|
+
description:
|
|
2092
|
+
"List Workers KV namespaces in an account, with the namespace ids bindings refer to.",
|
|
2093
|
+
annotations: readOnly,
|
|
2094
|
+
inputSchema: {
|
|
2095
|
+
type: "object",
|
|
2096
|
+
properties: {
|
|
2097
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2098
|
+
...pagingInputProperties(1, 1000, { defaultPerPage: 20 }),
|
|
2099
|
+
raw: RAW_INPUT_PROPERTY,
|
|
2100
|
+
},
|
|
2101
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
2102
|
+
additionalProperties: false,
|
|
2103
|
+
},
|
|
2104
|
+
outputSchema: listOutputSchema("namespaces", {
|
|
2105
|
+
type: "object",
|
|
2106
|
+
properties: {
|
|
2107
|
+
id: { type: "string" },
|
|
2108
|
+
title: { type: "string" },
|
|
2109
|
+
supportsUrlEncoding: { type: "boolean" },
|
|
2110
|
+
},
|
|
2111
|
+
required: ["id", "title"],
|
|
2112
|
+
}),
|
|
2113
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2114
|
+
const { result, resultInfo } = await callCloudflare(
|
|
2115
|
+
base,
|
|
2116
|
+
{
|
|
2117
|
+
method: "GET",
|
|
2118
|
+
path: `/accounts/${encodeURIComponent(accountArg(args))}/storage/kv/namespaces`,
|
|
2119
|
+
query: {
|
|
2120
|
+
page: optionalNumber(args, "page"),
|
|
2121
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2122
|
+
},
|
|
2123
|
+
},
|
|
2124
|
+
ctx,
|
|
2125
|
+
);
|
|
2126
|
+
if (args["raw"] === true)
|
|
2127
|
+
return { namespaces: result, page: pageInfo(resultInfo) };
|
|
2128
|
+
return {
|
|
2129
|
+
namespaces: asArray(result).map(projectKvNamespace),
|
|
2130
|
+
page: pageInfo(resultInfo),
|
|
2131
|
+
};
|
|
2132
|
+
},
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
name: "get_kv_namespace",
|
|
2136
|
+
description: "Get one Workers KV namespace by id.",
|
|
2137
|
+
annotations: readOnly,
|
|
2138
|
+
inputSchema: {
|
|
2139
|
+
type: "object",
|
|
2140
|
+
properties: {
|
|
2141
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2142
|
+
namespaceId: {
|
|
2143
|
+
type: "string",
|
|
2144
|
+
minLength: 1,
|
|
2145
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2146
|
+
},
|
|
2147
|
+
},
|
|
2148
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
2149
|
+
additionalProperties: false,
|
|
2150
|
+
},
|
|
2151
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2152
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2153
|
+
const { result } = await callCloudflare(
|
|
2154
|
+
base,
|
|
2155
|
+
{
|
|
2156
|
+
method: "GET",
|
|
2157
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
2158
|
+
},
|
|
2159
|
+
ctx,
|
|
2160
|
+
);
|
|
2161
|
+
return projectKvNamespace(result);
|
|
2162
|
+
},
|
|
2163
|
+
},
|
|
2164
|
+
{
|
|
2165
|
+
name: "create_kv_namespace",
|
|
2166
|
+
description: "Create a Workers KV namespace.",
|
|
2167
|
+
annotations: { readOnlyHint: false },
|
|
2168
|
+
inputSchema: {
|
|
2169
|
+
type: "object",
|
|
2170
|
+
properties: {
|
|
2171
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2172
|
+
title: {
|
|
2173
|
+
type: "string",
|
|
2174
|
+
minLength: 1,
|
|
2175
|
+
maxLength: 512,
|
|
2176
|
+
description: "Human-readable namespace title.",
|
|
2177
|
+
},
|
|
2178
|
+
},
|
|
2179
|
+
required: [...scopeRequired("accountId", scope.accountId), "title"],
|
|
2180
|
+
additionalProperties: false,
|
|
2181
|
+
},
|
|
2182
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2183
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2184
|
+
const { result } = await callCloudflare(
|
|
2185
|
+
base,
|
|
2186
|
+
{
|
|
2187
|
+
method: "POST",
|
|
2188
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces`,
|
|
2189
|
+
body: { title: requireString(args, "title") },
|
|
2190
|
+
},
|
|
2191
|
+
ctx,
|
|
2192
|
+
);
|
|
2193
|
+
return projectKvNamespace(result);
|
|
2194
|
+
},
|
|
2195
|
+
},
|
|
2196
|
+
{
|
|
2197
|
+
name: "rename_kv_namespace",
|
|
2198
|
+
description: "Rename an existing Workers KV namespace without changing its id or keys.",
|
|
2199
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2200
|
+
inputSchema: {
|
|
2201
|
+
type: "object",
|
|
2202
|
+
properties: {
|
|
2203
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2204
|
+
namespaceId: {
|
|
2205
|
+
type: "string",
|
|
2206
|
+
minLength: 1,
|
|
2207
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2208
|
+
},
|
|
2209
|
+
title: {
|
|
2210
|
+
type: "string",
|
|
2211
|
+
minLength: 1,
|
|
2212
|
+
maxLength: 512,
|
|
2213
|
+
description: "Replacement namespace title.",
|
|
2214
|
+
},
|
|
2215
|
+
},
|
|
2216
|
+
required: [
|
|
2217
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2218
|
+
"namespaceId",
|
|
2219
|
+
"title",
|
|
2220
|
+
],
|
|
2221
|
+
additionalProperties: false,
|
|
2222
|
+
},
|
|
2223
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2224
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2225
|
+
const { result } = await callCloudflare(
|
|
2226
|
+
base,
|
|
2227
|
+
{
|
|
2228
|
+
method: "PUT",
|
|
2229
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
2230
|
+
body: { title: requireString(args, "title") },
|
|
2231
|
+
},
|
|
2232
|
+
ctx,
|
|
2233
|
+
);
|
|
2234
|
+
return result ?? { renamed: true, namespaceId: args["namespaceId"] };
|
|
2235
|
+
},
|
|
2236
|
+
},
|
|
2237
|
+
{
|
|
2238
|
+
name: "delete_kv_namespace",
|
|
2239
|
+
description: "Permanently delete a Workers KV namespace and every key stored in it.",
|
|
2240
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2241
|
+
inputSchema: {
|
|
2242
|
+
type: "object",
|
|
2243
|
+
properties: {
|
|
2244
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2245
|
+
namespaceId: {
|
|
2246
|
+
type: "string",
|
|
2247
|
+
minLength: 1,
|
|
2248
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2249
|
+
},
|
|
2250
|
+
},
|
|
2251
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
2252
|
+
additionalProperties: false,
|
|
2253
|
+
},
|
|
2254
|
+
outputSchema: {
|
|
2255
|
+
type: "object",
|
|
2256
|
+
properties: {
|
|
2257
|
+
deleted: { type: "boolean" },
|
|
2258
|
+
namespaceId: { type: "string" },
|
|
2259
|
+
},
|
|
2260
|
+
required: ["deleted", "namespaceId"],
|
|
2261
|
+
},
|
|
2262
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2263
|
+
const namespaceId = requireString(args, "namespaceId");
|
|
2264
|
+
await callCloudflare(
|
|
2265
|
+
base,
|
|
2266
|
+
{
|
|
2267
|
+
method: "DELETE",
|
|
2268
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(namespaceId)}`,
|
|
2269
|
+
},
|
|
2270
|
+
ctx,
|
|
2271
|
+
);
|
|
2272
|
+
return { deleted: true, namespaceId };
|
|
2273
|
+
},
|
|
2274
|
+
},
|
|
2275
|
+
{
|
|
2276
|
+
name: "list_kv_keys",
|
|
2277
|
+
description:
|
|
2278
|
+
"List keys and metadata in a Workers KV namespace by prefix, using cursor pagination.",
|
|
2279
|
+
annotations: readOnly,
|
|
2280
|
+
inputSchema: {
|
|
2281
|
+
type: "object",
|
|
2282
|
+
properties: {
|
|
2283
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2284
|
+
namespaceId: {
|
|
2285
|
+
type: "string",
|
|
2286
|
+
minLength: 1,
|
|
2287
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2288
|
+
},
|
|
2289
|
+
prefix: {
|
|
2290
|
+
type: "string",
|
|
2291
|
+
description: "Return only keys beginning with this prefix.",
|
|
2292
|
+
},
|
|
2293
|
+
limit: {
|
|
2294
|
+
type: "integer",
|
|
2295
|
+
minimum: 10,
|
|
2296
|
+
maximum: 1000,
|
|
2297
|
+
description: "Keys per request, 10 to 1000. Defaults to 1000.",
|
|
2298
|
+
},
|
|
2299
|
+
cursor: {
|
|
2300
|
+
type: "string",
|
|
2301
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
2302
|
+
},
|
|
2303
|
+
},
|
|
2304
|
+
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
2305
|
+
additionalProperties: false,
|
|
2306
|
+
},
|
|
2307
|
+
outputSchema: {
|
|
2308
|
+
type: "object",
|
|
2309
|
+
properties: {
|
|
2310
|
+
keys: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
2311
|
+
nextCursor: { type: "string" },
|
|
2312
|
+
},
|
|
2313
|
+
required: ["keys"],
|
|
2314
|
+
},
|
|
2315
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2316
|
+
const { result, resultInfo } = await callCloudflare(
|
|
2317
|
+
base,
|
|
2318
|
+
{
|
|
2319
|
+
method: "GET",
|
|
2320
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/keys`,
|
|
2321
|
+
query: {
|
|
2322
|
+
prefix: optionalString(args, "prefix"),
|
|
2323
|
+
limit: optionalNumber(args, "limit"),
|
|
2324
|
+
cursor: optionalString(args, "cursor"),
|
|
2325
|
+
},
|
|
2326
|
+
},
|
|
2327
|
+
ctx,
|
|
2328
|
+
);
|
|
2329
|
+
const cursor = resultInfo?.cursor;
|
|
2330
|
+
return {
|
|
2331
|
+
keys: asArray(result).map(projectKvKey),
|
|
2332
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
2333
|
+
? { nextCursor: cursor }
|
|
2334
|
+
: {}),
|
|
2335
|
+
};
|
|
2336
|
+
},
|
|
2337
|
+
},
|
|
2338
|
+
{
|
|
2339
|
+
name: "bulk_get_kv_values",
|
|
2340
|
+
description:
|
|
2341
|
+
"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.",
|
|
2342
|
+
annotations: readOnly,
|
|
2343
|
+
inputSchema: {
|
|
2344
|
+
type: "object",
|
|
2345
|
+
properties: {
|
|
2346
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2347
|
+
namespaceId: {
|
|
2348
|
+
type: "string",
|
|
2349
|
+
minLength: 1,
|
|
2350
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2351
|
+
},
|
|
2352
|
+
keys: {
|
|
2353
|
+
type: "array",
|
|
2354
|
+
minItems: 1,
|
|
2355
|
+
maxItems: 100,
|
|
2356
|
+
items: { type: "string", minLength: 1, maxLength: 512 },
|
|
2357
|
+
description: "Key names to retrieve, up to 100.",
|
|
2358
|
+
},
|
|
2359
|
+
withMetadata: {
|
|
2360
|
+
type: "boolean",
|
|
2361
|
+
description: "Include each key's metadata and expiration when true.",
|
|
2362
|
+
},
|
|
2363
|
+
type: {
|
|
2364
|
+
type: "string",
|
|
2365
|
+
enum: ["text", "json"],
|
|
2366
|
+
description: "Return strings as stored, or parse JSON values before returning them.",
|
|
2367
|
+
},
|
|
2368
|
+
},
|
|
2369
|
+
required: [
|
|
2370
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2371
|
+
"namespaceId",
|
|
2372
|
+
"keys",
|
|
2373
|
+
],
|
|
2374
|
+
additionalProperties: false,
|
|
2375
|
+
},
|
|
2376
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2377
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2378
|
+
const { result } = await callCloudflare(
|
|
2379
|
+
base,
|
|
2380
|
+
{
|
|
2381
|
+
method: "POST",
|
|
2382
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/get`,
|
|
2383
|
+
body: {
|
|
2384
|
+
keys: args["keys"],
|
|
2385
|
+
...(args["withMetadata"] !== undefined
|
|
2386
|
+
? { withMetadata: args["withMetadata"] }
|
|
2387
|
+
: {}),
|
|
2388
|
+
...(args["type"] !== undefined ? { type: args["type"] } : {}),
|
|
2389
|
+
},
|
|
2390
|
+
},
|
|
2391
|
+
ctx,
|
|
2392
|
+
);
|
|
2393
|
+
return asRecord(result);
|
|
2394
|
+
},
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
name: "bulk_write_kv_values",
|
|
2398
|
+
description:
|
|
2399
|
+
"Create or replace multiple Workers KV values, with optional expirations and JSON metadata.",
|
|
2400
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2401
|
+
inputSchema: {
|
|
2402
|
+
type: "object",
|
|
2403
|
+
properties: {
|
|
2404
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2405
|
+
namespaceId: {
|
|
2406
|
+
type: "string",
|
|
2407
|
+
minLength: 1,
|
|
2408
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2409
|
+
},
|
|
2410
|
+
entries: {
|
|
2411
|
+
type: "array",
|
|
2412
|
+
minItems: 1,
|
|
2413
|
+
maxItems: 10_000,
|
|
2414
|
+
description: "Key/value entries to write, up to Cloudflare's 10,000-key bulk limit.",
|
|
2415
|
+
items: {
|
|
2416
|
+
type: "object",
|
|
2417
|
+
properties: {
|
|
2418
|
+
key: { type: "string", minLength: 1, maxLength: 512 },
|
|
2419
|
+
value: { type: "string", maxLength: 26_214_400 },
|
|
2420
|
+
expiration: { type: "number" },
|
|
2421
|
+
expiration_ttl: { type: "number", minimum: 60 },
|
|
2422
|
+
metadata: { type: ["object", "array", "string", "number", "boolean", "null"] },
|
|
2423
|
+
base64: { type: "boolean" },
|
|
2424
|
+
},
|
|
2425
|
+
required: ["key", "value"],
|
|
2426
|
+
additionalProperties: false,
|
|
2427
|
+
},
|
|
2428
|
+
},
|
|
2429
|
+
},
|
|
2430
|
+
required: [
|
|
2431
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2432
|
+
"namespaceId",
|
|
2433
|
+
"entries",
|
|
2434
|
+
],
|
|
2435
|
+
additionalProperties: false,
|
|
2436
|
+
},
|
|
2437
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2438
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2439
|
+
const { result } = await callCloudflare(
|
|
2440
|
+
base,
|
|
2441
|
+
{
|
|
2442
|
+
method: "PUT",
|
|
2443
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk`,
|
|
2444
|
+
body: args["entries"],
|
|
2445
|
+
},
|
|
2446
|
+
ctx,
|
|
2447
|
+
);
|
|
2448
|
+
return asRecord(result);
|
|
2449
|
+
},
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
name: "bulk_delete_kv_values",
|
|
2453
|
+
description: "Permanently delete multiple keys from a Workers KV namespace.",
|
|
2454
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2455
|
+
inputSchema: {
|
|
2456
|
+
type: "object",
|
|
2457
|
+
properties: {
|
|
2458
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2459
|
+
namespaceId: {
|
|
2460
|
+
type: "string",
|
|
2461
|
+
minLength: 1,
|
|
2462
|
+
description: "KV namespace id from list_kv_namespaces.",
|
|
2463
|
+
},
|
|
2464
|
+
keys: {
|
|
2465
|
+
type: "array",
|
|
2466
|
+
minItems: 1,
|
|
2467
|
+
maxItems: 10_000,
|
|
2468
|
+
items: { type: "string", minLength: 1, maxLength: 512 },
|
|
2469
|
+
description: "Key names to delete, up to Cloudflare's 10,000-key bulk limit.",
|
|
2470
|
+
},
|
|
2471
|
+
},
|
|
2472
|
+
required: [
|
|
2473
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2474
|
+
"namespaceId",
|
|
2475
|
+
"keys",
|
|
2476
|
+
],
|
|
2477
|
+
additionalProperties: false,
|
|
2478
|
+
},
|
|
2479
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2480
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2481
|
+
const { result } = await callCloudflare(
|
|
2482
|
+
base,
|
|
2483
|
+
{
|
|
2484
|
+
method: "POST",
|
|
2485
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/delete`,
|
|
2486
|
+
body: args["keys"],
|
|
2487
|
+
},
|
|
2488
|
+
ctx,
|
|
2489
|
+
);
|
|
2490
|
+
return asRecord(result);
|
|
2491
|
+
},
|
|
2492
|
+
},
|
|
2493
|
+
{
|
|
2494
|
+
name: "list_r2_buckets",
|
|
2495
|
+
description:
|
|
2496
|
+
"List R2 buckets in an account, with location and storage class.",
|
|
2497
|
+
annotations: readOnly,
|
|
2498
|
+
inputSchema: {
|
|
2499
|
+
type: "object",
|
|
2500
|
+
properties: {
|
|
2501
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2502
|
+
nameContains: {
|
|
2503
|
+
type: "string",
|
|
2504
|
+
description: "Filter to buckets whose name contains this string.",
|
|
2505
|
+
},
|
|
2506
|
+
perPage: {
|
|
2507
|
+
type: "integer",
|
|
2508
|
+
minimum: 1,
|
|
2509
|
+
maximum: 1000,
|
|
2510
|
+
description: "Buckets per request, 1 to 1000. Defaults to 20.",
|
|
2511
|
+
},
|
|
2512
|
+
cursor: {
|
|
2513
|
+
type: "string",
|
|
2514
|
+
description:
|
|
2515
|
+
"Opaque cursor from a previous call's nextCursor. R2 paginates by cursor, not page number.",
|
|
2516
|
+
},
|
|
2517
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2518
|
+
raw: RAW_INPUT_PROPERTY,
|
|
2519
|
+
},
|
|
2520
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
2521
|
+
additionalProperties: false,
|
|
2522
|
+
},
|
|
2523
|
+
outputSchema: {
|
|
2524
|
+
type: "object",
|
|
2525
|
+
properties: {
|
|
2526
|
+
buckets: {
|
|
2527
|
+
type: "array",
|
|
2528
|
+
items: {
|
|
2529
|
+
type: "object",
|
|
2530
|
+
properties: {
|
|
2531
|
+
name: { type: "string" },
|
|
2532
|
+
location: { type: "string" },
|
|
2533
|
+
storageClass: { type: "string" },
|
|
2534
|
+
jurisdiction: { type: "string" },
|
|
2535
|
+
creationDate: { type: "string" },
|
|
2536
|
+
},
|
|
1162
2537
|
required: ["name"],
|
|
1163
2538
|
},
|
|
1164
2539
|
},
|
|
1165
|
-
nextCursor: {
|
|
2540
|
+
nextCursor: {
|
|
2541
|
+
type: "string",
|
|
2542
|
+
description:
|
|
2543
|
+
"Pass back as `cursor` to continue. Absent when the listing is complete.",
|
|
2544
|
+
},
|
|
2545
|
+
},
|
|
2546
|
+
required: ["buckets"],
|
|
2547
|
+
},
|
|
2548
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2549
|
+
const { result, resultInfo } = await callCloudflare(
|
|
2550
|
+
base,
|
|
2551
|
+
{
|
|
2552
|
+
method: "GET",
|
|
2553
|
+
path: `/accounts/${encodeURIComponent(accountArg(args))}/r2/buckets`,
|
|
2554
|
+
query: {
|
|
2555
|
+
name_contains: optionalString(args, "nameContains"),
|
|
2556
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2557
|
+
cursor: optionalString(args, "cursor"),
|
|
2558
|
+
},
|
|
2559
|
+
headers: r2Headers(args),
|
|
2560
|
+
},
|
|
2561
|
+
ctx,
|
|
2562
|
+
);
|
|
2563
|
+
// R2 nests its list under `buckets` rather than returning a bare array,
|
|
2564
|
+
// and its result_info carries a cursor instead of page counters.
|
|
2565
|
+
const cursor = resultInfo?.cursor;
|
|
2566
|
+
const next =
|
|
2567
|
+
typeof cursor === "string" && cursor !== ""
|
|
2568
|
+
? { nextCursor: cursor }
|
|
2569
|
+
: {};
|
|
2570
|
+
if (args["raw"] === true) return { buckets: result, ...next };
|
|
2571
|
+
return {
|
|
2572
|
+
buckets: asArray(asRecord(result)["buckets"]).map(projectR2Bucket),
|
|
2573
|
+
...next,
|
|
2574
|
+
};
|
|
2575
|
+
},
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
name: "get_r2_bucket",
|
|
2579
|
+
description: "Get one R2 bucket's location, jurisdiction, storage class, and creation time.",
|
|
2580
|
+
annotations: readOnly,
|
|
2581
|
+
inputSchema: {
|
|
2582
|
+
type: "object",
|
|
2583
|
+
properties: {
|
|
2584
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2585
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2586
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2587
|
+
},
|
|
2588
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2589
|
+
additionalProperties: false,
|
|
2590
|
+
},
|
|
2591
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2592
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2593
|
+
const { result } = await callCloudflare(
|
|
2594
|
+
base,
|
|
2595
|
+
{
|
|
2596
|
+
method: "GET",
|
|
2597
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2598
|
+
headers: r2Headers(args),
|
|
2599
|
+
},
|
|
2600
|
+
ctx,
|
|
2601
|
+
);
|
|
2602
|
+
return projectR2Bucket(result);
|
|
2603
|
+
},
|
|
2604
|
+
},
|
|
2605
|
+
{
|
|
2606
|
+
name: "create_r2_bucket",
|
|
2607
|
+
description: "Create an R2 bucket with an optional location hint and default storage class.",
|
|
2608
|
+
annotations: { readOnlyHint: false },
|
|
2609
|
+
inputSchema: {
|
|
2610
|
+
type: "object",
|
|
2611
|
+
properties: {
|
|
2612
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2613
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2614
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2615
|
+
locationHint: {
|
|
2616
|
+
type: "string",
|
|
2617
|
+
enum: ["apac", "eeur", "enam", "weur", "wnam", "oc"],
|
|
2618
|
+
description: "Optional placement hint for the new bucket.",
|
|
2619
|
+
},
|
|
2620
|
+
storageClass: {
|
|
2621
|
+
type: "string",
|
|
2622
|
+
enum: ["Standard", "InfrequentAccess"],
|
|
2623
|
+
description: "Default storage class for new objects.",
|
|
2624
|
+
},
|
|
2625
|
+
},
|
|
2626
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2627
|
+
additionalProperties: false,
|
|
2628
|
+
},
|
|
2629
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2630
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2631
|
+
const { result } = await callCloudflare(
|
|
2632
|
+
base,
|
|
2633
|
+
{
|
|
2634
|
+
method: "POST",
|
|
2635
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets`,
|
|
2636
|
+
headers: r2Headers(args),
|
|
2637
|
+
body: {
|
|
2638
|
+
name: requireString(args, "bucketName"),
|
|
2639
|
+
...(args["locationHint"] !== undefined
|
|
2640
|
+
? { locationHint: args["locationHint"] }
|
|
2641
|
+
: {}),
|
|
2642
|
+
...(args["storageClass"] !== undefined
|
|
2643
|
+
? { storageClass: args["storageClass"] }
|
|
2644
|
+
: {}),
|
|
2645
|
+
},
|
|
2646
|
+
},
|
|
2647
|
+
ctx,
|
|
2648
|
+
);
|
|
2649
|
+
return projectR2Bucket(result);
|
|
2650
|
+
},
|
|
2651
|
+
},
|
|
2652
|
+
{
|
|
2653
|
+
name: "update_r2_bucket",
|
|
2654
|
+
description: "Change the default storage class used for newly uploaded objects in an R2 bucket.",
|
|
2655
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2656
|
+
inputSchema: {
|
|
2657
|
+
type: "object",
|
|
2658
|
+
properties: {
|
|
2659
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2660
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2661
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2662
|
+
storageClass: {
|
|
2663
|
+
type: "string",
|
|
2664
|
+
enum: ["Standard", "InfrequentAccess"],
|
|
2665
|
+
description: "New default storage class for future uploads.",
|
|
2666
|
+
},
|
|
2667
|
+
},
|
|
2668
|
+
required: [
|
|
2669
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2670
|
+
"bucketName",
|
|
2671
|
+
"storageClass",
|
|
2672
|
+
],
|
|
2673
|
+
additionalProperties: false,
|
|
2674
|
+
},
|
|
2675
|
+
outputSchema: R2_BUCKET_SCHEMA,
|
|
2676
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2677
|
+
const { result } = await callCloudflare(
|
|
2678
|
+
base,
|
|
2679
|
+
{
|
|
2680
|
+
method: "PATCH",
|
|
2681
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2682
|
+
headers: {
|
|
2683
|
+
...r2Headers(args),
|
|
2684
|
+
"cf-r2-storage-class": String(args["storageClass"]),
|
|
2685
|
+
},
|
|
2686
|
+
},
|
|
2687
|
+
ctx,
|
|
2688
|
+
);
|
|
2689
|
+
return projectR2Bucket(result);
|
|
2690
|
+
},
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
name: "delete_r2_bucket",
|
|
2694
|
+
description:
|
|
2695
|
+
"Permanently delete an empty R2 bucket and all of its configuration. Cloudflare refuses non-empty buckets.",
|
|
2696
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2697
|
+
inputSchema: {
|
|
2698
|
+
type: "object",
|
|
2699
|
+
properties: {
|
|
2700
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2701
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2702
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2703
|
+
},
|
|
2704
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2705
|
+
additionalProperties: false,
|
|
2706
|
+
},
|
|
2707
|
+
outputSchema: {
|
|
2708
|
+
type: "object",
|
|
2709
|
+
properties: {
|
|
2710
|
+
deleted: { type: "boolean" },
|
|
2711
|
+
bucketName: { type: "string" },
|
|
2712
|
+
},
|
|
2713
|
+
required: ["deleted", "bucketName"],
|
|
2714
|
+
},
|
|
2715
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2716
|
+
const bucketName = requireString(args, "bucketName");
|
|
2717
|
+
await callCloudflare(
|
|
2718
|
+
base,
|
|
2719
|
+
{
|
|
2720
|
+
method: "DELETE",
|
|
2721
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(bucketName)}`,
|
|
2722
|
+
headers: r2Headers(args),
|
|
2723
|
+
},
|
|
2724
|
+
ctx,
|
|
2725
|
+
);
|
|
2726
|
+
return { deleted: true, bucketName };
|
|
2727
|
+
},
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
name: "list_r2_objects",
|
|
2731
|
+
description:
|
|
2732
|
+
"List object keys and metadata in an R2 bucket by prefix, with delimiter grouping and cursor pagination.",
|
|
2733
|
+
annotations: readOnly,
|
|
2734
|
+
inputSchema: {
|
|
2735
|
+
type: "object",
|
|
2736
|
+
properties: {
|
|
2737
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2738
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2739
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2740
|
+
prefix: {
|
|
2741
|
+
type: "string",
|
|
2742
|
+
description: "Return only object keys beginning with this prefix.",
|
|
2743
|
+
},
|
|
2744
|
+
delimiter: {
|
|
2745
|
+
type: "string",
|
|
2746
|
+
minLength: 1,
|
|
2747
|
+
maxLength: 1,
|
|
2748
|
+
description: "One character used to group path-like keys, usually '/'.",
|
|
2749
|
+
},
|
|
2750
|
+
startAfter: {
|
|
2751
|
+
type: "string",
|
|
2752
|
+
description: "Begin after this key in lexicographic order.",
|
|
2753
|
+
},
|
|
2754
|
+
perPage: {
|
|
2755
|
+
type: "integer",
|
|
2756
|
+
minimum: 1,
|
|
2757
|
+
maximum: 1000,
|
|
2758
|
+
description: "Objects per request, 1 to 1000.",
|
|
2759
|
+
},
|
|
2760
|
+
cursor: {
|
|
2761
|
+
type: "string",
|
|
2762
|
+
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
2763
|
+
},
|
|
2764
|
+
},
|
|
2765
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2766
|
+
additionalProperties: false,
|
|
2767
|
+
},
|
|
2768
|
+
outputSchema: {
|
|
2769
|
+
type: "object",
|
|
2770
|
+
properties: {
|
|
2771
|
+
objects: { type: "array", items: R2_OBJECT_SCHEMA },
|
|
2772
|
+
commonPrefixes: { type: "array", items: { type: "string" } },
|
|
2773
|
+
nextCursor: { type: "string" },
|
|
2774
|
+
truncated: { type: "boolean" },
|
|
2775
|
+
},
|
|
2776
|
+
required: ["objects", "truncated"],
|
|
2777
|
+
},
|
|
2778
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2779
|
+
const { result, resultInfo } = await callCloudflare(
|
|
2780
|
+
base,
|
|
2781
|
+
{
|
|
2782
|
+
method: "GET",
|
|
2783
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects`,
|
|
2784
|
+
headers: r2Headers(args),
|
|
2785
|
+
query: {
|
|
2786
|
+
prefix: optionalString(args, "prefix"),
|
|
2787
|
+
delimiter: optionalString(args, "delimiter"),
|
|
2788
|
+
start_after: optionalString(args, "startAfter"),
|
|
2789
|
+
per_page: optionalNumber(args, "perPage"),
|
|
2790
|
+
cursor: optionalString(args, "cursor"),
|
|
2791
|
+
},
|
|
2792
|
+
},
|
|
2793
|
+
ctx,
|
|
2794
|
+
);
|
|
2795
|
+
const cursor = resultInfo?.cursor;
|
|
2796
|
+
return {
|
|
2797
|
+
objects: asArray(result).map(projectR2Object),
|
|
2798
|
+
...(Array.isArray(resultInfo?.delimited)
|
|
2799
|
+
? { commonPrefixes: resultInfo.delimited }
|
|
2800
|
+
: {}),
|
|
2801
|
+
...(typeof cursor === "string" && cursor !== ""
|
|
2802
|
+
? { nextCursor: cursor }
|
|
2803
|
+
: {}),
|
|
2804
|
+
truncated: resultInfo?.is_truncated === true,
|
|
2805
|
+
};
|
|
2806
|
+
},
|
|
2807
|
+
},
|
|
2808
|
+
{
|
|
2809
|
+
name: "delete_r2_object",
|
|
2810
|
+
description: "Permanently delete one object from an R2 bucket by key.",
|
|
2811
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2812
|
+
inputSchema: {
|
|
2813
|
+
type: "object",
|
|
2814
|
+
properties: {
|
|
2815
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2816
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2817
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2818
|
+
objectKey: {
|
|
1166
2819
|
type: "string",
|
|
2820
|
+
minLength: 1,
|
|
2821
|
+
description: "Exact object key. Slashes are preserved as path separators.",
|
|
2822
|
+
},
|
|
2823
|
+
},
|
|
2824
|
+
required: [
|
|
2825
|
+
...scopeRequired("accountId", scope.accountId),
|
|
2826
|
+
"bucketName",
|
|
2827
|
+
"objectKey",
|
|
2828
|
+
],
|
|
2829
|
+
additionalProperties: false,
|
|
2830
|
+
},
|
|
2831
|
+
outputSchema: {
|
|
2832
|
+
type: "object",
|
|
2833
|
+
properties: {
|
|
2834
|
+
deleted: { type: "boolean" },
|
|
2835
|
+
objectKey: { type: "string" },
|
|
2836
|
+
},
|
|
2837
|
+
required: ["deleted", "objectKey"],
|
|
2838
|
+
},
|
|
2839
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2840
|
+
const objectKey = requireString(args, "objectKey");
|
|
2841
|
+
await callCloudflare(
|
|
2842
|
+
base,
|
|
2843
|
+
{
|
|
2844
|
+
method: "DELETE",
|
|
2845
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects/${encodeObjectKey(objectKey)}`,
|
|
2846
|
+
headers: r2Headers(args),
|
|
2847
|
+
},
|
|
2848
|
+
ctx,
|
|
2849
|
+
);
|
|
2850
|
+
return { deleted: true, objectKey };
|
|
2851
|
+
},
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
name: "get_r2_metrics",
|
|
2855
|
+
description:
|
|
2856
|
+
"Get account-level R2 object-count and storage-size metrics split by storage class and publication state.",
|
|
2857
|
+
annotations: readOnly,
|
|
2858
|
+
inputSchema: {
|
|
2859
|
+
type: "object",
|
|
2860
|
+
properties: { accountId: scopeProperty("accountId", scope.accountId) },
|
|
2861
|
+
required: scopeRequired("accountId", scope.accountId),
|
|
2862
|
+
additionalProperties: false,
|
|
2863
|
+
},
|
|
2864
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2865
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2866
|
+
const { result } = await callCloudflare(
|
|
2867
|
+
base,
|
|
2868
|
+
{
|
|
2869
|
+
method: "GET",
|
|
2870
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/metrics`,
|
|
2871
|
+
},
|
|
2872
|
+
ctx,
|
|
2873
|
+
);
|
|
2874
|
+
return asRecord(result);
|
|
2875
|
+
},
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
name: "get_r2_cors",
|
|
2879
|
+
description: "Get the browser CORS rules configured on an R2 bucket.",
|
|
2880
|
+
annotations: readOnly,
|
|
2881
|
+
inputSchema: {
|
|
2882
|
+
type: "object",
|
|
2883
|
+
properties: {
|
|
2884
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2885
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2886
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2887
|
+
},
|
|
2888
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2889
|
+
additionalProperties: false,
|
|
2890
|
+
},
|
|
2891
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2892
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2893
|
+
const { result } = await callCloudflare(
|
|
2894
|
+
base,
|
|
2895
|
+
{
|
|
2896
|
+
method: "GET",
|
|
2897
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2898
|
+
headers: r2Headers(args),
|
|
2899
|
+
},
|
|
2900
|
+
ctx,
|
|
2901
|
+
);
|
|
2902
|
+
return asRecord(result);
|
|
2903
|
+
},
|
|
2904
|
+
},
|
|
2905
|
+
{
|
|
2906
|
+
name: "set_r2_cors",
|
|
2907
|
+
description:
|
|
2908
|
+
"Replace an R2 bucket's CORS policy. Supply the complete desired rule list; omitted existing rules are removed.",
|
|
2909
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2910
|
+
inputSchema: {
|
|
2911
|
+
type: "object",
|
|
2912
|
+
properties: {
|
|
2913
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2914
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2915
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2916
|
+
rules: {
|
|
2917
|
+
type: "array",
|
|
2918
|
+
maxItems: 100,
|
|
1167
2919
|
description:
|
|
1168
|
-
"
|
|
2920
|
+
"Complete CORS rule list using Cloudflare fields: allowed.methods, allowed.origins, optional allowed.headers, id, exposeHeaders, and maxAgeSeconds.",
|
|
2921
|
+
items: { type: "object", additionalProperties: true },
|
|
1169
2922
|
},
|
|
1170
2923
|
},
|
|
1171
|
-
required: ["
|
|
2924
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName", "rules"],
|
|
2925
|
+
additionalProperties: false,
|
|
2926
|
+
},
|
|
2927
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2928
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
2929
|
+
const { result } = await callCloudflare(
|
|
2930
|
+
base,
|
|
2931
|
+
{
|
|
2932
|
+
method: "PUT",
|
|
2933
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2934
|
+
headers: r2Headers(args),
|
|
2935
|
+
body: { rules: args["rules"] },
|
|
2936
|
+
},
|
|
2937
|
+
ctx,
|
|
2938
|
+
);
|
|
2939
|
+
return asRecord(result);
|
|
2940
|
+
},
|
|
2941
|
+
},
|
|
2942
|
+
{
|
|
2943
|
+
name: "delete_r2_cors",
|
|
2944
|
+
description: "Remove the complete CORS policy from an R2 bucket.",
|
|
2945
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2946
|
+
inputSchema: {
|
|
2947
|
+
type: "object",
|
|
2948
|
+
properties: {
|
|
2949
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
2950
|
+
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2951
|
+
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2952
|
+
},
|
|
2953
|
+
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2954
|
+
additionalProperties: false,
|
|
2955
|
+
},
|
|
2956
|
+
outputSchema: {
|
|
2957
|
+
type: "object",
|
|
2958
|
+
properties: { deleted: { type: "boolean" } },
|
|
2959
|
+
required: ["deleted"],
|
|
1172
2960
|
},
|
|
1173
2961
|
handler: async (args: JsonRecord, ctx) => {
|
|
1174
|
-
|
|
2962
|
+
await callCloudflare(
|
|
1175
2963
|
base,
|
|
1176
2964
|
{
|
|
1177
|
-
method: "
|
|
1178
|
-
path: `/accounts/${
|
|
1179
|
-
|
|
1180
|
-
name_contains: optionalString(args, "nameContains"),
|
|
1181
|
-
per_page: optionalNumber(args, "perPage"),
|
|
1182
|
-
cursor: optionalString(args, "cursor"),
|
|
1183
|
-
},
|
|
2965
|
+
method: "DELETE",
|
|
2966
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2967
|
+
headers: r2Headers(args),
|
|
1184
2968
|
},
|
|
1185
2969
|
ctx,
|
|
1186
2970
|
);
|
|
1187
|
-
|
|
1188
|
-
// and its result_info carries a cursor instead of page counters.
|
|
1189
|
-
const cursor = resultInfo?.cursor;
|
|
1190
|
-
const next =
|
|
1191
|
-
typeof cursor === "string" && cursor !== ""
|
|
1192
|
-
? { nextCursor: cursor }
|
|
1193
|
-
: {};
|
|
1194
|
-
if (args["raw"] === true) return { buckets: result, ...next };
|
|
1195
|
-
return {
|
|
1196
|
-
buckets: asArray(asRecord(result)["buckets"]).map(projectR2Bucket),
|
|
1197
|
-
...next,
|
|
1198
|
-
};
|
|
2971
|
+
return { deleted: true };
|
|
1199
2972
|
},
|
|
1200
2973
|
},
|
|
1201
2974
|
{
|
|
@@ -1254,6 +3027,338 @@ function buildTools(scope: Scoping): ApiTool[] {
|
|
|
1254
3027
|
};
|
|
1255
3028
|
},
|
|
1256
3029
|
},
|
|
3030
|
+
{
|
|
3031
|
+
name: "get_pages_project",
|
|
3032
|
+
description: "Get one Pages project, including build configuration, deployment configuration, domains, and latest deployment.",
|
|
3033
|
+
annotations: readOnly,
|
|
3034
|
+
inputSchema: {
|
|
3035
|
+
type: "object",
|
|
3036
|
+
properties: {
|
|
3037
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3038
|
+
projectName: {
|
|
3039
|
+
type: "string",
|
|
3040
|
+
minLength: 1,
|
|
3041
|
+
description: "Pages project name from list_pages_projects.",
|
|
3042
|
+
},
|
|
3043
|
+
raw: RAW_INPUT_PROPERTY,
|
|
3044
|
+
},
|
|
3045
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
3046
|
+
additionalProperties: false,
|
|
3047
|
+
},
|
|
3048
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
3049
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3050
|
+
const { result } = await callCloudflare(
|
|
3051
|
+
base,
|
|
3052
|
+
{
|
|
3053
|
+
method: "GET",
|
|
3054
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}`,
|
|
3055
|
+
},
|
|
3056
|
+
ctx,
|
|
3057
|
+
);
|
|
3058
|
+
return args["raw"] === true ? result : projectPagesProject(result);
|
|
3059
|
+
},
|
|
3060
|
+
},
|
|
3061
|
+
{
|
|
3062
|
+
name: "list_pages_deployments",
|
|
3063
|
+
description: "List production and preview deployments for a Pages project.",
|
|
3064
|
+
annotations: readOnly,
|
|
3065
|
+
inputSchema: {
|
|
3066
|
+
type: "object",
|
|
3067
|
+
properties: {
|
|
3068
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3069
|
+
projectName: {
|
|
3070
|
+
type: "string",
|
|
3071
|
+
minLength: 1,
|
|
3072
|
+
description: "Pages project name from list_pages_projects.",
|
|
3073
|
+
},
|
|
3074
|
+
env: {
|
|
3075
|
+
type: "string",
|
|
3076
|
+
enum: ["production", "preview"],
|
|
3077
|
+
description: "Optional deployment environment filter.",
|
|
3078
|
+
},
|
|
3079
|
+
...pagingInputProperties(1, 100, { bounds: "undocumented" }),
|
|
3080
|
+
},
|
|
3081
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
3082
|
+
additionalProperties: false,
|
|
3083
|
+
},
|
|
3084
|
+
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
3085
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3086
|
+
const { result, resultInfo } = await callCloudflare(
|
|
3087
|
+
base,
|
|
3088
|
+
{
|
|
3089
|
+
method: "GET",
|
|
3090
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments`,
|
|
3091
|
+
query: {
|
|
3092
|
+
env: optionalString(args, "env"),
|
|
3093
|
+
page: optionalNumber(args, "page"),
|
|
3094
|
+
per_page: optionalNumber(args, "perPage"),
|
|
3095
|
+
},
|
|
3096
|
+
},
|
|
3097
|
+
ctx,
|
|
3098
|
+
);
|
|
3099
|
+
return {
|
|
3100
|
+
deployments: asArray(result).map(projectPagesDeployment),
|
|
3101
|
+
page: pageInfo(resultInfo),
|
|
3102
|
+
};
|
|
3103
|
+
},
|
|
3104
|
+
},
|
|
3105
|
+
{
|
|
3106
|
+
name: "get_pages_deployment",
|
|
3107
|
+
description: "Get one Pages deployment including its environment, URLs, stages, source, and build configuration.",
|
|
3108
|
+
annotations: readOnly,
|
|
3109
|
+
inputSchema: {
|
|
3110
|
+
type: "object",
|
|
3111
|
+
properties: {
|
|
3112
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3113
|
+
projectName: {
|
|
3114
|
+
type: "string",
|
|
3115
|
+
minLength: 1,
|
|
3116
|
+
description: "Pages project name from list_pages_projects.",
|
|
3117
|
+
},
|
|
3118
|
+
deploymentId: {
|
|
3119
|
+
type: "string",
|
|
3120
|
+
minLength: 1,
|
|
3121
|
+
description: "Deployment id from list_pages_deployments.",
|
|
3122
|
+
},
|
|
3123
|
+
raw: RAW_INPUT_PROPERTY,
|
|
3124
|
+
},
|
|
3125
|
+
required: [
|
|
3126
|
+
...scopeRequired("accountId", scope.accountId),
|
|
3127
|
+
"projectName",
|
|
3128
|
+
"deploymentId",
|
|
3129
|
+
],
|
|
3130
|
+
additionalProperties: false,
|
|
3131
|
+
},
|
|
3132
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
3133
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3134
|
+
const { result } = await callCloudflare(
|
|
3135
|
+
base,
|
|
3136
|
+
{
|
|
3137
|
+
method: "GET",
|
|
3138
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
3139
|
+
},
|
|
3140
|
+
ctx,
|
|
3141
|
+
);
|
|
3142
|
+
return args["raw"] === true ? result : projectPagesDeployment(result);
|
|
3143
|
+
},
|
|
3144
|
+
},
|
|
3145
|
+
{
|
|
3146
|
+
name: "retry_pages_deployment",
|
|
3147
|
+
description: "Retry a failed or cancelled Pages deployment using its existing source and build configuration.",
|
|
3148
|
+
annotations: { readOnlyHint: false },
|
|
3149
|
+
inputSchema: {
|
|
3150
|
+
type: "object",
|
|
3151
|
+
properties: {
|
|
3152
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3153
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3154
|
+
deploymentId: { type: "string", minLength: 1, description: "Deployment id to retry." },
|
|
3155
|
+
},
|
|
3156
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
3157
|
+
additionalProperties: false,
|
|
3158
|
+
},
|
|
3159
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
3160
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3161
|
+
const { result } = await callCloudflare(
|
|
3162
|
+
base,
|
|
3163
|
+
{
|
|
3164
|
+
method: "POST",
|
|
3165
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/retry`,
|
|
3166
|
+
},
|
|
3167
|
+
ctx,
|
|
3168
|
+
);
|
|
3169
|
+
return projectPagesDeployment(result);
|
|
3170
|
+
},
|
|
3171
|
+
},
|
|
3172
|
+
{
|
|
3173
|
+
name: "rollback_pages_deployment",
|
|
3174
|
+
description: "Promote a previous Pages deployment to production, replacing the currently served production deployment.",
|
|
3175
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3176
|
+
inputSchema: {
|
|
3177
|
+
type: "object",
|
|
3178
|
+
properties: {
|
|
3179
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3180
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3181
|
+
deploymentId: { type: "string", minLength: 1, description: "Previous deployment id to promote." },
|
|
3182
|
+
},
|
|
3183
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
3184
|
+
additionalProperties: false,
|
|
3185
|
+
},
|
|
3186
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
3187
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3188
|
+
const { result } = await callCloudflare(
|
|
3189
|
+
base,
|
|
3190
|
+
{
|
|
3191
|
+
method: "POST",
|
|
3192
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/rollback`,
|
|
3193
|
+
},
|
|
3194
|
+
ctx,
|
|
3195
|
+
);
|
|
3196
|
+
return projectPagesDeployment(result);
|
|
3197
|
+
},
|
|
3198
|
+
},
|
|
3199
|
+
{
|
|
3200
|
+
name: "delete_pages_deployment",
|
|
3201
|
+
description: "Permanently delete a Pages deployment and its immutable deployment URL.",
|
|
3202
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3203
|
+
inputSchema: {
|
|
3204
|
+
type: "object",
|
|
3205
|
+
properties: {
|
|
3206
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3207
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3208
|
+
deploymentId: { type: "string", minLength: 1, description: "Deployment id to delete." },
|
|
3209
|
+
},
|
|
3210
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "deploymentId"],
|
|
3211
|
+
additionalProperties: false,
|
|
3212
|
+
},
|
|
3213
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, deploymentId: { type: "string" } }, required: ["deleted", "deploymentId"] },
|
|
3214
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3215
|
+
const deploymentId = requireString(args, "deploymentId");
|
|
3216
|
+
await callCloudflare(
|
|
3217
|
+
base,
|
|
3218
|
+
{
|
|
3219
|
+
method: "DELETE",
|
|
3220
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(deploymentId)}`,
|
|
3221
|
+
},
|
|
3222
|
+
ctx,
|
|
3223
|
+
);
|
|
3224
|
+
return { deleted: true, deploymentId };
|
|
3225
|
+
},
|
|
3226
|
+
},
|
|
3227
|
+
{
|
|
3228
|
+
name: "list_pages_domains",
|
|
3229
|
+
description: "List custom domains attached to a Pages project and their validation status.",
|
|
3230
|
+
annotations: readOnly,
|
|
3231
|
+
inputSchema: {
|
|
3232
|
+
type: "object",
|
|
3233
|
+
properties: {
|
|
3234
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3235
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3236
|
+
},
|
|
3237
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
3238
|
+
additionalProperties: false,
|
|
3239
|
+
},
|
|
3240
|
+
outputSchema: listOutputSchema("domains", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
3241
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3242
|
+
const { result } = await callCloudflare(
|
|
3243
|
+
base,
|
|
3244
|
+
{
|
|
3245
|
+
method: "GET",
|
|
3246
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
3247
|
+
},
|
|
3248
|
+
ctx,
|
|
3249
|
+
);
|
|
3250
|
+
return { domains: asArray(result).map(projectPagesDomain) };
|
|
3251
|
+
},
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
name: "add_pages_domain",
|
|
3255
|
+
description: "Attach a custom domain to a Pages project. DNS ownership and validation still apply.",
|
|
3256
|
+
annotations: { readOnlyHint: false },
|
|
3257
|
+
inputSchema: {
|
|
3258
|
+
type: "object",
|
|
3259
|
+
properties: {
|
|
3260
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3261
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3262
|
+
domain: { type: "string", minLength: 1, description: "Fully qualified custom domain to attach." },
|
|
3263
|
+
},
|
|
3264
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "domain"],
|
|
3265
|
+
additionalProperties: false,
|
|
3266
|
+
},
|
|
3267
|
+
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
3268
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3269
|
+
const { result } = await callCloudflare(
|
|
3270
|
+
base,
|
|
3271
|
+
{
|
|
3272
|
+
method: "POST",
|
|
3273
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
3274
|
+
body: { name: requireString(args, "domain") },
|
|
3275
|
+
},
|
|
3276
|
+
ctx,
|
|
3277
|
+
);
|
|
3278
|
+
return projectPagesDomain(result);
|
|
3279
|
+
},
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
name: "delete_pages_domain",
|
|
3283
|
+
description: "Detach a custom domain from a Pages project.",
|
|
3284
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3285
|
+
inputSchema: {
|
|
3286
|
+
type: "object",
|
|
3287
|
+
properties: {
|
|
3288
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3289
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3290
|
+
domain: { type: "string", minLength: 1, description: "Custom domain to detach." },
|
|
3291
|
+
},
|
|
3292
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName", "domain"],
|
|
3293
|
+
additionalProperties: false,
|
|
3294
|
+
},
|
|
3295
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, domain: { type: "string" } }, required: ["deleted", "domain"] },
|
|
3296
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3297
|
+
const domain = requireString(args, "domain");
|
|
3298
|
+
await callCloudflare(
|
|
3299
|
+
base,
|
|
3300
|
+
{
|
|
3301
|
+
method: "DELETE",
|
|
3302
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains/${encodePathSegment(domain)}`,
|
|
3303
|
+
},
|
|
3304
|
+
ctx,
|
|
3305
|
+
);
|
|
3306
|
+
return { deleted: true, domain };
|
|
3307
|
+
},
|
|
3308
|
+
},
|
|
3309
|
+
{
|
|
3310
|
+
name: "purge_pages_build_cache",
|
|
3311
|
+
description: "Clear a Pages project's build cache so its next deployment rebuilds dependencies and artifacts from scratch.",
|
|
3312
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3313
|
+
inputSchema: {
|
|
3314
|
+
type: "object",
|
|
3315
|
+
properties: {
|
|
3316
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3317
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name." },
|
|
3318
|
+
},
|
|
3319
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
3320
|
+
additionalProperties: false,
|
|
3321
|
+
},
|
|
3322
|
+
outputSchema: { type: "object", properties: { purged: { type: "boolean" } }, required: ["purged"] },
|
|
3323
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3324
|
+
await callCloudflare(
|
|
3325
|
+
base,
|
|
3326
|
+
{
|
|
3327
|
+
method: "POST",
|
|
3328
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/purge_build_cache`,
|
|
3329
|
+
},
|
|
3330
|
+
ctx,
|
|
3331
|
+
);
|
|
3332
|
+
return { purged: true };
|
|
3333
|
+
},
|
|
3334
|
+
},
|
|
3335
|
+
{
|
|
3336
|
+
name: "delete_pages_project",
|
|
3337
|
+
description: "Permanently delete a Pages project, its deployments, and project configuration.",
|
|
3338
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
3339
|
+
inputSchema: {
|
|
3340
|
+
type: "object",
|
|
3341
|
+
properties: {
|
|
3342
|
+
accountId: scopeProperty("accountId", scope.accountId),
|
|
3343
|
+
projectName: { type: "string", minLength: 1, description: "Pages project name to delete." },
|
|
3344
|
+
},
|
|
3345
|
+
required: [...scopeRequired("accountId", scope.accountId), "projectName"],
|
|
3346
|
+
additionalProperties: false,
|
|
3347
|
+
},
|
|
3348
|
+
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, projectName: { type: "string" } }, required: ["deleted", "projectName"] },
|
|
3349
|
+
handler: async (args: JsonRecord, ctx) => {
|
|
3350
|
+
const projectName = requireString(args, "projectName");
|
|
3351
|
+
await callCloudflare(
|
|
3352
|
+
base,
|
|
3353
|
+
{
|
|
3354
|
+
method: "DELETE",
|
|
3355
|
+
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(projectName)}`,
|
|
3356
|
+
},
|
|
3357
|
+
ctx,
|
|
3358
|
+
);
|
|
3359
|
+
return { deleted: true, projectName };
|
|
3360
|
+
},
|
|
3361
|
+
},
|
|
1257
3362
|
{
|
|
1258
3363
|
// Additive: brings a record into being and destroys nothing, so
|
|
1259
3364
|
// `destructiveHint` stays unset. `readOnlyHint: false` already routes it
|
|
@@ -1630,12 +3735,14 @@ Account purpose: ${purpose}
|
|
|
1630
3735
|
|
|
1631
3736
|
- ${zoneLine}
|
|
1632
3737
|
- ${accountLine}
|
|
1633
|
-
-
|
|
1634
|
-
-
|
|
3738
|
+
- 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.
|
|
3739
|
+
- 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.
|
|
3740
|
+
- 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.
|
|
3741
|
+
- 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.
|
|
1635
3742
|
- 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.
|
|
1636
3743
|
- 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.
|
|
1637
3744
|
- 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.
|
|
1638
|
-
-
|
|
3745
|
+
- 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\`.
|
|
1639
3746
|
${
|
|
1640
3747
|
accountInstructions
|
|
1641
3748
|
? `\n## Account instructions\n\n${accountInstructions}\n`
|
|
@@ -1660,7 +3767,7 @@ export function cloudflare(id: string, options: CloudflareOptions): Connector {
|
|
|
1660
3767
|
};
|
|
1661
3768
|
return api(id, {
|
|
1662
3769
|
title: options.title ?? "Cloudflare",
|
|
1663
|
-
description: `Cloudflare zones, DNS,
|
|
3770
|
+
description: `Cloudflare control-plane access for zones, DNS, Workers, KV, R2, Pages, media, email, and other v4 APIs — ${purpose}`,
|
|
1664
3771
|
credential: options.credential ?? DEFAULT_CREDENTIAL,
|
|
1665
3772
|
callAdmission: admissionPolicy(maxConcurrency),
|
|
1666
3773
|
usageGuide: usageGuide(purpose, scope, options.instructions),
|