[{"data":1,"prerenderedAt":2162},["ShallowReactive",2],{"post-go-mcp-gin":3},{"id":4,"title":5,"author":6,"body":7,"description":2146,"draft":2147,"extension":2148,"image":2149,"meta":2150,"navigation":141,"path":2151,"pinned":2147,"published":2152,"seo":2153,"stem":2154,"tags":2155,"__hash__":2161},"posts\u002Fposts\u002Fgo-mcp-gin\u002Findex.md","Go-MCP 接入 Gin：从零搭建 MCP 服务器",null,{"type":8,"value":9,"toc":2127},"minimark",[10,15,19,22,26,34,41,44,47,69,72,77,112,116,232,245,249,423,426,650,654,665,764,771,775,1063,1066,1147,1151,1154,1157,1168,1172,1179,1550,1744,1747,1803,1806,1960,1963,1966,2039,2042,2114,2123],[11,12,14],"h2",{"id":13},"什么是-mcp","什么是 MCP",[16,17,18],"p",{},"MCP（Model Context Protocol）是 Anthropic 提出的开放协议，用于让 AI 助手（如 Claude）与外部服务进行标准化交互。简单来说：你提供一个 MCP 服务器，AI 就能发现并调用你定义的工具（Tools），就像给 AI 装了一双手。",[16,20,21],{},"传统做法是写 REST API 然后让 AI 通过函数调用（Function Calling）间接使用，但 MCP 统一了协议层，AI 客户端只需要配置一个 URL 就能接入所有工具。",[11,23,25],{"id":24},"为什么选-gin-mcp-go","为什么选 Gin + mcp-go",[16,27,28,29,33],{},"Go 生态里做 MCP 服务器最成熟的库是 ",[30,31,32],"code",{},"mark3labs\u002Fmcp-go","，它支持 StreamableHTTP 传输模式，可以直接挂载到任意 HTTP 框架上。Gin 作为 Go 最流行的 HTTP 框架，天然适配。",[16,35,36,37],{},"核心思路：",[38,39,40],"strong",{},"MCP 服务器本质上就是一个 HTTP Handler，挂到 Gin 路由上即可。",[11,42,43],{"id":43},"需求分析",[16,45,46],{},"我们要搭建一个具备以下能力的 MCP 服务器：",[48,49,50,57,63],"ol",{},[51,52,53,56],"li",{},[38,54,55],{},"工具注册"," — 向 AI 暴露可调用的工具（列出资源、查看详情、创建\u002F删除）",[51,58,59,62],{},[38,60,61],{},"HTTP 集成"," — MCP 端点作为 Gin 路由的一部分，与业务 API 共存",[51,64,65,68],{},[38,66,67],{},"鉴权"," — MCP 请求必须经过身份验证，工具处理器能获取当前用户信息",[11,70,71],{"id":71},"实现步骤",[73,74,76],"h3",{"id":75},"_1-安装依赖","1. 安装依赖",[78,79,84],"pre",{"className":80,"code":81,"language":82,"meta":83,"style":83},"language-bash shiki shiki-themes github-light github-dark","go get github.com\u002Fmark3labs\u002Fmcp-go@v0.54.0\ngo get github.com\u002Fgin-gonic\u002Fgin@v1.12.0\n","bash","",[30,85,86,102],{"__ignoreMap":83},[87,88,91,95,99],"span",{"class":89,"line":90},"line",1,[87,92,94],{"class":93},"sw2iP","go",[87,96,98],{"class":97},"scXbn"," get",[87,100,101],{"class":97}," github.com\u002Fmark3labs\u002Fmcp-go@v0.54.0\n",[87,103,105,107,109],{"class":89,"line":104},2,[87,106,94],{"class":93},[87,108,98],{"class":97},[87,110,111],{"class":97}," github.com\u002Fgin-gonic\u002Fgin@v1.12.0\n",[73,113,115],{"id":114},"_2-创建-mcp-服务器","2. 创建 MCP 服务器",[78,117,120],{"className":118,"code":119,"language":94,"meta":83,"style":83},"language-go shiki shiki-themes github-light github-dark","import \"github.com\u002Fmark3labs\u002Fmcp-go\u002Fserver\"\n\nmcpServer := server.NewMCPServer(\n    \"my-app\",    \u002F\u002F 服务器名称\n    \"1.0.0\",     \u002F\u002F 版本\n    server.WithLogging(),\n    server.WithRecovery(),\n    server.WithInstructions(\"这是一个 MCP 服务器示例\"),\n)\n",[30,121,122,137,143,162,175,187,199,209,226],{"__ignoreMap":83},[87,123,124,128,131,134],{"class":89,"line":90},[87,125,127],{"class":126},"siTax","import",[87,129,130],{"class":97}," \"",[87,132,133],{"class":93},"github.com\u002Fmark3labs\u002Fmcp-go\u002Fserver",[87,135,136],{"class":97},"\"\n",[87,138,139],{"class":89,"line":104},[87,140,142],{"emptyLinePlaceholder":141},true,"\n",[87,144,146,150,153,156,159],{"class":89,"line":145},3,[87,147,149],{"class":148},"sIX_F","mcpServer ",[87,151,152],{"class":126},":=",[87,154,155],{"class":148}," server.",[87,157,158],{"class":93},"NewMCPServer",[87,160,161],{"class":148},"(\n",[87,163,165,168,171],{"class":89,"line":164},4,[87,166,167],{"class":97},"    \"my-app\"",[87,169,170],{"class":148},",    ",[87,172,174],{"class":173},"sHbNN","\u002F\u002F 服务器名称\n",[87,176,178,181,184],{"class":89,"line":177},5,[87,179,180],{"class":97},"    \"1.0.0\"",[87,182,183],{"class":148},",     ",[87,185,186],{"class":173},"\u002F\u002F 版本\n",[87,188,190,193,196],{"class":89,"line":189},6,[87,191,192],{"class":148},"    server.",[87,194,195],{"class":93},"WithLogging",[87,197,198],{"class":148},"(),\n",[87,200,202,204,207],{"class":89,"line":201},7,[87,203,192],{"class":148},[87,205,206],{"class":93},"WithRecovery",[87,208,198],{"class":148},[87,210,212,214,217,220,223],{"class":89,"line":211},8,[87,213,192],{"class":148},[87,215,216],{"class":93},"WithInstructions",[87,218,219],{"class":148},"(",[87,221,222],{"class":97},"\"这是一个 MCP 服务器示例\"",[87,224,225],{"class":148},"),\n",[87,227,229],{"class":89,"line":228},9,[87,230,231],{"class":148},")\n",[16,233,234,236,237,240,241,244],{},[30,235,158],{}," 创建一个 MCP 协议实例，它负责处理工具发现（",[30,238,239],{},"tools\u002Flist","）和工具调用（",[30,242,243],{},"tools\u002Fcall","）的协议逻辑。",[73,246,248],{"id":247},"_3-注册工具","3. 注册工具",[78,250,252],{"className":118,"code":251,"language":94,"meta":83,"style":83},"import \"github.com\u002Fmark3labs\u002Fmcp-go\u002Fmcp\"\n\n\u002F\u002F 定义工具\nlistTool := mcp.NewTool(\"list_items\",\n    mcp.WithDescription(\"列出所有项目\"),\n)\n\n\u002F\u002F 注册处理器\nmcpServer.AddTool(listTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n    \u002F\u002F 业务逻辑\n    return mcp.NewToolResultText(\"返回结果\"), nil\n})\n",[30,253,254,265,269,274,295,310,314,318,323,388,394,417],{"__ignoreMap":83},[87,255,256,258,260,263],{"class":89,"line":90},[87,257,127],{"class":126},[87,259,130],{"class":97},[87,261,262],{"class":93},"github.com\u002Fmark3labs\u002Fmcp-go\u002Fmcp",[87,264,136],{"class":97},[87,266,267],{"class":89,"line":104},[87,268,142],{"emptyLinePlaceholder":141},[87,270,271],{"class":89,"line":145},[87,272,273],{"class":173},"\u002F\u002F 定义工具\n",[87,275,276,279,281,284,287,289,292],{"class":89,"line":164},[87,277,278],{"class":148},"listTool ",[87,280,152],{"class":126},[87,282,283],{"class":148}," mcp.",[87,285,286],{"class":93},"NewTool",[87,288,219],{"class":148},[87,290,291],{"class":97},"\"list_items\"",[87,293,294],{"class":148},",\n",[87,296,297,300,303,305,308],{"class":89,"line":177},[87,298,299],{"class":148},"    mcp.",[87,301,302],{"class":93},"WithDescription",[87,304,219],{"class":148},[87,306,307],{"class":97},"\"列出所有项目\"",[87,309,225],{"class":148},[87,311,312],{"class":89,"line":189},[87,313,231],{"class":148},[87,315,316],{"class":89,"line":201},[87,317,142],{"emptyLinePlaceholder":141},[87,319,320],{"class":89,"line":211},[87,321,322],{"class":173},"\u002F\u002F 注册处理器\n",[87,324,325,328,331,334,337,339,343,346,349,352,355,358,361,363,366,369,372,375,377,380,382,385],{"class":89,"line":228},[87,326,327],{"class":148},"mcpServer.",[87,329,330],{"class":93},"AddTool",[87,332,333],{"class":148},"(listTool, ",[87,335,336],{"class":126},"func",[87,338,219],{"class":148},[87,340,342],{"class":341},"sPK6S","ctx",[87,344,345],{"class":93}," context",[87,347,348],{"class":148},".",[87,350,351],{"class":93},"Context",[87,353,354],{"class":148},", ",[87,356,357],{"class":341},"req",[87,359,360],{"class":93}," mcp",[87,362,348],{"class":148},[87,364,365],{"class":93},"CallToolRequest",[87,367,368],{"class":148},") (",[87,370,371],{"class":126},"*",[87,373,374],{"class":93},"mcp",[87,376,348],{"class":148},[87,378,379],{"class":93},"CallToolResult",[87,381,354],{"class":148},[87,383,384],{"class":126},"error",[87,386,387],{"class":148},") {\n",[87,389,391],{"class":89,"line":390},10,[87,392,393],{"class":173},"    \u002F\u002F 业务逻辑\n",[87,395,397,400,402,405,407,410,413],{"class":89,"line":396},11,[87,398,399],{"class":126},"    return",[87,401,283],{"class":148},[87,403,404],{"class":93},"NewToolResultText",[87,406,219],{"class":148},[87,408,409],{"class":97},"\"返回结果\"",[87,411,412],{"class":148},"), ",[87,414,416],{"class":415},"suQ91","nil\n",[87,418,420],{"class":89,"line":419},12,[87,421,422],{"class":148},"})\n",[16,424,425],{},"带参数的工具：",[78,427,429],{"className":118,"code":428,"language":94,"meta":83,"style":83},"getTool := mcp.NewTool(\"get_item\",\n    mcp.WithDescription(\"获取指定项目的详情\"),\n    mcp.WithString(\"item_id\",\n        mcp.Required(),\n        mcp.Description(\"项目 ID\"),\n    ),\n)\n\nmcpServer.AddTool(getTool, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n    itemID, err := req.RequireString(\"item_id\")\n    if err != nil {\n        return mcp.NewToolResultError(\"缺少 item_id 参数\"), nil\n    }\n    \u002F\u002F 查询并返回\n    return mcp.NewToolResultText(\"项目详情...\"), nil\n})\n",[30,430,431,449,462,476,486,500,505,509,513,560,579,596,615,621,627,645],{"__ignoreMap":83},[87,432,433,436,438,440,442,444,447],{"class":89,"line":90},[87,434,435],{"class":148},"getTool ",[87,437,152],{"class":126},[87,439,283],{"class":148},[87,441,286],{"class":93},[87,443,219],{"class":148},[87,445,446],{"class":97},"\"get_item\"",[87,448,294],{"class":148},[87,450,451,453,455,457,460],{"class":89,"line":104},[87,452,299],{"class":148},[87,454,302],{"class":93},[87,456,219],{"class":148},[87,458,459],{"class":97},"\"获取指定项目的详情\"",[87,461,225],{"class":148},[87,463,464,466,469,471,474],{"class":89,"line":145},[87,465,299],{"class":148},[87,467,468],{"class":93},"WithString",[87,470,219],{"class":148},[87,472,473],{"class":97},"\"item_id\"",[87,475,294],{"class":148},[87,477,478,481,484],{"class":89,"line":164},[87,479,480],{"class":148},"        mcp.",[87,482,483],{"class":93},"Required",[87,485,198],{"class":148},[87,487,488,490,493,495,498],{"class":89,"line":177},[87,489,480],{"class":148},[87,491,492],{"class":93},"Description",[87,494,219],{"class":148},[87,496,497],{"class":97},"\"项目 ID\"",[87,499,225],{"class":148},[87,501,502],{"class":89,"line":189},[87,503,504],{"class":148},"    ),\n",[87,506,507],{"class":89,"line":201},[87,508,231],{"class":148},[87,510,511],{"class":89,"line":211},[87,512,142],{"emptyLinePlaceholder":141},[87,514,515,517,519,522,524,526,528,530,532,534,536,538,540,542,544,546,548,550,552,554,556,558],{"class":89,"line":228},[87,516,327],{"class":148},[87,518,330],{"class":93},[87,520,521],{"class":148},"(getTool, ",[87,523,336],{"class":126},[87,525,219],{"class":148},[87,527,342],{"class":341},[87,529,345],{"class":93},[87,531,348],{"class":148},[87,533,351],{"class":93},[87,535,354],{"class":148},[87,537,357],{"class":341},[87,539,360],{"class":93},[87,541,348],{"class":148},[87,543,365],{"class":93},[87,545,368],{"class":148},[87,547,371],{"class":126},[87,549,374],{"class":93},[87,551,348],{"class":148},[87,553,379],{"class":93},[87,555,354],{"class":148},[87,557,384],{"class":126},[87,559,387],{"class":148},[87,561,562,565,567,570,573,575,577],{"class":89,"line":390},[87,563,564],{"class":148},"    itemID, err ",[87,566,152],{"class":126},[87,568,569],{"class":148}," req.",[87,571,572],{"class":93},"RequireString",[87,574,219],{"class":148},[87,576,473],{"class":97},[87,578,231],{"class":148},[87,580,581,584,587,590,593],{"class":89,"line":396},[87,582,583],{"class":126},"    if",[87,585,586],{"class":148}," err ",[87,588,589],{"class":126},"!=",[87,591,592],{"class":415}," nil",[87,594,595],{"class":148}," {\n",[87,597,598,601,603,606,608,611,613],{"class":89,"line":419},[87,599,600],{"class":126},"        return",[87,602,283],{"class":148},[87,604,605],{"class":93},"NewToolResultError",[87,607,219],{"class":148},[87,609,610],{"class":97},"\"缺少 item_id 参数\"",[87,612,412],{"class":148},[87,614,416],{"class":415},[87,616,618],{"class":89,"line":617},13,[87,619,620],{"class":148},"    }\n",[87,622,624],{"class":89,"line":623},14,[87,625,626],{"class":173},"    \u002F\u002F 查询并返回\n",[87,628,630,632,634,636,638,641,643],{"class":89,"line":629},15,[87,631,399],{"class":126},[87,633,283],{"class":148},[87,635,404],{"class":93},[87,637,219],{"class":148},[87,639,640],{"class":97},"\"项目详情...\"",[87,642,412],{"class":148},[87,644,416],{"class":415},[87,646,648],{"class":89,"line":647},16,[87,649,422],{"class":148},[73,651,653],{"id":652},"_4-挂载到-gin-路由","4. 挂载到 Gin 路由",[16,655,656,657,660,661,664],{},"关键一步：用 ",[30,658,659],{},"server.NewStreamableHTTPServer"," 把 MCP 实例转成 HTTP Handler，再用 ",[30,662,663],{},"gin.WrapH"," 适配 Gin。",[78,666,668],{"className":118,"code":667,"language":94,"meta":83,"style":83},"import \"github.com\u002Fmark3labs\u002Fmcp-go\u002Fserver\"\n\nmcpHTTPServer := server.NewStreamableHTTPServer(mcpServer,\n    server.WithEndpointPath(\"\u002Fmcp\"),\n    server.WithHeartbeatInterval(30*time.Second),\n)\n\n\u002F\u002F 挂载到 Gin\nr.Any(\"\u002Fmcp\", gin.WrapH(mcpHTTPServer))\n",[30,669,670,680,684,699,713,730,734,738,743],{"__ignoreMap":83},[87,671,672,674,676,678],{"class":89,"line":90},[87,673,127],{"class":126},[87,675,130],{"class":97},[87,677,133],{"class":93},[87,679,136],{"class":97},[87,681,682],{"class":89,"line":104},[87,683,142],{"emptyLinePlaceholder":141},[87,685,686,689,691,693,696],{"class":89,"line":145},[87,687,688],{"class":148},"mcpHTTPServer ",[87,690,152],{"class":126},[87,692,155],{"class":148},[87,694,695],{"class":93},"NewStreamableHTTPServer",[87,697,698],{"class":148},"(mcpServer,\n",[87,700,701,703,706,708,711],{"class":89,"line":164},[87,702,192],{"class":148},[87,704,705],{"class":93},"WithEndpointPath",[87,707,219],{"class":148},[87,709,710],{"class":97},"\"\u002Fmcp\"",[87,712,225],{"class":148},[87,714,715,717,720,722,725,727],{"class":89,"line":177},[87,716,192],{"class":148},[87,718,719],{"class":93},"WithHeartbeatInterval",[87,721,219],{"class":148},[87,723,724],{"class":415},"30",[87,726,371],{"class":126},[87,728,729],{"class":148},"time.Second),\n",[87,731,732],{"class":89,"line":189},[87,733,231],{"class":148},[87,735,736],{"class":89,"line":201},[87,737,142],{"emptyLinePlaceholder":141},[87,739,740],{"class":89,"line":211},[87,741,742],{"class":173},"\u002F\u002F 挂载到 Gin\n",[87,744,745,748,751,753,755,758,761],{"class":89,"line":228},[87,746,747],{"class":148},"r.",[87,749,750],{"class":93},"Any",[87,752,219],{"class":148},[87,754,710],{"class":97},[87,756,757],{"class":148},", gin.",[87,759,760],{"class":93},"WrapH",[87,762,763],{"class":148},"(mcpHTTPServer))\n",[16,765,766,767,770],{},"就这样，",[30,768,769],{},"POST \u002Fmcp"," 端点就生效了。AI 客户端通过这个 URL 发送 JSON-RPC 请求，MCP 服务器自动处理协议握手、工具列表返回、工具调用路由。",[73,772,774],{"id":773},"_5-完整的服务启动代码","5. 完整的服务启动代码",[78,776,778],{"className":118,"code":777,"language":94,"meta":83,"style":83},"func main() {\n    r := gin.Default()\n\n    \u002F\u002F 创建 MCP 服务器\n    mcpServer := server.NewMCPServer(\"my-app\", \"1.0.0\",\n        server.WithLogging(),\n        server.WithRecovery(),\n    )\n\n    \u002F\u002F 注册工具\n    registerTools(mcpServer)\n\n    \u002F\u002F 挂载到 Gin\n    mcpHTTP := server.NewStreamableHTTPServer(mcpServer,\n        server.WithEndpointPath(\"\u002Fmcp\"),\n        server.WithHeartbeatInterval(30*time.Second),\n    )\n    r.Any(\"\u002Fmcp\", gin.WrapH(mcpHTTP))\n\n    \u002F\u002F 其他业务路由\n    r.GET(\"\u002Fapi\u002Fhealth\", func(c *gin.Context) {\n        c.JSON(200, gin.H{\"status\": \"ok\"})\n    })\n\n    r.Run(\":8080\")\n}\n",[30,779,780,790,806,810,815,838,847,855,860,864,869,877,881,886,899,911,925,930,949,954,960,994,1031,1037,1042,1057],{"__ignoreMap":83},[87,781,782,784,787],{"class":89,"line":90},[87,783,336],{"class":126},[87,785,786],{"class":93}," main",[87,788,789],{"class":148},"() {\n",[87,791,792,795,797,800,803],{"class":89,"line":104},[87,793,794],{"class":148},"    r ",[87,796,152],{"class":126},[87,798,799],{"class":148}," gin.",[87,801,802],{"class":93},"Default",[87,804,805],{"class":148},"()\n",[87,807,808],{"class":89,"line":145},[87,809,142],{"emptyLinePlaceholder":141},[87,811,812],{"class":89,"line":164},[87,813,814],{"class":173},"    \u002F\u002F 创建 MCP 服务器\n",[87,816,817,820,822,824,826,828,831,833,836],{"class":89,"line":177},[87,818,819],{"class":148},"    mcpServer ",[87,821,152],{"class":126},[87,823,155],{"class":148},[87,825,158],{"class":93},[87,827,219],{"class":148},[87,829,830],{"class":97},"\"my-app\"",[87,832,354],{"class":148},[87,834,835],{"class":97},"\"1.0.0\"",[87,837,294],{"class":148},[87,839,840,843,845],{"class":89,"line":189},[87,841,842],{"class":148},"        server.",[87,844,195],{"class":93},[87,846,198],{"class":148},[87,848,849,851,853],{"class":89,"line":201},[87,850,842],{"class":148},[87,852,206],{"class":93},[87,854,198],{"class":148},[87,856,857],{"class":89,"line":211},[87,858,859],{"class":148},"    )\n",[87,861,862],{"class":89,"line":228},[87,863,142],{"emptyLinePlaceholder":141},[87,865,866],{"class":89,"line":390},[87,867,868],{"class":173},"    \u002F\u002F 注册工具\n",[87,870,871,874],{"class":89,"line":396},[87,872,873],{"class":93},"    registerTools",[87,875,876],{"class":148},"(mcpServer)\n",[87,878,879],{"class":89,"line":419},[87,880,142],{"emptyLinePlaceholder":141},[87,882,883],{"class":89,"line":617},[87,884,885],{"class":173},"    \u002F\u002F 挂载到 Gin\n",[87,887,888,891,893,895,897],{"class":89,"line":623},[87,889,890],{"class":148},"    mcpHTTP ",[87,892,152],{"class":126},[87,894,155],{"class":148},[87,896,695],{"class":93},[87,898,698],{"class":148},[87,900,901,903,905,907,909],{"class":89,"line":629},[87,902,842],{"class":148},[87,904,705],{"class":93},[87,906,219],{"class":148},[87,908,710],{"class":97},[87,910,225],{"class":148},[87,912,913,915,917,919,921,923],{"class":89,"line":647},[87,914,842],{"class":148},[87,916,719],{"class":93},[87,918,219],{"class":148},[87,920,724],{"class":415},[87,922,371],{"class":126},[87,924,729],{"class":148},[87,926,928],{"class":89,"line":927},17,[87,929,859],{"class":148},[87,931,933,936,938,940,942,944,946],{"class":89,"line":932},18,[87,934,935],{"class":148},"    r.",[87,937,750],{"class":93},[87,939,219],{"class":148},[87,941,710],{"class":97},[87,943,757],{"class":148},[87,945,760],{"class":93},[87,947,948],{"class":148},"(mcpHTTP))\n",[87,950,952],{"class":89,"line":951},19,[87,953,142],{"emptyLinePlaceholder":141},[87,955,957],{"class":89,"line":956},20,[87,958,959],{"class":173},"    \u002F\u002F 其他业务路由\n",[87,961,963,965,968,970,973,975,977,979,982,985,988,990,992],{"class":89,"line":962},21,[87,964,935],{"class":148},[87,966,967],{"class":93},"GET",[87,969,219],{"class":148},[87,971,972],{"class":97},"\"\u002Fapi\u002Fhealth\"",[87,974,354],{"class":148},[87,976,336],{"class":126},[87,978,219],{"class":148},[87,980,981],{"class":341},"c",[87,983,984],{"class":126}," *",[87,986,987],{"class":93},"gin",[87,989,348],{"class":148},[87,991,351],{"class":93},[87,993,387],{"class":148},[87,995,997,1000,1003,1005,1008,1010,1012,1014,1017,1020,1023,1026,1029],{"class":89,"line":996},22,[87,998,999],{"class":148},"        c.",[87,1001,1002],{"class":93},"JSON",[87,1004,219],{"class":148},[87,1006,1007],{"class":415},"200",[87,1009,354],{"class":148},[87,1011,987],{"class":93},[87,1013,348],{"class":148},[87,1015,1016],{"class":93},"H",[87,1018,1019],{"class":148},"{",[87,1021,1022],{"class":97},"\"status\"",[87,1024,1025],{"class":148},": ",[87,1027,1028],{"class":97},"\"ok\"",[87,1030,422],{"class":148},[87,1032,1034],{"class":89,"line":1033},23,[87,1035,1036],{"class":148},"    })\n",[87,1038,1040],{"class":89,"line":1039},24,[87,1041,142],{"emptyLinePlaceholder":141},[87,1043,1045,1047,1050,1052,1055],{"class":89,"line":1044},25,[87,1046,935],{"class":148},[87,1048,1049],{"class":93},"Run",[87,1051,219],{"class":148},[87,1053,1054],{"class":97},"\":8080\"",[87,1056,231],{"class":148},[87,1058,1060],{"class":89,"line":1059},26,[87,1061,1062],{"class":148},"}\n",[16,1064,1065],{},"此时启动服务，用 curl 测试：",[78,1067,1069],{"className":80,"code":1068,"language":82,"meta":83,"style":83},"# 发现工具列表\ncurl -X POST http:\u002F\u002Flocalhost:8080\u002Fmcp \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools\u002Flist\"}'\n\n# 调用工具\ncurl -X POST http:\u002F\u002Flocalhost:8080\u002Fmcp \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools\u002Fcall\",\"params\":{\"name\":\"list_items\",\"arguments\":{}}}'\n",[30,1070,1071,1076,1093,1103,1111,1115,1120,1132,1140],{"__ignoreMap":83},[87,1072,1073],{"class":89,"line":90},[87,1074,1075],{"class":173},"# 发现工具列表\n",[87,1077,1078,1081,1084,1087,1090],{"class":89,"line":104},[87,1079,1080],{"class":93},"curl",[87,1082,1083],{"class":415}," -X",[87,1085,1086],{"class":97}," POST",[87,1088,1089],{"class":97}," http:\u002F\u002Flocalhost:8080\u002Fmcp",[87,1091,1092],{"class":415}," \\\n",[87,1094,1095,1098,1101],{"class":89,"line":145},[87,1096,1097],{"class":415},"  -H",[87,1099,1100],{"class":97}," \"Content-Type: application\u002Fjson\"",[87,1102,1092],{"class":415},[87,1104,1105,1108],{"class":89,"line":164},[87,1106,1107],{"class":415},"  -d",[87,1109,1110],{"class":97}," '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools\u002Flist\"}'\n",[87,1112,1113],{"class":89,"line":177},[87,1114,142],{"emptyLinePlaceholder":141},[87,1116,1117],{"class":89,"line":189},[87,1118,1119],{"class":173},"# 调用工具\n",[87,1121,1122,1124,1126,1128,1130],{"class":89,"line":201},[87,1123,1080],{"class":93},[87,1125,1083],{"class":415},[87,1127,1086],{"class":97},[87,1129,1089],{"class":97},[87,1131,1092],{"class":415},[87,1133,1134,1136,1138],{"class":89,"line":211},[87,1135,1097],{"class":415},[87,1137,1100],{"class":97},[87,1139,1092],{"class":415},[87,1141,1142,1144],{"class":89,"line":228},[87,1143,1107],{"class":415},[87,1145,1146],{"class":97}," '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools\u002Fcall\",\"params\":{\"name\":\"list_items\",\"arguments\":{}}}'\n",[11,1148,1150],{"id":1149},"拓展接入鉴权","拓展：接入鉴权",[16,1152,1153],{},"裸奔的 MCP 端点任何人都能调用，生产环境必须加鉴权。思路和普通 API 鉴权一致：Bearer Token。",[73,1155,1156],{"id":1156},"问题",[16,1158,1159,1160,1163,1164,1167],{},"MCP 的工具处理器签名为 ",[30,1161,1162],{},"func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error)","，它不接收 Gin 的 ",[30,1165,1166],{},"*gin.Context","，所以无法直接在工具处理器里读取请求头。",[73,1169,1171],{"id":1170},"解决方案中间件传递用户信息","解决方案：中间件传递用户信息",[16,1173,1174,1175,1178],{},"用两层中间件：第一层做 Gin 的 Token 验证，第二层把用户信息从 Gin Context 复制到 ",[30,1176,1177],{},"request.Context","。",[78,1180,1182],{"className":118,"code":1181,"language":94,"meta":83,"style":83},"\u002F\u002F Gin 鉴权中间件\nfunc AuthRequired(authService *AuthService) gin.HandlerFunc {\n    return func(c *gin.Context) {\n        authHeader := c.GetHeader(\"Authorization\")\n        if authHeader == \"\" {\n            c.AbortWithStatusJSON(401, gin.H{\"message\": \"缺少认证令牌\"})\n            return\n        }\n\n        parts := strings.SplitN(authHeader, \" \", 2)\n        if len(parts) != 2 || !strings.EqualFold(parts[0], \"Bearer\") {\n            c.AbortWithStatusJSON(401, gin.H{\"message\": \"格式错误，请使用 Bearer \u003Ctoken>\"})\n            return\n        }\n\n        user, err := authService.GetUserByToken(parts[1])\n        if err != nil {\n            c.AbortWithStatusJSON(401, gin.H{\"message\": \"无效的认证令牌\"})\n            return\n        }\n\n        c.Set(\"userID\", user.ID)\n        c.Set(\"username\", user.Username)\n        c.Next()\n    }\n}\n",[30,1183,1184,1189,1218,1239,1259,1275,1308,1313,1318,1322,1348,1389,1418,1422,1426,1430,1451,1463,1492,1496,1500,1504,1519,1533,1542,1546],{"__ignoreMap":83},[87,1185,1186],{"class":89,"line":90},[87,1187,1188],{"class":173},"\u002F\u002F Gin 鉴权中间件\n",[87,1190,1191,1193,1196,1198,1201,1203,1206,1209,1211,1213,1216],{"class":89,"line":104},[87,1192,336],{"class":126},[87,1194,1195],{"class":93}," AuthRequired",[87,1197,219],{"class":148},[87,1199,1200],{"class":341},"authService",[87,1202,984],{"class":126},[87,1204,1205],{"class":93},"AuthService",[87,1207,1208],{"class":148},") ",[87,1210,987],{"class":93},[87,1212,348],{"class":148},[87,1214,1215],{"class":93},"HandlerFunc",[87,1217,595],{"class":148},[87,1219,1220,1222,1225,1227,1229,1231,1233,1235,1237],{"class":89,"line":145},[87,1221,399],{"class":126},[87,1223,1224],{"class":126}," func",[87,1226,219],{"class":148},[87,1228,981],{"class":341},[87,1230,984],{"class":126},[87,1232,987],{"class":93},[87,1234,348],{"class":148},[87,1236,351],{"class":93},[87,1238,387],{"class":148},[87,1240,1241,1244,1246,1249,1252,1254,1257],{"class":89,"line":164},[87,1242,1243],{"class":148},"        authHeader ",[87,1245,152],{"class":126},[87,1247,1248],{"class":148}," c.",[87,1250,1251],{"class":93},"GetHeader",[87,1253,219],{"class":148},[87,1255,1256],{"class":97},"\"Authorization\"",[87,1258,231],{"class":148},[87,1260,1261,1264,1267,1270,1273],{"class":89,"line":177},[87,1262,1263],{"class":126},"        if",[87,1265,1266],{"class":148}," authHeader ",[87,1268,1269],{"class":126},"==",[87,1271,1272],{"class":97}," \"\"",[87,1274,595],{"class":148},[87,1276,1277,1280,1283,1285,1288,1290,1292,1294,1296,1298,1301,1303,1306],{"class":89,"line":189},[87,1278,1279],{"class":148},"            c.",[87,1281,1282],{"class":93},"AbortWithStatusJSON",[87,1284,219],{"class":148},[87,1286,1287],{"class":415},"401",[87,1289,354],{"class":148},[87,1291,987],{"class":93},[87,1293,348],{"class":148},[87,1295,1016],{"class":93},[87,1297,1019],{"class":148},[87,1299,1300],{"class":97},"\"message\"",[87,1302,1025],{"class":148},[87,1304,1305],{"class":97},"\"缺少认证令牌\"",[87,1307,422],{"class":148},[87,1309,1310],{"class":89,"line":201},[87,1311,1312],{"class":126},"            return\n",[87,1314,1315],{"class":89,"line":211},[87,1316,1317],{"class":148},"        }\n",[87,1319,1320],{"class":89,"line":228},[87,1321,142],{"emptyLinePlaceholder":141},[87,1323,1324,1327,1329,1332,1335,1338,1341,1343,1346],{"class":89,"line":390},[87,1325,1326],{"class":148},"        parts ",[87,1328,152],{"class":126},[87,1330,1331],{"class":148}," strings.",[87,1333,1334],{"class":93},"SplitN",[87,1336,1337],{"class":148},"(authHeader, ",[87,1339,1340],{"class":97},"\" \"",[87,1342,354],{"class":148},[87,1344,1345],{"class":415},"2",[87,1347,231],{"class":148},[87,1349,1350,1352,1355,1358,1360,1363,1366,1369,1372,1375,1378,1381,1384,1387],{"class":89,"line":396},[87,1351,1263],{"class":126},[87,1353,1354],{"class":93}," len",[87,1356,1357],{"class":148},"(parts) ",[87,1359,589],{"class":126},[87,1361,1362],{"class":415}," 2",[87,1364,1365],{"class":126}," ||",[87,1367,1368],{"class":126}," !",[87,1370,1371],{"class":148},"strings.",[87,1373,1374],{"class":93},"EqualFold",[87,1376,1377],{"class":148},"(parts[",[87,1379,1380],{"class":415},"0",[87,1382,1383],{"class":148},"], ",[87,1385,1386],{"class":97},"\"Bearer\"",[87,1388,387],{"class":148},[87,1390,1391,1393,1395,1397,1399,1401,1403,1405,1407,1409,1411,1413,1416],{"class":89,"line":419},[87,1392,1279],{"class":148},[87,1394,1282],{"class":93},[87,1396,219],{"class":148},[87,1398,1287],{"class":415},[87,1400,354],{"class":148},[87,1402,987],{"class":93},[87,1404,348],{"class":148},[87,1406,1016],{"class":93},[87,1408,1019],{"class":148},[87,1410,1300],{"class":97},[87,1412,1025],{"class":148},[87,1414,1415],{"class":97},"\"格式错误，请使用 Bearer \u003Ctoken>\"",[87,1417,422],{"class":148},[87,1419,1420],{"class":89,"line":617},[87,1421,1312],{"class":126},[87,1423,1424],{"class":89,"line":623},[87,1425,1317],{"class":148},[87,1427,1428],{"class":89,"line":629},[87,1429,142],{"emptyLinePlaceholder":141},[87,1431,1432,1435,1437,1440,1443,1445,1448],{"class":89,"line":647},[87,1433,1434],{"class":148},"        user, err ",[87,1436,152],{"class":126},[87,1438,1439],{"class":148}," authService.",[87,1441,1442],{"class":93},"GetUserByToken",[87,1444,1377],{"class":148},[87,1446,1447],{"class":415},"1",[87,1449,1450],{"class":148},"])\n",[87,1452,1453,1455,1457,1459,1461],{"class":89,"line":927},[87,1454,1263],{"class":126},[87,1456,586],{"class":148},[87,1458,589],{"class":126},[87,1460,592],{"class":415},[87,1462,595],{"class":148},[87,1464,1465,1467,1469,1471,1473,1475,1477,1479,1481,1483,1485,1487,1490],{"class":89,"line":932},[87,1466,1279],{"class":148},[87,1468,1282],{"class":93},[87,1470,219],{"class":148},[87,1472,1287],{"class":415},[87,1474,354],{"class":148},[87,1476,987],{"class":93},[87,1478,348],{"class":148},[87,1480,1016],{"class":93},[87,1482,1019],{"class":148},[87,1484,1300],{"class":97},[87,1486,1025],{"class":148},[87,1488,1489],{"class":97},"\"无效的认证令牌\"",[87,1491,422],{"class":148},[87,1493,1494],{"class":89,"line":951},[87,1495,1312],{"class":126},[87,1497,1498],{"class":89,"line":956},[87,1499,1317],{"class":148},[87,1501,1502],{"class":89,"line":962},[87,1503,142],{"emptyLinePlaceholder":141},[87,1505,1506,1508,1511,1513,1516],{"class":89,"line":996},[87,1507,999],{"class":148},[87,1509,1510],{"class":93},"Set",[87,1512,219],{"class":148},[87,1514,1515],{"class":97},"\"userID\"",[87,1517,1518],{"class":148},", user.ID)\n",[87,1520,1521,1523,1525,1527,1530],{"class":89,"line":1033},[87,1522,999],{"class":148},[87,1524,1510],{"class":93},[87,1526,219],{"class":148},[87,1528,1529],{"class":97},"\"username\"",[87,1531,1532],{"class":148},", user.Username)\n",[87,1534,1535,1537,1540],{"class":89,"line":1039},[87,1536,999],{"class":148},[87,1538,1539],{"class":93},"Next",[87,1541,805],{"class":148},[87,1543,1544],{"class":89,"line":1044},[87,1545,620],{"class":148},[87,1547,1548],{"class":89,"line":1059},[87,1549,1062],{"class":148},[78,1551,1553],{"className":118,"code":1552,"language":94,"meta":83,"style":83},"\u002F\u002F 用户信息桥接中间件：Gin Context → request.Context\ntype ctxKey string\nconst (\n    ctxUserID   ctxKey = \"userID\"\n    ctxUsername ctxKey = \"username\"\n)\n\nfunc WithUserContext() gin.HandlerFunc {\n    return func(c *gin.Context) {\n        ctx := c.Request.Context()\n        ctx = context.WithValue(ctx, ctxUserID, c.GetUint(\"userID\"))\n        ctx = context.WithValue(ctx, ctxUsername, c.GetString(\"username\"))\n        c.Request = c.Request.WithContext(ctx)\n        c.Next()\n    }\n}\n",[30,1554,1555,1560,1571,1579,1593,1605,1609,1613,1631,1651,1665,1691,1713,1728,1736,1740],{"__ignoreMap":83},[87,1556,1557],{"class":89,"line":90},[87,1558,1559],{"class":173},"\u002F\u002F 用户信息桥接中间件：Gin Context → request.Context\n",[87,1561,1562,1565,1568],{"class":89,"line":104},[87,1563,1564],{"class":126},"type",[87,1566,1567],{"class":93}," ctxKey",[87,1569,1570],{"class":126}," string\n",[87,1572,1573,1576],{"class":89,"line":145},[87,1574,1575],{"class":126},"const",[87,1577,1578],{"class":148}," (\n",[87,1580,1581,1584,1587,1590],{"class":89,"line":164},[87,1582,1583],{"class":415},"    ctxUserID",[87,1585,1586],{"class":93},"   ctxKey",[87,1588,1589],{"class":126}," =",[87,1591,1592],{"class":97}," \"userID\"\n",[87,1594,1595,1598,1600,1602],{"class":89,"line":177},[87,1596,1597],{"class":415},"    ctxUsername",[87,1599,1567],{"class":93},[87,1601,1589],{"class":126},[87,1603,1604],{"class":97}," \"username\"\n",[87,1606,1607],{"class":89,"line":189},[87,1608,231],{"class":148},[87,1610,1611],{"class":89,"line":201},[87,1612,142],{"emptyLinePlaceholder":141},[87,1614,1615,1617,1620,1623,1625,1627,1629],{"class":89,"line":211},[87,1616,336],{"class":126},[87,1618,1619],{"class":93}," WithUserContext",[87,1621,1622],{"class":148},"() ",[87,1624,987],{"class":93},[87,1626,348],{"class":148},[87,1628,1215],{"class":93},[87,1630,595],{"class":148},[87,1632,1633,1635,1637,1639,1641,1643,1645,1647,1649],{"class":89,"line":228},[87,1634,399],{"class":126},[87,1636,1224],{"class":126},[87,1638,219],{"class":148},[87,1640,981],{"class":341},[87,1642,984],{"class":126},[87,1644,987],{"class":93},[87,1646,348],{"class":148},[87,1648,351],{"class":93},[87,1650,387],{"class":148},[87,1652,1653,1656,1658,1661,1663],{"class":89,"line":390},[87,1654,1655],{"class":148},"        ctx ",[87,1657,152],{"class":126},[87,1659,1660],{"class":148}," c.Request.",[87,1662,351],{"class":93},[87,1664,805],{"class":148},[87,1666,1667,1669,1672,1675,1678,1681,1684,1686,1688],{"class":89,"line":396},[87,1668,1655],{"class":148},[87,1670,1671],{"class":126},"=",[87,1673,1674],{"class":148}," context.",[87,1676,1677],{"class":93},"WithValue",[87,1679,1680],{"class":148},"(ctx, ctxUserID, c.",[87,1682,1683],{"class":93},"GetUint",[87,1685,219],{"class":148},[87,1687,1515],{"class":97},[87,1689,1690],{"class":148},"))\n",[87,1692,1693,1695,1697,1699,1701,1704,1707,1709,1711],{"class":89,"line":419},[87,1694,1655],{"class":148},[87,1696,1671],{"class":126},[87,1698,1674],{"class":148},[87,1700,1677],{"class":93},[87,1702,1703],{"class":148},"(ctx, ctxUsername, c.",[87,1705,1706],{"class":93},"GetString",[87,1708,219],{"class":148},[87,1710,1529],{"class":97},[87,1712,1690],{"class":148},[87,1714,1715,1718,1720,1722,1725],{"class":89,"line":617},[87,1716,1717],{"class":148},"        c.Request ",[87,1719,1671],{"class":126},[87,1721,1660],{"class":148},[87,1723,1724],{"class":93},"WithContext",[87,1726,1727],{"class":148},"(ctx)\n",[87,1729,1730,1732,1734],{"class":89,"line":623},[87,1731,999],{"class":148},[87,1733,1539],{"class":93},[87,1735,805],{"class":148},[87,1737,1738],{"class":89,"line":629},[87,1739,620],{"class":148},[87,1741,1742],{"class":89,"line":647},[87,1743,1062],{"class":148},[73,1745,1746],{"id":1746},"组合使用",[78,1748,1750],{"className":118,"code":1749,"language":94,"meta":83,"style":83},"r.Any(\"\u002Fmcp\",\n    AuthRequired(authService),  \u002F\u002F 1. 验证 Token\n    WithUserContext(),           \u002F\u002F 2. 桥接用户信息到 request.Context\n    gin.WrapH(mcpHTTP),         \u002F\u002F 3. MCP 处理\n)\n",[30,1751,1752,1764,1775,1786,1799],{"__ignoreMap":83},[87,1753,1754,1756,1758,1760,1762],{"class":89,"line":90},[87,1755,747],{"class":148},[87,1757,750],{"class":93},[87,1759,219],{"class":148},[87,1761,710],{"class":97},[87,1763,294],{"class":148},[87,1765,1766,1769,1772],{"class":89,"line":104},[87,1767,1768],{"class":93},"    AuthRequired",[87,1770,1771],{"class":148},"(authService),  ",[87,1773,1774],{"class":173},"\u002F\u002F 1. 验证 Token\n",[87,1776,1777,1780,1783],{"class":89,"line":145},[87,1778,1779],{"class":93},"    WithUserContext",[87,1781,1782],{"class":148},"(),           ",[87,1784,1785],{"class":173},"\u002F\u002F 2. 桥接用户信息到 request.Context\n",[87,1787,1788,1791,1793,1796],{"class":89,"line":164},[87,1789,1790],{"class":148},"    gin.",[87,1792,760],{"class":93},[87,1794,1795],{"class":148},"(mcpHTTP),         ",[87,1797,1798],{"class":173},"\u002F\u002F 3. MCP 处理\n",[87,1800,1801],{"class":89,"line":177},[87,1802,231],{"class":148},[16,1804,1805],{},"在工具处理器里就能拿到用户信息了：",[78,1807,1809],{"className":118,"code":1808,"language":94,"meta":83,"style":83},"func (h *Handler) handleListItems(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n    userID, ok := ctx.Value(ctxUserID).(uint)\n    if !ok {\n        return mcp.NewToolResultError(\"未认证\"), nil\n    }\n\n    items, _ := h.service.ListByUser(userID)\n    \u002F\u002F ...\n    return mcp.NewToolResultText(result), nil\n}\n",[30,1810,1811,1867,1888,1897,1914,1918,1922,1938,1943,1956],{"__ignoreMap":83},[87,1812,1813,1815,1818,1821,1823,1826,1828,1831,1833,1835,1837,1839,1841,1843,1845,1847,1849,1851,1853,1855,1857,1859,1861,1863,1865],{"class":89,"line":90},[87,1814,336],{"class":126},[87,1816,1817],{"class":148}," (",[87,1819,1820],{"class":341},"h ",[87,1822,371],{"class":126},[87,1824,1825],{"class":93},"Handler",[87,1827,1208],{"class":148},[87,1829,1830],{"class":93},"handleListItems",[87,1832,219],{"class":148},[87,1834,342],{"class":341},[87,1836,345],{"class":93},[87,1838,348],{"class":148},[87,1840,351],{"class":93},[87,1842,354],{"class":148},[87,1844,357],{"class":341},[87,1846,360],{"class":93},[87,1848,348],{"class":148},[87,1850,365],{"class":93},[87,1852,368],{"class":148},[87,1854,371],{"class":126},[87,1856,374],{"class":93},[87,1858,348],{"class":148},[87,1860,379],{"class":93},[87,1862,354],{"class":148},[87,1864,384],{"class":126},[87,1866,387],{"class":148},[87,1868,1869,1872,1874,1877,1880,1883,1886],{"class":89,"line":104},[87,1870,1871],{"class":148},"    userID, ok ",[87,1873,152],{"class":126},[87,1875,1876],{"class":148}," ctx.",[87,1878,1879],{"class":93},"Value",[87,1881,1882],{"class":148},"(ctxUserID).(",[87,1884,1885],{"class":126},"uint",[87,1887,231],{"class":148},[87,1889,1890,1892,1894],{"class":89,"line":145},[87,1891,583],{"class":126},[87,1893,1368],{"class":126},[87,1895,1896],{"class":148},"ok {\n",[87,1898,1899,1901,1903,1905,1907,1910,1912],{"class":89,"line":164},[87,1900,600],{"class":126},[87,1902,283],{"class":148},[87,1904,605],{"class":93},[87,1906,219],{"class":148},[87,1908,1909],{"class":97},"\"未认证\"",[87,1911,412],{"class":148},[87,1913,416],{"class":415},[87,1915,1916],{"class":89,"line":177},[87,1917,620],{"class":148},[87,1919,1920],{"class":89,"line":189},[87,1921,142],{"emptyLinePlaceholder":141},[87,1923,1924,1927,1929,1932,1935],{"class":89,"line":201},[87,1925,1926],{"class":148},"    items, _ ",[87,1928,152],{"class":126},[87,1930,1931],{"class":148}," h.service.",[87,1933,1934],{"class":93},"ListByUser",[87,1936,1937],{"class":148},"(userID)\n",[87,1939,1940],{"class":89,"line":211},[87,1941,1942],{"class":173},"    \u002F\u002F ...\n",[87,1944,1945,1947,1949,1951,1954],{"class":89,"line":228},[87,1946,399],{"class":126},[87,1948,283],{"class":148},[87,1950,404],{"class":93},[87,1952,1953],{"class":148},"(result), ",[87,1955,416],{"class":415},[87,1957,1958],{"class":89,"line":390},[87,1959,1062],{"class":148},[73,1961,1962],{"id":1962},"客户端配置",[16,1964,1965],{},"AI 客户端（如 Claude Desktop）配置 MCP 服务器时带上 Authorization 头：",[78,1967,1971],{"className":1968,"code":1969,"language":1970,"meta":83,"style":83},"language-json shiki shiki-themes github-light github-dark","{\n  \"mcpServers\": {\n    \"my-app\": {\n      \"url\": \"http:\u002F\u002Flocalhost:8080\u002Fmcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer your-token-here\"\n      }\n    }\n  }\n}\n","json",[30,1972,1973,1978,1986,1992,2004,2011,2021,2026,2030,2035],{"__ignoreMap":83},[87,1974,1975],{"class":89,"line":90},[87,1976,1977],{"class":148},"{\n",[87,1979,1980,1983],{"class":89,"line":104},[87,1981,1982],{"class":415},"  \"mcpServers\"",[87,1984,1985],{"class":148},": {\n",[87,1987,1988,1990],{"class":89,"line":145},[87,1989,167],{"class":415},[87,1991,1985],{"class":148},[87,1993,1994,1997,1999,2002],{"class":89,"line":164},[87,1995,1996],{"class":415},"      \"url\"",[87,1998,1025],{"class":148},[87,2000,2001],{"class":97},"\"http:\u002F\u002Flocalhost:8080\u002Fmcp\"",[87,2003,294],{"class":148},[87,2005,2006,2009],{"class":89,"line":177},[87,2007,2008],{"class":415},"      \"headers\"",[87,2010,1985],{"class":148},[87,2012,2013,2016,2018],{"class":89,"line":189},[87,2014,2015],{"class":415},"        \"Authorization\"",[87,2017,1025],{"class":148},[87,2019,2020],{"class":97},"\"Bearer your-token-here\"\n",[87,2022,2023],{"class":89,"line":201},[87,2024,2025],{"class":148},"      }\n",[87,2027,2028],{"class":89,"line":211},[87,2029,620],{"class":148},[87,2031,2032],{"class":89,"line":228},[87,2033,2034],{"class":148},"  }\n",[87,2036,2037],{"class":89,"line":390},[87,2038,1062],{"class":148},[11,2040,2041],{"id":2041},"总结",[2043,2044,2045,2058],"table",{},[2046,2047,2048],"thead",{},[2049,2050,2051,2055],"tr",{},[2052,2053,2054],"th",{},"步骤",[2052,2056,2057],{},"说明",[2059,2060,2061,2071,2083,2092,2101],"tbody",{},[2049,2062,2063,2068],{},[2064,2065,2066],"td",{},[30,2067,158],{},[2064,2069,2070],{},"创建 MCP 协议实例",[2049,2072,2073,2080],{},[2064,2074,2075,2077,2078],{},[30,2076,286],{}," + ",[30,2079,330],{},[2064,2081,2082],{},"定义并注册工具",[2049,2084,2085,2089],{},[2064,2086,2087],{},[30,2088,695],{},[2064,2090,2091],{},"转为 HTTP Handler",[2049,2093,2094,2098],{},[2064,2095,2096],{},[30,2097,663],{},[2064,2099,2100],{},"挂载到 Gin 路由",[2049,2102,2103,2111],{},[2064,2104,2105,2077,2108],{},[30,2106,2107],{},"AuthRequired",[30,2109,2110],{},"WithUserContext",[2064,2112,2113],{},"鉴权 + 用户信息桥接",[16,2115,2116,2117,2120,2121,1178],{},"核心就一句话：",[38,2118,2119],{},"MCP 是 HTTP Handler，挂上去就行。"," 鉴权复用你已有的中间件体系，只需要多写一个桥接函数把用户信息传进 ",[30,2122,1177],{},[2124,2125,2126],"style",{},"html pre.shiki code .sw2iP, html code.shiki .sw2iP{--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .scXbn, html code.shiki .scXbn{--shiki-light:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHbNN, html code.shiki .sHbNN{--shiki-light:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .suQ91, html code.shiki .suQ91{--shiki-light:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .siTax, html code.shiki .siTax{--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sIX_F, html code.shiki .sIX_F{--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sPK6S, html code.shiki .sPK6S{--shiki-light:#E36209;--shiki-dark:#FFAB70}",{"title":83,"searchDepth":104,"depth":145,"links":2128},[2129,2130,2131,2132,2139,2145],{"id":13,"depth":104,"text":14},{"id":24,"depth":104,"text":25},{"id":43,"depth":104,"text":43},{"id":71,"depth":104,"text":71,"children":2133},[2134,2135,2136,2137,2138],{"id":75,"depth":145,"text":76},{"id":114,"depth":145,"text":115},{"id":247,"depth":145,"text":248},{"id":652,"depth":145,"text":653},{"id":773,"depth":145,"text":774},{"id":1149,"depth":104,"text":1150,"children":2140},[2141,2142,2143,2144],{"id":1156,"depth":145,"text":1156},{"id":1170,"depth":145,"text":1171},{"id":1746,"depth":145,"text":1746},{"id":1962,"depth":145,"text":1962},{"id":2041,"depth":104,"text":2041},"介绍如何使用 mark3labs\u002Fmcp-go 与 Gin 框架集成，搭建一个带鉴权的 MCP（Model Context Protocol）服务器，让 AI Agent 能直接调用你的后端服务。",false,"md","\u002Fposts\u002Fgo-mcp-gin\u002Fimg\u002Fcover.svg",{},"\u002Fposts\u002Fgo-mcp-gin","2026-06-01T00:50:00",{"title":5,"description":2146},"posts\u002Fgo-mcp-gin\u002Findex",[2156,2157,2158,2159,2160],"Go","MCP","Gin","AI Agent","后端","hsvJkRE0ePPsoEmWVhOynhgvBdWZ_Z-vv8puDNRCQHs",1780733791991]