上一篇已經讓 .NET 10 ASP.NET Core 支援 MCP,OpenCode 也可以直接呼叫 UpdateHoroscope 去修改網站上的雙子座運勢
不過上一篇是直接連 http://localhost:5100/mcp
因為現在這個 MCP Tool 不是只能查資料,它是真的可以修改 Server Data
所以這篇就補一個最簡單的 Bearer Token,先不要扯 OAuth,單純做到 Token 不對就不能使用 MCP
OpenCode
↓
Bearer Token
↓
ASP.NET Core MCP
↓
UpdateHoroscopeProgram.cs 加入 Bearer Token
上一篇的 HoroscopeService 跟 HoroscopeTools 都不用改
這次直接改 Program.cs 就好
using System.Net;
using McpHoroscope;
using ModelContextProtocol.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<HoroscopeService>();
builder.Services
.AddMcpServer()
.WithHttpTransport(options =>
{
// 這篇沒有要保存 MCP Session
// 先用 Stateless 就好
options.SessionMode =
HttpServerSessionMode.Stateless;
})
.WithTools<HoroscopeTools>();
var app = builder.Build();
// 這篇只是測試,所以先直接寫死比較快
// 正式環境不要真的這樣放
const string mcpApiKey =
"my-mcp-secret-key";
app.Use(
async (context, next) =>
{
// 一般網站不用驗證
// 只有 /mcp 進來才檢查 Token
if (context.Request.Path.StartsWithSegments("/mcp"))
{
var authorization =
context.Request.Headers.Authorization.ToString();
var expected =
$"Bearer {mcpApiKey}";
// Token 不對就直接擋掉
// 不要讓 Request 繼續跑到 MCP Tool
if (authorization != expected)
{
context.Response.StatusCode =
StatusCodes.Status401Unauthorized;
return;
}
}
await next();
});
app.MapGet(
"/",
(HoroscopeService horoscopeService) =>
{
var horoscope =
horoscopeService.Get();
var content =
WebUtility.HtmlEncode(
horoscope.Content);
var updatedAt =
horoscope.UpdatedAt is null
? "尚未更新"
: horoscope.UpdatedAt.Value
.ToString("yyyy-MM-dd HH:mm:ss");
// 這篇只是測 MCP
// 頁面先直接放在這裡,不另外拆 Razor
var html = $$"""
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<title>雙子座今日運勢</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 720px;
margin: 80px auto;
padding: 0 20px;
line-height: 1.8;
}
.card {
padding: 30px;
border: 1px solid #ddd;
border-radius: 12px;
}
.time {
margin-top: 20px;
color: #777;
font-size: 14px;
}
</style>
</head>
<body>
<div class="card">
<h1>雙子座今日運勢</h1>
<p>{{content}}</p>
<div class="time">
Updated: {{updatedAt}}
</div>
</div>
</body>
</html>
""";
return Results.Content(
html,
"text/html; charset=utf-8");
});
// MCP Endpoint 還是一樣放 /mcp
app.MapMcp("/mcp");
app.Run("http://localhost:5100");這邊其實沒有做什麼複雜的東西
只要 Request 是進 /mcp,我就先看 Authorization Header
這次直接規定要帶 Bearer my-mcp-secret-key
Token 不對就直接回 401 Unauthorized
一般網站首頁不受影響,只有 MCP Endpoint 需要驗證
API Key 我這篇直接寫死,單純讓範例比較容易看懂
不過順帶一提,最近被駭客上了一課
現在這種 API Key、Token、Password 我自己是真的會勤勞一點,正式環境盡量放 Environment Variable 或 Secret Manager,不要直接塞 Source Code,更不要丟進 Git
這篇只是測試,所以先直接寫死就好
OpenCode 帶上 Bearer Token
Server 這邊開始檢查 Token 之後,OpenCode 當然也要一起帶過來
修改上一篇的 opencode.jsonc
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"horoscope": {
"type": "remote",
"url": "http://localhost:5100/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer my-mcp-secret-key"
},
"protocol": "auto",
"codemode": false
}
}
}
}跟上一篇比起來其實就只是多了 headers
OpenCode 連 MCP Server 的時候,就會一起送出 Authorization: Bearer my-mcp-secret-key
ASP.NET Core 收到 Request 之後先檢查 Token,正確才會繼續進 MCP
先故意給錯 Token
我自己測這種東西會先故意給錯 Token,看是不是真的有擋到
例如把 OpenCode 改成 Bearer wrong-key
再執行 opencode mcp list
這時 ASP.NET Core 應該直接回 401 Unauthorized
再把 Token 改回 my-mcp-secret-key
重新連線後就可以正常 Connected
這樣至少先確認不是只有 Code 看起來像有驗證,結果實際上根本沒擋到
再測一次 UpdateHoroscope
接著回 OpenCode,Prompt 跟上一篇差不多
今天是 2026-09-18
請幫我產生雙子座今天的星座運勢,
使用繁體中文,大約 100 字,
適合直接顯示在網站上
產生完成後,
請更新網站上的雙子座今日運勢OpenCode 一樣先產生內容,再呼叫 UpdateHoroscope
差別只是現在每次連 MCP Server 都要先通過 Bearer Token 驗證
Token 正確才可以真的進到 MCP Tool
網站重新整理之後,一樣可以看到剛剛 OpenCode 產生的雙子座運勢
簡單結論一下
上一篇直接開 /mcp 在 localhost 測其實沒什麼問題
但 MCP Tool 一旦開始可以修改 Server Data,我覺得至少先加一層基本驗證比較合理
這次只是多一個 Bearer Token,知道 MCP URL 還不夠,還要有正確 Token 才能真的呼叫 Tool
當然這篇為了測試方便直接把 Token 寫死
真的上線就不要這樣搞,尤其我最近才被現實教育過一次,這種 Secret 還是乖乖放 Environment Variable 或 Secret Manager 比較安心
這篇先把最基本的 MCP Authentication 補起來
後面有機會再來測正式 OAuth,不然現在一次放太多東西進來又會開始亂掉,專案真實狀況加入OAuth 的驗證吧
如果不會寫,反正現在有 AI :)
---
The bug existed in all possible states.
Until I ran the code.
