新增token
This commit is contained in:
+25
-13
@@ -1,10 +1,14 @@
|
||||
# 汽车零部件智能报价 — 两个 MCP 服务编排
|
||||
# 数据库为外部 PostgreSQL(192.168.1.206:5432),不在本编排内
|
||||
# 启动:docker compose up -d
|
||||
# 生产环境:docker compose up -d
|
||||
# 默认连生产库 192.168.1.206:5432,对外 192.168.1.119:8001/8002
|
||||
# 开发环境:本机直接 python server.py(不入容器)
|
||||
# source .env.dev 后到 mcp-for-{erp,crm}/src 执行 python server.py
|
||||
# 连开发库 47.101.220.40:5432,对外 localhost:8001/8002
|
||||
# 也可用 docker 跑开发:docker compose --env-file .env.dev up -d
|
||||
# 停止:docker compose down
|
||||
# 日志:docker compose logs -f
|
||||
# 网络模式 host:容器直接共享宿主网络栈,绕开 Docker 桥接 FORWARD 链,
|
||||
# 便于局域网其他机器经 192.168.1.119:8001/8002 直接访问(无需 ports 映射)
|
||||
# 便于局域网其他机器直接访问(无需 ports 映射)
|
||||
services:
|
||||
mcp-for-erp:
|
||||
build:
|
||||
@@ -16,11 +20,15 @@ services:
|
||||
container_name: mcp-for-erp
|
||||
network_mode: host
|
||||
environment:
|
||||
- ERP_DB_HOST=192.168.1.206
|
||||
- ERP_DB_PORT=5432
|
||||
- ERP_DB_USER=postgres
|
||||
- ERP_DB_PASSWORD=digiwin
|
||||
- ERP_DB_NAME=smart_quotation_auto
|
||||
- ERP_DB_HOST=${ERP_DB_HOST:-192.168.1.206}
|
||||
- ERP_DB_PORT=${ERP_DB_PORT:-5432}
|
||||
- ERP_DB_USER=${ERP_DB_USER:-postgres}
|
||||
- ERP_DB_PASSWORD=${ERP_DB_PASSWORD:-digiwin}
|
||||
- ERP_DB_NAME=${ERP_DB_NAME:-smart_quotation_auto}
|
||||
# 对外地址(用于 OAuth 资源元数据),按实际部署机 IP 调整
|
||||
- MCP_PUBLIC_URL=${MCP_PUBLIC_URL_ERP:-http://192.168.1.119:8001}
|
||||
# Bearer Token 表:token:client_id 逗号分隔,每个客户端一个独立 token
|
||||
- MCP_AUTH_TOKENS=${MCP_AUTH_TOKENS_ERP:-erp-token-trae-2026:trae,erp-token-partner-a:partner-a}
|
||||
restart: unless-stopped
|
||||
|
||||
mcp-for-crm:
|
||||
@@ -33,9 +41,13 @@ services:
|
||||
container_name: mcp-for-crm
|
||||
network_mode: host
|
||||
environment:
|
||||
- CRM_DB_HOST=192.168.1.206
|
||||
- CRM_DB_PORT=5432
|
||||
- CRM_DB_USER=postgres
|
||||
- CRM_DB_PASSWORD=digiwin
|
||||
- CRM_DB_NAME=smart_quotation_auto
|
||||
- CRM_DB_HOST=${CRM_DB_HOST:-192.168.1.206}
|
||||
- CRM_DB_PORT=${CRM_DB_PORT:-5432}
|
||||
- CRM_DB_USER=${CRM_DB_USER:-postgres}
|
||||
- CRM_DB_PASSWORD=${CRM_DB_PASSWORD:-digiwin}
|
||||
- CRM_DB_NAME=${CRM_DB_NAME:-smart_quotation_auto}
|
||||
# 对外地址(用于 OAuth 资源元数据),按实际部署机 IP 调整
|
||||
- MCP_PUBLIC_URL=${MCP_PUBLIC_URL_CRM:-http://192.168.1.119:8002}
|
||||
# Bearer Token 表:token:client_id 逗号分隔,每个客户端一个独立 token
|
||||
- MCP_AUTH_TOKENS=${MCP_AUTH_TOKENS_CRM:-crm-token-trae-2026:trae,crm-token-partner-a:partner-a}
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""静态 Bearer Token 鉴权(Authorization)。
|
||||
|
||||
token → 客户端 映射来自环境变量 MCP_AUTH_TOKENS,格式(逗号分隔,每对 token:client_id):
|
||||
MCP_AUTH_TOKENS=token-trae:trae,token-partner:partner-a
|
||||
|
||||
每个客户端配置自己的 token 调用,服务端校验失败返回 401;
|
||||
工具内可通过 get_caller() 获取当前调用方标识。
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from mcp.server.auth.middleware.auth_context import get_access_token
|
||||
from mcp.server.auth.provider import AccessToken
|
||||
from mcp.server.auth.settings import AuthSettings
|
||||
|
||||
|
||||
class StaticTokenVerifier:
|
||||
"""静态 token 查表校验:命中返回 AccessToken(client_id 即客户端标识),未命中返回 None(401)。"""
|
||||
|
||||
def __init__(self, tokens: dict[str, str]):
|
||||
self._tokens = tokens
|
||||
|
||||
async def verify_token(self, token: str) -> AccessToken | None:
|
||||
client_id = self._tokens.get(token)
|
||||
if client_id is None:
|
||||
return None
|
||||
return AccessToken(token=token, client_id=client_id, scopes=[], expires_at=None)
|
||||
|
||||
|
||||
def _parse_tokens(raw: str) -> dict[str, str]:
|
||||
"""解析 'token1:client1,token2:client2' → {token: client_id}"""
|
||||
tokens: dict[str, str] = {}
|
||||
for item in raw.split(","):
|
||||
item = item.strip()
|
||||
if not item:
|
||||
continue
|
||||
token, _, client_id = item.partition(":")
|
||||
token, client_id = token.strip(), client_id.strip()
|
||||
if token and client_id:
|
||||
tokens[token] = client_id
|
||||
return tokens
|
||||
|
||||
|
||||
def get_auth(port: int) -> tuple[AuthSettings, StaticTokenVerifier]:
|
||||
"""构建 MCPServer 的 (auth, token_verifier) 参数。
|
||||
|
||||
服务对外地址默认 http://localhost:{port},部署时用 MCP_PUBLIC_URL 覆盖
|
||||
(如 http://192.168.1.119:8002),用于 OAuth 资源元数据发现。
|
||||
"""
|
||||
url = os.getenv("MCP_PUBLIC_URL") or f"http://localhost:{port}"
|
||||
tokens = _parse_tokens(os.getenv("MCP_AUTH_TOKENS", ""))
|
||||
if not tokens:
|
||||
raise RuntimeError("环境变量 MCP_AUTH_TOKENS 未配置,格式:token1:client1,token2:client2")
|
||||
return (
|
||||
AuthSettings(issuer_url=url, resource_server_url=url),
|
||||
StaticTokenVerifier(tokens),
|
||||
)
|
||||
|
||||
|
||||
def get_caller() -> str:
|
||||
"""工具内获取当前调用方标识(未认证时返回 anonymous)。"""
|
||||
access_token = get_access_token()
|
||||
return access_token.client_id if access_token else "anonymous"
|
||||
@@ -8,6 +8,7 @@ import asyncio
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from mcp.server import MCPServer
|
||||
from auth import get_auth
|
||||
from db import get_pool
|
||||
|
||||
|
||||
@@ -23,10 +24,15 @@ def _s(value):
|
||||
return value
|
||||
|
||||
|
||||
# 鉴权:静态 Bearer Token,token → 客户端 映射来自环境变量 MCP_AUTH_TOKENS
|
||||
_auth, _token_verifier = get_auth(port=8002)
|
||||
|
||||
app = MCPServer(
|
||||
name="mcp-for-crm-auto",
|
||||
description="汽车零部件智能报价 CRM 数据服务",
|
||||
version="1.0.0"
|
||||
version="1.0.0",
|
||||
auth=_auth,
|
||||
token_verifier=_token_verifier,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""静态 Bearer Token 鉴权(Authorization)。
|
||||
|
||||
token → 客户端 映射来自环境变量 MCP_AUTH_TOKENS,格式(逗号分隔,每对 token:client_id):
|
||||
MCP_AUTH_TOKENS=token-trae:trae,token-partner:partner-a
|
||||
|
||||
每个客户端配置自己的 token 调用,服务端校验失败返回 401;
|
||||
工具内可通过 get_caller() 获取当前调用方标识。
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from mcp.server.auth.middleware.auth_context import get_access_token
|
||||
from mcp.server.auth.provider import AccessToken
|
||||
from mcp.server.auth.settings import AuthSettings
|
||||
|
||||
|
||||
class StaticTokenVerifier:
|
||||
"""静态 token 查表校验:命中返回 AccessToken(client_id 即客户端标识),未命中返回 None(401)。"""
|
||||
|
||||
def __init__(self, tokens: dict[str, str]):
|
||||
self._tokens = tokens
|
||||
|
||||
async def verify_token(self, token: str) -> AccessToken | None:
|
||||
client_id = self._tokens.get(token)
|
||||
if client_id is None:
|
||||
return None
|
||||
return AccessToken(token=token, client_id=client_id, scopes=[], expires_at=None)
|
||||
|
||||
|
||||
def _parse_tokens(raw: str) -> dict[str, str]:
|
||||
"""解析 'token1:client1,token2:client2' → {token: client_id}"""
|
||||
tokens: dict[str, str] = {}
|
||||
for item in raw.split(","):
|
||||
item = item.strip()
|
||||
if not item:
|
||||
continue
|
||||
token, _, client_id = item.partition(":")
|
||||
token, client_id = token.strip(), client_id.strip()
|
||||
if token and client_id:
|
||||
tokens[token] = client_id
|
||||
return tokens
|
||||
|
||||
|
||||
def get_auth(port: int) -> tuple[AuthSettings, StaticTokenVerifier]:
|
||||
"""构建 MCPServer 的 (auth, token_verifier) 参数。
|
||||
|
||||
服务对外地址默认 http://localhost:{port},部署时用 MCP_PUBLIC_URL 覆盖
|
||||
(如 http://192.168.1.119:8002),用于 OAuth 资源元数据发现。
|
||||
"""
|
||||
url = os.getenv("MCP_PUBLIC_URL") or f"http://localhost:{port}"
|
||||
tokens = _parse_tokens(os.getenv("MCP_AUTH_TOKENS", ""))
|
||||
if not tokens:
|
||||
raise RuntimeError("环境变量 MCP_AUTH_TOKENS 未配置,格式:token1:client1,token2:client2")
|
||||
return (
|
||||
AuthSettings(issuer_url=url, resource_server_url=url),
|
||||
StaticTokenVerifier(tokens),
|
||||
)
|
||||
|
||||
|
||||
def get_caller() -> str:
|
||||
"""工具内获取当前调用方标识(未认证时返回 anonymous)。"""
|
||||
access_token = get_access_token()
|
||||
return access_token.client_id if access_token else "anonymous"
|
||||
@@ -7,6 +7,7 @@ HTTP 模式,支持远程调用
|
||||
import asyncio
|
||||
import json
|
||||
from mcp.server import MCPServer
|
||||
from auth import get_auth
|
||||
from db import get_pool
|
||||
|
||||
|
||||
@@ -22,10 +23,15 @@ def _s(value):
|
||||
return value
|
||||
|
||||
|
||||
# 鉴权:静态 Bearer Token,token → 客户端 映射来自环境变量 MCP_AUTH_TOKENS
|
||||
_auth, _token_verifier = get_auth(port=8001)
|
||||
|
||||
app = MCPServer(
|
||||
name="mcp-for-erp-auto",
|
||||
description="汽车零部件智能报价 ERP 数据服务",
|
||||
version="1.0.0"
|
||||
version="1.0.0",
|
||||
auth=_auth,
|
||||
token_verifier=_token_verifier,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user