defade 0.1.0 → 0.2.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
@@ -93,6 +93,24 @@ claude mcp add --transport http defade "https://api.defade.org/mcp?api_key=YOUR_
93
93
 
94
94
  In **Claude** or **ChatGPT**, add a custom connector with the URL above. Tool calls are metered against your key exactly like REST calls. Full instructions: [defade.org/api-docs#mcp](https://defade.org/api-docs#mcp).
95
95
 
96
+ ### stdio transport (Claude Desktop, local clients)
97
+
98
+ This package also ships `defade-mcp`, a local stdio MCP server that proxies to the hosted endpoint — for clients that only launch local commands:
99
+
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "defade": {
104
+ "command": "npx",
105
+ "args": ["-y", "defade-mcp"],
106
+ "env": { "DEFADE_API_KEY": "df_your_key" }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ Keyless runs still handshake and list tools; scan tools then reply with instructions to get a key. `DEFADE_MCP_URL` overrides the upstream endpoint for testing.
113
+
96
114
  ## Links
97
115
 
98
116
  - [API docs](https://defade.org/api-docs) · [get a key](https://defade.org/developers) · [live API self-description](https://api.defade.org/v1)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Local MCP server (stdio transport) proxying to the hosted DeFade MCP
5
+ // endpoint. For clients that only speak stdio — Claude Desktop config,
6
+ // `claude mcp add defade -- npx -y defade-mcp`, inspectors — while the
7
+ // remote streamable-http endpoint stays the single implementation.
8
+ //
9
+ // The remote server is stateless and answers plain JSON per POST, so the
10
+ // proxy is one HTTP round trip per JSON-RPC message: read a line from
11
+ // stdin, POST it, write the response line to stdout. Notifications (no id)
12
+ // are forwarded and produce no output line, per JSON-RPC.
13
+ //
14
+ // Auth: DEFADE_API_KEY env var or --api-key=df_… argument. Keyless runs
15
+ // still handshake and list tools (directory health checks rely on that);
16
+ // scan tools then answer with instructions to get a key.
17
+
18
+ const ENDPOINT = process.env.DEFADE_MCP_URL || 'https://api.defade.org/mcp';
19
+
20
+ let apiKey = process.env.DEFADE_API_KEY || '';
21
+ for (const a of process.argv.slice(2)) {
22
+ const m = a.match(/^--api-key=(.+)$/);
23
+ if (m) apiKey = m[1];
24
+ if (a === '--help' || a === '-h') {
25
+ process.stderr.write(
26
+ 'defade-mcp — stdio MCP server proxying to ' + ENDPOINT + '\n' +
27
+ 'usage: DEFADE_API_KEY=df_… defade-mcp (or --api-key=df_…)\n' +
28
+ 'Get a key at https://defade.org/developers — docs at https://defade.org/api-docs#mcp\n');
29
+ process.exit(0);
30
+ }
31
+ }
32
+
33
+ async function forward(msg) {
34
+ const headers = { 'content-type': 'application/json', accept: 'application/json' };
35
+ if (apiKey) headers['x-api-key'] = apiKey;
36
+ const res = await fetch(ENDPOINT, { method: 'POST', headers, body: JSON.stringify(msg) });
37
+ const text = await res.text();
38
+ if (!text) return null; // accepted notification
39
+ try { return JSON.parse(text); } catch {
40
+ return { jsonrpc: '2.0', id: msg.id ?? null, error: { code: -32603, message: `upstream sent non-JSON (HTTP ${res.status})` } };
41
+ }
42
+ }
43
+
44
+ function reply(obj) {
45
+ if (obj != null) process.stdout.write(JSON.stringify(obj) + '\n');
46
+ }
47
+
48
+ let buffer = '';
49
+ let pending = 0;
50
+ let stdinDone = false;
51
+ function maybeExit() { if (stdinDone && pending === 0) process.exit(0); }
52
+ process.stdin.setEncoding('utf8');
53
+ process.stdin.on('data', (chunk) => {
54
+ buffer += chunk;
55
+ let nl;
56
+ while ((nl = buffer.indexOf('\n')) !== -1) {
57
+ const line = buffer.slice(0, nl).trim();
58
+ buffer = buffer.slice(nl + 1);
59
+ if (!line) continue;
60
+ let msg;
61
+ try { msg = JSON.parse(line); } catch {
62
+ reply({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'parse error' } });
63
+ continue;
64
+ }
65
+ const isNotification = msg.id === undefined || msg.id === null;
66
+ pending++;
67
+ forward(msg)
68
+ .then((res) => { if (!isNotification) reply(res); })
69
+ .catch((e) => {
70
+ if (!isNotification) {
71
+ reply({ jsonrpc: '2.0', id: msg.id, error: { code: -32603, message: `upstream unreachable: ${e.message}` } });
72
+ }
73
+ })
74
+ .finally(() => { pending--; maybeExit(); });
75
+ }
76
+ });
77
+ // A closed stdin means the client is done SENDING — replies for requests
78
+ // already in flight still have to go out before the process may exit.
79
+ process.stdin.on('end', () => { stdinDone = true; maybeExit(); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "defade",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official SDK for the DeFade API — rug pull risk scores and on-chain forensics (bundles, funding traces, deployer history) for tokens on Solana, Ethereum, Base and Robinhood Chain.",
5
5
  "license": "MIT",
6
6
  "author": "DeFade Ltd (https://defade.org)",
@@ -12,6 +12,9 @@
12
12
  "bugs": {
13
13
  "url": "https://github.com/DeFadeLtd/defade-sdk/issues"
14
14
  },
15
+ "bin": {
16
+ "defade-mcp": "./bin/defade-mcp.js"
17
+ },
15
18
  "main": "./index.cjs",
16
19
  "types": "./index.d.ts",
17
20
  "exports": {
@@ -22,6 +25,7 @@
22
25
  }
23
26
  },
24
27
  "files": [
28
+ "bin/defade-mcp.js",
25
29
  "index.cjs",
26
30
  "index.mjs",
27
31
  "index.d.ts",
@@ -32,7 +36,7 @@
32
36
  "node": ">=18"
33
37
  },
34
38
  "scripts": {
35
- "test": "node --test test/client.test.js"
39
+ "test": "node --test test/client.test.js test/mcp-proxy.test.js"
36
40
  },
37
41
  "keywords": [
38
42
  "defade",