以「asset-price-mcp」为例,从 0 开发 MCP Server

简介

关于 MCP 的介绍之前已经写过,可以参考 《Model Context Protocol (MCP) 快速开始》。

今天从 0 开始开发一个 MCP Server,实现一个资产价格查询的 MCP Server。


实现步骤

1. 设置 MCP 服务器

首先,使用 @modelcontextprotocol/sdk 提供的 McpServer 类创建一个 MCP 服务器实例:

javascript 复制代码
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'

const server = new McpServer({
  name: 'asset-price',
  version: '1.0.0',
})

这里,我们将服务器命名为 "asset-price",版本号为 "1.0.0"。

2. 定义数据结构

使用 zod 库定义资产符号和资产价格的数据结构,以确保从 API 获取的数据符合预期格式:

javascript 复制代码
import { z } from 'zod'

const AssetSymbolSchema = z.object({
  name: z.string(),
  symbol: z.string(),
})

const AssetPriceSchema = z.object({
  name: z.string(),
  price: z.number(),
  symbol: z.string(),
  updatedAt: z.string(),
  updatedAtReadable: z.string(),
})

这些模式用于验证从外部 API 获取的数据的结构和类型。

3. 实现缓存机制

为了提高性能并减少对外部 API 的请求次数,实现了一个简单的内存缓存:

javascript 复制代码
class SimpleCache {
  private cache: Map<string, CacheEntry<any>> = new Map();

  get<T>(key: string): T | null {
    const entry = this.cache.get(key);
    if (!entry) return null;

    if (Date.now() - entry.timestamp > CACHE_TTL) {
      this.cache.delete(key);
      return null;
    }

    return entry.data as T;
  }

  set<T>(key: string, data: T): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
  }

  clear(): void {
    this.cache.clear();
  }
}

const apiCache = new SimpleCache();

该缓存会在设定的时间间隔后自动清除过期的数据。

4. 获取资产符号和价格数据

定义一个通用的函数 fetchApiData,用于从外部 API 获取数据并进行验证:

javascript 复制代码
async function fetchApiData<T>(url: string, schema: z.ZodSchema<T>, useCache = true): Promise<T | null> {
  if (useCache) {
    const cachedData = apiCache.get<T>(url);
    if (cachedData) {
      return cachedData;
    }
  }

  const headers = {
    "User-Agent": USER_AGENT,
    "Accept": "application/json",
  };

  try {
    const response = await fetchWithTimeout(url, { headers });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}, url: ${url}`);
    }

    const data = await response.json();
    const parsedData = schema.parse(data);

    if (useCache && parsedData) {
      apiCache.set(url, parsedData);
    }

    return parsedData;
  } catch (error: any) {
    if (error instanceof z.ZodError) {
      console.error(`Schema validation failed for ${url}:`, error.errors);
    } else if (error.name === 'AbortError') {
      console.error(`Request timeout for ${url}`);
    } else {
      console.error(`API request failed for ${url}:`, error);
    }
    return null;
  }
}

该函数首先检查缓存,如果缓存中没有数据,则从 API 获取数据,并使用 zod 模式进行验证。

5. 定义 MCP 工具

在 MCP 服务器上注册一个工具 get_asset_price,用于检索当前的资产价格信息:

javascript 复制代码
server.tool(
  "get_asset_price",
  "Retrieves current pricing information for various assets including precious metals and cryptocurrencies",
  {
    random_string: z.string().optional().describe("Dummy parameter for no-parameter tools")
  },
  async () => {
    try {
      const symbols = await fetchApiData(
        `${GOLD_API_BASE}/symbols`,
        z.array(AssetSymbolSchema)
      );

      if (!symbols?.length) {
        return {
          content: [{
            type: "text",
            text: "No available asset symbols found. Service might be temporarily unavailable.",
          }]
        };
      }

      const prices: AssetPrice[] = [];
      for (const { symbol } of symbols) {
        const priceData = await fetchApiData(`${GOLD_API_BASE}/price/${symbol}`, AssetPriceSchema);
        if (priceData) {
          prices.push(priceData);
        }
      }

      return {
        content: [{
          type: "text",
          text: prices.map(price => `${price.name}: ${price.price}`).join("\n"),
        }]
      };
    } catch (error) {
      console.error("Tool execution failed:", error);
      return {
        content: [{
          type: "text",
          text: "An error occurred while processing your request. Please try again later.",
        }]
      };
    }
  }
);

此工具允许 LLM 通过 MCP 服务器请求资产价格数据,并以文本形式返回结果。

相关推荐
DigitalOcean42 分钟前
Qwen3.8-2.4T-A95B 部署与性能解析:DigitalOcean 无服务器推理实测
llm
XLYcmy1 小时前
小红书 算法一面 四
深度学习·llm·sft·强化学习·多模态·grpo·奖励函数
幻灵尔依5 小时前
LLM 推理核心链路&缓存命中讲解
llm·agent·ai编程
武子康7 小时前
DeepSeek Harness:Cordis 如何让插件可卸载、可依赖、可重组
人工智能·llm·agent
武子康7 小时前
从 Pi 学习设计自己的 Agent Harness:一条可验证的垂直生产线
人工智能·llm·agent
徐且行7 小时前
DeepSeek Harness初体验:入门、安装与自定义插件
深度学习·自然语言处理·llm·agents·deepseek·harness
拾年2757 小时前
让 AI 自己卷自己:50 行代码实现「LLM 当评委 + 多选一」的 Harness 工程
langchain·llm
漂流瓶jz7 小时前
一文读懂大模型生态:分类/参数/结构/训练/GPU/评测/排行/社区
人工智能·大模型·llm·openai·nvidia·deepseek·anthropic
AINative软件工程9 小时前
LLM 请求合并工程实践:用 Single-Flight 把并发重复调用从 N 次砍成 1 次
后端·llm·ai编程
Flynt17 小时前
花一下午把Qwen3.8-27B跑在本地,最坑的不是显存
开源·llm·llama