本文已收錄在Github,關(guān)注我,緊跟本系列專欄文章,咱們下篇再續(xù)!
- ?? 魔都架構(gòu)師 | 全網(wǎng)30W技術(shù)追隨者
- ?? 大廠分布式系統(tǒng)/數(shù)據(jù)中臺實(shí)戰(zhàn)專家
- ?? 主導(dǎo)交易系統(tǒng)百萬級流量調(diào)優(yōu) & 車聯(lián)網(wǎng)平臺架構(gòu)
- ?? AIGC應(yīng)用開發(fā)先行者 | 區(qū)塊鏈落地實(shí)踐者
- ?? 以技術(shù)驅(qū)動創(chuàng)新,我們的征途是改變世界!
- ?? 實(shí)戰(zhàn)干貨:編程嚴(yán)選網(wǎng)
0 前言
向LLM暴露服務(wù)器上的數(shù)據(jù)和內(nèi)容
資源(Resources)是MCP核心概念,允許服務(wù)器將數(shù)據(jù)和內(nèi)容暴露,供客戶端讀取,并作為大模型交互的上下文使用。
注意
資源是由應(yīng)用控制的,即客戶端應(yīng)用可自行決定資源的使用方式和時機(jī)。不同 MCP 客戶端可能不同方式處理資源,如:
- Claude Desktop 要求用戶手動選擇資源后才能用
- 其他客戶端可能會基于啟發(fā)式方法自動選擇資源
- 有些實(shí)現(xiàn)甚至允許 AI 模型自行決定使用哪些資源。
因此,服務(wù)器開發(fā)者在實(shí)現(xiàn)資源支持時,應(yīng)考慮各種交互方式。如希望自動將數(shù)據(jù)暴露給模型,應(yīng)使用由模型控制的工具。
1 概述
資源可代表 MCP 服務(wù)器希望提供給客戶端的任何類型的數(shù)據(jù),包括但不限于:
- 文件內(nèi)容
- 數(shù)據(jù)庫記錄
- API 響應(yīng)
- 實(shí)時系統(tǒng)數(shù)據(jù)
- 截圖和圖像
- 日志文件
- 其他各種數(shù)據(jù)
每個資源都有一個唯一 URI 標(biāo)識,內(nèi)容可以是文本或二進(jìn)制數(shù)據(jù)。
2 URI
資源通過 URI 唯一標(biāo)識,格式如下:
[protocol]://[host]/[path]
示例:
file:///home/user/documents/report.pdf
postgres://database/customers/schema
screen://localhost/display1
URI 的協(xié)議和路徑結(jié)構(gòu)由 MCP 服務(wù)器自行定義,服務(wù)器可定制自己的 URI 方案。
3 類型
資源內(nèi)容可分為兩種類型:
3.1 文本
包含 UTF-8 編碼的文本數(shù)據(jù),適合以下內(nèi)容:
- 源代碼
- 配置文件
- 日志文件
- JSON/XML 數(shù)據(jù)
- 純文本
3.2 二進(jìn)制
包含使用 base64 編碼的原始二進(jìn)制數(shù)據(jù),適合以下內(nèi)容:
- 圖像
- 音頻文件
- 視頻文件
- 其他非文本格式
4 發(fā)現(xiàn)方式
客戶端可通過兩種方式發(fā)現(xiàn)可用資源:
4.1 直接資源列表
服務(wù)器通過 resources/list
接口提供明確的資源清單,每個資源包含以下信息:
{
uri: string; // 資源唯一標(biāo)識
name: string; // 可讀名稱
description?: string; // 可選描述信息
mimeType?: string; // 可選 MIME 類型
}
4.2 資源模板
對于動態(tài)資源,服務(wù)器可暴露 URI 模板,客戶端可根據(jù)模板生成合法的資源 URI:
{
uriTemplate: string; // 遵循 RFC 6570 的 URI 模板
name: string; // 可讀名稱
description?: string; // 可選描述
mimeType?: string; // 可選 MIME 類型
}
5 讀取資源內(nèi)容
客戶端使用資源 URI 通過 resources/read
請求讀取資源,服務(wù)器返回資源內(nèi)容列表:
{
contents: [
{
uri: string; // 資源 URI
mimeType?: string; // 可選 MIME 類型
// 二選一:
text?: string; // 文本內(nèi)容
blob?: string; // 二進(jìn)制內(nèi)容(base64 編碼)
}
]
}
服務(wù)器可在一次 resources/read
請求中返回多個資源,如讀取目錄時可返回其中的所有文件。
6 更新
MCP 支持實(shí)時資源更新,主要有兩種機(jī)制:
6.1 列表變更
當(dāng)服務(wù)器的資源列表發(fā)生變化時,通過 notifications/resources/list_changed
通知客戶端。
6.2 內(nèi)容變更
客戶端可以訂閱某個資源的變更:
- 客戶端發(fā)送
resources/subscribe
請求并附帶資源 URI; - 服務(wù)器在資源內(nèi)容變更時,發(fā)送
notifications/resources/updated
通知; - 客戶端通過
resources/read
獲取最新內(nèi)容; - 客戶端可以發(fā)送
resources/unsubscribe
取消訂閱。
7 示例
簡單的 MCP 服務(wù)器實(shí)現(xiàn)資源支持的例子:
// 提供資源列表
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "file:///logs/app.log",
name: "應(yīng)用日志",
mimeType: "text/plain"
}
]
};
});
// 讀取資源內(nèi)容
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
if (uri === "file:///logs/app.log") {
const logContents = await readLogFile();
return {
contents: [
{
uri,
mimeType: "text/plain",
text: logContents
}
]
};
}
throw new Error("未找到資源");
});
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri="file:///logs/app.log",
name="應(yīng)用日志",
mimeType="text/plain"
)
]
@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:
if str(uri) == "file:///logs/app.log":
log_contents = await read_log_file()
return log_contents
raise ValueError("未找到資源")
# 啟動服務(wù)器
async with stdio_server() as streams:
await app.run(
streams[0],
streams[1],
app.create_initialization_options()
)
8 最佳實(shí)踐
實(shí)現(xiàn)資源支持時,建議:
- 使用清晰、具描述性的資源名稱和 URI;
- 添加有用的描述,幫助大模型理解資源;
- 在已知的情況下設(shè)置合適的 MIME 類型;
- 為動態(tài)內(nèi)容實(shí)現(xiàn)資源模板;
- 對頻繁變更的資源使用訂閱機(jī)制;
- 出錯時返回清晰明了的錯誤信息;
- 對資源列表進(jìn)行分頁處理(如有必要);
- 在適當(dāng)情況下緩存資源內(nèi)容;
- 處理資源前先驗(yàn)證 URI;
- 文檔中注明自定義的 URI 方案。
9 安全
在暴露資源時,請注意以下安全措施:
- 驗(yàn)證所有資源 URI 的合法性;
- 實(shí)施適當(dāng)?shù)脑L問控制策略;
- 清理文件路徑,防止目錄遍歷攻擊;
- 謹(jǐn)慎處理二進(jìn)制數(shù)據(jù);
- 對資源讀取設(shè)置速率限制;
- 審計資源訪問記錄;
- 傳輸過程中加密敏感數(shù)據(jù);
- 驗(yàn)證 MIME 類型是否符合預(yù)期;
- 為耗時較長的讀取操作設(shè)置超時機(jī)制;
- 適時清理過期或無效資源。
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!