@voyant-travel/max-sdk 0.1.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.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @voyant-travel/max-sdk
2
+
3
+ Write **custom tools** for Max, Voyant's AI travel agent.
4
+
5
+ A custom tool lets Max call _your_ backend — look up a booking in your PMS, price
6
+ a quote, trigger a workflow — as part of a conversation. You:
7
+
8
+ 1. **Define** tools with a [Zod](https://zod.dev) schema for their arguments.
9
+ 2. **Serve** them from an HTTP endpoint with the included request handler.
10
+ 3. **Register** the generated manifest with Voyant.
11
+
12
+ Max validates each call against your schema, asks for confirmation on
13
+ `destructive` tools, and renders results — optionally as rich **cards**
14
+ (typed in this package; see below).
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @voyant-travel/max-sdk zod
20
+ ```
21
+
22
+ ## 1. Define tools
23
+
24
+ ```ts
25
+ import { defineTool } from "@voyant-travel/max-sdk"
26
+ import { z } from "zod"
27
+
28
+ export const lookupBooking = defineTool({
29
+ name: "acme_lookup_booking",
30
+ description: "Look up a booking by its reference.",
31
+ tier: "read", // "read" | "routine-write" | "destructive"
32
+ input: z.object({
33
+ reference: z.string().describe("Booking reference, e.g. AC-1234"),
34
+ }),
35
+ handler: async ({ reference }, ctx) => {
36
+ // ctx = { toolName, operatorId, organizationId, userId } — all from Voyant
37
+ const booking = await db.bookings.find(reference, ctx.organizationId)
38
+ return booking ?? { notFound: true }
39
+ },
40
+ })
41
+ ```
42
+
43
+ Tool names must be unique and use only letters, digits, `_` or `-`. Prefix them
44
+ with your operator handle (e.g. `acme_`).
45
+
46
+ ## 2. Serve them
47
+
48
+ `createMaxToolsHandler` returns a standard Web `fetch` handler — it runs anywhere
49
+ that speaks `Request`/`Response` (Cloudflare Workers, Hono, Next.js route
50
+ handlers, Deno, Bun). Mount it so it receives `POST /v1/max/tools/:name/call`.
51
+
52
+ ```ts
53
+ import { createMaxToolsHandler } from "@voyant-travel/max-sdk"
54
+
55
+ const handler = createMaxToolsHandler([lookupBooking /*, ...*/], {
56
+ authToken: process.env.MAX_TOOLS_SECRET, // Voyant presents this as a Bearer token
57
+ })
58
+
59
+ // Cloudflare Worker
60
+ export default { fetch: handler }
61
+ ```
62
+
63
+ It authenticates the request, validates `args` against the tool's schema (422 on
64
+ mismatch), runs your handler, and returns the result as JSON.
65
+
66
+ ## 3. Register the manifest
67
+
68
+ ```ts
69
+ import { toManifest } from "@voyant-travel/max-sdk"
70
+
71
+ const manifest = toManifest([lookupBooking], {
72
+ callBaseUrl: "https://acme.example.com",
73
+ })
74
+ // Register `manifest` with Voyant for your operator. Each tool's Zod schema is
75
+ // emitted as JSON Schema so the model knows how to call it.
76
+ ```
77
+
78
+ ## Rich results (cards)
79
+
80
+ Cards are **optional** — return plain JSON and Max renders it. When you want to
81
+ control the widget, return a `card` alongside your data. The card types ship with
82
+ this package:
83
+
84
+ ```ts
85
+ import { defineTool, type WithCard } from "@voyant-travel/max-sdk"
86
+
87
+ handler: async ({ reference }, ctx): Promise<WithCard> => {
88
+ const booking = await db.bookings.find(reference, ctx.organizationId)
89
+ return {
90
+ ...booking,
91
+ card: { kind: "booking", reference: booking.reference, status: booking.status /* … */ },
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## File results
97
+
98
+ For generated PDFs/exports, set `outputKind: "file"` and return `file(...)`:
99
+
100
+ ```ts
101
+ import { defineTool, file } from "@voyant-travel/max-sdk"
102
+
103
+ export const exportInvoice = defineTool({
104
+ name: "acme_export_invoice",
105
+ description: "Export an invoice as a PDF.",
106
+ tier: "read",
107
+ outputKind: "file",
108
+ input: z.object({ invoiceId: z.string() }),
109
+ handler: async ({ invoiceId }) => {
110
+ const url = await renderInvoicePdf(invoiceId)
111
+ return file({
112
+ label: "Invoice",
113
+ filename: `${invoiceId}.pdf`,
114
+ mediaType: "application/pdf",
115
+ downloadUrl: url,
116
+ })
117
+ },
118
+ })
119
+ ```
120
+
121
+ ## Tiers
122
+
123
+ | Tier | Behaviour |
124
+ | --------------- | --------------------------------------------------------------- |
125
+ | `read` | Runs automatically. |
126
+ | `routine-write` | Runs with light guarding. |
127
+ | `destructive` | Requires explicit user approval before it executes. |
128
+
129
+ ## License
130
+
131
+ Apache-2.0