@zuora/agent-toolkit 0.1.0-alpha.0 → 0.1.0-beta.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/README.md CHANGED
@@ -1,10 +1,21 @@
1
1
  # @zuora/agent-toolkit
2
2
 
3
- > **Alpha release** — APIs may change before the 1.0 stable release.
3
+ > **Beta release — not intended for production use yet.** Customers are welcome to try the kit in sandbox or other non-production environments for evaluation and feedback. APIs may change before the 1.0 stable release.
4
4
 
5
- Zuora billing operations as callable tools for AI agent frameworks.
5
+ ## Introduction
6
6
 
7
- Built on the [Zuora MCP server](https://developer.zuora.com). Tool inventory is discovered at runtime from your tenant's MCP endpoint — it is not hardcoded in this package.
7
+ Zuora Agent Kit is an SDK that turns Zuora billing operations into tools an AI agent can call. This package, `@zuora/agent-toolkit`, provides the TypeScript SDK; the Python package, `zuora-agent-toolkit`, is planned.
8
+
9
+ Instead of wiring each Zuora REST API by hand for an LLM, the kit:
10
+
11
+ 1. Connects to your tenant's [Zuora MCP server](https://developer.zuora.com) using OAuth.
12
+ 2. Discovers available operations at runtime — the tool inventory is not hardcoded.
13
+ 3. Normalizes them into a shared tool shape, `ZuoraToolDefinition`.
14
+ 4. Exposes them in the format your agent framework expects: OpenAI Chat Completions, OpenAI Agents SDK, LangChain, or Vercel AI SDK.
15
+
16
+ Your agent can then do things like "list accounts" or "run a query" through tool calls, while the kit handles authentication, filtering (read-only vs. writes and tool bundles), approval rules, and observability.
17
+
18
+ To discover the bundles and tools available for your tenant, see [Discovery and execution](#discovery-and-execution) below.
8
19
 
9
20
  ## Quick start
10
21
 
@@ -34,12 +45,30 @@ Set `ZUORA_BASE_URL`, `ZUORA_CLIENT_ID`, and `ZUORA_CLIENT_SECRET` in your envir
34
45
  | `@zuora/agent-toolkit/vercel-ai` | `createZuoraVercelAIToolkit` | ✅ Implemented |
35
46
  | `@zuora/agent-toolkit/mcp` | — | 🚧 Coming soon |
36
47
 
48
+ ## Changelog
49
+
50
+ ### [0.1.0-beta.1]
51
+
52
+ - First beta release — customers are welcome to evaluate it in sandbox or other non-production environments; it is **not intended for production use yet**.
53
+ - Added an introduction explaining runtime tool discovery, the shared tool definition, framework adapters, and built-in safety and observability controls.
54
+
55
+ ### [0.1.0-alpha.1]
56
+
57
+ - Fixed HITL approval policy handling with correct `request.environment` usage
58
+ - Improved tool name resolution
59
+ - Removed deprecated `CLI_AGENT_SPLIT_OPERATIONS` references
60
+ - Test and documentation updates
61
+
62
+ ### [0.1.0-alpha.0]
63
+
64
+ Initial alpha release.
65
+
37
66
  ## Installation
38
67
 
39
68
  Requires **Node.js 18** or later.
40
69
 
41
70
  ```bash
42
- npm install @zuora/agent-toolkit
71
+ npm install @zuora/agent-toolkit@beta
43
72
  ```
44
73
 
45
74
  Framework packages are peer dependencies — install only what you use:
@@ -348,7 +377,11 @@ configuration: {
348
377
 
349
378
  ### Approval policy
350
379
 
351
- Gate write / high-risk operations before they reach Zuora. `approvalPolicy` is a **top-level** config field (not inside `configuration`):
380
+ Approval policies are checked before a tool executes. An `"allow"` decision lets execution proceed; `"deny"` blocks it with an `APPROVAL_REJECTED` error. If no policy is configured, no additional approval check is performed.
381
+
382
+ Approval policies are an additional guardrail, not a way to enable writes: write operations must also be enabled through `configuration.mode: SafetyMode.WriteEnabled`. The read-only execution guard runs before the approval policy.
383
+
384
+ `approvalPolicy` is a **top-level** config field (not inside `configuration`):
352
385
 
353
386
  ```ts
354
387
  import { ApprovalPolicyPreset, SafetyMode } from "@zuora/agent-toolkit";
@@ -365,9 +398,29 @@ const toolkit = await createZuoraOpenAIAgentToolkit({
365
398
  });
366
399
  ```
367
400
 
368
- Presets: `Off`, `AllWrites`, `HighRisk`, `ProductionWrites`, `ProductionHighRisk`.
401
+ Built-in presets automatically allow or deny operations; they do **not** display an approval prompt. Operations that do not match a preset's denial condition are allowed by that policy.
402
+
403
+ | Preset | Behavior |
404
+ |---|---|
405
+ | `Off` | No additional approval restriction |
406
+ | `AllWrites` | Denies all write operations |
407
+ | `HighRisk` | Denies all high-risk operations, including reads |
408
+ | `ProductionWrites` | Denies writes when the detected environment is production |
409
+ | `ProductionHighRisk` | Denies high-risk operations when the detected environment is production |
410
+
411
+ Custom handlers can be synchronous or async and receive:
412
+
413
+ | Field | Contents |
414
+ |---|---|
415
+ | `request.tool` | Tool definition, including access and risk metadata for the invoked operation |
416
+ | `request.input` | Arguments supplied to the tool |
417
+ | `request.environment` | `"production"` or `"sandbox"`, inferred from `baseUrl` |
418
+
419
+ **Environment detection:** unrecognized URLs currently default to `"sandbox"`. Production-only presets therefore do not block operations on unrecognized URLs; use a custom policy if your endpoint is not recognized as production.
420
+
421
+ Custom handler return values: `"allow"` (proceed) · `"deny"` (block) · `{ decision: "allow" | "deny", reason?: string }`. Only `decision` is used; `reason` is not currently included in the rejection error.
369
422
 
370
- Custom handler return values: `"allow"` (proceed) · `"deny"` (block) · `{ decision: "allow" | "deny", reason?: string }` (with an explanation added to the rejection error).
423
+ A custom async `approvalPolicy` can integrate your own approval workflow. For an example combining interactive approval through the OpenAI `shouldExecute` hook with an automated toolkit policy, see [`examples/openai/hitl.ts`](examples/openai/hitl.ts). In that example, the hook prompts before writes and returns a declined result to the model if the user refuses. The toolkit policy separately denies high-risk operations in production, even if the user approved them.
371
424
 
372
425
  ### Context
373
426
 
@@ -495,6 +548,7 @@ Working examples are available in the source repository:
495
548
  | `examples/openai/advanced.ts` | Observability, hooks, async jobs, discovery |
496
549
  | `examples/openai/hitl.ts` | Human-in-the-loop (HITL) approval |
497
550
  | `examples/openai-agents/index.ts` | OpenAI Agents SDK agent |
498
- | `examples/openai-agents-cli/` | Interactive CLI agent + planner |
551
+ | `examples/openai-agents-cli/` | Interactive CLI agent + planner + dual-layer approval (HITL + toolkit) |
552
+ | `examples/langchain-agents-cli/` | Interactive LangChain CLI agent + planner + dual-layer approval (HITL + toolkit) |
499
553
  | `examples/langchain/index.ts` | LangChain ReactAgent with Zuora tools |
500
554
  | `examples/vercel-ai/index.ts` | Vercel AI SDK `streamText` agent |
@@ -4175,8 +4175,17 @@ var createHttpMcpClientConfig = (config) => {
4175
4175
  };
4176
4176
  var isTenantAuthConnection = (connection) => connection?.tenantAuth?.enabled === true;
4177
4177
  var filterCatalogTools = (tools, policy) => tools.filter((tool2) => {
4178
- if (policy.mode === SafetyMode.ReadOnly && tool2.meta.access === "write") {
4179
- return false;
4178
+ if (policy.mode === SafetyMode.ReadOnly) {
4179
+ if (tool2.meta.operationAccess) {
4180
+ const hasReadOperation = Object.values(tool2.meta.operationAccess).some(
4181
+ (access) => access === "read"
4182
+ );
4183
+ if (!hasReadOperation) {
4184
+ return false;
4185
+ }
4186
+ } else if (tool2.meta.access === "write") {
4187
+ return false;
4188
+ }
4180
4189
  }
4181
4190
  if (policy.bundles.size > 0 && !policy.bundles.has(tool2.meta.bundle)) {
4182
4191
  return false;
@@ -4338,18 +4347,18 @@ var ToolkitCore = class {
4338
4347
  });
4339
4348
  }
4340
4349
  const mode = this.config.configuration?.mode ?? SafetyMode.ReadOnly;
4341
- if (mode === SafetyMode.ReadOnly && tool2.meta.access === "write") {
4342
- throw new ZuoraAgentToolkitError({
4343
- code: "TOOL_EXECUTION_ERROR",
4344
- message: `Write tool "${operationName}" is disabled in readOnly mode.`,
4345
- category: "tool_execution"
4346
- });
4347
- }
4348
4350
  const nativeRuntimeTool = this.nativeToolRegistry?.get(operationName);
4349
4351
  const nativeApprovalMetadata = nativeRuntimeTool?.approvalMetadata(input);
4350
4352
  const invokedOperation = tool2.meta.operationSelector ? String(input[tool2.meta.operationSelector] ?? "") : void 0;
4351
4353
  const effectiveAccess = (invokedOperation ? tool2.meta.operationAccess?.[invokedOperation] : void 0) ?? tool2.meta.access;
4352
4354
  const effectiveRiskLevel = (invokedOperation ? tool2.meta.operationRiskLevel?.[invokedOperation] : void 0) ?? tool2.meta.riskLevel;
4355
+ if (mode === SafetyMode.ReadOnly && effectiveAccess === "write") {
4356
+ throw new ZuoraAgentToolkitError({
4357
+ code: "TOOL_EXECUTION_ERROR",
4358
+ message: `Write operation "${invokedOperation ?? operationName}" is disabled in readOnly mode.`,
4359
+ category: "tool_execution"
4360
+ });
4361
+ }
4353
4362
  const effectiveTool = effectiveAccess === tool2.meta.access && effectiveRiskLevel === tool2.meta.riskLevel ? tool2 : { ...tool2, meta: { ...tool2.meta, access: effectiveAccess, riskLevel: effectiveRiskLevel } };
4354
4363
  await enforceApproval(
4355
4364
  effectiveTool,