news 2026/8/31 4:39:24

MCP内容系统开发实战:多商户隔离与付费墙安全设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP内容系统开发实战:多商户隔离与付费墙安全设计

做了内容平台之后,你迟早会遇到这样的诉求:想接入 AI 能力,让模型能查资料、能写内容、能帮用户找文章。最开始大家本能地写一个 REST API,然后把 API 文档扔给模型,希望模型自己学会调用。结果模型要么找不到入口,要么把付费内容当成免费资源直接搬运,要么在一个多商户系统里串了数据。

这不是开发能力的问题,而是内容系统正在面对一个过去没有的结构性变化:你的平台不再只服务“人”,还要服务“AI 代理”。人可以通过网页操作,AI 代理却需要一套它能理解、能安全调用的工具接口。MCP(Model Context Protocol)就是这个环节里目前最值得采用的标准化方案。

这篇文章想聊的不是“MCP 是什么”这种扫盲,而是把“MCP 内容系统开发”当做一个真实的工程项目来分析:它的架构应该怎么设计,多商户和付费内容怎么落在 MCP 工具层,写代码时有哪些容易踩的坑,以及怎么在生产环境里保证数据不越权、内容不泄漏。读完你会得到一套可以落地的设计思路和最小示例。

1. 为什么要关注 MCP 内容系统

过去做内容系统,核心用户是浏览器和 App。你提供页面、API、权限体系,人在界面上完成阅读、购买和管理。这套模型很成熟,REST API、前后端分离、微服务,任何一个做了三五年开发的工程师都能顺手搭出来。

但现在不一样了。越来越多用户通过 AI 助手访问内容:让助手总结某篇付费文章,让助手在平台内搜索指定领域的资料,甚至让助手在内容平台上创建文案。这些场景里,AI 代理不是一个“浏览器模拟器”,它需要按协议调用工具。你原来的 REST API 当然也能被模型调用,但问题是模型并不知道你的接口该怎么拼、参数是什么、什么时候能用、什么时候不能用。

MCP 解决的是这个“接口发现与标准化”的问题。它像一个插头标准,把内容系统包装成 AI 代理可以动态发现的工具集。只要客户端支持 MCP,模型就能读取你提供的工具清单,按 Schema 调用,拿到结构化结果。

如果只看到这一层,很多人会觉得 MCP 不过是“给 AI 用的 API 网关”。真实情况比这复杂。内容系统一旦接入 MCP,至少有三个问题会被瞬间放大:

  • 数据越权:MCP 工具一旦返回了全部内容,就可能被 AI 代理透传出来,付费墙形同虚设。
  • 多商户隔离:一个内容平台往往有多个商户,商户 A 的内容不能让商户 B 的 AI 代理读到,更不能让平台运营方之外的人随意搜索到。
  • 状态管理:普通的 REST 调用有身份认证,有会话,有审计;MCP 工具调用如果设计不好,就会变成无状态、无身份、无日志的“三无接口”。

所以我的判断是:MCP 内容系统开发的重点,并不是“把一个 API 改写成 MCP”,而是你在协议层重新思考内容授权、租户边界和支付边界。工具只是外壳,隔离和权限才是内核。

2. MCP 核心概念与内容系统的关系

先花一小节把基础概念对齐,后面所有代码和架构讨论都依赖这里。

2.1 MCP 的三个关键概念

MCP 由三个角色组成:

  • MCP Client:发起工具调用的 AI 应用,比如 Claude Desktop、Cursor、Codex,以及你自研的 AI Agent。
  • MCP Server:提供工具、资源、提示词的本地服务或远程服务,你的内容系统需要暴露能力时,就是实现一个 MCP Server。
  • 传输层:MCP 支持 stdio 和 Streamable HTTP 两种主流传输方式。本地客户端常用 stdio,生产环境多采用 HTTP 方式远程部署。

在 MCP 协议里,内容系统最常用到三类能力:

能力说明内容系统里的典型场景
Tool(工具)可被模型调用的函数,带参数和返回值搜索内容、获取文章详情、提交内容、管理订单
Resource(资源)暴露结构化数据,供模型读取上下文商户配置、系统提示词、内容目录
Prompt(提示)预定义提示模板内容总结、SEO 文案生成、付费推荐话术

对“MCP 内容系统”而言,工具是最核心的部分。因为付费内容、多商户权限、内容管理,本质上都是一个个“可授权操作”。

2.2 MCP 与 REST API 有什么区别

很多人问:我都有 REST API 了,为什么还要 MCP?直接让 AI 调用 REST API 不行吗?

可以,但代价高。REST API 是给开发者看的,开发者会看文档、按规范调、处理错误码。AI 模型没有这个“阅读文档并严格按文档调用”的稳定性。MCP 的意义在于让工具 Schema 成为模型上下文的一部分,模型通过工具描述就知道该传什么参数、不该传什么参数。

口径上可以这样理解:

  • REST API 适合“人写代码去调用”,MCP 适合“AI 动态理解和调用”。
  • REST API 是接口标准,MCP 是工具发现与调用协议。
  • 内容系统可以两者共存:REST 服务业务前台,MCP 服务 AI 代理,底层复用同一套领域逻辑。

2.3 MCP 与 Agent Skill 的区别

近半年社区里讨论很多的一个问题是“Agent Skill 和 MCP 有什么区别”。如果只记一句话:MCP 是协议层,Skill 是能力层

MCP 规定的是“工具怎么被客户端发现、怎么调用、怎么返回结果”,它不关心你把这些工具组合成一个多大的“技能包”。Agent Skill 则更像一种指令集和行为编排,比如“内容编辑技能”可以包含:调用搜索工具、调用文章生成工具、调用审核工具,并且定义了这些工具的执行顺序和策略。

在实际工程里,两者是配合关系。你可以在 Agent 系统里定义多个 Skill,每个 Skill 内部调用不同的 MCP 工具;也可以只把 MCP 工具暴露给通用 Agent,让 Agent 自己决定调用顺序。本书的例子以后者为主,这样更接近 MCP 的原生使用方式。

3. 整体架构与技术选型

3.1 系统分层

一个可扩展的 MCP 内容系统,我建议至少分四层:

[人工客户端 Web/App] [AI 客户端 Claude/Cursor/自研 Agent] | | | REST API | MCP 协议 v v [接入网关层] [MCP Server 层] | | +-----------+--------------+ v [内容引擎层:内容、订单、订阅、支付] | v [数据层:PostgreSQL、Redis、对象存储]

关键点在于:MCP Server 不应直接操作数据库,它应该调用内容引擎的领域服务。这样同一个业务逻辑既能服务 REST API,也能服务 MCP 工具,不会出现两套规则。

3.2 技术选型

  • 后端语言:我基于 TypeScript/Node.js 写示例,因为 MCP 的 TypeScript SDK 非常成熟,社区样例多。项目生产环境也可以选 Python,原理一致。
  • Web 框架:Express 或 Fastify,用于对外 REST API 和 Streamable HTTP 传输。
  • 数据库:PostgreSQL,多商户隔离和事务处理能力强。
  • 缓存与限流:Redis,用于接口限流和热点内容缓存。
  • 对象存储:S3 兼容存储,用于保存富媒体内容。
  • MCP SDK@modelcontextprotocol/sdk,目前版本迭代较快,建议按官方文档锁定版本。

关于“mcp服务java”这类搜索词也说明一点:MCP 不是某种语言的专属,官方 SDK 覆盖 TypeScript、Python、Java、Kotlin 等。如果你的技术栈是 Java,完全可以用 Spring Boot 实现 MCP Server,架构思路不变。

4. 环境准备与基础配置

先跑通一个最小环境,后面所有代码都基于这个环境。版本号以你当前工具链为准,本文不锁死具体版本。

4.1 安装基础环境

# 需要 Node.js 18+ node -v # 初始化项目 mkdir mcp-content-system cd mcp-content-system npm init -y # 安装核心依赖 npm install express pg redis @modelcontextprotocol/sdk zod dotenv # 开发依赖 npm install -D typescript tsx @types/express @types/node

4.2 创建基础目录结构

mcp-content-system/ ├── src/ │ ├── mcp/ │ │ └── server.ts # MCP Server 定义 │ ├── routes/ │ │ └── content.ts # REST API 路由 │ ├── services/ │ │ └── contentService.ts # 内容领域服务 │ ├── db/ │ │ └── pool.ts # 数据库连接池 │ └── config/ │ └── index.ts # 环境配置 ├── .env ├── tsconfig.json └── package.json

4.3 配置环境变量

.env文件中准备数据库和对象存储配置:

DATABASE_URL=postgres://user:password@localhost:5432/content_db REDIS_URL=redis://localhost:6379 OBJECT_STORAGE_ENDPOINT=http://localhost:9000 OBJECT_STORAGE_BUCKET=content-bucket MCP_AUTH_TOKEN=sk-local-dev-token PORT=8080

环境变量统一集中管理,不要在代码里硬编码。

5. 内容引擎数据模型与多商户设计

内容系统的第一步,是把数据模型设计好。尤其要多商户,这一步做错了后面全是坑。

5.1 商户表

CREATE TABLE merchants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, status SMALLINT NOT NULL DEFAULT 1, created_at TIMESTAMPTZ NOT NULL DEFAULT now() );

5.2 内容表

CREATE TABLE contents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), merchant_id UUID NOT NULL REFERENCES merchants(id), title VARCHAR(255) NOT NULL, summary TEXT NOT NULL DEFAULT '', body TEXT NOT NULL, status SMALLINT NOT NULL DEFAULT 0, access_type VARCHAR(20) NOT NULL DEFAULT 'free', price DECIMAL(10,2) NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_contents_merchant_status ON contents(merchant_id, status);

access_type只有两种值:freepaidstatus为 1 表示已发布,0 表示草稿。

多商户的核心设计原则是:所有内容表必须有merchant_id,并且所有查询强制带merchant_id条件。不要靠应用层记住“这个用户属于哪个商户”,要在数据库层就隔离。

5.3 付费访问表

用户是否买过某个内容、订阅是否还在有效期内,单独建表:

CREATE TABLE content_access ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), content_id UUID NOT NULL REFERENCES contents(id), user_id UUID NOT NULL, merchant_id UUID NOT NULL, access_type VARCHAR(20) NOT NULL DEFAULT 'single', expires_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(content_id, user_id) ); CREATE INDEX idx_content_access_user ON content_access(user_id, expires_at);

这个表的用途是:只要这里有一条有效记录,用户对这篇内容就是已授权状态。MCP 工具在返回付费正文前,必须先查这张表。

6. 核心业务实现:付费内容 API

在写 MCP 之前,先把普通 API 写出来。因为 MCP 工具最终会复用这里的领域逻辑,而不是另起炉灶。

先创建基础服务文件src/services/contentService.ts

// 文件路径:src/services/contentService.ts import { pool } from '../db/pool'; export interface ContentDetail { id: string; merchantId: string; title: string; summary: string; body: string; accessType: string; price: number; } const PUBLIC_FIELDS = ` id, merchant_id, title, summary, body, access_type, price `; export async function findContentById(id: string): Promise<ContentDetail | null> { const result = await pool.query( `SELECT ${PUBLIC_FIELDS} FROM contents WHERE id = $1 AND status = 1`, [id] ); if (result.rowCount === 0) return null; const row = result.rows[0]; return { id: row.id, merchantId: row.merchant_id, title: row.title, summary: row.summary, body: row.body, accessType: row.access_type, price: row.price, }; } export async function checkUserContentAccess( contentId: string, userId: string ): Promise<boolean> { const result = await pool.query( `SELECT 1 FROM content_access WHERE content_id = $1 AND user_id = $2 AND (expires_at IS NULL OR expires_at > now())`, [contentId, userId] ); return (result.rowCount ?? 0) > 0; }

然后写 REST 路由src/routes/content.ts

// 文件路径:src/routes/content.ts import { Router } from 'express'; import { findContentById, checkUserContentAccess } from '../services/contentService'; export const contentRouter = Router(); contentRouter.get('/api/v1/contents/:id', async (req, res) => { const { id } = req.params; const userId = req.headers['x-user-id'] as string | undefined; const content = await findContentById(id); if (!content) { return res.status(404).json({ error: 'content_not_found' }); } if (content.accessType === 'free') { return res.json(content); } if (!userId || !(await checkUserContentAccess(content.id, userId))) { return res.status(403).json({ error: 'paid_content_requires_access', preview: content.summary, price: content.price, }); } return res.json(content); });

这段逻辑是整篇文章的关键。后续 MCP Server 里的“读付费内容”工具,会调用同一个findContentByIdcheckUserContentAccess,只是入参来源从 HTTP Header 换成 MCP 上下文。

如果你在真实项目中已经有一套成熟的用户体系,可以把userId换成你自己的认证信息,比如 JWT 解析结果。务必要做到:MCP 拿到的用户身份不是客户端自己报出来的,而是认证网关解析并注入的。

7. MCP Server 实现:把内容系统暴露给 AI

现在进入正题。用 MCP SDK 创建一个内容平台 MCP Server。

7.1 创建设置基本框架

// 文件路径:src/mcp/server.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { findContentById, checkUserContentAccess } from '../services/contentService'; const server = new McpServer({ name: 'content-hub-mcp', version: '1.0.0', });

7.2 定义第一个工具:搜索内容

AI 代理最常见的需求是搜索内容。注意,搜索工具只返回标题和摘要,不返回正文。这是付费内容安全的第一道防线。

// 搜索内容,只返回元信息 server.tool( 'search_contents', '在内容平台搜索已发布内容,返回标题、摘要、价格和访问类型,不返回正文', { keyword: z.string().describe('搜索关键词'), merchantId: z.string().uuid().optional().describe('商户ID,可选'), }, async ({ keyword, merchantId }) => { const result = await pool.query( `SELECT id, merchant_id, title, summary, price, access_type FROM contents WHERE status = 1 AND ($1 = '' OR title ILIKE '%' || $1 || '%') AND ($2::uuid IS NULL OR merchant_id = $2) ORDER BY created_at DESC LIMIT 20`, [keyword, merchantId ?? null] ); return { content: [{ type: 'text', text: JSON.stringify(result.rows), }], }; } );

7.3 定义第二个工具:读取免费内容

免费内容可以直接返回正文,但应在工具描述里明确规定“仅限免费内容”。

server.tool( 'read_free_content', '读取免费内容的正文,仅适用于 access_type 为 free 的内容,付费内容请调用 check_paid_content_access', { contentId: z.string().uuid(), }, async ({ contentId }) => { const content = await findContentById(contentId); if (!content) { return { content: [{ type: 'text', text: 'content_not_found' }], isError: true }; } if (content.accessType !== 'free') { return { content: [{ type: 'text', text: 'this_content_is_paid_access_denied' }], isError: true, }; } return { content: [{ type: 'text', text: content.body }] }; } );

7.4 定义第三个工具:读取付费内容

付费内容的权限校验必须放在 MCP Server 内,不能依赖外部模型自觉。这里我们从 MCP 调用上下文里取userId。实际项目中,这个userId可以由 MCP Client 通过 OAuth 或令牌注入,也可以在远程 HTTP 部署时由网关解析。

server.tool( 'read_paid_content', '读取付费内容全文。只有已购买或已订阅的用户才可以读取。若未授权,返回错误提示。', { contentId: z.string().uuid(), }, async ({ contentId }, extra) => { const userId = (extra as any).auth?.userId; // 真实项目中由认证网关注入 if (!userId) { return { content: [{ type: 'text', text: 'unauthorized_missing_user' }], isError: true, }; } const content = await findContentById(contentId); if (!content) { return { content: [{ type: 'text', text: 'content_not_found' }], isError: true }; } const hasAccess = await checkUserContentAccess(content.id, userId); if (!hasAccess) { return { content: [{ type: 'text', text: 'paid_content_requires_access' }], isError: true, }; } return { content: [{ type: 'text', text: content.body }] }; } );

7.5 启动 MCP Server

MCP 最常用的本地传输方式是 stdio。把服务挂到标准输入输出上:

const transport = new StdioServerTransport(); await server.connect(transport);

用 tsx 直接运行:

npx tsx src/mcp/server.ts

你也可以用 MCP Inspector 可视化调试:

npx @modelcontextprotocol/inspector node dist/mcp/server.js

Inspector 会打开一个本地调试面板,列出所有工具,手动传参调用,非常适合验证工具是否注册成功。

8. 多商户隔离与安全边界

前面几次提到多商户隔离,这一节专门把它讲透。

8.1 多商户可能出现的三个问题

  • 数据越权:商户 A 的 AI 代理请求了商户 B 的内容。
  • 搜索越权:全平台搜索返回了其他商户的私密内容。
  • 管理越权:商户 A 的运营人员通过 MCP 工具修改了商户 B 的内容。

8.2 制定隔离策略

在多商户系统里,所有 MCP 工具应该遵循同一套租户上下文。什么是租户上下文?就是一次 MCP 调用里,服务端能够识别这是哪个商户发起的请求

推荐做法:

  1. 每个商户一个独立 API Key / Token。AI 客户端配置 MCP Server 时,通过环境变量注入该商户的 Token。
  2. MCP Server 启动时读取 Token,把它绑定到当前会话的merchantId
  3. 所有数据查询都强制加上merchant_id条件

对应到 MCP 工具里,可以在工具外层包一个公共上下文:

// 文件路径:src/mcp/context.ts export interface McpContext { merchantId: string; userId?: string; }

然后在读取内容时,不仅校验contentId,还校验这个内容的merchant_id是否与当前上下文一致。修改read_free_content为例:

server.tool( 'read_free_content', '读取免费内容的正文,仅限当前商户可见内容', { contentId: z.string().uuid(), }, async ({ contentId }, extra) => { const context = getMcpContext(extra); const content = await findContentById(contentId); if (!content) { return { content: [{ type: 'text', text: 'content_not_found' }], isError: true }; } if (content.merchantId !== context.merchantId) { return { content: [{ type: 'text', text: 'cross_merchant_access_denied' }], isError: true }; } if (content.accessType !== 'free') { return { content: [{ type: 'text', text: 'this_content_is_paid_access_denied' }], isError: true }; } return { content: [{ type: 'text', text: content.body }] }; } );

还可以定义全站工具查重。下面的check_merchant_content_count工具返回某个商家库存的已发布内容总数,供运营 Agent 辅助统计:

server.tool( 'count_merchant_contents', '统计当前商户内容总数,按访问类型分组', {}, async (_args, extra) => { const context = getMcpContext(extra); const result = await pool.query( `SELECT access_type, count(*)::int AS cnt FROM contents WHERE merchant_id = $1 AND status = 1 GROUP BY access_type`, [context.merchantId] ); return { content: [{ type: 'text', text: JSON.stringify(result.rows) }] }; } );

在数据库层还可以加行级安全策略(RLS),这样即使应用层代码漏写了merchant_id过滤,数据库也会拒绝跨商户查询。这一步强烈建议在生产开启:

ALTER TABLE contents ENABLE ROW LEVEL SECURITY; CREATE POLICY content_merchant_isolation ON contents USING (merchant_id = current_setting('app.merchant_id')::uuid);

RLS 是双保险,不是替代品。它不能解决所有 MCP 上下文传递问题,但能挡住最危险的一类 SQL 漏洞。

9. 接入 AI 客户端与效果验证

写完 MCP Server,需要在真实的 AI 客户端里验证工具能不能被正确调用。

9.1 配置 MCP Server

一般支持 MCP 的客户端都使用一个 JSON 配置。下面以命令行工具为例:

mcp add content-hub \ -- command node \ -- args dist/mcp/server.js \ -- env CONTENT_API_BASE=http://localhost:8080 \ -- env MCP_AUTH_TOKEN=sk-merchant-a-token

也可以手写 JSON 配置文件:

{ "mcpServers": { "content-hub": { "command": "node", "args": ["dist/mcp/server.js"], "env": { "CONTENT_API_BASE": "http://localhost:8080", "MCP_AUTH_TOKEN": "sk-merchant-a-token" } } } }

注意:MCP Server 通过 stdio 进程启动,环境变量里的MCP_AUTH_TOKEN就是当前商户的标识。生产环境建议使用 HTTP 传输,把 Token 放到 Authorization Header,而不是进程环境变量。

9.2 手动验证工具注册

在支持 MCP 的客户端里,输入:

列出当前可用的 MCP 工具

如果配置正确,应该能看到search_contentsread_free_contentread_paid_contentcount_merchant_contents这些工具名。

然后可以下两句自然语言指令:

搜索关键词“MCP”的内容,返回前三条的标题和摘要。

此时客户端会调用search_contents,返回结果应该是元信息 JSON,而不是正文。

再下一条:

读取这篇内容的全文:<contentId>

如果内容为付费且未授权,工具返回paid_content_requires_access;如果授权了,才返回正文。这一步能直观验证付费边界是否生效。

9.3 观察 MCP 调用日志

MCP Server 启动后,控制台不会被 AI 客户端的输出打断,因为 stdio 是走标准输入输出传输,日志应该写到 stderr 或独立日志文件。开发时可以在服务里加一个简单日志:

console.error(`[MCP] tool=read_paid_content contentId=${contentId} user=${userId}`);

在生产环境,建议把 MCP 调用日志结构化输出到日志系统,包含:客户端标识、商户 ID、用户 ID、工具名、请求参数摘要、返回状态、耗时。这套日志是未来审计和安全分析的基础。

10. 常见问题与排查思路

下面这些问题,是内容系统接入 MCP 后被问得最多的情况。

问题现象可能原因排查方式解决方案
MCP 客户端提示工具注册不上MCP Server 启动失败,或 stdout 被业务日志污染在客户端外部直接运行node dist/mcp/server.js,检查 stderr 输出日志输出到 stderr,确保 stdout 只留给 MCP 协议
工具存在但模型不调用工具描述不清晰,或参数 Schema 太复杂查看客户端调试界面,看模型是否已经读到工具列表缩短工具描述,给出典型参数示例,同类工具合并
搜索返回了别的商户内容SQL 查询没有强制带merchant_id条件查看 SQL 日志,检查查询参数所有查询加租户条件,开启 RLS 做数据库兜底
付费内容被模型以“总结”的形式全部透传MCP 工具提供了正文,AI 把它复述出来检查 Agent 提示词里是否允许全文引用对 AI 客户端只提供摘要,正文只通过受控渲染读取
付费内容校验失败content_access表查不到记录,或过期时间判断错误手动执行 SQL 查询该用户访问记录检查时间比较逻辑,确保使用数据库时间而非应用服务器时间
工具调用超时每次调用都全量读数据库,没有缓存查看慢查询日志对高频、低实时性需求增加 Redis 缓存;付费校验仍需实时
本地跑得通,远程连不上使用了 stdio 传输,但远程环境不支持检查远程进程是否把 stdout 作为日志输出生产改用 Streamable HTTP 传输,不依赖 stdio

关于“figma mcp 在 codex 中总是工具注册不上”这类类似工具注册问题,本质上也多是配置路径、stdout 污染或 SDK 版本不一致导致。调通一个 MCP Server 后,把同样的排查顺序应用到其他工具,往往能快速定位。

11. 生产环境最佳实践与安全边界

11.1 认证与最小权限

绝不能把管理员 Token 直接配置到 MCP Server 里。应该按商户维度签发独立的 Token,并且每个 Token 只具备该商户操作范围内的权限。可以理解为“商户级最小权限”。

同时要明确:MCP 工具列表里不要暴露不该暴露的工具。例如“删除商户”这样的管理工具,不应该出现在 AI 客户端的工具清单中。工具暴露范围要单独治理。

11.2 付费墙的保护措施

内容系统接 MCP 后,付费墙比普通网页更难保护。网页可以“一次加载全文,前端控制展示”,但 AI 客户端拿到全文后不会受前端控制。所以:

  • 核心原则:正文只对已授权用户可见,摘要和元信息才是公开层。
  • AI 代理需要“总结一篇付费文章”时,不应直接给正文,而是给摘要,或者由内容平台自己提供一个“生成授权摘要”工具。
  • 不建议在一次工具调用里把所有正文塞进返回值,更不要同时返回多篇付费正文,避免上下文被批量带出。

11.3 限流与数据配额

AI 代理调用内容系统的频率可能远高于人工用户。一个会话内,模型可能反复调用搜索、反复读取。生产环境必须有配额和限流:

  • 按商户和用户维度限制每分钟工具调用次数。
  • read_paid_content这类高价值工具单独设每日调用上限。
  • 设置单次返回内容最大字节数,避免超大正文拖垮模型上下文。

11.4 审计日志

MCP 调用日志至少要包含这些字段:

{ "event": "mcp_tool_call", "timestamp": "2025-07-01T10:00:00Z", "merchantId": "merchant-a", "userId": "user-123", "toolName": "read_paid_content", "contentId": "content-456", "authorized": true, "durationMs": 120, "resultSize": 1024 }

有了这些日志,当出现多商户数据越权、付费内容泄漏、配额超限时,你才有可能回溯责任链路。

11.5 部署形态建议

  • 本地/开发环境:stdio 传输,进程由客户端拉起。
  • 生产环境:用 Streamable HTTP 部署 MCP Server,统一走网关做认证、限流、审计。这样能避免 stdio 进程管理导致的不稳定。
  • MCP Server 是无状态最佳。所有状态(用户身份、商户 ID、访问权限)从请求上下文解析,不放进 MCP Server 进程里。

11.6 与 RAG 管线的配合

如果你的系统已经做了 RAG 内容检索,MCP 能承担“更上层的工具调用”职责。RAG 负责向量化召回,MCP 负责向模型暴露可操作的工具。两者不是同一个层次,也不要互相替代。

例如:RAG 可以根据用户问题检索到“哪些内容相关”,然后把候选内容 ID 交给工具,由 MCP 的read_paid_content去校验权限并返回正文。这样的链路比把所有正文灌进向量库更安全,也更接近工程实践。

12. 总结与后续学习方向

MCP 内容系统开发,不是一个“做一个新网站”的活儿,而是给原有内容系统增加一层 AI 协议的接入能力。在这个过程中,最值得投入精力的不是 MCP 协议本身,而是内容授权、多商户隔离、付费边界这三件事。技术协议会随着版本迭代,但这三个工程问题会长期存在。

如果你接下来要实操,建议按这个顺序走:

  1. 先把内容表、访问表、商户表设计好,确保数据层有隔离基础。
  2. 实现一个最小 REST API,验证付费逻辑是否成立。
  3. 实现 MCP Server,把 REST 里已经写好的 area service 方法暴露成工具。
  4. 在 MCP Inspector 里验证工具注册和调用。
  5. 接入 AI 客户端,重点测试付费内容和跨商户访问。
  6. 最后再补生产环境的认证、限流、审计和远程部署。

后续值得深入的方向包括:MCP 的 Streamable HTTP 传输与网关集成、基于 OAuth 的 MCP 授权流程、以及多商户场景下的 RLS 策略细化。要是你在接入 MCP 时遇到工具注册、权限隔离或付费墙绕过的问题,欢迎在评论区把现象和日志贴出来一起讨论。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 4:38:30

大模型评测榜单MLCR-AA深度解析:Claude Fable 5登顶背后的技术选型指南

这次我们来看一个关于大模型评测榜单的新动态。MLCR-AA 榜单的发布&#xff0c;特别是 Claude Fable 5 登顶&#xff0c;对于关注模型能力量化评估的开发者来说&#xff0c;是一个重要的参考坐标。这个榜单不是简单地跑个分&#xff0c;它背后反映的是当前大模型在特定任务集上…

作者头像 李华
网站建设 2026/8/31 4:34:32

魔方盲拧进阶:从PLL原理到WR级执行策略的优化之路

在魔方盲拧领域&#xff0c;PLL&#xff08;Permutation of the Last Layer&#xff0c;最后一层棱块和角块的排列&#xff09;是高级速拧和盲拧中至关重要的一个环节。对于顶尖的三阶魔方盲拧&#xff08;3x3x3 Blindfolded&#xff0c;简称三盲&#xff09;选手而言&#xff…

作者头像 李华
网站建设 2026/8/31 4:32:31

零基础学板绘:数位板与绘画软件选择全指南

零基础学数字绘画时&#xff0c;最先让人焦虑的往往不是“画得不好”&#xff0c;而是“不知道买什么”。搜索一圈之后&#xff0c;会看到数位板、数位屏、iPad、Procreate、Photoshop、SAI2、Clip Studio Paint、Krita 这些名词混在一起&#xff0c;价格从三百到上万都有&…

作者头像 李华
网站建设 2026/8/31 4:32:03

揭秘!市面上那些专业的SEO优化服务机构究竟咋样?

痛点深度剖析我们团队在实践中发现&#xff0c;众多企业在SEO优化方面面临诸多困境。一方面&#xff0c;流量获取方面难题频出&#xff0c;SEO见效慢&#xff0c;不少企业做了半年优化&#xff0c;关键词排名毫无变动&#xff0c;开始质疑SEO的有效性&#xff1b;SEM烧钱快&…

作者头像 李华
网站建设 2026/8/31 4:30:39

DeepSeek Flash接入Claude Code实战:与GPT-5.6 Luna编码对比

这次我们直接看一个最近热度非常高的组合&#xff1a;DeepSeek Flash 正式版接入 Claude Code&#xff0c;再拉上 GPT-5.6 Luna 做一轮编码场景下的“二番战”。先说结论方向&#xff1a;DeepSeek Flash 更适合高频、批量、预算敏感的任务&#xff0c;GPT-5.6 Luna 更适合需要高…

作者头像 李华
网站建设 2026/8/31 4:26:24

基于Spark的河南省空气质量数据分析与预测系统实战

每年到毕业设计选题季&#xff0c;很多同学都会纠结一个问题&#xff1a;想做大数据方向&#xff0c;又担心纯理论不好做&#xff1b;想带上机器学习预测&#xff0c;又怕自己基础不够。如果你也处于这个阶段&#xff0c;可以考虑“基于 Spark 的河南省空气质量数据分析与预测系…

作者头像 李华