UNPKG

@agentauth/mcp

Version:

A universal proxy for remote MCP server connections, with and without authentication.

156 lines (155 loc) 6.08 kB
#!/usr/bin/env node /* * Copyright (c) 2025 AgentAuth * SPDX-License-Identifier: MIT */ /** * AgentAuth MCP Client Proxy * * A command-line tool to manage AgentAuth credentials and create authenticated * connections to an MCP server using address-based identity. * * Commands: * - generate: Create a new private key token. * - derive <private_key>: Derive address and ID from any private key format. * - connect <server_url>: Start a proxy using AGENTAUTH_TOKEN environment variable. */ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { generateIdentity, deriveAddress, generateId } from '@agentauth/core'; import { connectToRemoteServer, log, mcpProxy, setDebug, validateServerUrlSecurity } from './lib/utils.js'; /** * Parse custom headers from CLI arguments with environment variable substitution */ function parseCustomHeaders(headerArgs) { const headers = {}; for (const header of headerArgs) { const colonIndex = header.indexOf(':'); if (colonIndex === -1) { console.error(`Error: Invalid header format '${header}'. Use format "Key:Value".`); process.exit(1); } const key = header.slice(0, colonIndex).trim(); const value = header.slice(colonIndex + 1).trim(); if (!key) { console.error(`Error: Empty header key in '${header}'.`); process.exit(1); } // Environment variable substitution const expandedValue = value.replace(/\$\{([^}]+)\}/g, (match, envVarName) => { return process.env[envVarName] || ''; }); headers[key] = expandedValue; } return headers; } async function run() { await yargs(hideBin(process.argv)) .command('generate', 'Generate a new AgentAuth identity', () => { }, () => { const { agentauth_token, agentauth_id } = generateIdentity(); console.log(`AGENTAUTH_ID=${agentauth_id}`); console.log(`AGENTAUTH_TOKEN=${agentauth_token}`); }) .command('derive <private_key>', 'Derive address and ID from a private key', (y) => { return y.positional('private_key', { describe: 'Private key in any format (aa-, 0x, or raw hex)', type: 'string', }); }, (argv) => { const { private_key } = argv; if (!private_key) { console.error('Error: Missing <private_key> argument.'); process.exit(1); } try { const agentauth_address = deriveAddress(private_key); const agentauth_id = generateId(agentauth_address); console.log(`AGENTAUTH_ID=${agentauth_id}`); console.log(`AGENTAUTH_ADDRESS=${agentauth_address}`); } catch (error) { console.error('Error: Invalid private key format.'); process.exit(1); } }) .command('connect <server_url>', 'Connect to an MCP server using AGENTAUTH_TOKEN environment variable', (y) => { return y.positional('server_url', { describe: 'The URL of the remote MCP server', type: 'string', }).option('transport', { alias: 't', describe: 'The transport strategy to use.', choices: ['sse-only', 'http-only', 'sse-first', 'http-first'], default: 'http-first', }).option('allow-http', { type: 'boolean', description: 'Allow HTTP connections (not recommended for production)', default: false, }).option('header', { alias: 'H', type: 'array', description: 'Custom headers (format: "Key:Value", supports ${ENV_VAR} substitution)', default: [], }); }, async (argv) => { const { server_url } = argv; if (!server_url) { console.error('Error: Missing <server_url> argument.'); process.exit(1); } if (argv.debug) { setDebug(true); } // Validate server URL security (HTTPS enforcement) validateServerUrlSecurity(server_url, argv['allow-http']); const transportStrategy = argv.transport; // Parse custom headers with environment variable substitution const customHeaders = parseCustomHeaders(argv.header); log(`Connecting to ${server_url} with strategy ${transportStrategy}...`); const token = process.env.AGENTAUTH_TOKEN; if (token) { log('AGENTAUTH_TOKEN found, connecting with authentication.'); // Validate token format early try { deriveAddress(token); // This will throw if invalid } catch (error) { console.error('Error: Invalid AGENTAUTH_TOKEN format.'); process.exit(1); } } else { log('No AGENTAUTH_TOKEN found, proceeding with an unauthenticated connection.'); } try { const remoteTransport = await connectToRemoteServer(server_url, transportStrategy, new Set(), token, customHeaders); const localTransport = new StdioServerTransport(); mcpProxy({ transportToClient: localTransport, transportToServer: remoteTransport, }); await localTransport.start(); log('Proxy established. Waiting for local client connection...'); } catch (error) { console.error('Failed to connect to the remote server:'); console.error(error); process.exit(1); } }) .option('debug', { alias: 'd', type: 'boolean', description: 'Run in debug mode', default: false, }) .demandCommand(1, 'You need at least one command before moving on') .strict() .help().argv; } run().catch((error) => { console.error('An unexpected error occurred:'); console.error(error); process.exit(1); });