@coinvello/mcp 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,197 @@
1
- # Temporary Holding Version
1
+ # @coinvello/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Model Context Protocol server for the [CoinVello](https://coinvello.com) API, the non-custodial crypto
4
+ payment processor (no KYC, merchant keeps the keys, fees as prepaid credits, 11 networks).
5
+
6
+ Give Claude, Cursor, Windsurf, ChatGPT or any MCP client the ability to create checkout sessions,
7
+ inspect invoices and payments, register webhooks, verify webhook signatures, manage credits and
8
+ simulate test payments while it writes your integration.
9
+
10
+ ```bash
11
+ npx -y https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz # stdio (what every desktop client uses)
12
+ npx -y https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz --http 3333 # Streamable HTTP on http://127.0.0.1:3333/mcp
13
+ ```
14
+
15
+ Not on the npm registry yet: the package is hosted on coinvello.com (checksums in https://coinvello.com/downloads/SHA256SUMS).
16
+
17
+ ```bash
18
+ # from a checkout of this repo instead
19
+ npm install && npm run build && node dist/index.js
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ | Variable | Required | Meaning |
25
+ |----------|----------|---------|
26
+ | `COINVELLO_API_KEY` | yes | `cv_test_...` from https://coinvello.com/app/settings.php (Settings -> API keys). Any `cv_test_` string works against the local mock. |
27
+ | `COINVELLO_API_BASE` | no | Default `https://api.coinvello.com/v1`. Use `http://localhost:4242/v1` for the reference mock server. |
28
+ | `COINVELLO_ALLOW_LIVE` | no | The server **refuses to start with a `cv_live_` key** unless this is `1`. Agents should develop against test mode. |
29
+ | `COINVELLO_API_VERSION` | no | Pins `CoinVello-Version` (default: account version, currently `2026-10-01`). |
30
+
31
+ ## Client setup
32
+
33
+ ### Claude Desktop
34
+
35
+ `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "coinvello": {
41
+ "command": "npx",
42
+ "args": ["-y", "https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz"],
43
+ "env": { "COINVELLO_API_KEY": "cv_test_..." }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Claude Code
50
+
51
+ ```bash
52
+ claude mcp add coinvello -e COINVELLO_API_KEY=cv_test_... -- npx -y https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz
53
+ ```
54
+
55
+ or in `.mcp.json` at the project root (shared with the team, key via env):
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "coinvello": {
61
+ "command": "npx",
62
+ "args": ["-y", "https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz"],
63
+ "env": { "COINVELLO_API_KEY": "${COINVELLO_API_KEY}" }
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### Cursor
70
+
71
+ `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "coinvello": {
77
+ "command": "npx",
78
+ "args": ["-y", "https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz"],
79
+ "env": { "COINVELLO_API_KEY": "cv_test_..." }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Windsurf
86
+
87
+ `~/.codeium/windsurf/mcp_config.json`:
88
+
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "coinvello": {
93
+ "command": "npx",
94
+ "args": ["-y", "https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz"],
95
+ "env": { "COINVELLO_API_KEY": "cv_test_..." }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### VS Code (Copilot agent mode)
102
+
103
+ `.vscode/mcp.json`:
104
+
105
+ ```json
106
+ {
107
+ "servers": {
108
+ "coinvello": { "type": "stdio", "command": "npx", "args": ["-y", "https://coinvello.com/downloads/sdk/coinvello-mcp-0.1.0.tgz"], "env": { "COINVELLO_API_KEY": "cv_test_..." } }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Remote / HTTP clients (ChatGPT connectors, hosted agents)
114
+
115
+ Run `coinvello-mcp --http 3333` behind your own HTTPS reverse proxy and point the client at
116
+ `https://your-host/mcp`. The HTTP mode is stateless (one server instance per request) and binds to
117
+ `127.0.0.1` by default (`MCP_HTTP_HOST=0.0.0.0` to change). It uses the key from the environment,
118
+ so put it behind authentication: it is meant for your own agents, not the public.
119
+
120
+ ## Tools
121
+
122
+ | Tool | Does |
123
+ |------|------|
124
+ | `create_checkout_session` | `POST /checkout-sessions`: hosted payment page; returns `url` to redirect to and `invoice_id` |
125
+ | `get_invoice` | `GET /invoices/{id}` (or `GET /checkout-sessions/{cs_id}?expand=invoice`) |
126
+ | `list_invoices` | `GET /invoices` with status / order_id / email filters |
127
+ | `list_payments` | `GET /payments` with invoice_id / status / chain / tx_hash filters |
128
+ | `create_wallet` | `POST /wallets` (xpub for BTC, address for EVM/Solana/Tron). Refuses xprv locally. |
129
+ | `list_wallets` | `GET /wallets` |
130
+ | `create_webhook` | `POST /webhooks`; returns the `whsec_` secret once |
131
+ | `verify_webhook_signature` | Local HMAC check of a `CoinVello-Signature` header against a raw body (no API call) |
132
+ | `get_credits_balance` | `GET /credits/balance` |
133
+ | `list_credit_bundles` | `GET /credits/bundles` |
134
+ | `create_credit_purchase` | `POST /credits/purchases`; returns payment options + `checkout_url` |
135
+ | `get_rates` | `GET /rates` |
136
+ | `list_assets` | `GET /assets` (optional chain filter) |
137
+ | `simulate_test_payment` | `POST /test/simulate_payment` (test keys only) |
138
+ | `list_records` | `GET /records`: the book of record (payments, fees, credit purchases) valued when they happened; filters type / asset / chain / invoice / order / tx / dates / currency |
139
+ | `get_records_summary` | `GET /records/summary`: sales, market value received, refunds, fees, net received for a period, optionally grouped |
140
+ | `create_export` | `POST /exports`: CSV, Excel or JSON file of records or any dataset (QuickBooks, Xero, Koinly, CoinTracker, CoinLedger presets); returns the export with `download_url` |
141
+ | `get_export` | `GET /exports/{id}`: poll status until `ready`, read `file.sha256` and `download_url` |
142
+
143
+ Every tool returns the raw API JSON (also as `structuredContent`). API errors come back as tool errors
144
+ containing `http_status`, the CoinVello `error` envelope (`type`, `code`, `message`, `request_id`) and a `hint`.
145
+
146
+ ## Resources
147
+
148
+ | URI | Content |
149
+ |-----|---------|
150
+ | `coinvello://docs/llms-full.txt` | Complete single-file integration guide (auth, checkout, redirect, webhook verification, polling, credits, errors, examples in curl/Node/Python/PHP) |
151
+ | `coinvello://docs/llms.txt` | Short llms.txt index |
152
+ | `coinvello://openapi.yaml` | OpenAPI 3.1 specification |
153
+
154
+ Documents are bundled at build time; if missing they are fetched from coinvello.com.
155
+
156
+ ## Prompts
157
+
158
+ `integrate-coinvello-into-<framework>` for `nextjs`, `express`, `fastapi`, `flask`, `django`,
159
+ `laravel`, `rails`, `sinatra`, `go`, `woocommerce`, `shopify`, `generic`, plus
160
+ `integrate-coinvello-into-framework` with a free-form `framework` argument. Optional arguments:
161
+ `currency`, `assets`. Each prompt tells the agent to read the guide resource, add a checkout route, a
162
+ raw-body webhook route with signature verification and `event.id` dedupe, and to verify end to end
163
+ with `simulate_test_payment`.
164
+
165
+ ## Typical agent session
166
+
167
+ 1. `list_assets` -> pick assets the merchant can accept.
168
+ 2. `create_webhook` with the app's `/webhooks/coinvello` URL -> store the secret in the env file.
169
+ 3. Write the code (see the `integrate-coinvello-into-*` prompt or `api/ai/cookbook`).
170
+ 4. `create_checkout_session` -> `simulate_test_payment` on `invoice_id` with enough confirmations.
171
+ 5. `get_invoice` shows `paid`; the app's webhook handler was called with a real signed event.
172
+ 6. `verify_webhook_signature` to debug a handler that rejects deliveries.
173
+ 7. Bookkeeping: `list_records` (with `include_simulated: true` in test mode) -> `get_records_summary` for a period -> `create_export` (`format: "xlsx"`) -> `get_export` until `ready` -> download the `download_url` and check `file.sha256`.
174
+
175
+ ## Development
176
+
177
+ ```bash
178
+ npm install
179
+ npm run build # copies docs into dist/docs and compiles TypeScript
180
+ npm test # smoke test against the mock: needs api/reference-server running on :4242
181
+ npm run dev # tsx src/index.ts (stdio)
182
+ ```
183
+
184
+ Smoke test: `cd api/reference-server && npm install && npm start`, then
185
+ `COINVELLO_API_BASE=http://localhost:4242/v1 npm test` here. It exercises all 18 tools, the three
186
+ resources and the prompts, verifies a real signed delivery captured by the mock's webhook sink, and
187
+ checks the xprv guard. The mock does not implement wallet CRUD, so `create_wallet` / `list_wallets`
188
+ are expected to return a clean 404 tool error there.
189
+
190
+ ## Security notes
191
+
192
+ - Keep the server on test keys while an agent is writing code. `COINVELLO_ALLOW_LIVE=1` is a deliberate opt-in.
193
+ - The server never logs the API key or webhook secrets. Tool output does include `secret` from `create_webhook` because that is the only time the API returns it.
194
+ - `create_wallet` rejects anything that looks like an extended private key before it leaves the machine.
195
+ - Nothing here moves funds: CoinVello is non-custodial, and the API has no "send" operation.
196
+
197
+ License MIT. Issues: support@coinvello.com
@@ -0,0 +1,32 @@
1
+ export interface CoinVelloClientOptions {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ apiVersion?: string;
5
+ timeoutMs?: number;
6
+ }
7
+ export interface CoinVelloErrorBody {
8
+ type: string;
9
+ code: string;
10
+ message: string;
11
+ param?: string;
12
+ request_id?: string;
13
+ doc_url?: string;
14
+ details?: Record<string, unknown>;
15
+ }
16
+ export declare class CoinVelloApiError extends Error {
17
+ readonly status: number;
18
+ readonly error: CoinVelloErrorBody;
19
+ constructor(status: number, error: CoinVelloErrorBody);
20
+ }
21
+ export type Query = Record<string, string | number | boolean | undefined | null>;
22
+ export declare class CoinVelloClient {
23
+ readonly baseUrl: string;
24
+ readonly livemode: boolean;
25
+ private readonly apiKey;
26
+ private readonly apiVersion?;
27
+ private readonly timeoutMs;
28
+ constructor(opts: CoinVelloClientOptions);
29
+ get<T = unknown>(path: string, query?: Query): Promise<T>;
30
+ post<T = unknown>(path: string, body?: unknown, idempotencyKey?: string): Promise<T>;
31
+ request<T>(method: string, path: string, body?: unknown, query?: Query, idempotencyKey?: string): Promise<T>;
32
+ }
package/dist/client.js ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Minimal HTTP client for the CoinVello REST API used by the MCP tools.
3
+ * No SDK dependency so the server stays small and `npx -y @coinvello/mcp` starts fast.
4
+ */
5
+ import { randomUUID } from "node:crypto";
6
+ export class CoinVelloApiError extends Error {
7
+ status;
8
+ error;
9
+ constructor(status, error) {
10
+ super(`${status} ${error.type}/${error.code}: ${error.message}`);
11
+ this.status = status;
12
+ this.error = error;
13
+ this.name = "CoinVelloApiError";
14
+ }
15
+ }
16
+ export class CoinVelloClient {
17
+ baseUrl;
18
+ livemode;
19
+ apiKey;
20
+ apiVersion;
21
+ timeoutMs;
22
+ constructor(opts) {
23
+ this.apiKey = opts.apiKey;
24
+ this.baseUrl = (opts.baseUrl ?? "https://api.coinvello.com/v1").replace(/\/+$/, "");
25
+ this.apiVersion = opts.apiVersion;
26
+ this.timeoutMs = opts.timeoutMs ?? 20_000;
27
+ this.livemode = /^cv_live_/.test(opts.apiKey);
28
+ }
29
+ async get(path, query) {
30
+ return this.request("GET", path, undefined, query);
31
+ }
32
+ async post(path, body, idempotencyKey) {
33
+ return this.request("POST", path, body, undefined, idempotencyKey ?? randomUUID());
34
+ }
35
+ async request(method, path, body, query, idempotencyKey) {
36
+ const url = new URL(this.baseUrl + path);
37
+ for (const [k, v] of Object.entries(query ?? {})) {
38
+ if (v !== undefined && v !== null && v !== "")
39
+ url.searchParams.set(k, String(v));
40
+ }
41
+ const headers = {
42
+ Authorization: `Bearer ${this.apiKey}`,
43
+ Accept: "application/json",
44
+ "User-Agent": "coinvello-mcp/0.1.0",
45
+ };
46
+ if (body !== undefined)
47
+ headers["Content-Type"] = "application/json";
48
+ if (idempotencyKey)
49
+ headers["Idempotency-Key"] = idempotencyKey;
50
+ if (this.apiVersion)
51
+ headers["CoinVello-Version"] = this.apiVersion;
52
+ const controller = new AbortController();
53
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
54
+ let res;
55
+ try {
56
+ res = await fetch(url, {
57
+ method,
58
+ headers,
59
+ body: body === undefined ? undefined : JSON.stringify(body),
60
+ signal: controller.signal,
61
+ });
62
+ }
63
+ finally {
64
+ clearTimeout(timer);
65
+ }
66
+ const text = await res.text();
67
+ let json = null;
68
+ if (text) {
69
+ try {
70
+ json = JSON.parse(text);
71
+ }
72
+ catch {
73
+ json = { raw: text };
74
+ }
75
+ }
76
+ if (!res.ok) {
77
+ const err = json?.error ?? {
78
+ type: "api_error",
79
+ code: `http_${res.status}`,
80
+ message: text || res.statusText,
81
+ };
82
+ throw new CoinVelloApiError(res.status, err);
83
+ }
84
+ return json;
85
+ }
86
+ }