时间服务
一个简单的Go网络服务,通过HTTP提供标准REST API和模型上下文协议(MCP)服务器接口,以获取服务器当前时间。
特点/功能
- REST API(Representational State Transfer Application Programming Interface,表述性状态传递应用程序编程接口)简单的端点以获取当前服务器时间
- 命名位置基于SQLite的存储,用于自定义位置管理
- MCP 服务器带有时间相关工具的模型上下文协议服务器
- 身份验证与授权基于JWT声明的OAuth2/OIDC授权
- 结构化日志记录使用 slog 的 JSON 格式日志
- 优雅地关闭在终止信号时进行适当的清理
- 中间件栈日志记录、恢复、认证和CORS支持
- Prometheus 指标HTTP和MCP指标,包括认证和数据库指标
- 最小化的Docker镜像多阶段构建生成小于10MB的镜像
快速入门
重要服务器需要CORS配置才能启动。对于本地开发,请使用 ALLOW_CORS_WILDCARD_DEV=true 环境变量。
本地运行
HTTP服务器模式(用于远程访问)
# Download dependencies
make deps
# Run the server (development mode with wildcard CORS)
ALLOW_CORS_WILDCARD_DEV=true make run服务器将在8080端口(或指定的端口)上启动 PORT 环境变量)。
用于生产始终设置明确的允许来源:
ALLOWED_ORIGINS="https://example.com,https://app.example.com" make run标准输入输出模式(适用于Claude Code/MCP客户端)
# Run in stdio mode for MCP communication
# Note: stdio mode doesn't require CORS configuration
go run cmd/server/main.go --stdio此模式通过stdin/stdout使用JSON-RPC进行通信,这是Claude Code和其他本地MCP客户端所必需的。
构建二进制文件
make build
# Run with development CORS (local only)
ALLOW_CORS_WILDCARD_DEV=true ./bin/server
# Or with explicit origins (production)
ALLOWED_ORIGINS="https://example.com" ./bin/server使用 Docker 运行
# Build image (creates both v1.0.0 and latest tags)
make docker
# Run with versioned tag (recommended)
docker run -p 8080:8080 -e ALLOW_CORS_WILDCARD_DEV=true timeservice:v1.0.0
# Or run with latest tag (local dev only)
docker run -p 8080:8080 -e ALLOW_CORS_WILDCARD_DEV=true timeservice:latest制作说明: 始终使用带版本的标签(v1.0.0) 或图像摘要 (@sha256:...) 用于生产部署,以确保确定性、可重复的部署。该 latest 标签是可变的,仅应用于本地开发。
使用 Docker Compose 运行(加固版)
该项目包含一个经过加固的 docker-compose.yml 文件,遵循了安全最佳实践:
docker-compose up此配置包括:
- 只读根文件系统
- 已移除的功能(全部)
- 没有新的特权
- 资源限制
- 非root用户执行
- 用于可写目录的Tmpfs
API终端点
1. 根端点
获取服务信息:
curl http://localhost:8080/回复:
{
"service": "timeservice",
"version": "1.0.0",
"endpoints": {
"time": "GET /api/time",
"locations": "GET /api/locations",
"location_detail": "GET /api/locations/{name}",
"location_time": "GET /api/locations/{name}/time",
"mcp": "POST /mcp",
"health": "GET /health"
},
"mcp_info": "Supports both stdio mode (--stdio flag) and HTTP transport (POST /mcp)"
}2. 时间终点
获取当前服务器时间:
curl http://localhost:8080/api/time回答:
{
"current_time": "2025-10-17T15:30:45.123456Z",
"unix_time": 1729180245,
"timezone": "UTC",
"formatted": "2025-10-17T15:30:45Z"
}3. 健康终端(或健康终点)
检查服务状态:
curl http://localhost:8080/health回答:
{
"status": "healthy",
"time": "2025-10-17T15:30:45Z"
}MCP服务器端点
该服务包括一个模型上下文协议(MCP)服务器,为AI代理和其他客户端提供时间相关工具。
列出可用工具
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/list"
}'回答:
{
"result": {
"tools": [
{
"name": "get_current_time",
"description": "Get the current server time in various formats",
"inputSchema": {
"type": "object",
"properties": {
"format": {
"type": "string",
"description": "Time format (iso8601, unix, rfc3339, or custom Go format)",
"default": "iso8601"
},
"timezone": {
"type": "string",
"description": "IANA timezone (e.g., America/New_York, UTC)",
"default": "UTC"
}
}
}
},
{
"name": "add_time_offset",
"description": "Add a time offset to the current time",
"inputSchema": {
"type": "object",
"properties": {
"hours": {
"type": "number",
"description": "Hours to add (can be negative)",
"default": 0
},
"minutes": {
"type": "number",
"description": "Minutes to add (can be negative)",
"default": 0
},
"format": {
"type": "string",
"description": "Output format",
"default": "iso8601"
}
}
}
}
]
}
}调用工具:获取当前时间
获取当前时间的ISO8601格式表示(UTC):
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "get_current_time",
"arguments": {
"format": "iso8601",
"timezone": "UTC"
}
}
}'获取特定时区的当前时间:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "get_current_time",
"arguments": {
"format": "rfc3339",
"timezone": "America/New_York"
}
}
}'获取当前的Unix时间戳:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "get_current_time",
"arguments": {
"format": "unix"
}
}
}'调用工具:添加时间偏移
在当前时间上加3小时:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "add_time_offset",
"arguments": {
"hours": 3,
"minutes": 0,
"format": "rfc3339"
}
}
}'从当前时间减去30分钟:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "add_time_offset",
"arguments": {
"hours": 0,
"minutes": -30,
"format": "iso8601"
}
}
}'命名位置管理
该服务提供基于数据库的存储功能,用于管理带有相关IANA时区的命名位置。这样,您可以自定义位置名称(如“总部”、“东京办公室”、“数据中心西区”),并查询这些位置的当前时间,而无需记住时区字符串。
位置存储
- 数据库带有性能优化的 SQLite(WAL 模式,64MB 缓存)
- 模式(或架构)不区分大小写的地点名称,IANA时区验证
- 坚持不懈存储在(某处)的数据
data/timeservice.db(可通过DB_PATH) - 自动迁移模式在启动时自动创建和更新
位置API终端点
创建一个位置
创建一个新命名的位置(需要 locations:write (当启用认证时,需要权限):
curl -X POST http://localhost:8080/api/locations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"name": "headquarters",
"timezone": "America/New_York",
"description": "Company HQ in NYC"
}'回应:
{
"id": 1,
"name": "headquarters",
"timezone": "America/New_York",
"description": "Company HQ in NYC",
"created_at": "2025-10-19T10:00:00Z",
"updated_at": "2025-10-19T10:00:00Z"
}列出所有位置
获取所有已配置的位置:
curl http://localhost:8080/api/locations回答:
{
"locations": [
{
"id": 1,
"name": "headquarters",
"timezone": "America/New_York",
"description": "Company HQ in NYC",
"created_at": "2025-10-19T10:00:00Z",
"updated_at": "2025-10-19T10:00:00Z"
},
{
"id": 2,
"name": "tokyo-office",
"timezone": "Asia/Tokyo",
"description": "Tokyo branch office",
"created_at": "2025-10-19T10:05:00Z",
"updated_at": "2025-10-19T10:05:00Z"
}
]
}获取特定位置
检索指定位置的详细信息:
curl http://localhost:8080/api/locations/headquarters回答:
{
"id": 1,
"name": "headquarters",
"timezone": "America/New_York",
"description": "Company HQ in NYC",
"created_at": "2025-10-19T10:00:00Z",
"updated_at": "2025-10-19T10:00:00Z"
}获取某个地点的当前时间
获取指定地点的当前时间:
curl http://localhost:8080/api/locations/headquarters/time回答:
{
"location": "headquarters",
"timezone": "America/New_York",
"current_time": "2025-10-19T06:30:45.123456-04:00",
"unix_time": 1729180245,
"formatted": "2025-10-19T06:30:45-04:00"
}更新位置
更新现有位置的时区或描述(需要 locations:write (权限):
curl -X PUT http://localhost:8080/api/locations/headquarters \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"timezone": "America/Los_Angeles",
"description": "Company HQ relocated to LA"
}'删除一个位置
移除一个已命名的位置(需要 locations:write (权限):
curl -X DELETE http://localhost:8080/api/locations/headquarters \
-H "Authorization: Bearer $TOKEN"回复:
{
"message": "location deleted successfully"
}租赁MCP工具
MCP服务器提供了通过AI代理和其他MCP客户端来管理位置的工具。
添加位置工具
添加一个新命名位置:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "add_location",
"arguments": {
"name": "london-office",
"timezone": "Europe/London",
"description": "London branch office"
}
}
}'列出位置工具
列出所有已配置的位置:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "list_locations",
"arguments": {}
}
}'获取位置时间工具
获取指定地点的当前时间:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "get_location_time",
"arguments": {
"name": "london-office",
"format": "rfc3339"
}
}
}'更新位置工具
更新现有位置:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "update_location",
"arguments": {
"name": "london-office",
"timezone": "Europe/Paris",
"description": "Relocated to Paris"
}
}
}'移除位置工具
删除一个命名位置:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "remove_location",
"arguments": {
"name": "london-office"
}
}
}'位置数据库配置
配置SQLite数据库的位置和性能设置:
| 变量 | 默认值 | 描述 |
|---|---|---|
DB_PATH | data/timeservice.db | SQLite数据库文件的路径 |
DB_MAX_OPEN_CONNS | 25 | 最大打开数据库连接数 |
DB_MAX_IDLE_CONNS | 5 | 连接池中的最大空闲连接数 |
DB_CACHE_SIZE_KB | 64000 | 缓存大小(以KB为单位,内部转换为页面) |
DB_WAL_MODE | true | 启用预写日志以提高并发性 |
自定义数据库路径的示例:
DB_PATH=/var/lib/timeservice/locations.db \
ALLOWED_ORIGINS="https://example.com" \
./bin/server性能调优:
# For high-traffic workloads
DB_MAX_OPEN_CONNS=50 \
DB_CACHE_SIZE_KB=128000 \
./bin/server
# For low-memory environments
DB_MAX_OPEN_CONNS=10 \
DB_CACHE_SIZE_KB=32000 \
./bin/server数据库备份与恢复
该服务包含一个用于创建数据库一致性备份的备份脚本:
创建备份:
# Basic usage
./scripts/backup-db.sh data/timeservice.db backups/
# With custom retention (days)
RETENTION_DAYS=30 ./scripts/backup-db.sh data/timeservice.db backups/该脚本使用了SQLite的 VACUUM INTO 命令用于创建优化且一致的备份,并自动删除超过保留期限(默认:7天)的备份。
从备份中恢复:
# Stop the service
docker-compose down
# Replace database with backup
cp backups/timeservice_20251020_094227.db data/timeservice.db
# Restart service
docker-compose up -dDocker Compose 备份:
# Backup volume data
docker run --rm -v time-server_timeservice-data:/data -v $(pwd)/backups:/backup alpine \
tar czf /backup/timeservice-data-$(date +%Y%m%d).tar.gz -C /data .
# Restore volume data
docker run --rm -v time-server_timeservice-data:/data -v $(pwd)/backups:/backup alpine \
tar xzf /backup/timeservice-data-YYYYMMDD.tar.gz -C /dataKubernetes 备份:
# Copy database from pod
kubectl cp timeservice-0:/app/data/timeservice.db ./timeservice-backup.db
# Restore to pod
kubectl cp ./timeservice-backup.db timeservice-0:/app/data/timeservice.db
kubectl rollout restart statefulset timeservice关于在 Kubernetes 中进行自动化备份,请参阅 k8s/README.md 用于CronJob示例。
位置使用场景
团队协作:
# Add team member locations
curl -X POST .../api/locations -d '{"name":"alice-home","timezone":"America/New_York",...}'
curl -X POST .../api/locations -d '{"name":"bob-home","timezone":"Europe/London",...}'
# Check what time it is for Alice
curl .../api/locations/alice-home/time多区域基础设施:
# Define datacenter locations
curl -X POST .../api/locations -d '{"name":"us-east-dc","timezone":"America/New_York",...}'
curl -X POST .../api/locations -d '{"name":"eu-west-dc","timezone":"Europe/Dublin",...}'
curl -X POST .../api/locations -d '{"name":"ap-south-dc","timezone":"Asia/Singapore",...}'
# Check maintenance window times
curl .../api/locations/us-east-dc/time国际商务时间:
# Store office locations
curl -X POST .../api/locations -d '{"name":"corporate","timezone":"America/Chicago",...}'
curl -X POST .../api/locations -d '{"name":"apac-support","timezone":"Asia/Tokyo",...}'
# Quickly check if offices are open
for loc in corporate apac-support; do
echo "$loc: $(curl -s .../api/locations/$loc/time | jq -r .formatted)"
done配置
该服务可以通过环境变量进行配置。所有配置在启动时都会进行验证,如果提供了无效值,服务器将无法启动。
服务器配置
| 变量 | 默认值 | 描述 | 有效值 | ||||
|---|---|---|---|---|---|---|---|
| (无对应中文翻译) | (无对应中文翻译) | (无对应中文翻译) | (无对应中文翻译) | PORT | 8080 | 1-65535 | HTTP服务器端口 |
HOST |
| (所有接口)| 绑定地址 | 任何有效的IP地址或主机名 |
日志配置 | 变量 | 默认值 | 描述 | 有效值 | |----------|---------|-------------|--------------| LOG_LEVEL | 中文翻译 | 中文 | 中文 | 中文 | info | debug| info| 日志级别 | warn, warning, error ,
,
|CORS 配置
安全关键(或:至关重要的安全) 服务器启动需要CORS配置。 | 变量 | 默认值 | 描述 | 有效值 | ALLOWED_ORIGINS |----------|---------|-------------|--------------| | 中文翻译 | 中文 | 中文 | 中文 | | https://example.com,https://app.example.com | 必需的 ALLOW_CORS_WILDCARD_DEV | 允许的CORS来源(逗号分隔) | 显式来源如 true | * |
| (无) | 仅开发者使用的逃生口,允许使用通配符CORS |使能够;启用
- 原点(仅限开发使用)|安全注意事项
- :ALLOWED_ORIGINS 是必需的
- 如果未设置 ALLOWED_ORIGINS,服务器将无法启动,从而防止在生产环境中意外启用通配符 CORS。无通配符默认值
ALLOWED_ORIGINS="https://example.com,https://app.example.com"没有默认值。您必须明确配置允许的来源。 - 生产总是使用明确的来源(例如。,
ALLOW_CORS_WILDCARD_DEV=true)。*仅限开发 - 使用启用通配符CORS(
*) 用于本地开发。这是一种有意识的选择加入机制,可防止意外暴露生产环境。
为什么这很重要
ALLOWED_ORIGINS="https://example.com,https://app.example.com" ./bin/server通配符 CORS(
ALLOW_CORS_WILDCARD_DEV=true ./bin/server) 允许任何网站向您的API发送经过身份验证的请求,这可能会导致cookie、会话令牌和用户数据被盗。这是一个严重的安全漏洞。
$ ./bin/server
Configuration error: invalid configuration: ALLOWED_ORIGINS is required. Set explicit origins (e.g., ALLOWED_ORIGINS="https://example.com") or use ALLOW_CORS_WILDCARD_DEV=true for development ONLY. Wildcard CORS (*) is a security vulnerability in production示例 - 生产(正确):
示例 - 开发(谨慎使用): 10s未进行配置时会发生什么: 1m超时配置 500ms所有超时值均使用Go语言的持续时间格式(例如。,
, , )。 READ_TIMEOUT | 变量 | 默认值 | 描述 | 有效值 | 10s |----------|---------|-------------|--------------| | 中文翻译 | 中文 | 中文 | 中文 | WRITE_TIMEOUT | 10s | | 读取请求的最大持续时间 | 正数持续时间 | IDLE_TIMEOUT | 60s | | 写入响应的最大持续时间 | 正持续时间 | READ_HEADER_TIMEOUT | 5s | | 请求之间的最大空闲时间 | 正数时长 | SHUTDOWN_TIMEOUT | 10s |
| 读取请求头的最大持续时间 | 正数持续时间 |
| | | 优雅关闭的最大持续时间 | 正数持续时间 | MAX_HEADER_BYTES 资源限制 1048576 | 变量 | 默认值 | 描述 | 有效值 | 1-10485760 |----------|---------|-------------|--------------|
|
|(1MB) | 请求头的最大大小 | (1 字节 - 10MB) | 认证与授权配置 安全 该服务支持使用基于JWT的声明(角色、权限、范围)进行OAuth2/OIDC身份验证及授权。身份验证是
选择加入(或主动订阅) 为了向后兼容,但 强烈推荐 AUTH_ENABLED 用于生产。 false | 变量 | 默认值 | 描述 | 有效值 | true |----------|---------|-------------|--------------| false | | OIDC_ISSUER_URL | 启用认证(选择加入) | 或者 | https://auth.example.com | https://login.microsoftonline.com/{tenant-id}/v2.0| 如果启用认证,则为必填项 OIDC_AUDIENCE | OIDC 提供商 URL | 有效的 HTTPS URL(例如。, 或者 ) | timeservice | api://timeservice| 如果启用了认证,则必需 AUTH_PUBLIC_PATHS | JWT中的预期受众声明 | 您的服务标识符(例如。, /health,/,/metrics 或者 /health,/,/metrics) | | AUTH_REQUIRED_ROLE | time-reader| 逗号分隔的公共路径列表(无需认证) | 路径模式(例如。, )| AUTH_REQUIRED_PERMISSION | time:read| (无) | 所有受保护端点所需的角色 | 角色名称(例如。, ) | AUTH_REQUIRED_SCOPE | time:read| (无) | 所有受保护端点所需的权限 | 权限字符串(例如。, )| OIDC_SKIP_EXPIRY_CHECK | false | (无) | 所有受保护端点所需的OAuth2范围 | 范围字符串(例如。, ) || true | | OIDC_SKIP_CLIENT_ID_CHECK 危险 false 跳过令牌过期检查 | (仅开发者)|| true | | OIDC_SKIP_ISSUER_CHECK 危险 false 跳过观众验证 (仅开发者)|| true | | ALLOW_HTTP_OIDC_DEV 危险 true 跳过发行者验证 |
(仅开发者可见)||
- | (无) | 允许用于开发的HTTP(不安全)OIDC发行者 |(仅限开发者)|
AUTH_ENABLED=true - 安全注意事项:
- 生产建议在生产环境中始终启用身份验证,使用
- 与提供商无关的与任何符合OIDC标准的提供商(如Auth0、Okta、Azure Entra ID、Keycloak、AWS Cognito、Google等)兼容工作
- 基于声明的授权使用JWT声明(角色、权限、范围)实现细粒度访问控制
ALLOW_HTTP_OIDC_DEV=true无状态的
无需数据库查询;所有授权数据都在JWT中需要HTTPS
/healthOIDC 发行者在生产环境中必须使用 HTTPS(设置/(仅用于本地测试)/metrics公共路径解析
:
\- 用于容器健康检查和负载均衡器探测 - 提供服务发现信息(哪些端点存在) - Prometheus 抓取所需(监控工具通常不使用身份验证令牌)
- 关键:CORS 和认证中间件的顺序 当启用身份验证时,服务器
- 需要适当的中间件排序
Authorization与浏览器客户端正确配合工作: - CORS 中间件必须放在 Auth 中间件之前
- 在链条中
浏览器CORS预检请求(OPTIONS)不包含 cmd/server/main.go头球 如果在CORS之前运行Auth,预检请求会因缺少CORS头部而收到401错误 这会导致浏览器阻止对API的所有请求,使其无法使用
服务器已正确配置为CORS→Auth顺序。如果您修改了中间件链中的 ,
保持这个顺序
AUTH_ENABLED=true \
OIDC_ISSUER_URL="https://keycloak.example.com/realms/myrealm" \
OIDC_AUDIENCE="timeservice" \
AUTH_PUBLIC_PATHS="/health,/" \
AUTH_REQUIRED_ROLE="time-reader" \
ALLOWED_ORIGINS="https://app.example.com" \
./bin/server或者浏览器客户端将会出现故障。
AUTH_ENABLED=true \
OIDC_ISSUER_URL="https://your-tenant.auth0.com/" \
OIDC_AUDIENCE="https://timeservice.example.com" \
AUTH_PUBLIC_PATHS="/health,/,/metrics" \
AUTH_REQUIRED_SCOPE="time:read" \
ALLOWED_ORIGINS="https://app.example.com" \
./bin/server如需详细解释,请参阅
AUTH_ENABLED=true \
OIDC_ISSUER_URL="https://login.microsoftonline.com/{tenant-id}/v2.0" \
OIDC_AUDIENCE="api://timeservice" \
AUTH_PUBLIC_PATHS="/health,/" \
AUTH_REQUIRED_ROLE="time-reader" \
ALLOWED_ORIGINS="https://app.example.com" \
./bin/server在DESIGN.md中的中间件排序要求
AUTH_ENABLED=true \
OIDC_ISSUER_URL="http://localhost:8080/realms/test" \
OIDC_AUDIENCE="timeservice" \
ALLOW_HTTP_OIDC_DEV=true \
ALLOW_CORS_WILDCARD_DEV=true \
./bin/server示例 - 使用 Keycloak 的生产环境:
# Obtain JWT token from your OIDC provider first
TOKEN="eyJhbGc..."
# Make authenticated request
curl http://localhost:8080/api/time \
-H "Authorization: Bearer $TOKEN"示例 - 使用 Auth0 的生产环境:
$ ./bin/server
{"level":"INFO","msg":"authentication disabled - all endpoints are unprotected","recommendation":"enable auth in production with AUTH_ENABLED=true"}示例 - 使用 Azure Entra ID 进行生产
$ AUTH_ENABLED=true ./bin/server
Configuration error: invalid configuration: OIDC_ISSUER_URL is required when AUTH_ENABLED=true示例 - 开发(本地OIDC用于测试): 认证API请求示例: 当认证被禁用时(默认状态)会发生什么: 当启用认证但未配置必要设置时会发生什么:如需详细的认证设置说明、提供商示例以及安全最佳实践,请参阅
docs/SECURITY.md 翻译为中文是:docs/安全指南.md(或根据具体语境,也可译为“docs/安全文档.md”)
并且
PORT=8080 \
LOG_LEVEL=info \
ALLOWED_ORIGINS="https://example.com,https://app.example.com" \
READ_TIMEOUT=15s \
WRITE_TIMEOUT=15s \
make runADR 0005
PORT=3000 \
LOG_LEVEL=debug \
ALLOW_CORS_WILDCARD_DEV=true \
make run。
PORT=8080 \
ALLOWED_ORIGINS="https://api.example.com,https://app.example.com" \
READ_TIMEOUT=5s \
WRITE_TIMEOUT=5s \
IDLE_TIMEOUT=30s \
MAX_HEADER_BYTES=524288 \
make run配置示例
environment:
- PORT=8080
- LOG_LEVEL=info
- ALLOWED_ORIGINS=https://example.com,https://app.example.com
- READ_TIMEOUT=15s
- WRITE_TIMEOUT=15s基本生产配置:
带调试日志记录的开发配置:
# Missing ALLOWED_ORIGINS example
$ ./bin/server
Configuration error: invalid configuration: ALLOWED_ORIGINS is required. Set explicit origins (e.g., ALLOWED_ORIGINS="https://example.com") or use ALLOW_CORS_WILDCARD_DEV=true for development ONLY. Wildcard CORS (*) is a security vulnerability in production
# Invalid port example
$ PORT=999999 ALLOWED_ORIGINS="https://example.com" ./bin/server
Configuration error: invalid configuration: invalid PORT 999999: must be between 1 and 65535
# Invalid timeout example
$ READ_TIMEOUT=-5s ALLOWED_ORIGINS="https://example.com" ./bin/server
Configuration error: invalid configuration: READ_TIMEOUT must be positive, got -5s高性能配置:
{
"time": "2025-10-18T09:22:14Z",
"level": "INFO",
"msg": "configuration loaded",
"port": "8080",
"log_level": "INFO",
"allowed_origins": ["https://example.com","https://app.example.com"],
"read_timeout": 10000000000,
"write_timeout": 10000000000,
"idle_timeout": 60000000000
}Docker Compose 配置: ALLOW_CORS_WILDCARD_DEV=true配置验证
{
"time": "2025-10-18T09:22:14Z",
"level": "WARN",
"msg": "wildcard CORS (*) is enabled - this is INSECURE for production",
"recommendation": "set explicit origins in ALLOWED_ORIGINS",
"dev_only": "use ALLOW_CORS_WILDCARD_DEV=true only in development"
}服务器在启动时验证所有配置,如果发现任何值无效,则会显示错误信息并退出:
在启动时(INFO 级别)会记录配置值,以便调试部署问题:
timeservice/
├── cmd/ # Command-line applications
│ ├── server/ # Main server application
│ │ └── main.go
│ └── healthcheck/ # Healthcheck utility
│ └── main.go
├── internal/ # Private application code
│ ├── handler/ # HTTP handlers
│ ├── mcpserver/ # MCP server implementation (using mcp-go SDK)
│ ├── middleware/ # HTTP middleware (CORS, logging, metrics, recovery)
│ └── testutil/ # Testing utilities
├── pkg/ # Public packages
│ ├── config/ # Configuration management
│ ├── metrics/ # Prometheus metrics
│ ├── model/ # Data models
│ └── version/ # Version information
├── k8s/ # Kubernetes deployment manifests
│ ├── deployment.yaml # K8s deployment with ServiceMonitor
│ └── prometheus.yml # Sample Prometheus configuration
├── docs/ # Documentation
│ └── TESTING.md # Testing guide
├── bin/ # Compiled binaries (gitignored)
│ ├── server # Main server binary
│ └── healthcheck # Healthcheck binary
├── run-mcp.sh # Helper script to run in stdio mode
├── Makefile # Build commands
├── Dockerfile # Multi-stage container image
├── docker-compose.yml # Docker Compose configuration
└── README.md如果检测到通配符CORS(通过
make help # Show available commands
make build # Build binary
make run # Run server
make test # Run tests
make fmt # Format code
make lint # Lint code
make clean # Remove build artifacts
make deps # Download dependencies
make docker # Build Docker image), 将记录一条警告:
发展
项目结构
可用的Make命令 .git/hooks/pre-commit 预提交钩子(或:提交前钩子)
- 该项目包含了预提交钩子,用于强制执行代码质量标准,并防止提交二进制文件或覆盖率文件。
.exe传统的 Git 钩子.dll一个预提交钩子(pre-commit hook)会自动安装在.so阻止提交:.dylib二进制文件( - ,
bin/, - ,
.test) - 在(指定位置)构建工件
.out目录.coverprofile测试二进制文件(
)
- 覆盖文件(
- ,
- )
该钩子(hook)会在每次提交时自动运行。如果它检测到被禁止的文件,它将:
阻止提交 显示匹配到禁止模式的文件提供解决问题的说明 .pre-commit-config.yaml 现代预提交框架(可选)
对于使用该(工具/系统/服务)的团队来说
# Install pre-commit (if not already installed)
pip install pre-commit
# Install the git hooks
pre-commit install
# Run hooks manually on all files
pre-commit run --all-files预提交框架
- 一
- 提供了额外的检查:
- 设置:
- 包含的支票:
go fmt文件大小限制(最大500KB) - 合并冲突检测
go vetYAML语法验证 - Go 语言格式化(
- )
- 进行背景调查(或审查)
- )
- Go 导入组织(或“Go 导入包组织”)
Go mod tidy(Go模块整理)
进行构建验证 .gitignore 运行Go测试
- 二进制文件和覆盖率文件的防护
bin/.gitignore(Git忽略文件)*.exe这个(或“该”) - 文件防止意外添加:
*.test构建产物( - ,
*.out等coverage.html测试二进制文件( - )
.idea/覆盖率文件(.vscode/, - )
.env*IDE 文件( - ,
.DS_Store) - 环境文件(
tmp/)*.tmp操作系统文件(
)
临时文件(
,
- )所有开发人员应确保其本地构建过程遵循这些忽略规则。
- 建筑这项服务遵循了Go语言中惯用的网络服务模式:
log/slog关注点分离(或译为:职责分离) - 处理程序 → 服务 → 存储层(此例中简化)结构化日志记录
- 使用用于结构化、JSON格式的日志
- 中间件链用于处理横切关注点的可组合中间件
- 优雅关闭在接收到SIGINT/SIGTERM信号时进行适当的清理
上下文传播
请求上下文贯穿所有层级
最小化依赖主要依赖于Go标准库 ~/.config/Claude/claude_desktop_config.json 与Claude Desktop一起使用要在Claude Desktop中使用此MCP服务器,请将以下配置添加到您的Claude Desktop MCP设置文件中: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"timeservice": {
"command": "/full/path/to/time-server/bin/server",
"args": ["--stdio"],
"description": "Time service providing current time and time offset calculations"
}
}
}macOS/Linux(操作系统) /full/path/to/time-server :
Windows
- :
make build - 替换
- 使用此项目目录的实际绝对路径。
添加配置后:
构建服务器:
重启Claude桌面版
get_current_time时区服务工具将对克劳德(Claude)可用
- 你可以通过询问克劳德来验证它是否正常工作:“现在东京是什么时间?” format 可用的MCP工具 timezone 时间工具:
add_time_offset- 获取当前服务器时间,支持多种格式和时区
- 参数: hours (iso8601, unix, unixmilli, rfc3339), minutes (IANA时区名称) format - 为当前时间添加小时/分钟偏移量
参数:
add_location(数字),
- (数字), name (输出格式) timezone 位置管理工具: description - 添加一个带有时区的命名位置
list_locations参数:
- (字符串),
get_location_time(IANA时区),
- (字符串,可选) name - 列出所有已配置的位置 format 参数:无
update_location- 获取指定地点的当前时间
- 参数: name (字符串), timezone (输出格式,可选) description - 更新现有位置
remove_location参数:
- (字符串), name (IANA时区,可选),
(字符串,可选)
\- 删除指定位置 参数: (字符串)
- MCP协议 模型上下文协议(MCP)是一种允许人工智能模型与工具和资源进行交互的协议。此服务通过(某种技术或框架)实现了一个MCP服务器
- mcp-go SDK 翻译为中文是:“mcp-go 软件开发工具包(SDK)” 以两种模式:
标准输入输出模式(Stdio mode)
tools/list(适用于Claude Desktop和本地MCP客户端):通过stdin/stdout的JSON-RPCtools/callHTTP模式
(用于远程访问):使用StreamableHTTPServer通过HTTP POST进行JSON-RPC通信
MCP 方法
{
"result": { ... }
}列出所有可用的工具
{
"error": {
"code": 400,
"message": "error description"
}
}调用具有参数的特定工具
MCP响应格式
make test成功响应:
make test-race错误响应:
make test-coverage测试
make test-coverage-html
# Open coverage.html in your browser运行测试套件:
使用竞态检测器运行测试:
生成覆盖率报告:
生成HTML覆盖率报告:.github/workflows/ci.yml持续集成/持续交付流水线(CI/CD Pipeline)
这个项目包含一个使用GitHub Actions构建的全面CI/CD(持续集成/持续交付)流水线,该流水线在每次推送和拉取请求时都会运行。
- GitHub Actions 工作流CI流水线(
- 包括:测试任务
go fmt - 多版本测试针对 Go 1.22、1.23 和 1.24 的测试
go vet代码格式化 - 确保代码格式化为静态分析
- 运行捕捉常见错误
- 单元测试执行所有测试,并输出详细信息
- 竞态条件检测启用竞态检测器运行测试
覆盖率报告
- 生成并上传覆盖率报告Codecov 集成
- 可选地上传到Codecov以跟踪随时间变化的覆盖率“Lint Job”可以翻译为“代码检查任务”或“静态代码分析任务”,具体取决于上下文和所使用的工具或方法。在这里,“Lint”通常指的是对代码进行静态检查的过程,用于发现潜在的错误、不符合编码规范的地方等,而“Job”则指的是这项任务或工作。因此,“Lint Job”可以理解为对代码进行静态检查的工作或任务
- golangci-lint(中文可译为“Go语言代码检查工具”或保持原名,因其是一个专有名词)运行带有多个静态代码分析工具的全面代码检查
超时
- 5分钟的代码静态检查超时并行执行
- 与测试并行运行构建任务
- 二进制编译构建服务器二进制文件
工件上传
- 将二进制文件上传为 GitHub 艺术品(保留7天)尺寸报告
- 报告二进制文件大小Docker 作业
- 镜像构建使用 BuildKit 构建 Docker 镜像
- 缓存优化使用GitHub Actions缓存以加快构建速度
图像测试
- 验证构建的镜像尺寸报告
- 报告最终图像尺寸保安工作
- Gosec 扫描器注重安全的Go语言检查工具
Trivy 扫描器
依赖项和代码漏洞扫描器
make ci-localSARIF 上传
make deps将安全发现结果上传到GitHub的安全选项卡make fmt本地CI(持续集成)模拟make vet在推送之前,先在本地运行所有CI检查:make lint这运行着:make test-race- 下载并验证依赖项make test-coverage- 格式代码
- 运行 go vet
\- 运行 golangci-lint.golangci.yml- 使用竞态检测器运行测试
- - 生成覆盖率报告代码检查配置
- 该项目使用了golangci-lint,并采用了全面的配置()其中包括:
- 错误检查errcheck,gosec
- 代码质量gosimple,staticcheck,unused
- 风格gofmt,goimports,revive
- 演出带有性能检查的gocritic
安全
# Linux/macOS
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
# Or using Go
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest带有安全检查功能的gosec
make lint最佳实践
bodyclose(关闭体),nilerr(空错误),unconvert(无法转换)
- 安装 golangci-lint:
- 在本地运行代码检查:
- 覆盖率工件(或覆盖率相关制品)
- 覆盖率报告会自动:
为每个测试的Go版本生成
作为GitHub Actions工件上传(保留30天)
[](https://github.com/yourorg/timeservice/actions)可在“操作”选项卡中下载
可选择上传到Codecov以追踪趋势
CI徽章
- 在你的 README 文件中添加 CI 状态徽章(更新仓库 URL):
- 容器安全加固 golang:1.24-alpine Docker镜像已按照安全最佳实践进行了加固: alpine:3.20 - Dockerfile 安全特性
- 固定基础镜像
- 使用特定版本: - 并且 - 确保构建过程可重复,并防止供应链攻击
- 最小攻击面
- 多阶段构建将最终镜像大小减小至约16MB appuser 仅包含必要的运行时依赖(ca-certificates,tzdata) appgroup 最终镜像中不含shell或不必要的二进制文件 - 非管理员用户 - 创建专用用户
- (用户ID 10001) 和组
- (GID 10001) apk add --no-cache tzdata 所有进程默认以非root用户身份运行 - 非root用户拥有的应用程序文件 /usr/share/zoneinfo 时区数据 - 通过……安装
- 在构建器中
- -trimpath复制自 - -w -s而不是使用 Go 内置的 zoneinfo - -extldflags "-static"支持所有IANA时区,无需在二进制中嵌入 - go mod verify构建安全标志
- 从二进制文件中移除绝对路径
- 去除调试信息 - 生成静态二进制文件(无动态依赖)
确保依赖项未被篡改
健康检查 docker-compose.yml 内置的 Docker HEALTHCHECK 指令 k8s/deployment.yaml 验证容器是否正常工作
运行时安全(Docker/Kubernetes)
# Read-only root filesystem
read_only: true
# Drop all capabilities
cap_drop: - ALL
# Prevent privilege escalation
security_opt:
- no-new-privileges:true
# Resource limits
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M所包含的
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault并且
展示运行时加固:
- Docker Compose 功能: Kubernetes 安全上下文:
- 容器镜像扫描 - CI流水线包含三个扫描工具: - Trivy
- (Aqua Security) 可以翻译为“(Aqua 安全)”。不过,通常在中文语境中,我们可能会更直接地翻译为“(Aqua 安全公司)”或者根据具体上下文调整为更贴切的表述,但基本保留了原名中的“Aqua”和“Security”两部分。如果“Aqua Security”是一个特定公司或产品的名称,那么直接保留原名也是可以的,只是可能需要在上下文中稍作解释或说明 扫描操作系统和应用程序漏洞
- 检查配置错误 - 结果已上传至GitHub安全选项卡 - Grype(可能指某种疾病或病毒的名称,具体需根据上下文确定,此处直译为“格雷普”)
- (Anchore)
- 多源漏洞数据库 - 从不同来源捕获CVE(可利用漏洞) - 用于GitHub集成的SARIF格式
Docker Scout(可译为“Docker侦察兵”或根据具体语境简化为“Docker侦察工具”)
- 官方Docker漏洞扫描器
- 与Docker Hub的CVE数据库集成
- 提供补救建议
所有扫描结果均可在以下位置查看:
GitHub Actions 工作流日志
docker run -d \
--name timeservice \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
--tmpfs /tmp:noexec,nosuid,size=10M \
-p 8080:8080 \
-e ALLOW_CORS_WILDCARD_DEV=true \
timeservice:v1.0.0GitHub 安全 → 代码扫描警报
docker-compose up -d可下载的SARIF工件
# Build and tag image for production
docker build -t timeservice:v1.0.0 .
# Push to your container registry (update with your registry)
# docker tag timeservice:v1.0.0 your-registry.com/timeservice:v1.0.0
# docker push your-registry.com/timeservice:v1.0.0
# Deploy to Kubernetes
kubectl apply -f k8s/deployment.yaml在全面安全模式下运行 Docker 运行: image: timeservice:v1.0.0 Docker Compose: k8s/deployment.yaml Kubernetes:(通常不直接翻译,保持原名,若需解释性翻译可为“Kubernetes:一个容器编排平台”)
注:
Kubernetes 部署使用
docker run --rm timeservice:v1.0.0 id
# Expected: uid=10001(appuser) gid=10001(appgroup)用于确定性部署。更新
docker scout cves timeservice:v1.0.0
# Or
trivy image timeservice:v1.0.0如果要部署到真实集群,请提供您的注册表URL和凭据。
安全验证
验证容器是否以非root用户身份运行:
检查镜像漏洞: /metrics Prometheus 可观测性
curl http://localhost:8080/metrics此服务提供了Prometheus指标,以实现全面的可观测性和监控。
指标端点
这个(或“该”) 端点暴露了Prometheus格式的指标: 可用指标 timeservice_http_requests_total HTTP 指标 method| 指标 | 类型 | 标签 | 描述 | path|--------|------|--------|-------------| status | 项目 | 类型 | 数量 | 描述 | | timeservice_http_request_duration_seconds | 计数器 | method, path , | HTTP请求的总数 | timeservice_http_request_size_bytes | method| 直方图 | path , | HTTP请求持续时间(秒) | timeservice_http_response_size_bytes | method| 直方图 | path , | HTTP请求大小(以字节为单位) | timeservice_http_requests_in_flight |
| 直方图 |
, | HTTP响应大小(以字节为单位) | | timeservice_mcp_tool_calls_total | 指标 | - | 当前正在处理的HTTP请求数 | toolMCP工具指标 status | 指标 | 类型 | 标签 | 描述 | |--------|------|--------|-------------| timeservice_mcp_tool_call_duration_seconds |(无) |(无)|(无) |(无) | tool | | 计数器 | timeservice_mcp_tool_calls_in_flight ,
| MCP工具调用的总次数 |
| | 直方图 | |MCP工具调用持续时间(秒)| timeservice_build_info | version| 指标 | - | 当前正在处理的MCP工具调用数量 | go_version 应用指标
| 指标 | 类型 | 标签 | 描述 |
|--------|------|--------|-------------|
go_goroutines|(无对应中文)|(无对应中文)|(无对应中文)|(无对应中文)|go_memstats_*|go_gc_*| 表盘/刻度盘 |process_*,
| 构建信息(始终为1) |
标准Go指标
该服务还公开了标准的Go运行时指标: docker-compose.yml - 协程(goroutine)的数量
labels:
- "prometheus.scrape=true"
- "prometheus.port=8080"
- "prometheus.path=/metrics"- 内存统计
\- 垃圾回收统计
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"\- 进程统计信息(CPU、内存、文件描述符) ServiceMonitor Prometheus 配置
kubectl apply -f k8s/deployment.yamlDocker Compose
这个(或:那个)k8s/prometheus.yml包含用于 Prometheus 服务发现的标签:
scrape_configs:
- job_name: 'timeservice'
static_configs:
- targets: ['localhost:8080']
metrics_path: '/metrics'
scrape_interval: 30sKubernetes(通常简称为K8s)
Kubernetes 部署中包含了用于自动 Prometheus 抓取的 Pod 注解:
A.
rate(timeservice_http_requests_total[5m])同时也为 Prometheus Operator 提供了资源:
histogram_quantile(0.95, rate(timeservice_http_request_duration_seconds_bucket[5m]))独立部署的Prometheus
rate(timeservice_http_requests_total{status=~"5.."}[5m])
/ rate(timeservice_http_requests_total[5m])示例Prometheus配置(
rate(timeservice_mcp_tool_calls_total{status="success"}[5m])
/ rate(timeservice_mcp_tool_calls_total[5m])):
timeservice_http_requests_in_flightGrafana 仪表板
- 示例查询
- 请求速率(请求/秒):
- 请求持续时间(p95):
- 错误率: - MCP工具成功率: - 飞行中请求:
创建仪表板
将指标端点导入到Grafana数据源中
使用上述查询创建面板
为以下设置提醒:
- alert: HighErrorRate
expr: |
rate(timeservice_http_requests_total{status=~"5.."}[5m])
/ rate(timeservice_http_requests_total[5m]) > 0.05
for: 5m
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"高错误率(> 5%)
- alert: HighLatency
expr: |
histogram_quantile(0.95,
rate(timeservice_http_request_duration_seconds_bucket[5m])
) > 1.0
for: 5m
annotations:
summary: "High latency detected"
description: "P95 latency is {{ $value }}s"高延迟(p95 > 1秒)
- alert: ServiceDown
expr: up{job="timeservice"} == 0
for: 1m
annotations:
summary: "Timeservice is down"
description: "Service has been down for more than 1 minute"服务宕机(未抓取指标)
监控最佳实践
groups:
- name: timeservice
interval: 30s
rules:
- record: timeservice:http_requests:rate5m
expr: rate(timeservice_http_requests_total[5m])
- record: timeservice:http_request_duration:p95
expr: histogram_quantile(0.95, rate(timeservice_http_request_duration_seconds_bucket[5m]))
- record: timeservice:http_error_rate:rate5m
expr: |
rate(timeservice_http_requests_total{status=~"5.."}[5m])
/ rate(timeservice_http_requests_total[5m])警报
推荐提醒:
# Start server
ALLOWED_ORIGINS="*" ./bin/server
# Generate requests
for i in {1..100}; do
curl -s http://localhost:8080/health > /dev/null
curl -s http://localhost:8080/api/time > /dev/null
done
# View metrics
curl http://localhost:8080/metrics | grep timeservice高错误率:
高延迟:
- 服务宕机:录制规则
- 预计算常见查询:测试指标
- 生成测试流量:指标架构
- 指标的实现遵循了Prometheus的最佳实践:自动仪器化
timeserviceHTTP 中间件自动追踪所有请求 - 工具级追踪MCP工具调用被封装在指标收集之中
基数控制
标签经过精心选择,以防止指标爆炸(或指标数量激增) docs/ 命名空间
所有指标均使用
- 命名空间以避免冲突 标准桶
- 直方图使用Prometheus默认的桶以实现广泛覆盖 文档
- 全面的文档资料可在此查阅 目录:
- 核心文档 VERSION_MANAGEMENT.md 翻译为中文是:版本管理.md
- - 版本管理实践与验证 TESTING.md 翻译为中文是:“测试说明文件.md” 或者 “测试文档.md”(具体翻译可能根据上下文有所调整,但“TESTING”通常指的是与测试相关的说明或文档,“.md”是Markdown格式的文件扩展名)
- - 测试策略、覆盖率总结及测试组织 测试分析文档(或:测试分析报告).md
- - 详细的覆盖率分析和测试指标 SECURITY.md 翻译为中文是:“安全指南/声明文件”或“安全说明文件”。不过,具体翻译可能根据上下文有所调整,以更贴合文件的实际内容和用途。在这里,“SECURITY.md”通常是一个用于说明项目或软件安全方面的注意事项、漏洞报告流程、安全最佳实践等内容的Markdown格式文件
- 安全实践、认证和威胁模型
DEVSECOPS.md 翻译为中文是:“开发安全运维(DevSecOps).md” 或者更简洁地表述为:“DevSecOps 文档.md”。这里,“DEVSECOPS”代表“Development Security Operations”的缩写,意为开发安全运维,是一种将安全性融入到开发和运维流程中的实践方法。而“.md”是Markdown文件格式的扩展名 docs/adr/ - DevSecOps实践、安全控制和合规性
- DESIGN.md 翻译为中文是:“设计说明文件”或“设计文档” - 系统架构和设计决策
- 中间件排序要求 - 关键的CORS/认证顺序说明
- 架构决策记录(ADRs) 该
- 目录包含详细的架构决策: ADR 0001
- - 采用MCP-Go SDK ADR 0002
- - SQLite 数据库选择 ADR 0003
- - Prometheus指标策略 ADR 0004
- 使用 slog 进行结构化日志记录
ADR 0005 docs/implementation-plans/ - OAuth2/OIDC 授权
ADR 0006
\- MCP HTTP传输实现
