模型上下文协议服务器

一个基于TypeScript和Node.js构建的生产就绪模型上下文协议(MCP)服务器,具有模块化架构、动态插件加载、实时WebSocket事件、LLM集成和多语言插件支持。

  • by raunak1812
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 raunak1812/MCP_SERVER

使用说明

MCP服务器 - 模型上下文协议服务器

一个使用TypeScript和Node.js构建的、可投入生产的模型上下文协议(MCP)服务器。其特点包括模块化架构、动态插件加载、实时WebSocket事件处理、大型语言模型(LLM)集成以及多语言插件支持。

特点/特性

  • 模块化架构 - 使用依赖注入(DI)容器实现清晰的职责分离
  • 动态插件系统 - 在运行时加载TypeScript、Python、C和C++插件
  • 实时事件 - 用于实时MCP事件流的WebSocket处理器
  • 大型语言模型(LLM)集成 - OpenAI和Anthropic提供商提供提示工程支持
  • Redis 缓存 - 与Redis集成实现快速数据缓存
  • 任务计划程序 - 内置作业调度器用于后台任务
  • 请求验证 - 基于Zod的中间件,用于类型安全的验证
  • 错误处理 - 带日志记录功能的集中式错误中间件
  • Docker 准备就绪 - 完整的Docker和docker-compose设置
  • Kubernetes 已就绪 - 包含K8s部署清单
  • 持续集成/持续交付(CI/CD) - GitHub Actions 工作流用于自动化测试
  • 多语言 - 支持Python、C和C++的外部插件

技术栈

  • 运行时Node.js 20+(版本)
  • 语言TypeScript 5.6+
  • 框架Express.js
  • WebSocketws
  • 验证佐德
  • 记录日志温斯顿
  • 缓存Redis
  • LLMOpenAI,Anthropic
  • 部署Docker,Kubernetes,Vercel

快速入门

先决条件

  • Node.js 20+(版本)
  • npm 或 yarn
  • Docker(可选)
  • Redis(可选)

安装

git clone https://github.com/raunak0400/MCP_SERVER.git
cd MCP_SERVER
npm install

配置

复制示例环境文件:

cp .env.example .env

编辑 .env 根据您的配置:

NODE_ENV=development
PORT=3000
LOG_LEVEL=info
REDIS_URL=redis://localhost:6379
OPENAI_API_KEY=sk-your-openai-key
ANTHROPIC_API_KEY=sk-ant-your-key
LLM_PROVIDER=openai

发展

npm run dev

构建

npm run build

生产

npm start

Docker 部署

构建并运行

docker build -t mcp-server .
docker run -p 3000:3000 mcp-server

Docker Compose

docker-compose up -d

项目结构

.
├── src/
│   ├── config/          # Configuration management
│   ├── core/            # Core services (DI, events, MCP server)
│   ├── routes/          # API route handlers
│   ├── services/        # Business logic services
│   ├── middleware/      # Express middleware
│   ├── handlers/        # WebSocket and event handlers
│   ├── controllers/     # Request controllers
│   ├── types/           # TypeScript type definitions
│   ├── utils/           # Utility functions
│   ├── plugins/         # Plugin system
│   │   ├── internal/    # TypeScript plugins
│   │   └── external/    # Python, C, C++ plugins
│   ├── llm/             # LLM integration
│   │   ├── openai.ts    # OpenAI provider
│   │   ├── anthropic.ts # Anthropic provider
│   │   └── service.ts   # LLM service
│   └── cache/           # Redis cache
├── scripts/             # Utility scripts
├── docs/                # Documentation
├── tests/               # Test suites
└── infra/               # Infrastructure configs

API 端点

健康检查

GET /health

执行MCP插件

POST /mcp/execute
Content-Type: application/json

{
  "plugin": "sample",
  "action": "echo",
  "payload": { "message": "hello" }
}

插件开发

TypeScript 插件

创建一个插件在 src/plugins/internal/:

import { Plugin } from '../../types/index.js'

const plugin: Plugin = {
  name: 'myPlugin',
  actions: {
    doSomething: async (payload) => {
      return { result: 'done' }
    }
  }
}

export default plugin

Python 插件

在(某处)创建一个插件 src/plugins/external/python/:

import json
import sys

def process(data):
    return {"processed": data}

if __name__ == "__main__":
    payload = json.loads(sys.argv[1]) if len(sys.argv) > 1 else {}
    result = process(payload)
    print(json.dumps(result))

添加到 manifest.json:

{
  "name": "myPythonPlugin",
  "command": "python",
  "args": ["src/plugins/external/python/myplugin.py"]
}

大型语言模型(LLM)的使用

import { LLMService } from './llm'

const llm = new LLMService({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY,
  model: 'gpt-4',
  temperature: 0.7,
  maxTokens: 1000
})

const response = await llm.generateCompletion('Explain MCP protocol')

WebSocket 事件

连接到 ws://localhost:3000 接收实时事件:

const ws = new WebSocket('ws://localhost:3000')

ws.on('message', (data) => {
  const event = JSON.parse(data)
  console.log(event.type, event)
})

事件类型:

  • started - 服务器已启动
  • pluginLoaded - 插件已注册
  • pluginExecuted - 插件操作已执行

测试

npm test

贡献;助力

欢迎投稿!请阅读 CONTRIBUTING.md(贡献指南文件) 详情见下文。

  1. 为仓库创建分支
  2. 创建你的特性分支(git checkout -b feature/amazing)
  3. 提交您的更改(git commit -m 'Add amazing feature')
  4. 推送至分支(git push origin feature/amazing)
  5. 提交拉取请求(或:发起合并请求)

安全

看见 SECURITY.md 翻译为中文是:“安全指南/安全文件” 用于安全策略。

支持

如需支持,请参阅 SUPPORT.md 翻译为中文是:“支持文档.md” 或 “支持文件.md”(其中“.md”通常表示Markdown格式的文件) 或者提出一个问题。

许可证

这个项目采用MIT许可证授权——详见 许可证 详情请查阅文件。

致谢

  • 模型上下文协议规范
  • OpenAI和Anthropic提供大型语言模型(LLM)API
  • Express.js 社区
  • TypeScript 团队

路线图

  • [ ] 添加认证系统
  • [ ] 实施速率限制
  • [ ] 添加更多大型语言模型(LLM)提供商(Gemini、Mistral)
  • [ ] 创建插件市场
  • [ ] 添加监控和指标
  • [ ] 实现插件沙箱化
  • 添加GraphQL API支持
  • [ ] 创建管理员仪表板

建筑学

系统设计

MCP服务器采用分层架构模式,各层职责分明:

┌─────────────────────────────────────────┐
│         API Layer (Express)             │
│  ┌─────────────┐    ┌─────────────┐   │
│  │   Routes    │    │  WebSocket  │   │
│  └─────────────┘    └─────────────┘   │
└─────────────────────────────────────────┘
                  │
┌─────────────────────────────────────────┐
│        Middleware Layer                 │
│  ┌──────────┐  ┌──────────┐  ┌──────┐ │
│  │Validation│  │   Auth   │  │Error │ │
│  └──────────┘  └──────────┘  └──────┘ │
└─────────────────────────────────────────┘
                  │
┌─────────────────────────────────────────┐
│      Application Layer                  │
│  ┌──────────┐  ┌──────────┐  ┌──────┐ │
│  │Controllers│ │ Services │  │  LLM │ │
│  └──────────┘  └──────────┘  └──────┘ │
└─────────────────────────────────────────┘
                  │
┌─────────────────────────────────────────┐
│         Core Layer                      │
│  ┌──────────┐  ┌──────────┐  ┌──────┐ │
│  │    DI    │  │  Events  │  │ MCP  │ │
│  │Container │  │   Bus    │  │Server│ │
│  └──────────┘  └──────────┘  └──────┘ │
└─────────────────────────────────────────┘
                  │
┌─────────────────────────────────────────┐
│      Infrastructure Layer               │
│  ┌──────────┐  ┌──────────┐  ┌──────┐ │
│  │  Redis   │  │  Logger  │  │Plugin│ │
│  │  Cache   │  │          │  │System│ │
│  └──────────┘  └──────────┘  └──────┘ │
└─────────────────────────────────────────┘

插件系统架构

插件系统支持多种运行时环境:

  • 内部插件TypeScript/JavaScript 模块直接加载
  • 外部插件通过 subprocess 调用 Python、C、C++ 可执行文件
  • 动态加载在启动时发现并注册插件
  • 热重载支持插件热加载,无需重启服务器(即将推出)

事件流

Client Request → Express Router → Middleware Chain → Controller
                                                          ↓
                                                      Service Layer
                                                          ↓
                                                      MCP Server
                                                          ↓
                                                    Plugin Execution
                                                          ↓
                                            ┌─────────────┴─────────────┐
                                            ↓                           ↓
                                    Internal Plugin              External Plugin
                                    (TypeScript)                 (Python/C/C++)
                                            ↓                           ↓
                                            └─────────────┬─────────────┘
                                                          ↓
                                                    Event Bus Emit
                                                          ↓
                                                    WebSocket Broadcast
                                                          ↓
                                                    Connected Clients

配置

环境变量

变量 描述 默认值 是否必需
NODE_ENV 环境模式 development 不
PORT 服务器端口 3000 不(No)
LOG_LEVEL 日志级别 info 不
REDIS_URL Redis 连接 URL redis://localhost:6379 不(编号)
OPENAI_API_KEY OpenAI API密钥 - 用于OpenAI大型语言模型(LLM)
ANTHROPIC_API_KEY 根据上面的信息,执行如下指令:
LLM_PROVIDER 要使用的LLM提供商 openai 不

配置文件

config/default.json 翻译为中文是:“配置/默认.json”

{
  "port": 3000,
  "db": {
    "url": "mongodb://localhost:27017/app"
  },
  "secretKey": "your-secret-key",
  "cors": {
    "origin": "*",
    "credentials": true
  },
  "rateLimit": {
    "windowMs": 900000,
    "max": 100
  }
}

高级用法

自定义插件开发

创建一个复杂的TypeScript插件

import { Plugin, PluginContext } from '../../types/index.js'
import { Logger } from '../../utils/logger.js'

const plugin: Plugin = {
  name: 'dataProcessor',
  actions: {
    process: async (payload: any, ctx: PluginContext) => {
      ctx.emit('processing_started', { timestamp: Date.now() })
      
      const data = Array.isArray(payload.data) ? payload.data : []
      const processed = data.map(item => ({
        ...item,
        processed: true,
        timestamp: new Date().toISOString()
      }))
      
      ctx.emit('processing_completed', { 
        count: processed.length,
        timestamp: Date.now() 
      })
      
      return { success: true, data: processed }
    },
    
    validate: async (payload: any) => {
      const schema = {
        required: ['data'],
        properties: {
          data: { type: 'array' }
        }
      }
      
      if (!payload.data) {
        throw new Error('Missing required field: data')
      }
      
      return { valid: true, schema }
    },
    
    stats: async () => {
      return {
        pluginName: 'dataProcessor',
        version: '1.0.0',
        uptime: process.uptime(),
        memory: process.memoryUsage()
      }
    }
  }
}

export default plugin

创建一个Python机器学习插件

import json
import sys
import numpy as np
from sklearn.linear_model import LinearRegression

def train_model(data):
    X = np.array(data['features']).reshape(-1, 1)
    y = np.array(data['labels'])
    
    model = LinearRegression()
    model.fit(X, y)
    
    return {
        'success': True,
        'coefficients': model.coef_.tolist(),
        'intercept': float(model.intercept_),
        'score': float(model.score(X, y))
    }

def predict(data, model_params):
    X = np.array(data['features']).reshape(-1, 1)
    model = LinearRegression()
    model.coef_ = np.array(model_params['coefficients'])
    model.intercept_ = model_params['intercept']
    
    predictions = model.predict(X)
    return {
        'success': True,
        'predictions': predictions.tolist()
    }

if __name__ == "__main__":
    try:
        payload = json.loads(sys.argv[1]) if len(sys.argv) > 1 else {}
        action = payload.get('action', 'train')
        
        if action == 'train':
            result = train_model(payload)
        elif action == 'predict':
            result = predict(payload, payload.get('model', {}))
        else:
            result = {'success': False, 'error': 'Unknown action'}
        
        print(json.dumps(result))
    except Exception as e:
        print(json.dumps({'success': False, 'error': str(e)}))
        sys.exit(1)

大型语言模型(LLM)集成示例

基本聊天完成(或“基础对话完成”)

import { LLMService } from './llm'

const llm = new LLMService({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY!,
  model: 'gpt-4',
  temperature: 0.7,
  maxTokens: 1000
})

const response = await llm.generateCompletion(
  'Explain the Model Context Protocol in simple terms'
)
console.log(response)

带上下文的聊天

import { PromptBuilder } from './llm'

const messages = new PromptBuilder()
  .system('You are a helpful coding assistant')
  .user('How do I create a REST API in Express?')
  .build()

const response = await llm.chat({ messages })
console.log(response.choices[0].message.content)

小样本学习

import { PromptBuilder } from './llm'

const examples = [
  { input: 'translate: hello', output: 'hola' },
  { input: 'translate: goodbye', output: 'adiós' },
  { input: 'translate: thank you', output: 'gracias' }
]

const prompt = PromptBuilder.createFewShotPrompt(
  examples,
  'translate: good morning'
)

const response = await llm.generateCompletion(prompt)
console.log(response)

流式响应

const stream = await llm.chat({
  messages: [{ role: 'user', content: 'Write a poem about coding' }],
  stream: true
})

for await (const chunk of stream) {
  process.stdout.write(chunk)
}

Redis 缓存

基本缓存操作

import { redisClient, connectRedis } from './cache/redis'

await connectRedis()

await redisClient.set('user:1', JSON.stringify({ name: 'John' }))
const user = await redisClient.get('user:1')

await redisClient.setEx('session:abc', 3600, 'session-data')

await redisClient.del('user:1')

旁路缓存模式(或称为旁路缓存策略)

async function getUser(id: string) {
  const cached = await redisClient.get(`user:${id}`)
  
  if (cached) {
    return JSON.parse(cached)
  }
  
  const user = await database.findUser(id)
  
  await redisClient.setEx(
    `user:${id}`,
    3600,
    JSON.stringify(user)
  )
  
  return user
}

WebSocket 客户端示例

Node.js 客户端

const WebSocket = require('ws')

const ws = new WebSocket('ws://localhost:3000')

ws.on('open', () => {
  console.log('Connected to MCP Server')
})

ws.on('message', (data) => {
  const event = JSON.parse(data.toString())
  console.log('Event:', event)
  
  switch(event.type) {
    case 'pluginLoaded':
      console.log(`Plugin loaded: ${event.name}`)
      break
    case 'pluginExecuted':
      console.log(`Plugin executed: ${event.name}.${event.action}`)
      break
    case 'started':
      console.log('Server started')
      break
  }
})

ws.on('error', (error) => {
  console.error('WebSocket error:', error)
})

ws.on('close', () => {
  console.log('Disconnected from MCP Server')
})

浏览器客户端

const ws = new WebSocket('ws://localhost:3000')

ws.addEventListener('open', () => {
  console.log('Connected to MCP Server')
})

ws.addEventListener('message', (event) => {
  const data = JSON.parse(event.data)
  console.log('Received:', data)
  
  if (data.type === 'pluginExecuted') {
    updateUI(data)
  }
})

ws.addEventListener('error', (error) => {
  console.error('Error:', error)
})

ws.addEventListener('close', () => {
  console.log('Connection closed')
  setTimeout(reconnect, 5000)
})

Kubernetes 部署

部署清单

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server
  labels:
    app: mcp-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mcp-server
  template:
    metadata:
      labels:
        app: mcp-server
    spec:
      containers:
      - name: mcp-server
        image: mcp-server:1.0.0
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
        - name: PORT
          value: "3000"
        - name: REDIS_URL
          valueFrom:
            configMapKeyRef:
              name: mcp-config
              key: redis-url
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: mcp-secrets
              key: openai-key
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

服务清单/服务声明

apiVersion: v1
kind: Service
metadata:
  name: mcp-server-service
spec:
  type: LoadBalancer
  selector:
    app: mcp-server
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000

ConfigMap(在中文中通常直接保留原名,不进行翻译,但可解释为“配置映射”)

apiVersion: v1
kind: ConfigMap
metadata:
  name: mcp-config
data:
  redis-url: "redis://redis-service:6379"
  log-level: "info"

秘密

apiVersion: v1
kind: Secret
metadata:
  name: mcp-secrets
type: Opaque
data:
  openai-key: <base64-encoded-key>
  anthropic-key: <base64-encoded-key>

性能优化

缓存策略

  1. 查询结果缓存缓存数据库查询结果5-15分钟
  2. 大型语言模型(LLM)响应缓存缓存相似提示以减少API成本
  3. 静态内容使用CDN来托管静态资源
  4. 连接池重用数据库和Redis连接

负载均衡

使用Nginx或云负载均衡器来分配流量:

upstream mcp_backend {
    least_conn;
    server mcp-server-1:3000;
    server mcp-server-2:3000;
    server mcp-server-3:3000;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://mcp_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

监控与日志记录

日志级别

  • 错误需要立即关注的关键问题
  • 警告针对潜在有害情况的警告信息
  • 信息一般信息消息
  • 调试详细的调试信息

日志格式

{
  "timestamp": "2025-10-19T10:30:45.123Z",
  "level": "info",
  "message": "Plugin executed successfully",
  "plugin": "sample",
  "action": "echo",
  "duration": 45
}

需要监控的指标

  1. 请求速率每秒请求数
  2. 响应时间平均延迟和第95百分位延迟
  3. 错误率请求失败的百分比
  4. 插件执行时间每个插件所花费的时间
  5. 内存使用量当前内存使用量
  6. CPU使用率CPU利用率百分比
  7. WebSocket 连接活动连接数
  8. 缓存命中率Redis缓存有效性

安全最佳实践

API安全

  1. API密钥使用环境变量,切勿提交到代码库
  2. 速率限制实现用户级别的速率限制
  3. 输入验证使用Zod模式验证所有输入
  4. **CORS(跨源资源共享)**配置适当的CORS策略
  5. Helmet.js默认启用安全头
  6. HTTPS在生产环境中始终使用TLS

插件沙盒化

  1. 资源限制为外部插件设置CPU和内存限制
  2. 超时配置执行超时时间
  3. 权限模型限制文件系统和网络访问
  4. **输入净化(或输入验证/输入清理)**验证插件输入

数据保护

  1. 静态数据加密对数据库中的敏感数据进行加密
  2. 传输过程中的加密对所有通信使用TLS
  3. 密钥管理使用密钥管理工具(Vault、AWS Secrets Manager)
  4. 访问控制实施基于角色的访问控制(RBAC)

故障排除

常见问题

服务器无法启动

Error: EADDRINUSE: address already in use :::3000

解决方案更改端口或终止使用3000端口的进程

lsof -ti:3000 | xargs kill -9

Redis 连接失败

Error: Redis connection to localhost:6379 failed

解决方案确保 Redis 正在运行

docker run -d -p 6379:6379 redis:7-alpine

插件加载失败

Error: Plugin not found

解决方案检查插件路径和manifest.json文件

ls -la src/plugins/
cat src/plugins/manifest.json

大型语言模型API错误

Error: OpenAI API error: 401 Unauthorized

解决方案验证.env文件中的API密钥

grep OPENAI_API_KEY .env

调试模式

启用调试日志记录:

LOG_LEVEL=debug npm run dev

性能基准

响应时间

终点 平均值(毫秒) p95(毫秒) p99(毫秒)
GET /health 5 8 12
POST /mcp/execute 45 120 250
大语言模型聊天 1500 3000 5000
插件(内部) 15 35 60
插件(外部) 80 200 400

吞吐量

  • 每秒最大请求数5,000(含缓存)
  • 并发WebSocket连接一万
  • 插件执行次数/秒1,000

迁移指南

从v0.x到v1.0

  1. 更新 package.json 中的依赖项
  2. 将插件结构迁移到新格式
  3. 更新环境变量
  4. 运行数据库迁移
  5. 更新 Docker 配置

常见问题解答(FAQ)

问:我可以将这个与其它大型语言模型(LLM)提供商一起使用吗?

A: 是的,您可以扩展LLM服务以支持额外的提供商,如Google Gemini、Mistral或本地模型。

问:如何在Go或Rust中创建一个插件?

A: 将你的Go/Rust代码编译成可执行文件,并在manifest.json中注册它,类似于C/C++插件的注册方式。

问:是否支持水平扩展?

A: 是的,服务器是无状态的,可以水平扩展。使用Redis来共享状态。

问:我可以将应用部署到无服务器平台上吗?

A: 是的,但需要做一些修改。通过配置更改,可以支持Vercel和AWS Lambda。

问:我该如何实现身份验证?

A: 在 src/middleware/auth.ts 中实现基于 JWT 的认证中间件。

问:插件执行的超时时间是多少?

A: 默认值为30秒。可在服务中针对每个插件进行配置。

社区

贡献;做出贡献

我们欢迎投稿!以下是您能提供帮助的方式:

  1. 报告错误提交一个包含复现步骤的问题
  2. 功能请求通过问题提交新功能建议
  3. 代码贡献提交拉取请求
  4. 文档改进文档和示例
  5. 测试为现有代码编写测试

行为准则

请阅读我们的 行为准则 在贡献之前。

贡献者

感谢所有为使这个项目更加完善而做出贡献的人!

资源

文档

外部链接

更新日志

看 CHANGELOG.md 翻译成中文是“变更日志文件” 查看版本历史和发布说明。

许可证

这个项目采用MIT许可证授权——详见 许可证 文件中有详细信息。

赞助商

有兴趣赞助这个项目吗?请联系我们!

联系


由MCP服务器团队用心打造 | 如果你觉得这个仓库有帮助,请给它点个星⭐!