Acidni MCP门户
MCP发现门户 按用户密钥管理 -用户仪表板、自助APIM密钥生命周期和Acidni MCP服务器的自动发现。
特性
- 每个用户的APIM密钥:每个用户为每个MCP服务器获得唯一的API密钥(无共享密钥)
- 自助服务密钥生命周期:从仪表板生成、旋转和撤销密钥
- 审计跟踪:每个关键操作都记录到Cosmos DB中以符合要求
- 用户黑板:使用Microsoft登录以查看可用的MCP服务器并管理您的密钥
- 自动发现:
/.well-known/mcpMCP客户端自动发现服务器的端点 - 复制粘贴配置:Claude Desktop、VS Code和cURL的预格式化配置-使用您的密钥进行个性化设置
- 健康监测:所有注册的MCP服务器的实时运行状况
可用的MCP服务器
| 服务器 | ID | APIM产品 | 描述 |
|---|---|---|---|
| 🌿 三重打印MCP | terprint-mcp | prod-terprint-mcp | 大麻批次数据、萜烯概况、库存分析 |
| 🤖 AI SDO MCP | sdo-mcp | prod-sdo-mcp | CMDB、产品、治理、代理配置 |
| ☀️ 太阳能MCP | solar-mcp | prod-solar-mcp | 太阳能监测、EG4逆变器数据、电池分析 |
| 🔍 RepoLens MCP | repolens-mcp | prod-repolens-mcp | GitHub仓库分析、清单、CI/CD、PR差异 |
______________________________________________________________________
每个用户密钥管理
为什么是每个用户密钥?
以前,所有用户在每台服务器上共享相同的APIM订阅密钥。这违反了 秒-003 (按产品APIM订阅密钥),并且无法:
- 在不影响所有人的情况下撤销单个用户的访问权限
- 跟踪哪个用户进行了哪些API调用
- 在不干扰所有用户的情况下旋转受损密钥
现在,每个用户在每台服务器上都有自己唯一的APIM订阅,范围为正确的APIM产品。密钥是按需创建的,可以独立旋转或撤销。
建筑
graph TD
U[User signs in via Entra ID] --> P[MCP Portal
mcp.acidni.net]
P -->|"Generate Key"| APIM[Azure APIM
apim-terprint-dev]
APIM -->|"Creates subscription scoped to Product"| SUB["APIM Subscription
mcp-{oid}-{server}"]
P -->|Stores key record| COSMOS[(Cosmos DB
mcp-portal-dev)]
P -->|Logs audit event| AUDIT[(audit-log container)]
U2[User's MCP Client
Claude / VS Code] -->|"Per-user key in header"| APIM
APIM -->|Routes to correct backend| MCP1[Terprint MCP]
APIM --> MCP2[SDO MCP]
APIM --> MCP3[Solar MCP]
APIM --> MCP4[RepoLens MCP]关键生命周期
| 动作 | 发生了什么 | APIM效应 | 宇宙效应 |
|---|---|---|---|
| 生成 | 创建服务器产品范围内的新APIM订阅 | PUT /subscriptions/{sid} 随着 state: active | 在中更新关键记录 user-keys |
| 旋转 | 重新生成主键(旧密钥立即停止工作) | POST /subscriptions/{sid}/regeneratePrimaryKey | 更新 key_hint,增量 rotation_count |
| 撤销 | 暂停订阅(软删除,可以重新启用) | PATCH /subscriptions/{sid} 随着 state: suspended | 套装 state: revoked,记录 revoked_at |
订阅命名
APIM订阅使用确定性命名模式:
mcp-{first 12 chars of user OID}-{server-id}例子: mcp-a1b2c3d4e5f6-terprint-mcp
这确保了每个用户+服务器组合映射到一个订阅,并重新生成创建/更新,而不是复制。
数据流
- 用户登录: mcp.acidni.net 通过输入ID(MSAL)
- 仪表板显示所有4台MCP服务器的关键状态(绿色=活动,红色=无)
- 用户点击 生成密钥 在服务器上
- 门户调用APIM Management API以创建范围为服务器的APIM产品的订阅
- 通过APIM获取键值
listSecrets - 存储在Cosmos DB中的关键记录(提示+元数据)
user-keys容器 - 审计事件记录到Cosmos DB
audit-log容器 - 用户从服务器详细信息页面复制其个性化配置(Claude Desktop、VS Code或cURL)
- MCP客户端使用每个用户密钥发送请求
Ocp-Apim-Subscription-Key头球 - APIM验证密钥,根据产品到API的映射路由到正确的后端
共享密钥(仅健康检查)
门户仍然保存来自密钥库的共享APIM订阅密钥(apim-*-subscription-key).这些被使用 仅 用于服务器端健康检查代理(/api/health/{id}).它们永远不会暴露给用户或包含在导出的配置中。
______________________________________________________________________
快速开始
对于用户
- 访问 https://mcp.acidni.net
- 使用您的Microsoft帐户登录
- 在仪表板上,单击 生成密钥 在要使用的每个MCP服务器旁边
- 转到服务器详细信息页面并复制您的配置代码段
- 粘贴到MCP客户端配置文件中
配置Claude桌面
在门户上生成密钥后,转到每个服务器的详细信息页面。门户生成一个即贴即用的 claude_desktop_config.json 使用您的个人密钥的片段:
{
"mcpServers": {
"terprint-mcp": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-proxy@latest", "--header",
"Ocp-Apim-Subscription-Key: YOUR_PERSONAL_KEY_HERE",
"https://api.acidni.net/terprint-mcp/mcp"]
}
}
}每个服务器都有自己的唯一密钥。做 不 重用旧的共享密钥-从门户生成新的每个用户密钥。
配置VS代码
该门户还生成VS代码MCP设置。从服务器详细信息页面复制:
{
"mcp": {
"servers": {
"terprint-mcp": {
"type": "sse",
"url": "https://api.acidni.net/terprint-mcp/mcp",
"headers": {
"Ocp-Apim-Subscription-Key": "YOUR_PERSONAL_KEY_HERE"
}
}
}
}
}自动发现(适用于MCP客户端)
MCP客户端可以通过以下方式发现可用服务器:
GET https://mcp.acidni.net/.well-known/mcp______________________________________________________________________
API终点
门户网站和Web
| 端点 | 身份验证 | 描述 |
|---|---|---|
GET / | 否 | 登录页面 |
GET /dashboard | 是 | 每个服务器都有密钥状态的用户仪表板 |
GET /server/{id} | 是 | 服务器详细信息,包括密钥管理+配置片段 |
GET /.well-known/mcp | 无 | MCP发现清单 |
GET /health | 否 | 门户健康检查 |
数据API
| 端点 | 身份验证 | 描述 |
|---|---|---|
GET /api/servers | 可选 | 列出所有服务器(包括 has_key 每个用户) |
GET /api/configs/claude-desktop | 是 | 带每个用户密钥的完整Claude桌面配置 |
GET /api/configs/vscode | 是 | 具有每个用户密钥的完整VS代码配置 |
GET /api/health/{id} | 否 | 代理服务器健康检查(使用共享密钥) |
密钥管理API
| 端点 | 身份验证 | 描述 |
|---|---|---|
GET /api/keys | 是 | 列出当前用户的所有关键记录 |
POST /api/keys/{server_id} | 是 | 为服务器生成新的每个用户密钥 |
POST /api/keys/{server_id}/rotate | 是 | 旋转(重新生成)用户密钥 |
DELETE /api/keys/{server_id} | 是 | 撤销(挂起)用户密钥 |
______________________________________________________________________
基础设施
Azure资源
| 资源 | 类型 | 资源组 |
|---|---|---|
ca-mcp-portal | 容器应用程序 | rg-dev-acidni-shared |
apim-terprint-dev | API管理 | rg-terprint-apim-dev |
acidni-cosmos-dev | Cosmos数据库 | rg-acidni-shared-dev |
kv-terprint-dev | 密钥库 | rg-terprint-dev |
Cosmos数据库
| 财产 | 价值 |
|---|---|
账户 acidni-cosmos-dev | |
| 端点 | https://acidni-cosmos-dev.documents.azure.com:443/ |
| 数据库 | mcp-portal-dev |
集装箱: user-keys | 分区密钥: /user_oid |
集装箱: audit-log | 分区密钥: /user_oid |
APIM产品
每个MCP服务器都映射到一个APIM产品。每个用户的订阅仅限于这些产品,因此APIM会自动处理到正确后端API的路由。
| 服务器ID | APIM产品 |
|---|---|
terprint-mcp | prod-terprint-mcp |
sdo-mcp | prod-sdo-mcp |
solar-mcp | prod-solar-mcp |
repolens-mcp | prod-repolens-mcp |
所需的RBAC角色分配
容器应用程序的托管身份(ca-mcp-portal)需求:
| 资源 | 角色 | 目的 |
|---|---|---|
apim-terprint-dev | API Management Service Contributor | 创建/管理每个用户的订阅 |
acidni-cosmos-dev / mcp-portal-dev | Cosmos DB Built-in Data Contributor | 读/写关键记录和审计日志 |
kv-terprint-dev | Key Vault Secrets User | 读取共享密钥和身份验证密钥 |
______________________________________________________________________
发展
先决条件
- Python 3.12+
- Azure CLI(用于密钥库和托管身份访问)
本地设置
# Clone the repository
git clone https://github.com/Acidni-LLC/acidni-mcp-portal.git
cd acidni-mcp-portal
# Create virtual environment
python -m venv venv
venv/Scripts/Activate.ps1 # Windows
source venv/bin/activate # Linux/Mac
# Install dependencies
pip install -e ".[dev]"
# Create .env file
cat > .env << EOF
ENVIRONMENT=development
DEBUG=true
KEYVAULT_NAME=kv-terprint-dev
EOF
# Run locally
python -m src.main环境变量
| 变量 | 描述 | 默认值 |
|---|---|---|
ENVIRONMENT | 开发/生产 | |
DEBUG | 启用调试模式 | false |
KEYVAULT_NAME | Azure密钥保管库名称 | kv terprint dev |
SECRET_KEY | 会话加密密钥 | (从KV加载) |
密钥库秘密
| 秘密 | 目的 |
|---|---|
azure-tenant-id 输入租户ID。 | |
mcp-portal-client-id | 应用注册客户端ID |
mcp-portal-client-secret | 应用注册客户端密码 |
mcp-portal-session-secret | 会话加密密钥 |
apim-terprint-mcp-subscription-key | 共享密钥-仅用于健康检查代理 |
apim-sdo-mcp-subscription-key | 共享密钥-仅用于健康检查代理 |
apim-solar-mcp-subscription-key | 共享密钥-仅用于健康检查代理 |
apim-repolens-mcp-subscription-key | 共享密钥-仅用于健康检查代理 |
项目结构
src/
main.py # FastAPI app, lifespan init for Cosmos + APIM manager
config.py # Settings loaded from env + Key Vault
auth.py # Entra ID / MSAL authentication
registry.py # MCP server registry (static catalog)
routes/
__init__.py # Exports api_router, keys_router, web_router
api.py # Data API routes (servers, configs, health proxy)
keys.py # Key lifecycle API (generate, rotate, revoke)
web.py # HTML page routes (dashboard, server detail)
services/
key_manager.py # APIM Management REST API client
cosmos_client.py # Cosmos DB user-keys + audit-log client
templates/
dashboard.html # Dashboard with key status + HTMX key management
server_detail.html # Server detail with key display + config snippets
...部署
通过GitHub Actions部署到Azure容器应用程序:
- 容器应用:
ca-mcp-portal - 资源组:
rg-dev-acidni-shared - 自定义域名:
mcp.acidni.net - ACR图像:
cracidnidev.azurecr.io/acidni-mcp-portal
______________________________________________________________________
安全
- 用户访问没有共享密钥 -每个用户都获得唯一的APIM订阅密钥(SEC-ASR-003)
- APIM验证所有请求 -MCP服务器位于内部入口后面,只能通过APIM(SEC-ASR-001)访问
- 处处管理身份 -门户使用
DefaultAzureCredential适用于APIM、Cosmos和密钥库 - 审计日志 -每个键的创建/旋转/撤销都会与用户OID、电子邮件、时间戳一起记录到Cosmos DB中
- 撤销时软删除 -订阅被暂停,而不是被销毁,允许进行取证审查
- 仅在Cosmos中提供关键提示 -仅存储最后4个字符;APIM是完整键值的真实来源
许可证
专有-Acidni有限责任公司
