@haven_ai/mcp 0.0.0-dev.202609031523.fd49e1a

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.
@@ -0,0 +1,252 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { HavenClient } from '@haven_ai/sdk';
3
+ import { z } from 'zod/v3';
4
+
5
+ interface HavenCredentialFile {
6
+ apiKey: string;
7
+ delegateKey: string;
8
+ agentId?: string;
9
+ safeAddress?: string;
10
+ delegateAddress?: string;
11
+ chainId?: number;
12
+ network?: string;
13
+ apiUrl?: string;
14
+ allowanceSummary?: readonly HavenCredentialAllowance[];
15
+ /**
16
+ * Absolute path the credentials were loaded from, if any. Set when the
17
+ * caller pointed at a JSON file via `--credentials` or `HAVEN_CREDENTIALS`;
18
+ * left undefined when credentials came purely from environment variables.
19
+ * The MCP server uses this to locate the consent sidecar
20
+ * (`<sourcePath>.ack.json`) so `--ack` works regardless of how the
21
+ * credential path was supplied.
22
+ */
23
+ sourcePath?: string;
24
+ identityPath?: string;
25
+ signerPath?: string;
26
+ }
27
+ interface HavenCredentialAllowance {
28
+ token: string;
29
+ amount: string;
30
+ resetMinutes: number | null;
31
+ }
32
+ interface HavenCredentialSource {
33
+ credentialsPath?: string;
34
+ identityPath?: string;
35
+ signerPath?: string;
36
+ }
37
+ /**
38
+ * Load Haven agent credentials for the MCP server.
39
+ *
40
+ * Resolution order — earlier sources win, later sources are fallbacks:
41
+ *
42
+ * 1. Explicit `path` argument (typically from `--credentials <path>`).
43
+ * 2. `HAVEN_CREDENTIALS` env var pointing at a credential JSON file.
44
+ * 3. Inline env vars: `HAVEN_API_KEY` + `HAVEN_DELEGATE_KEY` (+ optional
45
+ * `HAVEN_AGENT_ID`, `HAVEN_SAFE_ADDRESS`, `HAVEN_API_URL`).
46
+ *
47
+ * The inline-env path exists so that runtime config snippets emitted by the
48
+ * Haven dashboard (Claude Desktop / Cursor / generic MCP configs) can be a
49
+ * single self-contained block — paste the snippet, restart the runtime, done.
50
+ * The values still live only in the agent operator's process environment;
51
+ * Haven's backend never sees the delegate key either way.
52
+ */
53
+ declare function loadCredentials(source?: string | HavenCredentialSource | undefined): Promise<HavenCredentialFile>;
54
+
55
+ /**
56
+ * Local (stdio) MCP tool handlers. This package runs in the AGENT OPERATOR'S
57
+ * OWN environment — it reads the local credential file and signs locally; the
58
+ * hosted backend is not in the loop for the merchant call. So `merchant_url`
59
+ * passed to `haven.fetch` below is NOT an SSRF surface: the agent is the
60
+ * principal paying a merchant IT chose, on its own machine — the same trust
61
+ * position as running `curl`. There is no server reflecting attacker input
62
+ * into an internal request. (Hosted, multi-tenant fetches live in mcp-server,
63
+ * which is where URL-hardening would matter.)
64
+ */
65
+
66
+ type HavenMcpToolName = 'haven_send' | 'haven_pay_mcp_tool' | 'haven_quote_x402' | 'haven_pay_x402_quote' | 'haven_pay_x402' | 'haven_resume_x402_payment' | 'haven_get_payment_status' | 'haven_get_resume_state' | 'haven_get_agent' | 'haven_get_allowances' | 'haven_list_receipts' | 'haven_verify_receipt' | 'haven_sweep_delegate' | 'haven_discover_tools' | 'haven_submit_catalog_entry';
67
+ declare const toolSchemas: Record<HavenMcpToolName, z.ZodRawShape>;
68
+ /**
69
+ * MCP tool descriptions, composed from the shared semantic source in
70
+ * `@haven_ai/sdk`'s `tool-descriptions.ts`. Keeping both the SDK tool-calling
71
+ * surface and the MCP surface pointed at the same prose source means new
72
+ * guidance lands in both places at once and a parity test can catch drift.
73
+ */
74
+ declare const toolDescriptions: Record<HavenMcpToolName, string>;
75
+ interface ToolSuccess<T> {
76
+ success: true;
77
+ data: T;
78
+ }
79
+ interface ToolFailure {
80
+ success: false;
81
+ code: string;
82
+ message: string;
83
+ /** Structured hint pointing the agent at the correct tool for this operation. */
84
+ suggested_tool?: string;
85
+ statusCode?: number;
86
+ paymentId?: string;
87
+ status?: string;
88
+ phase?: string;
89
+ nextAction?: string;
90
+ resume_state?: unknown;
91
+ body?: unknown;
92
+ }
93
+ type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
94
+ declare function createToolHandlers(haven: HavenClient): Record<HavenMcpToolName, (input: unknown) => Promise<ToolPayload>>;
95
+
96
+ /**
97
+ * First-launch consent gate for the Haven MCP server.
98
+ *
99
+ * Why this exists (option A of issue #163): an agent runtime that loads a
100
+ * Haven credential file is about to expose Haven payment tools to a model.
101
+ * Before the server starts taking JSON-RPC calls we want the operator to
102
+ * acknowledge — exactly once per credential + tool set — what those tools
103
+ * can do and what the on-chain budget actually is. The agent's budget
104
+ * delegation and its caveat enforcers remain the policy primitive; this gate
105
+ * is informational rather than enforcement.
106
+ *
107
+ * #2086: the copy below used to describe the legacy Safe AllowanceModule and
108
+ * an approval queue that would catch an over-budget payment. Both are gone —
109
+ * the AllowanceModule rail is retired (#1986) and the approval queue's table
110
+ * with it (#2055) — so the gate was promising an operator a human backstop
111
+ * that does not exist. It is now one accurate description of the delegation
112
+ * rail rather than rail-aware prose, because the delegation rail is the only
113
+ * rail that can pay at all: a legacy account gets HTTP 410 from the payment
114
+ * paths, so a second branch here would describe a state no reader can be in.
115
+ *
116
+ * Resolution:
117
+ * - `HAVEN_MCP_ACK=<hash>` env var matching the current consent hash → pass.
118
+ * - `HAVEN_MCP_ACK=skip` → pass (intended for CI / scripted setups).
119
+ * - sidecar file `<credentials>.ack.json` containing `{ ack: <hash> }` → pass.
120
+ * - `--ack` CLI flag → write the sidecar file, print the consent block, pass.
121
+ * - otherwise → print the consent block to stderr and exit non-zero.
122
+ *
123
+ * The hash binds the api-key prefix to the registered tool set and the
124
+ * agent's current allowance summary, so a configuration change re-triggers
125
+ * the prompt.
126
+ */
127
+ interface ConsentInput {
128
+ apiKeyPrefix: string;
129
+ /** Haven API base URL the credential will hit. */
130
+ apiUrl?: string;
131
+ /** Agent identity from the credential file, when present. */
132
+ agentId?: string;
133
+ /** Haven wallet (Safe) the agent spends from. */
134
+ safeAddress?: string;
135
+ /** Agent's delegate EOA — the local signer. */
136
+ delegateAddress?: string;
137
+ /** Chain the agent operates on. */
138
+ chainId?: number;
139
+ toolNames: readonly HavenMcpToolName[];
140
+ allowanceSummary: readonly {
141
+ token: string;
142
+ amount: string;
143
+ resetMinutes: number | null;
144
+ }[];
145
+ }
146
+ interface ConsentDecision {
147
+ /** True if the gate is satisfied and the server may start. */
148
+ ok: boolean;
149
+ /** Hash representing the current consent surface. */
150
+ hash: string;
151
+ /** Reason the gate accepted (or rejected) the run. */
152
+ reason: 'env_var_match' | 'env_var_skip' | 'ack_file_match' | 'wrote_ack_file' | 'env_var_mismatch' | 'no_acknowledgement';
153
+ }
154
+ interface ConsentOptions {
155
+ /** Path to the credential file; used to locate the sidecar `<path>.ack.json`. */
156
+ credentialsPath?: string;
157
+ /** When true, write the sidecar file with the current hash and accept. */
158
+ writeAck?: boolean;
159
+ /** Override the environment lookup (testing). */
160
+ env?: Record<string, string | undefined>;
161
+ /** Override the writable stream the consent block is printed to (testing). */
162
+ out?: {
163
+ write: (chunk: string) => unknown;
164
+ };
165
+ }
166
+ declare function computeConsentHash(input: ConsentInput): string;
167
+ declare function renderConsentBlock(input: ConsentInput, hash: string): string;
168
+ /** Resolve the consent gate. Does not exit the process; the caller decides. */
169
+ declare function ensureConsent(input: ConsentInput, options?: ConsentOptions): Promise<ConsentDecision>;
170
+ interface CredentialIdentitySeed {
171
+ apiKey: string;
172
+ apiUrl?: string;
173
+ agentId?: string;
174
+ /** Safe address from the credential file, used as a fallback. */
175
+ safeAddress?: string;
176
+ /** Delegate address from the credential file, used before live allowance metadata is available. */
177
+ delegateAddress?: string;
178
+ /** Chain from the credential file, used as a fallback. */
179
+ chainId?: number;
180
+ /** Intended agent budget from the setup flow, used before on-chain approval is visible. */
181
+ allowanceSummary?: readonly {
182
+ token: string;
183
+ amount: string;
184
+ resetMinutes: number | null;
185
+ }[];
186
+ }
187
+ /**
188
+ * Build the consent input from credential identity plus a live allowance
189
+ * lookup. The on-chain (or configured) allowance is what the operator
190
+ * actually cares about — that's the real spend ceiling — but we also bind
191
+ * the hash to the Haven wallet / delegate / chain so a credential swap
192
+ * cannot quietly reuse a prior sidecar acknowledgement.
193
+ *
194
+ * If `getAllowances()` fails (e.g. backend unreachable on first launch) we
195
+ * fall through to whatever identity fields the credential file provided,
196
+ * so the operator at least sees the tool list and the api-key prefix.
197
+ */
198
+ declare function consentInputFromClient(haven: HavenClient, seed: CredentialIdentitySeed, toolNames: readonly HavenMcpToolName[]): Promise<ConsentInput>;
199
+ /** Convenience: the canonical tool list registered by the server. */
200
+ declare function registeredToolNames(): HavenMcpToolName[];
201
+
202
+ interface HavenMcpServerOptions {
203
+ credentialsPath?: string;
204
+ identityPath?: string;
205
+ signerPath?: string;
206
+ credentials?: HavenCredentialFile;
207
+ /**
208
+ * When true, write the consent sidecar file (`<credentials>.ack.json`)
209
+ * with the current consent hash and proceed. Surfaced via the `--ack`
210
+ * CLI flag.
211
+ */
212
+ writeAck?: boolean;
213
+ /**
214
+ * When true, skip the consent gate entirely. Reserved for tests and
215
+ * controlled embedding — production CLIs should not set this.
216
+ */
217
+ skipConsent?: boolean;
218
+ /** Overridable so the Node-floor refusal is testable without spawning a Node. */
219
+ nodeVersion?: string;
220
+ }
221
+ interface ResolvedHavenClient {
222
+ client: HavenClient;
223
+ credentials: HavenCredentialFile;
224
+ }
225
+ declare function createHavenClient(options?: HavenMcpServerOptions): Promise<HavenClient>;
226
+ declare function resolveHavenClient(options?: HavenMcpServerOptions): Promise<ResolvedHavenClient>;
227
+ declare function createHavenMcpServer(options?: HavenMcpServerOptions): Promise<McpServer>;
228
+ /**
229
+ * Build an MCP server bound to the supplied Haven client.
230
+ *
231
+ * Each tool dispatch is wrapped in `haven.withRequestContext` so every
232
+ * Haven API request the dispatch issues carries `X-Haven-MCP-Tool: <name>`
233
+ * — and *only* that dispatch's requests see the header. The SDK uses an
234
+ * `AsyncLocalStorage` for the context, so two tool calls running
235
+ * concurrently cannot leak headers into each other and the backend
236
+ * `agent_tool_invocations` rows are always attributed to the right tool.
237
+ */
238
+ declare const MCP_NAME = "@haven_ai/mcp";
239
+ declare const MCP_VERSION = "0.0.0-dev.202609031523.fd49e1a";
240
+ /**
241
+ * Refuse to start on an unsupported Node (#1161).
242
+ *
243
+ * Same reasoning as the signer's identical guard: install-time enforcement
244
+ * cannot see a Node downgrade after setup, or a version manager handing the
245
+ * agent runtime a different Node than the shell that connected. The local MCP
246
+ * server drives payment construction against the signer, so it is held to the
247
+ * package's declared floor rather than whatever happens to boot.
248
+ */
249
+ declare function assertSupportedNodeVersion(nodeVersion?: string): void;
250
+ declare function runStdioServer(options?: HavenMcpServerOptions): Promise<void>;
251
+
252
+ export { type ConsentDecision, type ConsentInput, type ConsentOptions, type CredentialIdentitySeed, type HavenCredentialAllowance, type HavenCredentialFile, type HavenCredentialSource, type HavenMcpServerOptions, type HavenMcpToolName, MCP_NAME, MCP_VERSION, type ToolFailure, type ToolPayload, type ToolSuccess, assertSupportedNodeVersion, computeConsentHash, consentInputFromClient, createHavenClient, createHavenMcpServer, createToolHandlers, ensureConsent, loadCredentials, registeredToolNames, renderConsentBlock, resolveHavenClient, runStdioServer, toolDescriptions, toolSchemas };