mcpay
Version:
SDK and CLI for MCPay functionality - MCP servers with payment capabilities
151 lines • 7.09 kB
JavaScript
import { Command } from "commander";
import { config } from "dotenv";
import { createSigner } from "x402/types";
import packageJson from '../../package.json' with { type: 'json' };
import { ServerType, startStdioServer } from '../server/stdio/start-stdio-server.js';
import { SupportedEVMNetworks, SupportedSVMNetworks } from "x402/types";
config();
const program = new Command();
program
.name('mcpay')
.description('MCPay CLI - MCP servers with payment capabilities')
.version(packageJson.version);
program
.command('connect')
.description('Start an MCP stdio server with payment transport')
.requiredOption('-u, --urls <urls>', 'Comma-separated list of server URLs')
.option('-a, --api-key <key>', 'API key for authentication (or set API_KEY env var). Get yours at https://mcpay.tech')
.option('--max-atomic <value>', 'Max payment in atomic units (e.g. 100000 for 0.1 USDC). Env: X402_MAX_ATOMIC')
.option('--evm <privateKey>', 'EVM private key (0x...) (env: EVM_PRIVATE_KEY)')
.option('--svm <secretKey>', 'SVM secret key (base58/hex) (env: SVM_SECRET_KEY)')
.option('--evm-network <network>', 'EVM network (base-sepolia, base, avalanche-fuji, avalanche, iotex, sei, sei-testnet). Default: base-sepolia (env: EVM_NETWORK)')
.option('--svm-network <network>', 'SVM network (solana-devnet, solana). Default: solana-devnet (env: SVM_NETWORK)')
.action(async (options) => {
try {
const apiKey = options.apiKey || process.env.API_KEY;
const maxAtomicArg = options.x402MaxAtomic || process.env.X402_MAX_ATOMIC;
const evmPkArg = options.evm || process.env.EVM_PRIVATE_KEY;
const svmSkArg = options.svm || process.env.SVM_SECRET_KEY;
const evmNetwork = (options.evmNetwork || process.env.EVM_NETWORK || 'base-sepolia');
const svmNetwork = (options.svmNetwork || process.env.SVM_NETWORK || 'solana-devnet');
if (!apiKey && !evmPkArg && !svmSkArg) {
console.error('Error: Provide either an API key for proxying or a signer with --evm/--svm (or env EVM_PRIVATE_KEY/SVM_SECRET_KEY).');
process.exit(1);
}
// Validate networks only for wallet types that are provided
const supportedEvmNetworks = SupportedEVMNetworks;
const supportedSvmNetworks = SupportedSVMNetworks;
if (evmPkArg && !supportedEvmNetworks.includes(evmNetwork)) {
console.error(`Error: Invalid EVM network '${evmNetwork}'. Supported networks: ${supportedEvmNetworks.join(', ')}`);
process.exit(1);
}
if (svmSkArg && !supportedSvmNetworks.includes(svmNetwork)) {
console.error(`Error: Invalid SVM network '${svmNetwork}'. Supported networks: ${supportedSvmNetworks.join(', ')}`);
process.exit(1);
}
const serverType = ServerType.HTTPStream;
const serverUrls = options.urls.split(',').map((url) => url.trim());
if (serverUrls.length === 0) {
console.error('Error: At least one server URL is required.');
process.exit(1);
}
// Determine if we're using proxy mode or direct mode
// API keys can be used with any proxy endpoint, not just mcpay.tech
const isProxyMode = apiKey && serverUrls.some(url => url.includes('/v1/mcp') || url.includes('mcpay.tech') || url.includes('proxy'));
// API key can only be used with proxy mode
if (apiKey && !isProxyMode) {
console.error('Error: API key can only be used with proxy URLs (containing /v1/mcp, mcpay.tech, or proxy). Use --evm/--svm for direct payments to other servers.');
process.exit(1);
}
// Create individual server connections with appropriate transport options
// Only apply API key authentication to proxy URLs
const serverConnections = serverUrls.map(url => {
const isProxyUrl = url.includes('/v1/mcp') || url.includes('mcpay.tech') || url.includes('proxy');
let transportOptions = undefined;
if (apiKey && isProxyUrl) {
// Only apply API key to proxy URLs
transportOptions = {
requestInit: {
credentials: 'include',
headers: new Headers({
'x-api-key': apiKey
})
}
};
}
return {
url,
serverType,
transportOptions
};
});
// Optional X402 client configuration (only when not using API key)
let x402ClientConfig = undefined;
if (!apiKey && (evmPkArg || svmSkArg)) {
const walletObj = {};
if (evmPkArg) {
const pk = evmPkArg.trim();
if (!pk.startsWith('0x') || pk.length !== 66) {
console.error('Error: Invalid --evm private key. Must be 0x-prefixed 64-hex.');
process.exit(1);
}
try {
walletObj.evm = await createSigner(evmNetwork, pk);
}
catch (error) {
console.error(`Error: Failed to create EVM signer for network '${evmNetwork}':`, error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
if (svmSkArg) {
const sk = svmSkArg.trim();
if (!sk) {
console.error('Error: Invalid --svm secret key.');
process.exit(1);
}
try {
walletObj.svm = await createSigner(svmNetwork, sk);
}
catch (error) {
console.error(`Error: Failed to create SVM signer for network '${svmNetwork}':`, error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
const maybeMax = maxAtomicArg ? (() => { try {
return BigInt(maxAtomicArg);
}
catch {
return undefined;
} })() : undefined;
x402ClientConfig = {
wallet: walletObj,
...(maybeMax !== undefined ? { maxPaymentValue: maybeMax } : {}),
confirmationCallback: async (payment) => {
return true;
}
};
}
await startStdioServer({
serverConnections,
x402ClientConfig,
});
}
catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
});
program
.command('version')
.description('Show version information')
.action(() => {
console.log('mcpay-sdk version ' + packageJson.version);
});
// Parse command line arguments
program.parse();
// If no command was provided, show help
if (!process.argv.slice(2).length) {
program.outputHelp();
}
//# sourceMappingURL=index.js.map