@bound/mcp 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bound Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @bound/mcp
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server that gives any MCP-capable agent —
4
+ Claude Desktop, Claude Code, Cursor, your own client — the tools of
5
+ [Bound Protocol](https://github.com/iamnotdou/bound): verify an agent's certificate
6
+ before transacting with it, pay for a service autonomously over x402, read the meter
7
+ that records what a certificate has spent, price and buy coverage, and prove a false
8
+ attestation on-chain so the auditor is slashed.
9
+
10
+ It is the same tool table the Bound dashboard drives through the AI SDK, defined once
11
+ and adapted twice. Everything it does goes through [`@bound/sdk`](../sdk), which is a
12
+ regular dependency — there is nothing else to install.
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ npx @bound/mcp
18
+ ```
19
+
20
+ The server speaks MCP over stdio, so it is normally launched by a client rather than by
21
+ hand. `stdout` is the JSON-RPC channel; every diagnostic goes to `stderr`.
22
+
23
+ ### Claude Desktop
24
+
25
+ `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,
26
+ `%APPDATA%\Claude\claude_desktop_config.json` on Windows:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "bound": {
32
+ "command": "npx",
33
+ "args": ["-y", "@bound/mcp"],
34
+ "env": {
35
+ "AGENT_SECRET": "S...",
36
+ "OPERATOR_SECRET": "S...",
37
+ "AUDITOR_SECRET": "S...",
38
+ "CHALLENGER_SECRET": "S...",
39
+ "COUNTERPARTY_SECRET": "S..."
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### Cursor
47
+
48
+ `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` globally — same shape:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "bound": {
54
+ "command": "npx",
55
+ "args": ["-y", "@bound/mcp"],
56
+ "env": {
57
+ "AGENT_SECRET": "S...",
58
+ "OPERATOR_SECRET": "S...",
59
+ "AUDITOR_SECRET": "S...",
60
+ "CHALLENGER_SECRET": "S...",
61
+ "COUNTERPARTY_SECRET": "S..."
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ Claude Code takes the same block in `.mcp.json`, or `claude mcp add bound -- npx -y
69
+ @bound/mcp`.
70
+
71
+ ## Environment
72
+
73
+ | Variable | Needed for |
74
+ | --------------------- | ----------------------------------------------------------------------- |
75
+ | `AGENT_SECRET` | `execute_payment`, `fetch_paid_service`, `fund_float`, `enroll_agent` |
76
+ | `OPERATOR_SECRET` | `enroll_agent`, `halt_certificate`, `resume_certificate`, `pay_premium` |
77
+ | `AUDITOR_SECRET` | `claim_premium` |
78
+ | `CHALLENGER_SECRET` | `challenge_certificate` |
79
+ | `COUNTERPARTY_SECRET` | reserved for the demo counterparty role |
80
+ | `STELLAR_NETWORK` | optional; only `testnet` exists today, and it is the default |
81
+
82
+ Every key is read lazily, so a session that only reads never touches one and starts
83
+ fine with none of them set. Contract addresses and network endpoints are committed
84
+ inside `@bound/sdk` — there is nothing to configure.
85
+
86
+ **These are Stellar secret keys (`S...`).** They sign real transactions on testnet, and
87
+ the write tools spend from them without asking. Bound is testnet-only today (see
88
+ [Status](#status)), so there is no mainnet balance to lose — but treat the config block
89
+ as a credential file regardless. If you are running inside a checkout of the Bound repo,
90
+ the server will also pick the same five variables up from a `.env.testnet` found by
91
+ walking upward from its working directory.
92
+
93
+ ## Tools
94
+
95
+ Read-only tools simulate against the chain, sign nothing and spend nothing; they are
96
+ marked `readOnlyHint` so a client can run them without prompting. Everything else signs
97
+ with one of the keys above.
98
+
99
+ | Tool | Read | What it does |
100
+ | -------------------------- | :--: | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `verify_agent_certificate` | ✓ | The headline check: is this agent's certificate valid, and what bound, reserve and auditor stake back it |
102
+ | `get_balance` | ✓ | USDC balance of an address; defaults to the agent |
103
+ | `get_routing_status` | ✓ | Whether an address's payments are metered, which certificate against, and the float it holds |
104
+ | `get_cert_meter` | ✓ | A certificate's routed spend, float, float cap and halt state, next to the bound they measure against |
105
+ | `quote_premium` | ✓ | Price coverage — for a published certificate, or a hypothetical bound × duration |
106
+ | `get_coverage` | ✓ | Premium paid, protocol fee, and how much of the auditor's yield has accrued and is claimable |
107
+ | `execute_payment` | | Send USDC. Routed through the meter when the agent is enrolled, raw USDC when it is not |
108
+ | `fetch_paid_service` | | x402: fetch a URL, and if it answers `402`, pay the demanded price and retry — no human approves it |
109
+ | `enroll_agent` | | Bind the agent to a certificate and set its float cap. Operator **and** agent both authorize |
110
+ | `fund_float` | | Move USDC into the router's custody to pay from |
111
+ | `halt_certificate` | | Operator's kill switch: stop every transfer, withdrawal and burn on a certificate |
112
+ | `resume_certificate` | | Lift a halt |
113
+ | `pay_premium` | | Operator buys coverage. Once per certificate, and only after an auditor has attested |
114
+ | `claim_premium` | | Auditor withdraws accrued-and-unclaimed yield |
115
+ | `challenge_certificate` | | Prove an attestation false. For `InsufficientReserve` the contract verifies the fraud itself, slashes the auditor's stake and compensates the victim |
116
+
117
+ Two things are worth knowing before an agent acts on these:
118
+
119
+ - **There is no spending cap.** The agent pays what it is asked. What protects the
120
+ counterparty is the certificate — a pre-funded reserve and an auditor's slashable
121
+ stake bounding the worst case — not a limit on the call.
122
+ - **Routed spend is gross flow, not loss.** A certificate that has routed more than its
123
+ bound has broken a covenant about its own conduct; it has not thereby cost anyone that
124
+ much money. `get_cert_meter` returns the bound alongside the counter so the comparison
125
+ is explicit rather than implied.
126
+
127
+ ## Status
128
+
129
+ Stellar **testnet only**. The contract addresses, network endpoints and demo actor
130
+ public keys are committed inside `@bound/sdk`; there is no mainnet deployment yet.
131
+
132
+ ## Embedding it
133
+
134
+ The package root exports the pieces, for callers who want them somewhere other than a
135
+ stdio process:
136
+
137
+ ```ts
138
+ import { createBoundMcpServer } from "@bound/mcp";
139
+
140
+ const server = createBoundMcpServer();
141
+ await server.connect(myOwnTransport);
142
+ ```
143
+
144
+ `@bound/mcp/tools` is a second, lighter entry point carrying only the tool table —
145
+ `{ description, parameters (a Zod raw shape), execute }` per tool — with no MCP server
146
+ attached. Import that to drive the same tools from a framework that is not MCP; the
147
+ Bound dashboard uses it to build AI SDK tools.
148
+
149
+ Both entry points are **server-side only**: the write tools sign with secret keys from
150
+ the environment, so they belong behind your own backend, never in a browser bundle.
151
+
152
+ ## Learn more
153
+
154
+ Source, contracts, and full documentation: <https://github.com/iamnotdou/bound>
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
+ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
6
+ var zod = require('zod');
7
+ var sdk = require('@bound/sdk');
8
+ var fs = require('fs');
9
+ var module$1 = require('module');
10
+ var path = require('path');
11
+ var stellarSdk = require('@stellar/stellar-sdk');
12
+
13
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.26_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js
14
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
15
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
16
+ function findEnvTestnet(startDir) {
17
+ let dir = startDir;
18
+ for (; ; ) {
19
+ const candidate = path.join(dir, ".env.testnet");
20
+ if (fs.existsSync(candidate)) return candidate;
21
+ const parent = path.dirname(dir);
22
+ if (parent === dir) return void 0;
23
+ dir = parent;
24
+ }
25
+ }
26
+ if (!process.env.OPERATOR_SECRET) {
27
+ const envPath = findEnvTestnet(process.cwd());
28
+ if (envPath) {
29
+ try {
30
+ const load = module$1.createRequire(importMetaUrl)("dotenv");
31
+ load.config({ path: envPath });
32
+ } catch {
33
+ }
34
+ }
35
+ }
36
+ function kp(secretKey) {
37
+ const secret = process.env[secretKey];
38
+ if (!secret) {
39
+ throw new Error(
40
+ `missing ${secretKey} \u2014 set it in the MCP server's env block (see @bound/mcp README)`
41
+ );
42
+ }
43
+ return stellarSdk.Keypair.fromSecret(secret);
44
+ }
45
+ var accounts = {
46
+ get operator() {
47
+ return kp("OPERATOR_SECRET");
48
+ },
49
+ get agent() {
50
+ return kp("AGENT_SECRET");
51
+ },
52
+ get auditor() {
53
+ return kp("AUDITOR_SECRET");
54
+ },
55
+ get challenger() {
56
+ return kp("CHALLENGER_SECRET");
57
+ }};
58
+
59
+ // src/tools.ts
60
+ var SECONDS_PER_DAY = 86400;
61
+ var boundTools = {
62
+ verify_agent_certificate: {
63
+ description: "Check an AI agent's Bound Certificate before transacting with it. Returns whether it is valid, the bounded worst-case loss, the locked reserve, and the auditor's slashable stake.",
64
+ readOnly: true,
65
+ parameters: { agent: zod.z.string().describe("The G... address of the agent to verify") },
66
+ execute: async ({ agent }) => {
67
+ const v = await sdk.bound.verifyCertificate(agent);
68
+ return {
69
+ valid: v.valid,
70
+ status: v.status.tag,
71
+ bound: sdk.formatUsdc(v.bound),
72
+ reserve: sdk.formatUsdc(v.reserve),
73
+ auditorStake: sdk.formatUsdc(v.auditor_stake),
74
+ auditor: v.auditor ?? null
75
+ };
76
+ }
77
+ },
78
+ get_balance: {
79
+ description: "Get the USDC balance of an address. Defaults to this agent's own balance.",
80
+ readOnly: true,
81
+ parameters: {
82
+ address: zod.z.string().optional().describe("G... address; omit for the agent itself")
83
+ },
84
+ execute: async ({ address }) => {
85
+ const addr = address ?? accounts.agent.publicKey();
86
+ return { address: addr, balance: sdk.formatUsdc(await sdk.bound.usdcBalance(addr)) };
87
+ }
88
+ },
89
+ execute_payment: {
90
+ description: "Send USDC directly to a recipient. The agent has no spending cap \u2014 it pays what it is asked. The counterparty's exposure is bounded by the agent's certificate, not by this call.",
91
+ parameters: {
92
+ recipient: zod.z.string().describe("G... address to pay"),
93
+ amountUsd: zod.z.number().describe("Amount in USDC dollars")
94
+ },
95
+ execute: async ({ recipient, amountUsd }) => {
96
+ const receipt = await sdk.bound.executePayment(accounts.agent, recipient, sdk.usdc(amountUsd));
97
+ return {
98
+ success: true,
99
+ amountUsd,
100
+ recipient,
101
+ txHash: receipt.hash,
102
+ // Whether this payment is on the record a challenger can read.
103
+ routed: receipt.routed,
104
+ certId: receipt.certId
105
+ };
106
+ }
107
+ },
108
+ fetch_paid_service: {
109
+ description: "Fetch an HTTP resource that may require payment (x402). If the server answers 402, the agent autonomously pays the demanded price in USDC and retries. No human approves the amount \u2014 the service sets it, the agent decides.",
110
+ parameters: { url: zod.z.string().describe("URL of the paid service") },
111
+ execute: async ({ url }) => {
112
+ const { response, paid } = await sdk.agentFetch(url, accounts.agent, sdk.bound);
113
+ const body = await response.text();
114
+ return {
115
+ status: response.status,
116
+ paid: paid ? { amountUsd: paid.amount, recipient: paid.recipient, txHash: paid.txHash } : null,
117
+ body
118
+ };
119
+ }
120
+ },
121
+ challenge_certificate: {
122
+ description: "Prove a certificate's attestation is false and trigger on-chain enforcement. For InsufficientReserve the contract verifies the fraud itself (claimed reserve > actual), then slashes the auditor's stake and compensates the victim. Returns the resulting certificate status.",
123
+ parameters: {
124
+ certId: zod.z.number().describe("Certificate id to challenge"),
125
+ victim: zod.z.string().describe("G... address of the harmed counterparty to compensate"),
126
+ proofType: zod.z.enum(["InsufficientReserve", "BoundExceeded", "FakeSignature", "ExpiredCertificate"]).default("InsufficientReserve").describe("Kind of fraud; only InsufficientReserve is verified trustlessly on-chain")
127
+ },
128
+ execute: async ({
129
+ certId,
130
+ victim,
131
+ proofType
132
+ }) => {
133
+ const before = await sdk.bound.verifyCertificate(accounts.agent.publicKey());
134
+ const challengeId = await sdk.bound.challengeCertificate(accounts.challenger, {
135
+ certId: BigInt(certId),
136
+ proofType: proofType ?? "InsufficientReserve",
137
+ victim,
138
+ bond: sdk.usdc(100)
139
+ });
140
+ const after = await sdk.bound.verifyCertificate(accounts.agent.publicKey());
141
+ return {
142
+ challengeId: Number(challengeId),
143
+ certStatusBefore: before.status.tag,
144
+ certStatusAfter: after.status.tag,
145
+ outcome: after.status.tag === "Invalid" ? "FRAUD_PROVEN \u2014 auditor slashed, victim compensated" : "challenge did not invalidate"
146
+ };
147
+ }
148
+ },
149
+ // ---- PaymentRouter: the metered rail ---------------------------------------
150
+ get_routing_status: {
151
+ description: "Check whether an address's payments are metered. An address bound to a certificate in the PaymentRouter pays from a float the router holds in custody, and every payment lands on that certificate's spend counter. An unenrolled address pays over the raw USDC rail instead, which leaves no trace any challenger can read.",
152
+ readOnly: true,
153
+ parameters: {
154
+ address: zod.z.string().optional().describe("G... address; omit for the agent itself")
155
+ },
156
+ execute: async ({ address }) => {
157
+ const addr = address ?? accounts.agent.publicKey();
158
+ const certId = await sdk.bound.routedCertId(addr);
159
+ return {
160
+ address: addr,
161
+ routed: certId !== null,
162
+ certId,
163
+ floatHeld: sdk.formatUsdc(await sdk.bound.routedBalance(addr))
164
+ };
165
+ }
166
+ },
167
+ get_cert_meter: {
168
+ description: "Read a certificate's meter: cumulative routed spend, the float the router holds for it, that float's ceiling, and whether the operator has halted it. Spend is GROSS FLOW, not loss \u2014 a certificate that has routed more than its bound has broken a covenant about its own conduct, it has not thereby cost anyone that much money. The bound is returned alongside so the two can be compared.",
169
+ readOnly: true,
170
+ parameters: { certId: zod.z.number().describe("Certificate id to read") },
171
+ execute: async ({ certId }) => {
172
+ const id = BigInt(certId);
173
+ const [spent, float, floatCap, halted, cert] = await Promise.all([
174
+ sdk.bound.spendForCert(id),
175
+ sdk.bound.floatForCert(id),
176
+ sdk.bound.floatCapForCert(id),
177
+ sdk.bound.certHalted(id),
178
+ sdk.getCertificate(certId)
179
+ ]);
180
+ return {
181
+ certId,
182
+ agent: cert?.agent ?? null,
183
+ status: cert?.status ?? null,
184
+ bound: cert?.boundUsd ?? null,
185
+ spent: sdk.formatUsdc(spent),
186
+ float: sdk.formatUsdc(float),
187
+ floatCap: sdk.formatUsdc(floatCap),
188
+ halted
189
+ };
190
+ }
191
+ },
192
+ enroll_agent: {
193
+ description: "Bind the agent to a certificate in the PaymentRouter and set that certificate's float cap. Both the operator and the agent authorize: enrolment attaches spend to the operator's certificate and subjects the agent to metering and to the operator's kill switch, so neither may conscript the other. A binding is permanent \u2014 an operator cannot walk an agent off a certificate whose counter is climbing.",
194
+ parameters: {
195
+ certId: zod.z.number().describe("Certificate id to meter the agent against"),
196
+ floatCapUsd: zod.z.number().describe("Ceiling on the float the router will hold \u2014 what a stolen agent key can reach")
197
+ },
198
+ execute: async ({ certId, floatCapUsd }) => {
199
+ await sdk.bound.enrollAgent(accounts.operator, accounts.agent, BigInt(certId), sdk.usdc(floatCapUsd));
200
+ return {
201
+ success: true,
202
+ agent: accounts.agent.publicKey(),
203
+ certId,
204
+ floatCapUsd,
205
+ note: "the agent's payments are now metered on this certificate's spend counter"
206
+ };
207
+ }
208
+ },
209
+ fund_float: {
210
+ description: "Move USDC into the PaymentRouter's custody, crediting the agent an equal routed balance to pay from. execute_payment tops this up on demand, so funding ahead of time is optional: float that is never idle is float that cannot be stolen.",
211
+ parameters: {
212
+ amountUsd: zod.z.number().describe("Amount in USDC dollars to move into the router")
213
+ },
214
+ execute: async ({ amountUsd }) => {
215
+ const { hash } = await sdk.bound.fundFloat(accounts.agent, sdk.usdc(amountUsd));
216
+ const addr = accounts.agent.publicKey();
217
+ return {
218
+ success: true,
219
+ amountUsd,
220
+ txHash: hash,
221
+ floatHeld: sdk.formatUsdc(await sdk.bound.routedBalance(addr))
222
+ };
223
+ }
224
+ },
225
+ halt_certificate: {
226
+ description: "The operator's kill switch: stop every transfer, withdrawal and burn on a certificate. Signed by the operator, not the agent \u2014 this is the lever that exists precisely for the case where the agent's key is the problem.",
227
+ parameters: { certId: zod.z.number().describe("Certificate id to halt") },
228
+ execute: async ({ certId }) => {
229
+ const { hash } = await sdk.bound.haltCert(accounts.operator, BigInt(certId));
230
+ return { success: true, certId, halted: true, txHash: hash };
231
+ }
232
+ },
233
+ resume_certificate: {
234
+ description: "Lift an operator halt and let a certificate's payments flow again.",
235
+ parameters: { certId: zod.z.number().describe("Certificate id to resume") },
236
+ execute: async ({ certId }) => {
237
+ const { hash } = await sdk.bound.resumeCert(accounts.operator, BigInt(certId));
238
+ return { success: true, certId, halted: false, txHash: hash };
239
+ }
240
+ },
241
+ // ---- PremiumVault: the economy ---------------------------------------------
242
+ quote_premium: {
243
+ description: "Price coverage: bound x duration x rate. Pass a certId to quote a published certificate from its own recorded terms, or boundUsd + durationDays to price a hypothetical one before publishing it.",
244
+ readOnly: true,
245
+ parameters: {
246
+ certId: zod.z.number().optional().describe("Published certificate id to price from its terms"),
247
+ boundUsd: zod.z.number().optional().describe("Hypothetical bound in USDC dollars"),
248
+ durationDays: zod.z.number().optional().describe("Hypothetical coverage length in days")
249
+ },
250
+ execute: async ({
251
+ certId,
252
+ boundUsd,
253
+ durationDays
254
+ }) => {
255
+ if (certId !== void 0) {
256
+ const premium2 = await sdk.bound.quotePremiumForCert(BigInt(certId));
257
+ return { certId, premium: sdk.formatUsdc(premium2) };
258
+ }
259
+ if (boundUsd === void 0 || durationDays === void 0) {
260
+ throw new Error("pass either certId, or both boundUsd and durationDays");
261
+ }
262
+ const premium = await sdk.bound.quotePremium(
263
+ sdk.usdc(boundUsd),
264
+ BigInt(Math.round(durationDays * SECONDS_PER_DAY))
265
+ );
266
+ return { boundUsd, durationDays, premium: sdk.formatUsdc(premium) };
267
+ }
268
+ },
269
+ get_coverage: {
270
+ description: "Read a certificate's coverage: what the operator paid, the protocol's cut, and how much of the rest the auditor has earned so far. The auditor's yield accrues in a straight line across the term, and a slash forfeits only what is still unclaimed \u2014 the premium is yield on the stake, never a second bond. Returns the quote instead when no premium has been paid yet.",
271
+ readOnly: true,
272
+ parameters: { certId: zod.z.number().describe("Certificate id to read coverage for") },
273
+ execute: async ({ certId }) => {
274
+ const id = BigInt(certId);
275
+ if (!await sdk.bound.premiumPaid(id)) {
276
+ return { certId, paid: false, quote: sdk.formatUsdc(await sdk.bound.quotePremiumForCert(id)) };
277
+ }
278
+ const [c, accrued, claimable] = await Promise.all([
279
+ sdk.bound.coverage(id),
280
+ sdk.bound.premiumAccrued(id),
281
+ sdk.bound.premiumClaimable(id)
282
+ ]);
283
+ return {
284
+ certId,
285
+ paid: true,
286
+ payer: c.payer,
287
+ auditor: c.auditor,
288
+ premium: sdk.formatUsdc(c.premium),
289
+ protocolFee: sdk.formatUsdc(c.protocol_fee),
290
+ yieldPot: sdk.formatUsdc(c.yield_pot),
291
+ claimed: sdk.formatUsdc(c.claimed),
292
+ accrued: sdk.formatUsdc(accrued),
293
+ claimable: sdk.formatUsdc(claimable),
294
+ startUnix: Number(c.start),
295
+ durationSeconds: Number(c.duration),
296
+ closed: c.closed,
297
+ closedAtUnix: Number(c.closed_at)
298
+ };
299
+ }
300
+ },
301
+ pay_premium: {
302
+ description: "Operator buys coverage for a certificate. Payable once, and only on a VERIFIED certificate \u2014 the premium is yield on the auditor's staked capital, and there is no auditor to pay until one has attested. The protocol's share leaves in the same transaction; the rest accrues to the auditor across the term.",
303
+ parameters: { certId: zod.z.number().describe("Certificate id to buy coverage for") },
304
+ execute: async ({ certId }) => {
305
+ const id = BigInt(certId);
306
+ const quote = await sdk.bound.quotePremiumForCert(id);
307
+ const { hash } = await sdk.bound.payPremium(accounts.operator, id);
308
+ return { success: true, certId, premium: sdk.formatUsdc(quote), txHash: hash };
309
+ }
310
+ },
311
+ claim_premium: {
312
+ description: "Auditor withdraws accrued-and-unclaimed yield. Allowed at any time, including mid-coverage: at every instant the accrued figure is payment for coverage already delivered. Claiming continuously converts forfeitable yield into settled income, which is deliberate \u2014 see get_coverage.",
313
+ parameters: { certId: zod.z.number().describe("Certificate id to claim yield from") },
314
+ execute: async ({ certId }) => {
315
+ const { hash, result } = await sdk.bound.claimPremium(accounts.auditor, BigInt(certId));
316
+ return { success: true, certId, claimed: sdk.formatUsdc(result), txHash: hash };
317
+ }
318
+ }
319
+ };
320
+
321
+ // src/server.ts
322
+ var VERSION = "0.1.0";
323
+ function createBoundMcpServer(tools = boundTools) {
324
+ const server = new mcp_js.McpServer({ name: "bound-protocol", version: VERSION });
325
+ for (const [name, t] of Object.entries(tools)) {
326
+ server.registerTool(
327
+ name,
328
+ {
329
+ description: t.description,
330
+ inputSchema: t.parameters,
331
+ // Only the read-only hint is claimed. `destructiveHint` and
332
+ // `idempotentHint` would both be lies here: a payment is neither
333
+ // reversible nor safe to replay.
334
+ annotations: { readOnlyHint: t.readOnly === true }
335
+ },
336
+ async (args) => {
337
+ try {
338
+ const result = await t.execute(args);
339
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
340
+ } catch (err) {
341
+ const message = err instanceof Error ? err.message : String(err);
342
+ return {
343
+ isError: true,
344
+ content: [{ type: "text", text: `Error: ${message}` }]
345
+ };
346
+ }
347
+ }
348
+ );
349
+ }
350
+ return server;
351
+ }
352
+
353
+ // src/bin.ts
354
+ async function main() {
355
+ const server = createBoundMcpServer();
356
+ await server.connect(new stdio_js.StdioServerTransport());
357
+ console.error(`Bound MCP server ${VERSION} running on stdio`);
358
+ }
359
+ main().catch((err) => {
360
+ console.error(err);
361
+ process.exit(1);
362
+ });
363
+ //# sourceMappingURL=bound-mcp.js.map
364
+ //# sourceMappingURL=bound-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.26_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js","../src/accounts.ts","../src/tools.ts","../src/server.ts","../src/bin.ts"],"names":["join","existsSync","dirname","createRequire","Keypair","z","bound","formatUsdc","usdc","agentFetch","getCertificate","premium","McpServer","StdioServerTransport"],"mappings":"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","file":"bound-mcp.js","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","// Demo keypairs, loaded from the environment. SERVER ONLY — these hold secret\n// keys, and every write tool in this package signs with one of them.\n//\n// This is a deliberate near-copy of apps/dashboard/app/lib/accounts.ts. The two\n// read the SAME five environment variables, so a repo checkout keeps working\n// either way, but a published package cannot reach into a Next.js app for its\n// credentials — that import is exactly what stopped `mcp/server.ts` from being\n// installable. Duplicating thirty lines is the cost of the package standing on\n// its own; the env var names are the contract that keeps them in step.\n//\n// Bound is testnet-only (see the SDK's committed deployment record), so naming\n// five fixed roles here is honest rather than limiting: there is no mainnet key\n// anyone could lose. An MCP client supplies them through the `env` block of its\n// server config; `.env.testnet` is only the in-repo convenience.\nimport { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { Keypair } from \"@stellar/stellar-sdk\";\n\nfunction findEnvTestnet(startDir: string): string | undefined {\n let dir = startDir;\n for (;;) {\n const candidate = join(dir, \".env.testnet\");\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\n// An MCP client launches this server with cwd set to wherever it pleases, so we\n// walk upward rather than assuming a fixed relative path. Skipped entirely once\n// the environment already carries the keys, which is the normal case for an\n// installed server: the client's config block is the supply route.\n//\n// dotenv is loaded through createRequire and swallowed on failure because it is\n// NOT a dependency of this package — it is a dev-time convenience that happens\n// to be present inside the Bound repo. An installed copy has no .env.testnet to\n// find and never reaches this line.\nif (!process.env.OPERATOR_SECRET) {\n const envPath = findEnvTestnet(process.cwd());\n if (envPath) {\n try {\n const load = createRequire(import.meta.url)(\"dotenv\") as {\n config: (options: { path: string }) => void;\n };\n load.config({ path: envPath });\n } catch {\n // No dotenv on the resolution path: fall through and let kp() report the\n // missing variable by name, which is the more useful error anyway.\n }\n }\n}\n\nfunction kp(secretKey: string): Keypair {\n const secret = process.env[secretKey];\n if (!secret) {\n throw new Error(\n `missing ${secretKey} — set it in the MCP server's env block (see @bound/mcp README)`,\n );\n }\n return Keypair.fromSecret(secret);\n}\n\n// Lazily constructed so importing this module doesn't throw when only some\n// roles are needed. A read-only session never touches a secret at all.\nexport const accounts = {\n get operator() {\n return kp(\"OPERATOR_SECRET\");\n },\n get agent() {\n return kp(\"AGENT_SECRET\");\n },\n get auditor() {\n return kp(\"AUDITOR_SECRET\");\n },\n get challenger() {\n return kp(\"CHALLENGER_SECRET\");\n },\n get counterparty() {\n return kp(\"COUNTERPARTY_SECRET\");\n },\n};\n","// Framework-agnostic Bound tool definitions. Each tool is a description + a Zod\n// raw shape (the param map) + an execute() that calls BoundClient. The MCP\n// server here and the AI SDK loop in apps/dashboard both adapt these — one\n// core, two mouths. This file is the single definition of each tool; the app\n// imports it back from `@bound/mcp/tools` rather than owning a second copy.\n//\n// SERVER ONLY: execute() signs with secret keys held in the environment.\n//\n// Model A (pure bonding): there is NO spending cap. The agent pays whatever it\n// is asked. Safety is economic — the certificate bounds the counterparty's loss,\n// and a false vouch is slashed via challenge_certificate.\n//\n// v2 hangs two rails off that. The PaymentRouter meters what an enrolled agent\n// spends, and its counter is the on-chain state a BoundExceeded proof is read\n// from — routing is what turns a payment into evidence. The PremiumVault prices\n// the auditor's risk and pays them yield for carrying it, so vouching is a\n// business rather than a favour.\nimport { z } from \"zod\";\nimport { bound, type ProofType, agentFetch, getCertificate, usdc, formatUsdc } from \"@bound/sdk\";\nimport { accounts } from \"./accounts\";\n\nexport interface BoundTool {\n description: string;\n parameters: z.ZodRawShape;\n /**\n * Simulates against the chain, signs nothing, spends nothing. Surfaced to MCP\n * clients as `readOnlyHint` so a human is asked to approve only the calls\n * that actually move money.\n */\n readOnly?: boolean;\n execute: (args: any) => Promise<unknown>;\n}\n\nconst SECONDS_PER_DAY = 86_400;\n\nexport const boundTools: Record<string, BoundTool> = {\n verify_agent_certificate: {\n description:\n \"Check an AI agent's Bound Certificate before transacting with it. Returns whether it is valid, the bounded worst-case loss, the locked reserve, and the auditor's slashable stake.\",\n readOnly: true,\n parameters: { agent: z.string().describe(\"The G... address of the agent to verify\") },\n execute: async ({ agent }: { agent: string }) => {\n const v = await bound.verifyCertificate(agent);\n return {\n valid: v.valid,\n status: v.status.tag,\n bound: formatUsdc(v.bound),\n reserve: formatUsdc(v.reserve),\n auditorStake: formatUsdc(v.auditor_stake),\n auditor: v.auditor ?? null,\n };\n },\n },\n\n get_balance: {\n description: \"Get the USDC balance of an address. Defaults to this agent's own balance.\",\n readOnly: true,\n parameters: {\n address: z.string().optional().describe(\"G... address; omit for the agent itself\"),\n },\n execute: async ({ address }: { address?: string }) => {\n const addr = address ?? accounts.agent.publicKey();\n return { address: addr, balance: formatUsdc(await bound.usdcBalance(addr)) };\n },\n },\n\n execute_payment: {\n description:\n \"Send USDC directly to a recipient. The agent has no spending cap — it pays what it is asked. The counterparty's exposure is bounded by the agent's certificate, not by this call.\",\n parameters: {\n recipient: z.string().describe(\"G... address to pay\"),\n amountUsd: z.number().describe(\"Amount in USDC dollars\"),\n },\n execute: async ({ recipient, amountUsd }: { recipient: string; amountUsd: number }) => {\n const receipt = await bound.executePayment(accounts.agent, recipient, usdc(amountUsd));\n return {\n success: true,\n amountUsd,\n recipient,\n txHash: receipt.hash,\n // Whether this payment is on the record a challenger can read.\n routed: receipt.routed,\n certId: receipt.certId,\n };\n },\n },\n\n fetch_paid_service: {\n description:\n \"Fetch an HTTP resource that may require payment (x402). If the server answers 402, the agent autonomously pays the demanded price in USDC and retries. No human approves the amount — the service sets it, the agent decides.\",\n parameters: { url: z.string().describe(\"URL of the paid service\") },\n execute: async ({ url }: { url: string }) => {\n const { response, paid } = await agentFetch(url, accounts.agent, bound);\n const body = await response.text();\n return {\n status: response.status,\n paid: paid\n ? { amountUsd: paid.amount, recipient: paid.recipient, txHash: paid.txHash }\n : null,\n body,\n };\n },\n },\n\n challenge_certificate: {\n description:\n \"Prove a certificate's attestation is false and trigger on-chain enforcement. For InsufficientReserve the contract verifies the fraud itself (claimed reserve > actual), then slashes the auditor's stake and compensates the victim. Returns the resulting certificate status.\",\n parameters: {\n certId: z.number().describe(\"Certificate id to challenge\"),\n victim: z.string().describe(\"G... address of the harmed counterparty to compensate\"),\n proofType: z\n .enum([\"InsufficientReserve\", \"BoundExceeded\", \"FakeSignature\", \"ExpiredCertificate\"])\n .default(\"InsufficientReserve\")\n .describe(\"Kind of fraud; only InsufficientReserve is verified trustlessly on-chain\"),\n },\n execute: async ({\n certId,\n victim,\n proofType,\n }: {\n certId: number;\n victim: string;\n proofType?: ProofType[\"tag\"];\n }) => {\n const before = await bound.verifyCertificate(accounts.agent.publicKey());\n const challengeId = await bound.challengeCertificate(accounts.challenger, {\n certId: BigInt(certId),\n proofType: (proofType ?? \"InsufficientReserve\") as ProofType[\"tag\"],\n victim,\n bond: usdc(100),\n });\n const after = await bound.verifyCertificate(accounts.agent.publicKey());\n return {\n challengeId: Number(challengeId),\n certStatusBefore: before.status.tag,\n certStatusAfter: after.status.tag,\n outcome:\n after.status.tag === \"Invalid\"\n ? \"FRAUD_PROVEN — auditor slashed, victim compensated\"\n : \"challenge did not invalidate\",\n };\n },\n },\n\n // ---- PaymentRouter: the metered rail ---------------------------------------\n\n get_routing_status: {\n description:\n \"Check whether an address's payments are metered. An address bound to a certificate in the PaymentRouter pays from a float the router holds in custody, and every payment lands on that certificate's spend counter. An unenrolled address pays over the raw USDC rail instead, which leaves no trace any challenger can read.\",\n readOnly: true,\n parameters: {\n address: z.string().optional().describe(\"G... address; omit for the agent itself\"),\n },\n execute: async ({ address }: { address?: string }) => {\n const addr = address ?? accounts.agent.publicKey();\n const certId = await bound.routedCertId(addr);\n return {\n address: addr,\n routed: certId !== null,\n certId,\n floatHeld: formatUsdc(await bound.routedBalance(addr)),\n };\n },\n },\n\n get_cert_meter: {\n description:\n \"Read a certificate's meter: cumulative routed spend, the float the router holds for it, that float's ceiling, and whether the operator has halted it. Spend is GROSS FLOW, not loss — a certificate that has routed more than its bound has broken a covenant about its own conduct, it has not thereby cost anyone that much money. The bound is returned alongside so the two can be compared.\",\n readOnly: true,\n parameters: { certId: z.number().describe(\"Certificate id to read\") },\n execute: async ({ certId }: { certId: number }) => {\n const id = BigInt(certId);\n const [spent, float, floatCap, halted, cert] = await Promise.all([\n bound.spendForCert(id),\n bound.floatForCert(id),\n bound.floatCapForCert(id),\n bound.certHalted(id),\n getCertificate(certId),\n ]);\n return {\n certId,\n agent: cert?.agent ?? null,\n status: cert?.status ?? null,\n bound: cert?.boundUsd ?? null,\n spent: formatUsdc(spent),\n float: formatUsdc(float),\n floatCap: formatUsdc(floatCap),\n halted,\n };\n },\n },\n\n enroll_agent: {\n description:\n \"Bind the agent to a certificate in the PaymentRouter and set that certificate's float cap. Both the operator and the agent authorize: enrolment attaches spend to the operator's certificate and subjects the agent to metering and to the operator's kill switch, so neither may conscript the other. A binding is permanent — an operator cannot walk an agent off a certificate whose counter is climbing.\",\n parameters: {\n certId: z.number().describe(\"Certificate id to meter the agent against\"),\n floatCapUsd: z\n .number()\n .describe(\"Ceiling on the float the router will hold — what a stolen agent key can reach\"),\n },\n execute: async ({ certId, floatCapUsd }: { certId: number; floatCapUsd: number }) => {\n await bound.enrollAgent(accounts.operator, accounts.agent, BigInt(certId), usdc(floatCapUsd));\n return {\n success: true,\n agent: accounts.agent.publicKey(),\n certId,\n floatCapUsd,\n note: \"the agent's payments are now metered on this certificate's spend counter\",\n };\n },\n },\n\n fund_float: {\n description:\n \"Move USDC into the PaymentRouter's custody, crediting the agent an equal routed balance to pay from. execute_payment tops this up on demand, so funding ahead of time is optional: float that is never idle is float that cannot be stolen.\",\n parameters: {\n amountUsd: z.number().describe(\"Amount in USDC dollars to move into the router\"),\n },\n execute: async ({ amountUsd }: { amountUsd: number }) => {\n const { hash } = await bound.fundFloat(accounts.agent, usdc(amountUsd));\n const addr = accounts.agent.publicKey();\n return {\n success: true,\n amountUsd,\n txHash: hash,\n floatHeld: formatUsdc(await bound.routedBalance(addr)),\n };\n },\n },\n\n halt_certificate: {\n description:\n \"The operator's kill switch: stop every transfer, withdrawal and burn on a certificate. Signed by the operator, not the agent — this is the lever that exists precisely for the case where the agent's key is the problem.\",\n parameters: { certId: z.number().describe(\"Certificate id to halt\") },\n execute: async ({ certId }: { certId: number }) => {\n const { hash } = await bound.haltCert(accounts.operator, BigInt(certId));\n return { success: true, certId, halted: true, txHash: hash };\n },\n },\n\n resume_certificate: {\n description: \"Lift an operator halt and let a certificate's payments flow again.\",\n parameters: { certId: z.number().describe(\"Certificate id to resume\") },\n execute: async ({ certId }: { certId: number }) => {\n const { hash } = await bound.resumeCert(accounts.operator, BigInt(certId));\n return { success: true, certId, halted: false, txHash: hash };\n },\n },\n\n // ---- PremiumVault: the economy ---------------------------------------------\n\n quote_premium: {\n description:\n \"Price coverage: bound x duration x rate. Pass a certId to quote a published certificate from its own recorded terms, or boundUsd + durationDays to price a hypothetical one before publishing it.\",\n readOnly: true,\n parameters: {\n certId: z.number().optional().describe(\"Published certificate id to price from its terms\"),\n boundUsd: z.number().optional().describe(\"Hypothetical bound in USDC dollars\"),\n durationDays: z.number().optional().describe(\"Hypothetical coverage length in days\"),\n },\n execute: async ({\n certId,\n boundUsd,\n durationDays,\n }: {\n certId?: number;\n boundUsd?: number;\n durationDays?: number;\n }) => {\n if (certId !== undefined) {\n const premium = await bound.quotePremiumForCert(BigInt(certId));\n return { certId, premium: formatUsdc(premium) };\n }\n if (boundUsd === undefined || durationDays === undefined) {\n throw new Error(\"pass either certId, or both boundUsd and durationDays\");\n }\n const premium = await bound.quotePremium(\n usdc(boundUsd),\n BigInt(Math.round(durationDays * SECONDS_PER_DAY)),\n );\n return { boundUsd, durationDays, premium: formatUsdc(premium) };\n },\n },\n\n get_coverage: {\n description:\n \"Read a certificate's coverage: what the operator paid, the protocol's cut, and how much of the rest the auditor has earned so far. The auditor's yield accrues in a straight line across the term, and a slash forfeits only what is still unclaimed — the premium is yield on the stake, never a second bond. Returns the quote instead when no premium has been paid yet.\",\n readOnly: true,\n parameters: { certId: z.number().describe(\"Certificate id to read coverage for\") },\n execute: async ({ certId }: { certId: number }) => {\n const id = BigInt(certId);\n if (!(await bound.premiumPaid(id))) {\n return { certId, paid: false, quote: formatUsdc(await bound.quotePremiumForCert(id)) };\n }\n const [c, accrued, claimable] = await Promise.all([\n bound.coverage(id),\n bound.premiumAccrued(id),\n bound.premiumClaimable(id),\n ]);\n return {\n certId,\n paid: true,\n payer: c.payer,\n auditor: c.auditor,\n premium: formatUsdc(c.premium),\n protocolFee: formatUsdc(c.protocol_fee),\n yieldPot: formatUsdc(c.yield_pot),\n claimed: formatUsdc(c.claimed),\n accrued: formatUsdc(accrued),\n claimable: formatUsdc(claimable),\n startUnix: Number(c.start),\n durationSeconds: Number(c.duration),\n closed: c.closed,\n closedAtUnix: Number(c.closed_at),\n };\n },\n },\n\n pay_premium: {\n description:\n \"Operator buys coverage for a certificate. Payable once, and only on a VERIFIED certificate — the premium is yield on the auditor's staked capital, and there is no auditor to pay until one has attested. The protocol's share leaves in the same transaction; the rest accrues to the auditor across the term.\",\n parameters: { certId: z.number().describe(\"Certificate id to buy coverage for\") },\n execute: async ({ certId }: { certId: number }) => {\n const id = BigInt(certId);\n const quote = await bound.quotePremiumForCert(id);\n const { hash } = await bound.payPremium(accounts.operator, id);\n return { success: true, certId, premium: formatUsdc(quote), txHash: hash };\n },\n },\n\n claim_premium: {\n description:\n \"Auditor withdraws accrued-and-unclaimed yield. Allowed at any time, including mid-coverage: at every instant the accrued figure is payment for coverage already delivered. Claiming continuously converts forfeitable yield into settled income, which is deliberate — see get_coverage.\",\n parameters: { certId: z.number().describe(\"Certificate id to claim yield from\") },\n execute: async ({ certId }: { certId: number }) => {\n const { hash, result } = await bound.claimPremium(accounts.auditor, BigInt(certId));\n return { success: true, certId, claimed: formatUsdc(result), txHash: hash };\n },\n },\n};\n","// Adapts the Bound tool table onto an MCP server. Transport-free on purpose:\n// the stdio wiring lives in bin.ts, so an embedder that wants Bound's tools\n// inside its own server (HTTP, SSE, in-process) can take this and supply its\n// own transport.\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { boundTools, type BoundTool } from \"./tools\";\n\n/** Kept in step with package.json by hand; it is reported over the wire. */\nexport const VERSION = \"0.1.0\";\n\n/**\n * Build a server exposing every Bound tool.\n *\n * Results go back as pretty-printed JSON text rather than a structured output\n * schema: the tools return small, self-describing objects with money already\n * formatted as dollar strings, and a model reads those fine. A thrown error is\n * returned as an `isError` result instead of being allowed to escape — an MCP\n * client should see \"that call failed and here is why\" and be able to try\n * something else, not lose the session because a chain read timed out.\n */\nexport function createBoundMcpServer(tools: Record<string, BoundTool> = boundTools): McpServer {\n const server = new McpServer({ name: \"bound-protocol\", version: VERSION });\n\n for (const [name, t] of Object.entries(tools)) {\n server.registerTool(\n name,\n {\n description: t.description,\n inputSchema: t.parameters,\n // Only the read-only hint is claimed. `destructiveHint` and\n // `idempotentHint` would both be lies here: a payment is neither\n // reversible nor safe to replay.\n annotations: { readOnlyHint: t.readOnly === true },\n },\n async (args: unknown) => {\n try {\n const result = await t.execute(args);\n return { content: [{ type: \"text\" as const, text: JSON.stringify(result, null, 2) }] };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n isError: true,\n content: [{ type: \"text\" as const, text: `Error: ${message}` }],\n };\n }\n },\n );\n }\n\n return server;\n}\n","#!/usr/bin/env node\n// Bound MCP server over stdio — the executable an MCP client launches.\n//\n// Run: npx @bound/mcp\n//\n// stdout is the JSON-RPC channel and nothing else may touch it. Every diagnostic\n// this process emits goes to stderr; a stray console.log here corrupts the\n// stream and the client's only symptom is a server that \"won't connect\".\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createBoundMcpServer, VERSION } from \"./server\";\n\nasync function main() {\n const server = createBoundMcpServer();\n await server.connect(new StdioServerTransport());\n console.error(`Bound MCP server ${VERSION} running on stdio`);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"]}
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { createBoundMcpServer, VERSION } from './chunk-EOWCXK3R.mjs';
3
+ import './chunk-X6XXF6NT.mjs';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+
6
+ async function main() {
7
+ const server = createBoundMcpServer();
8
+ await server.connect(new StdioServerTransport());
9
+ console.error(`Bound MCP server ${VERSION} running on stdio`);
10
+ }
11
+ main().catch((err) => {
12
+ console.error(err);
13
+ process.exit(1);
14
+ });
15
+ //# sourceMappingURL=bound-mcp.mjs.map
16
+ //# sourceMappingURL=bound-mcp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bin.ts"],"names":[],"mappings":";;;;;AAWA,eAAe,IAAA,GAAO;AACpB,EAAA,MAAM,SAAS,oBAAA,EAAqB;AACpC,EAAA,MAAM,MAAA,CAAO,OAAA,CAAQ,IAAI,oBAAA,EAAsB,CAAA;AAC/C,EAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iBAAA,EAAoB,OAAO,CAAA,iBAAA,CAAmB,CAAA;AAC9D;AAEA,IAAA,EAAK,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AACpB,EAAA,OAAA,CAAQ,MAAM,GAAG,CAAA;AACjB,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAChB,CAAC,CAAA","file":"bound-mcp.mjs","sourcesContent":["#!/usr/bin/env node\n// Bound MCP server over stdio — the executable an MCP client launches.\n//\n// Run: npx @bound/mcp\n//\n// stdout is the JSON-RPC channel and nothing else may touch it. Every diagnostic\n// this process emits goes to stderr; a stray console.log here corrupts the\n// stream and the client's only symptom is a server that \"won't connect\".\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createBoundMcpServer, VERSION } from \"./server\";\n\nasync function main() {\n const server = createBoundMcpServer();\n await server.connect(new StdioServerTransport());\n console.error(`Bound MCP server ${VERSION} running on stdio`);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"]}