MCP 服务器开发协议
🚀 构建并与世界分享您的 MCP 服务器。 创建出色的 MCP 服务器后,将其提交到 Careti MCP 市场,使其可被数千名开发人员发现并一键安装。
什么是 MCP 服务器?
模型上下文协议(MCP)服务器通过赋予 AI 助手(如 Careti)以下能力来扩展它们:
- 访问外部 API 和服务
- 检索实时数据
- 控制应用程序和本地系统
- 执行单靠文本提示无法实现的操作
没有 MCP,AI 助手虽然强大但是孤立的。有了 MCP,它们获得了与几乎任何数字系统交互的能力。
开发协议
有效 MCP 服务器开发的核心是遵循结构化协议。该协议通过位于 MCP 工作目录根目录(/Users/your-name/Documents/Careti/MCP)的 .agents/context 文件实现。
使用 .agents/context 文件
.agents/context 文件是 Careti 在其所在目录工作时自动读取的特殊配置。这些文件:
- 配置 Careti 的行为并强制执行最佳实践
- 将 Careti 切换到专门的 MCP 开发模式
- 提供构建服务器的分步协议
- 实施安全措施,如防止过早完成
- 引导您完成规划、实施和测试阶段
以下是应放置在 .agents/context 文件中的完整 MCP 服务器开发协议:
# MCP 服务器开发协议
⚠️ 关键:在测试之前不要使用 attempt_completion ⚠️
## 步骤 1:规划(PLAN 模式)
- 此工具解决什么问题?
- 它将使用什么 API/服务?
- 身份验证要求是什么?
□ 标准 API 密钥
□ OAuth(需要单独的设置脚本)
□ 其他凭据
## 步骤 2:实施(ACT 模式)
1. 引导
- 对于 Web 服务、JavaScript 集成或 Node.js 环境:
```bash
npx @modelcontextprotocol/create-server my-server
cd my-server
npm install
```
- 对于数据科学、ML 工作流或 Python 环境:
```bash
pip install mcp
# 或使用 uv(推荐)
uv add "mcp[cli]"
```
2. 核心实施
- 使用 MCP SDK
- 实施全面的日志记录
- TypeScript(用于 Web/JS 项目):
```typescript
console.error("[Setup] Initializing server...")
console.error("[API] Request to endpoint:", endpoint)
console.error("[Error] Failed with:", error)
```
- Python(用于数据科学/ML 项目):
```python
import logging
logging.error('[Setup] Initializing server...')
logging.error(f'[API] Request to endpoint: {endpoint}')
logging.error(f'[Error] Failed with: {str(error)}')
```
- 添加类型定义
- 使用上下文处理错误
- 如果需要,实施速率限制
3. 配置
- 如果需要,从用户获取凭据
- 添加到 MCP 设置:
- 对于 TypeScript 项目:
```json
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["path/to/build/index.js"],
"env": {
"API_KEY": "key"
},
"disabled": false,
"autoApprove": []
}
}
}
```
- 对于 Python 项目:
```bash
# 直接使用命令行
mcp install server.py -v API_KEY=key
# 或在 settings.json 中
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["server.py"],
"env": {
"API_KEY": "key"
},
"disabled": false,
"autoApprove": []
}
}
}
```
## 步骤 3:测试(阻断器 ⛔️)
<thinking>
在使用 attempt_completion 之前,我必须验证:
□ 我是否测试了每个工具?
□ 我是否确认了每个测试的用户成功?
□ 我是否记录了测试结果?
如果任何答案是"否",我不得使用 attempt_completion。
</thinking>
1. 测试每个工具(必需)
□ 使用有效输入测试每个工具
□ 验证输出格式是否正确
⚠️ 在所有工具测试之前不要继续
## 步骤 4:完成
❗ 停止并验证:
□ 每个工具都已使用有效输入进行测试
□ 每个工具的输出格式都是正确的
只有在所有工具都经过测试后才能使用 attempt_completion。
## 关键要求
- ✓ 必须使用 MCP SDK
- ✓ 必须有全面的日志记录
- ✓ 必须单独测试每个工具
- ✓ 必须优雅地处理错误
- ⛔️ 永远不要在完成前跳过测试
当此 .agents/context 文件存在于您的工作目录中时,Careti 将:
- 在PLAN 模式下启动,在实施之前设计您的服务器
- 在ACT 模式下强制执行适当的实施模式
- 在允许完成之前要求测试所有工具
- 引导您完成整个开发生命周期
入门
创建 MCP 服务器只需要几个简单的步骤即可开始:
1. 创建 .agents/context 文件(🚨 重要)
首先,使用上述协议将 .agents/context 文件添加到 MCP 工作目录的根目录。此文件配置 Careti 在此文件夹中工作时使用 MCP 开发协议。
2. 从清晰的描述开始聊天
通过清晰地描述您想要构建的内容来开始您的 Careti 聊天。具体说明:
- MCP 服务器的目的
- 您想要集成的 API 或服务
- 您需要的任何特定工具或功能
例如:
我想为 AlphaAdvantage 金融 API 构建一个 MCP 服务器。
它应该允许我获取实时股票数据、进行技术分析
并检索公司财务信息。
3. 完成协议
Careti 将自动在 PLAN 模式下启动,引导您完成规划过程:
- 讨论问题范围
- 审查 API 文档
- 规划身份验证方法
- 设计工具接口
准备好后,使用聊天底部的切换开关切换到 ACT 模式以开始实施。
4. 尽早提供 API 文档
帮助 Careti 构建 MCP 服务器的最有效方法之一是在开始时就分享官方 API 文档:
这是该服务的 API 文档:
[在此粘贴 API 文档]
提供全面的 API 详细信息(端点、身份验证、数据结构)显著提高了 Careti 实现有效 MCP 服务器的能力。
理解两种模式
PLAN 模式
在此协作阶段,您与 Careti 一起设计 MCP 服务器:
- 定义问题范围
- 选择适当的 API
- 规划身份验证方法
- 设计工具接口
- 确定数据格式
ACT 模式
规划完成后,Careti 帮助实施服务器:
- 设置项目结构
- 编写实施代码
- 配置设置
- 彻底测试每个组件
- 完成文档
案例研究:AlphaAdvantage 股票分析服务器
让我们详细了解 AlphaAdvantage MCP 服务器的开发过程,该服务器提供股票数据分析和报告功能。
规划阶段

在规划阶段,我们:
- 定义了问题:用户需要通过其 AI 助手直接访问金融数据、股票分析和市场见解
- 选择了 API:AlphaAdvantage API 用于金融市场数据
- 标准 API 密钥身份验证
- 速率限制为每分钟 5 个请求(免费层)
- 不同金融数据类型的各种端点
- 设计了所需的工具:
- 股票概览信息(当前价格、公司详细信息)
- 带指标的技术分析(RSI、MACD 等)
- 基本面分析(财务报表、比率)
- 收益报告数据
- 新闻和情绪分析
- 规划了数据格式:
- 干净、格式良好的 markdown 输出
- 结构化数据的表格
- 趋势的视觉指标(↑/↓)
- 适当的财务数字格式
实施

我们从引导项目开始:
npx @modelcontextprotocol/create-server alphaadvantage-mcp
cd alphaadvantage-mcp
npm install axios node-cache
接下来,我们使用以下结构构建项目:
src/
├── api/
│ └── alphaAdvantageClient.ts # 具有速率限制和缓存的 API 客户端
├── formatters/
│ └── markdownFormatter.ts # 用于干净 markdown 的输出格式化程序
└── index.ts # 主 MCP 服务器实施
API 客户端实施
API 客户端实施包括:
- 速率限制:强制执行每分钟 5 个请求的限制
- 缓存:通过战略缓存减少 API 调用
- 错误处理:强大的错误检测和报告
- 类型化接口:所有数据的清晰 TypeScript 类型
关键实施详细信息:
/**
* 根据免费层管理速率限制(每分钟 5 次调用)
*/
private async enforceRateLimit() {
if (this.requestsThisMinute >= 5) {
console.error("[Rate Limit] Rate limit reached. Waiting for next minute...");
return new Promise<void>((resolve) => {
const remainingMs = 60 * 1000 - (Date.now() % (60 * 1000));
setTimeout(resolve, remainingMs + 100); // 添加 100ms 缓冲
});
}
this.requestsThisMinute++;
return Promise.resolve();
}
Markdown 格式化
我们实施了格式化程序以精美地显示金融数据:
/**
* 将公司概览格式化为 markdown
*/
export function formatStockOverview(overviewData: any, quoteData: any): string {
// 提取数据
const overview = overviewData
const quote = quoteData["Global Quote"]
// 计算价格变化
const currentPrice = parseFloat(quote["05. price"] || "0")
const priceChange = parseFloat(quote["09. change"] || "0")
const changePercent = parseFloat(quote["10. change percent"]?.replace("%", "") || "0")
// 格式化 markdown
let markdown = `# ${overview.Symbol} (${overview.Name}) - ${formatCurrency(currentPrice)} ${addTrendIndicator(priceChange)}${changePercent > 0 ? "+" : ""}${changePercent.toFixed(2)}%\n\n`
// 添加更多详细信息...
return markdown
}
工具实施
我们定义了五个具有清晰接口的工具:
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error("[Setup] Listing available tools")
return {
tools: [
{
name: "get_stock_overview",
description: "获取股票代码的基本公司信息和当前报价",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "股票代码(例如,'AAPL')",
},
market: {
type: "string",
description: "可选市场(例如,'US')",
default: "US",
},
},
required: ["symbol"],
},
},
// 在此定义其他工具...
],
}
})
每个工具的处理程序包括:
- 输入验证
- 带错误处理的 API 客户端调用
- 响应的 Markdown 格式化
- 全面的日志记录
测试阶段
此关键阶段涉及系统地测试每个工具:
- 首先,我们在设置中配置了 MCP 服务器:
{
"mcpServers": {
"alphaadvantage-mcp": {
"command": "node",
"args": ["/path/to/alphaadvantage-mcp/build/index.js"],
"env": {
"ALPHAVANTAGE_API_KEY": "YOUR_API_KEY"
},
"disabled": false,
"autoApprove": []
}
}
}
- 然后我们单独测试每个工具:
-
get_stock_overview:检索 AAPL 股票概览信息
# AAPL (Apple Inc) - $241.84 ↑+1.91%
**行业:** 技术
**产业:** 电子计算机
**市值:** 3.63T
**市盈率:** 38.26
... -
get_technical_analysis:获取价格行为和 RSI 数据
# 技术分析:AAPL
## 日价格行为
当前价格:$241.84(↑$4.54,+1.91%)
### 最近的每日价格
| 日期 | 开盘 | 最高 | 最低 | 收盘 | 成交量 |
| ---------- | ------- | ------- | ------- | ------- | ------ |
| 2025-02-28 | $236.95 | $242.09 | $230.20 | $241.84 | 56.83M |
... -
get_earnings_report:检索 MSFT 收益历史和格式化报告
# 收益报告:MSFT(微软公司)
**行业:** 技术
**产业:** 服务-预打包软件
**当前每股收益:** $12.43
## 最近的季度收益
| 季度 | 日期 | 每股收益预估 | 实际每股收益 | 惊喜 % |
| ---------- | ---------- | ------------ | ------------ | ------ |
| 2024-12-31 | 2025-01-29 | $3.11 | $3.23 | ↑4.01% |
...
挑战和解决方案
在开发过程中,我们遇到了几个挑战:
- API 速率限制:
- 挑战:免费层限制为每分钟 5 次调用
- 解决方案:实施了队列、强制执行速率限制并添加了全面的缓存
- 数据格式化:
- 挑战:原始 API 数据不够用户友好
- 解决方案:创建了格式化实用程序以一致地显示金融数据
- 超时问题:
- 挑战:进行多个 API 调用的复杂工具可能会超时
- 解决方案:建议将复杂工具分解为更小的部分,优化缓存
经验教训
我们的 AlphaAdvantage 实施教会了我们几个关键教训:
- 规划 API 限制:从一开始就理解并围绕 API 速率限制进行设计
- 战略缓存:识别高价值缓存机会以提高性能
- 格式化可读性:投资于良好的数据格式化以改善用户体验
- 测试每条路径:在完成之前单独测试所有工具
- 处理 API 复杂性:对于需要多次调用的 API,设计具有更简单范围的工具
核心实施最佳实践
全面的日志记录
有效的日志记录对于调试 MCP 服务器至关重要:
// 启动日志记录
console.error("[Setup] Initializing AlphaAdvantage MCP server...")
// API 请求日志记录
console.error(`[API] Getting stock overview for ${symbol}`)
// 带上下文的错误处理
console.error(`[Error] Tool execution failed: ${error.message}`)
// 缓存操作
console.error(`[Cache] Using cached data for: ${cacheKey}`)
强类型
类型定义防止错误并提高可维护性:
export interface AlphaAdvantageConfig {
apiKey: string
cacheTTL?: Partial<typeof DEFAULT_CACHE_TTL>
baseURL?: string
}
/**
* 验证是否提供了股票代码并且看起来有效
*/
function validateSymbol(symbol: unknown): asserts symbol is string {
if (typeof symbol !== "string" || symbol.trim() === "") {
throw new McpError(ErrorCode.InvalidParams, "A valid stock symbol is required")
}
// 基本符号验证(字母、数字、点)
const symbolRegex = /^[A-Za-z0-9.]+$/
if (!symbolRegex.test(symbol)) {
throw new McpError(ErrorCode.InvalidParams, `Invalid stock symbol: ${symbol}`)
}
}
智能缓存
减少 API 调用并提高性能:
// 默认缓存 TTL(秒)
const DEFAULT_CACHE_TTL = {
STOCK_OVERVIEW: 60 * 60, // 1 小时
TECHNICAL_ANALYSIS: 60 * 30, // 30 分钟
FUNDAMENTAL_ANALYSIS: 60 * 60 * 24, // 24 小时
EARNINGS_REPORT: 60 * 60 * 24, // 24 小时
NEWS: 60 * 15, // 15 分钟
}
// 首先检查缓存
const cachedData = this.cache.get<T>(cacheKey)
if (cachedData) {
console.error(`[Cache] Using cached data for: ${cacheKey}`)
return cachedData
}
// 缓存成功的响应
this.cache.set(cacheKey, response.data, cacheTTL)
优雅的错误处理
实施保持良好用户体验的强大错误处理:
try {
switch (request.params.name) {
case "get_stock_overview": {
// 实施...
}
// 其他情况...
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`)
}
} catch (error) {
console.error(`[Error] Tool execution failed: ${error instanceof Error ? error.message : String(error)}`)
if (error instanceof McpError) {
throw error
}
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
}
}
MCP 资源
资源让您的 MCP 服务器在不执行代码的情况下向 Careti 公开数据。它们非常适合提供上下文,如文件、API 响应或 Careti 在对话期间可以引用的数据库记录。
向 MCP 服务器添加资源
- 定义资源您的服务器将公开:
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "file:///project/readme.md",
name: "Project README",
mimeType: "text/markdown",
},
],
}
})
- 实施读取处理程序以传递内容:
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === "file:///project/readme.md") {
const content = await fs.promises.readFile("/path/to/readme.md", "utf-8")
return {
contents: [
{
uri: request.params.uri,
mimeType: "text/markdown",
text: content,
},
],
}
}
throw new Error("Resource not found")
})
资源使您的 MCP 服务器更具上下文感知能力,允许 Careti 访问特定信息而无需您复制/粘贴。有关更多信息,请参阅官方文档。
常见挑战和解决方案
API 身份验证复杂性
挑战:API 通常有不同的身份验证方法。
解决方案:
- 对于 API 密钥,在 MCP 配置中使用环境变量
- 对于 OAuth,创建单独的脚本以获取刷新令牌
- 安全地存储敏感令牌
// 使用环境中的 API 密钥进行身份验证
const API_KEY = process.env.ALPHAVANTAGE_API_KEY
if (!API_KEY) {
console.error("[Error] Missing ALPHAVANTAGE_API_KEY environment variable")
process.exit(1)
}
// 初始化 API 客户端
const apiClient = new AlphaAdvantageClient({
apiKey: API_KEY,
})
缺失或有限的 API 功能
挑战:API 可能不提供您需要的所有功能。
解决方案:
- 使用可用端点实施回退
- 在必要时创建模拟功能
- 转换 API 数据以满足您的需求
API 速率限制
挑战:大多数 API 都有可能导致失败的速率限制。
解决方案:
- 实施适当的速率限制
- 添加智能缓存
- 提供优雅降级
- 添加关于速率限制的透明错误
if (this.requestsThisMinute >= 5) {
console.error("[Rate Limit] Rate limit reached. Waiting for next minute...")
return new Promise<void>((resolve) => {
const remainingMs = 60 * 1000 - (Date.now() % (60 * 1000))
setTimeout(resolve, remainingMs + 100) // 添加 100ms 缓冲
})
}