UNPKG

mcpay

Version:

SDK and CLI for MCPay functionality - MCP servers with payment capabilities

309 lines (290 loc) 10.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.scaffoldServer = scaffoldServer; exports.listTemplates = listTemplates; const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const TEMPLATES = { basic: { name: 'Basic MCP Server', description: 'A minimal MCP server with essential dependencies only', dependencies: { '@modelcontextprotocol/sdk': '^1.12.3', '@vercel/mcp-adapter': '^0.11.1', 'zod': '^3.25.67' }, devDependencies: { '@types/node': '^22.13.10' }, scripts: { 'test': 'echo "Error: no test specified" && exit 1', 'dev': 'vercel dev', 'deploy': 'vercel' }, envVariables: ['VALID_KEYS'] } }; const basicServerTemplate = `import { createMcpHandler } from "@vercel/mcp-adapter"; import { z } from "zod"; const VALID_KEYS = process.env.VALID_KEYS?.split(","); const handler = createMcpHandler((server) => { // Add your tools here server.tool( "exampleTool", "Example tool description", { input: z.string().describe("Input parameter description") }, async ({ input }, { authInfo }) => { // Your tool implementation here return { content: [{ type: "text", text: \`Hello \${input}!\` }] }; } ); }); const wrappedHandler = async (req: Request) => { const apiKey = req.headers.get('x-api-key'); const isAuthenticated = apiKey && VALID_KEYS?.includes(apiKey); // Add auth info to request headers const modifiedReq = new Request(req.url, { method: req.method, headers: { ...Object.fromEntries(req.headers), 'x-auth-status': isAuthenticated ? 'authenticated' : 'unauthenticated' }, body: req.body, // @ts-ignore -- 'duplex' required by Node.js 18+ but not in TypeScript types yet duplex: 'half' }); return handler(modifiedReq); }; export { wrappedHandler as GET, wrappedHandler as POST, wrappedHandler as DELETE }; `; const typescriptConfig = `{ "compilerOptions": { "module": "NodeNext", "declaration": true, "target": "ES2021", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true }, "include": ["src/**/*", "api/**/*"], "exclude": ["node_modules", "**/*.test.ts"] } `; const vercelConfig = `{ "rewrites": [{ "source": "/(.+)", "destination": "/api/server" }], "functions": { "api/server.ts": { "maxDuration": 800 } } } `; const gitignoreTemplate = `node_modules .env .vercel dist `; const indexHtmlTemplate = (serverName) => `<h1>${serverName}</h1> <p>MCP Server - Protocol is mounted at the root.</p> `; const readmeTemplate = (name, description, envVars) => `# ${name} ${description} ## Setup 1. Install dependencies: \`\`\`bash pnpm install \`\`\` 2. Set up environment variables: \`\`\`bash cp env.example .env \`\`\` Then edit \`.env\` with your API keys: ${envVars.map(env => `- \`${env}\`: Your ${env.toLowerCase().replace(/_/g, ' ')}`).join('\n')} ## Development Run locally with Vercel: \`\`\`bash vercel dev \`\`\` ## Deployment Deploy to Vercel: \`\`\`bash vercel \`\`\` ## Usage Once deployed, you can use this MCP server with the mcpay CLI: \`\`\`bash mcpay server -u https://your-deployment.vercel.app/api/server \`\`\` `; async function scaffoldServer(options) { const { template, name, directory = process.cwd(), useExample = false } = options; if (!TEMPLATES[template]) { throw new Error(`Unknown template: ${template}. Available templates: ${Object.keys(TEMPLATES).join(', ')}`); } const templateConfig = TEMPLATES[template]; const projectDir = path.join(directory, name); try { // Check if directory already exists try { await fs.access(projectDir); throw new Error(`Directory ${name} already exists`); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } // Create project directory await fs.mkdir(projectDir, { recursive: true }); // Create api directory await fs.mkdir(path.join(projectDir, 'api'), { recursive: true }); // Create public directory await fs.mkdir(path.join(projectDir, 'public'), { recursive: true }); // Create package.json const packageJson = { name: name, version: "1.0.0", description: templateConfig.description, main: "index.js", type: "module", scripts: templateConfig.scripts, keywords: [], author: "", license: "ISC", dependencies: templateConfig.dependencies, devDependencies: templateConfig.devDependencies }; await fs.writeFile(path.join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2)); // Create server.ts - either from example or basic template let serverContent = basicServerTemplate; if (useExample) { try { // Try to find the corresponding example directory const exampleDirs = ['mcp-examples', '../mcp-examples', '../../mcp-examples']; let exampleDir = null; for (const dir of exampleDirs) { try { const examplePath = path.join(process.cwd(), dir, template === 'financialdatasets' ? 'financialdatasets.ai' : template); await fs.access(examplePath); exampleDir = examplePath; break; } catch { continue; } } if (exampleDir) { const exampleServerPath = path.join(exampleDir, 'api', 'server.ts'); serverContent = await fs.readFile(exampleServerPath, 'utf-8'); console.log(`📄 Using example implementation from ${template}`); // Also copy additional files if they exist const additionalFiles = ['lib', 'scripts']; for (const file of additionalFiles) { try { const sourcePath = path.join(exampleDir, file); const destPath = path.join(projectDir, file); await copyDirectory(sourcePath, destPath); console.log(`📁 Copied ${file} directory`); } catch { // File doesn't exist, skip } } } } catch (error) { console.warn(`⚠️ Could not copy example implementation, using basic template instead`); } } await fs.writeFile(path.join(projectDir, 'api', 'server.ts'), serverContent); // Create tsconfig.json await fs.writeFile(path.join(projectDir, 'tsconfig.json'), typescriptConfig); // Create vercel.json await fs.writeFile(path.join(projectDir, 'vercel.json'), vercelConfig); // Create .gitignore await fs.writeFile(path.join(projectDir, '.gitignore'), gitignoreTemplate); // Create public/index.html await fs.writeFile(path.join(projectDir, 'public', 'index.html'), indexHtmlTemplate(templateConfig.name)); // Create env.example const envExample = templateConfig.envVariables.map(env => `${env}=`).join('\n'); await fs.writeFile(path.join(projectDir, 'env.example'), envExample); // Create README.md await fs.writeFile(path.join(projectDir, 'README.md'), readmeTemplate(templateConfig.name, templateConfig.description, templateConfig.envVariables)); console.log(`✅ Successfully scaffolded "${templateConfig.name}" project in ${name}/`); console.log('\nNext steps:'); console.log(`1. cd ${name}`); console.log('2. pnpm install'); console.log('3. cp env.example .env'); console.log('4. Edit .env with your API keys'); console.log('5. vercel dev'); } catch (error) { // Clean up on error try { await fs.rmdir(projectDir, { recursive: true }); } catch (cleanupError) { // Ignore cleanup errors } throw error; } } async function copyDirectory(src, dest) { await fs.mkdir(dest, { recursive: true }); const files = await fs.readdir(src); for (const file of files) { const srcPath = path.join(src, file); const destPath = path.join(dest, file); const stat = await fs.stat(srcPath); if (stat.isDirectory()) { await copyDirectory(srcPath, destPath); } else { await fs.copyFile(srcPath, destPath); } } } function listTemplates() { console.log('Available templates:\n'); Object.entries(TEMPLATES).forEach(([key, config]) => { console.log(`${key.padEnd(20)} - ${config.name}`); console.log(`${' '.repeat(22)}${config.description}\n`); }); } //# sourceMappingURL=scaffold.js.map