drain-mcp 0.2.0 → 0.2.2
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 +262 -262
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/constants.d.ts +31 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +20 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +59 -77
- package/dist/index.js.map +1 -1
- package/dist/services/channel.d.ts +9 -0
- package/dist/services/channel.d.ts.map +1 -1
- package/dist/services/channel.js +41 -0
- package/dist/services/channel.js.map +1 -1
- package/dist/services/wallet.d.ts +1 -1
- package/dist/services/wallet.js +1 -1
- package/dist/tools/balance.d.ts.map +1 -1
- package/dist/tools/balance.js +47 -29
- package/dist/tools/balance.js.map +1 -1
- package/dist/tools/channel.d.ts +4 -3
- package/dist/tools/channel.d.ts.map +1 -1
- package/dist/tools/channel.js +117 -81
- package/dist/tools/channel.js.map +1 -1
- package/dist/tools/chat.js +15 -15
- package/dist/tools/providers.js +27 -27
- package/package.json +50 -50
package/dist/tools/channel.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Channel Tools
|
|
3
3
|
*/
|
|
4
|
-
import { SESSION_FEE, USDC_DECIMALS } from '../constants.js';
|
|
5
|
-
import { formatUnits } from 'viem';
|
|
6
4
|
function parseDuration(duration) {
|
|
7
5
|
const match = duration.match(/^(\d+)(s|m|h|d)$/);
|
|
8
6
|
if (!match) {
|
|
@@ -21,7 +19,7 @@ function parseDuration(duration) {
|
|
|
21
19
|
default: throw new Error(`Unknown duration unit: ${unit}`);
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
|
-
export async function openChannel(channelService, providerService,
|
|
22
|
+
export async function openChannel(channelService, providerService, args) {
|
|
25
23
|
let providerAddress;
|
|
26
24
|
let providerName;
|
|
27
25
|
let resolvedProvider = null;
|
|
@@ -45,15 +43,6 @@ export async function openChannel(channelService, providerService, walletService
|
|
|
45
43
|
resolvedProvider = provider;
|
|
46
44
|
}
|
|
47
45
|
const durationSeconds = parseDuration(args.duration);
|
|
48
|
-
let feeTxHash = null;
|
|
49
|
-
if (feeWallet) {
|
|
50
|
-
try {
|
|
51
|
-
feeTxHash = await walletService.transferUsdc(feeWallet, SESSION_FEE);
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
console.error('Session fee payment failed:', err instanceof Error ? err.message : err);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
46
|
const result = await channelService.openChannel(providerAddress, args.amount, durationSeconds);
|
|
58
47
|
if (resolvedProvider) {
|
|
59
48
|
channelService.setChannelMeta(result.channelId, {
|
|
@@ -67,12 +56,6 @@ export async function openChannel(channelService, providerService, walletService
|
|
|
67
56
|
}
|
|
68
57
|
const expiryDate = result.channel.expiry.toISOString();
|
|
69
58
|
const hours = Math.floor(durationSeconds / 3600);
|
|
70
|
-
const feeAmount = formatUnits(SESSION_FEE, USDC_DECIMALS);
|
|
71
|
-
const feeSection = feeTxHash
|
|
72
|
-
? `- **Session Fee:** $${feeAmount} USDC (tx: \`${feeTxHash}\`)`
|
|
73
|
-
: feeWallet
|
|
74
|
-
? `- **Session Fee:** ⚠️ Payment failed (channel opened anyway)`
|
|
75
|
-
: '';
|
|
76
59
|
let docsSection = '';
|
|
77
60
|
if (resolvedProvider) {
|
|
78
61
|
const docs = await providerService.fetchDocs(resolvedProvider);
|
|
@@ -84,33 +67,66 @@ export async function openChannel(channelService, providerService, walletService
|
|
|
84
67
|
docsSection = `\n## Provider Documentation\nBefore sending requests, read the provider docs: ${docsUrl}\n`;
|
|
85
68
|
}
|
|
86
69
|
}
|
|
87
|
-
return `# Channel Opened
|
|
88
|
-
|
|
89
|
-
**Channel ID:** \`${result.channelId}\`
|
|
90
|
-
**Transaction:** \`${result.txHash}\`
|
|
91
|
-
|
|
92
|
-
## Details
|
|
93
|
-
- **Provider:** ${providerName} (${resolvedProvider?.category || 'llm'})
|
|
94
|
-
- **Deposit:** $${args.amount} USDC
|
|
95
|
-
|
|
96
|
-
- **Expires:** ${expiryDate}
|
|
97
|
-
${docsSection}
|
|
98
|
-
## Next Steps
|
|
99
|
-
Use \`drain_chat\` to send requests through this channel.
|
|
100
|
-
|
|
70
|
+
return `# Channel Opened
|
|
71
|
+
|
|
72
|
+
**Channel ID:** \`${result.channelId}\`
|
|
73
|
+
**Transaction:** \`${result.txHash}\`
|
|
74
|
+
|
|
75
|
+
## Details
|
|
76
|
+
- **Provider:** ${providerName} (${resolvedProvider?.category || 'llm'})
|
|
77
|
+
- **Deposit:** $${args.amount} USDC
|
|
78
|
+
- **Duration:** ${hours} hours
|
|
79
|
+
- **Expires:** ${expiryDate}
|
|
80
|
+
${docsSection}
|
|
81
|
+
## Next Steps
|
|
82
|
+
Use \`drain_chat\` to send requests through this channel.
|
|
83
|
+
|
|
101
84
|
**FUND RECOVERY:** After expiry (${expiryDate}), call \`drain_close_channel\` to reclaim unspent USDC. Funds do NOT return automatically. If you have cron/scheduling, set a timer for the expiry time.`;
|
|
102
85
|
}
|
|
103
86
|
export async function closeChannel(channelService, args) {
|
|
104
87
|
const channelId = args.channelId;
|
|
105
88
|
const result = await channelService.closeChannel(channelId);
|
|
106
|
-
return `# Channel Closed
|
|
107
|
-
|
|
108
|
-
**Channel ID:** \`${channelId}\`
|
|
109
|
-
**Transaction:** \`${result.txHash}\`
|
|
110
|
-
**Refunded:** $${result.refundAmount} USDC
|
|
111
|
-
|
|
89
|
+
return `# Channel Closed
|
|
90
|
+
|
|
91
|
+
**Channel ID:** \`${channelId}\`
|
|
92
|
+
**Transaction:** \`${result.txHash}\`
|
|
93
|
+
**Refunded:** $${result.refundAmount} USDC
|
|
94
|
+
|
|
112
95
|
The remaining balance has been returned to your wallet.`;
|
|
113
96
|
}
|
|
97
|
+
export async function cooperativeClose(channelService, providerService, args) {
|
|
98
|
+
const channelId = args.channelId;
|
|
99
|
+
const meta = channelService.getChannelMeta(channelId);
|
|
100
|
+
let providerUrl;
|
|
101
|
+
if (meta?.providerId) {
|
|
102
|
+
const provider = await providerService.getProvider(meta.providerId);
|
|
103
|
+
if (provider)
|
|
104
|
+
providerUrl = provider.apiUrl;
|
|
105
|
+
}
|
|
106
|
+
if (!providerUrl) {
|
|
107
|
+
const channel = await channelService.getChannel(channelId);
|
|
108
|
+
const allProviders = await providerService.getProviders();
|
|
109
|
+
const matched = allProviders.find(p => p.providerAddress.toLowerCase() === channel.provider.toLowerCase());
|
|
110
|
+
if (matched)
|
|
111
|
+
providerUrl = matched.apiUrl;
|
|
112
|
+
}
|
|
113
|
+
if (!providerUrl) {
|
|
114
|
+
throw new Error('Could not resolve provider API URL for this channel. ' +
|
|
115
|
+
'The channel may have been opened outside this session.');
|
|
116
|
+
}
|
|
117
|
+
const result = await channelService.cooperativeCloseChannel(channelId, providerUrl);
|
|
118
|
+
return `# Channel Cooperatively Closed
|
|
119
|
+
|
|
120
|
+
**Channel ID:** \`${channelId}\`
|
|
121
|
+
**Transaction:** \`${result.txHash}\`
|
|
122
|
+
|
|
123
|
+
## Settlement
|
|
124
|
+
- **Provider Payout:** $${result.payout} USDC
|
|
125
|
+
- **Protocol Fee (2%):** $${result.fee} USDC
|
|
126
|
+
- **Refunded to You:** $${result.refundAmount} USDC
|
|
127
|
+
|
|
128
|
+
Channel closed immediately without waiting for expiry.`;
|
|
129
|
+
}
|
|
114
130
|
export async function getChannelStatus(channelService, args) {
|
|
115
131
|
const channelId = args.channelId;
|
|
116
132
|
const channel = await channelService.getChannel(channelId);
|
|
@@ -118,21 +134,21 @@ export async function getChannelStatus(channelService, args) {
|
|
|
118
134
|
const hoursRemaining = Math.floor(channel.secondsRemaining / 3600);
|
|
119
135
|
const minutesRemaining = Math.floor((channel.secondsRemaining % 3600) / 60);
|
|
120
136
|
const statusLabel = channel.isExpired ? 'EXPIRED' : 'ACTIVE';
|
|
121
|
-
return `# Channel Status
|
|
122
|
-
|
|
123
|
-
**Channel ID:** \`${channel.id}\`
|
|
124
|
-
**Status:** ${statusLabel}
|
|
125
|
-
|
|
126
|
-
## Balances
|
|
127
|
-
- **Deposit:** $${channel.deposit} USDC
|
|
128
|
-
- **Claimed by Provider:** $${channel.claimed} USDC
|
|
129
|
-
- **Remaining:** $${channel.remaining} USDC
|
|
130
|
-
- **Local Spending (this session):** $${localSpending} USDC
|
|
131
|
-
|
|
132
|
-
## Timing
|
|
133
|
-
- **Expires:** ${channel.expiry.toISOString()}
|
|
134
|
-
- **Time Remaining:** ${channel.isExpired ? 'EXPIRED' : `${hoursRemaining}h ${minutesRemaining}m`}
|
|
135
|
-
|
|
137
|
+
return `# Channel Status
|
|
138
|
+
|
|
139
|
+
**Channel ID:** \`${channel.id}\`
|
|
140
|
+
**Status:** ${statusLabel}
|
|
141
|
+
|
|
142
|
+
## Balances
|
|
143
|
+
- **Deposit:** $${channel.deposit} USDC
|
|
144
|
+
- **Claimed by Provider:** $${channel.claimed} USDC
|
|
145
|
+
- **Remaining:** $${channel.remaining} USDC
|
|
146
|
+
- **Local Spending (this session):** $${localSpending} USDC
|
|
147
|
+
|
|
148
|
+
## Timing
|
|
149
|
+
- **Expires:** ${channel.expiry.toISOString()}
|
|
150
|
+
- **Time Remaining:** ${channel.isExpired ? 'EXPIRED' : `${hoursRemaining}h ${minutesRemaining}m`}
|
|
151
|
+
|
|
136
152
|
${channel.isExpired ? 'Channel has expired. Call drain_close_channel to reclaim remaining funds.' : ''}`;
|
|
137
153
|
}
|
|
138
154
|
export async function listChannels(channelService) {
|
|
@@ -166,17 +182,17 @@ export async function listChannels(channelService) {
|
|
|
166
182
|
export const channelTools = [
|
|
167
183
|
{
|
|
168
184
|
name: 'drain_open_channel',
|
|
169
|
-
description: `Open a payment channel with a provider. Deposits USDC into the smart contract.
|
|
170
|
-
|
|
171
|
-
Prerequisites:
|
|
172
|
-
1. Sufficient USDC (drain_balance to check)
|
|
173
|
-
2. USDC approved for contract (drain_approve, one-time)
|
|
174
|
-
3. Small amount of POL for gas (~$0.02)
|
|
175
|
-
|
|
176
|
-
The response includes the channel expiry time and provider usage instructions.
|
|
177
|
-
|
|
178
|
-
FUND RECOVERY: After expiry, you MUST call drain_close_channel to reclaim unspent USDC. Funds do NOT auto-return. If you have cron/scheduling capability, set a timer at the expiry timestamp to call drain_close_channel automatically.
|
|
179
|
-
|
|
185
|
+
description: `Open a payment channel with a provider. Deposits USDC into the smart contract.
|
|
186
|
+
|
|
187
|
+
Prerequisites:
|
|
188
|
+
1. Sufficient USDC (drain_balance to check)
|
|
189
|
+
2. USDC approved for contract (drain_approve, one-time)
|
|
190
|
+
3. Small amount of POL for gas (~$0.02)
|
|
191
|
+
|
|
192
|
+
The response includes the channel expiry time and provider usage instructions.
|
|
193
|
+
|
|
194
|
+
FUND RECOVERY: After expiry, you MUST call drain_close_channel to reclaim unspent USDC. Funds do NOT auto-return. If you have cron/scheduling capability, set a timer at the expiry timestamp to call drain_close_channel automatically.
|
|
195
|
+
|
|
180
196
|
You can have multiple channels to different providers simultaneously.`,
|
|
181
197
|
inputSchema: {
|
|
182
198
|
type: 'object',
|
|
@@ -199,12 +215,12 @@ You can have multiple channels to different providers simultaneously.`,
|
|
|
199
215
|
},
|
|
200
216
|
{
|
|
201
217
|
name: 'drain_close_channel',
|
|
202
|
-
description: `Close an expired channel and reclaim unspent USDC.
|
|
203
|
-
|
|
204
|
-
Only callable after the channel has expired. Returns remaining deposit to wallet.
|
|
205
|
-
|
|
206
|
-
Schedule this at channel-open time. If you have cron/timer capability, set it for the expiry timestamp from drain_open_channel. Unclosed expired channels = locked funds.
|
|
207
|
-
|
|
218
|
+
description: `Close an expired channel and reclaim unspent USDC.
|
|
219
|
+
|
|
220
|
+
Only callable after the channel has expired. Returns remaining deposit to wallet.
|
|
221
|
+
|
|
222
|
+
Schedule this at channel-open time. If you have cron/timer capability, set it for the expiry timestamp from drain_open_channel. Unclosed expired channels = locked funds.
|
|
223
|
+
|
|
208
224
|
If "Channel not expired" -> wait until expiry, then retry.`,
|
|
209
225
|
inputSchema: {
|
|
210
226
|
type: 'object',
|
|
@@ -219,13 +235,13 @@ If "Channel not expired" -> wait until expiry, then retry.`,
|
|
|
219
235
|
},
|
|
220
236
|
{
|
|
221
237
|
name: 'drain_channel_status',
|
|
222
|
-
description: `Check current status and balance of a payment channel.
|
|
223
|
-
|
|
224
|
-
Returns: deposit, spent, remaining balance, expiry time, and whether expired.
|
|
225
|
-
|
|
226
|
-
Use this to:
|
|
227
|
-
- Estimate remaining budget
|
|
228
|
-
- Check if channel needs closing (expired)
|
|
238
|
+
description: `Check current status and balance of a payment channel.
|
|
239
|
+
|
|
240
|
+
Returns: deposit, spent, remaining balance, expiry time, and whether expired.
|
|
241
|
+
|
|
242
|
+
Use this to:
|
|
243
|
+
- Estimate remaining budget
|
|
244
|
+
- Check if channel needs closing (expired)
|
|
229
245
|
- Decide whether to open a new channel (low balance)`,
|
|
230
246
|
inputSchema: {
|
|
231
247
|
type: 'object',
|
|
@@ -238,12 +254,32 @@ Use this to:
|
|
|
238
254
|
required: ['channelId'],
|
|
239
255
|
},
|
|
240
256
|
},
|
|
257
|
+
{
|
|
258
|
+
name: 'drain_cooperative_close',
|
|
259
|
+
description: `Close a payment channel IMMEDIATELY by requesting a cooperative close from the provider.
|
|
260
|
+
|
|
261
|
+
Unlike drain_close_channel (which requires waiting for expiry), this contacts the provider's API to get a close signature, then settles on-chain right away. The provider gets paid for actual usage, the 2% protocol fee is deducted, and the remaining deposit is refunded to your wallet.
|
|
262
|
+
|
|
263
|
+
Use this when you're done with a channel and want your funds back now.
|
|
264
|
+
|
|
265
|
+
Falls back to drain_close_channel if the provider is offline or refuses.`,
|
|
266
|
+
inputSchema: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
channelId: {
|
|
270
|
+
type: 'string',
|
|
271
|
+
description: 'The channel ID to close (0x...)',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
required: ['channelId'],
|
|
275
|
+
},
|
|
276
|
+
},
|
|
241
277
|
{
|
|
242
278
|
name: 'drain_channels',
|
|
243
|
-
description: `List all known payment channels with their current status.
|
|
244
|
-
|
|
245
|
-
Shows channels this wallet has opened: status (active/expired/closed), provider, category, remaining balance, and expiry.
|
|
246
|
-
|
|
279
|
+
description: `List all known payment channels with their current status.
|
|
280
|
+
|
|
281
|
+
Shows channels this wallet has opened: status (active/expired/closed), provider, category, remaining balance, and expiry.
|
|
282
|
+
|
|
247
283
|
Use this to find expired channels that need closing to recover funds. If any channel shows EXPIRED, call drain_close_channel immediately.`,
|
|
248
284
|
inputSchema: {
|
|
249
285
|
type: 'object',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/tools/channel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/tools/channel.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/chat.js
CHANGED
|
@@ -40,9 +40,9 @@ export async function chat(channelService, providerService, inferenceService, ar
|
|
|
40
40
|
const assistantMessage = response.choices[0]?.message?.content ?? '';
|
|
41
41
|
const cost = response.drain?.cost ?? 'unknown';
|
|
42
42
|
const totalSpent = response.drain?.totalSpent ?? 'unknown';
|
|
43
|
-
return `${assistantMessage}
|
|
44
|
-
|
|
45
|
-
---
|
|
43
|
+
return `${assistantMessage}
|
|
44
|
+
|
|
45
|
+
---
|
|
46
46
|
*Cost: $${formatCost(cost)} USDC | Total spent: $${formatCost(totalSpent)} USDC | Tokens: ${response.usage.total_tokens}*`;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
@@ -61,18 +61,18 @@ function formatCost(cost) {
|
|
|
61
61
|
export const chatTools = [
|
|
62
62
|
{
|
|
63
63
|
name: 'drain_chat',
|
|
64
|
-
description: `Send a paid request to a provider through an open payment channel.
|
|
65
|
-
|
|
66
|
-
Works for ALL provider types — not just LLM chat:
|
|
67
|
-
- LLM providers (category "llm"): Standard chat messages [{role, content}]
|
|
68
|
-
- Non-LLM providers (scraping, image, VPN, etc.): Put structured JSON in the user message content. The expected format is in the provider's docs — call drain_provider_info first.
|
|
69
|
-
|
|
70
|
-
Payment is automatic: signs a voucher, deducts from channel balance, returns the response.
|
|
71
|
-
|
|
72
|
-
Requires an open, non-expired channel with sufficient balance.
|
|
73
|
-
|
|
74
|
-
If "Channel expired" -> open a new channel.
|
|
75
|
-
If "Insufficient balance" -> open a new channel with more funds.
|
|
64
|
+
description: `Send a paid request to a provider through an open payment channel.
|
|
65
|
+
|
|
66
|
+
Works for ALL provider types — not just LLM chat:
|
|
67
|
+
- LLM providers (category "llm"): Standard chat messages [{role, content}]
|
|
68
|
+
- Non-LLM providers (scraping, image, VPN, etc.): Put structured JSON in the user message content. The expected format is in the provider's docs — call drain_provider_info first.
|
|
69
|
+
|
|
70
|
+
Payment is automatic: signs a voucher, deducts from channel balance, returns the response.
|
|
71
|
+
|
|
72
|
+
Requires an open, non-expired channel with sufficient balance.
|
|
73
|
+
|
|
74
|
+
If "Channel expired" -> open a new channel.
|
|
75
|
+
If "Insufficient balance" -> open a new channel with more funds.
|
|
76
76
|
If "Provider offline" -> use drain_providers to find an alternative.`,
|
|
77
77
|
inputSchema: {
|
|
78
78
|
type: 'object',
|
package/dist/tools/providers.js
CHANGED
|
@@ -14,21 +14,21 @@ function formatProvider(p) {
|
|
|
14
14
|
const latency = p.status.latencyMs ? `${p.status.latencyMs}ms` : 'N/A';
|
|
15
15
|
const docsUrl = p.docsUrl || `${p.apiUrl}/v1/docs`;
|
|
16
16
|
const models = p.models.map(m => ` - ${m.name} (${formatPricing(m)})`).join('\n');
|
|
17
|
-
return `
|
|
18
|
-
## ${p.name}
|
|
19
|
-
- **ID:** ${p.id}
|
|
20
|
-
- **Category:** ${p.category || 'llm'}
|
|
21
|
-
- **Status:** ${status}
|
|
22
|
-
- **Latency:** ${latency}
|
|
23
|
-
- **Address:** ${p.providerAddress}
|
|
24
|
-
- **API:** ${p.apiUrl}
|
|
25
|
-
- **Docs:** ${docsUrl}
|
|
26
|
-
- **Chain:** ${p.chainId === 137 ? 'Polygon Mainnet' : 'Polygon Amoy'}
|
|
27
|
-
|
|
28
|
-
**Description:** ${p.description}
|
|
29
|
-
|
|
30
|
-
**Models / Services:**
|
|
31
|
-
${models}
|
|
17
|
+
return `
|
|
18
|
+
## ${p.name}
|
|
19
|
+
- **ID:** ${p.id}
|
|
20
|
+
- **Category:** ${p.category || 'llm'}
|
|
21
|
+
- **Status:** ${status}
|
|
22
|
+
- **Latency:** ${latency}
|
|
23
|
+
- **Address:** ${p.providerAddress}
|
|
24
|
+
- **API:** ${p.apiUrl}
|
|
25
|
+
- **Docs:** ${docsUrl}
|
|
26
|
+
- **Chain:** ${p.chainId === 137 ? 'Polygon Mainnet' : 'Polygon Amoy'}
|
|
27
|
+
|
|
28
|
+
**Description:** ${p.description}
|
|
29
|
+
|
|
30
|
+
**Models / Services:**
|
|
31
|
+
${models}
|
|
32
32
|
`.trim();
|
|
33
33
|
}
|
|
34
34
|
export async function listProviders(providerService, args) {
|
|
@@ -76,14 +76,14 @@ export async function getProvider(providerService, args) {
|
|
|
76
76
|
export const providerTools = [
|
|
77
77
|
{
|
|
78
78
|
name: 'drain_providers',
|
|
79
|
-
description: `List available service providers on the DRAIN marketplace.
|
|
80
|
-
|
|
81
|
-
Providers offer diverse services by category: llm, image, audio, code, scraping, vpn, multi-modal, other. Each provider has a docs endpoint with usage instructions for that service.
|
|
82
|
-
|
|
83
|
-
For any provider that is not category "llm", read its docs (via drain_provider_info) before sending requests to learn the expected message format.
|
|
84
|
-
|
|
85
|
-
You can open channels to multiple providers simultaneously for multi-service workflows.
|
|
86
|
-
|
|
79
|
+
description: `List available service providers on the DRAIN marketplace.
|
|
80
|
+
|
|
81
|
+
Providers offer diverse services by category: llm, image, audio, code, scraping, vpn, multi-modal, other. Each provider has a docs endpoint with usage instructions for that service.
|
|
82
|
+
|
|
83
|
+
For any provider that is not category "llm", read its docs (via drain_provider_info) before sending requests to learn the expected message format.
|
|
84
|
+
|
|
85
|
+
You can open channels to multiple providers simultaneously for multi-service workflows.
|
|
86
|
+
|
|
87
87
|
Returns: Providers with category, models/services, pricing, docs URL, and online status.`,
|
|
88
88
|
inputSchema: {
|
|
89
89
|
type: 'object',
|
|
@@ -105,10 +105,10 @@ Returns: Providers with category, models/services, pricing, docs URL, and online
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: 'drain_provider_info',
|
|
108
|
-
description: `Get detailed information about a provider including usage instructions.
|
|
109
|
-
|
|
110
|
-
Returns provider details, available models/services, pricing, and docs content.
|
|
111
|
-
The docs explain how to format the messages parameter in drain_chat for this provider.
|
|
108
|
+
description: `Get detailed information about a provider including usage instructions.
|
|
109
|
+
|
|
110
|
+
Returns provider details, available models/services, pricing, and docs content.
|
|
111
|
+
The docs explain how to format the messages parameter in drain_chat for this provider.
|
|
112
112
|
For non-LLM providers this is essential — the docs specify the expected JSON payload.`,
|
|
113
113
|
inputSchema: {
|
|
114
114
|
type: 'object',
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "drain-mcp",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"mcpName": "io.github.kimbo128/drain-mcp",
|
|
5
|
-
"description": "MCP server for DRAIN protocol. Agents discover providers, open USDC payment channels, and call any service — LLM, scraping, image, VPN, and more.",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "https://github.com/kimbo128/DRAIN.git"
|
|
9
|
-
},
|
|
10
|
-
"type": "module",
|
|
11
|
-
"main": "dist/index.js",
|
|
12
|
-
"bin": {
|
|
13
|
-
"drain-mcp": "./dist/index.js"
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsc",
|
|
17
|
-
"dev": "tsc --watch",
|
|
18
|
-
"start": "node dist/index.js",
|
|
19
|
-
"prepublishOnly": "npm run build"
|
|
20
|
-
},
|
|
21
|
-
"keywords": [
|
|
22
|
-
"mcp",
|
|
23
|
-
"model-context-protocol",
|
|
24
|
-
"drain",
|
|
25
|
-
"ai-agent",
|
|
26
|
-
"ai-payments",
|
|
27
|
-
"micropayments",
|
|
28
|
-
"polygon",
|
|
29
|
-
"usdc",
|
|
30
|
-
"crypto",
|
|
31
|
-
"inference"
|
|
32
|
-
],
|
|
33
|
-
"author": "Handshake58",
|
|
34
|
-
"license": "MIT",
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
37
|
-
"viem": "^2.21.0"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.0.0",
|
|
41
|
-
"typescript": "^5.6.0"
|
|
42
|
-
},
|
|
43
|
-
"engines": {
|
|
44
|
-
"node": ">=18.0.0"
|
|
45
|
-
},
|
|
46
|
-
"files": [
|
|
47
|
-
"dist",
|
|
48
|
-
"README.md"
|
|
49
|
-
]
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "drain-mcp",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"mcpName": "io.github.kimbo128/drain-mcp",
|
|
5
|
+
"description": "MCP server for DRAIN protocol. Agents discover providers, open USDC payment channels, and call any service — LLM, scraping, image, VPN, and more.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/kimbo128/DRAIN.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"bin": {
|
|
13
|
+
"drain-mcp": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"drain",
|
|
25
|
+
"ai-agent",
|
|
26
|
+
"ai-payments",
|
|
27
|
+
"micropayments",
|
|
28
|
+
"polygon",
|
|
29
|
+
"usdc",
|
|
30
|
+
"crypto",
|
|
31
|
+
"inference"
|
|
32
|
+
],
|
|
33
|
+
"author": "Handshake58",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
37
|
+
"viem": "^2.21.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.0.0",
|
|
41
|
+
"typescript": "^5.6.0"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md"
|
|
49
|
+
]
|
|
50
|
+
}
|