SMCP代理
 ](https://github.com/ksysoev/smcp-proxy/actions/workflows/docker-publish.yml)   
具有OIDC身份验证的模型上下文协议(MCP)服务的安全反向代理。
什么是模型上下文协议(MCP)?
模型上下文协议(MCP)是一个开放协议,它规范了应用程序如何向大型语言模型(LLM)提供上下文。它的工作原理类似于“用于AI应用程序的USB-C端口”,在应用程序和AI模型之间创建一致的接口。
MCP遵循客户端-服务器架构,其中主机应用程序连接到多个服务器,在保持一致的数据处理实践和安全性的同时,实现了在不同AI提供商之间切换的灵活性。
有关更多信息,请访问 模型上下文协议.io.
概述
SMCP代理在模型上下文协议(MCP)服务之前提供了一个安全层,支持使用OIDC进行企业级身份验证和授权。MCP是一种协议,旨在以标准化的方式与大型语言模型(LLM)进行交互。
代理由两个主要部分组成:
- 代理服务器:验证来自客户端的OIDC令牌,并将经过身份验证的请求转发给MCP服务器
- 代理客户端:实现客户端凭据流以获取令牌,并充当本地未经身份验证的MCP服务
该项目的主要目标是:
- 使用OIDC身份验证提供对MCP服务的安全访问
- 支持企业环境中可扩展的MCP基础架构
- 为MCP服务启用集中身份验证和授权
- 简化客户端与经过身份验证的MCP服务的集成
建筑
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Application │ │ Proxy Client │ │ Proxy Server │ ┌───────────┐
│ (no auth) │──────│ (local) │──────│ (with auth) │──────│ MCP Server│
└───────────────┘ └───────────────┘ └───────────────┘ └───────────┘
│ ▲
│ │
│ │
▼ │
┌────────────┐ │
│ OIDC │───────────────┘
│ Provider │
└────────────┘特性
服务器端(代理服务器)
- 可配置的身份验证模式(无,OIDC)
- 启用OIDC时:
- 验证来自客户端的OIDC令牌 - 可配置的可信发卡机构和令牌声明验证
- 将请求转发到MCP服务器
- 支持多个MCP后端
- 后端服务器的多种传输类型(HTTP和stdio)
- 基于路径的路由,可选路径前缀剥离
- 具有stdio通信的本地MCP进程管理
- 用于发现可用模型的模型API
- 支持可扩展部署
- 健康检查端点
- 结构化日志记录和指标
客户端(代理客户端)
- 可配置的身份验证模式(无,OIDC)
- 启用OIDC时:
- 实现客户端凭据流以获取令牌 - 自动令牌刷新
- 充当本地MCP服务
- 将请求代理到服务器端组件
- 健康检查端点
- 结构化日志记录
安装
需求
- 达到1.24或更高
从源头构建
# Build the single executable that includes both server and client functionality
go build -o smcp-proxy ./cmd/smcpDocker镜像
SMCP Proxy也可以作为Docker镜像使用,可以从GitHub容器注册表中提取:
# Pull the latest image
docker pull ghcr.io/ksysoev/smcp-proxy:main
# Pull a specific version
docker pull ghcr.io/ksysoev/smcp-proxy:v1.0.0使用Docker运行
运行服务器:
docker run -p 8080:8080 -v $(pwd)/configs:/app/configs ghcr.io/ksysoev/smcp-proxy:main server --config=/app/configs/proxy-server.yml运行客户端:
docker run -p 8081:8081 ghcr.io/ksysoev/smcp-proxy:main client --server-url=http://your-server:8080Docker镜像是为AMD64和ARM64架构构建的。
配置
配置是通过YAML文件提供的。示例配置可在 configs 目录。
服务器配置
服务器配置在中指定 configs/proxy-server.yml:
server:
host: "0.0.0.0"
port: 8080
read_timeout: "30s"
write_timeout: "30s"
shutdown_timeout: "10s"
# Authentication configuration (default: none)
auth:
# Mode can be "none" or "oidc"
mode: "none"
mcp:
# Global timeout for all backends (can be overridden per backend)
timeout: "60s"
# Configure multiple MCP backends
backends:
# Sequential thinking MCP server
- id: "sequentialthinking"
name: "Sequential Thinking"
transport: "stdio"
path: "/v1/sequentialthinking"
strip_path: true
stdio:
command: "docker"
args: ["run", "--rm", "-i", "mcp/sequentialthinking"]
stdio_timeout: "60s"
# Memory MCP server
- id: "memory"
name: "Memory"
transport: "stdio"
path: "/v1/memory"
strip_path: true
stdio:
command: "docker"
args: ["run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory"]
stdio_timeout: "60s"
# OIDC settings (only used when auth.mode is "oidc")
oidc:
issuers:
- "https://your-identity-provider.com" # Replace with your actual OIDC issuer URL
audience: "your-api-audience" # Replace with your API audience
required_claims:
# Define required claims that must be present in the token
# For example:
# roles: "admin"
optional_claims:
# Define optional claims that if present must match specific values
# For example:
# scope: "read:data"
tls:
enabled: false
# cert_file: "/path/to/cert.pem"
# key_file: "/path/to/key.pem"
metrics:
enabled: true
path: "/metrics"客户端配置
代理客户端已被简化为使用命令行参数和环境变量,而不是配置文件。
命令行参数
Client flags:
--host string Host to bind the client to (default "127.0.0.1")
--port int Port to bind the client to (default 8081)
--read-timeout duration HTTP read timeout (default 30s)
--write-timeout duration HTTP write timeout (default 30s)
--shutdown-timeout duration Graceful shutdown timeout (default 10s)
Server flags:
--server-url string URL of the proxy server (required)
--server-timeout duration Timeout for requests to the server (default 60s)
Auth flags:
--auth-mode string Authentication mode (none, oidc) (default "none")
OIDC flags (only used when auth-mode is "oidc"):
--oidc-issuer string OIDC issuer URL
--oidc-client-id string OIDC client ID
--oidc-client-secret string OIDC client secret
--oidc-audience string OIDC audience
--oidc-scopes string OIDC scopes (comma-separated) (default "openid")
--oidc-cache-ttl duration OIDC token cache TTL (default 5m0s)
--oidc-token-ttl-delta duration OIDC token TTL delta (default 30s)
TLS flags:
--tls Enable TLS (default false)
--tls-cert string Path to TLS certificate file
--tls-key string Path to TLS key file
Metrics flags:
--metrics Enable metrics endpoint (default true)
--metrics-path string Metrics endpoint path (default "/metrics")
Logger flags:
-l, --log-level string Log level (debug, info, warn, error) (default "info")
-f, --log-format string Log format (text, json) (default "text")环境变量
所有命令行选项也可以使用带有前缀的环境变量进行设置 SMCP_CLIENT_例如:
# Required configuration
export SMCP_SERVER_URL="http://localhost:8080"
# Authentication mode (default is "none")
export SMCP_AUTH_MODE="none" # or "oidc"
# OIDC settings (only needed if SMCP_AUTH_MODE="oidc")
export SMCP_OIDC_ISSUER="https://your-identity-provider.com"
export SMCP_OIDC_CLIENT_ID="your-client-id"
export SMCP_OIDC_CLIENT_SECRET="your-client-secret"
# Optional configuration
export SMCP_CLIENT_HOST="127.0.0.1"
export SMCP_CLIENT_PORT=8081
export SMCP_OIDC_AUDIENCE="your-api-audience"
export SMCP_OIDC_SCOPES="openid,profile,email"服务器配置仍然使用YAML文件,并且可以使用带有前缀的环境变量进行覆盖 SMCP_PROXY_.
用法
启动服务器
禁用身份验证(默认)
# Run the server without authentication
./smcp-proxy server --config=configs/proxy-server.yml --log-level=debug使用OIDC身份验证
# Run the server with OIDC authentication
./smcp-proxy server --config=configs/proxy-server.yml --auth-mode=oidc --log-level=debug启动客户端
无身份验证(默认)
# Run the client without authentication
./smcp-proxy client --server-url="http://localhost:8080" --log-level=debug
# Or using environment variables
export SMCP_SERVER_URL="http://localhost:8080"
./smcp-proxy client --log-level=debug使用OIDC身份验证
# Run the client with OIDC authentication
./smcp-proxy client \
--auth-mode=oidc \
--server-url="http://localhost:8080" \
--oidc-issuer="https://your-identity-provider.com" \
--oidc-client-id="your-client-id" \
--oidc-client-secret="your-client-secret" \
--log-level=debug
# Or using environment variables
export SMCP_SERVER_URL="http://localhost:8080"
export SMCP_AUTH_MODE="oidc"
export SMCP_OIDC_ISSUER="https://your-identity-provider.com"
export SMCP_OIDC_CLIENT_ID="your-client-id"
export SMCP_OIDC_CLIENT_SECRET="your-client-secret"
./smcp-proxy client --log-level=debug两个组件都运行后:
- 应用程序可以连接到客户端组件(默认情况下为http://localhost:8081)
- 客户端组件将请求转发到服务器
- 如果启用了身份验证,客户端将向OIDC提供程序进行身份验证,服务器将验证令牌
- 服务器根据请求路径将请求转发到相应的MCP后端
多后端支持
代理服务器支持多种不同传输类型的MCP后端:
┌─────────────┐ ┌───────────────────────────────┐
│ Request to │ │ Proxy Server │
│ /v1/seq../ ├─────────────────►│ │──► Stdio Sequential Thinking Backend
└─────────────┘ │ │
│ │
┌─────────────┐ │ │
│ Request to │ │ OIDC Authentication + │
│ /v1/memory/ ├─────────────────►│ Path-Based Routing │──► Stdio Memory Backend
└─────────────┘ │ │
│ │
┌─────────────┐ │ │
│ Request to │ │ │
│ /other/path ├─────────────────►│ │──► 404 Not Found
└─────────────┘ └───────────────────────────────┘基于路径的路由
随着 strip_path 启用选项后,代理将在将请求转发到后端之前删除路径前缀:
- ……的请求
/v1/sequentialthinking/completions→ 转发到顺序思维后端作为/completions - ……的请求
/v1/memory/messages→ 作为转发到内存后端/messages - ……的请求
/some/other/path→ 返回404 Not Found(没有匹配的后端)
运输类型
代理支持两种类型的后端:
- HTTP后端 (
transport: "http"):
- 可通过HTTP访问远程MCP服务器 - 配置了URL和标准代理设置 - 例子: url: "http://mcp-server.example.com"
- Stdio后端 (
transport: "stdio"):
- 作为子进程运行的本地MCP服务器 - 通过标准输入/输出进行通信 - 适用于运行本地模型服务器 - 示例:
# Sequential thinking MCP server
stdio:
command: "docker"
args: ["run", "--rm", "-i", "mcp/sequentialthinking"]
# Memory MCP server
stdio:
command: "docker"
args: ["run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory"]API型号
代理提供 /api/models 按照Anthropic API模型格式返回有关所有配置的后端的信息的端点。这允许客户发现可用的模型及其功能。
发展
项目结构
.
├── cmd/ # Application entry points
│ └── smcp/ # Single executable directory
│ └── main.go # Main entry point
├── configs/ # Configuration files
│ ├── proxy-server.yml # Server configuration
│ └── proxy-client.yml # Client configuration example (not required)
├── internal/ # Private application code
│ ├── middleware/ # HTTP middleware
│ │ ├── logging.go # Request logging middleware
│ │ └── recovery.go # Panic recovery middleware
│ └── metrics/ # Metrics implementation (placeholder)
├── pkg/ # Public API
│ ├── auth/ # Authentication components
│ │ ├── validator.go # OIDC token validation
│ │ └── client.go # OIDC client credentials flow
│ ├── cmd/ # Command line interface
│ │ ├── root.go # Root command
│ │ ├── server.go # Server command
│ │ └── client.go # Client command
│ ├── config/ # Configuration handling
│ │ ├── server_config.go # Server configuration
│ │ └── client_config.go # Client configuration
│ └── proxy/ # Proxy implementation
│ ├── server.go # Server-side proxy
│ └── client.go # Client-side proxy
├── go.mod # Go module definition
└── README.md # This file设计原则
- 安全第一:所有身份验证和授权都是按照安全最佳实践实施的。
- 可扩展性:代理旨在处理多个MCP服务器和客户端。
- 可配置性:广泛的配置选项允许为不同的环境进行定制。
- 韧性:系统优雅地处理故障,并进行适当的错误处理和恢复。
- 可观测性:全面的日志记录和指标支持,以监控系统的行为。
持续集成
本项目使用GitHub Actions进行持续集成:
- 代码检查:跑步
golangci-lint和fieldalignment确保代码质量和性能 - 测试:运行具有竞争检测的单元测试并报告代码覆盖率
- 建筑:确保项目在每个推送和拉取请求上成功构建
- 码头工人:构建并验证Docker镜像以进行PR检查,并在合并到main和推送标签时将多平台镜像(AMD64/ARM64)发布到GitHub容器注册表
要在本地运行测试和linting:
# Run all tests
go test ./...
# Run tests with race detection and coverage
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
# Run linting
golangci-lint run
# Check struct field alignment
fieldalignment -test ./...