发布于 2026-01-06 5 阅读
0

[Agentica] 为什么你的 MCP 服务器会失败(如何打造 100% 成功的 MCP 服务器)

[Agentica] 为什么你的 MCP 服务器会失败(如何打造 100% 成功的 MCP 服务器)

概括


特吉卡

MCP(模型上下文协议)给人工智能生态系统带来了一股新浪潮,但最近对其稳定性和有效性提出了许多批评。

但是,如果您将 MCP 转换为函数单元,并使用以下三种策略实现函数调用,则可以创建一个始终有效(100%)的 AI 代理。将函数调用与 MCP 和 Agentica 结合使用。

不要直接将 MCP 服务器添加到mcp_servers列表中,而是使用函数调用与 MCP 进行交互。这种方法可以确保 MCP 服务器成功运行。

1. 前言


Claude Desktop 上的 Github MCP 崩溃太多

近年来,MCP(模型上下文协议)在人工智能代理生态系统中越来越受欢迎。

然而,与此同时,人工智能的早期采用者们常常对MCP的不稳定性表示不满。我经常浏览LinkedIn上的帖子,毫不夸张地说,我每天至少会看到3条类似下面这样的帖子,这表明MCP还为时尚早:

人们把MCP吹捧得好像万能灵药,但它极其不稳定。它经常崩溃、故障,而且代币成本也高得令人望而却步。它根本不适用于企业环境。

或许当更便宜、更大、更智能的车型出现时,MCP的愿景才能实现。但就目前而言,MCP还为时尚早,且被过度炒作。

通过Agentica的函数调用,100% 可用的 GitHub 代理。

令人惊讶的是,通过分解 MCP 服务器的功能并自行实现 AI 函数调用,您可以将 AI 应用程序的稳定性提升至 100%。即使是像 8b 这样可以在个人笔记本电脑上运行的小型模型,它也能完美运行。

Agentica建议使用 MCP 进行函数调用,因为您可以高效地调用 MCP 服务器提供的函数,避免出现幻觉,并使用以下策略安全地管理它们:

如果你想创建一个每次都能 100% 成功的 AI 应用程序——不仅仅是像 Github MCP 那样仅限于单个服务器、只有 30 个函数调用的玩具型 AI 应用程序,而是包含多个服务器和数百个函数的应用程序——你应该实施文档驱动开发

下面是一个拥有 289 个功能的电商平台 AI 代理,正如您所见,它运行完美,没有任何错误。让我们使用Agentica ,通过 MCP 函数调用和文档驱动开发,创建一个 100% 成功的 AI 应用。

100% 可用的电子商务代理,具备 289 项功能,并配备 8b 迷你模型

2. 函数调用


2.1. JSON Schema 规范

JSON Schema 规范

MCP 中定义的 JSON 模式必须转换为符合目标 AI 供应商规范的格式。

您知道吗?不同的人工智能厂商使用的 JSON 模式规范各不相同。OpenAI、Anthropic Claude 和 Google Gemini 使用的 JSON 模式规范就各不相同。

然而,模型上下文协议对 JSON 模式规范没有任何限制。原则上,它可以使用所有现有的 JSON 模式版本。但实际上,每个人工智能厂商都有自己的一套 JSON 模式规范。

问题就出在这里。

{
  name: "divide",
  inputSchema: {
    x: {
      type: "number",
    },
    y: {
      anyOf: [ // not supported in Gemini
        {
          type: "number",
          exclusiveMaximum: 0, // not supported in OpenAI and Gemini
        },
        {
          type: "number",
          exclusiveMinimum: 0, // not supported in OpenAI and Gemini
        },
      ],
    },
  },
  description: "Calculate x / y",
}
Enter fullscreen mode Exit fullscreen mode

我的 MCP 服务器与 Claude 配合良好,但与 OpenAI 配合不佳。

  • 这是因为您使用了 OpenAI 不支持的 JSON 模式功能。
  • { type: "string", format: "uuid" }
  • { type: "integer", minimum: 0 }

为什么我的 MCP 服务器可以与 OpenAI 和 Claude 配合使用,但不能与 Gemini 配合使用?

  • 因为您使用了 Gemini 不支持的 JSON 模式功能。
  • { anyOf: [ { type: "boolean" }, { type: "integer" } ] }
  • { $ref: "#/$defs/Recursive" }

Claude 支持最新的 JSON Schema 规范,即 2020-12 草案版本。然而,该规范包含大量冗余、歧义和模糊的表达式,必须进行修正才能正确使用。

OpenAI 和 Gemini 拥有各自独特的 JSON Schema 规范。它们没有遵循标准,而是创建了自己的 JSON Schema 规范。遗憾的是,它们非但没有增强 JSON Schema 规范的表达能力,反而大幅削减了标准 JSON Schema 规范中的许多功能。

更令人困惑的是 Anthropic Claude 为 创建的服务器 SDK model-context-protocol。该服务器 SDK(通过zod-to-schema)基于已有 10 年历史的 v7 草案版本生成 JSON 模式。

错误的 JSON Schema

现在你明白为什么你的 MCP 服务器在启动之前就会失败了吗?

只有将 MCP 服务器分解为功能单元,将其 JSON 模式转换为与目标 AI 供应商匹配的模式,并进行修正,才能创建一个功能完善的 AI 应用程序。

我们的Agentica通过以下 JSON 模式转换流程进行转换@samchon/openapi。当基于 MCP 创建 AI 应用程序时,您也必须经历类似的 JSON 模式转换流程:

  1. 将输入的 JSON 模式转换为 2020-12 草案版本
  2. 删除所有冗余/含糊/模棱两可的表达方式以进行更正。
  3. 转换为适合目标 AI 供应商的 JSON 模式模型
import { Agentica, assertMcpController } from "@agentica/core";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const client = new Client({
  name: "shopping",
  version: "1.0.0",
});
await client.connect(new StdioClientTransport({
  command: "npx",
  args: ["-y", "@wrtnlabs/shopping-mcp"],
}));

const agent = new Agentica({
  model: "chatgpt",
  vendor: {
    api: new OpenAI({ apiKey: "********" }),
    model: "gpt-4o-mini",
  },
  controllers: [
    await assertMcpController({
      name: "shopping",
      model: "chatgpt",
      client,
    }),
  ],
});
await agent.conversate("I wanna buy a Macbook");
Enter fullscreen mode Exit fullscreen mode

2.2 验证反馈

姓名 地位
ObjectConstraint 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ObjectFunctionSchema 2️⃣2️⃣4️⃣2️⃣2️⃣2️⃣2️⃣2️⃣5️⃣2️⃣
ObjectHierarchical 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣2️⃣1️⃣1️⃣2️⃣
ObjectJsonSchema 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ObjectSimple 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ObjectUnionExplicit 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ObjectUnionImplicit 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ShoppingCartCommodity 1️⃣2️⃣2️⃣3️⃣1️⃣1️⃣4️⃣2️⃣1️⃣2️⃣
ShoppingOrderCreate 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ShoppingOrderPublish 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣❌1️⃣1️⃣1️⃣
ShoppingSaleDetail 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣
ShoppingSalePage 1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣1️⃣

https://wrtnlabs.io/agentica/docs/concepts/function-calling/#orchestration-strategy

验证反馈可以纠正人工智能生成的论证中的错误。

MCP 和 AI 函数调用经常会组合类型错误的参数(参数值)。这是导致 MCP 服务器和 AI 应用程序崩溃的另一个主要原因。

为了解决这个问题,当 AI 代理为特定函数组合错误类型的参数时,你需要提供有关哪里出错以及如何出错的具体详细信息,然后请求 AI 代理重建参数值。

Agentica将此称为验证反馈策略,它是提高函数调用成功率的核心策略之一。上表显示了在 OpenAIo3-mini模型上,AI 在每种场景下成功组合参数所需的尝试次数。除 1 以外的任何数字都表示执行了相应的验证反馈次数。

以上示例ObjectFunctionSchema代表ShoppingCartCommodity了相当复杂的类型,我们可以看出,如果没有验证反馈,成功的可能性很小。MCP 服务器失败的第二个原因可能是缺少这种验证反馈策略。

export interface IMcpLlmFunction<Model extends ILlmSchema.Model> {
  name: string;
  description?: string | undefined;
  parameters: ILlmSchema.IParameters<Model>;
  validate: (args: unknown) => IValidation<unknown>;
}
Enter fullscreen mode Exit fullscreen mode

IMcpLlmFunction类型@samchon/openapi

2.3 选择器代理

编排策略

https://wrtnlabs.io/agentica/docs/concepts/function-calling/#validation-feedback

选择候选函数以减少上下文信息。

查看 OpenAI 或 Claude API 时,你会注意到一个名为 `<field_name>` 的字段,mcp_servers它旨在接收整个 MCP 服务器的信息。当你一次性将整个 MCP 服务器文档输入到 AI 代理时,上下文和标记的数量会变得非常庞大。

这就是为什么即使使用只有 30 个功能的 GitHub MCP 服务器,Claude Desktop 也经常崩溃或出现幻觉,甚至还没完成任何操作。如果功能从 30 个增加到 300 个,文档大小将达到约 8 MB,代码量将达到 21 万行。这样的配置根本无法处理。

agent=Agent(
  name="Assistant",
  instructions="Use the tools to achieve the task",
  mcp_servers=[mcp_server_1, mcp_server_2]
)
Enter fullscreen mode Exit fullscreen mode

为了解决这个问题,Agentica内部使用了一个选择器代理来筛选待调用的候选函数。该选择器代理收集每个函数的名称和描述IMcpLlmFunction.description,并执行 RAG(检索增强生成)搜索,以检查用户的语句是否暗示调用特定函数。如果暗示调用,则将该函数添加到候选列表中。

这样,调用代理通常只有 0-2 个目标函数,很少超过 4-5 个,这大大减少了上下文和令牌的消耗。它避免了将整个 MCP 服务器全部投入使用时,因大量上下文消耗而导致的幻觉mcp_servers

使用选择器代理来筛选候选函数并节省上下文消耗是使 MCP 服务器 100% 成功的第三种策略。

3. 文档驱动开发


@Controller(`shoppings/customers/sales`)
export class ShoppingCustomerSaleController {
  /**
   * List up every summarized sales.
   *
   * List up every {@link IShoppingSale.ISummary summarized sales}.
   *
   * As you can see, returned sales are summarized, not detailed. It does not
   * contain the SKU (Stock Keeping Unit) information represented by the
   * {@link IShoppingSaleUnitOption} and {@link IShoppingSaleUnitStock} types.
   * If you want to get such detailed information of a sale, use
   * `GET /shoppings/customers/sales/{id}` operation for each sale.
   *
   * > If you're an A.I. chatbot, and the user wants to buy or compose
   * > {@link IShoppingCartCommodity shopping cart} from a sale, please
   * > call the `GET /shoppings/customers/sales/{id}` operation at least once
   * > to the target sale to get detailed SKU information about the sale.
   * > It needs to be run at least once for the next steps.
   *
   * @param input Request info of pagination, searching and sorting
   * @returns Paginated sales with summarized information
   * @tag Sale
   *
   * @author Samchon
   */
  @TypedRoute.Patch()
  public index(
    @ShoppingCustomerAuth() customer: IShoppingCustomer,
    @TypedBody() input: IShoppingSale.IRequest,
  ): Promise<IPage<IShoppingSale.ISummary>>;
  /**
   * Get a sale with detailed information.
   *
   * Get a {@link IShoppingSale sale} with detailed information including
   * the SKU (Stock Keeping Unit) information represented by the
   * {@link IShoppingSaleUnitOption} and {@link IShoppingSaleUnitStock} types.
   *
   * > If you're an A.I. chatbot, and the user wants to buy or compose a
   * > {@link IShoppingCartCommodity shopping cart} from a sale, please call
   * > this operation at least once to the target sale to get detailed SKU
   * > information about the sale.
   * >
   * > It needs to be run at least once for the next steps. In other words,
   * > if you A.I. agent has called this operation to a specific sale, you
   * > don't need to call this operation again for the same sale.
   * >
   * > Additionally, please do not summarize the SKU information. Just show
   * > the every options and stocks in the sale with detailed information.
   *
   * @param id Target sale's {@link IShoppingSale.id}
   * @returns Detailed sale information
   * @tag Sale
   *
   * @author Samchon
   */
  @TypedRoute.Get(":id")
  public at(
    @props.AuthGuard() actor: Actor,
    @TypedParam("id") id: string & tags.Format<"uuid">,
  ): Promise<IShoppingSale>;
}
Enter fullscreen mode Exit fullscreen mode

https://github.com/samchon/shopping-backend

函数和类型的文档完善程度决定了您的人工智能应用程序的质量。

如果你想使用 MCP 创建一个企业级 AI 应用,而不仅仅是一个玩具项目——一个由 MCP 服务器提供数百个功能的 AI 应用——你需要认真记录每个功能。

请考虑选择器代理应如何确定在哪些情况下调用哪个函数,并详细说明每个函数的用途。如果函数之间存在执行前提条件,请将其包含在内IMcpLlmFunction.description。AI 代理将自行按正确的顺序执行这些函数。

此外,请详细记录每种 DTO 类型及其属性。仅仅定义类型是不够的。您的文档应该解释 AI 代理如何根据用户的语音构建每个属性。

@samchon/shopping-backend这是一个拥有 289 个函数的 API 服务器,可能比你见过的任何服务器都多。它还展示了 AI 应用开发文档的最佳实践,函数调用成功率接近 100%。

与其按照 MCP 理念构建工作流代理,不如列出适合您 AI 应用用途的功能。重点在于为每个功能和类型编写详尽的文档。这样,您的 AI 应用将变得可扩展、灵活且高效。

import { tags } from "typia";

/**
 * Restriction information of the coupon.
 *
 * @author Samchon
 */
export interface IShoppingCouponRestriction {
  /**
   * Access level of coupon.
   *
   * - public: possible to find from public API
   * - private: unable to find from public API
   *   - arbitrarily assigned by the seller or administrator
   *   - issued from one-time link
   */
  access: "public" | "private";

  /**
   * Exclusivity or not.
   *
   * An exclusive discount coupon refers to a discount coupon that has an
   * exclusive relationship with other discount coupons and can only be
   * used alone. That is, when an exclusive discount coupon is used, no
   * other discount coupon can be used for the same
   * {@link IShoppingOrder order} or {@link IShoppingOrderGood good}.
   *
   * Please note that this exclusive attribute is a very different concept
   * from multiplicative, which means whether the same coupon can be
   * multiplied and applied to multiple coupons of the same order, so
   * please do not confuse them.
   */
  exclusive: boolean;

  /**
   * Limited quantity issued.
   *
   * If there is a limit to the quantity issued, it becomes impossible
   * to issue tickets exceeding this value.
   *
   * In other words, the concept of N coupons being issued on
   * a first-come, first-served basis is created.
   */
  volume: null | (number & tags.Type<"uint32">);

  /**
   * Limited quantity issued per person.
   *
   * As a limit to the total amount of issuance per person, it is
   * common to assign 1 to limit duplicate issuance to the same citizen,
   * or to use the NULL value to set no limit.
   *
   * Of course, by assigning a value of N, the total amount issued
   * to the same citizen can be limited.
   */
  volume_per_citizen: null | (number & tags.Type<"uint32">);

  /**
   * Expiration day(s) value.
   *
   * The concept of expiring N days after a discount coupon ticket is issued.
   *
   * Therefore, customers must use the ticket within N days, if possible,
   * from the time it is issued.
   */
  expired_in: null | (number & tags.Type<"uint32">);

  /**
   * Expiration date.
   *
   * A concept that expires after YYYY-MM-DD after a discount coupon ticket
   * is issued.
   *
   * Double restrictions are possible with expired_in, of which the one
   * with the shorter expiration date is used.
   */
  expired_at: null | (string & tags.Format<"date-time">);
}
Enter fullscreen mode Exit fullscreen mode

4. TypeScript 类函数调用


import { Agentica } from "@agentica/core";
import OpenAI from "openai";
import typia from "typia";

import { BbsArticleService } from "./services/BbsArticleService";

const agent = new Agentica({
  vendor: {
    model: "gpt-4o-mini",
    api: new OpenAI({ apiKey: "********" }),
  },
  controllers: [
    {
      protocol: "class",
      application: typia.llm.application<BbsArticleService, "chatgpt">(),
      execute: new BbsArticleService(),
    },
  ],
});
await agent.conversate("I want to write an article.");
Enter fullscreen mode Exit fullscreen mode

不一定要用 MCP。直接调用 TypeScript 类的函数即可。

您已经了解了Agentica为确保 MCP 100% 成功而采用的策略。但Agentica支持的协议不仅限于 MCP,还包括TypeScript Class

如果您想为 AI 应用程序提供的功能不需要服务器,并且只需使用 TypeScript 类即可实现,那就去做吧。

您的 TypeScript 类类型将在编译级别进行分析typia,以便自动生成匹配的 AI 函数调用模式。

5. OpenAPI 函数调用


import { Agentica } from "@agentica/core";
import { HttpLlm, OpenApi } from "@samchon/openapi";
import typia from "typia";

const agent = new Agentica({
  model: "chatgpt",
  vendor: {
    api: new OpenAI({ apiKey: "*****" }),
    model: "gpt-4o-mini",
  },
  controllers: [
    {
      protocol: "http",
      name: "shopping",
      application: HttpLlm.application({
        model: "chatgpt",
        document: await fetch(
          "https://shopping-be.wrtn.ai/editor/swagger.json",
        ).then((r) => r.json()),
      }),
      connection: {
        host: "https://shopping-be.wrtn.ai",
        headers: {
          Authorization: "Bearer *****",
        },
      },
    },
  ],
});
await agent.conversate("I want to buy a MacBook Pro");
Enter fullscreen mode Exit fullscreen mode

不要不必要地迁移到MCP。OpenAPI目前运行良好。

您已经了解了Agentica为确保 MCP 100% 成功而采用的策略。但Agentica也支持Swagger/OpenAPI协议。

如果您正在考虑迁移到 MCP 以基于现有后端服务器开发 AI 应用程序,则无需这样做。只需使用后端服务器的 OpenAPI 文档(swagger.json)。

您的 OpenAPI 文档将被转换为匹配的 AI 函数调用模式,绕过转换逻辑。@samchon/openapi

OpenAPI转换图

6. Agentica


Agentica 标志

Agentica 是一个专门用于函数调用的 TypeScript AI 代理框架。

它包含了本单元讨论的所有 MCP 增强策略,并且还支持 TypeScript 类和 OpenAPI 的函数调用。

它可能是目前最简单、最可靠的人工智能框架,请务必了解一下。

文章来源:https://dev.to/samchon/why-your-mcp-server-fails-how-to-make-100-successful-mcp-server-iem