@paybond/kit 0.7.1 → 0.9.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 +45 -2
- package/dist/index.d.ts +79 -0
- package/dist/index.js +114 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +174 -0
- package/dist/mcp-server.js +62 -3
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@paybond/kit`
|
|
2
2
|
|
|
3
|
-
Paybond Kit for TypeScript is the npm package for tenant-bound Paybond integrations. It opens hosted Gateway sessions, verifies capability tokens, signs intent and evidence payloads, uses Stripe Connect or x402 / USDC-on-Base settlement rails,
|
|
3
|
+
Paybond Kit for TypeScript is the npm package for tenant-bound Paybond integrations and delegated agent spend controls. It opens hosted Gateway sessions, verifies capability tokens, authorizes tool-call spend, signs intent and evidence payloads, uses Stripe Connect or x402 / USDC-on-Base settlement rails, reads tenant-scoped Signal, fraud, ledger, protocol, and A2A data, and includes agent-runtime integrations.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -59,6 +59,45 @@ try {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
## Agent spend controls
|
|
63
|
+
|
|
64
|
+
Use Paybond Kit when an agent workflow needs delegated spend guardrails, tool-call budget checks, paid API or vendor action approval, evidence, release/refund logic, disputes, or audit-ready receipts.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { Paybond } from "@paybond/kit";
|
|
68
|
+
|
|
69
|
+
const paybond = await Paybond.open({
|
|
70
|
+
apiKey: process.env.PAYBOND_API_KEY!,
|
|
71
|
+
expectedEnvironment: "sandbox",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const created = await paybond.intents.create({
|
|
75
|
+
// principal, payee, budget, predicate, evidence schema, deadline...
|
|
76
|
+
allowedTools: ["travel.book_hotel"],
|
|
77
|
+
settlementRail: "stripe_connect",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const intentId = String(created.intent_id);
|
|
81
|
+
const capabilityToken = String(created.capability_token ?? "");
|
|
82
|
+
if (!capabilityToken) {
|
|
83
|
+
throw new Error("fund the intent before guarding tools");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const guard = paybond.spendGuard(intentId, capabilityToken);
|
|
87
|
+
const guardedTool = guard.guardTool(
|
|
88
|
+
{ operation: "travel.book_hotel", requestedSpendCents: 20_000 },
|
|
89
|
+
async (input) => bookHotel(input),
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The `paybond.harbor` client is created by `Paybond.open(...)` and bound to the tenant resolved from the service-account API key. Normal integrations read `capability_token` from `paybond.intents.create(...)`, or from `paybond.intents.fund(...)` after an `x402_usdc_base` payment challenge is satisfied.
|
|
94
|
+
|
|
95
|
+
Scaffold a wrapper:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx -p @paybond/kit paybond-init --framework provider-agnostic --out paybond-spend-guard.ts
|
|
99
|
+
```
|
|
100
|
+
|
|
62
101
|
## What the package includes
|
|
63
102
|
|
|
64
103
|
Core SDK:
|
|
@@ -67,6 +106,9 @@ Core SDK:
|
|
|
67
106
|
- `HarborClient` for capability verification, intent creation, x402 funding, evidence submission, and ledger reads
|
|
68
107
|
- `paybond.signal` and `paybond.fraud` on `Paybond` sessions opened from one service-account API key
|
|
69
108
|
- `PaybondIntents` helpers for principal-signed intent creation, x402 funding, and payee-signed evidence submission
|
|
109
|
+
- `PaybondSpendGuard`, `authorizeSpend`, and `guardTool` for spend-named wrappers around capability verification
|
|
110
|
+
- Runtime-neutral and framework aliases: `paybondAgentToolSpendGuard`, `paybondRuntimeNeutralToolSpendGuard`, `paybondLangGraphToolSpendGuard`, and `paybondMCPToolSpendGuard`
|
|
111
|
+
- `paybondRuntimeToolCallAdapter` for agent SDKs and custom runtimes that expose a tool-call object plus an application-owned executor
|
|
70
112
|
|
|
71
113
|
Gateway and trust helpers:
|
|
72
114
|
|
|
@@ -74,6 +116,7 @@ Gateway and trust helpers:
|
|
|
74
116
|
- `GatewayFraudClient` and `ServiceAccountFraudSession` for tenant-scoped fraud assessments, review queues, review events, metrics, and release-gate config
|
|
75
117
|
- Protocol-v2 helpers for mandate verification, replay-safe recognition proof verification, receipt reads, and A2A discovery
|
|
76
118
|
- `paybond-mcp-server` for tenant-bound MCP tool exposure to any MCP-compatible host
|
|
119
|
+
- `paybond-init` for generating a small spend guard wrapper
|
|
77
120
|
|
|
78
121
|
Agent-facing surfaces are model-provider agnostic. Paybond verifies tool operations and tenant scope, not whether a tool call came from OpenAI, Anthropic, Gemini, a local model, or another runtime.
|
|
79
122
|
|
|
@@ -92,7 +135,7 @@ Gateway-backed protocol helpers throw `ProtocolHttpError` with parsed `errorCode
|
|
|
92
135
|
## What it does not include
|
|
93
136
|
|
|
94
137
|
- No operator-tier settlement or console workflows
|
|
95
|
-
- No model-provider-specific TypeScript agent wrapper; use the documented app-side wrapper pattern with `
|
|
138
|
+
- No model-provider-specific TypeScript agent wrapper; use the documented app-side wrapper pattern with `paybond.spendGuard(...)`
|
|
96
139
|
- No model-provider-specific MCP wrapper; the MCP server is host-agnostic and works with any MCP-compatible runtime
|
|
97
140
|
|
|
98
141
|
## Docs
|
package/dist/index.d.ts
CHANGED
|
@@ -652,7 +652,52 @@ export declare class PaybondCapabilityBinding {
|
|
|
652
652
|
readonly intentId: string;
|
|
653
653
|
readonly capabilityToken: string;
|
|
654
654
|
constructor(harbor: Pick<HarborClient | GatewayHarborClient, "tenantId" | "verifyCapability">, intentId: string, capabilityToken: string);
|
|
655
|
+
verifySpendCapability(input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
656
|
+
authorizeSpend(input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
655
657
|
}
|
|
658
|
+
export type PaybondSpendGuardInit = {
|
|
659
|
+
harbor: Pick<HarborClient | GatewayHarborClient, "tenantId" | "verifyCapability">;
|
|
660
|
+
intentId: string;
|
|
661
|
+
capabilityToken: string;
|
|
662
|
+
};
|
|
663
|
+
export type PaybondSpendAuthorizationInput = {
|
|
664
|
+
operation: string;
|
|
665
|
+
requestedSpendCents?: number;
|
|
666
|
+
};
|
|
667
|
+
export declare class PaybondSpendDeniedError extends Error {
|
|
668
|
+
readonly result: VerifyCapabilityResult;
|
|
669
|
+
constructor(result: VerifyCapabilityResult);
|
|
670
|
+
}
|
|
671
|
+
export type PaybondToolHandler<TArgs extends unknown[], TResult> = (...args: TArgs) => TResult | Promise<TResult>;
|
|
672
|
+
export type PaybondGuardedToolHandler<TArgs extends unknown[], TResult> = (...args: TArgs) => Promise<Awaited<TResult>>;
|
|
673
|
+
export declare class PaybondSpendGuard {
|
|
674
|
+
readonly harbor: Pick<HarborClient | GatewayHarborClient, "tenantId" | "verifyCapability">;
|
|
675
|
+
readonly intentId: string;
|
|
676
|
+
readonly capabilityToken: string;
|
|
677
|
+
constructor(init: PaybondSpendGuardInit | PaybondCapabilityBinding);
|
|
678
|
+
verifySpendCapability(input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
679
|
+
authorizeSpend(input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
680
|
+
assertSpendAuthorized(input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
681
|
+
guardTool<TArgs extends unknown[], TResult>(input: PaybondSpendAuthorizationInput, handler: PaybondToolHandler<TArgs, TResult>): PaybondGuardedToolHandler<TArgs, TResult>;
|
|
682
|
+
}
|
|
683
|
+
export declare function authorizeSpend(source: PaybondSpendGuardInit | PaybondCapabilityBinding, input: PaybondSpendAuthorizationInput): Promise<VerifyCapabilityResult>;
|
|
684
|
+
export declare function guardTool<TArgs extends unknown[], TResult>(source: PaybondSpendGuardInit | PaybondCapabilityBinding, input: PaybondSpendAuthorizationInput, handler: PaybondToolHandler<TArgs, TResult>): PaybondGuardedToolHandler<TArgs, TResult>;
|
|
685
|
+
export declare const paybondAgentToolSpendGuard: typeof guardTool;
|
|
686
|
+
export declare const paybondRuntimeNeutralToolSpendGuard: typeof guardTool;
|
|
687
|
+
export declare const paybondLangGraphToolSpendGuard: typeof guardTool;
|
|
688
|
+
export declare const paybondMCPToolSpendGuard: typeof guardTool;
|
|
689
|
+
export type PaybondRuntimeOperation<TCall> = string | ((call: TCall) => string);
|
|
690
|
+
export type PaybondRuntimeSpendCents<TCall> = number | ((call: TCall) => number | undefined);
|
|
691
|
+
export type PaybondRuntimeToolExecutor<TCall, TResult> = (call: TCall) => TResult | Promise<TResult>;
|
|
692
|
+
export type PaybondRuntimeDenyHandler<TCall, TResult> = (result: VerifyCapabilityResult, call: TCall) => TResult | Promise<TResult>;
|
|
693
|
+
export type PaybondRuntimeToolCallAdapterInit<TCall, TResult> = {
|
|
694
|
+
source: PaybondSpendGuardInit | PaybondCapabilityBinding;
|
|
695
|
+
operation: PaybondRuntimeOperation<TCall>;
|
|
696
|
+
execute: PaybondRuntimeToolExecutor<TCall, TResult>;
|
|
697
|
+
requestedSpendCents?: PaybondRuntimeSpendCents<TCall>;
|
|
698
|
+
onDeny?: PaybondRuntimeDenyHandler<TCall, TResult>;
|
|
699
|
+
};
|
|
700
|
+
export declare function paybondRuntimeToolCallAdapter<TCall, TResult>(init: PaybondRuntimeToolCallAdapterInit<TCall, TResult>): (call: TCall) => Promise<Awaited<TResult>>;
|
|
656
701
|
export type PaybondOpenOptions = {
|
|
657
702
|
apiKey: string;
|
|
658
703
|
gatewayBaseUrl?: string;
|
|
@@ -701,6 +746,18 @@ export declare class HarborClient {
|
|
|
701
746
|
operation: string;
|
|
702
747
|
requestedSpendCents?: number;
|
|
703
748
|
}): Promise<VerifyCapabilityResult>;
|
|
749
|
+
verifySpendCapability(input: {
|
|
750
|
+
intentId: string;
|
|
751
|
+
token: string;
|
|
752
|
+
operation: string;
|
|
753
|
+
requestedSpendCents?: number;
|
|
754
|
+
}): Promise<VerifyCapabilityResult>;
|
|
755
|
+
authorizeSpend(input: {
|
|
756
|
+
intentId: string;
|
|
757
|
+
token: string;
|
|
758
|
+
operation: string;
|
|
759
|
+
requestedSpendCents?: number;
|
|
760
|
+
}): Promise<VerifyCapabilityResult>;
|
|
704
761
|
/**
|
|
705
762
|
* POST `/intents` with a principal-signed `CreateIntentRequest` JSON body.
|
|
706
763
|
*
|
|
@@ -787,6 +844,18 @@ export declare class GatewayHarborClient {
|
|
|
787
844
|
operation: string;
|
|
788
845
|
requestedSpendCents?: number;
|
|
789
846
|
}): Promise<VerifyCapabilityResult>;
|
|
847
|
+
verifySpendCapability(input: {
|
|
848
|
+
intentId: string;
|
|
849
|
+
token: string;
|
|
850
|
+
operation: string;
|
|
851
|
+
requestedSpendCents?: number;
|
|
852
|
+
}): Promise<VerifyCapabilityResult>;
|
|
853
|
+
authorizeSpend(input: {
|
|
854
|
+
intentId: string;
|
|
855
|
+
token: string;
|
|
856
|
+
operation: string;
|
|
857
|
+
requestedSpendCents?: number;
|
|
858
|
+
}): Promise<VerifyCapabilityResult>;
|
|
790
859
|
createIntent(body: Record<string, unknown>, options: GatewayHarborMutationOptions): Promise<Record<string, unknown>>;
|
|
791
860
|
fundIntent(intentId: string, options: GatewayHarborMutationOptions & {
|
|
792
861
|
paymentSignature?: string;
|
|
@@ -962,6 +1031,9 @@ export declare class PaybondIntents {
|
|
|
962
1031
|
create(params: PaybondCreateIntentParams & {
|
|
963
1032
|
idempotencyKey?: string;
|
|
964
1033
|
}): Promise<Record<string, unknown>>;
|
|
1034
|
+
createSpendIntent(params: PaybondCreateIntentParams & {
|
|
1035
|
+
idempotencyKey?: string;
|
|
1036
|
+
}): Promise<Record<string, unknown>>;
|
|
965
1037
|
/**
|
|
966
1038
|
* Advance Harbor `/intents/{id}/fund` for x402 / USDC-on-Base intents.
|
|
967
1039
|
*/
|
|
@@ -993,6 +1065,13 @@ export declare class Paybond {
|
|
|
993
1065
|
static open(init: PaybondOpenOptions): Promise<Paybond>;
|
|
994
1066
|
/** Reserved for future HTTP client cleanup; safe to call after work completes. */
|
|
995
1067
|
aclose(): Promise<void>;
|
|
1068
|
+
spendGuard(intentId: string, capabilityToken: string): PaybondSpendGuard;
|
|
1069
|
+
authorizeSpend(input: {
|
|
1070
|
+
intentId: string;
|
|
1071
|
+
token: string;
|
|
1072
|
+
operation: string;
|
|
1073
|
+
requestedSpendCents?: number;
|
|
1074
|
+
}): Promise<VerifyCapabilityResult>;
|
|
996
1075
|
}
|
|
997
1076
|
export { normalizeJson, jsonValueDigest } from "./json-digest.js";
|
|
998
1077
|
export { buildSignedCreateIntentBody, intentCreationSignBytesRaw, type BuildSignedCreateIntentParams, type SettlementRail, } from "./principal-intent.js";
|
package/dist/index.js
CHANGED
|
@@ -156,6 +156,99 @@ export class PaybondCapabilityBinding {
|
|
|
156
156
|
this.intentId = intentId;
|
|
157
157
|
this.capabilityToken = capabilityToken;
|
|
158
158
|
}
|
|
159
|
+
async verifySpendCapability(input) {
|
|
160
|
+
return this.harbor.verifyCapability({
|
|
161
|
+
intentId: this.intentId,
|
|
162
|
+
token: this.capabilityToken,
|
|
163
|
+
operation: input.operation,
|
|
164
|
+
requestedSpendCents: input.requestedSpendCents,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async authorizeSpend(input) {
|
|
168
|
+
return this.verifySpendCapability(input);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export class PaybondSpendDeniedError extends Error {
|
|
172
|
+
result;
|
|
173
|
+
constructor(result) {
|
|
174
|
+
const reason = result.message ?? result.code ?? "denied";
|
|
175
|
+
super(`Paybond spend authorization denied: ${reason}`);
|
|
176
|
+
this.name = "PaybondSpendDeniedError";
|
|
177
|
+
this.result = result;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export class PaybondSpendGuard {
|
|
181
|
+
harbor;
|
|
182
|
+
intentId;
|
|
183
|
+
capabilityToken;
|
|
184
|
+
constructor(init) {
|
|
185
|
+
this.harbor = init.harbor;
|
|
186
|
+
this.intentId = init.intentId;
|
|
187
|
+
this.capabilityToken = init.capabilityToken;
|
|
188
|
+
}
|
|
189
|
+
async verifySpendCapability(input) {
|
|
190
|
+
return this.harbor.verifyCapability({
|
|
191
|
+
intentId: this.intentId,
|
|
192
|
+
token: this.capabilityToken,
|
|
193
|
+
operation: input.operation,
|
|
194
|
+
requestedSpendCents: input.requestedSpendCents,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async authorizeSpend(input) {
|
|
198
|
+
return this.verifySpendCapability(input);
|
|
199
|
+
}
|
|
200
|
+
async assertSpendAuthorized(input) {
|
|
201
|
+
const result = await this.authorizeSpend(input);
|
|
202
|
+
if (!result.allow) {
|
|
203
|
+
throw new PaybondSpendDeniedError(result);
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
guardTool(input, handler) {
|
|
208
|
+
return async (...args) => {
|
|
209
|
+
await this.assertSpendAuthorized(input);
|
|
210
|
+
return await handler(...args);
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export async function authorizeSpend(source, input) {
|
|
215
|
+
return new PaybondSpendGuard(source).authorizeSpend(input);
|
|
216
|
+
}
|
|
217
|
+
export function guardTool(source, input, handler) {
|
|
218
|
+
return new PaybondSpendGuard(source).guardTool(input, handler);
|
|
219
|
+
}
|
|
220
|
+
export const paybondAgentToolSpendGuard = guardTool;
|
|
221
|
+
export const paybondRuntimeNeutralToolSpendGuard = guardTool;
|
|
222
|
+
export const paybondLangGraphToolSpendGuard = guardTool;
|
|
223
|
+
export const paybondMCPToolSpendGuard = guardTool;
|
|
224
|
+
function resolveRuntimeOperation(operation, call) {
|
|
225
|
+
const value = typeof operation === "function" ? operation(call) : operation;
|
|
226
|
+
const trimmed = String(value).trim();
|
|
227
|
+
if (!trimmed) {
|
|
228
|
+
throw new Error("Paybond operation must be a non-empty string");
|
|
229
|
+
}
|
|
230
|
+
return trimmed;
|
|
231
|
+
}
|
|
232
|
+
function resolveRuntimeSpendCents(requestedSpendCents, call) {
|
|
233
|
+
if (requestedSpendCents === undefined) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
return typeof requestedSpendCents === "function" ? requestedSpendCents(call) : requestedSpendCents;
|
|
237
|
+
}
|
|
238
|
+
export function paybondRuntimeToolCallAdapter(init) {
|
|
239
|
+
const guard = new PaybondSpendGuard(init.source);
|
|
240
|
+
return async (call) => {
|
|
241
|
+
const operation = resolveRuntimeOperation(init.operation, call);
|
|
242
|
+
const requestedSpendCents = resolveRuntimeSpendCents(init.requestedSpendCents, call);
|
|
243
|
+
const result = await guard.authorizeSpend({ operation, requestedSpendCents });
|
|
244
|
+
if (!result.allow) {
|
|
245
|
+
if (init.onDeny) {
|
|
246
|
+
return await init.onDeny(result, call);
|
|
247
|
+
}
|
|
248
|
+
throw new PaybondSpendDeniedError(result);
|
|
249
|
+
}
|
|
250
|
+
return await init.execute(call);
|
|
251
|
+
};
|
|
159
252
|
}
|
|
160
253
|
/**
|
|
161
254
|
* HTTP client for Harbor: capability verify, intents, evidence, and tenant-scoped ledger reads
|
|
@@ -329,6 +422,12 @@ export class HarborClient {
|
|
|
329
422
|
message: body.message,
|
|
330
423
|
};
|
|
331
424
|
}
|
|
425
|
+
async verifySpendCapability(input) {
|
|
426
|
+
return this.verifyCapability(input);
|
|
427
|
+
}
|
|
428
|
+
async authorizeSpend(input) {
|
|
429
|
+
return this.verifyCapability(input);
|
|
430
|
+
}
|
|
332
431
|
/**
|
|
333
432
|
* POST `/intents` with a principal-signed `CreateIntentRequest` JSON body.
|
|
334
433
|
*
|
|
@@ -653,6 +752,12 @@ export class GatewayHarborClient {
|
|
|
653
752
|
message: body.message,
|
|
654
753
|
};
|
|
655
754
|
}
|
|
755
|
+
async verifySpendCapability(input) {
|
|
756
|
+
return this.verifyCapability(input);
|
|
757
|
+
}
|
|
758
|
+
async authorizeSpend(input) {
|
|
759
|
+
return this.verifyCapability(input);
|
|
760
|
+
}
|
|
656
761
|
async createIntent(body, options) {
|
|
657
762
|
const { res, text, url } = await this.postJSON("/harbor/intents", body, this.mutationHeaders("createIntent", options));
|
|
658
763
|
if (!res.ok) {
|
|
@@ -1642,6 +1747,9 @@ export class PaybondIntents {
|
|
|
1642
1747
|
});
|
|
1643
1748
|
return this.harbor.createIntent(body, { idempotencyKey, recognitionProof });
|
|
1644
1749
|
}
|
|
1750
|
+
async createSpendIntent(params) {
|
|
1751
|
+
return this.create(params);
|
|
1752
|
+
}
|
|
1645
1753
|
/**
|
|
1646
1754
|
* Advance Harbor `/intents/{id}/fund` for x402 / USDC-on-Base intents.
|
|
1647
1755
|
*/
|
|
@@ -1715,6 +1823,12 @@ export class Paybond {
|
|
|
1715
1823
|
async aclose() {
|
|
1716
1824
|
await Promise.resolve();
|
|
1717
1825
|
}
|
|
1826
|
+
spendGuard(intentId, capabilityToken) {
|
|
1827
|
+
return new PaybondSpendGuard({ harbor: this.harbor, intentId, capabilityToken });
|
|
1828
|
+
}
|
|
1829
|
+
async authorizeSpend(input) {
|
|
1830
|
+
return this.harbor.authorizeSpend(input);
|
|
1831
|
+
}
|
|
1718
1832
|
}
|
|
1719
1833
|
export { normalizeJson, jsonValueDigest } from "./json-digest.js";
|
|
1720
1834
|
export { buildSignedCreateIntentBody, intentCreationSignBytesRaw, } from "./principal-intent.js";
|
package/dist/init.d.ts
ADDED
package/dist/init.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const FRAMEWORKS = new Set([
|
|
3
|
+
"generic",
|
|
4
|
+
"provider-agnostic",
|
|
5
|
+
"openai",
|
|
6
|
+
"claude",
|
|
7
|
+
"anthropic",
|
|
8
|
+
"gemini",
|
|
9
|
+
"google-ai",
|
|
10
|
+
"vercel-ai",
|
|
11
|
+
"langgraph",
|
|
12
|
+
"mcp",
|
|
13
|
+
]);
|
|
14
|
+
const FRAMEWORK_NOTES = {
|
|
15
|
+
generic: "Wrap the returned function around any side-effecting tool handler.",
|
|
16
|
+
"provider-agnostic": "Use the guarded handler with OpenAI, Gemini, Claude/Anthropic, local models, or any custom runtime.",
|
|
17
|
+
openai: "Call the guarded handler before the OpenAI tool call performs paid or external work.",
|
|
18
|
+
claude: "Call the guarded handler before the Claude tool-use action performs paid or external work.",
|
|
19
|
+
anthropic: "Call the guarded handler before the Anthropic tool-use action performs paid or external work.",
|
|
20
|
+
gemini: "Call the guarded handler before the Gemini function call performs paid or external work.",
|
|
21
|
+
"google-ai": "Call the guarded handler before the Google AI function call performs paid or external work.",
|
|
22
|
+
"vercel-ai": "Call the guarded handler from your Vercel AI SDK tool execute function.",
|
|
23
|
+
langgraph: "Call the guarded handler from the LangGraph JS node or tool wrapper that performs paid work.",
|
|
24
|
+
mcp: "Call the guarded handler inside the MCP tool implementation before paid or external work runs.",
|
|
25
|
+
};
|
|
26
|
+
function usage() {
|
|
27
|
+
return [
|
|
28
|
+
"Usage: paybond-init [--framework generic|provider-agnostic|openai|claude|anthropic|gemini|google-ai|vercel-ai|langgraph|mcp] [--out paybond-spend-guard.ts] [--force]",
|
|
29
|
+
"",
|
|
30
|
+
"Scaffolds a Paybond spend guard wrapper for delegated agent spend controls.",
|
|
31
|
+
].join("\n");
|
|
32
|
+
}
|
|
33
|
+
function parseArgs(argv) {
|
|
34
|
+
let framework = "generic";
|
|
35
|
+
let out = "paybond-spend-guard.ts";
|
|
36
|
+
let force = false;
|
|
37
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
38
|
+
const arg = argv[i];
|
|
39
|
+
if (arg === "--help" || arg === "-h") {
|
|
40
|
+
process.stdout.write(`${usage()}\n`);
|
|
41
|
+
process.exitCode = 0;
|
|
42
|
+
return { framework, out, force };
|
|
43
|
+
}
|
|
44
|
+
if (arg === "--force") {
|
|
45
|
+
force = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (arg === "--framework") {
|
|
49
|
+
const raw = argv[i + 1];
|
|
50
|
+
i += 1;
|
|
51
|
+
if (!raw || !FRAMEWORKS.has(raw)) {
|
|
52
|
+
throw new Error("invalid --framework");
|
|
53
|
+
}
|
|
54
|
+
framework = raw;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (arg === "--out") {
|
|
58
|
+
const raw = argv[i + 1];
|
|
59
|
+
i += 1;
|
|
60
|
+
if (!raw || raw.startsWith("-")) {
|
|
61
|
+
throw new Error("invalid --out");
|
|
62
|
+
}
|
|
63
|
+
out = raw;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
67
|
+
}
|
|
68
|
+
return { framework, out, force };
|
|
69
|
+
}
|
|
70
|
+
function template(framework) {
|
|
71
|
+
return `import { Paybond, type FundIntentResult } from "@paybond/kit";
|
|
72
|
+
|
|
73
|
+
type ToolInput = {
|
|
74
|
+
city: string;
|
|
75
|
+
maxPriceCents: number;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type FundedIntent = {
|
|
79
|
+
intentId: string;
|
|
80
|
+
capabilityToken: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export async function openPaybond(): Promise<Paybond> {
|
|
84
|
+
return Paybond.open({
|
|
85
|
+
apiKey: process.env.PAYBOND_API_KEY!,
|
|
86
|
+
expectedEnvironment: "sandbox",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function fundedIntentFromCreate(created: Record<string, unknown>): FundedIntent {
|
|
91
|
+
const intentId = String(created["intent_id"] ?? "");
|
|
92
|
+
const capabilityToken = String(created["capability_token"] ?? "");
|
|
93
|
+
if (!intentId) {
|
|
94
|
+
throw new Error("intent create response missing intent_id");
|
|
95
|
+
}
|
|
96
|
+
if (!capabilityToken) {
|
|
97
|
+
throw new Error("intent is not funded yet; call paybond.intents.fund(...) before guarding tools");
|
|
98
|
+
}
|
|
99
|
+
return { intentId, capabilityToken };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function fundedIntentFromFund(funded: FundIntentResult): FundedIntent {
|
|
103
|
+
if (!funded.capabilityToken) {
|
|
104
|
+
throw new Error("intent funding did not return a capabilityToken yet");
|
|
105
|
+
}
|
|
106
|
+
return { intentId: funded.intentId, capabilityToken: funded.capabilityToken };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function bookHotel(input: ToolInput): Promise<{ confirmation: string }> {
|
|
110
|
+
// Put the side-effecting tool call here.
|
|
111
|
+
return { confirmation: \`demo-\${input.city}-\${input.maxPriceCents}\` };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function buildGuardedHotelTool(params: {
|
|
115
|
+
paybond: Paybond;
|
|
116
|
+
intentId: string;
|
|
117
|
+
capabilityToken: string;
|
|
118
|
+
}): Promise<(input: ToolInput) => Promise<{ confirmation: string }>> {
|
|
119
|
+
if (!params.capabilityToken.trim()) {
|
|
120
|
+
throw new Error("fund the intent before guarding tools");
|
|
121
|
+
}
|
|
122
|
+
const guard = params.paybond.spendGuard(params.intentId, params.capabilityToken);
|
|
123
|
+
|
|
124
|
+
// ${FRAMEWORK_NOTES[framework]}
|
|
125
|
+
return guard.guardTool(
|
|
126
|
+
{
|
|
127
|
+
operation: "travel.book_hotel",
|
|
128
|
+
requestedSpendCents: 20_000,
|
|
129
|
+
},
|
|
130
|
+
bookHotel,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
async function writeScaffold(out, body, force) {
|
|
136
|
+
// @ts-expect-error Node builtins are available in the published CLI runtime.
|
|
137
|
+
const fs = await import("node:fs/promises");
|
|
138
|
+
try {
|
|
139
|
+
await fs.stat(out);
|
|
140
|
+
if (!force) {
|
|
141
|
+
throw new Error(`${out} already exists; pass --force to overwrite`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
if (!(err && typeof err === "object" && "code" in err && err.code === "ENOENT")) {
|
|
146
|
+
if (!force) {
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
await fs.writeFile(out, body, "utf8");
|
|
152
|
+
}
|
|
153
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
154
|
+
let parsed;
|
|
155
|
+
try {
|
|
156
|
+
parsed = parseArgs(argv);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n\n${usage()}\n`);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
await writeScaffold(parsed.out, template(parsed.framework), parsed.force);
|
|
166
|
+
process.stdout.write(`Created ${parsed.out}\n`);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
main().then((code) => {
|
|
170
|
+
process.exitCode = code;
|
|
171
|
+
}, (err) => {
|
|
172
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
});
|
package/dist/mcp-server.js
CHANGED
|
@@ -364,6 +364,8 @@ export class PaybondMCPServer {
|
|
|
364
364
|
version: SERVER_VERSION,
|
|
365
365
|
},
|
|
366
366
|
instructions: "This MCP server is tenant-bound to the configured Paybond service-account API key. " +
|
|
367
|
+
"Use paybond_create_spend_intent or paybond_fund_intent to obtain the intent_id and " +
|
|
368
|
+
"capability_token, then call paybond_authorize_agent_spend before side-effecting tools. " +
|
|
367
369
|
"It works with any MCP-compatible host and does not assume a specific model provider.",
|
|
368
370
|
},
|
|
369
371
|
};
|
|
@@ -447,10 +449,37 @@ export class PaybondMCPServer {
|
|
|
447
449
|
},
|
|
448
450
|
{
|
|
449
451
|
name: "paybond_verify_capability",
|
|
450
|
-
description: "Verify a capability token for one tenant-bound Harbor intent
|
|
452
|
+
description: "Verify a capability token returned by a created or funded Paybond intent for one tenant-bound Harbor intent.",
|
|
451
453
|
inputSchema: objectSchema({
|
|
452
454
|
intent_id: { type: "string", description: "Canonical Harbor intent UUID." },
|
|
453
|
-
token: {
|
|
455
|
+
token: {
|
|
456
|
+
type: "string",
|
|
457
|
+
description: "Capability token returned by paybond_create_spend_intent or paybond_fund_intent.",
|
|
458
|
+
},
|
|
459
|
+
operation: { type: "string", description: "Delegated operation or tool name." },
|
|
460
|
+
requested_spend_cents: {
|
|
461
|
+
type: "integer",
|
|
462
|
+
description: "Optional requested spend in cents for this tool call.",
|
|
463
|
+
},
|
|
464
|
+
}, ["intent_id", "token", "operation"]),
|
|
465
|
+
call: async (args) => this.runtime.verifyCapability({
|
|
466
|
+
intentId: uuidArg(args.intent_id, "intent_id"),
|
|
467
|
+
token: stringArg(args.token, "token"),
|
|
468
|
+
operation: stringArg(args.operation, "operation"),
|
|
469
|
+
requestedSpendCents: args.requested_spend_cents === undefined
|
|
470
|
+
? 0
|
|
471
|
+
: intArg(args.requested_spend_cents, "requested_spend_cents"),
|
|
472
|
+
}),
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: "paybond_authorize_agent_spend",
|
|
476
|
+
description: "Provider-agnostic spend gate: verify the funded intent's capability token before a side-effecting tool, paid API, vendor action, or settlement workflow executes.",
|
|
477
|
+
inputSchema: objectSchema({
|
|
478
|
+
intent_id: { type: "string", description: "Canonical Harbor intent UUID." },
|
|
479
|
+
token: {
|
|
480
|
+
type: "string",
|
|
481
|
+
description: "Capability token returned by paybond_create_spend_intent or paybond_fund_intent.",
|
|
482
|
+
},
|
|
454
483
|
operation: { type: "string", description: "Delegated operation or tool name." },
|
|
455
484
|
requested_spend_cents: {
|
|
456
485
|
type: "integer",
|
|
@@ -630,9 +659,23 @@ export class PaybondMCPServer {
|
|
|
630
659
|
idempotencyKey: optionalString(args.idempotency_key),
|
|
631
660
|
}),
|
|
632
661
|
},
|
|
662
|
+
{
|
|
663
|
+
name: "paybond_create_spend_intent",
|
|
664
|
+
description: "Create a signed Paybond spend intent through the gateway /harbor route. Use this when an agent workflow needs bounded budget, allowed operations, evidence, and settlement review. If the selected rail funds immediately, use the returned intent_id and capability_token with paybond_authorize_agent_spend.",
|
|
665
|
+
inputSchema: objectSchema({
|
|
666
|
+
body: { type: "object", additionalProperties: true },
|
|
667
|
+
recognition_proof: { type: "object", additionalProperties: true },
|
|
668
|
+
idempotency_key: { type: "string" },
|
|
669
|
+
}, ["body", "recognition_proof"]),
|
|
670
|
+
call: async (args) => this.runtime.createHarborIntent({
|
|
671
|
+
body: ensureObject(args.body, "body"),
|
|
672
|
+
recognitionProof: ensureObject(args.recognition_proof, "recognition_proof"),
|
|
673
|
+
idempotencyKey: optionalString(args.idempotency_key),
|
|
674
|
+
}),
|
|
675
|
+
},
|
|
633
676
|
{
|
|
634
677
|
name: "paybond_fund_intent",
|
|
635
|
-
description: "Advance Harbor funding through the gateway /harbor path with a replay-safe recognition proof.",
|
|
678
|
+
description: "Advance Harbor funding through the gateway /harbor path with a replay-safe recognition proof. When funding succeeds, use the returned capability_token with intent_id in paybond_authorize_agent_spend.",
|
|
636
679
|
inputSchema: objectSchema({
|
|
637
680
|
intent_id: { type: "string" },
|
|
638
681
|
recognition_proof: { type: "object", additionalProperties: true },
|
|
@@ -662,6 +705,22 @@ export class PaybondMCPServer {
|
|
|
662
705
|
idempotencyKey: optionalString(args.idempotency_key),
|
|
663
706
|
}),
|
|
664
707
|
},
|
|
708
|
+
{
|
|
709
|
+
name: "paybond_submit_spend_evidence",
|
|
710
|
+
description: "Submit signed evidence for a Paybond spend intent so release, refund, review, and receipt generation use the same audit-ready record.",
|
|
711
|
+
inputSchema: objectSchema({
|
|
712
|
+
intent_id: { type: "string" },
|
|
713
|
+
body: { type: "object", additionalProperties: true },
|
|
714
|
+
recognition_proof: { type: "object", additionalProperties: true },
|
|
715
|
+
idempotency_key: { type: "string" },
|
|
716
|
+
}, ["intent_id", "body", "recognition_proof"]),
|
|
717
|
+
call: async (args) => this.runtime.submitHarborEvidence({
|
|
718
|
+
intentId: uuidArg(args.intent_id, "intent_id"),
|
|
719
|
+
body: ensureObject(args.body, "body"),
|
|
720
|
+
recognitionProof: ensureObject(args.recognition_proof, "recognition_proof"),
|
|
721
|
+
idempotencyKey: optionalString(args.idempotency_key),
|
|
722
|
+
}),
|
|
723
|
+
},
|
|
665
724
|
{
|
|
666
725
|
name: "paybond_confirm_settlement",
|
|
667
726
|
description: "Confirm Harbor settlement through the gateway /harbor path with a replay-safe recognition proof.",
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paybond/kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Paybond Kit for TypeScript: hosted Gateway sessions, capability verification, signed intent/evidence flows, and Stripe Connect or x402 / USDC-on-Base settlement.",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Paybond Kit for TypeScript: agent spend controls, hosted Gateway sessions, capability verification, signed intent/evidence flows, and Stripe Connect or x402 / USDC-on-Base settlement.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"bin": {
|
|
10
|
+
"paybond-init": "dist/init.js",
|
|
10
11
|
"paybond-mcp-server": "dist/mcp-server.js"
|
|
11
12
|
},
|
|
12
13
|
"exports": {
|
|
@@ -41,6 +42,14 @@
|
|
|
41
42
|
"kit",
|
|
42
43
|
"agents",
|
|
43
44
|
"agent-runtime",
|
|
45
|
+
"agent-spend-controls",
|
|
46
|
+
"ai-agent-budget",
|
|
47
|
+
"delegated-spend",
|
|
48
|
+
"spend-guardrails",
|
|
49
|
+
"tool-call-spend-limits",
|
|
50
|
+
"gemini",
|
|
51
|
+
"claude",
|
|
52
|
+
"anthropic",
|
|
44
53
|
"agent-payments",
|
|
45
54
|
"escrow",
|
|
46
55
|
"intent-escrow",
|