@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/http.cjs CHANGED
@@ -308,6 +308,14 @@ function validateArguments(inputSchema, args) {
308
308
  }
309
309
  return null;
310
310
  }
311
+ function withoutNullOptionals(inputSchema, args) {
312
+ const required = new Set(inputSchema.required ?? []);
313
+ return Object.fromEntries(
314
+ Object.entries(args).filter(
315
+ ([name, value]) => value !== null || required.has(name)
316
+ )
317
+ );
318
+ }
311
319
  async function dispatchGeneratedTool(client, tool, args) {
312
320
  const rejected = validateArguments(tool.inputSchema, args);
313
321
  if (rejected) return rejected;
@@ -432,6 +440,104 @@ function coerceScalars(value, schema, defs, depth = 0) {
432
440
 
433
441
  // src/specgen/generated/tools.gen.ts
434
442
  var generatedTools = [
443
+ {
444
+ name: "add-organization-group-member",
445
+ title: "Add organization group member",
446
+ operationId: "addOrganizationGroupMember",
447
+ 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.",
448
+ method: "PUT",
449
+ path: "/v2/organization/groups/{id}/members/{userId}",
450
+ params: [
451
+ {
452
+ name: "id",
453
+ location: "path"
454
+ },
455
+ {
456
+ name: "userId",
457
+ location: "path"
458
+ }
459
+ ],
460
+ hasBody: false,
461
+ annotations: {
462
+ readOnlyHint: false,
463
+ destructiveHint: false,
464
+ idempotentHint: true,
465
+ openWorldHint: true
466
+ },
467
+ inputSchema: {
468
+ type: "object",
469
+ properties: {
470
+ id: {
471
+ type: "string",
472
+ minLength: 1,
473
+ description: "Group ID."
474
+ },
475
+ userId: {
476
+ type: "string",
477
+ minLength: 1,
478
+ description: "Organization member user ID."
479
+ }
480
+ },
481
+ required: ["id", "userId"]
482
+ }
483
+ },
484
+ {
485
+ name: "attach-cost-center-resource",
486
+ title: "Attach cost center resource",
487
+ operationId: "attachCostCenterResource",
488
+ 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.",
489
+ method: "PUT",
490
+ path: "/v2/cost-centers/{id}/{resourceType}/{resourceId}",
491
+ params: [
492
+ {
493
+ name: "id",
494
+ location: "path"
495
+ },
496
+ {
497
+ name: "resourceType",
498
+ location: "path"
499
+ },
500
+ {
501
+ name: "resourceId",
502
+ location: "path"
503
+ }
504
+ ],
505
+ hasBody: false,
506
+ annotations: {
507
+ readOnlyHint: false,
508
+ destructiveHint: false,
509
+ idempotentHint: true,
510
+ openWorldHint: true
511
+ },
512
+ inputSchema: {
513
+ type: "object",
514
+ properties: {
515
+ id: {
516
+ type: "string",
517
+ minLength: 1,
518
+ description: "Cost center identifier."
519
+ },
520
+ resourceType: {
521
+ $ref: "#/$defs/ResourceType",
522
+ description: "Kind of resource.",
523
+ type: "string"
524
+ },
525
+ resourceId: {
526
+ type: "string",
527
+ minLength: 1,
528
+ description: "Resource identifier."
529
+ }
530
+ },
531
+ required: ["id", "resourceType", "resourceId"],
532
+ $defs: {
533
+ ResourceType: {
534
+ type: "string",
535
+ description: "Kind of resource a cost center or tag can be attached to.",
536
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
537
+ }
538
+ }
539
+ }
540
+ },
435
541
  {
436
542
  name: "create-cluster",
437
543
  title: "Create cluster",
@@ -530,7 +636,6 @@ var generatedTools = [
530
636
  },
531
637
  network: {
532
638
  type: "array",
533
- maxItems: 1,
534
639
  items: {
535
640
  $ref: "#/$defs/NetworkMount"
536
641
  }
@@ -552,7 +657,7 @@ var generatedTools = [
552
657
  gpuCountPerPod: {
553
658
  type: "integer",
554
659
  minimum: 1,
555
- 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.",
660
+ description: "Number of GPUs on each pod. Bounded above by the GPU type's per-cloud maximum (GpuType.maxCount); values beyond it are rejected.",
556
661
  examples: [8]
557
662
  },
558
663
  podCount: {
@@ -565,7 +670,7 @@ var generatedTools = [
565
670
  }
566
671
  },
567
672
  ClusterMounts: {
568
- description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
673
+ description: "Storage mounts for a cluster create: `persistent` or one `network` volume, not both. Clusters cannot mount global volumes, so `global` is rejected.",
569
674
  "x-go-type": "BaseMounts",
570
675
  allOf: [
571
676
  {
@@ -585,6 +690,7 @@ var generatedTools = [
585
690
  },
586
691
  network: {
587
692
  type: "array",
693
+ maxItems: 1,
588
694
  items: {
589
695
  type: "object",
590
696
  properties: {
@@ -663,7 +769,7 @@ var generatedTools = [
663
769
  type: "object",
664
770
  required: ["volumeId", "path"],
665
771
  additionalProperties: false,
666
- 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.",
772
+ 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`).",
667
773
  properties: {
668
774
  volumeId: {
669
775
  type: "string",
@@ -686,7 +792,7 @@ var generatedTools = [
686
792
  size: {
687
793
  type: "integer",
688
794
  minimum: 10,
689
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
795
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
690
796
  examples: [20]
691
797
  },
692
798
  path: {
@@ -699,6 +805,77 @@ var generatedTools = [
699
805
  }
700
806
  }
701
807
  },
808
+ {
809
+ name: "create-cost-center",
810
+ title: "Create cost center",
811
+ operationId: "createCostCenter",
812
+ 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.",
813
+ method: "POST",
814
+ path: "/v2/cost-centers",
815
+ params: [],
816
+ hasBody: true,
817
+ annotations: {
818
+ readOnlyHint: false,
819
+ destructiveHint: false,
820
+ idempotentHint: false,
821
+ openWorldHint: true
822
+ },
823
+ inputSchema: {
824
+ type: "object",
825
+ properties: {
826
+ body: {
827
+ $ref: "#/$defs/CreateCostCenterRequest",
828
+ type: "object"
829
+ }
830
+ },
831
+ required: ["body"],
832
+ $defs: {
833
+ CostCenterResource: {
834
+ type: "object",
835
+ additionalProperties: false,
836
+ required: ["id", "type"],
837
+ properties: {
838
+ id: {
839
+ type: "string",
840
+ minLength: 1,
841
+ examples: ["p9k2m4n6q8"]
842
+ },
843
+ type: {
844
+ $ref: "#/$defs/ResourceType"
845
+ }
846
+ }
847
+ },
848
+ CreateCostCenterRequest: {
849
+ type: "object",
850
+ additionalProperties: false,
851
+ required: ["value"],
852
+ properties: {
853
+ value: {
854
+ type: "string",
855
+ minLength: 1,
856
+ maxLength: 50,
857
+ pattern: "^\\s*\\S",
858
+ description: "Leading and trailing whitespace is trimmed.",
859
+ examples: ["team-ml"]
860
+ },
861
+ resources: {
862
+ type: "array",
863
+ description: "Resources to attribute to the new cost center.",
864
+ maxItems: 100,
865
+ items: {
866
+ $ref: "#/$defs/CostCenterResource"
867
+ }
868
+ }
869
+ }
870
+ },
871
+ ResourceType: {
872
+ type: "string",
873
+ description: "Kind of resource a cost center or tag can be attached to.",
874
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
875
+ }
876
+ }
877
+ }
878
+ },
702
879
  {
703
880
  name: "create-delegation",
704
881
  title: "Create ECR delegation",
@@ -1227,6 +1404,7 @@ var generatedTools = [
1227
1404
  type: "string",
1228
1405
  minLength: 1,
1229
1406
  maxLength: 191,
1407
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1230
1408
  description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
1231
1409
  examples: ["training-data"]
1232
1410
  }
@@ -1302,6 +1480,169 @@ var generatedTools = [
1302
1480
  }
1303
1481
  }
1304
1482
  },
1483
+ {
1484
+ name: "create-organization-group",
1485
+ title: "Create organization group",
1486
+ operationId: "createOrganizationGroup",
1487
+ description: "Create a group in your current organization",
1488
+ method: "POST",
1489
+ path: "/v2/organization/groups",
1490
+ params: [],
1491
+ hasBody: true,
1492
+ annotations: {
1493
+ readOnlyHint: false,
1494
+ destructiveHint: false,
1495
+ idempotentHint: false,
1496
+ openWorldHint: true
1497
+ },
1498
+ inputSchema: {
1499
+ type: "object",
1500
+ properties: {
1501
+ body: {
1502
+ $ref: "#/$defs/CreateOrganizationGroupRequest",
1503
+ type: "object"
1504
+ }
1505
+ },
1506
+ required: ["body"],
1507
+ $defs: {
1508
+ CreateOrganizationGroupRequest: {
1509
+ type: "object",
1510
+ additionalProperties: false,
1511
+ required: ["name"],
1512
+ properties: {
1513
+ name: {
1514
+ type: "string",
1515
+ minLength: 1,
1516
+ maxLength: 191,
1517
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1518
+ description: "Group name with at least one non-whitespace character; surrounding whitespace is trimmed.",
1519
+ examples: ["Engineering"]
1520
+ }
1521
+ }
1522
+ }
1523
+ }
1524
+ }
1525
+ },
1526
+ {
1527
+ name: "create-organization-invite",
1528
+ title: "Create organization invite",
1529
+ operationId: "createOrganizationInvite",
1530
+ 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.",
1531
+ method: "POST",
1532
+ path: "/v2/organization/invites",
1533
+ params: [],
1534
+ hasBody: true,
1535
+ annotations: {
1536
+ readOnlyHint: false,
1537
+ destructiveHint: false,
1538
+ idempotentHint: false,
1539
+ openWorldHint: true
1540
+ },
1541
+ inputSchema: {
1542
+ type: "object",
1543
+ properties: {
1544
+ body: {
1545
+ $ref: "#/$defs/CreateOrganizationInviteRequest",
1546
+ type: "object"
1547
+ }
1548
+ },
1549
+ required: ["body"],
1550
+ $defs: {
1551
+ CreateOrganizationInviteRequest: {
1552
+ type: "object",
1553
+ additionalProperties: false,
1554
+ required: ["email", "role"],
1555
+ properties: {
1556
+ email: {
1557
+ type: "string",
1558
+ format: "email",
1559
+ minLength: 1,
1560
+ maxLength: 254,
1561
+ description: "Address to invite, stored lowercased. The invitee accepts by signing in with a verified account on this address.",
1562
+ examples: ["new-member@example.com"]
1563
+ },
1564
+ role: {
1565
+ $ref: "#/$defs/OrganizationRoleInput"
1566
+ }
1567
+ }
1568
+ },
1569
+ OrganizationRoleInput: {
1570
+ type: "object",
1571
+ description: "An organization role to assign, by ID.",
1572
+ additionalProperties: false,
1573
+ required: ["id"],
1574
+ properties: {
1575
+ id: {
1576
+ type: "string",
1577
+ minLength: 1,
1578
+ 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.",
1579
+ examples: ["builtin:dev"]
1580
+ }
1581
+ }
1582
+ }
1583
+ }
1584
+ }
1585
+ },
1586
+ {
1587
+ name: "create-organization-role",
1588
+ title: "Create organization role",
1589
+ operationId: "createOrganizationRole",
1590
+ 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.",
1591
+ method: "POST",
1592
+ path: "/v2/organization/roles",
1593
+ params: [],
1594
+ hasBody: true,
1595
+ annotations: {
1596
+ readOnlyHint: false,
1597
+ destructiveHint: false,
1598
+ idempotentHint: false,
1599
+ openWorldHint: true
1600
+ },
1601
+ inputSchema: {
1602
+ type: "object",
1603
+ properties: {
1604
+ body: {
1605
+ $ref: "#/$defs/CreateOrganizationRoleRequest",
1606
+ type: "object"
1607
+ }
1608
+ },
1609
+ required: ["body"],
1610
+ $defs: {
1611
+ CreateOrganizationRoleRequest: {
1612
+ type: "object",
1613
+ additionalProperties: false,
1614
+ required: ["name", "capabilities"],
1615
+ properties: {
1616
+ name: {
1617
+ $ref: "#/$defs/OrganizationRoleName"
1618
+ },
1619
+ capabilities: {
1620
+ $ref: "#/$defs/OrganizationRoleCapabilitiesInput"
1621
+ }
1622
+ }
1623
+ },
1624
+ OrganizationRoleCapabilitiesInput: {
1625
+ type: "array",
1626
+ description: "What the role grants, as `FAMILY:VERB` capabilities from `get-organization-role-capabilities`. The caller must hold every capability it lists.",
1627
+ minItems: 1,
1628
+ uniqueItems: true,
1629
+ items: {
1630
+ type: "string",
1631
+ pattern: "^[A-Z0-9_]+:[A-Z0-9_]+$"
1632
+ },
1633
+ examples: [["POD:VIEW"]]
1634
+ },
1635
+ OrganizationRoleName: {
1636
+ type: "string",
1637
+ minLength: 1,
1638
+ maxLength: 191,
1639
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
1640
+ 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.",
1641
+ examples: ["Pod viewer"]
1642
+ }
1643
+ }
1644
+ }
1645
+ },
1305
1646
  {
1306
1647
  name: "create-pod",
1307
1648
  title: "Create Pod",
@@ -1422,6 +1763,7 @@ var generatedTools = [
1422
1763
  count: {
1423
1764
  type: "integer",
1424
1765
  minimum: 1,
1766
+ maximum: 1024,
1425
1767
  default: 1,
1426
1768
  description: "Number of GPUs",
1427
1769
  examples: [1]
@@ -1436,7 +1778,6 @@ var generatedTools = [
1436
1778
  },
1437
1779
  network: {
1438
1780
  type: "array",
1439
- maxItems: 1,
1440
1781
  items: {
1441
1782
  $ref: "#/$defs/NetworkMount"
1442
1783
  }
@@ -1500,12 +1841,14 @@ var generatedTools = [
1500
1841
  minRamPerGpu: {
1501
1842
  type: "integer",
1502
1843
  minimum: 1,
1844
+ maximum: 65536,
1503
1845
  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.",
1504
1846
  examples: [8]
1505
1847
  },
1506
1848
  minVcpuCountPerGpu: {
1507
1849
  type: "integer",
1508
1850
  minimum: 1,
1851
+ maximum: 4096,
1509
1852
  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.",
1510
1853
  examples: [2]
1511
1854
  }
@@ -1596,7 +1939,7 @@ var generatedTools = [
1596
1939
  type: "object",
1597
1940
  required: ["volumeId"],
1598
1941
  additionalProperties: false,
1599
- 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.",
1942
+ 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`).",
1600
1943
  properties: {
1601
1944
  volumeId: {
1602
1945
  type: "string",
@@ -1605,7 +1948,7 @@ var generatedTools = [
1605
1948
  },
1606
1949
  path: {
1607
1950
  type: "string",
1608
- 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.",
1951
+ description: "Mount path inside the container. Optional; a default is chosen for you when omitted.",
1609
1952
  examples: ["/data"]
1610
1953
  }
1611
1954
  }
@@ -1619,10 +1962,18 @@ var generatedTools = [
1619
1962
  {
1620
1963
  type: "object",
1621
1964
  properties: {
1965
+ network: {
1966
+ type: "array",
1967
+ maxItems: 4,
1968
+ description: "Up to 4 network volumes, all in the pod's data center.",
1969
+ items: {
1970
+ $ref: "#/$defs/NetworkMount"
1971
+ }
1972
+ },
1622
1973
  global: {
1623
1974
  type: "array",
1624
1975
  maxItems: 1,
1625
- 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.",
1976
+ 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`.",
1626
1977
  items: {
1627
1978
  $ref: "#/$defs/GlobalMount"
1628
1979
  }
@@ -1636,7 +1987,7 @@ var generatedTools = [
1636
1987
  type: "object",
1637
1988
  required: ["volumeId", "path"],
1638
1989
  additionalProperties: false,
1639
- 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.",
1990
+ 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`).",
1640
1991
  properties: {
1641
1992
  volumeId: {
1642
1993
  type: "string",
@@ -1659,7 +2010,7 @@ var generatedTools = [
1659
2010
  size: {
1660
2011
  type: "integer",
1661
2012
  minimum: 10,
1662
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
2013
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
1663
2014
  examples: [20]
1664
2015
  },
1665
2016
  path: {
@@ -1956,7 +2307,7 @@ var generatedTools = [
1956
2307
  size: {
1957
2308
  type: "integer",
1958
2309
  minimum: 10,
1959
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
2310
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
1960
2311
  examples: [20]
1961
2312
  },
1962
2313
  path: {
@@ -2017,13 +2368,12 @@ var generatedTools = [
2017
2368
  }
2018
2369
  },
2019
2370
  {
2020
- name: "delete-endpoint",
2021
- title: "Delete endpoint",
2022
- operationId: "deleteEndpoint",
2023
- 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.",
2024
- directoryDescription: "Delete a Serverless endpoint permanently and immediately.",
2371
+ name: "delete-cost-center",
2372
+ title: "Delete cost center",
2373
+ operationId: "deleteCostCenter",
2374
+ 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.",
2025
2375
  method: "DELETE",
2026
- path: "/v2/serverless/{id}",
2376
+ path: "/v2/cost-centers/{id}",
2027
2377
  params: [
2028
2378
  {
2029
2379
  name: "id",
@@ -2042,17 +2392,50 @@ var generatedTools = [
2042
2392
  properties: {
2043
2393
  id: {
2044
2394
  type: "string",
2045
- description: "Serverless endpoint identifier"
2395
+ minLength: 1,
2396
+ description: "Cost center identifier."
2046
2397
  }
2047
2398
  },
2048
2399
  required: ["id"]
2049
2400
  }
2050
2401
  },
2051
2402
  {
2052
- name: "delete-global-volume",
2053
- title: "Delete global volume",
2054
- operationId: "deleteGlobalVolume",
2055
- 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.",
2403
+ name: "delete-endpoint",
2404
+ title: "Delete endpoint",
2405
+ operationId: "deleteEndpoint",
2406
+ 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.",
2407
+ directoryDescription: "Delete a Serverless endpoint permanently and immediately.",
2408
+ method: "DELETE",
2409
+ path: "/v2/serverless/{id}",
2410
+ params: [
2411
+ {
2412
+ name: "id",
2413
+ location: "path"
2414
+ }
2415
+ ],
2416
+ hasBody: false,
2417
+ annotations: {
2418
+ readOnlyHint: false,
2419
+ destructiveHint: true,
2420
+ idempotentHint: true,
2421
+ openWorldHint: true
2422
+ },
2423
+ inputSchema: {
2424
+ type: "object",
2425
+ properties: {
2426
+ id: {
2427
+ type: "string",
2428
+ description: "Serverless endpoint identifier"
2429
+ }
2430
+ },
2431
+ required: ["id"]
2432
+ }
2433
+ },
2434
+ {
2435
+ name: "delete-global-volume",
2436
+ title: "Delete global volume",
2437
+ operationId: "deleteGlobalVolume",
2438
+ 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.",
2056
2439
  method: "DELETE",
2057
2440
  path: "/v2/global-volumes/{id}",
2058
2441
  params: [
@@ -2110,6 +2493,131 @@ var generatedTools = [
2110
2493
  required: ["id"]
2111
2494
  }
2112
2495
  },
2496
+ {
2497
+ name: "delete-organization-group",
2498
+ title: "Delete organization group",
2499
+ operationId: "deleteOrganizationGroup",
2500
+ 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.",
2501
+ method: "DELETE",
2502
+ path: "/v2/organization/groups/{id}",
2503
+ params: [
2504
+ {
2505
+ name: "id",
2506
+ location: "path"
2507
+ }
2508
+ ],
2509
+ hasBody: false,
2510
+ annotations: {
2511
+ readOnlyHint: false,
2512
+ destructiveHint: true,
2513
+ idempotentHint: true,
2514
+ openWorldHint: true
2515
+ },
2516
+ inputSchema: {
2517
+ type: "object",
2518
+ properties: {
2519
+ id: {
2520
+ type: "string",
2521
+ minLength: 1,
2522
+ description: "Group ID."
2523
+ }
2524
+ },
2525
+ required: ["id"]
2526
+ }
2527
+ },
2528
+ {
2529
+ name: "delete-organization-invite",
2530
+ title: "Delete organization invite",
2531
+ operationId: "deleteOrganizationInvite",
2532
+ 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.",
2533
+ method: "DELETE",
2534
+ path: "/v2/organization/invites/{id}",
2535
+ params: [
2536
+ {
2537
+ name: "id",
2538
+ location: "path"
2539
+ }
2540
+ ],
2541
+ hasBody: false,
2542
+ annotations: {
2543
+ readOnlyHint: false,
2544
+ destructiveHint: true,
2545
+ idempotentHint: true,
2546
+ openWorldHint: true
2547
+ },
2548
+ inputSchema: {
2549
+ type: "object",
2550
+ properties: {
2551
+ id: {
2552
+ type: "string",
2553
+ description: "Invite identifier"
2554
+ }
2555
+ },
2556
+ required: ["id"]
2557
+ }
2558
+ },
2559
+ {
2560
+ name: "delete-organization-role",
2561
+ title: "Delete organization role",
2562
+ operationId: "deleteOrganizationRole",
2563
+ 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.",
2564
+ method: "DELETE",
2565
+ path: "/v2/organization/roles/{id}",
2566
+ params: [
2567
+ {
2568
+ name: "id",
2569
+ location: "path"
2570
+ }
2571
+ ],
2572
+ hasBody: false,
2573
+ annotations: {
2574
+ readOnlyHint: false,
2575
+ destructiveHint: true,
2576
+ idempotentHint: true,
2577
+ openWorldHint: true
2578
+ },
2579
+ inputSchema: {
2580
+ type: "object",
2581
+ properties: {
2582
+ id: {
2583
+ type: "string",
2584
+ description: "Role identifier"
2585
+ }
2586
+ },
2587
+ required: ["id"]
2588
+ }
2589
+ },
2590
+ {
2591
+ name: "delete-organization-user",
2592
+ title: "Delete organization user",
2593
+ operationId: "deleteOrganizationUser",
2594
+ 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.",
2595
+ method: "DELETE",
2596
+ path: "/v2/organization/users/{id}",
2597
+ params: [
2598
+ {
2599
+ name: "id",
2600
+ location: "path"
2601
+ }
2602
+ ],
2603
+ hasBody: false,
2604
+ annotations: {
2605
+ readOnlyHint: false,
2606
+ destructiveHint: true,
2607
+ idempotentHint: true,
2608
+ openWorldHint: true
2609
+ },
2610
+ inputSchema: {
2611
+ type: "object",
2612
+ properties: {
2613
+ id: {
2614
+ type: "string",
2615
+ description: "User identifier"
2616
+ }
2617
+ },
2618
+ required: ["id"]
2619
+ }
2620
+ },
2113
2621
  {
2114
2622
  name: "delete-pod",
2115
2623
  title: "Delete Pod",
@@ -2235,6 +2743,63 @@ var generatedTools = [
2235
2743
  required: ["id"]
2236
2744
  }
2237
2745
  },
2746
+ {
2747
+ name: "detach-cost-center-resource",
2748
+ title: "Detach cost center resource",
2749
+ operationId: "detachCostCenterResource",
2750
+ 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.",
2751
+ method: "DELETE",
2752
+ path: "/v2/cost-centers/{id}/{resourceType}/{resourceId}",
2753
+ params: [
2754
+ {
2755
+ name: "id",
2756
+ location: "path"
2757
+ },
2758
+ {
2759
+ name: "resourceType",
2760
+ location: "path"
2761
+ },
2762
+ {
2763
+ name: "resourceId",
2764
+ location: "path"
2765
+ }
2766
+ ],
2767
+ hasBody: false,
2768
+ annotations: {
2769
+ readOnlyHint: false,
2770
+ destructiveHint: true,
2771
+ idempotentHint: true,
2772
+ openWorldHint: true
2773
+ },
2774
+ inputSchema: {
2775
+ type: "object",
2776
+ properties: {
2777
+ id: {
2778
+ type: "string",
2779
+ minLength: 1,
2780
+ description: "Cost center identifier."
2781
+ },
2782
+ resourceType: {
2783
+ $ref: "#/$defs/ResourceType",
2784
+ description: "Kind of resource.",
2785
+ type: "string"
2786
+ },
2787
+ resourceId: {
2788
+ type: "string",
2789
+ minLength: 1,
2790
+ description: "Resource identifier."
2791
+ }
2792
+ },
2793
+ required: ["id", "resourceType", "resourceId"],
2794
+ $defs: {
2795
+ ResourceType: {
2796
+ type: "string",
2797
+ description: "Kind of resource a cost center or tag can be attached to.",
2798
+ enum: ["POD", "CLUSTER", "SERVERLESS_ENDPOINT", "NETWORK_VOLUME"]
2799
+ }
2800
+ }
2801
+ }
2802
+ },
2238
2803
  {
2239
2804
  name: "get-cluster",
2240
2805
  title: "Get cluster",
@@ -2266,6 +2831,38 @@ var generatedTools = [
2266
2831
  required: ["id"]
2267
2832
  }
2268
2833
  },
2834
+ {
2835
+ name: "get-cost-center",
2836
+ title: "Get cost center",
2837
+ operationId: "getCostCenter",
2838
+ 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.",
2839
+ method: "GET",
2840
+ path: "/v2/cost-centers/{id}",
2841
+ params: [
2842
+ {
2843
+ name: "id",
2844
+ location: "path"
2845
+ }
2846
+ ],
2847
+ hasBody: false,
2848
+ annotations: {
2849
+ readOnlyHint: true,
2850
+ destructiveHint: false,
2851
+ idempotentHint: true,
2852
+ openWorldHint: true
2853
+ },
2854
+ inputSchema: {
2855
+ type: "object",
2856
+ properties: {
2857
+ id: {
2858
+ type: "string",
2859
+ minLength: 1,
2860
+ description: "Cost center identifier."
2861
+ }
2862
+ },
2863
+ required: ["id"]
2864
+ }
2865
+ },
2269
2866
  {
2270
2867
  name: "get-cpu-type",
2271
2868
  title: "Get CPU type",
@@ -2463,6 +3060,261 @@ var generatedTools = [
2463
3060
  required: ["id", "buildId"]
2464
3061
  }
2465
3062
  },
3063
+ {
3064
+ name: "get-endpoint-cold-start-metrics",
3065
+ title: "Get endpoint cold start metrics",
3066
+ operationId: "getEndpointColdStartMetrics",
3067
+ 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.",
3068
+ method: "GET",
3069
+ path: "/v2/serverless/{id}/metrics/cold-starts",
3070
+ params: [
3071
+ {
3072
+ name: "id",
3073
+ location: "path"
3074
+ },
3075
+ {
3076
+ name: "startTime",
3077
+ location: "query"
3078
+ },
3079
+ {
3080
+ name: "endTime",
3081
+ location: "query"
3082
+ },
3083
+ {
3084
+ name: "interval",
3085
+ location: "query"
3086
+ },
3087
+ {
3088
+ name: "cursor",
3089
+ location: "query"
3090
+ },
3091
+ {
3092
+ name: "limit",
3093
+ location: "query"
3094
+ }
3095
+ ],
3096
+ hasBody: false,
3097
+ annotations: {
3098
+ readOnlyHint: true,
3099
+ destructiveHint: false,
3100
+ idempotentHint: true,
3101
+ openWorldHint: true
3102
+ },
3103
+ inputSchema: {
3104
+ type: "object",
3105
+ properties: {
3106
+ id: {
3107
+ type: "string",
3108
+ description: "Serverless endpoint identifier"
3109
+ },
3110
+ startTime: {
3111
+ type: "string",
3112
+ format: "date-time",
3113
+ 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."
3114
+ },
3115
+ endTime: {
3116
+ type: "string",
3117
+ format: "date-time",
3118
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3119
+ },
3120
+ interval: {
3121
+ $ref: "#/$defs/EndpointMetricsInterval",
3122
+ description: "Bucket size. Defaults to 1h.",
3123
+ type: "string"
3124
+ },
3125
+ cursor: {
3126
+ type: "string",
3127
+ minLength: 1,
3128
+ 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."
3129
+ },
3130
+ limit: {
3131
+ type: "integer",
3132
+ minimum: 1,
3133
+ maximum: 1e3,
3134
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3135
+ }
3136
+ },
3137
+ required: ["id"],
3138
+ $defs: {
3139
+ EndpointMetricsInterval: {
3140
+ type: "string",
3141
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3142
+ default: "1h",
3143
+ description: "Bucket size for endpoint metrics."
3144
+ }
3145
+ }
3146
+ }
3147
+ },
3148
+ {
3149
+ name: "get-endpoint-request-metrics",
3150
+ title: "Get endpoint request metrics",
3151
+ operationId: "getEndpointRequestMetrics",
3152
+ 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.",
3153
+ method: "GET",
3154
+ path: "/v2/serverless/{id}/metrics/requests",
3155
+ params: [
3156
+ {
3157
+ name: "id",
3158
+ location: "path"
3159
+ },
3160
+ {
3161
+ name: "startTime",
3162
+ location: "query"
3163
+ },
3164
+ {
3165
+ name: "endTime",
3166
+ location: "query"
3167
+ },
3168
+ {
3169
+ name: "interval",
3170
+ location: "query"
3171
+ },
3172
+ {
3173
+ name: "cursor",
3174
+ location: "query"
3175
+ },
3176
+ {
3177
+ name: "limit",
3178
+ location: "query"
3179
+ }
3180
+ ],
3181
+ hasBody: false,
3182
+ annotations: {
3183
+ readOnlyHint: true,
3184
+ destructiveHint: false,
3185
+ idempotentHint: true,
3186
+ openWorldHint: true
3187
+ },
3188
+ inputSchema: {
3189
+ type: "object",
3190
+ properties: {
3191
+ id: {
3192
+ type: "string",
3193
+ description: "Serverless endpoint identifier"
3194
+ },
3195
+ startTime: {
3196
+ type: "string",
3197
+ format: "date-time",
3198
+ 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."
3199
+ },
3200
+ endTime: {
3201
+ type: "string",
3202
+ format: "date-time",
3203
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3204
+ },
3205
+ interval: {
3206
+ $ref: "#/$defs/EndpointMetricsInterval",
3207
+ description: "Bucket size. Defaults to 1h.",
3208
+ type: "string"
3209
+ },
3210
+ cursor: {
3211
+ type: "string",
3212
+ minLength: 1,
3213
+ 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."
3214
+ },
3215
+ limit: {
3216
+ type: "integer",
3217
+ minimum: 1,
3218
+ maximum: 1e3,
3219
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3220
+ }
3221
+ },
3222
+ required: ["id"],
3223
+ $defs: {
3224
+ EndpointMetricsInterval: {
3225
+ type: "string",
3226
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3227
+ default: "1h",
3228
+ description: "Bucket size for endpoint metrics."
3229
+ }
3230
+ }
3231
+ }
3232
+ },
3233
+ {
3234
+ name: "get-endpoint-worker-metrics",
3235
+ title: "Get endpoint worker metrics",
3236
+ operationId: "getEndpointWorkerMetrics",
3237
+ 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.",
3238
+ method: "GET",
3239
+ path: "/v2/serverless/{id}/metrics/workers",
3240
+ params: [
3241
+ {
3242
+ name: "id",
3243
+ location: "path"
3244
+ },
3245
+ {
3246
+ name: "startTime",
3247
+ location: "query"
3248
+ },
3249
+ {
3250
+ name: "endTime",
3251
+ location: "query"
3252
+ },
3253
+ {
3254
+ name: "interval",
3255
+ location: "query"
3256
+ },
3257
+ {
3258
+ name: "cursor",
3259
+ location: "query"
3260
+ },
3261
+ {
3262
+ name: "limit",
3263
+ location: "query"
3264
+ }
3265
+ ],
3266
+ hasBody: false,
3267
+ annotations: {
3268
+ readOnlyHint: true,
3269
+ destructiveHint: false,
3270
+ idempotentHint: true,
3271
+ openWorldHint: true
3272
+ },
3273
+ inputSchema: {
3274
+ type: "object",
3275
+ properties: {
3276
+ id: {
3277
+ type: "string",
3278
+ description: "Serverless endpoint identifier"
3279
+ },
3280
+ startTime: {
3281
+ type: "string",
3282
+ format: "date-time",
3283
+ 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."
3284
+ },
3285
+ endTime: {
3286
+ type: "string",
3287
+ format: "date-time",
3288
+ description: "End of the window (RFC 3339), exclusive. Defaults to now; a future value is treated as now."
3289
+ },
3290
+ interval: {
3291
+ $ref: "#/$defs/EndpointMetricsInterval",
3292
+ description: "Bucket size. Defaults to 1h.",
3293
+ type: "string"
3294
+ },
3295
+ cursor: {
3296
+ type: "string",
3297
+ minLength: 1,
3298
+ 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."
3299
+ },
3300
+ limit: {
3301
+ type: "integer",
3302
+ minimum: 1,
3303
+ maximum: 1e3,
3304
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3305
+ }
3306
+ },
3307
+ required: ["id"],
3308
+ $defs: {
3309
+ EndpointMetricsInterval: {
3310
+ type: "string",
3311
+ enum: ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "1d"],
3312
+ default: "1h",
3313
+ description: "Bucket size for endpoint metrics."
3314
+ }
3315
+ }
3316
+ }
3317
+ },
2466
3318
  {
2467
3319
  name: "get-global-volume",
2468
3320
  title: "Get global volume",
@@ -2575,7 +3427,7 @@ var generatedTools = [
2575
3427
  },
2576
3428
  cloud: {
2577
3429
  $ref: "#/$defs/GpuCloudFilter",
2578
- description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Upstream default when omitted: SECURE.",
3430
+ description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Default when omitted: SECURE.",
2579
3431
  type: "string"
2580
3432
  },
2581
3433
  countryCodes: {
@@ -2614,19 +3466,133 @@ var generatedTools = [
2614
3466
  },
2615
3467
  Product: {
2616
3468
  type: "string",
2617
- description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
2618
- enum: ["POD", "CLUSTER", "SERVERLESS"]
3469
+ description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
3470
+ enum: ["POD", "CLUSTER", "SERVERLESS"]
3471
+ }
3472
+ }
3473
+ }
3474
+ },
3475
+ {
3476
+ name: "get-network-volume",
3477
+ title: "Get network volume",
3478
+ operationId: "getNetworkVolume",
3479
+ description: "Get a network volume. Returns a single network volume by ID.",
3480
+ method: "GET",
3481
+ path: "/v2/network-volumes/{id}",
3482
+ params: [
3483
+ {
3484
+ name: "id",
3485
+ location: "path"
3486
+ }
3487
+ ],
3488
+ hasBody: false,
3489
+ annotations: {
3490
+ readOnlyHint: true,
3491
+ destructiveHint: false,
3492
+ idempotentHint: true,
3493
+ openWorldHint: true
3494
+ },
3495
+ inputSchema: {
3496
+ type: "object",
3497
+ properties: {
3498
+ id: {
3499
+ type: "string",
3500
+ description: "Network volume identifier"
3501
+ }
3502
+ },
3503
+ required: ["id"]
3504
+ }
3505
+ },
3506
+ {
3507
+ name: "get-organization",
3508
+ title: "Get organization",
3509
+ operationId: "getOrganization",
3510
+ 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.",
3511
+ method: "GET",
3512
+ path: "/v2/organization",
3513
+ params: [],
3514
+ hasBody: false,
3515
+ annotations: {
3516
+ readOnlyHint: true,
3517
+ destructiveHint: false,
3518
+ idempotentHint: true,
3519
+ openWorldHint: true
3520
+ },
3521
+ inputSchema: {
3522
+ type: "object",
3523
+ properties: {}
3524
+ }
3525
+ },
3526
+ {
3527
+ name: "get-organization-group",
3528
+ title: "Get organization group",
3529
+ operationId: "getOrganizationGroup",
3530
+ 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.",
3531
+ method: "GET",
3532
+ path: "/v2/organization/groups/{id}",
3533
+ params: [
3534
+ {
3535
+ name: "id",
3536
+ location: "path"
3537
+ }
3538
+ ],
3539
+ hasBody: false,
3540
+ annotations: {
3541
+ readOnlyHint: true,
3542
+ destructiveHint: false,
3543
+ idempotentHint: true,
3544
+ openWorldHint: true
3545
+ },
3546
+ inputSchema: {
3547
+ type: "object",
3548
+ properties: {
3549
+ id: {
3550
+ type: "string",
3551
+ minLength: 1,
3552
+ description: "Group ID."
3553
+ }
3554
+ },
3555
+ required: ["id"]
3556
+ }
3557
+ },
3558
+ {
3559
+ name: "get-organization-invite",
3560
+ title: "Get organization invite",
3561
+ operationId: "getOrganizationInvite",
3562
+ 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.",
3563
+ method: "GET",
3564
+ path: "/v2/organization/invites/{id}",
3565
+ params: [
3566
+ {
3567
+ name: "id",
3568
+ location: "path"
3569
+ }
3570
+ ],
3571
+ hasBody: false,
3572
+ annotations: {
3573
+ readOnlyHint: true,
3574
+ destructiveHint: false,
3575
+ idempotentHint: true,
3576
+ openWorldHint: true
3577
+ },
3578
+ inputSchema: {
3579
+ type: "object",
3580
+ properties: {
3581
+ id: {
3582
+ type: "string",
3583
+ description: "Invite identifier"
2619
3584
  }
2620
- }
3585
+ },
3586
+ required: ["id"]
2621
3587
  }
2622
3588
  },
2623
3589
  {
2624
- name: "get-network-volume",
2625
- title: "Get network volume",
2626
- operationId: "getNetworkVolume",
2627
- description: "Get a network volume. Returns a single network volume by ID.",
3590
+ name: "get-organization-role",
3591
+ title: "Get organization role",
3592
+ operationId: "getOrganizationRole",
3593
+ 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.",
2628
3594
  method: "GET",
2629
- path: "/v2/network-volumes/{id}",
3595
+ path: "/v2/organization/roles/{id}",
2630
3596
  params: [
2631
3597
  {
2632
3598
  name: "id",
@@ -2645,20 +3611,31 @@ var generatedTools = [
2645
3611
  properties: {
2646
3612
  id: {
2647
3613
  type: "string",
2648
- description: "Network volume identifier"
3614
+ description: "Role identifier"
2649
3615
  }
2650
3616
  },
2651
3617
  required: ["id"]
2652
3618
  }
2653
3619
  },
2654
3620
  {
2655
- name: "get-organization",
2656
- title: "Get organization",
2657
- operationId: "getOrganization",
2658
- 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.",
3621
+ name: "get-organization-role-capabilities",
3622
+ title: "Get organization role capabilities",
3623
+ operationId: "getOrganizationRoleCapabilities",
3624
+ 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.",
2659
3625
  method: "GET",
2660
- path: "/v2/organization",
2661
- params: [],
3626
+ path: "/v2/organization/roles/capabilities",
3627
+ params: [
3628
+ {
3629
+ name: "family",
3630
+ location: "query",
3631
+ explode: false
3632
+ },
3633
+ {
3634
+ name: "verb",
3635
+ location: "query",
3636
+ explode: false
3637
+ }
3638
+ ],
2662
3639
  hasBody: false,
2663
3640
  annotations: {
2664
3641
  readOnlyHint: true,
@@ -2668,7 +3645,22 @@ var generatedTools = [
2668
3645
  },
2669
3646
  inputSchema: {
2670
3647
  type: "object",
2671
- properties: {}
3648
+ properties: {
3649
+ family: {
3650
+ type: "array",
3651
+ items: {
3652
+ type: "string"
3653
+ },
3654
+ description: "Only return these capability families, matched exactly. Comma-separated; a family matching any listed value is returned. An empty value is ignored."
3655
+ },
3656
+ verb: {
3657
+ type: "array",
3658
+ items: {
3659
+ type: "string"
3660
+ },
3661
+ 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."
3662
+ }
3663
+ }
2672
3664
  }
2673
3665
  },
2674
3666
  {
@@ -3151,6 +4143,107 @@ var generatedTools = [
3151
4143
  }
3152
4144
  }
3153
4145
  },
4146
+ {
4147
+ name: "list-cost-center-resources",
4148
+ title: "List cost center resources",
4149
+ operationId: "listCostCenterResources",
4150
+ 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.",
4151
+ method: "GET",
4152
+ path: "/v2/cost-centers/{id}/resources",
4153
+ params: [
4154
+ {
4155
+ name: "id",
4156
+ location: "path"
4157
+ },
4158
+ {
4159
+ name: "cursor",
4160
+ location: "query"
4161
+ },
4162
+ {
4163
+ name: "limit",
4164
+ location: "query"
4165
+ }
4166
+ ],
4167
+ hasBody: false,
4168
+ annotations: {
4169
+ readOnlyHint: true,
4170
+ destructiveHint: false,
4171
+ idempotentHint: true,
4172
+ openWorldHint: true
4173
+ },
4174
+ inputSchema: {
4175
+ type: "object",
4176
+ properties: {
4177
+ id: {
4178
+ type: "string",
4179
+ minLength: 1,
4180
+ description: "Cost center identifier."
4181
+ },
4182
+ cursor: {
4183
+ type: "string",
4184
+ minLength: 1,
4185
+ 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."
4186
+ },
4187
+ limit: {
4188
+ type: "integer",
4189
+ minimum: 1,
4190
+ maximum: 1e3,
4191
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4192
+ }
4193
+ },
4194
+ required: ["id"]
4195
+ }
4196
+ },
4197
+ {
4198
+ name: "list-cost-centers",
4199
+ title: "List cost centers",
4200
+ operationId: "listCostCenters",
4201
+ 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.",
4202
+ method: "GET",
4203
+ path: "/v2/cost-centers",
4204
+ params: [
4205
+ {
4206
+ name: "search",
4207
+ location: "query"
4208
+ },
4209
+ {
4210
+ name: "cursor",
4211
+ location: "query"
4212
+ },
4213
+ {
4214
+ name: "limit",
4215
+ location: "query"
4216
+ }
4217
+ ],
4218
+ hasBody: false,
4219
+ annotations: {
4220
+ readOnlyHint: true,
4221
+ destructiveHint: false,
4222
+ idempotentHint: true,
4223
+ openWorldHint: true
4224
+ },
4225
+ inputSchema: {
4226
+ type: "object",
4227
+ properties: {
4228
+ search: {
4229
+ type: "string",
4230
+ minLength: 1,
4231
+ description: "Case-insensitive substring match against `value`."
4232
+ },
4233
+ cursor: {
4234
+ type: "string",
4235
+ minLength: 1,
4236
+ 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."
4237
+ },
4238
+ limit: {
4239
+ type: "integer",
4240
+ minimum: 1,
4241
+ maximum: 1e3,
4242
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4243
+ }
4244
+ }
4245
+ }
4246
+ },
3154
4247
  {
3155
4248
  name: "list-cpu-types",
3156
4249
  title: "List CPU types",
@@ -3681,7 +4774,7 @@ var generatedTools = [
3681
4774
  },
3682
4775
  cloud: {
3683
4776
  $ref: "#/$defs/GpuCloudFilter",
3684
- description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Upstream default when omitted: SECURE.",
4777
+ description: "Cloud type for availability and lowest-price calculations. Valid only with include=AVAILABILITY. Supported values: SECURE, COMMUNITY. Default when omitted: SECURE.",
3685
4778
  type: "string"
3686
4779
  },
3687
4780
  countryCodes: {
@@ -3714,43 +4807,151 @@ var generatedTools = [
3714
4807
  },
3715
4808
  GpuCloudFilter: {
3716
4809
  type: "string",
3717
- description: "GPU availability cloud filter.",
3718
- enum: ["SECURE", "COMMUNITY"]
4810
+ description: "GPU availability cloud filter.",
4811
+ enum: ["SECURE", "COMMUNITY"]
4812
+ },
4813
+ Product: {
4814
+ type: "string",
4815
+ description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
4816
+ enum: ["POD", "CLUSTER", "SERVERLESS"]
4817
+ }
4818
+ }
4819
+ }
4820
+ },
4821
+ {
4822
+ name: "list-network-volume-billing",
4823
+ title: "List network volume billing",
4824
+ operationId: "listNetworkVolumeBilling",
4825
+ 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
+ method: "GET",
4827
+ path: "/v2/billing/network-volumes",
4828
+ params: [
4829
+ {
4830
+ name: "startTime",
4831
+ location: "query"
4832
+ },
4833
+ {
4834
+ name: "endTime",
4835
+ location: "query"
4836
+ },
4837
+ {
4838
+ name: "bucketSize",
4839
+ location: "query"
4840
+ },
4841
+ {
4842
+ name: "lastN",
4843
+ location: "query"
4844
+ },
4845
+ {
4846
+ name: "networkVolumeId",
4847
+ location: "query"
4848
+ }
4849
+ ],
4850
+ hasBody: false,
4851
+ annotations: {
4852
+ readOnlyHint: true,
4853
+ destructiveHint: false,
4854
+ idempotentHint: true,
4855
+ openWorldHint: true
4856
+ },
4857
+ inputSchema: {
4858
+ type: "object",
4859
+ properties: {
4860
+ startTime: {
4861
+ type: "string",
4862
+ format: "date-time",
4863
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4864
+ },
4865
+ endTime: {
4866
+ type: "string",
4867
+ format: "date-time",
4868
+ 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."
4869
+ },
4870
+ bucketSize: {
4871
+ $ref: "#/$defs/BillingBucketSize",
4872
+ description: "Length of each billing time bucket. Defaults to day.",
4873
+ type: "string"
4874
+ },
4875
+ lastN: {
4876
+ type: "integer",
4877
+ minimum: 1,
4878
+ 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".'
4879
+ },
4880
+ networkVolumeId: {
4881
+ type: "string",
4882
+ description: "Filter to a specific network volume."
4883
+ }
4884
+ },
4885
+ $defs: {
4886
+ BillingBucketSize: {
4887
+ type: "string",
4888
+ enum: ["hour", "day", "week", "month", "year"],
4889
+ default: "day",
4890
+ description: "Length of each billing time bucket.",
4891
+ examples: ["day"]
4892
+ }
4893
+ }
4894
+ }
4895
+ },
4896
+ {
4897
+ name: "list-network-volumes",
4898
+ title: "List network volumes",
4899
+ operationId: "listNetworkVolumes",
4900
+ 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.",
4901
+ method: "GET",
4902
+ path: "/v2/network-volumes",
4903
+ params: [
4904
+ {
4905
+ name: "cursor",
4906
+ location: "query"
4907
+ },
4908
+ {
4909
+ name: "limit",
4910
+ location: "query"
4911
+ }
4912
+ ],
4913
+ hasBody: false,
4914
+ annotations: {
4915
+ readOnlyHint: true,
4916
+ destructiveHint: false,
4917
+ idempotentHint: true,
4918
+ openWorldHint: true
4919
+ },
4920
+ inputSchema: {
4921
+ type: "object",
4922
+ properties: {
4923
+ cursor: {
4924
+ type: "string",
4925
+ minLength: 1,
4926
+ 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."
3719
4927
  },
3720
- Product: {
3721
- type: "string",
3722
- description: "Catalog product availability context. Availability is product-specific, so this is required whenever availability is requested.",
3723
- enum: ["POD", "CLUSTER", "SERVERLESS"]
4928
+ limit: {
4929
+ type: "integer",
4930
+ minimum: 1,
4931
+ maximum: 1e3,
4932
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3724
4933
  }
3725
4934
  }
3726
4935
  }
3727
4936
  },
3728
4937
  {
3729
- name: "list-network-volume-billing",
3730
- title: "List network volume billing",
3731
- operationId: "listNetworkVolumeBilling",
3732
- 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.",
4938
+ name: "list-organization-group-members",
4939
+ title: "List organization group members",
4940
+ operationId: "listOrganizationGroupMembers",
4941
+ 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.",
3733
4942
  method: "GET",
3734
- path: "/v2/billing/network-volumes",
4943
+ path: "/v2/organization/groups/{id}/members",
3735
4944
  params: [
3736
4945
  {
3737
- name: "startTime",
3738
- location: "query"
3739
- },
3740
- {
3741
- name: "endTime",
3742
- location: "query"
3743
- },
3744
- {
3745
- name: "bucketSize",
3746
- location: "query"
4946
+ name: "id",
4947
+ location: "path"
3747
4948
  },
3748
4949
  {
3749
- name: "lastN",
4950
+ name: "cursor",
3750
4951
  location: "query"
3751
4952
  },
3752
4953
  {
3753
- name: "networkVolumeId",
4954
+ name: "limit",
3754
4955
  location: "query"
3755
4956
  }
3756
4957
  ],
@@ -3764,50 +4965,38 @@ var generatedTools = [
3764
4965
  inputSchema: {
3765
4966
  type: "object",
3766
4967
  properties: {
3767
- startTime: {
4968
+ id: {
3768
4969
  type: "string",
3769
- format: "date-time",
3770
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4970
+ minLength: 1,
4971
+ description: "Group ID."
3771
4972
  },
3772
- endTime: {
4973
+ cursor: {
3773
4974
  type: "string",
3774
- format: "date-time",
3775
- 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."
3776
- },
3777
- bucketSize: {
3778
- $ref: "#/$defs/BillingBucketSize",
3779
- description: "Length of each billing time bucket. Defaults to day.",
3780
- type: "string"
4975
+ minLength: 1,
4976
+ 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."
3781
4977
  },
3782
- lastN: {
4978
+ limit: {
3783
4979
  type: "integer",
3784
4980
  minimum: 1,
3785
- 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".'
3786
- },
3787
- networkVolumeId: {
3788
- type: "string",
3789
- description: "Filter to a specific network volume."
4981
+ maximum: 100,
4982
+ description: "Page size, 1\u2013100. Defaults to 100 when omitted."
3790
4983
  }
3791
4984
  },
3792
- $defs: {
3793
- BillingBucketSize: {
3794
- type: "string",
3795
- enum: ["hour", "day", "week", "month", "year"],
3796
- default: "day",
3797
- description: "Length of each billing time bucket.",
3798
- examples: ["day"]
3799
- }
3800
- }
4985
+ required: ["id"]
3801
4986
  }
3802
4987
  },
3803
4988
  {
3804
- name: "list-network-volumes",
3805
- title: "List network volumes",
3806
- operationId: "listNetworkVolumes",
3807
- 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.",
4989
+ name: "list-organization-groups",
4990
+ title: "List organization groups",
4991
+ operationId: "listOrganizationGroups",
4992
+ 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.",
3808
4993
  method: "GET",
3809
- path: "/v2/network-volumes",
4994
+ path: "/v2/organization/groups",
3810
4995
  params: [
4996
+ {
4997
+ name: "name",
4998
+ location: "query"
4999
+ },
3811
5000
  {
3812
5001
  name: "cursor",
3813
5002
  location: "query"
@@ -3827,6 +5016,10 @@ var generatedTools = [
3827
5016
  inputSchema: {
3828
5017
  type: "object",
3829
5018
  properties: {
5019
+ name: {
5020
+ type: "string",
5021
+ description: "Optional case-insensitive substring of the group name. An empty value applies no filter."
5022
+ },
3830
5023
  cursor: {
3831
5024
  type: "string",
3832
5025
  minLength: 1,
@@ -3835,12 +5028,52 @@ var generatedTools = [
3835
5028
  limit: {
3836
5029
  type: "integer",
3837
5030
  minimum: 1,
3838
- maximum: 1e3,
3839
- description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
5031
+ maximum: 100,
5032
+ description: "Page size, 1\u2013100. Defaults to 100 when omitted."
3840
5033
  }
3841
5034
  }
3842
5035
  }
3843
5036
  },
5037
+ {
5038
+ name: "list-organization-invites",
5039
+ title: "List organization invites",
5040
+ operationId: "listOrganizationInvites",
5041
+ 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.",
5042
+ method: "GET",
5043
+ path: "/v2/organization/invites",
5044
+ params: [],
5045
+ hasBody: false,
5046
+ annotations: {
5047
+ readOnlyHint: true,
5048
+ destructiveHint: false,
5049
+ idempotentHint: true,
5050
+ openWorldHint: true
5051
+ },
5052
+ inputSchema: {
5053
+ type: "object",
5054
+ properties: {}
5055
+ }
5056
+ },
5057
+ {
5058
+ name: "list-organization-roles",
5059
+ title: "List organization roles",
5060
+ operationId: "listOrganizationRoles",
5061
+ 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.",
5062
+ method: "GET",
5063
+ path: "/v2/organization/roles",
5064
+ params: [],
5065
+ hasBody: false,
5066
+ annotations: {
5067
+ readOnlyHint: true,
5068
+ destructiveHint: false,
5069
+ idempotentHint: true,
5070
+ openWorldHint: true
5071
+ },
5072
+ inputSchema: {
5073
+ type: "object",
5074
+ properties: {}
5075
+ }
5076
+ },
3844
5077
  {
3845
5078
  name: "list-organization-users",
3846
5079
  title: "List organization users",
@@ -4195,6 +5428,47 @@ var generatedTools = [
4195
5428
  }
4196
5429
  }
4197
5430
  },
5431
+ {
5432
+ name: "remove-organization-group-member",
5433
+ title: "Remove organization group member",
5434
+ operationId: "removeOrganizationGroupMember",
5435
+ 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.",
5436
+ method: "DELETE",
5437
+ path: "/v2/organization/groups/{id}/members/{userId}",
5438
+ params: [
5439
+ {
5440
+ name: "id",
5441
+ location: "path"
5442
+ },
5443
+ {
5444
+ name: "userId",
5445
+ location: "path"
5446
+ }
5447
+ ],
5448
+ hasBody: false,
5449
+ annotations: {
5450
+ readOnlyHint: false,
5451
+ destructiveHint: true,
5452
+ idempotentHint: true,
5453
+ openWorldHint: true
5454
+ },
5455
+ inputSchema: {
5456
+ type: "object",
5457
+ properties: {
5458
+ id: {
5459
+ type: "string",
5460
+ minLength: 1,
5461
+ description: "Group ID."
5462
+ },
5463
+ userId: {
5464
+ type: "string",
5465
+ minLength: 1,
5466
+ description: "Organization member user ID."
5467
+ }
5468
+ },
5469
+ required: ["id", "userId"]
5470
+ }
5471
+ },
4198
5472
  {
4199
5473
  name: "revoke-delegation",
4200
5474
  title: "Revoke ECR delegation",
@@ -4275,6 +5549,59 @@ var generatedTools = [
4275
5549
  }
4276
5550
  }
4277
5551
  },
5552
+ {
5553
+ name: "update-cost-center",
5554
+ title: "Update cost center",
5555
+ operationId: "updateCostCenter",
5556
+ 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.",
5557
+ method: "PATCH",
5558
+ path: "/v2/cost-centers/{id}",
5559
+ params: [
5560
+ {
5561
+ name: "id",
5562
+ location: "path"
5563
+ }
5564
+ ],
5565
+ hasBody: true,
5566
+ annotations: {
5567
+ readOnlyHint: false,
5568
+ destructiveHint: false,
5569
+ idempotentHint: true,
5570
+ openWorldHint: true
5571
+ },
5572
+ inputSchema: {
5573
+ type: "object",
5574
+ properties: {
5575
+ id: {
5576
+ type: "string",
5577
+ minLength: 1,
5578
+ description: "Cost center identifier."
5579
+ },
5580
+ body: {
5581
+ $ref: "#/$defs/UpdateCostCenterRequest",
5582
+ type: "object"
5583
+ }
5584
+ },
5585
+ required: ["id", "body"],
5586
+ $defs: {
5587
+ UpdateCostCenterRequest: {
5588
+ type: "object",
5589
+ additionalProperties: false,
5590
+ required: ["value"],
5591
+ properties: {
5592
+ value: {
5593
+ type: "string",
5594
+ minLength: 1,
5595
+ maxLength: 50,
5596
+ pattern: "^\\s*\\S",
5597
+ description: "Leading and trailing whitespace is trimmed.",
5598
+ examples: ["team-ml-eu"]
5599
+ }
5600
+ }
5601
+ }
5602
+ }
5603
+ }
5604
+ },
4278
5605
  {
4279
5606
  name: "update-endpoint",
4280
5607
  title: "Update endpoint",
@@ -4662,19 +5989,130 @@ var generatedTools = [
4662
5989
  }
4663
5990
  }
4664
5991
  }
4665
- ],
4666
- unevaluatedProperties: false
5992
+ ],
5993
+ unevaluatedProperties: false
5994
+ }
5995
+ }
5996
+ }
5997
+ },
5998
+ {
5999
+ name: "update-global-volume",
6000
+ title: "Update global volume",
6001
+ operationId: "updateGlobalVolume",
6002
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
6003
+ method: "PATCH",
6004
+ path: "/v2/global-volumes/{id}",
6005
+ params: [
6006
+ {
6007
+ name: "id",
6008
+ location: "path"
6009
+ }
6010
+ ],
6011
+ hasBody: true,
6012
+ annotations: {
6013
+ readOnlyHint: false,
6014
+ destructiveHint: false,
6015
+ idempotentHint: true,
6016
+ openWorldHint: true
6017
+ },
6018
+ inputSchema: {
6019
+ type: "object",
6020
+ properties: {
6021
+ id: {
6022
+ type: "string",
6023
+ description: "Global volume identifier"
6024
+ },
6025
+ body: {
6026
+ $ref: "#/$defs/UpdateGlobalVolumeRequest",
6027
+ type: "object"
6028
+ }
6029
+ },
6030
+ required: ["id", "body"],
6031
+ $defs: {
6032
+ UpdateGlobalVolumeRequest: {
6033
+ type: "object",
6034
+ additionalProperties: false,
6035
+ minProperties: 1,
6036
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6037
+ properties: {
6038
+ name: {
6039
+ type: "string",
6040
+ minLength: 1,
6041
+ maxLength: 191,
6042
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6043
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
6044
+ examples: ["renamed-volume"]
6045
+ }
6046
+ }
6047
+ }
6048
+ }
6049
+ }
6050
+ },
6051
+ {
6052
+ name: "update-network-volume",
6053
+ title: "Update network volume",
6054
+ operationId: "updateNetworkVolume",
6055
+ 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.",
6056
+ method: "PATCH",
6057
+ path: "/v2/network-volumes/{id}",
6058
+ params: [
6059
+ {
6060
+ name: "id",
6061
+ location: "path"
6062
+ }
6063
+ ],
6064
+ hasBody: true,
6065
+ annotations: {
6066
+ readOnlyHint: false,
6067
+ destructiveHint: false,
6068
+ idempotentHint: true,
6069
+ openWorldHint: true
6070
+ },
6071
+ inputSchema: {
6072
+ type: "object",
6073
+ properties: {
6074
+ id: {
6075
+ type: "string",
6076
+ description: "Network volume identifier"
6077
+ },
6078
+ body: {
6079
+ $ref: "#/$defs/UpdateNetworkVolumeRequest",
6080
+ type: "object"
6081
+ }
6082
+ },
6083
+ required: ["id", "body"],
6084
+ $defs: {
6085
+ UpdateNetworkVolumeRequest: {
6086
+ type: "object",
6087
+ additionalProperties: false,
6088
+ minProperties: 1,
6089
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6090
+ properties: {
6091
+ name: {
6092
+ type: "string",
6093
+ minLength: 1,
6094
+ description: "New human-readable name",
6095
+ examples: ["renamed-volume"]
6096
+ },
6097
+ size: {
6098
+ type: "integer",
6099
+ minimum: 10,
6100
+ maximum: 4096,
6101
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
6102
+ examples: [100]
6103
+ }
6104
+ }
4667
6105
  }
4668
6106
  }
4669
6107
  }
4670
6108
  },
4671
6109
  {
4672
- name: "update-global-volume",
4673
- title: "Update global volume",
4674
- operationId: "updateGlobalVolume",
4675
- description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
6110
+ name: "update-organization-group",
6111
+ title: "Update organization group",
6112
+ operationId: "updateOrganizationGroup",
6113
+ description: "Rename a group in your current organization",
4676
6114
  method: "PATCH",
4677
- path: "/v2/global-volumes/{id}",
6115
+ path: "/v2/organization/groups/{id}",
4678
6116
  params: [
4679
6117
  {
4680
6118
  name: "id",
@@ -4693,27 +6131,28 @@ var generatedTools = [
4693
6131
  properties: {
4694
6132
  id: {
4695
6133
  type: "string",
4696
- description: "Global volume identifier"
6134
+ minLength: 1,
6135
+ description: "Group ID."
4697
6136
  },
4698
6137
  body: {
4699
- $ref: "#/$defs/UpdateGlobalVolumeRequest",
6138
+ $ref: "#/$defs/UpdateOrganizationGroupRequest",
4700
6139
  type: "object"
4701
6140
  }
4702
6141
  },
4703
6142
  required: ["id", "body"],
4704
6143
  $defs: {
4705
- UpdateGlobalVolumeRequest: {
6144
+ UpdateOrganizationGroupRequest: {
4706
6145
  type: "object",
4707
6146
  additionalProperties: false,
4708
- minProperties: 1,
4709
- description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6147
+ required: ["name"],
4710
6148
  properties: {
4711
6149
  name: {
4712
6150
  type: "string",
4713
6151
  minLength: 1,
4714
6152
  maxLength: 191,
4715
- description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
4716
- examples: ["renamed-volume"]
6153
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6154
+ description: "Group name with at least one non-whitespace character; surrounding whitespace is trimmed.",
6155
+ examples: ["Research"]
4717
6156
  }
4718
6157
  }
4719
6158
  }
@@ -4721,12 +6160,12 @@ var generatedTools = [
4721
6160
  }
4722
6161
  },
4723
6162
  {
4724
- name: "update-network-volume",
4725
- title: "Update network volume",
4726
- operationId: "updateNetworkVolume",
4727
- 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.",
6163
+ name: "update-organization-role",
6164
+ title: "Update organization role",
6165
+ operationId: "updateOrganizationRole",
6166
+ 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.",
4728
6167
  method: "PATCH",
4729
- path: "/v2/network-volumes/{id}",
6168
+ path: "/v2/organization/roles/{id}",
4730
6169
  params: [
4731
6170
  {
4732
6171
  name: "id",
@@ -4736,7 +6175,7 @@ var generatedTools = [
4736
6175
  hasBody: true,
4737
6176
  annotations: {
4738
6177
  readOnlyHint: false,
4739
- destructiveHint: false,
6178
+ destructiveHint: true,
4740
6179
  idempotentHint: true,
4741
6180
  openWorldHint: true
4742
6181
  },
@@ -4745,33 +6184,129 @@ var generatedTools = [
4745
6184
  properties: {
4746
6185
  id: {
4747
6186
  type: "string",
4748
- description: "Network volume identifier"
6187
+ description: "Role identifier"
4749
6188
  },
4750
6189
  body: {
4751
- $ref: "#/$defs/UpdateNetworkVolumeRequest",
6190
+ $ref: "#/$defs/UpdateOrganizationRoleRequest",
4752
6191
  type: "object"
4753
6192
  }
4754
6193
  },
4755
6194
  required: ["id", "body"],
4756
6195
  $defs: {
4757
- UpdateNetworkVolumeRequest: {
6196
+ OrganizationRoleCapabilitiesInput: {
6197
+ type: "array",
6198
+ description: "What the role grants, as `FAMILY:VERB` capabilities from `get-organization-role-capabilities`. The caller must hold every capability it lists.",
6199
+ minItems: 1,
6200
+ uniqueItems: true,
6201
+ items: {
6202
+ type: "string",
6203
+ pattern: "^[A-Z0-9_]+:[A-Z0-9_]+$"
6204
+ },
6205
+ examples: [["POD:VIEW"]]
6206
+ },
6207
+ OrganizationRoleName: {
6208
+ type: "string",
6209
+ minLength: 1,
6210
+ maxLength: 191,
6211
+ pattern: "[^\\t-\\r \x85\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]",
6212
+ 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.",
6213
+ examples: ["Pod viewer"]
6214
+ },
6215
+ UpdateOrganizationRoleRequest: {
4758
6216
  type: "object",
4759
6217
  additionalProperties: false,
4760
6218
  minProperties: 1,
4761
- description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
6219
+ description: "The fields to change; omitted fields are unchanged. `capabilities` replaces the role's whole grant set rather than adding to it.",
4762
6220
  properties: {
4763
6221
  name: {
6222
+ $ref: "#/$defs/OrganizationRoleName"
6223
+ },
6224
+ capabilities: {
6225
+ $ref: "#/$defs/OrganizationRoleCapabilitiesInput"
6226
+ }
6227
+ }
6228
+ }
6229
+ }
6230
+ }
6231
+ },
6232
+ {
6233
+ name: "update-organization-user",
6234
+ title: "Update organization user",
6235
+ operationId: "updateOrganizationUser",
6236
+ 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.",
6237
+ method: "PATCH",
6238
+ path: "/v2/organization/users/{id}",
6239
+ params: [
6240
+ {
6241
+ name: "id",
6242
+ location: "path"
6243
+ }
6244
+ ],
6245
+ hasBody: true,
6246
+ annotations: {
6247
+ readOnlyHint: false,
6248
+ destructiveHint: true,
6249
+ idempotentHint: true,
6250
+ openWorldHint: true
6251
+ },
6252
+ inputSchema: {
6253
+ type: "object",
6254
+ properties: {
6255
+ id: {
6256
+ type: "string",
6257
+ description: "User identifier"
6258
+ },
6259
+ body: {
6260
+ $ref: "#/$defs/UpdateOrganizationUserRequest",
6261
+ type: "object"
6262
+ }
6263
+ },
6264
+ required: ["id", "body"],
6265
+ $defs: {
6266
+ OrganizationGroupInput: {
6267
+ type: "object",
6268
+ description: "An organization group, by ID.",
6269
+ additionalProperties: false,
6270
+ required: ["id"],
6271
+ properties: {
6272
+ id: {
4764
6273
  type: "string",
4765
6274
  minLength: 1,
4766
- description: "New human-readable name",
4767
- examples: ["renamed-volume"]
6275
+ description: "Group ID.",
6276
+ examples: ["group_id_abc_123"]
6277
+ }
6278
+ }
6279
+ },
6280
+ OrganizationRoleInput: {
6281
+ type: "object",
6282
+ description: "An organization role to assign, by ID.",
6283
+ additionalProperties: false,
6284
+ required: ["id"],
6285
+ properties: {
6286
+ id: {
6287
+ type: "string",
6288
+ minLength: 1,
6289
+ 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.",
6290
+ examples: ["builtin:dev"]
6291
+ }
6292
+ }
6293
+ },
6294
+ UpdateOrganizationUserRequest: {
6295
+ type: "object",
6296
+ description: "Changes to a member. Send `role`, `groups`, or both; an omitted field is left as it is.",
6297
+ additionalProperties: false,
6298
+ minProperties: 1,
6299
+ properties: {
6300
+ role: {
6301
+ $ref: "#/$defs/OrganizationRoleInput"
4768
6302
  },
4769
- size: {
4770
- type: "integer",
4771
- minimum: 10,
4772
- maximum: 4096,
4773
- description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4774
- examples: [100]
6303
+ groups: {
6304
+ type: "array",
6305
+ description: "The member's groups after the change: the whole set, by ID. An empty list removes the member from every group.",
6306
+ uniqueItems: true,
6307
+ items: {
6308
+ $ref: "#/$defs/OrganizationGroupInput"
6309
+ }
4775
6310
  }
4776
6311
  }
4777
6312
  }
@@ -4782,7 +6317,7 @@ var generatedTools = [
4782
6317
  name: "update-pod",
4783
6318
  title: "Update Pod",
4784
6319
  operationId: "updatePod",
4785
- 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.",
6320
+ 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.",
4786
6321
  method: "PATCH",
4787
6322
  path: "/v2/pods/{id}",
4788
6323
  params: [
@@ -4876,21 +6411,6 @@ var generatedTools = [
4876
6411
  }
4877
6412
  }
4878
6413
  },
4879
- BaseMounts: {
4880
- type: "object",
4881
- properties: {
4882
- persistent: {
4883
- $ref: "#/$defs/PersistentMount"
4884
- },
4885
- network: {
4886
- type: "array",
4887
- maxItems: 1,
4888
- items: {
4889
- $ref: "#/$defs/NetworkMount"
4890
- }
4891
- }
4892
- }
4893
- },
4894
6414
  ContainerConfig: {
4895
6415
  allOf: [
4896
6416
  {
@@ -4908,11 +6428,11 @@ var generatedTools = [
4908
6428
  }
4909
6429
  ]
4910
6430
  },
4911
- GlobalMount: {
6431
+ GlobalMountPatch: {
4912
6432
  type: "object",
4913
6433
  required: ["volumeId"],
4914
6434
  additionalProperties: false,
4915
- 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.",
6435
+ description: "Mounts, moves, or unmounts the pod's global volume.",
4916
6436
  properties: {
4917
6437
  volumeId: {
4918
6438
  type: "string",
@@ -4921,38 +6441,21 @@ var generatedTools = [
4921
6441
  },
4922
6442
  path: {
4923
6443
  type: "string",
4924
- 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.",
6444
+ minLength: 1,
6445
+ description: "Mount path inside the container. Optional; when mounting, a default is chosen for you if omitted.",
4925
6446
  examples: ["/data"]
4926
- }
4927
- }
4928
- },
4929
- Mounts: {
4930
- 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.",
4931
- allOf: [
4932
- {
4933
- $ref: "#/$defs/BaseMounts"
4934
6447
  },
4935
- {
4936
- type: "object",
4937
- properties: {
4938
- global: {
4939
- type: "array",
4940
- maxItems: 1,
4941
- 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.",
4942
- items: {
4943
- $ref: "#/$defs/GlobalMount"
4944
- }
4945
- }
4946
- }
6448
+ remove: {
6449
+ type: "boolean",
6450
+ description: "Unmount the volume. Cannot be combined with `path`."
4947
6451
  }
4948
- ],
4949
- unevaluatedProperties: false
6452
+ }
4950
6453
  },
4951
- NetworkMount: {
6454
+ NetworkMountPatch: {
4952
6455
  type: "object",
4953
- required: ["volumeId", "path"],
6456
+ required: ["volumeId"],
4954
6457
  additionalProperties: false,
4955
- 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.",
6458
+ description: "Mounts, moves, or unmounts one network volume. Mounting a volume the pod does not have requires `path`.",
4956
6459
  properties: {
4957
6460
  volumeId: {
4958
6461
  type: "string",
@@ -4961,27 +6464,62 @@ var generatedTools = [
4961
6464
  },
4962
6465
  path: {
4963
6466
  type: "string",
4964
- description: "Mount path inside the container. No default \u2014 must be specified explicitly.",
6467
+ minLength: 1,
6468
+ description: "Mount path inside the container. Required when mounting; optional when moving a mounted volume.",
4965
6469
  examples: ["/runpod-volume"]
6470
+ },
6471
+ remove: {
6472
+ type: "boolean",
6473
+ description: "Unmount the volume. Cannot be combined with `path`."
4966
6474
  }
4967
6475
  }
4968
6476
  },
4969
- PersistentMount: {
6477
+ PersistentMountPatch: {
4970
6478
  type: "object",
4971
- required: ["size", "path"],
4972
6479
  additionalProperties: false,
4973
- 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.",
6480
+ 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.",
4974
6481
  properties: {
4975
6482
  size: {
4976
6483
  type: "integer",
4977
6484
  minimum: 10,
4978
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
4979
- examples: [20]
6485
+ description: "Volume disk size in GB. Can only grow; a smaller size than the current one is rejected.",
6486
+ examples: [50]
4980
6487
  },
4981
6488
  path: {
4982
6489
  type: "string",
4983
- description: "Mount path inside the container. May be changed via PATCH.",
6490
+ minLength: 1,
6491
+ description: "Mount path inside the container.",
4984
6492
  examples: ["/workspace"]
6493
+ },
6494
+ remove: {
6495
+ type: "boolean",
6496
+ description: "Removes the volume disk. Not supported yet: `remove: true` is rejected."
6497
+ }
6498
+ }
6499
+ },
6500
+ UpdatePodMounts: {
6501
+ type: "object",
6502
+ additionalProperties: false,
6503
+ 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.",
6504
+ properties: {
6505
+ persistent: {
6506
+ $ref: "#/$defs/PersistentMountPatch"
6507
+ },
6508
+ network: {
6509
+ type: "array",
6510
+ maxItems: 8,
6511
+ 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.",
6512
+ items: {
6513
+ $ref: "#/$defs/NetworkMountPatch"
6514
+ }
6515
+ },
6516
+ global: {
6517
+ type: "array",
6518
+ maxItems: 2,
6519
+ 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.",
6520
+ items: {
6521
+ $ref: "#/$defs/GlobalMountPatch"
6522
+ }
4985
6523
  }
4986
6524
  }
4987
6525
  },
@@ -5002,7 +6540,7 @@ var generatedTools = [
5002
6540
  description: "Lock the pod (true) or unlock it (false). Locked pods cannot be stopped or reset."
5003
6541
  },
5004
6542
  mounts: {
5005
- $ref: "#/$defs/Mounts"
6543
+ $ref: "#/$defs/UpdatePodMounts"
5006
6544
  },
5007
6545
  name: {
5008
6546
  type: "string",
@@ -5249,7 +6787,7 @@ var generatedTools = [
5249
6787
  size: {
5250
6788
  type: "integer",
5251
6789
  minimum: 10,
5252
- description: "Host-local persistent storage in GB. Upstream enforces a 10 GB floor.",
6790
+ description: "Host-local persistent storage in GB. Minimum 10 GB.",
5253
6791
  examples: [20]
5254
6792
  },
5255
6793
  path: {
@@ -5510,33 +7048,53 @@ var CREDENTIAL_MANAGEMENT = "Manages stored credentials; OpenAI restricts plugin
5510
7048
  var CREDENTIAL_INPUT = "Takes a secret or password as input; directories forbid collecting credentials.";
5511
7049
  var ALP = "Collects conversation-derived data beyond the task; the directory profile never serves it.";
5512
7050
  var DIRECTORY_TOOLS = {
7051
+ "add-organization-group-member": IN,
7052
+ "attach-cost-center-resource": IN,
5513
7053
  "cancel-job": IN,
5514
7054
  "create-cluster": IN,
7055
+ "create-cost-center": IN,
5515
7056
  "create-delegation": IN,
5516
7057
  "create-endpoint": IN,
5517
7058
  "create-global-volume": IN,
5518
7059
  "create-network-volume": IN,
7060
+ "create-organization-group": IN,
7061
+ "create-organization-invite": IN,
7062
+ "create-organization-role": IN,
5519
7063
  "create-pod": IN,
5520
7064
  "create-template": IN,
5521
7065
  "delete-cluster": IN,
7066
+ "delete-cost-center": IN,
5522
7067
  "delete-endpoint": IN,
5523
7068
  "delete-global-volume": IN,
5524
7069
  "delete-network-volume": IN,
7070
+ "delete-organization-group": IN,
7071
+ "delete-organization-invite": IN,
7072
+ "delete-organization-role": IN,
7073
+ "delete-organization-user": IN,
5525
7074
  "delete-pod": IN,
5526
7075
  "delete-template": IN,
5527
7076
  "deploy-hub-repo": IN,
7077
+ "detach-cost-center-resource": IN,
5528
7078
  "endpoint-health": IN,
5529
7079
  "get-capacity": IN,
5530
7080
  "get-cluster": IN,
7081
+ "get-cost-center": IN,
5531
7082
  "get-cpu-type": IN,
5532
7083
  "get-data-center": IN,
5533
7084
  "get-endpoint": IN,
5534
7085
  "get-endpoint-build": IN,
7086
+ "get-endpoint-cold-start-metrics": IN,
7087
+ "get-endpoint-request-metrics": IN,
7088
+ "get-endpoint-worker-metrics": IN,
5535
7089
  "get-global-volume": IN,
5536
7090
  "get-gpu-type": IN,
5537
7091
  "get-job-status": IN,
5538
7092
  "get-network-volume": IN,
5539
7093
  "get-organization": IN,
7094
+ "get-organization-group": IN,
7095
+ "get-organization-invite": IN,
7096
+ "get-organization-role": IN,
7097
+ "get-organization-role-capabilities": IN,
5540
7098
  "get-organization-user": IN,
5541
7099
  "get-pod": IN,
5542
7100
  "get-ssh-keys": IN,
@@ -5546,6 +7104,8 @@ var DIRECTORY_TOOLS = {
5546
7104
  "list-cluster-billing": IN,
5547
7105
  "list-cluster-pods": IN,
5548
7106
  "list-clusters": IN,
7107
+ "list-cost-center-resources": IN,
7108
+ "list-cost-centers": IN,
5549
7109
  "list-cpu-types": IN,
5550
7110
  "list-data-centers": IN,
5551
7111
  "list-delegations": IN,
@@ -5559,6 +7119,10 @@ var DIRECTORY_TOOLS = {
5559
7119
  "list-hub-repos": IN,
5560
7120
  "list-network-volume-billing": IN,
5561
7121
  "list-network-volumes": IN,
7122
+ "list-organization-group-members": IN,
7123
+ "list-organization-groups": IN,
7124
+ "list-organization-invites": IN,
7125
+ "list-organization-roles": IN,
5562
7126
  "list-organization-users": IN,
5563
7127
  "list-pod-billing": IN,
5564
7128
  "list-pods": IN,
@@ -5568,6 +7132,7 @@ var DIRECTORY_TOOLS = {
5568
7132
  "list-templates": IN,
5569
7133
  "lookup-concept": IN,
5570
7134
  "purge-endpoint-queue": IN,
7135
+ "remove-organization-group-member": IN,
5571
7136
  "retry-job": IN,
5572
7137
  "revoke-delegation": IN,
5573
7138
  "search-concepts": IN,
@@ -5577,9 +7142,13 @@ var DIRECTORY_TOOLS = {
5577
7142
  "stream-pod-logs": IN,
5578
7143
  "stream-worker-logs": IN,
5579
7144
  "update-cluster": IN,
7145
+ "update-cost-center": IN,
5580
7146
  "update-endpoint": IN,
5581
7147
  "update-global-volume": IN,
5582
7148
  "update-network-volume": IN,
7149
+ "update-organization-group": IN,
7150
+ "update-organization-role": IN,
7151
+ "update-organization-user": IN,
5583
7152
  "update-pod": IN,
5584
7153
  "update-ssh-keys": IN,
5585
7154
  "update-template": IN,
@@ -8400,23 +9969,40 @@ function createRunpodClient(options = {}) {
8400
9969
  // src/specgen/clients/sse.ts
8401
9970
  var LOG_STREAM_DEFAULT_WAIT_MS = 5e3;
8402
9971
  var LOG_STREAM_MAX_BYTES = 256 * 1024;
9972
+ var LOG_STREAM_CONNECT_TIMEOUT_MS = 1e4;
9973
+ var LOG_SNAPSHOT_MAX_CHARS = 32e3;
8403
9974
  function createSseReader(options = {}) {
8404
9975
  const apiKey = options.apiKey ?? process.env.RUNPOD_API_KEY;
8405
9976
  const fetchImpl = options.fetchImpl ?? fetch;
9977
+ const connectTimeoutMs = options.connectTimeoutMs ?? LOG_STREAM_CONNECT_TIMEOUT_MS;
8406
9978
  return async (url, opts) => {
8407
9979
  if (!apiKey) throw missingKeyError();
8408
9980
  const chunks = [];
8409
9981
  let bytes = 0;
8410
9982
  let truncated = false;
8411
9983
  let streamEstablished = false;
9984
+ const connect = new AbortController();
9985
+ const connectTimer = setTimeout(
9986
+ () => connect.abort(
9987
+ new DOMException(
9988
+ `Runpod log stream sent no headers within ${connectTimeoutMs}ms`,
9989
+ "TimeoutError"
9990
+ )
9991
+ ),
9992
+ connectTimeoutMs
9993
+ );
8412
9994
  try {
8413
- const response = await createDeadlineFetch(fetchImpl, opts.maxWaitMs)(url, {
9995
+ const response = await createDeadlineFetch(
9996
+ fetchImpl,
9997
+ connectTimeoutMs + opts.maxWaitMs
9998
+ )(url, {
8414
9999
  method: "GET",
8415
10000
  headers: {
8416
10001
  Authorization: `Bearer ${apiKey}`,
8417
10002
  Accept: "text/event-stream"
8418
- }
8419
- });
10003
+ },
10004
+ signal: connect.signal
10005
+ }).finally(() => clearTimeout(connectTimer));
8420
10006
  if (!response.ok) {
8421
10007
  const body = await response.text().catch(() => "");
8422
10008
  throw new HttpError(
@@ -8427,14 +10013,31 @@ function createSseReader(options = {}) {
8427
10013
  }
8428
10014
  streamEstablished = true;
8429
10015
  if (response.body) {
8430
- for await (const chunk of response.body) {
8431
- const boundedChunk = chunk.subarray(0, opts.maxBytes - bytes);
8432
- chunks.push(boundedChunk);
8433
- bytes += boundedChunk.length;
8434
- if (bytes >= opts.maxBytes) {
8435
- truncated = true;
8436
- break;
10016
+ const reader = response.body.getReader();
10017
+ let timer;
10018
+ const windowClosed = new Promise((resolve) => {
10019
+ timer = setTimeout(() => resolve(null), opts.maxWaitMs);
10020
+ });
10021
+ try {
10022
+ for (; ; ) {
10023
+ const read = reader.read();
10024
+ read.catch(() => {
10025
+ });
10026
+ const next = await Promise.race([read, windowClosed]);
10027
+ if (next === null || next.done) break;
10028
+ const boundedChunk = next.value.subarray(0, opts.maxBytes - bytes);
10029
+ chunks.push(boundedChunk);
10030
+ bytes += boundedChunk.length;
10031
+ if (bytes >= opts.maxBytes) {
10032
+ truncated = true;
10033
+ break;
10034
+ }
8437
10035
  }
10036
+ } finally {
10037
+ clearTimeout(timer);
10038
+ void reader.cancel().catch(() => {
10039
+ });
10040
+ reader.releaseLock();
8438
10041
  }
8439
10042
  }
8440
10043
  } catch (err) {
@@ -8460,7 +10063,80 @@ async function collectLogSnapshot(reader, logsUrl, params) {
8460
10063
  for await (const event of iterateLogEvents(new Response(raw).body)) {
8461
10064
  items.push(event.data);
8462
10065
  }
8463
- return { items, count: items.length, truncated };
10066
+ return fitSnapshot(items, truncated);
10067
+ }
10068
+ var serializedLength = (snapshot) => JSON.stringify(snapshot, null, 2).length;
10069
+ var LOG_ENTRY_MAX_CHARS = 16e3;
10070
+ var CLIPPABLE_KEYS = ["line", "raw"];
10071
+ function clipText(text, keep) {
10072
+ const code = text.charCodeAt(keep - 1);
10073
+ if (keep > 0 && code >= 55296 && code <= 56319) keep--;
10074
+ return `${text.slice(0, keep)}\u2026 [clipped ${text.length - keep} chars]`;
10075
+ }
10076
+ function clipEntry(entry) {
10077
+ let clipped = false;
10078
+ const out = { ...entry };
10079
+ for (const key of CLIPPABLE_KEYS) {
10080
+ const text = out[key];
10081
+ if (typeof text === "string" && text.length > LOG_ENTRY_MAX_CHARS) {
10082
+ out[key] = clipText(text, LOG_ENTRY_MAX_CHARS);
10083
+ clipped = true;
10084
+ }
10085
+ }
10086
+ return { entry: out, clipped };
10087
+ }
10088
+ function fitEntry(entry, maxChars) {
10089
+ const size = (e) => serializedLength({ items: [e], count: 1, truncated: false });
10090
+ const out = { ...entry };
10091
+ const keys = Object.keys(out).filter((key) => key !== "ts" && typeof out[key] === "string").sort((a, b) => out[b].length - out[a].length);
10092
+ for (const key of keys) {
10093
+ if (size(out) <= maxChars) break;
10094
+ const text = entry[key];
10095
+ let lo = 0;
10096
+ let hi = Math.min(text.length, LOG_ENTRY_MAX_CHARS);
10097
+ while (lo < hi) {
10098
+ const mid = Math.ceil((lo + hi) / 2);
10099
+ if (size({ ...out, [key]: clipText(text, mid) }) <= maxChars)
10100
+ lo = mid;
10101
+ else hi = mid - 1;
10102
+ }
10103
+ out[key] = clipText(text, lo);
10104
+ }
10105
+ return out;
10106
+ }
10107
+ var entryTs = (entry) => entry?.ts;
10108
+ function fitSnapshot(items, truncated, maxChars = LOG_SNAPSHOT_MAX_CHARS) {
10109
+ let cut = truncated;
10110
+ const entries = items.map((item) => {
10111
+ const { entry, clipped } = clipEntry(item);
10112
+ if (clipped) cut = true;
10113
+ return entry;
10114
+ });
10115
+ const snapshot = (n, isCut) => ({
10116
+ items: entries.slice(0, n),
10117
+ count: n,
10118
+ truncated: isCut
10119
+ });
10120
+ const whole = snapshot(entries.length, cut);
10121
+ if (entries.length === 0 || serializedLength(whole) <= maxChars) return whole;
10122
+ if (serializedLength(snapshot(1, true)) > maxChars) {
10123
+ entries[0] = fitEntry(items[0], maxChars);
10124
+ if (entries.length === 1) return snapshot(1, true);
10125
+ }
10126
+ let lo = 1;
10127
+ let hi = entries.length - 1;
10128
+ while (lo < hi) {
10129
+ const mid = Math.ceil((lo + hi) / 2);
10130
+ if (serializedLength(snapshot(mid, true)) <= maxChars) lo = mid;
10131
+ else hi = mid - 1;
10132
+ }
10133
+ let keep = lo;
10134
+ const omittedTs = entryTs(entries[keep]);
10135
+ if (omittedTs !== void 0) {
10136
+ while (keep > 0 && entryTs(entries[keep - 1]) === omittedTs) keep--;
10137
+ if (keep === 0) keep = lo;
10138
+ }
10139
+ return snapshot(keep, true);
8464
10140
  }
8465
10141
 
8466
10142
  // src/specgen/tools/logs.ts
@@ -8478,13 +10154,13 @@ var logStreamProperties = {
8478
10154
  },
8479
10155
  since: {
8480
10156
  type: "string",
8481
- description: "RFC3339 timestamp to resume from; when set, `tail` is ignored."
10157
+ description: "RFC3339 timestamp to resume after (exclusive). Replays container lines only; system lines stream live from the call. When set, `tail` is ignored."
8482
10158
  },
8483
10159
  maxWaitMs: {
8484
10160
  type: "integer",
8485
10161
  minimum: 500,
8486
10162
  maximum: 3e4,
8487
- description: "How long to read the stream, in ms (default 5000, max 30000)"
10163
+ description: "How long to read the stream once it opens, in ms (default 5000, max 30000)"
8488
10164
  }
8489
10165
  };
8490
10166
  function baseUrl() {
@@ -8510,7 +10186,7 @@ var streamPodLogs = {
8510
10186
  title: "Stream Pod logs",
8511
10187
  holdsResponse: true,
8512
10188
  annotations: readOnly,
8513
- 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.",
10189
+ 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`.",
8514
10190
  inputSchema: {
8515
10191
  type: "object",
8516
10192
  properties: {
@@ -8532,10 +10208,10 @@ var streamPodLogs = {
8532
10208
  var streamWorkerLogs = {
8533
10209
  name: "stream-worker-logs",
8534
10210
  title: "Stream worker logs",
8535
- 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.",
10211
+ 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.",
8536
10212
  holdsResponse: true,
8537
10213
  annotations: readOnly,
8538
- 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.",
10214
+ 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.",
8539
10215
  inputSchema: {
8540
10216
  type: "object",
8541
10217
  properties: {
@@ -8679,6 +10355,24 @@ var listPublicEndpoints = {
8679
10355
  // src/specgen/tools/knowledge.ts
8680
10356
  var import_plugin_knowledge = require("@runpod/plugin-knowledge");
8681
10357
  var knowledge = (0, import_plugin_knowledge.loadKnowledge)();
10358
+ var resolveGuideId = (id) => knowledge.aliases?.[id] ?? id;
10359
+ var hiddenRules = new Set(
10360
+ (process.env.KNOWLEDGE_HIDDEN_RULES ?? "").split(",").map((id) => id.trim()).filter(Boolean)
10361
+ );
10362
+ var concepts = knowledge.concepts.map((concept) => ({
10363
+ ...concept,
10364
+ rules: concept.rules.filter((rule) => !hiddenRules.has(rule.id))
10365
+ }));
10366
+ for (const id of hiddenRules) {
10367
+ if (!knowledge.concepts.some(
10368
+ (concept) => concept.rules.some((rule) => rule.id === id)
10369
+ ))
10370
+ console.warn("knowledge_hidden_rule_unknown", {
10371
+ id,
10372
+ plugin_version: knowledge.version
10373
+ });
10374
+ }
10375
+ var visibleRuleIds = (ids) => ids.filter((id) => !hiddenRules.has(id));
8682
10376
  var GUIDE_KINDS = ["skill", "reference", "golden-path"];
8683
10377
  var STOPWORDS = new Set(
8684
10378
  "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(
@@ -8721,7 +10415,7 @@ var guideSummary = ({
8721
10415
  lanes,
8722
10416
  mcp,
8723
10417
  needs_shell,
8724
- concepts
10418
+ concepts: concepts2
8725
10419
  }) => ({
8726
10420
  id,
8727
10421
  kind,
@@ -8730,7 +10424,7 @@ var guideSummary = ({
8730
10424
  lanes,
8731
10425
  mcp,
8732
10426
  needs_shell,
8733
- concepts
10427
+ concepts: concepts2
8734
10428
  });
8735
10429
  function linkedGuides(conceptId) {
8736
10430
  const byId = new Map(knowledge.guides.map((guide) => [guide.id, guide]));
@@ -8742,19 +10436,20 @@ function linkedGuides(conceptId) {
8742
10436
  seen.add(link.from);
8743
10437
  const guide = byId.get(link.from);
8744
10438
  if (!guide) continue;
10439
+ const rules = link.rules && visibleRuleIds(link.rules);
8745
10440
  if (guide.kind === "golden-path")
8746
10441
  examples.push({ id: guide.id, title: guide.title, mcp: guide.mcp });
8747
10442
  else
8748
10443
  docs.push({
8749
10444
  id: guide.id,
8750
10445
  title: guide.title,
8751
- ...link.rules ? { rules: link.rules } : {}
10446
+ ...rules?.length ? { rules } : {}
8752
10447
  });
8753
10448
  }
8754
10449
  return { examples, docs };
8755
10450
  }
8756
10451
  var conceptIndex = /* @__PURE__ */ new Map();
8757
- for (const concept of knowledge.concepts) {
10452
+ for (const concept of concepts) {
8758
10453
  for (const key of [concept.id, concept.name, ...concept.aliases]) {
8759
10454
  if (!conceptIndex.has(key.toLowerCase()))
8760
10455
  conceptIndex.set(key.toLowerCase(), concept);
@@ -8788,7 +10483,7 @@ function resolveConcept(input) {
8788
10483
  return best && !tied ? { concept: best.concept, matchedAs: best.key } : void 0;
8789
10484
  }
8790
10485
  var ruleIndex = /* @__PURE__ */ new Map();
8791
- for (const concept of knowledge.concepts) {
10486
+ for (const concept of concepts) {
8792
10487
  for (const rule of concept.rules) ruleIndex.set(rule.id, { rule, concept });
8793
10488
  }
8794
10489
  var source = {
@@ -8837,7 +10532,9 @@ var readGuide = {
8837
10532
  title: "Read guide",
8838
10533
  annotations: bundledRead,
8839
10534
  recordedArguments: ["id"],
8840
- recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
10535
+ recordedGuideKind: (args) => knowledge.guides.find(
10536
+ (guide) => guide.id === resolveGuideId(String(args.id).trim())
10537
+ )?.kind,
8841
10538
  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").',
8842
10539
  inputSchema: {
8843
10540
  type: "object",
@@ -8849,7 +10546,9 @@ var readGuide = {
8849
10546
  },
8850
10547
  handler: async (_ctx, args) => {
8851
10548
  const id = String(args.id).trim();
8852
- const guide = knowledge.guides.find((candidate) => candidate.id === id);
10549
+ const guide = knowledge.guides.find(
10550
+ (candidate) => candidate.id === resolveGuideId(id)
10551
+ );
8853
10552
  if (!guide) {
8854
10553
  const close = knowledge.guides.filter(
8855
10554
  (candidate) => candidate.id.includes(id) || id.includes(candidate.id)
@@ -8907,7 +10606,7 @@ var lookupConcept = {
8907
10606
  };
8908
10607
  }
8909
10608
  const { concept, matchedAs } = resolved;
8910
- const pointedAtBy = knowledge.concepts.flatMap((other) => [
10609
+ const pointedAtBy = concepts.flatMap((other) => [
8911
10610
  ...other.is_a === concept.id ? [{ concept: other.id, type: "is_a" }] : [],
8912
10611
  ...other.part_of === concept.id ? [{ concept: other.id, type: "part_of" }] : [],
8913
10612
  ...other.relations.filter((relation) => relation.target === concept.id).map((relation) => ({ concept: other.id, type: relation.type }))
@@ -8921,6 +10620,14 @@ var lookupConcept = {
8921
10620
  ...source,
8922
10621
  ...matchedAs ? { matched_as: matchedAs } : {},
8923
10622
  ...concept,
10623
+ steps: concept.steps.map((step) => ({
10624
+ ...step,
10625
+ rules: visibleRuleIds(step.rules)
10626
+ })),
10627
+ rules: concept.rules.map((rule) => ({
10628
+ ...rule,
10629
+ see: visibleRuleIds(rule.see)
10630
+ })),
8924
10631
  pointed_at_by: pointedAtBy,
8925
10632
  linked_rules: linkedRules,
8926
10633
  ...linkedGuides(concept.id)
@@ -8996,6 +10703,12 @@ var guideResources = knowledge.guides.map((guide) => ({
8996
10703
  mimeType: "text/markdown",
8997
10704
  text: guide.body
8998
10705
  }));
10706
+ var findGuideResource = (resources, uri) => {
10707
+ const id = uri.startsWith(GUIDE_URI_PREFIX) ? resolveGuideId(uri.slice(GUIDE_URI_PREFIX.length)) : void 0;
10708
+ return resources.find(
10709
+ (resource) => resource.uri === uri || resource.uri === GUIDE_URI_PREFIX + id
10710
+ );
10711
+ };
8999
10712
 
9000
10713
  // src/specgen/prose.ts
9001
10714
  var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
@@ -9179,7 +10892,7 @@ function words2(name) {
9179
10892
 
9180
10893
  // src/server.ts
9181
10894
  var SERVER_NAME = "Runpod API Server";
9182
- var SERVER_VERSION = true ? "4.9.1" : "dev";
10895
+ var SERVER_VERSION = true ? "4.10.1" : "dev";
9183
10896
 
9184
10897
  // src/specgen/analytics.ts
9185
10898
  var import_node_crypto = require("crypto");
@@ -9247,6 +10960,7 @@ function captureToolCall(apiKey, event) {
9247
10960
  server_version: event.serverVersion,
9248
10961
  ...event.clientName ? { client_name: event.clientName } : {},
9249
10962
  ...event.guideKind ? { guide_kind: event.guideKind } : {},
10963
+ ...event.errorClass ? { error_class: event.errorClass } : {},
9250
10964
  ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
9251
10965
  ...event.profile ? { profile: event.profile } : {},
9252
10966
  ...args
@@ -9780,7 +11494,7 @@ These tools manage infrastructure only. They do not do SSH sessions, file transf
9780
11494
 
9781
11495
  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.
9782
11496
 
9783
- 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.
11497
+ 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.
9784
11498
 
9785
11499
  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.
9786
11500
 
@@ -9841,9 +11555,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9841
11555
  resourceTemplates: []
9842
11556
  }));
9843
11557
  server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
9844
- const guide = servedGuides.find(
9845
- (candidate) => candidate.uri === request.params.uri
9846
- );
11558
+ const guide = findGuideResource(servedGuides, request.params.uri);
9847
11559
  if (opts.analytics) {
9848
11560
  captureResourceCall(ctx.apiKey, {
9849
11561
  method: "resources/read",
@@ -9880,6 +11592,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9880
11592
  ok: false,
9881
11593
  status: 429,
9882
11594
  durationMs: 0,
11595
+ errorClass: "rate_limited",
9883
11596
  ...verdict.reason ? { blockedBy: verdict.reason } : {},
9884
11597
  profile,
9885
11598
  ...opts.analytics
@@ -9905,6 +11618,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9905
11618
  const generated = servedGeneratedTools.find(
9906
11619
  (tool) => tool.name === request.params.name
9907
11620
  );
11621
+ const curatedArgs = curated ? withoutNullOptionals(curated.inputSchema, args) : args;
9908
11622
  const omitted = directory ? directoryOmittedInputs(request.params.name).filter((key) => key in args) : [];
9909
11623
  const rejected = omitted.length ? {
9910
11624
  ok: false,
@@ -9914,10 +11628,10 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9914
11628
  }
9915
11629
  } : curated && !curated.lenientArguments ? validateArguments(
9916
11630
  curated.inputSchema,
9917
- args
11631
+ curatedArgs
9918
11632
  ) : null;
9919
11633
  const result = rejected ?? await runTool(
9920
- () => curated ? curated.handler(ctx, args) : dispatchTool(generated, request.params.name, args)
11634
+ () => curated ? curated.handler(ctx, curatedArgs) : dispatchTool(generated, request.params.name, args)
9921
11635
  );
9922
11636
  logToolCall({
9923
11637
  tool: request.params.name,
@@ -9932,6 +11646,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9932
11646
  ok: result.ok,
9933
11647
  status: result.status,
9934
11648
  durationMs: Date.now() - startedAt,
11649
+ ...result.ok ? {} : { errorClass: errorClass(result) },
9935
11650
  // The directory profile records which tool ran, never what was asked.
9936
11651
  ...directory ? {} : {
9937
11652
  args: recordedArgumentValues(curated?.recordedArguments, args),
@@ -9971,24 +11686,50 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9971
11686
  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.";
9972
11687
  var DETAIL_HINTS = [
9973
11688
  [
9974
- /no longer any instances available|not enough free GPUs/i,
9975
- "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."
11689
+ /no longer any instances available|no instances currently available|not enough free GPUs/i,
11690
+ "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.",
11691
+ "capacity"
9976
11692
  ],
9977
11693
  [
9978
11694
  /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
9979
- "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."
11695
+ "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.",
11696
+ "balance"
9980
11697
  ],
9981
11698
  [
9982
11699
  /mutually exclusive|cannot be combined with/i,
9983
- "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
11700
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else.",
11701
+ "conflicting_args"
11702
+ ],
11703
+ // A 400 with a bare "Unauthorized" detail from a write the same key can
11704
+ // otherwise make is upstream's answer for an unfunded account.
11705
+ [
11706
+ /^\s*unauthorized\s*$/i,
11707
+ "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.",
11708
+ "balance"
9984
11709
  ]
9985
11710
  ];
11711
+ function errorClass(result) {
11712
+ const { status } = result;
11713
+ const payload = asRecord(result.payload) ?? { error: result.payload };
11714
+ const detail = detailText(payload);
11715
+ if (status === 400 && detail) {
11716
+ for (const [pattern, , cls] of DETAIL_HINTS)
11717
+ if (pattern.test(detail)) return cls;
11718
+ }
11719
+ if (status === 400 || status === 422) return "validation";
11720
+ if (status === 401) return "auth";
11721
+ if (status === 402) return "balance";
11722
+ if (status === 403) return "permission";
11723
+ if (status === 404) return "not_found";
11724
+ if (status === 429) return "rate_limited";
11725
+ if (status === 504) return "timeout";
11726
+ if (status >= 500) return "server";
11727
+ return "other";
11728
+ }
9986
11729
  function errorHint(status, detail) {
9987
11730
  if (status === 400 && detail) {
9988
11731
  for (const [pattern, hint] of DETAIL_HINTS)
9989
11732
  if (pattern.test(detail)) return hint;
9990
- if (/^unauthorized$/i.test(detail.trim()))
9991
- 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.";
9992
11733
  }
9993
11734
  if (status === 400 || status === 422) return SHAPE_HINT;
9994
11735
  if (status === 401) return "The Runpod API key is missing or invalid.";