资讯详情

资讯详情

python-sdk 服务端資源開發指南:用 `@mcp.resource` 對應用程式公開資料

人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载資源Resource是 MCP 伺服器向應用程式公開、供其主動讀取的資料與「由模型決定呼叫」的工具形成互補。本文以 python-sdk 的MCPServer為主體完整講解靜態資源、資源範本、佔位符約定、回傳值序列化與 MIME 型別宣告並結合 src/mcp/server/mcpserver/server.py 的裝飾器原始碼說明底層驗證機制讓你能夠立即在自己的伺服器上公開設定檔、紀錄、文件與二進位內容。資源與工具的分界MCP 協定定義了三種基本元件工具、資源與提示詞。其中最容易混淆的就是工具與資源的區別但分界其實很清楚工具Tool是模型決定要呼叫的東西——模型在推理過程中主動發起tools/call資源Resource是應用程式決定要載入的東西——一個設定檔、一筆紀錄、一份文件由應用程式或使用者把它放到模型面前當作上下文。換句話說工具讓模型採取行動資源讓應用程式讀取。你只需要在一個普通的 Python 函式上加上mcp.resource(uri)就宣告了一個資源。第一個資源最簡單的資源範例如下完整範例見 docs_src/resources/tutorial001.pyfrom mcp.server import MCPServer mcp MCPServer(Bookshop) mcp.resource(config://app) def get_config() - str: The active shop configuration. return themedark\nlanguageen它的形狀和工具幾乎一樣只多了一樣東西URI。這是資源與工具最本質的差異——資源靠位址定位而不是靠名稱。用戶端要求的是config://app從來不是get_config。其餘的部分SDK 照樣從函式本身讀出來名稱就是函式名稱get_config用戶端看到的描述是 docstring內容就是你回傳的東西。用戶端眼中的資源清單在resources/list期間用戶端收到的清單項長這樣{ name: get_config, uri: config://app, description: The active shop configuration., mimeType: text/plain }對應的處理函式在 server.py 的_handle_list_resources中它直接呼叫list_resources()並返回ListResourcesResult不會執行任何資源函式。讀取資源當用戶端讀取config://app時你的函式才會被呼叫回傳值以文字形式送回result.contents # [TextResourceContents(uriconfig://app, mime_typetext/plain, textthemedark\nlanguageen)]列出是廉價的函式只在讀取時執行一個重要的效能特性列出資源的成本很低。函式在resources/list期間不會執行只有在resources/read時才會而且只針對用戶端要求的那個 URI。就算你公開了一千個資源也只會為有人打開的那幾個付出代價。從原始碼可以看到這正是FunctionResource的設計初衷——types.py 的註解明確寫著「函式只在資源被讀取時呼叫允許對可能昂貴的資料進行延遲載入」若函式是同步的SDK 會用anyio.to_thread.run_sync把它丟到執行緒池避免阻塞事件迴圈。試試看用 MCP Inspector 驗證用 MCP Inspector 執行伺服器uv run mcp dev server.py打開它印出的 URL切到Resources分頁。config://app會連同描述一起出現在清單裡。點一下Inspector 就會讀取它那兩行設定就在眼前。資源範本一筆紀錄一個 URI 行不通一筆紀錄一個 URI 沒有辦法擴展。當你有大量使用者、大量檔案、大量訂單時不可能為每一筆都寫一個裝飾器。解法是在 URI 裡放一個佔位符並在函式上放一個對應的參數完整範例見 docs_src/resources/tutorial002.pyfrom mcp.server import MCPServer mcp MCPServer(Bookshop) mcp.resource(config://app) def get_config() - str: The active shop configuration. return themedark\nlanguageen mcp.resource(users://{user_id}/profile) def get_user_profile(user_id: str) - str: A customers profile. return fUser {user_id}: 12 orders since 2021.users://{user_id}/profile這個 URI 裡有{user_id}函式上有user_id: str。整個約定就是這樣佔位符名稱等於函式參數名稱。範本搬家了從 list 搬到 templates/list加了佔位符的資源就成了資源範本而且它會搬家離開resources/list改出現在resources/templates/list以「樣式pattern」而不是「位址address」的形式呈現{ name: get_user_profile, uriTemplate: users://{user_id}/profile, description: A customers profile., mimeType: text/plain }用戶端看到範本後填入佔位符再讀取一個具體的 URIusers://42/profile、users://ada/profile。同一個函式回應所有符合的 URI比對到的值會以user_id傳入result.contents # [TextResourceContents(uriusers://42/profile, textUser 42: 12 orders since 2021.)]注意結果裡的uri——那是用戶端要求的具體URIusers://42/profile不是範本users://{user_id}/profile。佔位符與參數必須一致匯入時就拒絕佔位符和參數必須一致。如果你把函式參數改名為userURI 卻還寫著{user_id}裝飾器會在匯入時就拒絕任何用戶端都還來不及靠近ValueError: Mismatch between URI parameters {user_id} and function parameters {user}不一致只可能是 bug所以 SDK 讓帶著這種錯誤的伺服器根本啟動不了。從原始碼看這個檢查發生在 server.py 的resource()裝飾器內部它先用UriTemplate.parse(uri)解析 URI 並取出所有變數名稱再透過inspect.signature(fn)收集函式參數並用find_context_parameter排除被標記為Context的參數最後兩者做集合比較。值得注意的細節有兩點裝飾時即驗證UriTemplate.parse在裝飾階段就執行格式錯誤的範本malformed template會立刻以帶明確位置的錯誤拋出反方向的檢查同樣嚴格若 URI 不含任何變數靜態資源而函式卻宣告了參數同樣會拋出ValueError「Resource ... has no URI template variables, but the handler declares parameters ...」靜態資源甚至不允許宣告Context參數因為靜態資源函式無法參與多輪往返的輸入補齊流程。RFC 6570 佔位符語法與路徑安全佔位符語法遵循 RFC 6570{user_id}最簡單的單段變數{path}用於多段的值例如git://diff/{range}運算子不會把/編碼{?q,lang}用於選用的查詢參數query parameters用戶端可以省略{q,lang}連續的查詢參數接續運算子。一個由原始碼確認的實務細節查詢參數{?...}/{...}在線上傳輸時是可選的——用戶端不帶上它時match()會把它從提取參數中省略。因此 server.py 會強制要求綁定到查詢變數的函式參數必須帶有 Python 預設值否則在裝飾階段就拋出ValueError避免作者直到第一個省略該參數的請求進來才發現問題。SDK 預設也會對從範本提取出來的值做路徑安全檢查相關策略定義在 templates.py 的ResourceSecurity資料類別中reject_path_traversal: bool True拒絕包含..路徑元件的值reject_absolute_paths: bool True拒絕看起來像絕對檔案系統路徑的值reject_null_bytes: bool True拒絕含 NUL 字元\x00的值防止其繞過字串比較或在 C 擴充、子行程呼叫中被截斷exempt_params可指定跳過檢查的參數名稱例如當某個參數合法地包含..時from mcp.server.mcpserver.resources import ResourceSecurity mcp.resource( git://diff/{range}, securityResourceSecurity(exempt_params{range}), ) def git_diff(range: str) - str: ...這些檢查在UriTemplate.match提取並解碼參數值之後執行因此無論值在 URI 中以字面形式、%2F、%5C還是%2E%2E編碼都逃不過檢查。完整參考請見URI 範本與路徑安全。範本函式也能拿到 Contextget_user_profile也可以接受一個註記為Context的參數。SDK 會注入它而且絕不會把它當成 URI 參數它能提供什麼請求狀態、伺服器實例、輸入補齊的input_responses等Context頁面有說明。回傳什麼不限於 str資源函式的回傳值不限於str。你可以替每個資源指定mime_type回傳合適的東西即可完整範例見 docs_src/resources/tutorial003.pyimport base64 from mcp.server import MCPServer mcp MCPServer(Bookshop) mcp.resource(docs://readme, mime_typetext/markdown) def readme() - str: How to use this server. return # Bookshop\n\nSearch the catalog with the search_books tool. mcp.resource(stats://catalog, mime_typeapplication/json) def catalog_stats() - dict[str, int]: Live counts for the catalog. return {books: 1204, authors: 391} mcp.resource(covers://placeholder, mime_typeimage/gif) def placeholder_cover() - bytes: A 1x1 transparent GIF, shown when a book has no cover. return base64.b64decode(R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)三種回傳型別分別對應三種行為readme回傳str所以原樣送出。這是最常見的情況catalog_stats回傳dict所以 SDK 會替你序列化成JSON 文字{ books: 1204, authors: 391 }placeholder_cover回傳bytes所以用戶端收到的是BlobResourceContents而不是TextResourceContents位元組以 base64 編碼後放在blob欄位裡。序列化規則的原始碼實現這個「str 原樣、bytes 原樣、其他轉 JSON」的規則在 types.py 的FunctionResource.read()中可以看到具體實現if isinstance(result, Resource): return await result.read() elif isinstance(result, bytes): return result elif isinstance(result, str): return result else: return pydantic_core.to_json(result, fallbackstr, indent2).decode()注意最後一行用的是pydantic_core.to_json帶fallbackstr所以同樣的規則適用於其他任何可序列化為 JSON 的東西list、Pydantic 模型、dataclass……只要不是str也不是bytes就會變成 JSON 文字縮排為 2 個空格。在傳輸層server.py 的_handle_read_resource會根據內容型別分派bytes→BlobResourceContentsblob欄位存放base64.b64encode(...)的結果其他 →TextResourceContentstext欄位存放內容。mime_type 是宣告出來的不是猜出來的mime_type由你宣告預設為text/plain。SDK從不會檢查回傳的內容來猜測它所以一個沒標示的dict資源仍然會以純文字對外宣告。從 base.py 的Resource基類可以看到mime_type欄位預設值就是text/plain。因此宣告資源時記得顯式指定合適的mime_type如application/json、text/markdown、image/gif這會直接影響用戶端如何呈現與處理內容。不想從函式推導name / title / description 與現成的 Resource 類別mcp.resource()也接受name、title和description當你不想從函式推導這些欄位時可以直接指定。從 server.py 的簽名可以看到裝飾器還支援更多參數def resource( self, uri: str, *, name: str | None None, title: str | None None, description: str | None None, mime_type: str | None None, icons: list[Icon] | None None, annotations: Annotations | None None, meta: dict[str, Any] | None None, security: ResourceSecurity | None None, ) - Callable[[_CallableT], _CallableT]:其中security的預設值來自伺服器層級的resource_security設定只作用於範本資源。沒有函式要寫用現成 Resource 類別如果根本沒有函式要寫mcp.server.mcpserver.resources裡有現成的Resource類別用mcp.add_resource(...)註冊即可見 server.py 的add_resource它會委派給內部的_resource_manager.add_resourceTextResource靜態文字內容直接指定textBinaryResource靜態二進位內容直接指定databytesFileResource讀取檔案系統中的檔案——未指定encoding時會依mime_type宣告的charset決定解碼方式文字型 MIME 如text/*、JSON、XML 預設utf-8-sig其餘為 None 表示以 bytes 送出見 types.py 的說明HttpResource抓取遠端 HTTP 內容DirectoryResource列舉目錄中的檔案清單以 JSON 形式回傳。例如直接註冊一個靜態文字資源from mcp.server.mcpserver.resources import TextResource mcp.add_resource( TextResource( uriinfo://version, nameversion, descriptionServer version information., textbookshop/1.0.0, ) )這些類別都繼承自 base.py 的Resource抽象基類統一具備uri、name未提供時會從 URI 推導、title、description、mime_type、icons、annotations、meta欄位並以抽象方法async def read() - str | bytes定義讀取行為。訂閱資源當資料變更時收到通知用戶端也可以訂閱資源在它變更時收到通知——例如設定檔被重新載入、目錄內容被修改時伺服器主動推送notifications/resources/updated讓用戶端不必輪詢。那是用戶端那一半的故事寫在用戶端裡。重點回顧在函式上加mcp.resource(uri)它就成了資源。URI 是位址回傳值是內容docstring 是描述URI 裡有{placeholder}就成了範本它列在resources/templates/list底下同一個函式服務所有符合的 URI佔位符名稱必須等於函式的參數名稱。弄錯的話匯入時就會知道不用等到正式環境函式在資源被讀取時執行而不是被列出時——列出一千個資源只會為被打開的那幾個付出代價str變成文字bytes變成 base64 blobBlobResourceContents其他的都變成 JSON 文字用mime_type來標示SDK 不會替你猜測範本參數預設啟用路徑安全檢查拒絕路徑穿越、絕對路徑與 NUL 字元可用ResourceSecurity精細控制工具讓模型採取行動資源讓應用程式讀取。第三種基本元件由人從選單裡挑選的那種是提示詞。赞分享人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载相关推荐FinceptTerminal 台灣股市資料連接器實戰指南以 TWSE/TPEX 行情、指數與公司資訊擴充開源終端FinceptTerminal 台灣股市資料連接器實戰指南以 TWSE/TPEX 行情、指數與公司資訊擴充開源終端 本指南圍繞 FinceptTerminal金融科技桌面应用AI 应用繁體中文教育語料新突破FineWeb-Edu-zhtw 資料集重磅發布推動中文AI教育應用發展在當前人工智能技術飛速發展的浪潮中高質量、領域專屬的語料資源已成為訓練先進語言模型的核心基礎。近日一項針對繁體中文教育領域的重要語料工程——FineWeb2025最強Android TV直播應用開發指南從安裝到源碼深度解析2025最強Android TV直播應用開發指南從安裝到源碼深度解析 還在為Android TV應用開發中的兼容性問題頭痛還在苦苦尋找穩定的直播源解決方案音视频直播上一篇使用 Three.js 和 JavaScript 创建程序化树木生成器从零到森林的艺术下一篇实用指南如何用GHelper高效管理华硕笔记本性能与续航创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →