Base MCP Server

Official
by base
// CLI tool for initializing a new MCP server in various apps (Claude, etc.) import { group, isCancel, log, multiselect, password, text, } from '@clack/prompts'; import chalk from 'chalk'; import { english, generateMnemonic } from 'viem/accounts'; import { configureClaude } from './claude.js'; import { configureCursor } from './cursor.js'; import { isUuid, validateMnemonic, writeRootConfig } from './utils.js'; type ToolWithKeys = { name: string; keys: { name: string; label: string; required: boolean }[]; }; const TOOLS_WITH_REQUIRED_KEYS: ToolWithKeys[] = [ { name: 'OpenRouter', keys: [ { name: 'OPENROUTER_API_KEY', label: 'OpenRouter API Key', required: true, }, ], }, { name: 'Alchemy (NFTs)', keys: [ { name: 'ALCHEMY_API_KEY', label: 'Alchemy API Key', required: true, }, ], }, ]; const baseBlue = chalk.hex('#0052FF'); export async function init() { console.log( baseBlue(` ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓███████▓▒░▒▓████████▓▒░ ░▒▓██████████████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓████████▓▒░░▒▓██████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░`), ); console.log(`\n`); console.log( chalk.blueBright( `🔵 Welcome to the Base MCP CLI! \nThis tool will help you initialize a new MCP server in various apps (Claude, etc.)\nTo learn more, visit https://github.com/base/base-mcp.`, ), ); console.log(`\n`); log.step('Step 1 of 2: API Keys.'); log.info( 'Obtain Coinbase Developer Platform (CDP) API keys at https://portal.cdp.coinbase.com/projects/api-keys.', ); const keys = await group( { cdpKeyId: () => password({ message: 'CDP Key ID:', validate: (value) => { if (!value) return 'Enter a valid CDP Key ID'; if (!isUuid(value)) return 'Invalid API Key ID'; }, }), cdpSecret: () => password({ message: 'CDP Secret:', validate: (value) => { if (!value) return 'Enter a valid CDP Secret'; }, }), seedPhrase: () => password({ message: 'Mnemonic Phrase (optional, will generate a new one if not provided):', validate: (value) => { if (value) { if (!validateMnemonic(value)) { return 'Invalid Mnemonic Phrase'; } } }, }), }, { onCancel: () => { log.message('Exiting...'); process.exit(0); }, }, ); const optionalKeys = await multiselect({ message: 'Would you like to configure additional integrations?', options: TOOLS_WITH_REQUIRED_KEYS.map((tool) => ({ label: tool.name, value: tool.name, })), required: false, }); if (isCancel(optionalKeys)) { log.message('Exiting...'); process.exit(0); } let otherKeys: Record<string, string> = {}; // Collect other keys the user wants to configure if (Array.isArray(optionalKeys) && optionalKeys.length > 0) { const promptsObject: Record<string, () => Promise<string | symbol>> = {}; for (const key of optionalKeys) { const tool = TOOLS_WITH_REQUIRED_KEYS.find((t) => t.name === key); if (!tool) continue; for (const key of tool.keys) { promptsObject[key.name] = () => text({ message: `${key.label}:`, validate: (value) => { if (key.required && !value) { return `Enter a valid ${key.label}`; } }, }); } } otherKeys = await group(promptsObject); } log.step('Step 2 of 2: Configure MCP clients.'); const clients = await multiselect({ message: 'Which clients would you like to configure?', options: [ { label: 'Claude', value: 'claude' }, { label: 'Cursor', value: 'cursor' }, ], }); if (isCancel(clients)) { log.message('Exiting...'); process.exit(0); } if (!keys.seedPhrase) { keys.seedPhrase = generateMnemonic(english, 256); } // Set up root config file writeRootConfig({ envVars: { ...keys, ...otherKeys, }, clients: Array.isArray(clients) ? clients : [], }); if (Array.isArray(clients) && clients.includes('claude')) { log.step('Configuring Claude'); await configureClaude({ cdpKeyId: keys.cdpKeyId, cdpSecret: keys.cdpSecret, mnemonicPhrase: keys.seedPhrase, optionalKeys: otherKeys, }); } if (Array.isArray(clients) && clients.includes('cursor')) { log.step('Configuring Cursor'); await configureCursor({ cdpKeyId: keys.cdpKeyId, cdpSecret: keys.cdpSecret, mnemonicPhrase: keys.seedPhrase, optionalKeys: otherKeys, }); } }