@coinvello/mcp 0.0.0-stage → 0.1.1
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 +194 -2
- package/dist/client.d.ts +32 -0
- package/dist/client.js +86 -0
- package/dist/docs/llms-full.txt +792 -0
- package/dist/docs/llms.txt +56 -0
- package/dist/docs/openapi.yaml +8045 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.js +442 -0
- package/dist/verify.d.ts +17 -0
- package/dist/verify.js +58 -0
- package/package.json +57 -4
package/README.md
CHANGED
|
@@ -1,3 +1,195 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @coinvello/mcp
|
|
2
2
|
|
|
3
|
-
|
|
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 @coinvello/mcp # stdio (what every desktop client uses)
|
|
12
|
+
npx -y @coinvello/mcp --http 3333 # Streamable HTTP on http://127.0.0.1:3333/mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# from a checkout of this repo instead
|
|
17
|
+
npm install && npm run build && node dist/index.js
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
| Variable | Required | Meaning |
|
|
23
|
+
|----------|----------|---------|
|
|
24
|
+
| `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. |
|
|
25
|
+
| `COINVELLO_API_BASE` | no | Default `https://api.coinvello.com/v1`. Use `http://localhost:4242/v1` for the reference mock server. |
|
|
26
|
+
| `COINVELLO_ALLOW_LIVE` | no | The server **refuses to start with a `cv_live_` key** unless this is `1`. Agents should develop against test mode. |
|
|
27
|
+
| `COINVELLO_API_VERSION` | no | Pins `CoinVello-Version` (default: account version, currently `2026-10-01`). |
|
|
28
|
+
|
|
29
|
+
## Client setup
|
|
30
|
+
|
|
31
|
+
### Claude Desktop
|
|
32
|
+
|
|
33
|
+
`claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"coinvello": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@coinvello/mcp"],
|
|
41
|
+
"env": { "COINVELLO_API_KEY": "cv_test_..." }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Claude Code
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
claude mcp add coinvello -e COINVELLO_API_KEY=cv_test_... -- npx -y @coinvello/mcp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
or in `.mcp.json` at the project root (shared with the team, key via env):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"coinvello": {
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["-y", "@coinvello/mcp"],
|
|
61
|
+
"env": { "COINVELLO_API_KEY": "${COINVELLO_API_KEY}" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Cursor
|
|
68
|
+
|
|
69
|
+
`.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"coinvello": {
|
|
75
|
+
"command": "npx",
|
|
76
|
+
"args": ["-y", "@coinvello/mcp"],
|
|
77
|
+
"env": { "COINVELLO_API_KEY": "cv_test_..." }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Windsurf
|
|
84
|
+
|
|
85
|
+
`~/.codeium/windsurf/mcp_config.json`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"coinvello": {
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "@coinvello/mcp"],
|
|
93
|
+
"env": { "COINVELLO_API_KEY": "cv_test_..." }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### VS Code (Copilot agent mode)
|
|
100
|
+
|
|
101
|
+
`.vscode/mcp.json`:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"servers": {
|
|
106
|
+
"coinvello": { "type": "stdio", "command": "npx", "args": ["-y", "@coinvello/mcp"], "env": { "COINVELLO_API_KEY": "cv_test_..." } }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Remote / HTTP clients (ChatGPT connectors, hosted agents)
|
|
112
|
+
|
|
113
|
+
Run `coinvello-mcp --http 3333` behind your own HTTPS reverse proxy and point the client at
|
|
114
|
+
`https://your-host/mcp`. The HTTP mode is stateless (one server instance per request) and binds to
|
|
115
|
+
`127.0.0.1` by default (`MCP_HTTP_HOST=0.0.0.0` to change). It uses the key from the environment,
|
|
116
|
+
so put it behind authentication: it is meant for your own agents, not the public.
|
|
117
|
+
|
|
118
|
+
## Tools
|
|
119
|
+
|
|
120
|
+
| Tool | Does |
|
|
121
|
+
|------|------|
|
|
122
|
+
| `create_checkout_session` | `POST /checkout-sessions`: hosted payment page; returns `url` to redirect to and `invoice_id` |
|
|
123
|
+
| `get_invoice` | `GET /invoices/{id}` (or `GET /checkout-sessions/{cs_id}?expand=invoice`) |
|
|
124
|
+
| `list_invoices` | `GET /invoices` with status / order_id / email filters |
|
|
125
|
+
| `list_payments` | `GET /payments` with invoice_id / status / chain / tx_hash filters |
|
|
126
|
+
| `create_wallet` | `POST /wallets` (xpub for BTC, address for EVM/Solana/Tron). Refuses xprv locally. |
|
|
127
|
+
| `list_wallets` | `GET /wallets` |
|
|
128
|
+
| `create_webhook` | `POST /webhooks`; returns the `whsec_` secret once |
|
|
129
|
+
| `verify_webhook_signature` | Local HMAC check of a `CoinVello-Signature` header against a raw body (no API call) |
|
|
130
|
+
| `get_credits_balance` | `GET /credits/balance` |
|
|
131
|
+
| `list_credit_bundles` | `GET /credits/bundles` |
|
|
132
|
+
| `create_credit_purchase` | `POST /credits/purchases`; returns payment options + `checkout_url` |
|
|
133
|
+
| `get_rates` | `GET /rates` |
|
|
134
|
+
| `list_assets` | `GET /assets` (optional chain filter) |
|
|
135
|
+
| `simulate_test_payment` | `POST /test/simulate_payment` (test keys only) |
|
|
136
|
+
| `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 |
|
|
137
|
+
| `get_records_summary` | `GET /records/summary`: sales, market value received, refunds, fees, net received for a period, optionally grouped |
|
|
138
|
+
| `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` |
|
|
139
|
+
| `get_export` | `GET /exports/{id}`: poll status until `ready`, read `file.sha256` and `download_url` |
|
|
140
|
+
|
|
141
|
+
Every tool returns the raw API JSON (also as `structuredContent`). API errors come back as tool errors
|
|
142
|
+
containing `http_status`, the CoinVello `error` envelope (`type`, `code`, `message`, `request_id`) and a `hint`.
|
|
143
|
+
|
|
144
|
+
## Resources
|
|
145
|
+
|
|
146
|
+
| URI | Content |
|
|
147
|
+
|-----|---------|
|
|
148
|
+
| `coinvello://docs/llms-full.txt` | Complete single-file integration guide (auth, checkout, redirect, webhook verification, polling, credits, errors, examples in curl/Node/Python/PHP) |
|
|
149
|
+
| `coinvello://docs/llms.txt` | Short llms.txt index |
|
|
150
|
+
| `coinvello://openapi.yaml` | OpenAPI 3.1 specification |
|
|
151
|
+
|
|
152
|
+
Documents are bundled at build time; if missing they are fetched from coinvello.com.
|
|
153
|
+
|
|
154
|
+
## Prompts
|
|
155
|
+
|
|
156
|
+
`integrate-coinvello-into-<framework>` for `nextjs`, `express`, `fastapi`, `flask`, `django`,
|
|
157
|
+
`laravel`, `rails`, `sinatra`, `go`, `woocommerce`, `shopify`, `generic`, plus
|
|
158
|
+
`integrate-coinvello-into-framework` with a free-form `framework` argument. Optional arguments:
|
|
159
|
+
`currency`, `assets`. Each prompt tells the agent to read the guide resource, add a checkout route, a
|
|
160
|
+
raw-body webhook route with signature verification and `event.id` dedupe, and to verify end to end
|
|
161
|
+
with `simulate_test_payment`.
|
|
162
|
+
|
|
163
|
+
## Typical agent session
|
|
164
|
+
|
|
165
|
+
1. `list_assets` -> pick assets the merchant can accept.
|
|
166
|
+
2. `create_webhook` with the app's `/webhooks/coinvello` URL -> store the secret in the env file.
|
|
167
|
+
3. Write the code (see the `integrate-coinvello-into-*` prompt or `api/ai/cookbook`).
|
|
168
|
+
4. `create_checkout_session` -> `simulate_test_payment` on `invoice_id` with enough confirmations.
|
|
169
|
+
5. `get_invoice` shows `paid`; the app's webhook handler was called with a real signed event.
|
|
170
|
+
6. `verify_webhook_signature` to debug a handler that rejects deliveries.
|
|
171
|
+
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`.
|
|
172
|
+
|
|
173
|
+
## Development
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install
|
|
177
|
+
npm run build # copies docs into dist/docs and compiles TypeScript
|
|
178
|
+
npm test # smoke test against the mock: needs api/reference-server running on :4242
|
|
179
|
+
npm run dev # tsx src/index.ts (stdio)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Smoke test: `cd api/reference-server && npm install && npm start`, then
|
|
183
|
+
`COINVELLO_API_BASE=http://localhost:4242/v1 npm test` here. It exercises all 18 tools, the three
|
|
184
|
+
resources and the prompts, verifies a real signed delivery captured by the mock's webhook sink, and
|
|
185
|
+
checks the xprv guard. The mock does not implement wallet CRUD, so `create_wallet` / `list_wallets`
|
|
186
|
+
are expected to return a clean 404 tool error there.
|
|
187
|
+
|
|
188
|
+
## Security notes
|
|
189
|
+
|
|
190
|
+
- Keep the server on test keys while an agent is writing code. `COINVELLO_ALLOW_LIVE=1` is a deliberate opt-in.
|
|
191
|
+
- 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.
|
|
192
|
+
- `create_wallet` rejects anything that looks like an extended private key before it leaves the machine.
|
|
193
|
+
- Nothing here moves funds: CoinVello is non-custodial, and the API has no "send" operation.
|
|
194
|
+
|
|
195
|
+
License MIT. Issues: support@coinvello.com
|
package/dist/client.d.ts
ADDED
|
@@ -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.1",
|
|
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
|
+
}
|