@runpod/mcp-server 4.9.1 → 4.10.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/dist/hosted.cjs CHANGED
@@ -312,6 +312,14 @@ function validateArguments(inputSchema, args) {
312
312
  }
313
313
  return null;
314
314
  }
315
+ function withoutNullOptionals(inputSchema, args) {
316
+ const required = new Set(inputSchema.required ?? []);
317
+ return Object.fromEntries(
318
+ Object.entries(args).filter(
319
+ ([name, value]) => value !== null || required.has(name)
320
+ )
321
+ );
322
+ }
315
323
  async function dispatchGeneratedTool(client, tool, args) {
316
324
  const rejected = validateArguments(tool.inputSchema, args);
317
325
  if (rejected) return rejected;
@@ -436,6 +444,104 @@ function coerceScalars(value, schema, defs, depth = 0) {
436
444
 
437
445
  // src/specgen/generated/tools.gen.ts
438
446
  var generatedTools = [
447
+ {
448
+ name: "add-organization-group-member",
449
+ title: "Add organization group member",
450
+ operationId: "addOrganizationGroupMember",
451
+ description: "Add a member to a group. Adds the organization member to the group. The group and user must belong to the current organization. Adding an existing member is idempotent and returns the same no-content response.",
452
+ method: "PUT",
453
+ path: "/v2/organization/groups/{id}/members/{userId}",
454
+ params: [
455
+ {
456
+ name: "id",
457
+ location: "path"
458
+ },
459
+ {
460
+ name: "userId",
461
+ location: "path"
462
+ }
463
+ ],
464
+ hasBody: false,
465
+ annotations: {
466
+ readOnlyHint: false,
467
+ destructiveHint: false,
468
+ idempotentHint: true,
469
+ openWorldHint: true
470
+ },
471
+ inputSchema: {
472
+ type: "object",
473
+ properties: {
474
+ id: {
475
+ type: "string",
476
+ minLength: 1,
477
+ description: "Group ID."
478
+ },
479
+ userId: {
480
+ type: "string",
481
+ minLength: 1,
482
+ description: "Organization member user ID."
483
+ }
484
+ },
485
+ required: ["id", "userId"]
486
+ }
487
+ },
488
+ {
489
+ name: "attach-cost-center-resource",
490
+ title: "Attach cost center resource",
491
+ operationId: "attachCostCenterResource",
492
+ description: "Attribute a resource to a cost center. Attributes a resource to a cost center. Repeating the request is a no-op. A resource belongs to at most one cost center; one already attributed elsewhere is rejected with 409 rather than moved.",
493
+ method: "PUT",
494
+ path: "/v2/cost-centers/{id}/{resourceType}/{resourceId}",
495
+ params: [
496
+ {
497
+ name: "id",
498
+ location: "path"
499
+ },
500
+ {
501
+ name: "resourceType",
502
+ location: "path"
503
+ },
504
+ {
505
+ name: "resourceId",
506
+ location: "path"
507
+ }
508
+ ],
509
+ hasBody: false,
510
+ annotations: {
511
+ readOnlyHint: false,
512
+ destructiveHint: false,
513
+ idempotentHint: true,
514
+ openWorldHint: true
515
+ },
516
+ inputSchema: {
517
+ type: "object",
518
+ properties: {
519
+ id: {
520
+ type: "string",
521
+ minLength: 1,
522
+ description: "Cost center identifier."
523
+ },
524
+ resourceType: {
525
+ $ref: "#/$defs/ResourceType",
526
+ description: "Kind of resource.",
527
+ type: "string"
528
+ },
529
+ resourceId: {
530
+ type: "string",
531
+ minLength: 1,
532
+ description: "Resource identifier."
533
+ }
534
+ },
535
+ required: ["id", "resourceType", "resourceId"],
536
+ $defs: {
537
+ ResourceType: {
538
+ type: "string",
539
+ description: "Kind of resource a cost center or tag can be attached to.",
540
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
541
+ }
542
+ }
543
+ }
544
+ },
439
545
  {
440
546
  name: "create-cluster",
441
547
  title: "Create cluster",
@@ -534,7 +640,6 @@ var generatedTools = [
534
640
  },
535
641
  network: {
536
642
  type: "array",
537
- maxItems: 1,
538
643
  items: {
539
644
  $ref: "#/$defs/NetworkMount"
540
645
  }
@@ -556,7 +661,7 @@ var generatedTools = [
556
661
  gpuCountPerPod: {
557
662
  type: "integer",
558
663
  minimum: 1,
559
- description: "Number of GPUs on each pod. Bounded above by the GPU type's per-cloud maximum (GpuType.maxCount); the upstream rejects values beyond it.",
664
+ description: "Number of GPUs on each pod. Bounded above by the GPU type's per-cloud maximum (GpuType.maxCount); values beyond it are rejected.",
560
665
  examples: [8]
561
666
  },
562
667
  podCount: {
@@ -569,7 +674,7 @@ var generatedTools = [
569
674
  }
570
675
  },
571
676
  ClusterMounts: {
572
- description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
677
+ description: "Storage mounts for a cluster create: `persistent` or one `network` volume, not both. Clusters cannot mount global volumes, so `global` is rejected.",
573
678
  "x-go-type": "BaseMounts",
574
679
  allOf: [
575
680
  {
@@ -589,6 +694,7 @@ var generatedTools = [
589
694
  },
590
695
  network: {
591
696
  type: "array",
697
+ maxItems: 1,
592
698
  items: {
593
699
  type: "object",
594
700
  properties: {
@@ -667,7 +773,7 @@ var generatedTools = [
667
773
  type: "object",
668
774
  required: ["volumeId", "path"],
669
775
  additionalProperties: false,
670
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
776
+ description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. On a pod, the volume can be mounted, moved, or unmounted after creation (see `UpdatePodMounts`).",
671
777
  properties: {
672
778
  volumeId: {
673
779
  type: "string",
@@ -690,7 +796,7 @@ var generatedTools = [
690
796
  size: {
691
797
  type: "integer",
692
798
  minimum: 10,
693
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
799
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
694
800
  examples: [20]
695
801
  },
696
802
  path: {
@@ -703,6 +809,77 @@ var generatedTools = [
703
809
  }
704
810
  }
705
811
  },
812
+ {
813
+ name: "create-cost-center",
814
+ title: "Create cost center",
815
+ operationId: "createCostCenter",
816
+ description: "Create a cost center. Creates a cost center \u2014 a named label that groups resource spend so it can be broken out in billing. Pass `resources` to attribute resources at creation time; attaching is all-or-nothing, and a resource that already belongs to another cost center is rejected rather than moved.",
817
+ method: "POST",
818
+ path: "/v2/cost-centers",
819
+ params: [],
820
+ hasBody: true,
821
+ annotations: {
822
+ readOnlyHint: false,
823
+ destructiveHint: false,
824
+ idempotentHint: false,
825
+ openWorldHint: true
826
+ },
827
+ inputSchema: {
828
+ type: "object",
829
+ properties: {
830
+ body: {
831
+ $ref: "#/$defs/CreateCostCenterRequest",
832
+ type: "object"
833
+ }
834
+ },
835
+ required: ["body"],
836
+ $defs: {
837
+ CostCenterResource: {
838
+ type: "object",
839
+ additionalProperties: false,
840
+ required: ["id", "type"],
841
+ properties: {
842
+ id: {
843
+ type: "string",
844
+ minLength: 1,
845
+ examples: ["p9k2m4n6q8"]
846
+ },
847
+ type: {
848
+ $ref: "#/$defs/ResourceType"
849
+ }
850
+ }
851
+ },
852
+ CreateCostCenterRequest: {
853
+ type: "object",
854
+ additionalProperties: false,
855
+ required: ["value"],
856
+ properties: {
857
+ value: {
858
+ type: "string",
859
+ minLength: 1,
860
+ maxLength: 50,
861
+ pattern: "^\\s*\\S",
862
+ description: "Leading and trailing whitespace is trimmed.",
863
+ examples: ["team-ml"]
864
+ },
865
+ resources: {
866
+ type: "array",
867
+ description: "Resources to attribute to the new cost center.",
868
+ maxItems: 100,
869
+ items: {
870
+ $ref: "#/$defs/CostCenterResource"
871
+ }
872
+ }
873
+ }
874
+ },
875
+ ResourceType: {
876
+ type: "string",
877
+ description: "Kind of resource a cost center or tag can be attached to.",
878
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
879
+ }
880
+ }
881
+ }
882
+ },
706
883
  {
707
884
  name: "create-delegation",
708
885
  title: "Create ECR delegation",
@@ -1231,6 +1408,7 @@ var generatedTools = [
1231
1408
  type: "string",
1232
1409
  minLength: 1,
1233
1410
  maxLength: 191,
1411
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1234
1412
  description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
1235
1413
  examples: ["training-data"]
1236
1414
  }
@@ -1306,6 +1484,169 @@ var generatedTools = [
1306
1484
  }
1307
1485
  }
1308
1486
  },
1487
+ {
1488
+ name: "create-organization-group",
1489
+ title: "Create organization group",
1490
+ operationId: "createOrganizationGroup",
1491
+ description: "Create a group in your current organization",
1492
+ method: "POST",
1493
+ path: "/v2/organization/groups",
1494
+ params: [],
1495
+ hasBody: true,
1496
+ annotations: {
1497
+ readOnlyHint: false,
1498
+ destructiveHint: false,
1499
+ idempotentHint: false,
1500
+ openWorldHint: true
1501
+ },
1502
+ inputSchema: {
1503
+ type: "object",
1504
+ properties: {
1505
+ body: {
1506
+ $ref: "#/$defs/CreateOrganizationGroupRequest",
1507
+ type: "object"
1508
+ }
1509
+ },
1510
+ required: ["body"],
1511
+ $defs: {
1512
+ CreateOrganizationGroupRequest: {
1513
+ type: "object",
1514
+ additionalProperties: false,
1515
+ required: ["name"],
1516
+ properties: {
1517
+ name: {
1518
+ type: "string",
1519
+ minLength: 1,
1520
+ maxLength: 191,
1521
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1522
+ description: "Group name with at least one non-whitespace character; surrounding whitespace is trimmed.",
1523
+ examples: ["Engineering"]
1524
+ }
1525
+ }
1526
+ }
1527
+ }
1528
+ }
1529
+ },
1530
+ {
1531
+ name: "create-organization-invite",
1532
+ title: "Create organization invite",
1533
+ operationId: "createOrganizationInvite",
1534
+ description: "Invite a user to your organization. Sends an invitation to join the authenticated caller's organization and returns the pending invite; `expiresAt` is when it lapses. The invitee receives an email carrying the acceptance link, which is never returned here. Returns 409 when the address already belongs to a member or to a pending invite, or when the organization has reached its pending-invite limit; 404 when the role is unknown to the organization; and 403 when the caller does not hold every capability the role grants. Inviting requires the organization's invite-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable.",
1535
+ method: "POST",
1536
+ path: "/v2/organization/invites",
1537
+ params: [],
1538
+ hasBody: true,
1539
+ annotations: {
1540
+ readOnlyHint: false,
1541
+ destructiveHint: false,
1542
+ idempotentHint: false,
1543
+ openWorldHint: true
1544
+ },
1545
+ inputSchema: {
1546
+ type: "object",
1547
+ properties: {
1548
+ body: {
1549
+ $ref: "#/$defs/CreateOrganizationInviteRequest",
1550
+ type: "object"
1551
+ }
1552
+ },
1553
+ required: ["body"],
1554
+ $defs: {
1555
+ CreateOrganizationInviteRequest: {
1556
+ type: "object",
1557
+ additionalProperties: false,
1558
+ required: ["email", "role"],
1559
+ properties: {
1560
+ email: {
1561
+ type: "string",
1562
+ format: "email",
1563
+ minLength: 1,
1564
+ maxLength: 254,
1565
+ description: "Address to invite, stored lowercased. The invitee accepts by signing in with a verified account on this address.",
1566
+ examples: ["new-member@example.com"]
1567
+ },
1568
+ role: {
1569
+ $ref: "#/$defs/OrganizationRoleInput"
1570
+ }
1571
+ }
1572
+ },
1573
+ OrganizationRoleInput: {
1574
+ type: "object",
1575
+ description: "An organization role to assign, by ID.",
1576
+ additionalProperties: false,
1577
+ required: ["id"],
1578
+ properties: {
1579
+ id: {
1580
+ type: "string",
1581
+ minLength: 1,
1582
+ description: "Role ID, as reported on a member or an invite: `builtin:admin`, `builtin:dev`, `builtin:billing`, `builtin:basic`, or a custom role's own ID.",
1583
+ examples: ["builtin:dev"]
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+ },
1590
+ {
1591
+ name: "create-organization-role",
1592
+ title: "Create organization role",
1593
+ operationId: "createOrganizationRole",
1594
+ description: "Create a custom organization role. Creates a custom role in the authenticated caller's organization and returns it. Returns 409 when the organization already has a role with this name or has reached its custom-role limit; 422 when a capability is unknown or the name is reserved; and 403 when the caller does not hold every capability the role grants. Creating roles requires the organization's role-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable.",
1595
+ method: "POST",
1596
+ path: "/v2/organization/roles",
1597
+ params: [],
1598
+ hasBody: true,
1599
+ annotations: {
1600
+ readOnlyHint: false,
1601
+ destructiveHint: false,
1602
+ idempotentHint: false,
1603
+ openWorldHint: true
1604
+ },
1605
+ inputSchema: {
1606
+ type: "object",
1607
+ properties: {
1608
+ body: {
1609
+ $ref: "#/$defs/CreateOrganizationRoleRequest",
1610
+ type: "object"
1611
+ }
1612
+ },
1613
+ required: ["body"],
1614
+ $defs: {
1615
+ CreateOrganizationRoleRequest: {
1616
+ type: "object",
1617
+ additionalProperties: false,
1618
+ required: ["name", "capabilities"],
1619
+ properties: {
1620
+ name: {
1621
+ $ref: "#/$defs/OrganizationRoleName"
1622
+ },
1623
+ capabilities: {
1624
+ $ref: "#/$defs/OrganizationRoleCapabilitiesInput"
1625
+ }
1626
+ }
1627
+ },
1628
+ OrganizationRoleCapabilitiesInput: {
1629
+ type: "array",
1630
+ description: "What the role grants, as `FAMILY:VERB` capabilities from `get-organization-role-capabilities`. The caller must hold every capability it lists.",
1631
+ minItems: 1,
1632
+ uniqueItems: true,
1633
+ items: {
1634
+ type: "string",
1635
+ pattern: "^[A-Z0-9_]+:[A-Z0-9_]+$"
1636
+ },
1637
+ examples: [["POD:VIEW"]]
1638
+ },
1639
+ OrganizationRoleName: {
1640
+ type: "string",
1641
+ minLength: 1,
1642
+ maxLength: 191,
1643
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1644
+ description: "Display name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed. It must be unique within the organization and cannot be a built-in role's name.",
1645
+ examples: ["Pod viewer"]
1646
+ }
1647
+ }
1648
+ }
1649
+ },
1309
1650
  {
1310
1651
  name: "create-pod",
1311
1652
  title: "Create Pod",
@@ -1426,6 +1767,7 @@ var generatedTools = [
1426
1767
  count: {
1427
1768
  type: "integer",
1428
1769
  minimum: 1,
1770
+ maximum: 1024,
1429
1771
  default: 1,
1430
1772
  description: "Number of GPUs",
1431
1773
  examples: [1]
@@ -1440,7 +1782,6 @@ var generatedTools = [
1440
1782
  },
1441
1783
  network: {
1442
1784
  type: "array",
1443
- maxItems: 1,
1444
1785
  items: {
1445
1786
  $ref: "#/$defs/NetworkMount"
1446
1787
  }
@@ -1504,12 +1845,14 @@ var generatedTools = [
1504
1845
  minRamPerGpu: {
1505
1846
  type: "integer",
1506
1847
  minimum: 1,
1848
+ maximum: 65536,
1507
1849
  description: "Minimum host system RAM in GB per requested GPU. This is a placement filter, not a resource request. The actual total allocated system RAM may be higher and is returned in gpu.memory.",
1508
1850
  examples: [8]
1509
1851
  },
1510
1852
  minVcpuCountPerGpu: {
1511
1853
  type: "integer",
1512
1854
  minimum: 1,
1855
+ maximum: 4096,
1513
1856
  description: "Minimum host vCPU count per requested GPU. This is a placement filter, not a resource request. The actual total allocated vCPU count may be higher and is returned in gpu.vcpuCount.",
1514
1857
  examples: [2]
1515
1858
  }
@@ -1600,7 +1943,7 @@ var generatedTools = [
1600
1943
  type: "object",
1601
1944
  required: ["volumeId"],
1602
1945
  additionalProperties: false,
1603
- description: "Reference to a GlobalVolume. Usage-billed, so it has no size. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
1946
+ description: "Reference to a GlobalVolume. Usage-billed, so it has no size. On a pod, the volume can be mounted, moved, or unmounted after creation (see `UpdatePodMounts`).",
1604
1947
  properties: {
1605
1948
  volumeId: {
1606
1949
  type: "string",
@@ -1609,7 +1952,7 @@ var generatedTools = [
1609
1952
  },
1610
1953
  path: {
1611
1954
  type: "string",
1612
- description: "Mount path inside the container. Optional on create, where a default is chosen for you; a PATCH that omits it leaves the path unchanged.",
1955
+ description: "Mount path inside the container. Optional; a default is chosen for you when omitted.",
1613
1956
  examples: ["/data"]
1614
1957
  }
1615
1958
  }
@@ -1623,10 +1966,18 @@ var generatedTools = [
1623
1966
  {
1624
1967
  type: "object",
1625
1968
  properties: {
1969
+ network: {
1970
+ type: "array",
1971
+ maxItems: 4,
1972
+ description: "Up to 4 network volumes, all in the pod's data center.",
1973
+ items: {
1974
+ $ref: "#/$defs/NetworkMount"
1975
+ }
1976
+ },
1626
1977
  global: {
1627
1978
  type: "array",
1628
1979
  maxItems: 1,
1629
- description: "At most one global volume, on GPU and CPU pods, with no data center constraint. Combinable with `network` but not with `persistent` (rejected): upstream drops a persistent disk whenever a volume is attached.",
1980
+ description: "At most one global volume, on GPU and CPU pods, with no data center constraint. Combinable with `network` but not with `persistent` (rejected): a persistent disk is dropped whenever a volume is attached. Same PATCH rules as `network`.",
1630
1981
  items: {
1631
1982
  $ref: "#/$defs/GlobalMount"
1632
1983
  }
@@ -1640,7 +1991,7 @@ var generatedTools = [
1640
1991
  type: "object",
1641
1992
  required: ["volumeId", "path"],
1642
1993
  additionalProperties: false,
1643
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
1994
+ description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. On a pod, the volume can be mounted, moved, or unmounted after creation (see `UpdatePodMounts`).",
1644
1995
  properties: {
1645
1996
  volumeId: {
1646
1997
  type: "string",
@@ -1663,7 +2014,7 @@ var generatedTools = [
1663
2014
  size: {
1664
2015
  type: "integer",
1665
2016
  minimum: 10,
1666
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
2017
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
1667
2018
  examples: [20]
1668
2019
  },
1669
2020
  path: {
@@ -1960,7 +2311,7 @@ var generatedTools = [
1960
2311
  size: {
1961
2312
  type: "integer",
1962
2313
  minimum: 10,
1963
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
2314
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
1964
2315
  examples: [20]
1965
2316
  },
1966
2317
  path: {
@@ -2021,13 +2372,12 @@ var generatedTools = [
2021
2372
  }
2022
2373
  },
2023
2374
  {
2024
- name: "delete-endpoint",
2025
- title: "Delete endpoint",
2026
- operationId: "deleteEndpoint",
2027
- description: "Delete a serverless endpoint permanently and immediately. Verify the id belongs to the intended endpoint (get-endpoint) before deleting; delete only endpoints your own tool calls created in this conversation, or the one the user asked you to delete by id \u2014 a name found by listing is not attribution.",
2028
- directoryDescription: "Delete a Serverless endpoint permanently and immediately.",
2375
+ name: "delete-cost-center",
2376
+ title: "Delete cost center",
2377
+ operationId: "deleteCostCenter",
2378
+ description: "Delete a cost center. Deletes a cost center. The delete fails with 409 while any resource is still attributed to the cost center; detach those resources first.",
2029
2379
  method: "DELETE",
2030
- path: "/v2/serverless/{id}",
2380
+ path: "/v2/cost-centers/{id}",
2031
2381
  params: [
2032
2382
  {
2033
2383
  name: "id",
@@ -2046,17 +2396,50 @@ var generatedTools = [
2046
2396
  properties: {
2047
2397
  id: {
2048
2398
  type: "string",
2049
- description: "Serverless endpoint identifier"
2399
+ minLength: 1,
2400
+ description: "Cost center identifier."
2050
2401
  }
2051
2402
  },
2052
2403
  required: ["id"]
2053
2404
  }
2054
2405
  },
2055
2406
  {
2056
- name: "delete-global-volume",
2057
- title: "Delete global volume",
2058
- operationId: "deleteGlobalVolume",
2059
- description: "Delete a global volume. Permanently deletes a global volume and its stored data. Fails with `409` while the volume is still mounted: terminate every pod that mounts it (stopped pods count) and detach it from every serverless endpoint first. Nothing is detached or terminated on your behalf.",
2407
+ name: "delete-endpoint",
2408
+ title: "Delete endpoint",
2409
+ operationId: "deleteEndpoint",
2410
+ description: "Delete a serverless endpoint permanently and immediately. Verify the id belongs to the intended endpoint (get-endpoint) before deleting; delete only endpoints your own tool calls created in this conversation, or the one the user asked you to delete by id \u2014 a name found by listing is not attribution.",
2411
+ directoryDescription: "Delete a Serverless endpoint permanently and immediately.",
2412
+ method: "DELETE",
2413
+ path: "/v2/serverless/{id}",
2414
+ params: [
2415
+ {
2416
+ name: "id",
2417
+ location: "path"
2418
+ }
2419
+ ],
2420
+ hasBody: false,
2421
+ annotations: {
2422
+ readOnlyHint: false,
2423
+ destructiveHint: true,
2424
+ idempotentHint: true,
2425
+ openWorldHint: true
2426
+ },
2427
+ inputSchema: {
2428
+ type: "object",
2429
+ properties: {
2430
+ id: {
2431
+ type: "string",
2432
+ description: "Serverless endpoint identifier"
2433
+ }
2434
+ },
2435
+ required: ["id"]
2436
+ }
2437
+ },
2438
+ {
2439
+ name: "delete-global-volume",
2440
+ title: "Delete global volume",
2441
+ operationId: "deleteGlobalVolume",
2442
+ description: "Delete a global volume. Permanently deletes a global volume and its stored data. Fails with `409` while the volume is still mounted: terminate every pod that mounts it (stopped pods count) and detach it from every serverless endpoint first. Nothing is detached or terminated on your behalf.",
2060
2443
  method: "DELETE",
2061
2444
  path: "/v2/global-volumes/{id}",
2062
2445
  params: [
@@ -2114,6 +2497,131 @@ var generatedTools = [
2114
2497
  required: ["id"]
2115
2498
  }
2116
2499
  },
2500
+ {
2501
+ name: "delete-organization-group",
2502
+ title: "Delete organization group",
2503
+ operationId: "deleteOrganizationGroup",
2504
+ description: "Delete a group in your current organization. Permanently deletes the group and its group memberships. Organization members are not removed. Returns 404 when the group does not exist in your current organization, including when it has already been deleted.",
2505
+ method: "DELETE",
2506
+ path: "/v2/organization/groups/{id}",
2507
+ params: [
2508
+ {
2509
+ name: "id",
2510
+ location: "path"
2511
+ }
2512
+ ],
2513
+ hasBody: false,
2514
+ annotations: {
2515
+ readOnlyHint: false,
2516
+ destructiveHint: true,
2517
+ idempotentHint: true,
2518
+ openWorldHint: true
2519
+ },
2520
+ inputSchema: {
2521
+ type: "object",
2522
+ properties: {
2523
+ id: {
2524
+ type: "string",
2525
+ minLength: 1,
2526
+ description: "Group ID."
2527
+ }
2528
+ },
2529
+ required: ["id"]
2530
+ }
2531
+ },
2532
+ {
2533
+ name: "delete-organization-invite",
2534
+ title: "Delete organization invite",
2535
+ operationId: "deleteOrganizationInvite",
2536
+ description: "Cancel an organization invite. Cancels one invitation to the authenticated caller's organization; the invitee can no longer accept it. Returns 404 when the ID is unknown to the caller's organization: never issued, already accepted, already cancelled, or issued by another organization. An expired invite can still be cancelled. Cancelling invites requires the organization's invite-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable.",
2537
+ method: "DELETE",
2538
+ path: "/v2/organization/invites/{id}",
2539
+ params: [
2540
+ {
2541
+ name: "id",
2542
+ location: "path"
2543
+ }
2544
+ ],
2545
+ hasBody: false,
2546
+ annotations: {
2547
+ readOnlyHint: false,
2548
+ destructiveHint: true,
2549
+ idempotentHint: true,
2550
+ openWorldHint: true
2551
+ },
2552
+ inputSchema: {
2553
+ type: "object",
2554
+ properties: {
2555
+ id: {
2556
+ type: "string",
2557
+ description: "Invite identifier"
2558
+ }
2559
+ },
2560
+ required: ["id"]
2561
+ }
2562
+ },
2563
+ {
2564
+ name: "delete-organization-role",
2565
+ title: "Delete organization role",
2566
+ operationId: "deleteOrganizationRole",
2567
+ description: "Delete a custom organization role. Deletes a custom role from the authenticated caller's organization. Returns 409 while a member or a pending invite holds the role: reassign those members and cancel those invites first, since deleting never changes anyone's role. Expired invites do not block deletion. Returns 400 for a built-in role, which cannot be deleted; 404 when the ID is unknown, names another organization's role, or was already deleted. Deleting roles requires the organization's role-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable.",
2568
+ method: "DELETE",
2569
+ path: "/v2/organization/roles/{id}",
2570
+ params: [
2571
+ {
2572
+ name: "id",
2573
+ location: "path"
2574
+ }
2575
+ ],
2576
+ hasBody: false,
2577
+ annotations: {
2578
+ readOnlyHint: false,
2579
+ destructiveHint: true,
2580
+ idempotentHint: true,
2581
+ openWorldHint: true
2582
+ },
2583
+ inputSchema: {
2584
+ type: "object",
2585
+ properties: {
2586
+ id: {
2587
+ type: "string",
2588
+ description: "Role identifier"
2589
+ }
2590
+ },
2591
+ required: ["id"]
2592
+ }
2593
+ },
2594
+ {
2595
+ name: "delete-organization-user",
2596
+ title: "Delete organization user",
2597
+ operationId: "deleteOrganizationUser",
2598
+ description: "Remove an organization member. Removes one member from the authenticated caller's organization by user ID. Their group memberships end and the API keys attributed to them within the organization are revoked; the resources they created stay with the organization. Removing another member requires the organization's member-removal capability, which the built-in admin role holds; any member can remove themselves. The organization keeps at least one member with the built-in admin role, so removing the last one returns 409. A user who is not a member, including one from another organization, returns 404. A member without the capability removing someone else, a caller outside an organization, and a caller for whom organizations are unavailable receive 403.",
2599
+ method: "DELETE",
2600
+ path: "/v2/organization/users/{id}",
2601
+ params: [
2602
+ {
2603
+ name: "id",
2604
+ location: "path"
2605
+ }
2606
+ ],
2607
+ hasBody: false,
2608
+ annotations: {
2609
+ readOnlyHint: false,
2610
+ destructiveHint: true,
2611
+ idempotentHint: true,
2612
+ openWorldHint: true
2613
+ },
2614
+ inputSchema: {
2615
+ type: "object",
2616
+ properties: {
2617
+ id: {
2618
+ type: "string",
2619
+ description: "User identifier"
2620
+ }
2621
+ },
2622
+ required: ["id"]
2623
+ }
2624
+ },
2117
2625
  {
2118
2626
  name: "delete-pod",
2119
2627
  title: "Delete Pod",
@@ -2239,6 +2747,63 @@ var generatedTools = [
2239
2747
  required: ["id"]
2240
2748
  }
2241
2749
  },
2750
+ {
2751
+ name: "detach-cost-center-resource",
2752
+ title: "Detach cost center resource",
2753
+ operationId: "detachCostCenterResource",
2754
+ description: "Remove a resource from a cost center. Removes a resource's attribution to a cost center. The resource itself is untouched. Returns 404 when the cost center does not exist or the resource is not attributed to it, so repeating a DELETE that succeeded returns 404.",
2755
+ method: "DELETE",
2756
+ path: "/v2/cost-centers/{id}/{resourceType}/{resourceId}",
2757
+ params: [
2758
+ {
2759
+ name: "id",
2760
+ location: "path"
2761
+ },
2762
+ {
2763
+ name: "resourceType",
2764
+ location: "path"
2765
+ },
2766
+ {
2767
+ name: "resourceId",
2768
+ location: "path"
2769
+ }
2770
+ ],
2771
+ hasBody: false,
2772
+ annotations: {
2773
+ readOnlyHint: false,
2774
+ destructiveHint: true,
2775
+ idempotentHint: true,
2776
+ openWorldHint: true
2777
+ },
2778
+ inputSchema: {
2779
+ type: "object",
2780
+ properties: {
2781
+ id: {
2782
+ type: "string",
2783
+ minLength: 1,
2784
+ description: "Cost center identifier."
2785
+ },
2786
+ resourceType: {
2787
+ $ref: "#/$defs/ResourceType",
2788
+ description: "Kind of resource.",
2789
+ type: "string"
2790
+ },
2791
+ resourceId: {
2792
+ type: "string",
2793
+ minLength: 1,
2794
+ description: "Resource identifier."
2795
+ }
2796
+ },
2797
+ required: ["id", "resourceType", "resourceId"],
2798
+ $defs: {
2799
+ ResourceType: {
2800
+ type: "string",
2801
+ description: "Kind of resource a cost center or tag can be attached to.",
2802
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
2803
+ }
2804
+ }
2805
+ }
2806
+ },
2242
2807
  {
2243
2808
  name: "get-cluster",
2244
2809
  title: "Get cluster",
@@ -2270,6 +2835,38 @@ var generatedTools = [
2270
2835
  required: ["id"]
2271
2836
  }
2272
2837
  },
2838
+ {
2839
+ name: "get-cost-center",
2840
+ title: "Get cost center",
2841
+ operationId: "getCostCenter",
2842
+ description: "Get a cost center. Returns one cost center. Its attributed resources are listed by `GET /v2/cost-centers/{id}/resources`. Returns 404 when the cost center does not exist or is not the caller's.",
2843
+ method: "GET",
2844
+ path: "/v2/cost-centers/{id}",
2845
+ params: [
2846
+ {
2847
+ name: "id",
2848
+ location: "path"
2849
+ }
2850
+ ],
2851
+ hasBody: false,
2852
+ annotations: {
2853
+ readOnlyHint: true,
2854
+ destructiveHint: false,
2855
+ idempotentHint: true,
2856
+ openWorldHint: true
2857
+ },
2858
+ inputSchema: {
2859
+ type: "object",
2860
+ properties: {
2861
+ id: {
2862
+ type: "string",
2863
+ minLength: 1,
2864
+ description: "Cost center identifier."
2865
+ }
2866
+ },
2867
+ required: ["id"]
2868
+ }
2869
+ },
2273
2870
  {
2274
2871
  name: "get-cpu-type",
2275
2872
  title: "Get CPU type",
@@ -2467,6 +3064,261 @@ var generatedTools = [
2467
3064
  required: ["id", "buildId"]
2468
3065
  }
2469
3066
  },
3067
+ {
3068
+ name: "get-endpoint-cold-start-metrics",
3069
+ title: "Get endpoint cold start metrics",
3070
+ operationId: "getEndpointColdStartMetrics",
3071
+ description: "Get serverless endpoint cold start metrics. Returns the cold start series shown on the endpoint's Metrics tab in the console. **Window.** `startTime` is inclusive and `endTime` is exclusive. Both are optional: `endTime` defaults to now and `startTime` to 24 hours before `endTime`, rounded down to the start of an `interval` bucket. An `endTime` in the future is treated as now. Metrics are kept for 90 days, so `startTime` must be within the last 90 days. **Interval.** Each record covers one `interval`-sized bucket, aligned to UTC. **Paging.** Records are oldest first, at most `limit` (default 1000) per page. Follow `pagination.nextCursor` while `pagination.hasNextPage` is true. The cursor keeps the first page's window, so `startTime`, `endTime`, and `interval` can be left out on later pages; if sent, they must match the first page or the cursor is rejected with 422. **Sparse series.** Buckets with no activity are omitted, not zero-filled, so `records` can be shorter than the number of buckets in the window, or empty. **Units.** `durationMs` is how long each cold start took, in milliseconds.",
3072
+ method: "GET",
3073
+ path: "/v2/serverless/{id}/metrics/cold-starts",
3074
+ params: [
3075
+ {
3076
+ name: "id",
3077
+ location: "path"
3078
+ },
3079
+ {
3080
+ name: "startTime",
3081
+ location: "query"
3082
+ },
3083
+ {
3084
+ name: "endTime",
3085
+ location: "query"
3086
+ },
3087
+ {
3088
+ name: "interval",
3089
+ location: "query"
3090
+ },
3091
+ {
3092
+ name: "cursor",
3093
+ location: "query"
3094
+ },
3095
+ {
3096
+ name: "limit",
3097
+ location: "query"
3098
+ }
3099
+ ],
3100
+ hasBody: false,
3101
+ annotations: {
3102
+ readOnlyHint: true,
3103
+ destructiveHint: false,
3104
+ idempotentHint: true,
3105
+ openWorldHint: true
3106
+ },
3107
+ inputSchema: {
3108
+ type: "object",
3109
+ properties: {
3110
+ id: {
3111
+ type: "string",
3112
+ description: "Serverless endpoint identifier"
3113
+ },
3114
+ startTime: {
3115
+ type: "string",
3116
+ format: "date-time",
3117
+ description: "Start of the window (RFC 3339), inclusive. Defaults to 24 hours before endTime, rounded down to the start of an interval bucket. Must be within the last 90 days."
3118
+ },
3119
+ endTime: {
3120
+ type: "string",
3121
+ format: "date-time",
3122
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3123
+ },
3124
+ interval: {
3125
+ $ref: "#/$defs/EndpointMetricsInterval",
3126
+ description: "Bucket size. Defaults to 1h.",
3127
+ type: "string"
3128
+ },
3129
+ cursor: {
3130
+ type: "string",
3131
+ minLength: 1,
3132
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3133
+ },
3134
+ limit: {
3135
+ type: "integer",
3136
+ minimum: 1,
3137
+ maximum: 1e3,
3138
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3139
+ }
3140
+ },
3141
+ required: ["id"],
3142
+ $defs: {
3143
+ EndpointMetricsInterval: {
3144
+ type: "string",
3145
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3146
+ default: "1h",
3147
+ description: "Bucket size for endpoint metrics."
3148
+ }
3149
+ }
3150
+ }
3151
+ },
3152
+ {
3153
+ name: "get-endpoint-request-metrics",
3154
+ title: "Get endpoint request metrics",
3155
+ operationId: "getEndpointRequestMetrics",
3156
+ description: "Get serverless endpoint request metrics. Returns the request series shown on the endpoint's Metrics tab in the console. **Window.** `startTime` is inclusive and `endTime` is exclusive. Both are optional: `endTime` defaults to now and `startTime` to 24 hours before `endTime`, rounded down to the start of an `interval` bucket. An `endTime` in the future is treated as now. Metrics are kept for 90 days, so `startTime` must be within the last 90 days. **Interval.** Each record covers one `interval`-sized bucket, aligned to UTC. **Paging.** Records are oldest first, at most `limit` (default 1000) per page. Follow `pagination.nextCursor` while `pagination.hasNextPage` is true. The cursor keeps the first page's window, so `startTime`, `endTime`, and `interval` can be left out on later pages; if sent, they must match the first page or the cursor is rejected with 422. **Sparse series.** Buckets with no activity are omitted, not zero-filled, so `records` can be shorter than the number of buckets in the window, or empty. **Units.** Durations are milliseconds. `executionTimeMs` is the time a worker spent running a request; `delayTimeMs` is the time a request waited in the queue before a worker picked it up.",
3157
+ method: "GET",
3158
+ path: "/v2/serverless/{id}/metrics/requests",
3159
+ params: [
3160
+ {
3161
+ name: "id",
3162
+ location: "path"
3163
+ },
3164
+ {
3165
+ name: "startTime",
3166
+ location: "query"
3167
+ },
3168
+ {
3169
+ name: "endTime",
3170
+ location: "query"
3171
+ },
3172
+ {
3173
+ name: "interval",
3174
+ location: "query"
3175
+ },
3176
+ {
3177
+ name: "cursor",
3178
+ location: "query"
3179
+ },
3180
+ {
3181
+ name: "limit",
3182
+ location: "query"
3183
+ }
3184
+ ],
3185
+ hasBody: false,
3186
+ annotations: {
3187
+ readOnlyHint: true,
3188
+ destructiveHint: false,
3189
+ idempotentHint: true,
3190
+ openWorldHint: true
3191
+ },
3192
+ inputSchema: {
3193
+ type: "object",
3194
+ properties: {
3195
+ id: {
3196
+ type: "string",
3197
+ description: "Serverless endpoint identifier"
3198
+ },
3199
+ startTime: {
3200
+ type: "string",
3201
+ format: "date-time",
3202
+ description: "Start of the window (RFC 3339), inclusive. Defaults to 24 hours before endTime, rounded down to the start of an interval bucket. Must be within the last 90 days."
3203
+ },
3204
+ endTime: {
3205
+ type: "string",
3206
+ format: "date-time",
3207
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3208
+ },
3209
+ interval: {
3210
+ $ref: "#/$defs/EndpointMetricsInterval",
3211
+ description: "Bucket size. Defaults to 1h.",
3212
+ type: "string"
3213
+ },
3214
+ cursor: {
3215
+ type: "string",
3216
+ minLength: 1,
3217
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3218
+ },
3219
+ limit: {
3220
+ type: "integer",
3221
+ minimum: 1,
3222
+ maximum: 1e3,
3223
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3224
+ }
3225
+ },
3226
+ required: ["id"],
3227
+ $defs: {
3228
+ EndpointMetricsInterval: {
3229
+ type: "string",
3230
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3231
+ default: "1h",
3232
+ description: "Bucket size for endpoint metrics."
3233
+ }
3234
+ }
3235
+ }
3236
+ },
3237
+ {
3238
+ name: "get-endpoint-worker-metrics",
3239
+ title: "Get endpoint worker metrics",
3240
+ operationId: "getEndpointWorkerMetrics",
3241
+ description: "Get serverless endpoint worker metrics. Returns the worker state series shown on the endpoint's Metrics tab in the console. For the current worker list, use `GET /v2/serverless/{id}/workers`. **Window.** `startTime` is inclusive and `endTime` is exclusive. Both are optional: `endTime` defaults to now and `startTime` to 24 hours before `endTime`, rounded down to the start of an `interval` bucket. An `endTime` in the future is treated as now. Metrics are kept for 90 days, so `startTime` must be within the last 90 days. **Interval.** Each record covers one `interval`-sized bucket, aligned to UTC. **Paging.** Records are oldest first, at most `limit` (default 1000) per page. Follow `pagination.nextCursor` while `pagination.hasNextPage` is true. The cursor keeps the first page's window, so `startTime`, `endTime`, and `interval` can be left out on later pages; if sent, they must match the first page or the cursor is rejected with 422. **Sparse series.** Buckets with no activity are omitted, not zero-filled, so `records` can be shorter than the number of buckets in the window, or empty. **Snapshots.** Each record holds the last worker-count snapshot in its bucket, as point-in-time counts.",
3242
+ method: "GET",
3243
+ path: "/v2/serverless/{id}/metrics/workers",
3244
+ params: [
3245
+ {
3246
+ name: "id",
3247
+ location: "path"
3248
+ },
3249
+ {
3250
+ name: "startTime",
3251
+ location: "query"
3252
+ },
3253
+ {
3254
+ name: "endTime",
3255
+ location: "query"
3256
+ },
3257
+ {
3258
+ name: "interval",
3259
+ location: "query"
3260
+ },
3261
+ {
3262
+ name: "cursor",
3263
+ location: "query"
3264
+ },
3265
+ {
3266
+ name: "limit",
3267
+ location: "query"
3268
+ }
3269
+ ],
3270
+ hasBody: false,
3271
+ annotations: {
3272
+ readOnlyHint: true,
3273
+ destructiveHint: false,
3274
+ idempotentHint: true,
3275
+ openWorldHint: true
3276
+ },
3277
+ inputSchema: {
3278
+ type: "object",
3279
+ properties: {
3280
+ id: {
3281
+ type: "string",
3282
+ description: "Serverless endpoint identifier"
3283
+ },
3284
+ startTime: {
3285
+ type: "string",
3286
+ format: "date-time",
3287
+ description: "Start of the window (RFC 3339), inclusive. Defaults to 24 hours before endTime, rounded down to the start of an interval bucket. Must be within the last 90 days."
3288
+ },
3289
+ endTime: {
3290
+ type: "string",
3291
+ format: "date-time",
3292
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3293
+ },
3294
+ interval: {
3295
+ $ref: "#/$defs/EndpointMetricsInterval",
3296
+ description: "Bucket size. Defaults to 1h.",
3297
+ type: "string"
3298
+ },
3299
+ cursor: {
3300
+ type: "string",
3301
+ minLength: 1,
3302
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3303
+ },
3304
+ limit: {
3305
+ type: "integer",
3306
+ minimum: 1,
3307
+ maximum: 1e3,
3308
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3309
+ }
3310
+ },
3311
+ required: ["id"],
3312
+ $defs: {
3313
+ EndpointMetricsInterval: {
3314
+ type: "string",
3315
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3316
+ default: "1h",
3317
+ description: "Bucket size for endpoint metrics."
3318
+ }
3319
+ }
3320
+ }
3321
+ },
2470
3322
  {
2471
3323
  name: "get-global-volume",
2472
3324
  title: "Get global volume",
@@ -2579,7 +3431,7 @@ var generatedTools = [
2579
3431
  },
2580
3432
  cloud: {
2581
3433
  $ref: "#/$defs/GpuCloudFilter",
2582
- description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Upstream default when omitted: SECURE.",
3434
+ description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Default when omitted: SECURE.",
2583
3435
  type: "string"
2584
3436
  },
2585
3437
  countryCodes: {
@@ -2618,19 +3470,133 @@ var generatedTools = [
2618
3470
  },
2619
3471
  Product: {
2620
3472
  type: "string",
2621
- description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
2622
- enum: ["POD", "CLUSTER", "SERVERLESS"]
3473
+ description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
3474
+ enum: ["POD", "CLUSTER", "SERVERLESS"]
3475
+ }
3476
+ }
3477
+ }
3478
+ },
3479
+ {
3480
+ name: "get-network-volume",
3481
+ title: "Get network volume",
3482
+ operationId: "getNetworkVolume",
3483
+ description: "Get a network volume. Returns a single network volume by ID.",
3484
+ method: "GET",
3485
+ path: "/v2/network-volumes/{id}",
3486
+ params: [
3487
+ {
3488
+ name: "id",
3489
+ location: "path"
3490
+ }
3491
+ ],
3492
+ hasBody: false,
3493
+ annotations: {
3494
+ readOnlyHint: true,
3495
+ destructiveHint: false,
3496
+ idempotentHint: true,
3497
+ openWorldHint: true
3498
+ },
3499
+ inputSchema: {
3500
+ type: "object",
3501
+ properties: {
3502
+ id: {
3503
+ type: "string",
3504
+ description: "Network volume identifier"
3505
+ }
3506
+ },
3507
+ required: ["id"]
3508
+ }
3509
+ },
3510
+ {
3511
+ name: "get-organization",
3512
+ title: "Get organization",
3513
+ operationId: "getOrganization",
3514
+ description: "Get your current organization. Returns the authenticated caller's current organization. Organization context comes from the caller's membership; no organization selector is required. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
3515
+ method: "GET",
3516
+ path: "/v2/organization",
3517
+ params: [],
3518
+ hasBody: false,
3519
+ annotations: {
3520
+ readOnlyHint: true,
3521
+ destructiveHint: false,
3522
+ idempotentHint: true,
3523
+ openWorldHint: true
3524
+ },
3525
+ inputSchema: {
3526
+ type: "object",
3527
+ properties: {}
3528
+ }
3529
+ },
3530
+ {
3531
+ name: "get-organization-group",
3532
+ title: "Get organization group",
3533
+ operationId: "getOrganizationGroup",
3534
+ description: "Get a group in your current organization. Returns the group without its members. Returns 404 when the ID is unknown or belongs to another organization, or when the caller does not belong to an organization.",
3535
+ method: "GET",
3536
+ path: "/v2/organization/groups/{id}",
3537
+ params: [
3538
+ {
3539
+ name: "id",
3540
+ location: "path"
3541
+ }
3542
+ ],
3543
+ hasBody: false,
3544
+ annotations: {
3545
+ readOnlyHint: true,
3546
+ destructiveHint: false,
3547
+ idempotentHint: true,
3548
+ openWorldHint: true
3549
+ },
3550
+ inputSchema: {
3551
+ type: "object",
3552
+ properties: {
3553
+ id: {
3554
+ type: "string",
3555
+ minLength: 1,
3556
+ description: "Group ID."
3557
+ }
3558
+ },
3559
+ required: ["id"]
3560
+ }
3561
+ },
3562
+ {
3563
+ name: "get-organization-invite",
3564
+ title: "Get organization invite",
3565
+ operationId: "getOrganizationInvite",
3566
+ description: "Get a pending organization invite. Returns one pending invitation to the authenticated caller's organization. Only unexpired invites are pending: an invite that has expired, been accepted or been revoked returns 404, as does an ID from another organization. Reading invites requires the organization's invite-management capability, which the built-in admin role holds; other members receive 403. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
3567
+ method: "GET",
3568
+ path: "/v2/organization/invites/{id}",
3569
+ params: [
3570
+ {
3571
+ name: "id",
3572
+ location: "path"
3573
+ }
3574
+ ],
3575
+ hasBody: false,
3576
+ annotations: {
3577
+ readOnlyHint: true,
3578
+ destructiveHint: false,
3579
+ idempotentHint: true,
3580
+ openWorldHint: true
3581
+ },
3582
+ inputSchema: {
3583
+ type: "object",
3584
+ properties: {
3585
+ id: {
3586
+ type: "string",
3587
+ description: "Invite identifier"
2623
3588
  }
2624
- }
3589
+ },
3590
+ required: ["id"]
2625
3591
  }
2626
3592
  },
2627
3593
  {
2628
- name: "get-network-volume",
2629
- title: "Get network volume",
2630
- operationId: "getNetworkVolume",
2631
- description: "Get a network volume. Returns a single network volume by ID.",
3594
+ name: "get-organization-role",
3595
+ title: "Get organization role",
3596
+ operationId: "getOrganizationRole",
3597
+ description: "Get an organization role. Returns one role a member or invite of the authenticated caller's organization can hold: a built-in role, or one of the organization's custom roles. Returns 404 when the ID is unknown or names another organization's custom role, or when the caller does not belong to an organization; 403 when the caller may not view the organization's roles or organizations are unavailable to the caller.",
2632
3598
  method: "GET",
2633
- path: "/v2/network-volumes/{id}",
3599
+ path: "/v2/organization/roles/{id}",
2634
3600
  params: [
2635
3601
  {
2636
3602
  name: "id",
@@ -2649,20 +3615,31 @@ var generatedTools = [
2649
3615
  properties: {
2650
3616
  id: {
2651
3617
  type: "string",
2652
- description: "Network volume identifier"
3618
+ description: "Role identifier"
2653
3619
  }
2654
3620
  },
2655
3621
  required: ["id"]
2656
3622
  }
2657
3623
  },
2658
3624
  {
2659
- name: "get-organization",
2660
- title: "Get organization",
2661
- operationId: "getOrganization",
2662
- description: "Get your current organization. Returns the authenticated caller's current organization. Organization context comes from the caller's membership; no organization selector is required. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
3625
+ name: "get-organization-role-capabilities",
3626
+ title: "Get organization role capabilities",
3627
+ operationId: "getOrganizationRoleCapabilities",
3628
+ description: "List grantable role capabilities. Returns the capabilities a custom role can grant, grouped by family: each capability is a family and one of its verbs, written `FAMILY:VERB`. This is the set role creation accepts, not filtered to the caller's own access; saving a role also requires the caller to hold every capability it grants. `family` and `verb` narrow the result and apply together; with neither, the whole catalog is returned. Reading the catalog requires organization membership; other callers receive 403.",
2663
3629
  method: "GET",
2664
- path: "/v2/organization",
2665
- params: [],
3630
+ path: "/v2/organization/roles/capabilities",
3631
+ params: [
3632
+ {
3633
+ name: "family",
3634
+ location: "query",
3635
+ explode: false
3636
+ },
3637
+ {
3638
+ name: "verb",
3639
+ location: "query",
3640
+ explode: false
3641
+ }
3642
+ ],
2666
3643
  hasBody: false,
2667
3644
  annotations: {
2668
3645
  readOnlyHint: true,
@@ -2672,7 +3649,22 @@ var generatedTools = [
2672
3649
  },
2673
3650
  inputSchema: {
2674
3651
  type: "object",
2675
- properties: {}
3652
+ properties: {
3653
+ family: {
3654
+ type: "array",
3655
+ items: {
3656
+ type: "string"
3657
+ },
3658
+ description: "Only return these capability families, matched exactly. Comma-separated; a family matching any listed value is returned. An empty value is ignored."
3659
+ },
3660
+ verb: {
3661
+ type: "array",
3662
+ items: {
3663
+ type: "string"
3664
+ },
3665
+ description: "Only return these verbs, matched exactly. Comma-separated. Each family lists only the requested verbs it has, and a family with none of them is left out. An empty value is ignored."
3666
+ }
3667
+ }
2676
3668
  }
2677
3669
  },
2678
3670
  {
@@ -3155,6 +4147,107 @@ var generatedTools = [
3155
4147
  }
3156
4148
  }
3157
4149
  },
4150
+ {
4151
+ name: "list-cost-center-resources",
4152
+ title: "List cost center resources",
4153
+ operationId: "listCostCenterResources",
4154
+ description: "List a cost center's resources. Returns the resources attributed to one cost center, cursor-paginated; an omitted `limit` defaults to 1000. Each entry is the `{type}/{id}` pair `DELETE /v2/cost-centers/{id}/{resourceType}/{resourceId}` takes. A cost center with nothing attributed returns an empty list; one that does not exist or is not the caller's returns 404.",
4155
+ method: "GET",
4156
+ path: "/v2/cost-centers/{id}/resources",
4157
+ params: [
4158
+ {
4159
+ name: "id",
4160
+ location: "path"
4161
+ },
4162
+ {
4163
+ name: "cursor",
4164
+ location: "query"
4165
+ },
4166
+ {
4167
+ name: "limit",
4168
+ location: "query"
4169
+ }
4170
+ ],
4171
+ hasBody: false,
4172
+ annotations: {
4173
+ readOnlyHint: true,
4174
+ destructiveHint: false,
4175
+ idempotentHint: true,
4176
+ openWorldHint: true
4177
+ },
4178
+ inputSchema: {
4179
+ type: "object",
4180
+ properties: {
4181
+ id: {
4182
+ type: "string",
4183
+ minLength: 1,
4184
+ description: "Cost center identifier."
4185
+ },
4186
+ cursor: {
4187
+ type: "string",
4188
+ minLength: 1,
4189
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
4190
+ },
4191
+ limit: {
4192
+ type: "integer",
4193
+ minimum: 1,
4194
+ maximum: 1e3,
4195
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4196
+ }
4197
+ },
4198
+ required: ["id"]
4199
+ }
4200
+ },
4201
+ {
4202
+ name: "list-cost-centers",
4203
+ title: "List cost centers",
4204
+ operationId: "listCostCenters",
4205
+ description: "List cost centers. Returns the caller's cost centers, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
4206
+ method: "GET",
4207
+ path: "/v2/cost-centers",
4208
+ params: [
4209
+ {
4210
+ name: "search",
4211
+ location: "query"
4212
+ },
4213
+ {
4214
+ name: "cursor",
4215
+ location: "query"
4216
+ },
4217
+ {
4218
+ name: "limit",
4219
+ location: "query"
4220
+ }
4221
+ ],
4222
+ hasBody: false,
4223
+ annotations: {
4224
+ readOnlyHint: true,
4225
+ destructiveHint: false,
4226
+ idempotentHint: true,
4227
+ openWorldHint: true
4228
+ },
4229
+ inputSchema: {
4230
+ type: "object",
4231
+ properties: {
4232
+ search: {
4233
+ type: "string",
4234
+ minLength: 1,
4235
+ description: "Case-insensitive substring match against `value`."
4236
+ },
4237
+ cursor: {
4238
+ type: "string",
4239
+ minLength: 1,
4240
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
4241
+ },
4242
+ limit: {
4243
+ type: "integer",
4244
+ minimum: 1,
4245
+ maximum: 1e3,
4246
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4247
+ }
4248
+ }
4249
+ }
4250
+ },
3158
4251
  {
3159
4252
  name: "list-cpu-types",
3160
4253
  title: "List CPU types",
@@ -3685,7 +4778,7 @@ var generatedTools = [
3685
4778
  },
3686
4779
  cloud: {
3687
4780
  $ref: "#/$defs/GpuCloudFilter",
3688
- description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Upstream default when omitted: SECURE.",
4781
+ description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Default when omitted: SECURE.",
3689
4782
  type: "string"
3690
4783
  },
3691
4784
  countryCodes: {
@@ -3730,31 +4823,139 @@ var generatedTools = [
3730
4823
  }
3731
4824
  },
3732
4825
  {
3733
- name: "list-network-volume-billing",
3734
- title: "List network volume billing",
3735
- operationId: "listNetworkVolumeBilling",
3736
- description: "Get network volume billing history. Returns network volume billing history for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Use networkVolumeId to filter to one volume; without it, records are emitted per volume per bucket. Each record includes standard storage, high-performance storage, and total amounts, while metadata reports the resolved query, distinct volume count, and totals across the returned records.",
4826
+ name: "list-network-volume-billing",
4827
+ title: "List network volume billing",
4828
+ operationId: "listNetworkVolumeBilling",
4829
+ description: "Get network volume billing history. Returns network volume billing history for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Use networkVolumeId to filter to one volume; without it, records are emitted per volume per bucket. Each record includes standard storage, high-performance storage, and total amounts, while metadata reports the resolved query, distinct volume count, and totals across the returned records.",
4830
+ method: "GET",
4831
+ path: "/v2/billing/network-volumes",
4832
+ params: [
4833
+ {
4834
+ name: "startTime",
4835
+ location: "query"
4836
+ },
4837
+ {
4838
+ name: "endTime",
4839
+ location: "query"
4840
+ },
4841
+ {
4842
+ name: "bucketSize",
4843
+ location: "query"
4844
+ },
4845
+ {
4846
+ name: "lastN",
4847
+ location: "query"
4848
+ },
4849
+ {
4850
+ name: "networkVolumeId",
4851
+ location: "query"
4852
+ }
4853
+ ],
4854
+ hasBody: false,
4855
+ annotations: {
4856
+ readOnlyHint: true,
4857
+ destructiveHint: false,
4858
+ idempotentHint: true,
4859
+ openWorldHint: true
4860
+ },
4861
+ inputSchema: {
4862
+ type: "object",
4863
+ properties: {
4864
+ startTime: {
4865
+ type: "string",
4866
+ format: "date-time",
4867
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4868
+ },
4869
+ endTime: {
4870
+ type: "string",
4871
+ format: "date-time",
4872
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
4873
+ },
4874
+ bucketSize: {
4875
+ $ref: "#/$defs/BillingBucketSize",
4876
+ description: "Length of each billing time bucket. Defaults to day.",
4877
+ type: "string"
4878
+ },
4879
+ lastN: {
4880
+ type: "integer",
4881
+ minimum: 1,
4882
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
4883
+ },
4884
+ networkVolumeId: {
4885
+ type: "string",
4886
+ description: "Filter to a specific network volume."
4887
+ }
4888
+ },
4889
+ $defs: {
4890
+ BillingBucketSize: {
4891
+ type: "string",
4892
+ enum: ["hour", "day", "week", "month", "year"],
4893
+ default: "day",
4894
+ description: "Length of each billing time bucket.",
4895
+ examples: ["day"]
4896
+ }
4897
+ }
4898
+ }
4899
+ },
4900
+ {
4901
+ name: "list-network-volumes",
4902
+ title: "List network volumes",
4903
+ operationId: "listNetworkVolumes",
4904
+ description: "List network volumes. Returns network volumes owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
4905
+ method: "GET",
4906
+ path: "/v2/network-volumes",
4907
+ params: [
4908
+ {
4909
+ name: "cursor",
4910
+ location: "query"
4911
+ },
4912
+ {
4913
+ name: "limit",
4914
+ location: "query"
4915
+ }
4916
+ ],
4917
+ hasBody: false,
4918
+ annotations: {
4919
+ readOnlyHint: true,
4920
+ destructiveHint: false,
4921
+ idempotentHint: true,
4922
+ openWorldHint: true
4923
+ },
4924
+ inputSchema: {
4925
+ type: "object",
4926
+ properties: {
4927
+ cursor: {
4928
+ type: "string",
4929
+ minLength: 1,
4930
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
4931
+ },
4932
+ limit: {
4933
+ type: "integer",
4934
+ minimum: 1,
4935
+ maximum: 1e3,
4936
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4937
+ }
4938
+ }
4939
+ }
4940
+ },
4941
+ {
4942
+ name: "list-organization-group-members",
4943
+ title: "List organization group members",
4944
+ operationId: "listOrganizationGroupMembers",
4945
+ description: "List members of a group. Members are ordered newest first by membership creation time, with a unique upstream tie-breaker. Returns 404 when the group does not exist in the current organization, or when the caller does not belong to an organization.",
3737
4946
  method: "GET",
3738
- path: "/v2/billing/network-volumes",
4947
+ path: "/v2/organization/groups/{id}/members",
3739
4948
  params: [
3740
4949
  {
3741
- name: "startTime",
3742
- location: "query"
3743
- },
3744
- {
3745
- name: "endTime",
3746
- location: "query"
3747
- },
3748
- {
3749
- name: "bucketSize",
3750
- location: "query"
4950
+ name: "id",
4951
+ location: "path"
3751
4952
  },
3752
4953
  {
3753
- name: "lastN",
4954
+ name: "cursor",
3754
4955
  location: "query"
3755
4956
  },
3756
4957
  {
3757
- name: "networkVolumeId",
4958
+ name: "limit",
3758
4959
  location: "query"
3759
4960
  }
3760
4961
  ],
@@ -3768,50 +4969,38 @@ var generatedTools = [
3768
4969
  inputSchema: {
3769
4970
  type: "object",
3770
4971
  properties: {
3771
- startTime: {
4972
+ id: {
3772
4973
  type: "string",
3773
- format: "date-time",
3774
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4974
+ minLength: 1,
4975
+ description: "Group ID."
3775
4976
  },
3776
- endTime: {
4977
+ cursor: {
3777
4978
  type: "string",
3778
- format: "date-time",
3779
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
3780
- },
3781
- bucketSize: {
3782
- $ref: "#/$defs/BillingBucketSize",
3783
- description: "Length of each billing time bucket. Defaults to day.",
3784
- type: "string"
4979
+ minLength: 1,
4980
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3785
4981
  },
3786
- lastN: {
4982
+ limit: {
3787
4983
  type: "integer",
3788
4984
  minimum: 1,
3789
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
3790
- },
3791
- networkVolumeId: {
3792
- type: "string",
3793
- description: "Filter to a specific network volume."
4985
+ maximum: 100,
4986
+ description: "Page size, 1\u2013100. Defaults to 100 when omitted."
3794
4987
  }
3795
4988
  },
3796
- $defs: {
3797
- BillingBucketSize: {
3798
- type: "string",
3799
- enum: ["hour", "day", "week", "month", "year"],
3800
- default: "day",
3801
- description: "Length of each billing time bucket.",
3802
- examples: ["day"]
3803
- }
3804
- }
4989
+ required: ["id"]
3805
4990
  }
3806
4991
  },
3807
4992
  {
3808
- name: "list-network-volumes",
3809
- title: "List network volumes",
3810
- operationId: "listNetworkVolumes",
3811
- description: "List network volumes. Returns network volumes owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
4993
+ name: "list-organization-groups",
4994
+ title: "List organization groups",
4995
+ operationId: "listOrganizationGroups",
4996
+ description: "List groups in your current organization. Returns groups without their members or roles, ordered by creation time descending and then ID descending for deterministic pagination.",
3812
4997
  method: "GET",
3813
- path: "/v2/network-volumes",
4998
+ path: "/v2/organization/groups",
3814
4999
  params: [
5000
+ {
5001
+ name: "name",
5002
+ location: "query"
5003
+ },
3815
5004
  {
3816
5005
  name: "cursor",
3817
5006
  location: "query"
@@ -3831,6 +5020,10 @@ var generatedTools = [
3831
5020
  inputSchema: {
3832
5021
  type: "object",
3833
5022
  properties: {
5023
+ name: {
5024
+ type: "string",
5025
+ description: "Optional case-insensitive substring of the group name. An empty value applies no filter."
5026
+ },
3834
5027
  cursor: {
3835
5028
  type: "string",
3836
5029
  minLength: 1,
@@ -3839,12 +5032,52 @@ var generatedTools = [
3839
5032
  limit: {
3840
5033
  type: "integer",
3841
5034
  minimum: 1,
3842
- maximum: 1e3,
3843
- description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
5035
+ maximum: 100,
5036
+ description: "Page size, 1\u2013100. Defaults to 100 when omitted."
3844
5037
  }
3845
5038
  }
3846
5039
  }
3847
5040
  },
5041
+ {
5042
+ name: "list-organization-invites",
5043
+ title: "List organization invites",
5044
+ operationId: "listOrganizationInvites",
5045
+ description: "List pending organization invites. Returns the pending invitations to the authenticated caller's organization, newest first. Only unexpired invites are pending: an invite leaves the list once it is accepted, revoked or expired. The list is complete and takes no pagination parameters, because an organization's pending invites are capped. Reading invites requires the organization's invite-management capability, which the built-in admin role holds; other members receive 403. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
5046
+ method: "GET",
5047
+ path: "/v2/organization/invites",
5048
+ params: [],
5049
+ hasBody: false,
5050
+ annotations: {
5051
+ readOnlyHint: true,
5052
+ destructiveHint: false,
5053
+ idempotentHint: true,
5054
+ openWorldHint: true
5055
+ },
5056
+ inputSchema: {
5057
+ type: "object",
5058
+ properties: {}
5059
+ }
5060
+ },
5061
+ {
5062
+ name: "list-organization-roles",
5063
+ title: "List organization roles",
5064
+ operationId: "listOrganizationRoles",
5065
+ description: "List organization roles. Returns every role a member or invite of the authenticated caller's organization can hold: Runpod's built-in roles and the organization's custom roles, oldest first. The list is complete and takes no pagination parameters, because an organization's custom roles are capped. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
5066
+ method: "GET",
5067
+ path: "/v2/organization/roles",
5068
+ params: [],
5069
+ hasBody: false,
5070
+ annotations: {
5071
+ readOnlyHint: true,
5072
+ destructiveHint: false,
5073
+ idempotentHint: true,
5074
+ openWorldHint: true
5075
+ },
5076
+ inputSchema: {
5077
+ type: "object",
5078
+ properties: {}
5079
+ }
5080
+ },
3848
5081
  {
3849
5082
  name: "list-organization-users",
3850
5083
  title: "List organization users",
@@ -4199,6 +5432,47 @@ var generatedTools = [
4199
5432
  }
4200
5433
  }
4201
5434
  },
5435
+ {
5436
+ name: "remove-organization-group-member",
5437
+ title: "Remove organization group member",
5438
+ operationId: "removeOrganizationGroupMember",
5439
+ description: "Remove a member from a group. Removes the organization member from the group. The group and user must belong to the current organization. Returns 404 when the membership is absent, including repeated deletion, or when the group is unknown or belongs to another organization.",
5440
+ method: "DELETE",
5441
+ path: "/v2/organization/groups/{id}/members/{userId}",
5442
+ params: [
5443
+ {
5444
+ name: "id",
5445
+ location: "path"
5446
+ },
5447
+ {
5448
+ name: "userId",
5449
+ location: "path"
5450
+ }
5451
+ ],
5452
+ hasBody: false,
5453
+ annotations: {
5454
+ readOnlyHint: false,
5455
+ destructiveHint: true,
5456
+ idempotentHint: true,
5457
+ openWorldHint: true
5458
+ },
5459
+ inputSchema: {
5460
+ type: "object",
5461
+ properties: {
5462
+ id: {
5463
+ type: "string",
5464
+ minLength: 1,
5465
+ description: "Group ID."
5466
+ },
5467
+ userId: {
5468
+ type: "string",
5469
+ minLength: 1,
5470
+ description: "Organization member user ID."
5471
+ }
5472
+ },
5473
+ required: ["id", "userId"]
5474
+ }
5475
+ },
4202
5476
  {
4203
5477
  name: "revoke-delegation",
4204
5478
  title: "Revoke ECR delegation",
@@ -4279,6 +5553,59 @@ var generatedTools = [
4279
5553
  }
4280
5554
  }
4281
5555
  },
5556
+ {
5557
+ name: "update-cost-center",
5558
+ title: "Update cost center",
5559
+ operationId: "updateCostCenter",
5560
+ description: "Rename a cost center. Renames a cost center. Resources attributed to it are unaffected and follow the new name \u2014 the attribution is by identifier, not by name.",
5561
+ method: "PATCH",
5562
+ path: "/v2/cost-centers/{id}",
5563
+ params: [
5564
+ {
5565
+ name: "id",
5566
+ location: "path"
5567
+ }
5568
+ ],
5569
+ hasBody: true,
5570
+ annotations: {
5571
+ readOnlyHint: false,
5572
+ destructiveHint: false,
5573
+ idempotentHint: true,
5574
+ openWorldHint: true
5575
+ },
5576
+ inputSchema: {
5577
+ type: "object",
5578
+ properties: {
5579
+ id: {
5580
+ type: "string",
5581
+ minLength: 1,
5582
+ description: "Cost center identifier."
5583
+ },
5584
+ body: {
5585
+ $ref: "#/$defs/UpdateCostCenterRequest",
5586
+ type: "object"
5587
+ }
5588
+ },
5589
+ required: ["id", "body"],
5590
+ $defs: {
5591
+ UpdateCostCenterRequest: {
5592
+ type: "object",
5593
+ additionalProperties: false,
5594
+ required: ["value"],
5595
+ properties: {
5596
+ value: {
5597
+ type: "string",
5598
+ minLength: 1,
5599
+ maxLength: 50,
5600
+ pattern: "^\\s*\\S",
5601
+ description: "Leading and trailing whitespace is trimmed.",
5602
+ examples: ["team-ml-eu"]
5603
+ }
5604
+ }
5605
+ }
5606
+ }
5607
+ }
5608
+ },
4282
5609
  {
4283
5610
  name: "update-endpoint",
4284
5611
  title: "Update endpoint",
@@ -4666,19 +5993,130 @@ var generatedTools = [
4666
5993
  }
4667
5994
  }
4668
5995
  }
4669
- ],
4670
- unevaluatedProperties: false
5996
+ ],
5997
+ unevaluatedProperties: false
5998
+ }
5999
+ }
6000
+ }
6001
+ },
6002
+ {
6003
+ name: "update-global-volume",
6004
+ title: "Update global volume",
6005
+ operationId: "updateGlobalVolume",
6006
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
6007
+ method: "PATCH",
6008
+ path: "/v2/global-volumes/{id}",
6009
+ params: [
6010
+ {
6011
+ name: "id",
6012
+ location: "path"
6013
+ }
6014
+ ],
6015
+ hasBody: true,
6016
+ annotations: {
6017
+ readOnlyHint: false,
6018
+ destructiveHint: false,
6019
+ idempotentHint: true,
6020
+ openWorldHint: true
6021
+ },
6022
+ inputSchema: {
6023
+ type: "object",
6024
+ properties: {
6025
+ id: {
6026
+ type: "string",
6027
+ description: "Global volume identifier"
6028
+ },
6029
+ body: {
6030
+ $ref: "#/$defs/UpdateGlobalVolumeRequest",
6031
+ type: "object"
6032
+ }
6033
+ },
6034
+ required: ["id", "body"],
6035
+ $defs: {
6036
+ UpdateGlobalVolumeRequest: {
6037
+ type: "object",
6038
+ additionalProperties: false,
6039
+ minProperties: 1,
6040
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6041
+ properties: {
6042
+ name: {
6043
+ type: "string",
6044
+ minLength: 1,
6045
+ maxLength: 191,
6046
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6047
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
6048
+ examples: ["renamed-volume"]
6049
+ }
6050
+ }
6051
+ }
6052
+ }
6053
+ }
6054
+ },
6055
+ {
6056
+ name: "update-network-volume",
6057
+ title: "Update network volume",
6058
+ operationId: "updateNetworkVolume",
6059
+ description: "Update a network volume. Updates mutable fields on a network volume. Only provided fields are changed. Note: `size` may only increase; attempts to reduce size will be rejected.",
6060
+ method: "PATCH",
6061
+ path: "/v2/network-volumes/{id}",
6062
+ params: [
6063
+ {
6064
+ name: "id",
6065
+ location: "path"
6066
+ }
6067
+ ],
6068
+ hasBody: true,
6069
+ annotations: {
6070
+ readOnlyHint: false,
6071
+ destructiveHint: false,
6072
+ idempotentHint: true,
6073
+ openWorldHint: true
6074
+ },
6075
+ inputSchema: {
6076
+ type: "object",
6077
+ properties: {
6078
+ id: {
6079
+ type: "string",
6080
+ description: "Network volume identifier"
6081
+ },
6082
+ body: {
6083
+ $ref: "#/$defs/UpdateNetworkVolumeRequest",
6084
+ type: "object"
6085
+ }
6086
+ },
6087
+ required: ["id", "body"],
6088
+ $defs: {
6089
+ UpdateNetworkVolumeRequest: {
6090
+ type: "object",
6091
+ additionalProperties: false,
6092
+ minProperties: 1,
6093
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6094
+ properties: {
6095
+ name: {
6096
+ type: "string",
6097
+ minLength: 1,
6098
+ description: "New human-readable name",
6099
+ examples: ["renamed-volume"]
6100
+ },
6101
+ size: {
6102
+ type: "integer",
6103
+ minimum: 10,
6104
+ maximum: 4096,
6105
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
6106
+ examples: [100]
6107
+ }
6108
+ }
4671
6109
  }
4672
6110
  }
4673
6111
  }
4674
6112
  },
4675
6113
  {
4676
- name: "update-global-volume",
4677
- title: "Update global volume",
4678
- operationId: "updateGlobalVolume",
4679
- description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
6114
+ name: "update-organization-group",
6115
+ title: "Update organization group",
6116
+ operationId: "updateOrganizationGroup",
6117
+ description: "Rename a group in your current organization",
4680
6118
  method: "PATCH",
4681
- path: "/v2/global-volumes/{id}",
6119
+ path: "/v2/organization/groups/{id}",
4682
6120
  params: [
4683
6121
  {
4684
6122
  name: "id",
@@ -4697,27 +6135,28 @@ var generatedTools = [
4697
6135
  properties: {
4698
6136
  id: {
4699
6137
  type: "string",
4700
- description: "Global volume identifier"
6138
+ minLength: 1,
6139
+ description: "Group ID."
4701
6140
  },
4702
6141
  body: {
4703
- $ref: "#/$defs/UpdateGlobalVolumeRequest",
6142
+ $ref: "#/$defs/UpdateOrganizationGroupRequest",
4704
6143
  type: "object"
4705
6144
  }
4706
6145
  },
4707
6146
  required: ["id", "body"],
4708
6147
  $defs: {
4709
- UpdateGlobalVolumeRequest: {
6148
+ UpdateOrganizationGroupRequest: {
4710
6149
  type: "object",
4711
6150
  additionalProperties: false,
4712
- minProperties: 1,
4713
- description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6151
+ required: ["name"],
4714
6152
  properties: {
4715
6153
  name: {
4716
6154
  type: "string",
4717
6155
  minLength: 1,
4718
6156
  maxLength: 191,
4719
- description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
4720
- examples: ["renamed-volume"]
6157
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6158
+ description: "Group name with at least one non-whitespace character; surrounding whitespace is trimmed.",
6159
+ examples: ["Research"]
4721
6160
  }
4722
6161
  }
4723
6162
  }
@@ -4725,12 +6164,12 @@ var generatedTools = [
4725
6164
  }
4726
6165
  },
4727
6166
  {
4728
- name: "update-network-volume",
4729
- title: "Update network volume",
4730
- operationId: "updateNetworkVolume",
4731
- description: "Update a network volume. Updates mutable fields on a network volume. Only provided fields are changed. Note: `size` may only increase; attempts to reduce size will be rejected.",
6167
+ name: "update-organization-role",
6168
+ title: "Update organization role",
6169
+ operationId: "updateOrganizationRole",
6170
+ description: "Update a custom organization role. Changes a custom role's name, its capabilities, or both, and returns the role. Sent capabilities replace the whole grant set. Returns 400 for a built-in role, which cannot be changed; 404 when the ID is unknown or names another organization's role; 403 when the caller does not hold every capability the role grants, before or after the change; 409 when the organization already has a role with the new name; and 422 when a capability is unknown or the name is reserved. Updating roles requires the organization's role-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable.",
4732
6171
  method: "PATCH",
4733
- path: "/v2/network-volumes/{id}",
6172
+ path: "/v2/organization/roles/{id}",
4734
6173
  params: [
4735
6174
  {
4736
6175
  name: "id",
@@ -4740,7 +6179,7 @@ var generatedTools = [
4740
6179
  hasBody: true,
4741
6180
  annotations: {
4742
6181
  readOnlyHint: false,
4743
- destructiveHint: false,
6182
+ destructiveHint: true,
4744
6183
  idempotentHint: true,
4745
6184
  openWorldHint: true
4746
6185
  },
@@ -4749,33 +6188,129 @@ var generatedTools = [
4749
6188
  properties: {
4750
6189
  id: {
4751
6190
  type: "string",
4752
- description: "Network volume identifier"
6191
+ description: "Role identifier"
4753
6192
  },
4754
6193
  body: {
4755
- $ref: "#/$defs/UpdateNetworkVolumeRequest",
6194
+ $ref: "#/$defs/UpdateOrganizationRoleRequest",
4756
6195
  type: "object"
4757
6196
  }
4758
6197
  },
4759
6198
  required: ["id", "body"],
4760
6199
  $defs: {
4761
- UpdateNetworkVolumeRequest: {
6200
+ OrganizationRoleCapabilitiesInput: {
6201
+ type: "array",
6202
+ description: "What the role grants, as `FAMILY:VERB` capabilities from `get-organization-role-capabilities`. The caller must hold every capability it lists.",
6203
+ minItems: 1,
6204
+ uniqueItems: true,
6205
+ items: {
6206
+ type: "string",
6207
+ pattern: "^[A-Z0-9_]+:[A-Z0-9_]+$"
6208
+ },
6209
+ examples: [["POD:VIEW"]]
6210
+ },
6211
+ OrganizationRoleName: {
6212
+ type: "string",
6213
+ minLength: 1,
6214
+ maxLength: 191,
6215
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6216
+ description: "Display name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed. It must be unique within the organization and cannot be a built-in role's name.",
6217
+ examples: ["Pod viewer"]
6218
+ },
6219
+ UpdateOrganizationRoleRequest: {
4762
6220
  type: "object",
4763
6221
  additionalProperties: false,
4764
6222
  minProperties: 1,
4765
- description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6223
+ description: "The fields to change; omitted fields are unchanged. `capabilities` replaces the role's whole grant set rather than adding to it.",
4766
6224
  properties: {
4767
6225
  name: {
6226
+ $ref: "#/$defs/OrganizationRoleName"
6227
+ },
6228
+ capabilities: {
6229
+ $ref: "#/$defs/OrganizationRoleCapabilitiesInput"
6230
+ }
6231
+ }
6232
+ }
6233
+ }
6234
+ }
6235
+ },
6236
+ {
6237
+ name: "update-organization-user",
6238
+ title: "Update organization user",
6239
+ operationId: "updateOrganizationUser",
6240
+ description: "Update an organization member. Changes one member of the authenticated caller's organization by user ID: assigns a role, replaces their set of groups, or both, applied together or not at all. Send at least one field; an omitted field is left as it is, and an empty `groups` list removes the member from every group. Updating members requires the organization's member-management capability, which the built-in admin role holds; other members, and callers outside an organization, receive 403, as does a caller for whom organizations are unavailable. Returns 403 when the caller changes their own role or assigns a role granting a capability the caller lacks; 409 when the change would leave the organization without a member in the built-in admin role; and 404 when the user is not a member or the role or a group is unknown to the organization, with the detail naming which.",
6241
+ method: "PATCH",
6242
+ path: "/v2/organization/users/{id}",
6243
+ params: [
6244
+ {
6245
+ name: "id",
6246
+ location: "path"
6247
+ }
6248
+ ],
6249
+ hasBody: true,
6250
+ annotations: {
6251
+ readOnlyHint: false,
6252
+ destructiveHint: true,
6253
+ idempotentHint: true,
6254
+ openWorldHint: true
6255
+ },
6256
+ inputSchema: {
6257
+ type: "object",
6258
+ properties: {
6259
+ id: {
6260
+ type: "string",
6261
+ description: "User identifier"
6262
+ },
6263
+ body: {
6264
+ $ref: "#/$defs/UpdateOrganizationUserRequest",
6265
+ type: "object"
6266
+ }
6267
+ },
6268
+ required: ["id", "body"],
6269
+ $defs: {
6270
+ OrganizationGroupInput: {
6271
+ type: "object",
6272
+ description: "An organization group, by ID.",
6273
+ additionalProperties: false,
6274
+ required: ["id"],
6275
+ properties: {
6276
+ id: {
4768
6277
  type: "string",
4769
6278
  minLength: 1,
4770
- description: "New human-readable name",
4771
- examples: ["renamed-volume"]
6279
+ description: "Group ID.",
6280
+ examples: ["group_id_abc_123"]
6281
+ }
6282
+ }
6283
+ },
6284
+ OrganizationRoleInput: {
6285
+ type: "object",
6286
+ description: "An organization role to assign, by ID.",
6287
+ additionalProperties: false,
6288
+ required: ["id"],
6289
+ properties: {
6290
+ id: {
6291
+ type: "string",
6292
+ minLength: 1,
6293
+ description: "Role ID, as reported on a member or an invite: `builtin:admin`, `builtin:dev`, `builtin:billing`, `builtin:basic`, or a custom role's own ID.",
6294
+ examples: ["builtin:dev"]
6295
+ }
6296
+ }
6297
+ },
6298
+ UpdateOrganizationUserRequest: {
6299
+ type: "object",
6300
+ description: "Changes to a member. Send `role`, `groups`, or both; an omitted field is left as it is.",
6301
+ additionalProperties: false,
6302
+ minProperties: 1,
6303
+ properties: {
6304
+ role: {
6305
+ $ref: "#/$defs/OrganizationRoleInput"
4772
6306
  },
4773
- size: {
4774
- type: "integer",
4775
- minimum: 10,
4776
- maximum: 4096,
4777
- description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4778
- examples: [100]
6307
+ groups: {
6308
+ type: "array",
6309
+ description: "The member's groups after the change: the whole set, by ID. An empty list removes the member from every group.",
6310
+ uniqueItems: true,
6311
+ items: {
6312
+ $ref: "#/$defs/OrganizationGroupInput"
6313
+ }
4779
6314
  }
4780
6315
  }
4781
6316
  }
@@ -4786,7 +6321,7 @@ var generatedTools = [
4786
6321
  name: "update-pod",
4787
6322
  title: "Update Pod",
4788
6323
  operationId: "updatePod",
4789
- description: "Update a pod. Partially updates a pod's configuration. This is a PATCH: only the fields present in the body are changed, and omitted fields are left untouched. Use empty values only when you explicitly mean to clear a field (for example, set `registry` to `null` or set `ports` to `[]`). See `UpdatePodRequest` for the full body. Mutable fields: `name`, `image`, `args`, `disk`, `ports`, `env`, `registry`, `mounts`, `locked`, and `globalNetworking`. Some changes apply immediately while others (e.g. `globalNetworking`) take effect on the pod's next start/restart, as noted on the individual fields. Pods that belong to a Cluster cannot be updated here \u2014 manage them through `/v2/clusters/{id}`. Returns `200` with the full updated pod.",
6324
+ description: "Update a pod. Partially updates a pod's configuration. This is a PATCH: only the fields present in the body are changed, and omitted fields are left untouched. Use empty values only when you explicitly mean to clear a field (for example, set `registry` to `null` or set `ports` to `[]`). See `UpdatePodRequest` for the full body. Mutable fields: `name`, `image`, `args`, `disk`, `ports`, `env`, `registry`, `mounts`, `locked`, and `globalNetworking`. Some changes apply immediately while others (e.g. `globalNetworking`) take effect on the pod's next start/restart, as noted on the individual fields. Volumes can be mounted, moved, or unmounted after creation through `mounts`, one entry per volume; see `UpdatePodMounts` for the rules. Pods that belong to a Cluster cannot be updated here \u2014 manage them through `/v2/clusters/{id}`. Returns `200` with the full updated pod.",
4790
6325
  method: "PATCH",
4791
6326
  path: "/v2/pods/{id}",
4792
6327
  params: [
@@ -4880,21 +6415,6 @@ var generatedTools = [
4880
6415
  }
4881
6416
  }
4882
6417
  },
4883
- BaseMounts: {
4884
- type: "object",
4885
- properties: {
4886
- persistent: {
4887
- $ref: "#/$defs/PersistentMount"
4888
- },
4889
- network: {
4890
- type: "array",
4891
- maxItems: 1,
4892
- items: {
4893
- $ref: "#/$defs/NetworkMount"
4894
- }
4895
- }
4896
- }
4897
- },
4898
6418
  ContainerConfig: {
4899
6419
  allOf: [
4900
6420
  {
@@ -4912,11 +6432,11 @@ var generatedTools = [
4912
6432
  }
4913
6433
  ]
4914
6434
  },
4915
- GlobalMount: {
6435
+ GlobalMountPatch: {
4916
6436
  type: "object",
4917
6437
  required: ["volumeId"],
4918
6438
  additionalProperties: false,
4919
- description: "Reference to a GlobalVolume. Usage-billed, so it has no size. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
6439
+ description: "Mounts, moves, or unmounts the pod's global volume.",
4920
6440
  properties: {
4921
6441
  volumeId: {
4922
6442
  type: "string",
@@ -4925,38 +6445,21 @@ var generatedTools = [
4925
6445
  },
4926
6446
  path: {
4927
6447
  type: "string",
4928
- description: "Mount path inside the container. Optional on create, where a default is chosen for you; a PATCH that omits it leaves the path unchanged.",
6448
+ minLength: 1,
6449
+ description: "Mount path inside the container. Optional; when mounting, a default is chosen for you if omitted.",
4929
6450
  examples: ["/data"]
4930
- }
4931
- }
4932
- },
4933
- Mounts: {
4934
- description: "Storage mounts for the pod: `persistent` (size + path) excludes both `network` and `global`; `network` (volumeId + path) and `global` (volumeId, optional path) take one entry each and can be combined. Every entry needs its full shape. On update, omit the field to leave the mounts as they are; the mount kind and a volume's volumeId cannot change, and mounts cannot be cleared.",
4935
- allOf: [
4936
- {
4937
- $ref: "#/$defs/BaseMounts"
4938
6451
  },
4939
- {
4940
- type: "object",
4941
- properties: {
4942
- global: {
4943
- type: "array",
4944
- maxItems: 1,
4945
- description: "At most one global volume, on GPU and CPU pods, with no data center constraint. Combinable with `network` but not with `persistent` (rejected): upstream drops a persistent disk whenever a volume is attached.",
4946
- items: {
4947
- $ref: "#/$defs/GlobalMount"
4948
- }
4949
- }
4950
- }
6452
+ remove: {
6453
+ type: "boolean",
6454
+ description: "Unmount the volume. Cannot be combined with `path`."
4951
6455
  }
4952
- ],
4953
- unevaluatedProperties: false
6456
+ }
4954
6457
  },
4955
- NetworkMount: {
6458
+ NetworkMountPatch: {
4956
6459
  type: "object",
4957
- required: ["volumeId", "path"],
6460
+ required: ["volumeId"],
4958
6461
  additionalProperties: false,
4959
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
6462
+ description: "Mounts, moves, or unmounts one network volume. Mounting a volume the pod does not have requires `path`.",
4960
6463
  properties: {
4961
6464
  volumeId: {
4962
6465
  type: "string",
@@ -4965,27 +6468,62 @@ var generatedTools = [
4965
6468
  },
4966
6469
  path: {
4967
6470
  type: "string",
4968
- description: "Mount path inside the container. No default \u2014 must be specified explicitly.",
6471
+ minLength: 1,
6472
+ description: "Mount path inside the container. Required when mounting; optional when moving a mounted volume.",
4969
6473
  examples: ["/runpod-volume"]
6474
+ },
6475
+ remove: {
6476
+ type: "boolean",
6477
+ description: "Unmount the volume. Cannot be combined with `path`."
4970
6478
  }
4971
6479
  }
4972
6480
  },
4973
- PersistentMount: {
6481
+ PersistentMountPatch: {
4974
6482
  type: "object",
4975
- required: ["size", "path"],
4976
6483
  additionalProperties: false,
4977
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
6484
+ description: "Adds the pod's volume disk, grows it, or moves it. Adding a disk requires `size`; on a pod that already has one, omitted fields are left as they are. Not available on CPU pods.",
4978
6485
  properties: {
4979
6486
  size: {
4980
6487
  type: "integer",
4981
6488
  minimum: 10,
4982
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
4983
- examples: [20]
6489
+ description: "Volume disk size in GB. Can only grow; a smaller size than the current one is rejected.",
6490
+ examples: [50]
4984
6491
  },
4985
6492
  path: {
4986
6493
  type: "string",
4987
- description: "Mount path inside the container. May be changed via PATCH.",
6494
+ minLength: 1,
6495
+ description: "Mount path inside the container.",
4988
6496
  examples: ["/workspace"]
6497
+ },
6498
+ remove: {
6499
+ type: "boolean",
6500
+ description: "Removes the volume disk. Not supported yet: `remove: true` is rejected."
6501
+ }
6502
+ }
6503
+ },
6504
+ UpdatePodMounts: {
6505
+ type: "object",
6506
+ additionalProperties: false,
6507
+ description: "Changes to a pod's mounts. Each entry names one volume by `volumeId` and changes only that volume; volumes that are not listed, and kinds that are omitted, are left as they are. Omitting `mounts` or sending `{}` changes nothing.",
6508
+ properties: {
6509
+ persistent: {
6510
+ $ref: "#/$defs/PersistentMountPatch"
6511
+ },
6512
+ network: {
6513
+ type: "array",
6514
+ maxItems: 8,
6515
+ description: "Network volumes to mount, move, or unmount. Up to 8 entries, so a request can unmount 4 volumes and mount 4 others; the pod can still hold at most 4 afterwards.",
6516
+ items: {
6517
+ $ref: "#/$defs/NetworkMountPatch"
6518
+ }
6519
+ },
6520
+ global: {
6521
+ type: "array",
6522
+ maxItems: 2,
6523
+ description: "Global volumes to mount, move, or unmount. Up to 2 entries, so a request can swap the global volume; the pod can still hold at most 1 afterwards.",
6524
+ items: {
6525
+ $ref: "#/$defs/GlobalMountPatch"
6526
+ }
4989
6527
  }
4990
6528
  }
4991
6529
  },
@@ -5006,7 +6544,7 @@ var generatedTools = [
5006
6544
  description: "Lock the pod (true) or unlock it (false). Locked pods cannot be stopped or reset."
5007
6545
  },
5008
6546
  mounts: {
5009
- $ref: "#/$defs/Mounts"
6547
+ $ref: "#/$defs/UpdatePodMounts"
5010
6548
  },
5011
6549
  name: {
5012
6550
  type: "string",
@@ -5253,7 +6791,7 @@ var generatedTools = [
5253
6791
  size: {
5254
6792
  type: "integer",
5255
6793
  minimum: 10,
5256
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
6794
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
5257
6795
  examples: [20]
5258
6796
  },
5259
6797
  path: {
@@ -5514,33 +7052,53 @@ var CREDENTIAL_MANAGEMENT = "Manages stored credentials; OpenAI restricts plugin
5514
7052
  var CREDENTIAL_INPUT = "Takes a secret or password as input; directories forbid collecting credentials.";
5515
7053
  var ALP = "Collects conversation-derived data beyond the task; the directory profile never serves it.";
5516
7054
  var DIRECTORY_TOOLS = {
7055
+ "add-organization-group-member": IN,
7056
+ "attach-cost-center-resource": IN,
5517
7057
  "cancel-job": IN,
5518
7058
  "create-cluster": IN,
7059
+ "create-cost-center": IN,
5519
7060
  "create-delegation": IN,
5520
7061
  "create-endpoint": IN,
5521
7062
  "create-global-volume": IN,
5522
7063
  "create-network-volume": IN,
7064
+ "create-organization-group": IN,
7065
+ "create-organization-invite": IN,
7066
+ "create-organization-role": IN,
5523
7067
  "create-pod": IN,
5524
7068
  "create-template": IN,
5525
7069
  "delete-cluster": IN,
7070
+ "delete-cost-center": IN,
5526
7071
  "delete-endpoint": IN,
5527
7072
  "delete-global-volume": IN,
5528
7073
  "delete-network-volume": IN,
7074
+ "delete-organization-group": IN,
7075
+ "delete-organization-invite": IN,
7076
+ "delete-organization-role": IN,
7077
+ "delete-organization-user": IN,
5529
7078
  "delete-pod": IN,
5530
7079
  "delete-template": IN,
5531
7080
  "deploy-hub-repo": IN,
7081
+ "detach-cost-center-resource": IN,
5532
7082
  "endpoint-health": IN,
5533
7083
  "get-capacity": IN,
5534
7084
  "get-cluster": IN,
7085
+ "get-cost-center": IN,
5535
7086
  "get-cpu-type": IN,
5536
7087
  "get-data-center": IN,
5537
7088
  "get-endpoint": IN,
5538
7089
  "get-endpoint-build": IN,
7090
+ "get-endpoint-cold-start-metrics": IN,
7091
+ "get-endpoint-request-metrics": IN,
7092
+ "get-endpoint-worker-metrics": IN,
5539
7093
  "get-global-volume": IN,
5540
7094
  "get-gpu-type": IN,
5541
7095
  "get-job-status": IN,
5542
7096
  "get-network-volume": IN,
5543
7097
  "get-organization": IN,
7098
+ "get-organization-group": IN,
7099
+ "get-organization-invite": IN,
7100
+ "get-organization-role": IN,
7101
+ "get-organization-role-capabilities": IN,
5544
7102
  "get-organization-user": IN,
5545
7103
  "get-pod": IN,
5546
7104
  "get-ssh-keys": IN,
@@ -5550,6 +7108,8 @@ var DIRECTORY_TOOLS = {
5550
7108
  "list-cluster-billing": IN,
5551
7109
  "list-cluster-pods": IN,
5552
7110
  "list-clusters": IN,
7111
+ "list-cost-center-resources": IN,
7112
+ "list-cost-centers": IN,
5553
7113
  "list-cpu-types": IN,
5554
7114
  "list-data-centers": IN,
5555
7115
  "list-delegations": IN,
@@ -5563,6 +7123,10 @@ var DIRECTORY_TOOLS = {
5563
7123
  "list-hub-repos": IN,
5564
7124
  "list-network-volume-billing": IN,
5565
7125
  "list-network-volumes": IN,
7126
+ "list-organization-group-members": IN,
7127
+ "list-organization-groups": IN,
7128
+ "list-organization-invites": IN,
7129
+ "list-organization-roles": IN,
5566
7130
  "list-organization-users": IN,
5567
7131
  "list-pod-billing": IN,
5568
7132
  "list-pods": IN,
@@ -5572,6 +7136,7 @@ var DIRECTORY_TOOLS = {
5572
7136
  "list-templates": IN,
5573
7137
  "lookup-concept": IN,
5574
7138
  "purge-endpoint-queue": IN,
7139
+ "remove-organization-group-member": IN,
5575
7140
  "retry-job": IN,
5576
7141
  "revoke-delegation": IN,
5577
7142
  "search-concepts": IN,
@@ -5581,9 +7146,13 @@ var DIRECTORY_TOOLS = {
5581
7146
  "stream-pod-logs": IN,
5582
7147
  "stream-worker-logs": IN,
5583
7148
  "update-cluster": IN,
7149
+ "update-cost-center": IN,
5584
7150
  "update-endpoint": IN,
5585
7151
  "update-global-volume": IN,
5586
7152
  "update-network-volume": IN,
7153
+ "update-organization-group": IN,
7154
+ "update-organization-role": IN,
7155
+ "update-organization-user": IN,
5587
7156
  "update-pod": IN,
5588
7157
  "update-ssh-keys": IN,
5589
7158
  "update-template": IN,
@@ -8404,23 +9973,40 @@ function createRunpodClient(options = {}) {
8404
9973
  // src/specgen/clients/sse.ts
8405
9974
  var LOG_STREAM_DEFAULT_WAIT_MS = 5e3;
8406
9975
  var LOG_STREAM_MAX_BYTES = 256 * 1024;
9976
+ var LOG_STREAM_CONNECT_TIMEOUT_MS = 1e4;
9977
+ var LOG_SNAPSHOT_MAX_CHARS = 32e3;
8407
9978
  function createSseReader(options = {}) {
8408
9979
  const apiKey = options.apiKey ?? process.env.RUNPOD_API_KEY;
8409
9980
  const fetchImpl = options.fetchImpl ?? fetch;
9981
+ const connectTimeoutMs = options.connectTimeoutMs ?? LOG_STREAM_CONNECT_TIMEOUT_MS;
8410
9982
  return async (url, opts) => {
8411
9983
  if (!apiKey) throw missingKeyError();
8412
9984
  const chunks = [];
8413
9985
  let bytes = 0;
8414
9986
  let truncated = false;
8415
9987
  let streamEstablished = false;
9988
+ const connect = new AbortController();
9989
+ const connectTimer = setTimeout(
9990
+ () => connect.abort(
9991
+ new DOMException(
9992
+ `Runpod log stream sent no headers within ${connectTimeoutMs}ms`,
9993
+ "TimeoutError"
9994
+ )
9995
+ ),
9996
+ connectTimeoutMs
9997
+ );
8416
9998
  try {
8417
- const response = await createDeadlineFetch(fetchImpl, opts.maxWaitMs)(url, {
9999
+ const response = await createDeadlineFetch(
10000
+ fetchImpl,
10001
+ connectTimeoutMs + opts.maxWaitMs
10002
+ )(url, {
8418
10003
  method: "GET",
8419
10004
  headers: {
8420
10005
  Authorization: `Bearer ${apiKey}`,
8421
10006
  Accept: "text/event-stream"
8422
- }
8423
- });
10007
+ },
10008
+ signal: connect.signal
10009
+ }).finally(() => clearTimeout(connectTimer));
8424
10010
  if (!response.ok) {
8425
10011
  const body = await response.text().catch(() => "");
8426
10012
  throw new HttpError(
@@ -8431,14 +10017,31 @@ function createSseReader(options = {}) {
8431
10017
  }
8432
10018
  streamEstablished = true;
8433
10019
  if (response.body) {
8434
- for await (const chunk of response.body) {
8435
- const boundedChunk = chunk.subarray(0, opts.maxBytes - bytes);
8436
- chunks.push(boundedChunk);
8437
- bytes += boundedChunk.length;
8438
- if (bytes >= opts.maxBytes) {
8439
- truncated = true;
8440
- break;
10020
+ const reader = response.body.getReader();
10021
+ let timer;
10022
+ const windowClosed = new Promise((resolve) => {
10023
+ timer = setTimeout(() => resolve(null), opts.maxWaitMs);
10024
+ });
10025
+ try {
10026
+ for (; ; ) {
10027
+ const read = reader.read();
10028
+ read.catch(() => {
10029
+ });
10030
+ const next = await Promise.race([read, windowClosed]);
10031
+ if (next === null || next.done) break;
10032
+ const boundedChunk = next.value.subarray(0, opts.maxBytes - bytes);
10033
+ chunks.push(boundedChunk);
10034
+ bytes += boundedChunk.length;
10035
+ if (bytes >= opts.maxBytes) {
10036
+ truncated = true;
10037
+ break;
10038
+ }
8441
10039
  }
10040
+ } finally {
10041
+ clearTimeout(timer);
10042
+ void reader.cancel().catch(() => {
10043
+ });
10044
+ reader.releaseLock();
8442
10045
  }
8443
10046
  }
8444
10047
  } catch (err) {
@@ -8464,7 +10067,80 @@ async function collectLogSnapshot(reader, logsUrl, params) {
8464
10067
  for await (const event of iterateLogEvents(new Response(raw).body)) {
8465
10068
  items.push(event.data);
8466
10069
  }
8467
- return { items, count: items.length, truncated };
10070
+ return fitSnapshot(items, truncated);
10071
+ }
10072
+ var serializedLength = (snapshot) => JSON.stringify(snapshot, null, 2).length;
10073
+ var LOG_ENTRY_MAX_CHARS = 16e3;
10074
+ var CLIPPABLE_KEYS = ["line", "raw"];
10075
+ function clipText(text, keep) {
10076
+ const code = text.charCodeAt(keep - 1);
10077
+ if (keep > 0 && code >= 55296 && code <= 56319) keep--;
10078
+ return `${text.slice(0, keep)}\u2026 [clipped ${text.length - keep} chars]`;
10079
+ }
10080
+ function clipEntry(entry) {
10081
+ let clipped = false;
10082
+ const out = { ...entry };
10083
+ for (const key of CLIPPABLE_KEYS) {
10084
+ const text = out[key];
10085
+ if (typeof text === "string" && text.length > LOG_ENTRY_MAX_CHARS) {
10086
+ out[key] = clipText(text, LOG_ENTRY_MAX_CHARS);
10087
+ clipped = true;
10088
+ }
10089
+ }
10090
+ return { entry: out, clipped };
10091
+ }
10092
+ function fitEntry(entry, maxChars) {
10093
+ const size = (e) => serializedLength({ items: [e], count: 1, truncated: false });
10094
+ const out = { ...entry };
10095
+ const keys = Object.keys(out).filter((key) => key !== "ts" && typeof out[key] === "string").sort((a, b) => out[b].length - out[a].length);
10096
+ for (const key of keys) {
10097
+ if (size(out) <= maxChars) break;
10098
+ const text = entry[key];
10099
+ let lo = 0;
10100
+ let hi = Math.min(text.length, LOG_ENTRY_MAX_CHARS);
10101
+ while (lo < hi) {
10102
+ const mid = Math.ceil((lo + hi) / 2);
10103
+ if (size({ ...out, [key]: clipText(text, mid) }) <= maxChars)
10104
+ lo = mid;
10105
+ else hi = mid - 1;
10106
+ }
10107
+ out[key] = clipText(text, lo);
10108
+ }
10109
+ return out;
10110
+ }
10111
+ var entryTs = (entry) => entry?.ts;
10112
+ function fitSnapshot(items, truncated, maxChars = LOG_SNAPSHOT_MAX_CHARS) {
10113
+ let cut = truncated;
10114
+ const entries = items.map((item) => {
10115
+ const { entry, clipped } = clipEntry(item);
10116
+ if (clipped) cut = true;
10117
+ return entry;
10118
+ });
10119
+ const snapshot = (n, isCut) => ({
10120
+ items: entries.slice(0, n),
10121
+ count: n,
10122
+ truncated: isCut
10123
+ });
10124
+ const whole = snapshot(entries.length, cut);
10125
+ if (entries.length === 0 || serializedLength(whole) <= maxChars) return whole;
10126
+ if (serializedLength(snapshot(1, true)) > maxChars) {
10127
+ entries[0] = fitEntry(items[0], maxChars);
10128
+ if (entries.length === 1) return snapshot(1, true);
10129
+ }
10130
+ let lo = 1;
10131
+ let hi = entries.length - 1;
10132
+ while (lo < hi) {
10133
+ const mid = Math.ceil((lo + hi) / 2);
10134
+ if (serializedLength(snapshot(mid, true)) <= maxChars) lo = mid;
10135
+ else hi = mid - 1;
10136
+ }
10137
+ let keep = lo;
10138
+ const omittedTs = entryTs(entries[keep]);
10139
+ if (omittedTs !== void 0) {
10140
+ while (keep > 0 && entryTs(entries[keep - 1]) === omittedTs) keep--;
10141
+ if (keep === 0) keep = lo;
10142
+ }
10143
+ return snapshot(keep, true);
8468
10144
  }
8469
10145
 
8470
10146
  // src/specgen/tools/logs.ts
@@ -8482,13 +10158,13 @@ var logStreamProperties = {
8482
10158
  },
8483
10159
  since: {
8484
10160
  type: "string",
8485
- description: "RFC3339 timestamp to resume from; when set, `tail` is ignored."
10161
+ description: "RFC3339 timestamp to resume after (exclusive). Replays container lines only; system lines stream live from the call. When set, `tail` is ignored."
8486
10162
  },
8487
10163
  maxWaitMs: {
8488
10164
  type: "integer",
8489
10165
  minimum: 500,
8490
10166
  maximum: 3e4,
8491
- description: "How long to read the stream, in ms (default 5000, max 30000)"
10167
+ description: "How long to read the stream once it opens, in ms (default 5000, max 30000)"
8492
10168
  }
8493
10169
  };
8494
10170
  function baseUrl() {
@@ -8514,7 +10190,7 @@ var streamPodLogs = {
8514
10190
  title: "Stream Pod logs",
8515
10191
  holdsResponse: true,
8516
10192
  annotations: readOnly,
8517
- description: "Read a bounded snapshot of a Pod's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Use `since` to resume from a timestamp.",
10193
+ description: "Read a bounded snapshot of a Pod's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the size cap cut the output. For later container lines, call again with `since` set to the last container line's `ts`; `since` does not replay system lines, so for those request a smaller `tail`.",
8518
10194
  inputSchema: {
8519
10195
  type: "object",
8520
10196
  properties: {
@@ -8536,10 +10212,10 @@ var streamPodLogs = {
8536
10212
  var streamWorkerLogs = {
8537
10213
  name: "stream-worker-logs",
8538
10214
  title: "Stream worker logs",
8539
- directoryDescription: "Read a bounded snapshot of a Serverless worker's logs (container, system, or both). Holds the live stream open for maxWaitMs and returns the parsed log lines; truncated: true means the byte cap cut the output. A worker whose container keeps failing to start can report RUNNING or THROTTLED while its logs show the failure.",
10215
+ directoryDescription: "Read a bounded snapshot of a Serverless worker's logs (container, system, or both). Holds the live stream open for maxWaitMs and returns the parsed log lines; truncated: true means the size cap cut the output. A worker whose container keeps failing to start can report RUNNING or THROTTLED while its logs show the failure.",
8540
10216
  holdsResponse: true,
8541
10217
  annotations: readOnly,
8542
- description: "Read a bounded snapshot of a Serverless worker's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Get worker IDs from list-endpoint-workers. Use this whenever a job stays IN_QUEUE while workers exist, whatever status they report \u2014 a crash-looping container often still shows RUNNING or THROTTLED with unhealthy 0, so these logs, not the worker status, are what identify a crash loop.",
10218
+ description: "Read a bounded snapshot of a Serverless worker's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the size cap cut the output. For later container lines, call again with `since` set to the last container line's `ts`; `since` does not replay system lines, so for those request a smaller `tail`. Get worker IDs from list-endpoint-workers. Use this whenever a job stays IN_QUEUE while workers exist, whatever status they report \u2014 a crash-looping container often still shows RUNNING or THROTTLED with unhealthy 0, so these logs, not the worker status, are what identify a crash loop.",
8543
10219
  inputSchema: {
8544
10220
  type: "object",
8545
10221
  properties: {
@@ -8683,6 +10359,24 @@ var listPublicEndpoints = {
8683
10359
  // src/specgen/tools/knowledge.ts
8684
10360
  var import_plugin_knowledge = require("@runpod/plugin-knowledge");
8685
10361
  var knowledge = (0, import_plugin_knowledge.loadKnowledge)();
10362
+ var resolveGuideId = (id) => knowledge.aliases?.[id] ?? id;
10363
+ var hiddenRules = new Set(
10364
+ (process.env.KNOWLEDGE_HIDDEN_RULES ?? "").split(",").map((id) => id.trim()).filter(Boolean)
10365
+ );
10366
+ var concepts = knowledge.concepts.map((concept) => ({
10367
+ ...concept,
10368
+ rules: concept.rules.filter((rule) => !hiddenRules.has(rule.id))
10369
+ }));
10370
+ for (const id of hiddenRules) {
10371
+ if (!knowledge.concepts.some(
10372
+ (concept) => concept.rules.some((rule) => rule.id === id)
10373
+ ))
10374
+ console.warn("knowledge_hidden_rule_unknown", {
10375
+ id,
10376
+ plugin_version: knowledge.version
10377
+ });
10378
+ }
10379
+ var visibleRuleIds = (ids) => ids.filter((id) => !hiddenRules.has(id));
8686
10380
  var GUIDE_KINDS = ["skill", "reference", "golden-path"];
8687
10381
  var STOPWORDS = new Set(
8688
10382
  "a an and are as at be by can do does for from how i in is it my of on or the to what when why with you your".split(
@@ -8725,7 +10419,7 @@ var guideSummary = ({
8725
10419
  lanes,
8726
10420
  mcp,
8727
10421
  needs_shell,
8728
- concepts
10422
+ concepts: concepts2
8729
10423
  }) => ({
8730
10424
  id,
8731
10425
  kind,
@@ -8734,7 +10428,7 @@ var guideSummary = ({
8734
10428
  lanes,
8735
10429
  mcp,
8736
10430
  needs_shell,
8737
- concepts
10431
+ concepts: concepts2
8738
10432
  });
8739
10433
  function linkedGuides(conceptId) {
8740
10434
  const byId = new Map(knowledge.guides.map((guide) => [guide.id, guide]));
@@ -8746,19 +10440,20 @@ function linkedGuides(conceptId) {
8746
10440
  seen.add(link.from);
8747
10441
  const guide = byId.get(link.from);
8748
10442
  if (!guide) continue;
10443
+ const rules = link.rules && visibleRuleIds(link.rules);
8749
10444
  if (guide.kind === "golden-path")
8750
10445
  examples.push({ id: guide.id, title: guide.title, mcp: guide.mcp });
8751
10446
  else
8752
10447
  docs.push({
8753
10448
  id: guide.id,
8754
10449
  title: guide.title,
8755
- ...link.rules ? { rules: link.rules } : {}
10450
+ ...rules?.length ? { rules } : {}
8756
10451
  });
8757
10452
  }
8758
10453
  return { examples, docs };
8759
10454
  }
8760
10455
  var conceptIndex = /* @__PURE__ */ new Map();
8761
- for (const concept of knowledge.concepts) {
10456
+ for (const concept of concepts) {
8762
10457
  for (const key of [concept.id, concept.name, ...concept.aliases]) {
8763
10458
  if (!conceptIndex.has(key.toLowerCase()))
8764
10459
  conceptIndex.set(key.toLowerCase(), concept);
@@ -8792,7 +10487,7 @@ function resolveConcept(input) {
8792
10487
  return best && !tied ? { concept: best.concept, matchedAs: best.key } : void 0;
8793
10488
  }
8794
10489
  var ruleIndex = /* @__PURE__ */ new Map();
8795
- for (const concept of knowledge.concepts) {
10490
+ for (const concept of concepts) {
8796
10491
  for (const rule of concept.rules) ruleIndex.set(rule.id, { rule, concept });
8797
10492
  }
8798
10493
  var source = {
@@ -8841,7 +10536,9 @@ var readGuide = {
8841
10536
  title: "Read guide",
8842
10537
  annotations: bundledRead,
8843
10538
  recordedArguments: ["id"],
8844
- recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
10539
+ recordedGuideKind: (args) => knowledge.guides.find(
10540
+ (guide) => guide.id === resolveGuideId(String(args.id).trim())
10541
+ )?.kind,
8845
10542
  description: 'Read one official Runpod guide in full, by the id list-guides returns (for example "runpod-usage/storage" or "golden-path/06-dev-pod").',
8846
10543
  inputSchema: {
8847
10544
  type: "object",
@@ -8853,7 +10550,9 @@ var readGuide = {
8853
10550
  },
8854
10551
  handler: async (_ctx, args) => {
8855
10552
  const id = String(args.id).trim();
8856
- const guide = knowledge.guides.find((candidate) => candidate.id === id);
10553
+ const guide = knowledge.guides.find(
10554
+ (candidate) => candidate.id === resolveGuideId(id)
10555
+ );
8857
10556
  if (!guide) {
8858
10557
  const close = knowledge.guides.filter(
8859
10558
  (candidate) => candidate.id.includes(id) || id.includes(candidate.id)
@@ -8911,7 +10610,7 @@ var lookupConcept = {
8911
10610
  };
8912
10611
  }
8913
10612
  const { concept, matchedAs } = resolved;
8914
- const pointedAtBy = knowledge.concepts.flatMap((other) => [
10613
+ const pointedAtBy = concepts.flatMap((other) => [
8915
10614
  ...other.is_a === concept.id ? [{ concept: other.id, type: "is_a" }] : [],
8916
10615
  ...other.part_of === concept.id ? [{ concept: other.id, type: "part_of" }] : [],
8917
10616
  ...other.relations.filter((relation) => relation.target === concept.id).map((relation) => ({ concept: other.id, type: relation.type }))
@@ -8925,6 +10624,14 @@ var lookupConcept = {
8925
10624
  ...source,
8926
10625
  ...matchedAs ? { matched_as: matchedAs } : {},
8927
10626
  ...concept,
10627
+ steps: concept.steps.map((step) => ({
10628
+ ...step,
10629
+ rules: visibleRuleIds(step.rules)
10630
+ })),
10631
+ rules: concept.rules.map((rule) => ({
10632
+ ...rule,
10633
+ see: visibleRuleIds(rule.see)
10634
+ })),
8928
10635
  pointed_at_by: pointedAtBy,
8929
10636
  linked_rules: linkedRules,
8930
10637
  ...linkedGuides(concept.id)
@@ -9000,6 +10707,12 @@ var guideResources = knowledge.guides.map((guide) => ({
9000
10707
  mimeType: "text/markdown",
9001
10708
  text: guide.body
9002
10709
  }));
10710
+ var findGuideResource = (resources, uri) => {
10711
+ const id = uri.startsWith(GUIDE_URI_PREFIX) ? resolveGuideId(uri.slice(GUIDE_URI_PREFIX.length)) : void 0;
10712
+ return resources.find(
10713
+ (resource) => resource.uri === uri || resource.uri === GUIDE_URI_PREFIX + id
10714
+ );
10715
+ };
9003
10716
 
9004
10717
  // src/specgen/prose.ts
9005
10718
  var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
@@ -9183,7 +10896,7 @@ function words2(name) {
9183
10896
 
9184
10897
  // src/server.ts
9185
10898
  var SERVER_NAME = "Runpod API Server";
9186
- var SERVER_VERSION = true ? "4.9.1" : "dev";
10899
+ var SERVER_VERSION = true ? "4.10.1" : "dev";
9187
10900
 
9188
10901
  // src/specgen/analytics.ts
9189
10902
  var import_node_crypto = require("crypto");
@@ -9251,6 +10964,7 @@ function captureToolCall(apiKey, event) {
9251
10964
  server_version: event.serverVersion,
9252
10965
  ...event.clientName ? { client_name: event.clientName } : {},
9253
10966
  ...event.guideKind ? { guide_kind: event.guideKind } : {},
10967
+ ...event.errorClass ? { error_class: event.errorClass } : {},
9254
10968
  ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
9255
10969
  ...event.profile ? { profile: event.profile } : {},
9256
10970
  ...args
@@ -9874,7 +11588,7 @@ These tools manage infrastructure only. They do not do SSH sessions, file transf
9874
11588
 
9875
11589
  OFFICIAL GUIDES AND FACTS. list-guides and read-guide serve the official Runpod plugin's skills, reference docs and live-verified golden paths, so you have them even without the plugin installed. For a multi-step or billable task, look for a matching golden path first. lookup-concept and search-concepts answer exact questions about how Runpod works (what a stopped pod keeps, where a volume lives) with the public source for each fact.
9876
11590
 
9877
- PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook carrying its procedure, pitfalls and report format. Before the FIRST Runpod tool call of a session, load the plugin's runpod-mcp skill if you have it, otherwise read-guide runpod-mcp (also the runpod://guides/runpod-mcp resource): it routes the request to a journey guide \u2014 deploying an endpoint reads serverless-deploy, diagnosing a broken pod reads pod-doctor, cost questions read cost-audit, and so on \u2014 and states the answer contract. A reply produced without the routed playbook loaded is out of contract.
11591
+ PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook carrying its procedure, pitfalls and report format. Before the FIRST Runpod tool call of a session, load the plugin's runpod-mcp skill if you have it, otherwise read-guide runpod-mcp (also the runpod://guides/runpod-mcp resource): it routes the request to a journey guide \u2014 deploying an endpoint reads runpod-serverless-deploy, diagnosing a broken pod reads runpod-pod-doctor, cost questions read runpod-cost-audit, and so on \u2014 and states the answer contract. A reply produced without the routed playbook loaded is out of contract.
9878
11592
 
9879
11593
  THE OFFICIAL RUNPOD PLUGIN. Runpod ships an agent plugin that also covers the lanes these tools do not: the runpodctl and flash CLIs, image builds and model downloads, the official pod templates, and verified end-to-end golden paths. Check once per session whether skills named runpod, runpod-usage, or runpodctl are available to you \u2014 read what you already have, never shell out to look. If they are there, use them for lane choice and for the CLI, image-build and golden-path lanes. For work done with these tools, the journey playbook is the procedure, wherever it is served from: the plugin's runpod-mcp skill and the journey skills it routes to when you have them, otherwise the same playbooks through read-guide here. If you do not see them, recommend the plugin to the user, and recommend updating to the latest if what they have is missing something you need. Point them at https://github.com/runpod/runpod-plugins-official \u2014 its README carries the install, update, and clone commands for each client, so read it yourself when you need the exact one rather than guessing a command. This is a recommendation, never a prerequisite: make it once, briefly, and get on with the task. Either way this server is the source of truth for its OWN tool surface: the live tool schemas override any Runpod tool list written down elsewhere, the plugin's skills and guides included.
9880
11594
 
@@ -9935,9 +11649,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9935
11649
  resourceTemplates: []
9936
11650
  }));
9937
11651
  server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
9938
- const guide = servedGuides.find(
9939
- (candidate) => candidate.uri === request.params.uri
9940
- );
11652
+ const guide = findGuideResource(servedGuides, request.params.uri);
9941
11653
  if (opts.analytics) {
9942
11654
  captureResourceCall(ctx.apiKey, {
9943
11655
  method: "resources/read",
@@ -9974,6 +11686,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9974
11686
  ok: false,
9975
11687
  status: 429,
9976
11688
  durationMs: 0,
11689
+ errorClass: "rate_limited",
9977
11690
  ...verdict.reason ? { blockedBy: verdict.reason } : {},
9978
11691
  profile,
9979
11692
  ...opts.analytics
@@ -9999,6 +11712,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9999
11712
  const generated = servedGeneratedTools.find(
10000
11713
  (tool) => tool.name === request.params.name
10001
11714
  );
11715
+ const curatedArgs = curated ? withoutNullOptionals(curated.inputSchema, args) : args;
10002
11716
  const omitted = directory ? directoryOmittedInputs(request.params.name).filter((key) => key in args) : [];
10003
11717
  const rejected = omitted.length ? {
10004
11718
  ok: false,
@@ -10008,10 +11722,10 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
10008
11722
  }
10009
11723
  } : curated && !curated.lenientArguments ? validateArguments(
10010
11724
  curated.inputSchema,
10011
- args
11725
+ curatedArgs
10012
11726
  ) : null;
10013
11727
  const result = rejected ?? await runTool(
10014
- () => curated ? curated.handler(ctx, args) : dispatchTool(generated, request.params.name, args)
11728
+ () => curated ? curated.handler(ctx, curatedArgs) : dispatchTool(generated, request.params.name, args)
10015
11729
  );
10016
11730
  logToolCall({
10017
11731
  tool: request.params.name,
@@ -10026,6 +11740,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
10026
11740
  ok: result.ok,
10027
11741
  status: result.status,
10028
11742
  durationMs: Date.now() - startedAt,
11743
+ ...result.ok ? {} : { errorClass: errorClass(result) },
10029
11744
  // The directory profile records which tool ran, never what was asked.
10030
11745
  ...directory ? {} : {
10031
11746
  args: recordedArgumentValues(curated?.recordedArguments, args),
@@ -10065,24 +11780,50 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
10065
11780
  var SHAPE_HINT = "Input shape mismatch: check this tool's schema for required fields. For job payloads, the worker's release config defines the expected input object \u2014 fix the payload and retry; don't abandon the task.";
10066
11781
  var DETAIL_HINTS = [
10067
11782
  [
10068
- /no longer any instances available|not enough free GPUs/i,
10069
- "Capacity, not input: the request is valid but no machine can take it right now. Don't change the payload shape. Retry with another GPU type, data center, cloud, CUDA version or count, or try again later. For a stopped Pod, its host's GPUs are taken; that Pod can only start once they free up."
11783
+ /no longer any instances available|no instances currently available|not enough free GPUs/i,
11784
+ "Capacity, not input: the request is valid but no machine can take it right now. Don't change the payload shape. Retry with another GPU type, data center, cloud, CUDA version or count, or try again later. For a stopped Pod, its host's GPUs are taken; that Pod can only start once they free up.",
11785
+ "capacity"
10070
11786
  ],
10071
11787
  [
10072
11788
  /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
10073
- "Account balance, not input: this operation needs funds on the account. Ask the user to add credit in the Runpod console; list-billing shows recent spend."
11789
+ "Account balance, not input: this operation needs funds on the account. Ask the user to add credit in the Runpod console; list-billing shows recent spend.",
11790
+ "balance"
10074
11791
  ],
10075
11792
  [
10076
11793
  /mutually exclusive|cannot be combined with/i,
10077
- "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
11794
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else.",
11795
+ "conflicting_args"
11796
+ ],
11797
+ // A 400 with a bare "Unauthorized" detail from a write the same key can
11798
+ // otherwise make is upstream's answer for an unfunded account.
11799
+ [
11800
+ /^\s*unauthorized\s*$/i,
11801
+ "Upstream refused this write. With a key that can read, this usually means the account has no balance: ask the user to check their balance in the Runpod console before retrying.",
11802
+ "balance"
10078
11803
  ]
10079
11804
  ];
11805
+ function errorClass(result) {
11806
+ const { status } = result;
11807
+ const payload = asRecord(result.payload) ?? { error: result.payload };
11808
+ const detail = detailText(payload);
11809
+ if (status === 400 && detail) {
11810
+ for (const [pattern, , cls] of DETAIL_HINTS)
11811
+ if (pattern.test(detail)) return cls;
11812
+ }
11813
+ if (status === 400 || status === 422) return "validation";
11814
+ if (status === 401) return "auth";
11815
+ if (status === 402) return "balance";
11816
+ if (status === 403) return "permission";
11817
+ if (status === 404) return "not_found";
11818
+ if (status === 429) return "rate_limited";
11819
+ if (status === 504) return "timeout";
11820
+ if (status >= 500) return "server";
11821
+ return "other";
11822
+ }
10080
11823
  function errorHint(status, detail) {
10081
11824
  if (status === 400 && detail) {
10082
11825
  for (const [pattern, hint] of DETAIL_HINTS)
10083
11826
  if (pattern.test(detail)) return hint;
10084
- if (/^unauthorized$/i.test(detail.trim()))
10085
- return "Upstream refused this write. With a key that can read, this usually means the account has no balance: ask the user to check their balance in the Runpod console before retrying.";
10086
11827
  }
10087
11828
  if (status === 400 || status === 422) return SHAPE_HINT;
10088
11829
  if (status === 401) return "The Runpod API key is missing or invalid.";
@@ -10822,7 +12563,7 @@ async function handleMcpRequest(req, res, opts = {}) {
10822
12563
  }
10823
12564
 
10824
12565
  // src/alp/scrub.ts
10825
- var SCRUB_VERSION = 5;
12566
+ var SCRUB_VERSION = 6;
10826
12567
  var MARKER = /^["']?\[redacted:[a-z_]+\]["']?$/;
10827
12568
  var CREDENTIAL_PATTERNS = [
10828
12569
  // Runpod API keys.
@@ -11010,6 +12751,22 @@ function redactAssignments(text) {
11010
12751
  out += text.slice(copiedTo);
11011
12752
  return { text: out, redactions };
11012
12753
  }
12754
+ var EMAIL = /(?<![\p{L}\p{M}\p{N}_.%+-])([\p{L}\p{M}\p{N}_.%+-]{1,64})@((?:[\p{L}\p{M}\p{N}-]{1,63}\.){1,8}[\p{L}\p{M}]{2,63})(?![\p{L}\p{M}\p{N}])/gu;
12755
+ var RUNPOD_SSH_HOST = "ssh.runpod.io";
12756
+ function redactEmails(text) {
12757
+ let redactions = 0;
12758
+ const out = text.replace(
12759
+ EMAIL,
12760
+ (address, local, domain, offset) => {
12761
+ const next = text[offset + address.length];
12762
+ if (domain.toLowerCase() === RUNPOD_SSH_HOST || local === "git" && (next === ":" || next === "/"))
12763
+ return address;
12764
+ redactions++;
12765
+ return "[redacted:email]";
12766
+ }
12767
+ );
12768
+ return { text: out, redactions };
12769
+ }
11013
12770
  function scrub(text) {
11014
12771
  let out = text;
11015
12772
  let redactions = 0;
@@ -11017,7 +12774,8 @@ function scrub(text) {
11017
12774
  redactAnchoredCredentials,
11018
12775
  redactHeaderValues,
11019
12776
  redactQuerySecrets,
11020
- redactAssignments
12777
+ redactAssignments,
12778
+ redactEmails
11021
12779
  ]) {
11022
12780
  const result = stage(out);
11023
12781
  out = result.text;
@@ -11057,6 +12815,7 @@ var MAX_CONTENT_CHARS = 2e4;
11057
12815
  var MAX_REFERRAL_CONTENT_CHARS = 200;
11058
12816
  var MAX_FIELD_CHARS = 500;
11059
12817
  var SINK_TIMEOUT_MS3 = 5e3;
12818
+ var TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;
11060
12819
  var ALP_SEVERITIES = ["blocked", "degraded", "cosmetic"];
11061
12820
  function notRecorded(reason) {
11062
12821
  return {
@@ -11129,7 +12888,9 @@ async function handleAlpSubmit(req, res, opts = {}) {
11129
12888
  // ("critical", "high") turns a dimension back into prose, and the loss of
11130
12889
  // one label costs less than that.
11131
12890
  severity: !referral && ALP_SEVERITIES.includes(body.severity) ? body.severity : void 0,
11132
- tool: !referral && typeof body.tool === "string" ? body.tool : void 0,
12891
+ // Same rule as severity: a free-text tool would turn a grouping key back
12892
+ // into prose, so only a value shaped like a tool name is kept.
12893
+ tool: !referral && typeof body.tool === "string" && TOOL_NAME.test(body.tool) ? body.tool : void 0,
11133
12894
  workaround: !referral && typeof body.workaround === "string" ? body.workaround : void 0,
11134
12895
  trigger: !referral && typeof body.trigger === "string" ? body.trigger : void 0,
11135
12896
  // Referral-only fields. source follows the severity rule (off-enum is
@@ -11155,7 +12916,7 @@ async function handleAlpSubmit(req, res, opts = {}) {
11155
12916
  intention: clean.intention?.slice(0, MAX_FIELD_CHARS),
11156
12917
  modelType: clean.modelType?.slice(0, MAX_FIELD_CHARS),
11157
12918
  severity: clean.severity,
11158
- tool: clean.tool?.slice(0, MAX_FIELD_CHARS),
12919
+ tool: clean.tool,
11159
12920
  // Prose fields, so they get the content cap rather than the field cap: a
11160
12921
  // workaround worth reading is a paragraph, not a label.
11161
12922
  workaround: clean.workaround?.slice(0, MAX_CONTENT_CHARS),
@@ -11187,6 +12948,14 @@ async function handleAlpSubmit(req, res, opts = {}) {
11187
12948
  return;
11188
12949
  }
11189
12950
  const sinkBody = await response.json().catch(() => null);
12951
+ if (sinkBody?.capped === true) {
12952
+ console.log(
12953
+ "alp_submit_capped",
12954
+ JSON.stringify({ route: body.route, transport: row.transport })
12955
+ );
12956
+ send(200, notRecorded("this account reached its limit for this tool"));
12957
+ return;
12958
+ }
11190
12959
  if (sinkBody?.ok !== true || typeof sinkBody.id !== "string") {
11191
12960
  console.warn("alp_sink_unrecognized", { status: response.status });
11192
12961
  send(200, notRecorded("the store did not confirm the write"));