@rymi/mcp 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +9 -4
  2. package/dist/index.js +320 -39
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -65,7 +65,7 @@ Run the server on your own machine — it speaks **stdio** by default and talks
65
65
 
66
66
  > **Options:** `RYMI_MCP_READONLY=1` hides every mutating tool (including `create_call`, `batch_call`, `publish_agent`). `RYMI_WORKSPACE` makes the local server act in that workspace (sent as the `Rymi-Workspace` header). On the hosted endpoint, send the `Rymi-Workspace` header on the HTTP request instead. Every tool also takes an optional `workspace` argument, so one connection can act in any workspace it can reach, such as an agency's client workspaces.
67
67
  >
68
- > This package speaks **stdio** only. `--transport http` was removed in 2.0.0 — use the hosted endpoint above, which the local server could never match on auth (it has no OAuth, and no per-key tool gating).
68
+ > This package speaks **stdio** only. `--transport http` was removed in 2.0.0. Use the hosted endpoint above. The local server has no OAuth. It does filter tools by the key's scopes.
69
69
 
70
70
  ## 🧰 Tools
71
71
 
@@ -136,8 +136,12 @@ Run the server on your own machine — it speaks **stdio** by default and talks
136
136
  <td><code>estimate_call_cost</code> · <code>set_auto_recharge</code> · <code>set_spend_alerts</code></td>
137
137
  </tr>
138
138
  <tr>
139
+ <td><b>Accounts</b></td>
140
+ <td><code>list_accounts</code> · <code>list_account_members</code> · <code>add_account_member</code> · <code>remove_account_member</code></td>
141
+ </tr>
142
+ <tr>
139
143
  <td><b>Workspaces</b></td>
140
- <td><code>list_workspaces</code> · <code>create_workspace</code> · <code>update_workspace</code> · <code>get_workspace_usage</code> · <code>list_workspace_members</code> · <code>add_workspace_member</code> · <code>remove_workspace_member</code></td>
144
+ <td><code>list_workspaces</code> · <code>create_workspace</code> · <code>update_workspace</code> · <code>delete_workspace</code> · <code>get_workspace_usage</code> · <code>list_workspace_members</code> · <code>add_workspace_member</code> · <code>remove_workspace_member</code></td>
141
145
  </tr>
142
146
  <tr>
143
147
  <td><b>Compliance settings</b></td>
@@ -151,8 +155,9 @@ Run the server on your own machine — it speaks **stdio** by default and talks
151
155
 
152
156
  What restrains them depends on which server you're talking to.
153
157
 
154
- - **Hosted** `mcp.rymi.live` — tools are filtered by your **tenant role**, on both OAuth and API keys. An `owner`/`admin` sees everything; a `member` gets agent-scoped tools only, with account-scoped writes (billing, keys, numbers, DNC) never registered. `RYMI_MCP_READONLY` does not apply there.
155
- - **Local** `@rymi/mcp` — no role filtering. Full write surface unless you start it with `RYMI_MCP_READONLY=1`, which hides every mutating tool at once. That flag is set by whoever *starts* the server, so it guards a local run you configure, not a key you hand out.
158
+ - **Hosted** `mcp.rymi.live` over OAuth: tools are filtered by role. Account Owner is `owner`. Account Admin and workspace Admin are `admin`. Anyone else is `member`. An `owner`/`admin` sees every tool. A `member` gets agent-scoped tools only, with account-scoped writes (billing, keys, numbers, DNC) left out. `RYMI_MCP_READONLY` does not apply there.
159
+ - **A secret key**, hosted or local: the server calls `GET /v1/keys/self` once and lists tools whose scope the key holds. A legacy key lists every tool. A billing write from that list still returns `scope_missing`.
160
+ - **Local** `@rymi/mcp`: no OAuth role filter. `RYMI_MCP_READONLY=1` hides every mutating tool on top of the key's scopes. That flag is set by whoever starts the server.
156
161
 
157
162
  Either way, role filtering bounds the **tool surface**, not the credential: a key or token stays valid against the REST API directly. Treat MCP access as equivalent to handing over your own account access.
158
163
 
package/dist/index.js CHANGED
@@ -371,15 +371,23 @@ var require_dist = __commonJS({
371
371
  var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
372
372
  var index_exports = {};
373
373
  __export2(index_exports, {
374
+ BILLING_WRITE: () => BILLING_WRITE,
374
375
  defineTool: () => defineTool,
376
+ keyAllowsTool: () => keyAllowsTool2,
375
377
  opsToolCatalog: () => opsToolCatalog2,
376
378
  resolveToolDisposition: () => resolveToolDisposition,
377
379
  withWorkspaceParam: () => withWorkspaceParam2
378
380
  });
379
381
  module2.exports = __toCommonJS2(index_exports);
382
+ var BILLING_WRITE = "billing:write";
380
383
  function defineTool(def) {
381
384
  return def;
382
385
  }
386
+ function keyAllowsTool2(tool, key) {
387
+ if (tool.keyScopes.includes(BILLING_WRITE)) return false;
388
+ if (key.legacy === true) return true;
389
+ return tool.keyScopes.every((scope) => key.scopes.includes(scope));
390
+ }
383
391
  var PRIVILEGED = /* @__PURE__ */ new Set(["owner", "admin"]);
384
392
  function resolveToolDisposition(p) {
385
393
  if (p.risk === "read") return "execute";
@@ -459,6 +467,8 @@ var require_dist = __commonJS({
459
467
  description: "List all your Rymi AI voice agents.",
460
468
  scope: "agent",
461
469
  risk: "read",
470
+ routes: ["GET /v1/agents"],
471
+ keyScopes: ["agents:read"],
462
472
  input: {
463
473
  limit: import_zod.z.number().int().min(1).max(500).optional().describe("Max agents to return (default 50)"),
464
474
  offset: import_zod.z.number().int().min(0).optional().describe("Pagination offset")
@@ -470,6 +480,8 @@ var require_dist = __commonJS({
470
480
  description: "Retrieve a single Rymi voice agent by ID.",
471
481
  scope: "agent",
472
482
  risk: "read",
483
+ routes: ["GET /v1/agents/:id"],
484
+ keyScopes: ["agents:read"],
473
485
  input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID") },
474
486
  run: (client, { agent_id }) => client.agents.retrieve(agent_id)
475
487
  }),
@@ -478,6 +490,8 @@ var require_dist = __commonJS({
478
490
  description: "Fetch all available LLM models and voices you can use when creating or updating an agent. Always call this before create_agent to pick valid values.",
479
491
  scope: "agent",
480
492
  risk: "read",
493
+ routes: ["GET /v1/agents/llm-options"],
494
+ keyScopes: ["agents:read"],
481
495
  input: {},
482
496
  run: (client) => client.agents.llmOptions()
483
497
  }),
@@ -486,6 +500,8 @@ var require_dist = __commonJS({
486
500
  description: "List available agent/TTS voices, optionally filtered by provider or model. Much smaller payload than list_llm_options \u2014 use this to pick a `voice` value for create_agent/update_agent.",
487
501
  scope: "agent",
488
502
  risk: "read",
503
+ routes: ["GET /v1/agents/llm-options"],
504
+ keyScopes: ["agents:read"],
489
505
  input: {
490
506
  provider: import_zod.z.string().optional().describe('Filter to one voice provider, e.g. "elevenlabs", "sarvam", "gemini", "cartesia", "deepgram".'),
491
507
  model_id: import_zod.z.string().optional().describe("Filter to voices compatible with a given model id (matched against the voice's supported_model_ids).")
@@ -518,6 +534,8 @@ var require_dist = __commonJS({
518
534
  description: "Preview the resolved per-language model stack (STT/LLM/TTS), blockers, warnings, and model diffs for a set of supported languages \u2014 without saving. Use before create/update to confirm a multi-language setup is valid.",
519
535
  scope: "agent",
520
536
  risk: "read",
537
+ routes: ["POST /v1/agents/stack-preview"],
538
+ keyScopes: ["agents:write"],
521
539
  input: {
522
540
  supported_languages: import_zod.z.array(import_zod.z.string()).min(1).describe('BCP-47 languages to resolve a stack for, e.g. ["hi-IN","en-US"].'),
523
541
  language: import_zod.z.string().optional().describe("Primary BCP-47 language (defaults to the first supported language)."),
@@ -530,6 +548,8 @@ var require_dist = __commonJS({
530
548
  description: "List calls made with a specific agent.",
531
549
  scope: "agent",
532
550
  risk: "read",
551
+ routes: ["GET /v1/agents/:id/calls"],
552
+ keyScopes: ["agents:read"],
533
553
  input: {
534
554
  agent_id: import_zod.z.string().uuid(),
535
555
  limit: import_zod.z.number().int().min(1).max(200).optional(),
@@ -543,6 +563,8 @@ var require_dist = __commonJS({
543
563
  description: "Check whether an agent is ready to go live. Returns a validation report without persisting any changes.",
544
564
  scope: "agent",
545
565
  risk: "read",
566
+ routes: ["POST /v1/agents/validate-publish"],
567
+ keyScopes: ["agents:write"],
546
568
  input: {
547
569
  agent_id: import_zod.z.string().uuid().optional().describe("Merge validation with a persisted agent"),
548
570
  name: import_zod.z.string().optional(),
@@ -557,6 +579,8 @@ var require_dist = __commonJS({
557
579
  description: "Validate and resolve a flat key/value change-set against the AgentConfig field registry. Does NOT persist \u2014 follow up with update_agent.",
558
580
  scope: "agent",
559
581
  risk: "read",
582
+ routes: ["GET /v1/agents/:id", "POST /v1/agents/apply-changes"],
583
+ keyScopes: ["agents:read", "agents:write"],
560
584
  input: {
561
585
  agent_id: import_zod.z.string().uuid().describe("Agent to load currentConfig from (recommended)"),
562
586
  changes: import_zod.z.array(import_zod.z.object({ key: import_zod.z.string(), value: import_zod.z.any() })).describe("Array of {key, value} field changes"),
@@ -579,6 +603,8 @@ var require_dist = __commonJS({
579
603
  description: "Use AI + Google Search grounding to auto-generate a company description from a website URL, suitable for an agent's persona.",
580
604
  scope: "agent",
581
605
  risk: "read",
606
+ routes: ["POST /v1/agents/enrich-company"],
607
+ keyScopes: ["agents:write"],
582
608
  input: {
583
609
  company_name: import_zod.z.string().min(1),
584
610
  website_url: import_zod.z.string().url()
@@ -590,6 +616,8 @@ var require_dist = __commonJS({
590
616
  description: "Use AI to generate an agent configuration draft from a plain-text description.",
591
617
  scope: "agent",
592
618
  risk: "read",
619
+ routes: ["POST /v1/agents/generate"],
620
+ keyScopes: ["agents:write"],
593
621
  input: {
594
622
  prompt: import_zod.z.string().min(1).describe("Description of the agent you want to create"),
595
623
  mode: import_zod.z.enum(["create", "edit"]).optional().default("create"),
@@ -603,6 +631,8 @@ var require_dist = __commonJS({
603
631
  description: 'Create a new Rymi AI voice agent. Call list_llm_options first to discover valid voice and model values. For a multi-language agent, set supported_languages (e.g. ["hi-IN","en-US"]).',
604
632
  scope: "agent",
605
633
  risk: "write",
634
+ routes: ["POST /v1/agents"],
635
+ keyScopes: ["agents:write"],
606
636
  input: {
607
637
  name: import_zod.z.string().min(1).describe("Studio label: the name shown in the sidebar/agent list only. The name the agent speaks is persona.name."),
608
638
  ...agentConfigFields
@@ -614,6 +644,8 @@ var require_dist = __commonJS({
614
644
  description: "Update an existing Rymi voice agent's configuration. Only the fields you pass are changed; nullable fields accept null to clear them.",
615
645
  scope: "agent",
616
646
  risk: "write",
647
+ routes: ["GET /v1/agents", "PUT /v1/agents/:id"],
648
+ keyScopes: ["agents:read", "agents:write"],
617
649
  input: {
618
650
  agent_id: import_zod.z.string().uuid().describe("The agent UUID to update"),
619
651
  name: import_zod.z.string().optional().describe("Studio label (sidebar/agent list only). To change the spoken name, set persona.name."),
@@ -627,6 +659,8 @@ var require_dist = __commonJS({
627
659
  description: "Permanently delete a Rymi voice agent.",
628
660
  scope: "agent",
629
661
  risk: "sensitive",
662
+ routes: ["GET /v1/agents", "DELETE /v1/agents/:id"],
663
+ keyScopes: ["agents:read", "agents:write"],
630
664
  input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID to delete") },
631
665
  run: async (client, { agent_id }) => client.agents.delete(await resolveAgentId(client, agent_id))
632
666
  }),
@@ -635,6 +669,8 @@ var require_dist = __commonJS({
635
669
  description: 'Duplicate an existing agent. The copy gets " (Copy)" appended to its name.',
636
670
  scope: "agent",
637
671
  risk: "write",
672
+ routes: ["GET /v1/agents", "POST /v1/agents/:id/clone"],
673
+ keyScopes: ["agents:read", "agents:write"],
638
674
  input: { agent_id: import_zod.z.string().uuid().describe("The agent UUID to clone") },
639
675
  run: async (client, { agent_id }) => client.agents.clone(await resolveAgentId(client, agent_id))
640
676
  })
@@ -646,6 +682,8 @@ var require_dist = __commonJS({
646
682
  description: "List previous and active calls across your account.",
647
683
  scope: "agent",
648
684
  risk: "read",
685
+ routes: ["GET /v1/calls"],
686
+ keyScopes: ["calls:read"],
649
687
  input: {
650
688
  limit: import_zod2.z.number().int().min(1).max(200).optional().describe("Max calls to return"),
651
689
  offset: import_zod2.z.number().int().min(0).optional().describe("Pagination offset"),
@@ -659,6 +697,8 @@ var require_dist = __commonJS({
659
697
  description: "List calls currently in progress.",
660
698
  scope: "agent",
661
699
  risk: "read",
700
+ routes: ["GET /v1/calls/active"],
701
+ keyScopes: ["calls:read"],
662
702
  input: {
663
703
  limit: import_zod2.z.number().int().min(1).max(200).optional(),
664
704
  offset: import_zod2.z.number().int().min(0).optional()
@@ -670,6 +710,8 @@ var require_dist = __commonJS({
670
710
  description: "Retrieve details, participants, status, duration, and cost for a single call.",
671
711
  scope: "agent",
672
712
  risk: "read",
713
+ routes: ["GET /v1/calls/:id"],
714
+ keyScopes: ["calls:read"],
673
715
  input: { call_id: import_zod2.z.string().describe("The call ID") },
674
716
  run: (client, { call_id }) => client.calls.retrieve(call_id)
675
717
  }),
@@ -678,6 +720,8 @@ var require_dist = __commonJS({
678
720
  description: "Retrieve the post-call summary for a call.",
679
721
  scope: "agent",
680
722
  risk: "read",
723
+ routes: ["GET /v1/calls/:id/summary"],
724
+ keyScopes: ["calls:read"],
681
725
  input: { call_id: import_zod2.z.string().describe("The call ID") },
682
726
  run: (client, { call_id }) => client.calls.summary(call_id)
683
727
  }),
@@ -686,6 +730,8 @@ var require_dist = __commonJS({
686
730
  description: "Retrieve the full transcript for a call. May contain personal data \u2014 handle accordingly.",
687
731
  scope: "agent",
688
732
  risk: "read",
733
+ routes: ["GET /v1/calls/:id/transcript"],
734
+ keyScopes: ["calls:read"],
689
735
  input: { call_id: import_zod2.z.string().describe("The call ID") },
690
736
  run: (client, { call_id }) => client.calls.transcript(call_id)
691
737
  }),
@@ -694,6 +740,8 @@ var require_dist = __commonJS({
694
740
  description: "Retrieve recording metadata (e.g. playback URL) for a call, when recording is enabled.",
695
741
  scope: "agent",
696
742
  risk: "read",
743
+ routes: ["GET /v1/calls/:id/recording"],
744
+ keyScopes: ["calls:read"],
697
745
  input: { call_id: import_zod2.z.string().describe("The call ID") },
698
746
  run: (client, { call_id }) => client.calls.recording(call_id)
699
747
  }),
@@ -702,6 +750,8 @@ var require_dist = __commonJS({
702
750
  description: "Retrieve current outbound call queue statistics.",
703
751
  scope: "agent",
704
752
  risk: "read",
753
+ routes: ["GET /v1/calls/queue/stats"],
754
+ keyScopes: ["calls:read"],
705
755
  input: {},
706
756
  run: (client) => client.calls.queueStats()
707
757
  }),
@@ -710,6 +760,8 @@ var require_dist = __commonJS({
710
760
  description: "Re-run post-call intelligence (summary, extraction, evaluation) for a call.",
711
761
  scope: "agent",
712
762
  risk: "write",
763
+ routes: ["POST /v1/calls/:id/reprocess"],
764
+ keyScopes: ["calls:write"],
713
765
  input: { call_id: import_zod2.z.string().describe("The call ID to reprocess") },
714
766
  run: (client, { call_id }) => client.calls.reprocess(call_id)
715
767
  }),
@@ -718,6 +770,8 @@ var require_dist = __commonJS({
718
770
  description: "WARNING: places a real, billable PSTN call. `from_number` is the caller ID shown to the recipient and must be a phone number registered to your account \u2014 omit it to use the agent's attached number. `identity` is the destination in strict E.164 (e.g. +15555550123).",
719
771
  scope: "agent",
720
772
  risk: "sensitive",
773
+ routes: ["POST /v1/calls"],
774
+ keyScopes: ["calls:write"],
721
775
  input: {
722
776
  agent_id: import_zod2.z.string().uuid().describe("The agent that will handle the call"),
723
777
  participants: import_zod2.z.array(import_zod2.z.object({
@@ -737,6 +791,8 @@ var require_dist = __commonJS({
737
791
  description: "Queue up to 500 outbound PSTN recipients in one request. WARNING: places real phone calls and incurs per-minute charges. `from_number` is the caller ID shown to the recipient and must be a phone number registered to your account.",
738
792
  scope: "agent",
739
793
  risk: "sensitive",
794
+ routes: ["POST /v1/calls/batch"],
795
+ keyScopes: ["calls:write"],
740
796
  input: {
741
797
  agent_id: import_zod2.z.string().uuid().describe("The agent that will handle the calls"),
742
798
  to: import_zod2.z.array(import_zod2.z.string()).optional().describe("Simple list of destination phone numbers"),
@@ -758,6 +814,8 @@ var require_dist = __commonJS({
758
814
  description: "WARNING: immediately terminates an in-progress call. The call transitions to completed and post-call processing runs.",
759
815
  scope: "agent",
760
816
  risk: "sensitive",
817
+ routes: ["POST /v1/calls/:id/end"],
818
+ keyScopes: ["calls:write"],
761
819
  input: { call_id: import_zod2.z.string().describe("The call ID to end") },
762
820
  run: (client, { call_id }) => client.calls.end(call_id)
763
821
  }),
@@ -766,6 +824,8 @@ var require_dist = __commonJS({
766
824
  description: "WARNING: dials/adds participants to a live call (warm transfer / conference). PSTN additions are billable.",
767
825
  scope: "agent",
768
826
  risk: "sensitive",
827
+ routes: ["POST /v1/calls/:id/participants"],
828
+ keyScopes: ["calls:write"],
769
829
  input: {
770
830
  call_id: import_zod2.z.string().describe("The in-progress call ID"),
771
831
  participants: import_zod2.z.array(import_zod2.z.object({
@@ -785,6 +845,8 @@ var require_dist = __commonJS({
785
845
  description: "List all phone numbers on your Rymi account and which agent each is attached to.",
786
846
  scope: "account",
787
847
  risk: "read",
848
+ routes: ["GET /v1/numbers"],
849
+ keyScopes: ["numbers:read"],
788
850
  input: {
789
851
  limit: import_zod3.z.number().int().min(1).max(500).optional(),
790
852
  offset: import_zod3.z.number().int().min(0).optional()
@@ -796,6 +858,8 @@ var require_dist = __commonJS({
796
858
  description: "Register a phone number on your account, optionally attaching it to an agent.",
797
859
  scope: "account",
798
860
  risk: "write",
861
+ routes: ["POST /v1/numbers"],
862
+ keyScopes: ["numbers:write"],
799
863
  input: {
800
864
  number: import_zod3.z.string().describe("Phone number in E.164 format (e.g. +14155550123)"),
801
865
  agent_id: import_zod3.z.string().uuid().optional().describe("Agent to attach the number to on registration")
@@ -807,6 +871,8 @@ var require_dist = __commonJS({
807
871
  description: "Attach an existing number to an agent so inbound calls route to it.",
808
872
  scope: "account",
809
873
  risk: "write",
874
+ routes: ["POST /v1/numbers/:number/attach"],
875
+ keyScopes: ["numbers:write"],
810
876
  input: {
811
877
  number: import_zod3.z.string().describe("Phone number in E.164 format"),
812
878
  agent_id: import_zod3.z.string().uuid().describe("Agent to route this number to")
@@ -818,6 +884,8 @@ var require_dist = __commonJS({
818
884
  description: "Remove a phone number from your account. The number stops routing to any agent.",
819
885
  scope: "account",
820
886
  risk: "sensitive",
887
+ routes: ["DELETE /v1/numbers/:number"],
888
+ keyScopes: ["numbers:write"],
821
889
  input: { number: import_zod3.z.string().describe("Phone number in E.164 format") },
822
890
  run: (client, { number }) => client.numbers.remove(number)
823
891
  })
@@ -828,6 +896,8 @@ var require_dist = __commonJS({
828
896
  description: "Report whether a telephony carrier is connected, and which provider/account.",
829
897
  scope: "account",
830
898
  risk: "read",
899
+ routes: ["GET /v1/telephony/status"],
900
+ keyScopes: ["numbers:read"],
831
901
  input: {},
832
902
  run: (client) => client.telephony.status()
833
903
  }),
@@ -836,6 +906,8 @@ var require_dist = __commonJS({
836
906
  description: "List numbers available on the connected telephony carrier account.",
837
907
  scope: "account",
838
908
  risk: "read",
909
+ routes: ["GET /v1/telephony/numbers"],
910
+ keyScopes: ["numbers:read"],
839
911
  input: {},
840
912
  run: (client) => client.telephony.numbers()
841
913
  })
@@ -846,6 +918,8 @@ var require_dist = __commonJS({
846
918
  description: "List publishable (browser-safe) keys and which agent/channels each is scoped to. Returns key prefixes only, never full secrets.",
847
919
  scope: "account",
848
920
  risk: "read",
921
+ routes: ["GET /v1/keys/publishable"],
922
+ keyScopes: ["keys:read"],
849
923
  input: {},
850
924
  run: (client) => client.keys.listPublishable()
851
925
  })
@@ -857,6 +931,8 @@ var require_dist = __commonJS({
857
931
  description: "List the knowledge sources (RAG context) attached to an agent.",
858
932
  scope: "agent",
859
933
  risk: "read",
934
+ routes: ["GET /v1/agents/:id/knowledge-sources"],
935
+ keyScopes: ["knowledge:read"],
860
936
  input: { agent_id: import_zod4.z.string().uuid().describe("The agent UUID") },
861
937
  run: (client, { agent_id }) => client.agents.listKnowledgeSources(agent_id)
862
938
  }),
@@ -865,6 +941,8 @@ var require_dist = __commonJS({
865
941
  description: "List recorded configuration changes for an agent (for auditing or before an undo).",
866
942
  scope: "agent",
867
943
  risk: "read",
944
+ routes: ["GET /v1/agents/:id/changes"],
945
+ keyScopes: ["agents:read"],
868
946
  input: {
869
947
  agent_id: import_zod4.z.string().uuid().describe("The agent UUID"),
870
948
  since: import_zod4.z.string().optional().describe("ISO timestamp \u2014 only return changes after this time")
@@ -876,6 +954,8 @@ var require_dist = __commonJS({
876
954
  description: "Add a knowledge source to an agent from raw text or a URL (the URL is fetched and ingested).",
877
955
  scope: "agent",
878
956
  risk: "write",
957
+ routes: ["POST /v1/agents/:id/knowledge-sources"],
958
+ keyScopes: ["knowledge:write"],
879
959
  input: {
880
960
  agent_id: import_zod4.z.string().uuid().describe("The agent UUID"),
881
961
  kind: import_zod4.z.enum(["text", "url"]).describe("Source type"),
@@ -893,6 +973,8 @@ var require_dist = __commonJS({
893
973
  description: "Delete a knowledge source from an agent.",
894
974
  scope: "agent",
895
975
  risk: "sensitive",
976
+ routes: ["DELETE /v1/agents/:id/knowledge-sources/:sourceId"],
977
+ keyScopes: ["knowledge:write"],
896
978
  input: {
897
979
  agent_id: import_zod4.z.string().uuid().describe("The agent UUID"),
898
980
  source_id: import_zod4.z.string().describe("The knowledge source ID to delete")
@@ -904,6 +986,8 @@ var require_dist = __commonJS({
904
986
  description: "Undo a single recorded configuration change, reverting that field to its previous value.",
905
987
  scope: "agent",
906
988
  risk: "write",
989
+ routes: ["POST /v1/agents/:id/changes/:changeId/undo"],
990
+ keyScopes: ["agents:write"],
907
991
  input: {
908
992
  agent_id: import_zod4.z.string().uuid().describe("The agent UUID"),
909
993
  change_id: import_zod4.z.string().describe("The change_id to undo (from list_agent_changes)")
@@ -918,6 +1002,8 @@ var require_dist = __commonJS({
918
1002
  description: "Get this account's usage summary: remaining voice-runtime MINUTES, Studio AI unit usage, and post-call intelligence usage. Voice balance is reported in minutes, not dollars.",
919
1003
  scope: "account",
920
1004
  risk: "read",
1005
+ routes: ["GET /v1/billing/usage-summary"],
1006
+ keyScopes: ["billing:read"],
921
1007
  input: {},
922
1008
  run: (client) => client.billing.usageSummary()
923
1009
  }),
@@ -926,6 +1012,8 @@ var require_dist = __commonJS({
926
1012
  description: "List published agent templates. Use a template's `defaults` as the starting config for create_agent.",
927
1013
  scope: "agent",
928
1014
  risk: "read",
1015
+ routes: ["GET /v1/agent-templates"],
1016
+ keyScopes: ["agents:read"],
929
1017
  input: {},
930
1018
  run: (client) => client.templates.list()
931
1019
  }),
@@ -934,6 +1022,8 @@ var require_dist = __commonJS({
934
1022
  description: "List previous evaluation runs for an agent.",
935
1023
  scope: "agent",
936
1024
  risk: "read",
1025
+ routes: ["GET /v1/agents/:id/evals/runs"],
1026
+ keyScopes: ["agents:read"],
937
1027
  input: { agent_id: import_zod5.z.string().uuid().describe("The agent UUID") },
938
1028
  run: (client, { agent_id }) => client.agents.listEvalRuns(agent_id)
939
1029
  }),
@@ -942,6 +1032,8 @@ var require_dist = __commonJS({
942
1032
  description: "Retrieve a single evaluation run, including per-scenario scores.",
943
1033
  scope: "agent",
944
1034
  risk: "read",
1035
+ routes: ["GET /v1/agents/:id/evals/runs/:runId"],
1036
+ keyScopes: ["agents:read"],
945
1037
  input: {
946
1038
  agent_id: import_zod5.z.string().uuid().describe("The agent UUID"),
947
1039
  run_id: import_zod5.z.string().describe("The evaluation run ID")
@@ -953,6 +1045,8 @@ var require_dist = __commonJS({
953
1045
  description: 'Run the evaluation suite for an agent. mode="synthetic" (default) uses the offline scorer; mode="live" runs the model-driven runner (consumes Studio AI units). Set judge=true to supplement the synthetic heuristics with an opt-in LLM judge (requires a Gemini key; consumes Studio AI units).',
954
1046
  scope: "agent",
955
1047
  risk: "write",
1048
+ routes: ["POST /v1/agents/:id/evals/run"],
1049
+ keyScopes: ["agents:write"],
956
1050
  input: {
957
1051
  agent_id: import_zod5.z.string().uuid().describe("The agent UUID to evaluate"),
958
1052
  mode: import_zod5.z.enum(["synthetic", "live"]).optional().describe("Evaluation mode (default synthetic)"),
@@ -970,6 +1064,8 @@ var require_dist = __commonJS({
970
1064
  description: "Run the eval SUITE across many agents at once (agents \xD7 seeded scenarios) with bounded concurrency. One eval run is persisted per agent (visible in the per-agent eval UI); the aggregate report is returned. Set judge=true to add the LLM judge (requires a Gemini key; consumes Studio AI units).",
971
1065
  scope: "agent",
972
1066
  risk: "write",
1067
+ routes: ["POST /v1/agents/evals/suite"],
1068
+ keyScopes: ["agents:write"],
973
1069
  input: {
974
1070
  agent_ids: import_zod5.z.array(import_zod5.z.string().uuid()).min(1).describe("Agent UUIDs to evaluate"),
975
1071
  scenario_ids: import_zod5.z.array(import_zod5.z.string()).optional().describe("Optional subset of seeded scenario ids; omit to run all"),
@@ -991,6 +1087,8 @@ var require_dist = __commonJS({
991
1087
  description: "Publish a Rymi voice agent to make its current saved configuration live. IMPORTANT: this immediately makes the agent callable by end users. Returns published:false with a blockers list when the config has unresolved issues.",
992
1088
  scope: "agent",
993
1089
  risk: "sensitive",
1090
+ routes: ["GET /v1/agents", "POST /v1/agents/:id/publish"],
1091
+ keyScopes: ["agents:read", "agents:write"],
994
1092
  input: {
995
1093
  agent_id: import_zod6.z.string().uuid().describe("The agent UUID to publish")
996
1094
  },
@@ -1008,6 +1106,8 @@ var require_dist = __commonJS({
1008
1106
  description: "List all numbers on your Do-Not-Call registry.",
1009
1107
  scope: "account",
1010
1108
  risk: "read",
1109
+ routes: ["GET /v1/dnc"],
1110
+ keyScopes: ["contacts:read"],
1011
1111
  input: {
1012
1112
  limit: import_zod7.z.number().int().min(1).max(500).optional(),
1013
1113
  offset: import_zod7.z.number().int().min(0).optional()
@@ -1019,6 +1119,8 @@ var require_dist = __commonJS({
1019
1119
  description: "Check whether phone numbers are on the Do-Not-Call registry. Read-only \u2014 adds nothing.",
1020
1120
  scope: "account",
1021
1121
  risk: "read",
1122
+ routes: ["POST /v1/dnc/check"],
1123
+ keyScopes: ["contacts:write"],
1022
1124
  input: { phone_numbers: import_zod7.z.array(import_zod7.z.string()).min(1).max(500).describe("E.164 numbers to check") },
1023
1125
  run: (client, { phone_numbers }) => client.dnc.check({ phone_numbers })
1024
1126
  }),
@@ -1027,6 +1129,8 @@ var require_dist = __commonJS({
1027
1129
  description: "Add a phone number to the Do-Not-Call registry so outbound calls to it are blocked.",
1028
1130
  scope: "account",
1029
1131
  risk: "write",
1132
+ routes: ["POST /v1/dnc"],
1133
+ keyScopes: ["contacts:write"],
1030
1134
  input: {
1031
1135
  phone_number: import_zod7.z.string().describe("Phone number (any format; normalized to E.164)"),
1032
1136
  reason: import_zod7.z.string().optional()
@@ -1038,6 +1142,8 @@ var require_dist = __commonJS({
1038
1142
  description: "Add up to 1000 numbers to the Do-Not-Call registry. Invalid numbers are skipped and returned in `invalid`.",
1039
1143
  scope: "account",
1040
1144
  risk: "write",
1145
+ routes: ["POST /v1/dnc/batch"],
1146
+ keyScopes: ["contacts:write"],
1041
1147
  input: {
1042
1148
  phone_numbers: import_zod7.z.array(import_zod7.z.string()).min(1).max(1e3),
1043
1149
  reason: import_zod7.z.string().optional()
@@ -1049,6 +1155,8 @@ var require_dist = __commonJS({
1049
1155
  description: "WARNING: removes a number from the Do-Not-Call registry, re-enabling outbound calls to it.",
1050
1156
  scope: "account",
1051
1157
  risk: "sensitive",
1158
+ routes: ["DELETE /v1/dnc/:phone"],
1159
+ keyScopes: ["contacts:write"],
1052
1160
  input: { phone: import_zod7.z.string().describe("Phone number to remove") },
1053
1161
  run: (client, { phone }) => client.dnc.remove(phone)
1054
1162
  })
@@ -1061,6 +1169,8 @@ var require_dist = __commonJS({
1061
1169
  description: "List your registered webhook endpoints. Signing secrets are never returned.",
1062
1170
  scope: "account",
1063
1171
  risk: "read",
1172
+ routes: ["GET /v1/webhooks"],
1173
+ keyScopes: ["webhooks:read"],
1064
1174
  input: {
1065
1175
  limit: import_zod8.z.number().int().min(1).max(500).optional(),
1066
1176
  offset: import_zod8.z.number().int().min(0).optional()
@@ -1072,6 +1182,8 @@ var require_dist = __commonJS({
1072
1182
  description: "Register a webhook to receive call lifecycle events (e.g. call.completed, transcript.ready). URL must be public https. If you omit `secret`, one is generated and shown ONCE in the response \u2014 store it to verify signatures.",
1073
1183
  scope: "account",
1074
1184
  risk: "write",
1185
+ routes: ["POST /v1/webhooks"],
1186
+ keyScopes: ["webhooks:write"],
1075
1187
  input: {
1076
1188
  url: import_zod8.z.string().url().describe("Public https endpoint"),
1077
1189
  events: import_zod8.z.array(import_zod8.z.string()).min(1).describe("Event names to subscribe to"),
@@ -1089,6 +1201,8 @@ var require_dist = __commonJS({
1089
1201
  description: "Update a webhook endpoint. Only provided fields change. Pass `secret` to rotate it.",
1090
1202
  scope: "account",
1091
1203
  risk: "write",
1204
+ routes: ["PATCH /v1/webhooks/:id"],
1205
+ keyScopes: ["webhooks:write"],
1092
1206
  input: {
1093
1207
  id: import_zod8.z.string(),
1094
1208
  url: import_zod8.z.string().url().optional(),
@@ -1103,6 +1217,8 @@ var require_dist = __commonJS({
1103
1217
  description: "WARNING: stops event delivery to this endpoint.",
1104
1218
  scope: "account",
1105
1219
  risk: "sensitive",
1220
+ routes: ["DELETE /v1/webhooks/:id"],
1221
+ keyScopes: ["webhooks:write"],
1106
1222
  input: { id: import_zod8.z.string() },
1107
1223
  run: (client, { id }) => client.webhooks.delete(id)
1108
1224
  })
@@ -1114,6 +1230,8 @@ var require_dist = __commonJS({
1114
1230
  description: "Estimate how much balance a call will consume for a custom model stack and duration. Results are usage estimates; surface remaining balance to customers in minutes.",
1115
1231
  scope: "account",
1116
1232
  risk: "read",
1233
+ routes: ["POST /v1/billing/estimate"],
1234
+ keyScopes: ["agents:read"],
1117
1235
  input: {
1118
1236
  stt_model: import_zod9.z.string().optional(),
1119
1237
  llm_model: import_zod9.z.string().optional(),
@@ -1127,6 +1245,8 @@ var require_dist = __commonJS({
1127
1245
  description: "Configure auto-recharge. `pack_usd` must exceed `threshold_usd` or the API rejects it (recharge-loop guard).",
1128
1246
  scope: "account",
1129
1247
  risk: "sensitive",
1248
+ routes: ["PUT /v1/billing/auto-recharge"],
1249
+ keyScopes: [BILLING_WRITE],
1130
1250
  input: {
1131
1251
  enabled: import_zod9.z.boolean().optional(),
1132
1252
  pack_usd: import_zod9.z.number().min(1).max(1e3).optional(),
@@ -1139,6 +1259,8 @@ var require_dist = __commonJS({
1139
1259
  description: "Configure spend-alert thresholds and low-balance / email preferences.",
1140
1260
  scope: "account",
1141
1261
  risk: "write",
1262
+ routes: ["PUT /v1/billing/alerts"],
1263
+ keyScopes: [BILLING_WRITE],
1142
1264
  input: {
1143
1265
  thresholds_usd: import_zod9.z.array(import_zod9.z.number()).max(10).optional(),
1144
1266
  low_balance_pct: import_zod9.z.number().min(0).max(100).optional(),
@@ -1155,6 +1277,8 @@ var require_dist = __commonJS({
1155
1277
  description: "List campaigns for the authenticated tenant, paginated.",
1156
1278
  scope: "agent",
1157
1279
  risk: "read",
1280
+ routes: ["GET /v1/campaigns"],
1281
+ keyScopes: ["campaigns:read"],
1158
1282
  input: {
1159
1283
  limit: import_zod10.z.number().int().min(1).max(200).optional().describe("Max campaigns to return"),
1160
1284
  offset: import_zod10.z.number().int().min(0).optional().describe("Pagination offset"),
@@ -1169,6 +1293,8 @@ var require_dist = __commonJS({
1169
1293
  description: "Retrieve a single campaign by id, including its goal, policies, status, and stat counters.",
1170
1294
  scope: "agent",
1171
1295
  risk: "read",
1296
+ routes: ["GET /v1/campaigns/:id"],
1297
+ keyScopes: ["campaigns:read"],
1172
1298
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID") },
1173
1299
  run: (client, { campaign_id }) => client.campaigns.get(campaign_id)
1174
1300
  }),
@@ -1177,6 +1303,8 @@ var require_dist = __commonJS({
1177
1303
  description: "Retrieve the campaign report: stat_* summary counters plus outcome/sentiment/hourly/snapshot distributions. Cost is reported in credits, durations in minutes.",
1178
1304
  scope: "agent",
1179
1305
  risk: "read",
1306
+ routes: ["GET /v1/campaigns/:id/report"],
1307
+ keyScopes: ["campaigns:read"],
1180
1308
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID") },
1181
1309
  run: (client, { campaign_id }) => client.campaigns.report(campaign_id)
1182
1310
  }),
@@ -1185,6 +1313,8 @@ var require_dist = __commonJS({
1185
1313
  description: "List a campaign's dial attempts, paginated.",
1186
1314
  scope: "agent",
1187
1315
  risk: "read",
1316
+ routes: ["GET /v1/campaigns/:id/attempts"],
1317
+ keyScopes: ["campaigns:read"],
1188
1318
  input: {
1189
1319
  campaign_id: import_zod10.z.string().describe("The campaign ID"),
1190
1320
  limit: import_zod10.z.number().int().min(1).max(200).optional(),
@@ -1198,6 +1328,8 @@ var require_dist = __commonJS({
1198
1328
  description: "List a campaign's improvement suggestions (from the detectors + LLM proposal engine), paginated.",
1199
1329
  scope: "agent",
1200
1330
  risk: "read",
1331
+ routes: ["GET /v1/campaigns/:id/suggestions"],
1332
+ keyScopes: ["campaigns:read"],
1201
1333
  input: {
1202
1334
  campaign_id: import_zod10.z.string().describe("The campaign ID"),
1203
1335
  limit: import_zod10.z.number().int().min(1).max(200).optional(),
@@ -1211,6 +1343,8 @@ var require_dist = __commonJS({
1211
1343
  description: "List tenant-level contacts, paginated.",
1212
1344
  scope: "agent",
1213
1345
  risk: "read",
1346
+ routes: ["GET /v1/contacts"],
1347
+ keyScopes: ["contacts:read"],
1214
1348
  input: {
1215
1349
  limit: import_zod10.z.number().int().min(1).max(200).optional(),
1216
1350
  offset: import_zod10.z.number().int().min(0).optional()
@@ -1223,6 +1357,8 @@ var require_dist = __commonJS({
1223
1357
  description: "Create a new campaign in draft status against a published agent. Does not launch it or place any calls.",
1224
1358
  scope: "agent",
1225
1359
  risk: "write",
1360
+ routes: ["POST /v1/campaigns"],
1361
+ keyScopes: ["campaigns:write"],
1226
1362
  input: {
1227
1363
  agent_id: import_zod10.z.string().uuid().describe("The agent that will handle campaign calls"),
1228
1364
  type: import_zod10.z.enum(["outbound", "inbound"]).describe("Campaign type"),
@@ -1248,6 +1384,8 @@ var require_dist = __commonJS({
1248
1384
  description: "Import inline contact rows (JSON or CSV) into a campaign: creates/merges contacts and attaches them as campaign members in one call.",
1249
1385
  scope: "agent",
1250
1386
  risk: "write",
1387
+ routes: ["POST /v1/campaigns/:id/members/import"],
1388
+ keyScopes: ["campaigns:write"],
1251
1389
  input: {
1252
1390
  campaign_id: import_zod10.z.string().describe("The campaign ID"),
1253
1391
  contacts: import_zod10.z.array(import_zod10.z.record(import_zod10.z.any())).optional().describe("Inline contact rows as JSON objects"),
@@ -1260,6 +1398,8 @@ var require_dist = __commonJS({
1260
1398
  description: "WARNING: launches a campaign, which places real outbound PSTN calls at scale and incurs per-minute charges for every recipient dialed. Validates launch blockers first (published snapshot, valid members, caller ID, DNC, credits, follow-up connectors); if blockers fail, no calls are placed.",
1261
1399
  scope: "agent",
1262
1400
  risk: "sensitive",
1401
+ routes: ["POST /v1/campaigns/:id/launch"],
1402
+ keyScopes: ["campaigns:write"],
1263
1403
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID to launch") },
1264
1404
  run: (client, { campaign_id }) => client.campaigns.launch(campaign_id)
1265
1405
  }),
@@ -1268,6 +1408,8 @@ var require_dist = __commonJS({
1268
1408
  description: "Pause a running campaign. In-flight calls finish naturally; no new attempts are scheduled until resumed.",
1269
1409
  scope: "agent",
1270
1410
  risk: "write",
1411
+ routes: ["POST /v1/campaigns/:id/pause"],
1412
+ keyScopes: ["campaigns:write"],
1271
1413
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID to pause") },
1272
1414
  run: (client, { campaign_id }) => client.campaigns.pause(campaign_id)
1273
1415
  }),
@@ -1276,6 +1418,8 @@ var require_dist = __commonJS({
1276
1418
  description: "Resume a paused campaign. WARNING: resuming re-enables outbound dialing and will incur further per-minute charges as attempts continue.",
1277
1419
  scope: "agent",
1278
1420
  risk: "sensitive",
1421
+ routes: ["POST /v1/campaigns/:id/resume"],
1422
+ keyScopes: ["campaigns:write"],
1279
1423
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID to resume") },
1280
1424
  run: (client, { campaign_id }) => client.campaigns.resume(campaign_id)
1281
1425
  }),
@@ -1284,6 +1428,8 @@ var require_dist = __commonJS({
1284
1428
  description: "Accept an improvement suggestion. Agent-editing suggestions create an agent-changes draft for review; campaign-policy suggestions patch the campaign directly.",
1285
1429
  scope: "agent",
1286
1430
  risk: "write",
1431
+ routes: ["POST /v1/campaigns/:id/suggestions/:suggestionId/accept"],
1432
+ keyScopes: ["campaigns:write"],
1287
1433
  input: {
1288
1434
  campaign_id: import_zod10.z.string().describe("The campaign ID"),
1289
1435
  suggestion_id: import_zod10.z.string().describe("The suggestion ID to accept")
@@ -1295,6 +1441,8 @@ var require_dist = __commonJS({
1295
1441
  description: "Get a campaign's lead-intake URL settings (consent and default-country handling, last used). Returns intake: null if none. The URL itself is only shown when created or rotated.",
1296
1442
  scope: "agent",
1297
1443
  risk: "read",
1444
+ routes: ["GET /v1/campaigns/:id/intake"],
1445
+ keyScopes: ["campaigns:read"],
1298
1446
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID") },
1299
1447
  run: (client, { campaign_id }) => client.campaigns.intake.get(campaign_id)
1300
1448
  }),
@@ -1303,6 +1451,8 @@ var require_dist = __commonJS({
1303
1451
  description: 'Create a lead-intake URL for a campaign, or update its settings. Lead sources (website forms, Typeform, Google Apps Script, Zapier/Make) POST leads to the URL with no API key; a running campaign dials them within its calling window. The URL is returned ONCE on create (url: null on later updates) and anyone holding it can add numbers to the campaign. assume_voice_consent=true treats every posted lead as consenting to calls; default_country (e.g. "IN") makes local numbers without +<code> dialable.',
1304
1452
  scope: "agent",
1305
1453
  risk: "sensitive",
1454
+ routes: ["PUT /v1/campaigns/:id/intake"],
1455
+ keyScopes: ["campaigns:write"],
1306
1456
  input: {
1307
1457
  campaign_id: import_zod10.z.string().describe("The campaign ID"),
1308
1458
  assume_voice_consent: import_zod10.z.boolean().optional().describe("Treat every posted lead as having voice consent"),
@@ -1315,6 +1465,8 @@ var require_dist = __commonJS({
1315
1465
  description: "Issue a new lead-intake URL for a campaign. The old URL stops accepting leads immediately, so every lead source must be updated.",
1316
1466
  scope: "agent",
1317
1467
  risk: "sensitive",
1468
+ routes: ["POST /v1/campaigns/:id/intake/rotate"],
1469
+ keyScopes: ["campaigns:write"],
1318
1470
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID") },
1319
1471
  run: (client, { campaign_id }) => client.campaigns.intake.rotate(campaign_id)
1320
1472
  }),
@@ -1323,6 +1475,8 @@ var require_dist = __commonJS({
1323
1475
  description: "Remove a campaign's lead-intake URL. Leads already accepted stay in the campaign.",
1324
1476
  scope: "agent",
1325
1477
  risk: "write",
1478
+ routes: ["DELETE /v1/campaigns/:id/intake"],
1479
+ keyScopes: ["campaigns:write"],
1326
1480
  input: { campaign_id: import_zod10.z.string().describe("The campaign ID") },
1327
1481
  run: async (client, { campaign_id }) => {
1328
1482
  await client.campaigns.intake.disable(campaign_id);
@@ -1471,6 +1625,8 @@ var require_dist = __commonJS({
1471
1625
  description: "List an agent's tool bindings (API tools / call_webhook, handoff, calendar, \u2026). Header values are shown only as {{secrets.NAME}} references; literal values are [redacted].",
1472
1626
  scope: "agent",
1473
1627
  risk: "read",
1628
+ routes: ["GET /v1/agents", "GET /v1/agents/:id"],
1629
+ keyScopes: ["agents:read"],
1474
1630
  input: { agent_id: import_zod11.z.string().describe("The agent UUID") },
1475
1631
  run: async (client, { agent_id }) => {
1476
1632
  const agentId = await resolveAgentId(client, agent_id);
@@ -1483,6 +1639,8 @@ var require_dist = __commonJS({
1483
1639
  description: `Add an API tool (call_webhook) to an agent, keeping its existing tools. The agent can then call the endpoint mid-call with the fields you define. ${LIVE_NOTE}`,
1484
1640
  scope: "agent",
1485
1641
  risk: "write",
1642
+ routes: ["GET /v1/agents", "GET /v1/agents/:id", "PUT /v1/agents/:id"],
1643
+ keyScopes: ["agents:read", "agents:write"],
1486
1644
  input: addShape,
1487
1645
  run: async (client, params) => {
1488
1646
  const p = import_zod11.z.object(addShape).parse(params);
@@ -1515,6 +1673,8 @@ var require_dist = __commonJS({
1515
1673
  description: `Change one API tool (call_webhook) on an agent by name. Only the settings you pass change; fields replaces the whole list, headers merge by name. ${LIVE_NOTE}`,
1516
1674
  scope: "agent",
1517
1675
  risk: "write",
1676
+ routes: ["GET /v1/agents", "GET /v1/agents/:id", "PUT /v1/agents/:id"],
1677
+ keyScopes: ["agents:read", "agents:write"],
1518
1678
  input: updateShape,
1519
1679
  run: async (client, params) => {
1520
1680
  const p = import_zod11.z.object(updateShape).parse(params);
@@ -1554,6 +1714,8 @@ var require_dist = __commonJS({
1554
1714
  description: `Remove one API tool (call_webhook) from an agent by name, keeping its other tools. ${LIVE_NOTE}`,
1555
1715
  scope: "agent",
1556
1716
  risk: "write",
1717
+ routes: ["GET /v1/agents", "GET /v1/agents/:id", "PUT /v1/agents/:id"],
1718
+ keyScopes: ["agents:read", "agents:write"],
1557
1719
  input: { agent_id: import_zod11.z.string().describe("The agent UUID"), name: import_zod11.z.string().describe("Name of the API tool to remove.") },
1558
1720
  run: async (client, { agent_id, name }) => {
1559
1721
  const { agentId, tools } = await writeTools(client, agent_id, (tools2) => {
@@ -1568,6 +1730,8 @@ var require_dist = __commonJS({
1568
1730
  description: `Switch built-in tools on or off for one agent: calendar (check/list/create/update/delete), lookup_customer, lookup_ticket/create_ticket, send_whatsapp_message, send_telegram_message, send_asset, handoff_to_human, schedule_callback, and the always-on ones (end_call, forget_me, knowledge_search, get_caller_history, datetime_now). Groups: "calendar" (all five calendar tools), "tickets". Connected-app tools are opt-in per agent: connecting the app for the workspace makes them available, and an agent gets one only when switched on here or in Studio; it also needs the app connected (Settings \u2192 Connectors) to work on calls. Other agents are not affected. ${LIVE_NOTE}`,
1569
1731
  scope: "agent",
1570
1732
  risk: "write",
1733
+ routes: ["GET /v1/agents", "GET /v1/agents/:id", "PUT /v1/agents/:id"],
1734
+ keyScopes: ["agents:read", "agents:write"],
1571
1735
  input: {
1572
1736
  agent_id: import_zod11.z.string().describe("The agent UUID"),
1573
1737
  tools: import_zod11.z.array(import_zod11.z.string()).min(1).describe('Tool ids or groups, e.g. ["calendar"] or ["send_whatsapp_message", "send_asset"].'),
@@ -1584,6 +1748,8 @@ var require_dist = __commonJS({
1584
1748
  description: "List the workspace's API-tool secret names and the host each is pinned to. Values are never returned.",
1585
1749
  scope: "account",
1586
1750
  risk: "read",
1751
+ routes: ["GET /v1/tool-secrets"],
1752
+ keyScopes: ["connections:read"],
1587
1753
  input: {},
1588
1754
  run: (client) => client.toolSecrets.list()
1589
1755
  }),
@@ -1592,6 +1758,8 @@ var require_dist = __commonJS({
1592
1758
  description: 'Create or replace a workspace secret for API-tool headers, referenced as "{{secrets.NAME}}" (e.g. header x-api-key: "{{secrets.RESERVATIONS_API_KEY}}"). The value is encrypted, write-only, and only ever sent to `host`. Replacing an existing name changes it for every agent that references it.',
1593
1759
  scope: "account",
1594
1760
  risk: "sensitive",
1761
+ routes: ["PUT /v1/tool-secrets/:name"],
1762
+ keyScopes: ["connections:write"],
1595
1763
  input: {
1596
1764
  name: import_zod11.z.string().describe("UPPER_SNAKE_CASE, 2\u201364 characters."),
1597
1765
  value: import_zod11.z.string().min(1).max(4096).describe("The secret value. Never echoed back."),
@@ -1611,6 +1779,8 @@ var require_dist = __commonJS({
1611
1779
  description: "Get an agent's public share link (URL, limits, minutes used). Returns link: null if none exists yet.",
1612
1780
  scope: "agent",
1613
1781
  risk: "read",
1782
+ routes: ["GET /v1/agents/:id/testers"],
1783
+ keyScopes: ["agents:read"],
1614
1784
  input: { agent_id: import_zod12.z.string().uuid().describe("The agent UUID") },
1615
1785
  run: (client, { agent_id }) => client.agents.getShareLink(agent_id)
1616
1786
  }),
@@ -1619,6 +1789,8 @@ var require_dist = __commonJS({
1619
1789
  description: "Create an agent's public share link, or update its settings. Anyone with the URL can talk to the agent in a browser, billed to this workspace. Defaults on create: 30-minute pool, 300 s per call, 2 concurrent calls, 3 calls per IP per hour. Set enabled=false to switch the link off.",
1620
1790
  scope: "agent",
1621
1791
  risk: "sensitive",
1792
+ routes: ["PUT /v1/agents/:id/share-link"],
1793
+ keyScopes: ["agents:write"],
1622
1794
  input: {
1623
1795
  agent_id: import_zod12.z.string().uuid().describe("The agent UUID"),
1624
1796
  enabled: import_zod12.z.boolean().optional().describe("Turn the link on or off"),
@@ -1634,6 +1806,8 @@ var require_dist = __commonJS({
1634
1806
  description: "Issue a new URL for an agent's share link. The old URL stops working immediately.",
1635
1807
  scope: "agent",
1636
1808
  risk: "sensitive",
1809
+ routes: ["POST /v1/agents/:id/share-link/regenerate"],
1810
+ keyScopes: ["agents:write"],
1637
1811
  input: { agent_id: import_zod12.z.string().uuid().describe("The agent UUID") },
1638
1812
  run: (client, { agent_id }) => client.agents.regenerateShareLink(agent_id)
1639
1813
  })
@@ -1645,26 +1819,32 @@ var require_dist = __commonJS({
1645
1819
  description: "List the workspaces this credential can act in, marking the current one. Client workspaces show their agency in parent_id. Act in another by setting RYMI_WORKSPACE (local MCP) or the Rymi-Workspace header.",
1646
1820
  scope: "account",
1647
1821
  risk: "read",
1822
+ routes: ["GET /v1/workspaces"],
1823
+ keyScopes: [],
1648
1824
  input: {},
1649
1825
  run: (client) => client.workspaces.list()
1650
1826
  }),
1651
1827
  defineTool({
1652
1828
  name: "create_workspace",
1653
- description: "Create a standalone workspace you own, or, with parent, a client workspace of an agency workspace you run. The agency pays for a client workspace's calls. Neither gets the free monthly credit.",
1829
+ description: "Create a workspace in an account you own or administer. The account pays for its calls. New workspaces don't get the free monthly credit.",
1654
1830
  scope: "account",
1655
1831
  risk: "write",
1832
+ routes: ["POST /v1/workspaces"],
1833
+ keyScopes: ["workspaces:write"],
1656
1834
  input: {
1657
1835
  name: import_zod13.z.string().min(1).max(80).describe("Workspace name"),
1658
1836
  operating_country: import_zod13.z.string().length(2).optional().describe('ISO-3166 alpha-2 country, e.g. "IN"'),
1659
- parent: import_zod13.z.string().optional().describe("Agency workspace id. Makes this a client workspace the agency pays for.")
1837
+ parent: import_zod13.z.string().optional().describe("Any workspace in the account to create it in (defaults to the current account).")
1660
1838
  },
1661
1839
  run: (client, params) => client.workspaces.create(params)
1662
1840
  }),
1663
1841
  defineTool({
1664
1842
  name: "get_workspace_usage",
1665
- description: "Calls, minutes and credits for one workspace in a calendar month (UTC). Use it to invoice a client workspace. Credits are left out for client-role users.",
1843
+ description: "Calls, minutes and credits for one workspace in a calendar month (UTC). Use it to bill a customer for one workspace. Credits are left out for the client and viewer roles.",
1666
1844
  scope: "account",
1667
1845
  risk: "read",
1846
+ routes: ["GET /v1/workspaces/:id/usage"],
1847
+ keyScopes: ["analytics:read"],
1668
1848
  input: {
1669
1849
  workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces"),
1670
1850
  month: import_zod13.z.string().regex(/^\d{4}-\d{2}$/).optional().describe("YYYY-MM; default this month")
@@ -1673,42 +1853,60 @@ var require_dist = __commonJS({
1673
1853
  }),
1674
1854
  defineTool({
1675
1855
  name: "update_workspace",
1676
- description: "Rename a workspace, change its operating country, or set a client workspace's monthly spend cap in credits (1 credit = $0.01; null removes the cap). Calls in that client workspace stop at the cap until next month.",
1856
+ description: "Rename a workspace, change its operating country, or set its monthly spend cap in credits (1 credit = $0.01; null removes the cap). Calls in that workspace stop at the cap until next month. The cap needs an account Owner or Admin.",
1677
1857
  scope: "account",
1678
1858
  risk: "write",
1859
+ routes: ["PATCH /v1/workspaces/:id"],
1860
+ keyScopes: ["workspaces:write"],
1679
1861
  input: {
1680
1862
  workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces"),
1681
1863
  name: import_zod13.z.string().min(1).max(80).optional().describe("New name"),
1682
1864
  operating_country: import_zod13.z.string().length(2).nullable().optional().describe('ISO-3166 alpha-2 country, e.g. "IN"; null clears it'),
1683
- spend_cap_cents_monthly: import_zod13.z.number().int().min(0).nullable().optional().describe("Client workspaces only: monthly cap in credits; null removes it")
1865
+ spend_cap_cents_monthly: import_zod13.z.number().int().min(0).nullable().optional().describe("Monthly cap in credits; null removes it")
1684
1866
  },
1685
1867
  run: (client, { workspace_id, ...patch }) => client.workspaces.update(workspace_id, patch)
1686
1868
  }),
1869
+ defineTool({
1870
+ name: "delete_workspace",
1871
+ description: "Delete an empty workspace: no agents, numbers, calls or campaigns left in it. Only the account Owner may. The account's primary workspace can't be deleted.",
1872
+ scope: "account",
1873
+ risk: "sensitive",
1874
+ routes: ["DELETE /v1/workspaces/:id"],
1875
+ keyScopes: ["workspaces:write"],
1876
+ input: { workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces") },
1877
+ run: (client, params) => client.workspaces.delete(params.workspace_id)
1878
+ }),
1687
1879
  defineTool({
1688
1880
  name: "list_workspace_members",
1689
- description: "People in a workspace and their roles (owner, admin, client).",
1881
+ description: "People in a workspace and their roles (admin, editor, client, viewer).",
1690
1882
  scope: "account",
1691
1883
  risk: "read",
1884
+ routes: ["GET /v1/workspaces/:id/members"],
1885
+ keyScopes: ["workspace_members:read"],
1692
1886
  input: { workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces") },
1693
1887
  run: (client, params) => client.workspaces.listMembers(params.workspace_id)
1694
1888
  }),
1695
1889
  defineTool({
1696
1890
  name: "add_workspace_member",
1697
- description: "Add a person by email. A client workspace takes the client role (sees calls and minutes, edits contacts, the do-not-call list, knowledge and the transfer number); other workspaces take admin. Someone without a Rymi account gets an invite email.",
1891
+ description: "Add a person by email with a workspace role: admin, editor, client (sees calls and minutes, edits contacts, the do-not-call list, knowledge and the transfer number) or viewer (reads what a client sees). The role defaults to client. Someone without a Rymi account gets an invite email.",
1698
1892
  scope: "account",
1699
1893
  risk: "sensitive",
1894
+ routes: ["POST /v1/workspaces/:id/members"],
1895
+ keyScopes: ["workspace_members:write"],
1700
1896
  input: {
1701
1897
  workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces"),
1702
1898
  email: import_zod13.z.string().email().describe("Their email address"),
1703
- role: import_zod13.z.enum(["admin", "client"]).optional().describe("Defaults to the role the workspace takes")
1899
+ role: import_zod13.z.enum(["admin", "editor", "client", "viewer"]).optional().describe("Workspace role; default client")
1704
1900
  },
1705
1901
  run: (client, { workspace_id, ...member }) => client.workspaces.addMember(workspace_id, member)
1706
1902
  }),
1707
1903
  defineTool({
1708
1904
  name: "remove_workspace_member",
1709
- description: "Remove a person's access to a workspace. The owner can't be removed.",
1905
+ description: "Remove a person's role in a workspace.",
1710
1906
  scope: "account",
1711
1907
  risk: "sensitive",
1908
+ routes: ["DELETE /v1/workspaces/:id/members/:userId"],
1909
+ keyScopes: ["workspace_members:write"],
1712
1910
  input: {
1713
1911
  workspace_id: import_zod13.z.string().describe("Workspace id from list_workspaces"),
1714
1912
  user_id: import_zod13.z.string().describe("user_id from list_workspace_members")
@@ -1731,6 +1929,8 @@ var require_dist = __commonJS({
1731
1929
  description: "Read the workspace's compliance settings: operating country, callee-local calling hours, do-not-call check, voice consent, recording-notice default and allowed destination countries. Compliance is the customer's responsibility; these settings help.",
1732
1930
  scope: "account",
1733
1931
  risk: "read",
1932
+ routes: ["GET /v1/compliance/settings"],
1933
+ keyScopes: ["compliance:read"],
1734
1934
  input: {},
1735
1935
  run: (client) => client.compliance.getSettings()
1736
1936
  }),
@@ -1739,6 +1939,8 @@ var require_dist = __commonJS({
1739
1939
  description: "Change the workspace's compliance settings. Setting operating_country fills every setting not already edited from that country's preset. Blocked calls fail with destination_not_allowed, outside_calling_hours or no_voice_consent.",
1740
1940
  scope: "account",
1741
1941
  risk: "write",
1942
+ routes: ["PUT /v1/compliance/settings"],
1943
+ keyScopes: ["compliance:write"],
1742
1944
  input: {
1743
1945
  operating_country: import_zod14.z.string().length(2).nullable().optional().describe('ISO country, e.g. "IN"'),
1744
1946
  rules: rules.optional()
@@ -1746,6 +1948,57 @@ var require_dist = __commonJS({
1746
1948
  run: (client, params) => client.compliance.updateSettings(params)
1747
1949
  })
1748
1950
  ];
1951
+ var import_zod15 = require("zod");
1952
+ var accountId = import_zod15.z.string().describe("Account id from list_accounts");
1953
+ var accountTools = [
1954
+ defineTool({
1955
+ name: "list_accounts",
1956
+ description: "List the accounts you belong to and your role in each (owner, admin, or billing).",
1957
+ scope: "account",
1958
+ risk: "read",
1959
+ routes: ["GET /v1/accounts"],
1960
+ keyScopes: [],
1961
+ input: {},
1962
+ run: (client) => client.accounts.list()
1963
+ }),
1964
+ defineTool({
1965
+ name: "list_account_members",
1966
+ description: "List people in an account and their roles (owner, admin, billing).",
1967
+ scope: "account",
1968
+ risk: "read",
1969
+ routes: ["GET /v1/accounts/:id/members"],
1970
+ keyScopes: ["account_members:read"],
1971
+ input: { account_id: accountId },
1972
+ run: (client, { account_id }) => client.accounts.members.list(account_id)
1973
+ }),
1974
+ defineTool({
1975
+ name: "add_account_member",
1976
+ description: "Add a person to an account by email, with role owner, admin, or billing. An owner can grant owner. An admin can grant admin or billing.",
1977
+ scope: "account",
1978
+ risk: "sensitive",
1979
+ routes: ["POST /v1/accounts/:id/members"],
1980
+ keyScopes: ["account_members:write"],
1981
+ input: {
1982
+ account_id: accountId,
1983
+ email: import_zod15.z.string().email().describe("Their email address"),
1984
+ role: import_zod15.z.enum(["owner", "admin", "billing"]).describe("Account role")
1985
+ },
1986
+ run: (client, { account_id, email, role }) => client.accounts.members.add(account_id, { email, role })
1987
+ }),
1988
+ defineTool({
1989
+ name: "remove_account_member",
1990
+ description: "Remove a person from an account. The last owner stays.",
1991
+ scope: "account",
1992
+ risk: "sensitive",
1993
+ routes: ["DELETE /v1/accounts/:id/members/:userId"],
1994
+ keyScopes: ["account_members:write"],
1995
+ input: {
1996
+ account_id: accountId,
1997
+ user_id: import_zod15.z.string().describe("User id from list_account_members")
1998
+ },
1999
+ run: (client, { account_id, user_id }) => client.accounts.members.remove(account_id, user_id)
2000
+ })
2001
+ ];
1749
2002
  var import_agentTools2 = (init_agentTools(), __toCommonJS(agentTools_exports));
1750
2003
  var REDACTED = "[redacted]";
1751
2004
  function redactToolSecrets(value) {
@@ -1765,31 +2018,38 @@ var require_dist = __commonJS({
1765
2018
  }
1766
2019
  return out;
1767
2020
  }
2021
+ function publish(tools) {
2022
+ return tools.map((t) => ({
2023
+ ...t,
2024
+ run: async (client, params) => redactToolSecrets(await t.run(client, params))
2025
+ }));
2026
+ }
1768
2027
  var opsToolCatalog2 = [
1769
- ...agentTools,
1770
- ...agentToolBindingTools,
1771
- ...callTools,
1772
- ...numberTools,
1773
- ...telephonyTools,
1774
- ...keyTools,
1775
- ...knowledgeTools,
1776
- ...insightTools,
1777
- ...publishTools,
1778
- ...dncTools,
1779
- ...webhookTools,
1780
- ...billingTools,
1781
- ...campaignTools,
1782
- ...shareLinkTools,
1783
- ...workspaceTools,
1784
- ...complianceTools
1785
- ].map((t) => ({ ...t, run: async (client, params) => redactToolSecrets(await t.run(client, params)) }));
2028
+ ...publish(agentTools),
2029
+ ...publish(agentToolBindingTools),
2030
+ ...publish(callTools),
2031
+ ...publish(numberTools),
2032
+ ...publish(telephonyTools),
2033
+ ...publish(keyTools),
2034
+ ...publish(knowledgeTools),
2035
+ ...publish(insightTools),
2036
+ ...publish(publishTools),
2037
+ ...publish(dncTools),
2038
+ ...publish(webhookTools),
2039
+ ...publish(billingTools),
2040
+ ...publish(campaignTools),
2041
+ ...publish(shareLinkTools),
2042
+ ...publish(workspaceTools),
2043
+ ...publish(complianceTools),
2044
+ ...publish(accountTools)
2045
+ ];
1786
2046
  var names = /* @__PURE__ */ new Set();
1787
2047
  for (const tool of opsToolCatalog2) {
1788
2048
  if (names.has(tool.name)) throw new Error(`Duplicate ops tool name: ${tool.name}`);
1789
2049
  names.add(tool.name);
1790
2050
  }
1791
- var import_zod15 = require("zod");
1792
- var WORKSPACE = import_zod15.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
2051
+ var import_zod16 = require("zod");
2052
+ var WORKSPACE = import_zod16.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
1793
2053
  function withWorkspaceParam2(tool) {
1794
2054
  return {
1795
2055
  ...tool,
@@ -1829,7 +2089,7 @@ function withToolErrors(handler) {
1829
2089
  // package.json
1830
2090
  var package_default = {
1831
2091
  name: "@rymi/mcp",
1832
- version: "2.6.0",
2092
+ version: "2.8.0",
1833
2093
  description: "Rymi MCP server \u2014 manage AI voice agents via Model Context Protocol",
1834
2094
  license: "MIT",
1835
2095
  author: "Rymi AI <engineering@rymi.live>",
@@ -1890,16 +2150,38 @@ var package_default = {
1890
2150
  };
1891
2151
 
1892
2152
  // src/server.ts
1893
- function createServer(apiKey2) {
2153
+ var RETRY_DELAYS_MS = [500, 1500];
2154
+ async function readKeyScopes(rymi, delays) {
2155
+ for (let attempt = 0; ; attempt++) {
2156
+ try {
2157
+ const self = await rymi.keys.self();
2158
+ return { scopes: self.scopes ?? [], legacy: self.legacy === true };
2159
+ } catch (err) {
2160
+ const status = err.status;
2161
+ const final = status !== void 0 && status >= 400 && status < 500;
2162
+ if (final || attempt >= delays.length) {
2163
+ process.stderr.write(
2164
+ `Warning: could not read this key's scopes from GET /v1/keys/self (${err instanceof Error ? err.message : String(err)}). Serving read-only tools. Restart once the API is reachable; @rymi/mcp needs a Rymi API with /v1/keys/self.
2165
+ `
2166
+ );
2167
+ return null;
2168
+ }
2169
+ await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
2170
+ }
2171
+ }
2172
+ }
2173
+ async function createServer(apiKey2, options = {}) {
1894
2174
  const rymi = new import_node.default({ apiKey: apiKey2 });
1895
- const server2 = new import_mcp.McpServer({
2175
+ const key = await readKeyScopes(rymi, options.retryDelaysMs ?? RETRY_DELAYS_MS);
2176
+ const server = new import_mcp.McpServer({
1896
2177
  name: "rymi",
1897
2178
  version: package_default.version
1898
2179
  });
1899
- const isReadOnly = process.env.RYMI_MCP_READONLY === "1";
2180
+ const isReadOnly = process.env.RYMI_MCP_READONLY === "1" || key === null;
1900
2181
  for (const tool of import_ops_tools.opsToolCatalog.map(import_ops_tools.withWorkspaceParam)) {
1901
2182
  if (isReadOnly && tool.risk !== "read") continue;
1902
- server2.tool(
2183
+ if (key && !(0, import_ops_tools.keyAllowsTool)(tool, key)) continue;
2184
+ server.tool(
1903
2185
  tool.name,
1904
2186
  tool.description,
1905
2187
  tool.input,
@@ -1909,14 +2191,14 @@ function createServer(apiKey2) {
1909
2191
  })
1910
2192
  );
1911
2193
  }
1912
- return server2;
2194
+ return server;
1913
2195
  }
1914
2196
 
1915
2197
  // src/transport/stdio.ts
1916
2198
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
1917
- async function runStdio(server2) {
2199
+ async function runStdio(server) {
1918
2200
  const transport2 = new import_stdio.StdioServerTransport();
1919
- await server2.connect(transport2);
2201
+ await server.connect(transport2);
1920
2202
  }
1921
2203
 
1922
2204
  // src/index.ts
@@ -1936,9 +2218,8 @@ if (!apiKey) {
1936
2218
  process.stderr.write("Error: RYMI_API_KEY environment variable is required.\n");
1937
2219
  process.exit(1);
1938
2220
  }
1939
- var server = createServer(apiKey);
1940
- runStdio(server).catch((err) => {
1941
- process.stderr.write(`Fatal: ${err.message}
2221
+ createServer(apiKey).then((server) => runStdio(server)).catch((err) => {
2222
+ process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
1942
2223
  `);
1943
2224
  process.exit(1);
1944
2225
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rymi/mcp",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "Rymi MCP server — manage AI voice agents via Model Context Protocol",
5
5
  "license": "MIT",
6
6
  "author": "Rymi AI <engineering@rymi.live>",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "^1.11.1",
47
- "@rymi/node": "^2.5.0",
47
+ "@rymi/node": "^2.7.0",
48
48
  "zod": "^3.23.0"
49
49
  },
50
50
  "devDependencies": {