内容
mcp-webinar
关于 **MCP(模型上下文协议)** 的网络研讨会材料。
该项目展示了同一个代理通过五种不同的方式获得工具,您可以清楚地看到MCP相对于手写工具的优势,以及身份验证如何融入其中。
| # | 演示机应用 | MCP 服务器 |
|---|------|----------|------------|
| | 自定义工具,无MCP | `host/app_custom_tools.py` |
| 2 | MCP通过可流式HTTP | `host/app_mcp_no_auth (`TRANSPORT="http"`) | `servers/http_server.py` |
| 3 | MCP通过标准输入输出 | `host/app_mcp_no_auth.py` (`TRANSPORT="stdio"`) |/stdio_server.py`(自动生成) |
| 4 | MCP使用API密钥身份验证 | `host/app_mcp_auth.py` (`AUTH="api_key"`) | `servers/api_key_mcp_server.py` |
| 5 | MCP使用OAuth(Keycloak) | `host/app_mcp_auth.py` (`AUTH="oauth"`) | `servers/oauth_mcp_server.py` |
代理始终与 **[模拟用户服务](https://github.com/khshanovskyi/mock-user-service)** 交互(搜索/创建/更新/删除用户)。
## 设置
```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export OPENAI_API_KEY=sk-... # 必需
# 可选覆盖:OPENAI_MODEL, MCP_API_KEY, *_URL(参见 commons/constants.py)
# 模拟用户服务(所有演示都需要);Keycloak(仅演示 5)
docker compose up -d userservice
```
从 **仓库根目录** 运行以下每个命令。
### 演示 1 — 自定义工具
```bash
python -m host.app_custom_tools
```
---
### 演示 2 — MCP 通过 HTTP
```bash
m servers.http_server # 终端 1
python -m host.app_mcp_no_auth # 终端 2 (TRANSPORT="http")
```
---
### 演示 3 — MCP 通过标准输入输出
在 `host/app_mcp_no_auth.py` 中设置 `TRANSPORT="(服务器会自动生成):
```bash
python -m host.app_mcp_no_auth # 选择 "stdio" 当提示时
```
<details>
<summary><b>将您的标准输入输出MCP服务器连接到Claude Desktop</b></summary>
这使用您的 `servers/stdio_server.py` 入口点 — 对于本地开发最简单、最可靠。
### 步骤 1:查找Claude Desktop配置文件
| 操作系统 | 路径 |
|-------------|-------------------------------------------------------------------|
| **macOS** | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| **Windows** | `%APPDATA%\Claude\claude_desktop_config.json` |
### 步骤 2:编辑配置文件
打开文件(如果不存在则创建)并添加您的服务器:
```json
{
"mcpServers": {
"users-management": {
"command": "{ABSOLUTE_PATH}/mcp-webinar/.venv/bin/python",
"args": [
"{ABSOLUTE_PATH}/mcp-webinar/servers/stdio_server.py"
],
"env": {
"PYTHONPATH": "{ABSOLUTE_PATH}/mcp-webinar"
}
}
}
}
```
**重要注意事项:**
- 不要 `{ABSOLUTE_PATH}` 替换为项目在您本地机器上的绝对路径
- 将 `` 设置为项目根目录,以便导入 `servers` 和 `commons` 等模块正确解析
- 如果您正在使用虚拟环境,请指向其Python二进制:
```json
"command": "/path/to/your/venv/bin/python"
```
在Windows上:`"C:\\Users\\project\\.venv\\Scripts\\python.exe"`
<details>
<summary><b>如何在我的Mac上执行此操作:</b></summary>
```json
{
"mcpServers": {
"users-management": {
"command": "/Users/pavlokhshanovskyi/my-courses/mcp-webinar/.venv/bin/python",
"args": [
"/Users/pavlokhshanovskyi/my-courses/mcp-webinar/servers/stdio_server.py"
],
"env": {
"PYTHONPATH": "/Users/pavlokhshanovskyi/my-courses/mcp-webinar"
}
}
}
}
```

</details>
### 步骤 3:重启Claude Desktop
完全退出并重新打开Claude Desktop。在重新打开时,Claude可能会要求访问项目。在连接器部分,您将能够找到 `users-management`。
</details>
---
### 演示 4 — MCP 使用API密钥
```bash
python servers.api_key_mcp_server # 终端 1
python -m host.app_mcp_auth # 终端 2 (AUTH="api_key")
```
---
### 演示 5 — MCP 使用OAuth(Keycloak)
```bash
docker compose up -d keycloak
python -m servers.oauth_mcp_server # 终端 1
# 在 host/app_mcp_auth.py 中设置 AUTH="oauth"
python -m host.app_mcp_auth # 终端 2 — 打开浏览器登录
```
以 `mcp-user` / `password` 登录(它具有所需的角色)。服务器遵循MCP授权规范 — 它在 `/.well-known/oauth-protected-resource/mcp` 处提供受保护的资源元数据,并使用 `401` + `WWW-Authenticate` 质疑未经身份验证的调用,以便客户端自行发现Keycloak(客户端中没有硬编码端点)。
> ⚠️ 访问令牌TTL故意设置为60秒,以便您可以演示自动刷新。
→ 查看 [**Keycloak**](#keycloak) 部分以获取凭证、端点、用户和Postman设置。
输入 `exit`(或 `quit`)以退出任何聊天。
---
## Keycloak
[Keycloak](https://www.keycloak.org/) 是一个开源的身份和访问管理解决方案。它处理用户身份验证、颁发JWT令牌和管理角色/权限 — 这样您的服务就不必自己实现这些功能。
在演示 5 中,Keycloak 是 OAuth 2.0 流中的 **授权服务器**:它通过浏览器登录页面验证用户,并颁发一个签名的JWT访问令牌,MCP服务器在每个请求上验证该令牌。
该项目中MCP授权规范的各个部分如何映射:
- **资源服务器** → MCP服务器(`servers/oauth_mcp_server.py`)。它使用fastmcp的 `RemoteAuthProvider`,自动在 `http://localhost:8008/.well-known/oauth-protected-resource/mcp` 处提供受保护的资源元数据,并使用 `401` + `WWW-Authenticate` 回答未经身份验证的请求。令牌验证(JWKS签名、颁发者、所需角色)位于 `servers/auth/oauth.py` (`KeycloakRoleScopeVerifier`,fastmcp的 `JWTVerifier` 的薄子类)。
- **客户端** → 主机(`host/mcp_client.py`,`build_oauth`)。它使用fastmcp的 `OAuth` 助手,该助手从服务器元数据中发现Keycloak,运行PKCE浏览器流,并透明地刷新令牌 — Keycloak端点在客户端中没有硬编码。
###
| | |
|--------------|-----------------------|
| **网址** | http://localhost:8089 |
| **用户名** | `admin |
| **密码** | `admin` |
###配置
领域通过 [`_keycloak/mcp-re.json`](_keycloak/mcp-realm-config.json) 预先配置。在启动时,Keycloak会自动导入它(`start-dev --import-realm`)。
| 设置 | 值 |
|-------------|---------------------------------------------------------------|
| 领域名称 | `mcp-realm` |
| 客户端ID | `mcp-client` |
| 客户端类型 | 公共(PKCE,无客户端密钥) |
| 令牌TTL | **60秒** ⚠️(故意较短以测试令牌刷新) |
### Keycloak 端点
| 端点 | 网址 |
|----------------------------|-------------------------------------------------------------------------|
| 众所周ID配置 | http://localhost:8089/realms/mcp-realm/./openid-configuration |
| JWKS(公钥) | http://localhost:8089/realms/mcp-realm/protocol/openid-connect/certs |
| 令牌 | http://localhost:8089/realms/mcp-realm/protocol/openid-connect/token |
| 授权 | http://localhost:8089/realms/mcp-realm/protocol/openid-connect/auth |
> MCP服务器通过其自己的受保护资源元数据指向上述领域:
> `curl http://localhost:8008/.well-known/oauth-protected-resource/mcp`
### 预配置用户
| 用户名 | 密码 | 领域角色 | MCP访问 |
|------------------|------------|--------------------|-----------------------|
| `mcp-user` | `password` | `mcp-tools-access` | ✅ 允许 |
| `no-access-user` | `password` | *(无)* | ❌ `401 未授权` |
### (可选)在Postman中测试
Post连接到Keycloak(授权服务器)— MCP客户端自动发现的相同端点。创建一个 → `POST http://localhost:8008/mcp`,然后打开 **授权** 选项卡:
| 参数 | 值 |
|----------------------|
| 身份验证类型 | `OAuth 2.0` |
| 授予类型 | `授权码(带PKCE)` |
| 回调网址 | 参见下面的选项 |
| 授权网址 | http://localhost:8089/realms/mcp-realm/protocol/openid-connect/auth |
| 访问令牌网址 | http://localhost:8089/realms/mcp-realm/protocol/openid-connect/token |
| 客户端ID | `mcp-client` |
| 代码挑战 | `SHA-256` |
| 作用 | `openid profile` |
`mcp-client了两种回调网址,请选择一种:
- **Postman托管**(默认):`https://oauth.pstmn.io/v1/callback` — 取消选中 *使用浏览器授权*。
- **本地**: `http://localhost:9999/callback` — 选中 *使用浏览器授权*。
点击 **获取新访问令牌**,以 `mcp-user` / `password` 登录,然后 **使用令牌** 并发送请求。`mcp-tools-access` 角色会自动添加到令牌中(通过Keycloak的默认 `roles` 作用域),服务器会根据它进行授权 — 您 **不** 将其作为作用域请求。登录为 `no-access-user` 会产生一个没有该角色的令牌,因此服务器返回 `401`。