MCP服务器部署工具

一个快速部署MCP服务器到Vercel的工具,支持自定义服务器和预置服务器(如Gmail、Google Drive和Apollo.io),并提供可插拔的OAuth认证功能。

  • by n47vc
  • TypeScript
  • 本地部署
  • 2 星标
  • 更新于

使用说明

@n47vc/mcp

在几分钟内将MCP服务器部署到Vercel。构建自己的或使用附带的Gmail、Google Drive和Apollo.io服务器——所有这些服务器都具有可插拔的OAuth身份验证。

为什么存在: 从头开始构建MCP服务器意味着自己连接OAuth、令牌管理、发现端点和Vercel部署。这个包可以处理所有这些。你编写工具逻辑,其他一切都完成了。

快速开始

选项A:分叉和部署(最快)

分叉此仓库,然后:

cd app
cp .env.example .env.local   # fill in your credentials
npm install
vercel deploy

看 app/.env.示例 对于所有配置选项。

选项B:安装为npm包

  1. 创建一个新的Next.js项目:

    npx create-next-app@latest my-mcp-servers --typescript
    cd my-mcp-servers
    npm install @n47vc/mcp
    
  2. 创建 pages/api/mcp/[...mcp].ts:

    import { createMCPApp, createGoogleAuthProvider, gmail, gdrive } from '@n47vc/mcp';
    
    export const config = { api: { bodyParser: false } };
    
    export default createMCPApp({
      secret: process.env.MCP_SECRET!,
      authProvider: createGoogleAuthProvider({
        clientId: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      }),
      servers: [gmail, gdrive],
    });
    
  3. 将众所周知的重写添加到 next.config.js:

    module.exports = {
      async rewrites() {
        return [
          { source: '/.well-known/:path*', destination: '/api/mcp/well-known/:path*' },
        ];
      },
    };
    
  4. 设置环境变量并部署:

    vercel env add MCP_SECRET
    vercel env add GOOGLE_CLIENT_ID
    vercel env add GOOGLE_CLIENT_SECRET
    vercel deploy
    

构建自己的服务器

创建自定义MCP服务器是一个单独的文件。这是一个完整的模板:

// src/servers/myapi/index.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import type { MCPUserContext, MCPServerDefinition } from '../../types';

// 1. Define input schemas
const SearchSchema = z.object({
  query: z.string().min(1),
  limit: z.number().int().positive().max(100).optional().default(10),
});

// 2. Create server factory
export function createMyApiServer(context?: MCPUserContext): Server {
  const server = new Server(
    { name: 'myapi', version: '1.0.0' },
    { capabilities: { tools: {} } }
  );

  // 3. List available tools
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: [
      {
        name: 'myapi_search',
        description: 'Search my API for results',
        inputSchema: {
          type: 'object' as const,
          properties: {
            query: { type: 'string' as const, description: 'Search query' },
            limit: { type: 'number' as const, description: 'Max results', default: 10 },
          },
          required: ['query'],
        },
        annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
      },
    ],
  }));

  // 4. Handle tool calls
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    try {
      switch (name) {
        case 'myapi_search': {
          const input = SearchSchema.parse(args);
          const resp = await fetch(`https://api.example.com/search?q=${encodeURIComponent(input.query)}&limit=${input.limit}`);
          const data = await resp.json();
          return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
        }
        default:
          throw new Error(`Unknown tool: ${name}`);
      }
    } catch (error) {
      return {
        content: [{ type: 'text', text: JSON.stringify({ error: (error as Error).message }, null, 2) }],
        isError: true,
      };
    }
  });

  return server;
}

// 5. Export the server definition
export const myapi: MCPServerDefinition = {
  slug: 'myapi',
  name: 'My API MCP Server',
  createServer: createMyApiServer,
  auth: { scopes: [] }, // Add OAuth scopes if your API needs them
};

然后将其添加到您的应用程序中:

import { createMCPApp, createGoogleAuthProvider, gmail, myapi } from '@n47vc/mcp';

export default createMCPApp({
  secret: process.env.MCP_SECRET!,
  authProvider: createGoogleAuthProvider({
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  }),
  servers: [gmail, myapi],
});

就是这样。OAuth、令牌管理、PKCE和发现端点都为您处理。

生成新服务器的Claude代码提示

将此提示复制到Claude Code中,以便从任何API构建新服务器:

在此项目中构建一个新的MCP服务器。看 src/servers/apollo/index.ts 作为参考实现,它是现有最简单的服务器。我的新服务器应该:

  • 活在 src/servers/{name}/index.ts
  • 遵循完全相同的模式:Zod输入模式 create{Name}Server 工厂函数、switch语句中的工具处理程序和导出的 MCPServerDefinition
  • 使用 {name}_ 作为工具名称前缀
  • 包括与阿波罗模式匹配的正确错误处理
  • 我想要包装的API是:{描述你的API,粘贴文档,或者给出一个URL}

可用服务器

服务器 导入 描述 其他环境变量
Gmail gmail 阅读帖子、撰写草稿、发送电子邮件 --
谷歌云端硬盘 gdrive 搜索、读取和上传文件 --
阿波罗 apollo 公司丰富,人员搜索 APOLLO_API_KEY

有关完整的工具文档,请参阅每台服务器的README:

配置

createMCPApp(config)

选项 类型 必填 描述
secret string 是 JWT签名和令牌加密的秘密。生成方式 openssl rand -base64 32
authProvider AuthProviderConfig 是 OAuth提供者(见下文)
servers MCPServerDefinition[] 是 要启用的MCP服务器阵列
baseUrl string 否 覆盖自动检测到的基本URL
allowedDomain string 否 仅限于来自此域的电子邮件(例如。, 'mycompany.com')
onToolCall function 否 钩子在工具执行之前被触发。返回a CallToolResult 阻止执行。
onToolComplete function 否 工具执行后钩子被触发。返回a CallToolResult 以覆盖响应。
onAuthGranted function 否 每当生成访问令牌时都会触发Hook——第一次OAuth授权和每次后续刷新。接收 { email, name, refresh_token?, scopes }。可用于持久化提供程序刷新令牌。会记录错误,但不会导致身份验证失败。
tokenLifetimes object 否 覆盖令牌生命周期(accessToken, refreshToken, authCode --jose持续时间字符串如下 '1h', '90d', '5m')

createGoogleAuthProvider(options)

选项 类型 必填 描述
clientId string 是 谷歌OAuth客户端ID
clientSecret string 是 谷歌OAuth客户端密码
allowedDomain string 否 限制Google登录到此域

所有服务器都通过Google OAuth(openid+电子邮件+个人资料)对用户进行身份验证。需要额外API作用域(例如Gmail、Drive)的服务器在其 MCPServerDefinition.auth.scopes --不需要单独的作用域配置。

谷歌云控制台设置

  1. 首选 谷歌云控制台
  2. 创建新项目(或使用现有项目)
  3. 启用所需的API:
    • Gmail API --如果使用Gmail服务器
    • Google Drive API --如果使用Google Drive服务器
    • 谷歌文档API --如果使用Google Drive服务器
    • 谷歌网页API --如果使用Google Drive服务器
    • 谷歌幻灯片API --如果使用Google Drive服务器
  4. 首选 凭证 > 创建凭据 > OAuth 2.0客户端ID
  5. 应用程序类型: 网络应用程序
  6. 添加授权重定向URI: https://your-domain.com/api/mcp/oauth/callback
  7. 将客户端ID和客户端密钥复制到环境变量中

身份验证提供者接口

要添加新的身份验证提供者(例如Microsoft、GitHub),请实现 AuthProviderConfig 接口:

interface AuthProviderConfig {
  id: string;
  name: string;
  getAuthorizationUrl(callbackUrl: string, state: string, scopes: string[]): string;
  exchangeCode(code: string, callbackUrl: string): Promise<AuthProviderTokens>;
  refreshAccessToken(refreshToken: string): Promise<{ access_token: string } | undefined>;
  getBaseScopes(): string[];
}

建筑

  • 无状态 --每个请求都会创建一个新的MCP服务器实例。没有会话存储。
  • 基于JWT的代币 --访问令牌(1h)和刷新令牌(90d)是使用AES-256-GCM加密的提供者令牌签名的JWT。不需要数据库。
  • PKCE --OAuth授权代码流,带有用于代码交换的证明密钥。
  • 批准文号8414/9728 --用于自动客户端配置的标准OAuth发现端点。

已知限制

  • 身份验证码是可重放的 --授权码是无状态的JWT,因此同一代码可以在5分钟的到期窗口内多次交换。RFC 6749要求使用一次性身份验证码,但强制执行此要求需要服务器端存储。PKCE验证降低了风险,因为只有原始客户端拥有 code_verifier.

许可证

麻省理工学院