You've already forked agentic-coding-workflow
feat(mcp): domain types — Issuer / Scope / MCPToken + helpers
This commit is contained in:
@@ -0,0 +1,108 @@
|
|||||||
|
// Package mcp 实现 MCP(Model Context Protocol)Server 模块。
|
||||||
|
//
|
||||||
|
// 模块结构(spec §2.4 决策):单 package + 多文件,与 acp / chat 同模式。
|
||||||
|
// MCP 协议合规性由官方 github.com/modelcontextprotocol/go-sdk 承担;
|
||||||
|
// 本模块只负责:
|
||||||
|
// - mcp_tokens 表的 CRUD(admin 后台管理)
|
||||||
|
// - system token 在 ACP session 启动时签发、终止时撤销
|
||||||
|
// - 鉴权 middleware(bearer / ?token= → user_id + scope)
|
||||||
|
// - 速率限制
|
||||||
|
// - 工具/prompts/resources 的业务桥接(调 project / chat 现有 service)
|
||||||
|
//
|
||||||
|
// 三类核心抽象:
|
||||||
|
// - MCPToken:DB 行记录,token plaintext 仅签发时返回一次
|
||||||
|
// - Scope:JSONB 形态的工具白名单 + project_ids 限定
|
||||||
|
// - Issuer:'system' 或 'admin',决定生命期与是否绑定 ACP session
|
||||||
|
package mcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Issuer 是 mcp_tokens.issuer 枚举。
|
||||||
|
type Issuer string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IssuerSystem token 由 ACP supervisor 起 session 时自动签发,
|
||||||
|
// 必须绑定 acp_session_id,session 终止即撤销。
|
||||||
|
IssuerSystem Issuer = "system"
|
||||||
|
// IssuerAdmin token 由 admin 后台手动签发给团队成员(含 admin 自己)。
|
||||||
|
// 不绑定 ACP session,长期有效,admin 或持有者可主动撤销。
|
||||||
|
IssuerAdmin Issuer = "admin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsValid 判断 issuer 是否为合法枚举值。
|
||||||
|
func (i Issuer) IsValid() bool {
|
||||||
|
return i == IssuerSystem || i == IssuerAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope 是 mcp_tokens.scope JSONB 列的 Go 反射。
|
||||||
|
//
|
||||||
|
// 语义:
|
||||||
|
// - Tools 为 nil(JSON null/缺省)= 全工具允许;非 nil 但空切片 = 全部禁用
|
||||||
|
// - ProjectIDs 为 nil = 全 project 允许;非 nil 空切片 = 全部禁用
|
||||||
|
//
|
||||||
|
// JSON marshal 时 nil 字段用 omitempty 抹掉,与 spec §3.1 表头注释一致。
|
||||||
|
type Scope struct {
|
||||||
|
Tools []string `json:"tools,omitempty"`
|
||||||
|
ProjectIDs []uuid.UUID `json:"project_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowsTool 检查工具名是否在白名单。Tools 为 nil 时全允许。
|
||||||
|
func (s *Scope) AllowsTool(name string) bool {
|
||||||
|
if s == nil || s.Tools == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, t := range s.Tools {
|
||||||
|
if t == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowsProject 检查 project ID 是否在限定列表。ProjectIDs 为 nil 时全允许。
|
||||||
|
func (s *Scope) AllowsProject(id uuid.UUID) bool {
|
||||||
|
if s == nil || s.ProjectIDs == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, p := range s.ProjectIDs {
|
||||||
|
if p == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MCPToken 是 mcp_tokens 表的 Go 表示。
|
||||||
|
//
|
||||||
|
// TokenHash 是 SHA-256(plaintext);plaintext 仅在 IssueAdmin/IssueSystemToken
|
||||||
|
// 的返回值里出现一次,DB 与本结构都不持有 plaintext。
|
||||||
|
type MCPToken struct {
|
||||||
|
ID uuid.UUID
|
||||||
|
TokenHash []byte
|
||||||
|
UserID uuid.UUID
|
||||||
|
Name string
|
||||||
|
Issuer Issuer
|
||||||
|
Scope Scope
|
||||||
|
ACPSessionID *uuid.UUID
|
||||||
|
ExpiresAt *time.Time
|
||||||
|
LastUsedAt *time.Time
|
||||||
|
RevokedAt *time.Time
|
||||||
|
CreatedBy uuid.UUID
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActive 报告 token 当前是否可用(未撤销 + 未过期)。
|
||||||
|
// now 显式注入便于测试;生产调用方传 time.Now()。
|
||||||
|
func (t *MCPToken) IsActive(now time.Time) bool {
|
||||||
|
if t.RevokedAt != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.ExpiresAt != nil && !t.ExpiresAt.After(now) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user