go contextforge
](https://golang.org/) 
Go SDK IBM ContextForge MCP网关 -一个功能丰富的网关、代理和MCP注册表,用于联合MCP和REST服务。
目录
- 兼容性
- 客户端配置 - 指针助手和标签 - 管理工具 - 管理资源 - 管理网关 - 管理服务器 - 管理提示 - 管理代理 - 管理团队 - 分页 - 错误处理
- 工具服务 - 资源服务 - 网关服务 - 服务器服务 - 提示服务 - 代理服务 - 团队服务
概述
ContextForge是人工智能客户端的统一端点,整合了发现、身份验证、速率限制、可观察性和虚拟服务器管理。它是一个完全兼容的MCP服务器,支持具有Redis支持的联盟的多集群Kubernetes环境。
此Go SDK为ContextForge API提供了一个惯用接口,允许您:
- 管理工具 具有创建、更新、删除和切换操作
- 管理资源 具有基于URI的访问和模板支持
- 管理网关 用于MCP服务器联合和代理
- 管理服务器 具有CRUD操作和工具、资源和提示的关联端点
- 管理提示 具有基于模板的AI交互和论证支持
- 管理代理 具有代理到代理(A2A)协议支持、调用和性能跟踪
- 处理分页 使用基于光标或基于偏移(跳过/限制)的导航
- 跟踪速率限制 并优雅地处理API错误
- 验证 使用承载令牌(JWT)身份验证
MCP(模型上下文协议)
模型上下文协议(MCP)是一种开放协议,规范了人工智能系统访问外部数据源、工具和资源的方式。MCP使AI模型能够通过统一的接口与数据库、API、文件系统和其他服务安全地交互,从而更容易构建上下文感知的AI应用程序。
ContextForge将MCP协议实现为兼容的MCP服务器和联邦网关。它将多个MCP服务器和REST服务整合到一个端点中,提供集中身份验证、速率限制、可观察性和资源管理。这使人工智能客户端能够通过单个统一的API发现和访问来自多个来源的工具、资源和提示。
有关MCP协议的更多信息,请参阅 官方文件.
A2A协议
A2A(代理到代理)协议是一个开放标准,它实现了独立AI代理系统之间的通信和互操作性。虽然MCP专注于将AI模型与工具和资源连接起来,但A2A使代理能够发现、调用和与其他代理协作,而不管其底层框架或实现如何。
通过ContextForge,代理使用代理卡(JSON格式的功能描述)宣传他们的功能,允许其他代理发现并调用特定任务的最佳代理。A2A代理可以接收带有参数的调用,返回结构化响应,并跨交互跟踪性能指标。
SDK提供了对A2A代理的全面管理,包括创建、配置、端点注册、调用和性能指标跟踪。这使得构建多代理系统成为可能,在这些系统中,专门的代理可以通过标准化的协议进行协作。
兼容性
此SDK经过测试 ContextForge v1.0.0-RC1 (PyPI: mcp-contextforge-gateway==1.0.0rc1).
安装
go get github.com/leefowlercu/go-contextforge要求: 转到1.25.3或更高版本
快速开始
package main
import (
"context"
"fmt"
"log"
"github.com/leefowlercu/go-contextforge/contextforge"
)
func main() {
// Create a client with address and bearer token
client, err := contextforge.NewClient(nil, "http://localhost:8000/", "your-jwt-token")
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// List tools
tools, _, err := client.Tools.List(ctx, &contextforge.ToolListOptions{
ListOptions: contextforge.ListOptions{Limit: 10},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d tools:\n", len(tools))
for _, tool := range tools {
desc := contextforge.StringValue(tool.Description) // Safe nil handling
fmt.Printf("- %s: %s\n", tool.Name, desc)
}
// Get a specific tool
tool, _, err := client.Tools.Get(ctx, "tool-id")
if err != nil {
log.Fatal(err)
}
fmt.Printf("\nTool details: %s (Enabled: %v)\n", tool.Name, tool.Enabled)
}使用指南
客户端配置
import (
"net/http"
"time"
"github.com/leefowlercu/go-contextforge/contextforge"
)
// Create a client with locally hosted ContextForge instance
client, err := contextforge.NewClient(nil, "http://localhost:8000/", "your-jwt-token")
if err != nil {
log.Fatal(err)
}
// Create a client with remote ContextForge instance
client, err := contextforge.NewClient(nil, "https://contextforge.example.com/", "your-jwt-token")
if err != nil {
log.Fatal(err)
}
// Custom HTTP client with timeout
httpClient := &http.Client{
Timeout: 60 * time.Second,
}
client, err = contextforge.NewClient(httpClient, "https://contextforge.example.com/", "your-jwt-token")
if err != nil {
log.Fatal(err)
}
// Note: NewClient automatically adds trailing slash if missing指针助手和标签
SDK使用指针和切片来区分可选字段的三种状态:
- 无 -未设置字段(API请求中省略)
- 指向零值或空切片的指针 -字段已明确清除
- 指向值或填充切片的指针 -字段设置为该值
这种模式(由google/go-github、hashicorp/go-tfe、AWS SDK使用)允许部分更新,其中只有更改的字段被发送到API。
辅助功能:
// Creating pointers (for setting values)
name := contextforge.String("my-tool")
limit := contextforge.Int(10)
enabled := contextforge.Bool(true)
timeout := contextforge.Int64(3000)
weight := contextforge.Float64(0.95)
timestamp := contextforge.Time(time.Now())
// Extracting values (with zero-value fallback for nil)
nameStr := contextforge.StringValue(name) // "my-tool"
limitInt := contextforge.IntValue(nil) // 0
enabledBool := contextforge.BoolValue(enabled) // true部分更新示例:
// Update only the name (other fields unchanged)
update := &contextforge.ResourceUpdate{
Name: contextforge.String("new-name"),
// Description, Tags, etc. are nil and won't be sent
}
// Clear the description (set to empty string)
update := &contextforge.ResourceUpdate{
Description: contextforge.String(""),
}
// Don't update tags vs clear all tags
update1 := &contextforge.ResourceUpdate{
Tags: nil, // Tags field omitted - existing tags unchanged
}
update2 := &contextforge.ResourceUpdate{
Tags: []string{}, // Empty array sent - clears all tags
}
update3 := &contextforge.ResourceUpdate{
Tags: []string{"new-tag"}, // Sets new tags
}标签类型处理:
由于v1.0.0中API响应格式的变化,标签的输入和输出类型不同:
- 创建/更新类型 使用
[]string用于输入(例如。,ResourceCreate.Tags,PromptCreate.Tags) - 读取类型 返回
[]Tag结构体ID和Label字段(例如。,Tool.Tags,Prompt.Tags)
转换辅助函数:
// Convert strings to Tag structs (for update operations on read types)
tags := contextforge.NewTags([]string{"tag1", "tag2"})
// Extract tag names from Tag structs
names := contextforge.TagNames(tool.Tags) // Returns []string{"tag1", "tag2"}管理工具
ctx := context.Background()
// List tools with filtering
opts := &contextforge.ToolListOptions{
IncludeInactive: false,
Tags: "automation,api",
Visibility: "public",
ListOptions: contextforge.ListOptions{
Limit: 20,
},
}
tools, resp, err := client.Tools.List(ctx, opts)
// Get tool by ID
tool, _, err := client.Tools.Get(ctx, "tool-id")
// Create a new tool
newTool := &contextforge.Tool{
Name: "my-tool",
Description: contextforge.String("A custom tool"),
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"input": map[string]any{"type": "string"},
},
},
Enabled: true,
}
// Create with optional team/visibility settings
createOpts := &contextforge.ToolCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Tools.Create(ctx, newTool, createOpts)
// Create without options
created, _, err = client.Tools.Create(ctx, newTool, nil)
// Update tool
tool.Description = contextforge.String("Updated description")
updated, _, err := client.Tools.Update(ctx, "tool-id", tool)
// Toggle tool status
toggled, _, err := client.Tools.Toggle(ctx, "tool-id", true) // activate
// Delete tool
_, err = client.Tools.Delete(ctx, "tool-id")管理资源
由于API字段命名约定,资源对于不同的操作具有不同的类型:
- 资源创建:用于创建资源(使用snake_case:
mime_type) - 资源:阅读资源(使用camelCase:
mimeType) - 资源更新:用于更新资源(使用camelCase:
mimeType)
ctx := context.Background()
// List resources
resources, _, err := client.Resources.List(ctx, nil)
// Create a resource
newResource := &contextforge.ResourceCreate{
URI: "file:///path/to/resource",
Name: "my-resource",
Content: "Resource content here",
Description: contextforge.String("A custom resource"),
MimeType: contextforge.String("text/plain"), // Note: snake_case for Create
Tags: []string{"documentation", "example"},
}
// Create with optional team/visibility settings
createOpts := &contextforge.ResourceCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Resources.Create(ctx, newResource, createOpts)
// Create without options
created, _, err = client.Resources.Create(ctx, newResource, nil)
// Get resource content (hybrid REST endpoint returns MCP-compatible format)
content, _, err := client.Resources.Get(ctx, "resource-id")
if err == nil {
fmt.Printf("Resource type: %s\n", content.Type)
fmt.Printf("URI: %s\n", content.URI)
if content.Text != nil {
fmt.Printf("Text content: %s\n", *content.Text)
}
if content.Blob != nil {
fmt.Printf("Blob content (base64): %s\n", *content.Blob)
}
}
// Update resource (uses camelCase fields)
update := &contextforge.ResourceUpdate{
Description: contextforge.String("Updated description"),
MimeType: contextforge.String("text/markdown"), // Note: camelCase for Update
Tags: []string{"updated", "documentation"},
}
updated, _, err := client.Resources.Update(ctx, "resource-id", update)
// Toggle resource status
toggled, _, err := client.Resources.Toggle(ctx, "resource-id", false) // deactivate
// List available templates
templates, _, err := client.Resources.ListTemplates(ctx)
for _, template := range templates.Templates {
fmt.Printf("Template: %s\n", template.Name)
}
// Delete resource
_, err = client.Resources.Delete(ctx, "resource-id")管理网关
网关支持MCP服务器的联合和代理:
ctx := context.Background()
// List gateways
gateways, _, err := client.Gateways.List(ctx, nil)
// Create a gateway
newGateway := &contextforge.Gateway{
Name: "my-gateway",
URL: "http://mcp-server.example.com",
Description: contextforge.String("Proxy to external MCP server"),
Transport: "STREAMABLEHTTP",
AuthType: contextforge.String("bearer"),
AuthToken: contextforge.String("server-token"),
}
// Create with optional team/visibility settings
createOpts := &contextforge.GatewayCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Gateways.Create(ctx, newGateway, createOpts)
// Create without options
created, _, err = client.Gateways.Create(ctx, newGateway, nil)
// Get gateway by ID
gateway, _, err := client.Gateways.Get(ctx, "gateway-id")
// Update gateway
gateway.Description = contextforge.String("Updated gateway description")
updated, _, err := client.Gateways.Update(ctx, "gateway-id", gateway)
// Toggle gateway status
toggled, _, err := client.Gateways.Toggle(ctx, "gateway-id", true) // activate
// Delete gateway
_, err = client.Gateways.Delete(ctx, "gateway-id")管理服务器
服务器代表由ContextForge管理的MCP服务器实例:
ctx := context.Background()
// List servers
servers, _, err := client.Servers.List(ctx, nil)
// Create a server
newServer := &contextforge.ServerCreate{
Name: "my-server",
Description: contextforge.String("Custom MCP server"),
Icon: contextforge.String("server"),
Tags: []string{"mcp", "production"},
// Optional: Associate with existing resources
AssociatedTools: []string{"tool-id-1", "tool-id-2"},
AssociatedResources: []string{"resource-id-1"},
AssociatedPrompts: []string{"prompt-id-1"},
AssociatedA2aAgents: []string{"agent-id-1"},
}
// Create with optional team/visibility settings
createOpts := &contextforge.ServerCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Servers.Create(ctx, newServer, createOpts)
// Create without options
created, _, err = client.Servers.Create(ctx, newServer, nil)
// Get server by ID
server, _, err := client.Servers.Get(ctx, "server-id")
// Update server
update := &contextforge.ServerUpdate{
Description: contextforge.String("Updated server description"),
Tags: []string{"mcp", "production", "updated"},
AssociatedTools: []string{"tool-id-3"}, // Replace associations
}
updated, _, err := client.Servers.Update(ctx, "server-id", update)
// Toggle server status
toggled, _, err := client.Servers.Toggle(ctx, "server-id", true) // activate
// List server's tools
tools, _, err := client.Servers.ListTools(ctx, "server-id", nil)
// List server's resources
resources, _, err := client.Servers.ListResources(ctx, "server-id", nil)
// List server's prompts
prompts, _, err := client.Servers.ListPrompts(ctx, "server-id", nil)
// Delete server
_, err = client.Servers.Delete(ctx, "server-id")注: ServersService不包括MCP协议通信端点(GET /servers/{id}/sse 和 POST /servers/{id}/message).这些用于MCP协议通信,而不是REST API管理。
管理提示
提示为AI模型提供模板化交互:
ctx := context.Background()
// List prompts
prompts, _, err := client.Prompts.List(ctx, nil)
// List with filtering
opts := &contextforge.PromptListOptions{
IncludeInactive: true,
Tags: "ai,code-review",
TeamID: "team-123",
}
prompts, _, err = client.Prompts.List(ctx, opts)
// Create a prompt
newPrompt := &contextforge.PromptCreate{
Name: "code-review",
Description: contextforge.String("Code review prompt template"),
Template: "Please review this {{language}} code:\n\n{{code}}",
Arguments: []contextforge.PromptArgument{
{Name: "language", Description: contextforge.String("Programming language"), Required: true},
{Name: "code", Description: contextforge.String("Code to review"), Required: true},
},
Tags: []string{"ai", "code-review"},
}
// Create with optional team/visibility settings
createOpts := &contextforge.PromptCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Prompts.Create(ctx, newPrompt, createOpts)
// Get rendered prompt with arguments (hybrid REST endpoint)
args := map[string]string{
"language": "Go",
"code": "func main() { fmt.Println(\"Hello\") }",
}
result, _, err := client.Prompts.Get(ctx, "code-review", args)
if err == nil {
fmt.Printf("Description: %s\n", *result.Description)
for _, msg := range result.Messages {
fmt.Printf("Role: %s, Content: %s\n", msg.Role, *msg.Content.Text)
}
}
// Get prompt without arguments
result, _, err = client.Prompts.GetNoArgs(ctx, "simple-prompt")
// Update prompt (promptID is a string)
update := &contextforge.PromptUpdate{
Description: contextforge.String("Updated description"),
Template: contextforge.String("Updated template: {{new_arg}}"),
}
updated, _, err := client.Prompts.Update(ctx, "prompt-id", update)
// Toggle prompt status
toggled, _, err := client.Prompts.Toggle(ctx, "prompt-id", true) // activate
toggled, _, err = client.Prompts.Toggle(ctx, "prompt-id", false) // deactivate
// Delete prompt
_, err = client.Prompts.Delete(ctx, "prompt-id")管理代理
A2A(代理到代理)代理通过ContextForge实现跨年龄通信。由于API字段命名约定,代理对于不同的操作具有不同的类型:
- 代理创建:用于创建代理(使用snake_case:
endpoint_url,agent_type) - 代理:用于阅读代理(使用camelCase:
endpointUrl,agentType) - 代理更新:用于更新代理(使用camelCase:
endpointUrl,agentType)
ctx := context.Background()
// List agents with skip/limit pagination (not cursor-based)
agents, _, err := client.Agents.List(ctx, &contextforge.AgentListOptions{
Skip: 0,
Limit: 20,
})
// List with filtering
opts := &contextforge.AgentListOptions{
Skip: 10,
Limit: 50,
IncludeInactive: true,
Tags: "automation,integration",
TeamID: "team-123",
Visibility: "public",
}
agents, _, err = client.Agents.List(ctx, opts)
// Get agent by ID
agent, _, err := client.Agents.Get(ctx, "agent-id")
// Create a new agent
newAgent := &contextforge.AgentCreate{
Name: "data-processor",
EndpointURL: "https://agent.example.com/a2a",
Description: contextforge.String("Processes data records"),
AgentType: "generic", // Note: snake_case for Create
ProtocolVersion: "1.0",
Capabilities: map[string]any{
"streaming": true,
"batch": true,
},
Config: map[string]any{
"timeout": 30,
"retries": 3,
},
AuthType: contextforge.String("bearer"),
AuthValue: contextforge.String("secret-token"), // Encrypted by API
Tags: []string{"data", "processing"},
}
// Create with optional team/visibility settings
createOpts := &contextforge.AgentCreateOptions{
TeamID: contextforge.String("team-123"),
Visibility: contextforge.String("public"),
}
created, _, err := client.Agents.Create(ctx, newAgent, createOpts)
// Create without options
created, _, err = client.Agents.Create(ctx, newAgent, nil)
// Update agent (uses camelCase fields)
update := &contextforge.AgentUpdate{
Description: contextforge.String("Updated description"),
AgentType: contextforge.String("specialized"), // Note: camelCase for Update
ProtocolVersion: contextforge.String("2.0"),
Tags: []string{"updated", "enhanced"},
}
updated, _, err := client.Agents.Update(ctx, "agent-id", update)
// Toggle agent status
toggled, _, err := client.Agents.Toggle(ctx, "agent-id", true) // enable
toggled, _, err = client.Agents.Toggle(ctx, "agent-id", false) // disable
// Invoke an agent by name (not ID!)
invokeReq := &contextforge.AgentInvokeRequest{
Parameters: map[string]any{
"input": "data to process",
"options": map[string]any{
"format": "json",
"validate": true,
},
},
InteractionType: "query", // default: "query"
}
result, _, err := client.Agents.Invoke(ctx, created.Name, invokeReq)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Result: %v\n", result)
// Delete agent
_, err = client.Agents.Delete(ctx, "agent-id")重要提示:
- 分页:代理使用跳过/限制(基于偏移量)分页,而不是其他服务使用的基于光标的分页
- 调用端点:使用代理名称(不是ID)作为标识符
- 字段命名:AgentCreate使用snake_case,而AgentUpdate使用camelCase
- 认证:The
AuthValue字段在存储时由API加密 - 双重状态:代理商两者都有
Enabled(用户控制)和Reachable(系统状态)状态 - 性能指标:代理跟踪执行指标,包括成功/失败率和响应时间
管理团队
团队支持协作资源管理和访问控制。团队运营支持成员管理、邀请和团队发现:
ctx := context.Background()
// List teams with skip/limit pagination (not cursor-based)
teams, _, err := client.Teams.List(ctx, &contextforge.TeamListOptions{
Skip: 0,
Limit: 20,
})
// Get team by ID
team, _, err := client.Teams.Get(ctx, "team-id")
// Create a basic team
newTeam := &contextforge.TeamCreate{
Name: "engineering",
Description: contextforge.String("Engineering team for product development"),
}
created, _, err := client.Teams.Create(ctx, newTeam)
// Create a team with all options
completeTeam := &contextforge.TeamCreate{
Name: "design",
Slug: contextforge.String("design-team"), // Always auto-generated from name (API ignores user value)
Description: contextforge.String("Design team for UI/UX"),
Visibility: contextforge.String("public"), // "private" (default) | "public"
MaxMembers: contextforge.Int(50),
}
created, _, err = client.Teams.Create(ctx, completeTeam)
// Update team
update := &contextforge.TeamUpdate{
Description: contextforge.String("Updated description"),
Visibility: contextforge.String("public"),
MaxMembers: contextforge.Int(100),
}
updated, _, err := client.Teams.Update(ctx, "team-id", update)
// Delete team
_, err = client.Teams.Delete(ctx, "team-id")
// List team members
members, _, err := client.Teams.ListMembers(ctx, "team-id")
// Add a direct member
member, _, err := client.Teams.AddMember(ctx, "team-id", &contextforge.TeamMemberAdd{
Email: "user@example.com",
Role: "member",
})
// Update member role (uses email as identifier)
memberUpdate := &contextforge.TeamMemberUpdate{
Role: "owner", // "owner" | "member"
}
member, _, err := client.Teams.UpdateMember(ctx, "team-id", "user@example.com", memberUpdate)
// Remove member (uses email as identifier)
_, err = client.Teams.RemoveMember(ctx, "team-id", "user@example.com")
// Invite a new member
invite := &contextforge.TeamInvite{
Email: "newuser@example.com",
Role: contextforge.String("member"), // Optional, defaults to "member"
}
invitation, _, err := client.Teams.InviteMember(ctx, "team-id", invite)
// List team invitations
invitations, _, err := client.Teams.ListInvitations(ctx, "team-id")
// Accept invitation (using token)
member, _, err := client.Teams.AcceptInvitation(ctx, "invitation-token")
// Cancel invitation
_, err = client.Teams.CancelInvitation(ctx, "invitation-id")
// Discover public teams
discoveredTeams, _, err := client.Teams.Discover(ctx, &contextforge.TeamDiscoverOptions{
Limit: 10,
})
// Request to join a public team
joinRequest, _, err := client.Teams.Join(ctx, "team-id", &contextforge.TeamJoinRequest{
Message: contextforge.String("I'd like to contribute to this team"),
})
// Leave a team
_, err = client.Teams.Leave(ctx, "team-id")
// List join requests (owners only)
joinRequests, _, err := client.Teams.ListJoinRequests(ctx, "team-id")
// Approve join request
member, _, err = client.Teams.ApproveJoinRequest(ctx, "team-id", "request-id")
// Reject join request
_, err = client.Teams.RejectJoinRequest(ctx, "team-id", "request-id")重要提示:
- 分页:团队使用跳过/限制(基于偏移)分页,如代理
- 列表响应:返回结构化响应
{teams: [], total: N}不仅仅是数组 - 无请求包装:与工具/资源不同,团队创建/更新没有包装
- 蛞蝓生成:如果未提供,则根据团队名称自动生成
- 会员身份:成员端点使用电子邮件(不是ID)作为标识符
- 邀请令牌:接受邀请时使用过期的一次性令牌
- 个人团队:不能删除或保留;有特殊限制
- 最后所有者保护:如果是最后一个所有者,则不能离开或降级
分页
ContextForge支持两种分页模式:
基于光标的分页 (工具、资源、网关、服务器、提示):
opts := &contextforge.ToolListOptions{
ListOptions: contextforge.ListOptions{Limit: 50},
}
for {
tools, resp, err := client.Tools.List(ctx, opts)
if err != nil {
break
}
// Process tools
for _, tool := range tools {
fmt.Printf("Tool: %s\n", tool.Name)
}
// Check for more pages
if resp.NextCursor == "" {
break
}
opts.Cursor = resp.NextCursor
}跳过/限制(基于偏移量)分页 (代理人、团队):
opts := &contextforge.AgentListOptions{
Limit: 50,
}
for skip := 0; ; skip += opts.Limit {
opts.Skip = skip
agents, _, err := client.Agents.List(ctx, opts)
if err != nil {
break
}
// Process agents
for _, agent := range agents {
fmt.Printf("Agent: %s\n", agent.Name)
}
// Check if we've reached the end
if len(agents) 0 {
fmt.Printf("Rate limit: %d/%d remaining\n", resp.Rate.Remaining, resp.Rate.Limit)
}API方法参考
工具服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 具有分页和筛选功能的列表工具 |
Get(ctx, toolID) | 按ID获取工具 |
Create(ctx, tool, opts) | 使用可选设置创建新工具 |
Update(ctx, toolID, tool) | 更新工具 |
Delete(ctx, toolID) | 删除工具 |
Toggle(ctx, toolID, activate) | 切换工具启用状态 |
资源服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 列出具有分页和筛选功能的资源 |
Get(ctx, resourceID) | 获取资源内容(返回MCP兼容 ResourceContent) |
Create(ctx, resource, opts) | 使用可选设置创建新资源 |
Update(ctx, resourceID, resource) | 更新资源 |
Delete(ctx, resourceID) | 删除资源 |
Toggle(ctx, resourceID, activate) | 切换资源活动状态 |
ListTemplates(ctx) | 列出可用的资源模板 |
网关服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 列出具有分页和过滤功能的网关 |
Get(ctx, gatewayID) | 按ID获取网关 |
Create(ctx, gateway, opts) | 使用可选设置创建新网关 |
Update(ctx, gatewayID, gateway) | 更新网关 |
Delete(ctx, gatewayID) | 删除网关 |
Toggle(ctx, gatewayID, activate) | 切换网关活动状态 |
服务器服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 列出具有分页和筛选功能的服务器 |
Get(ctx, serverID) | 按ID获取服务器 |
Create(ctx, server, opts) | 使用可选设置创建新服务器 |
Update(ctx, serverID, server) | 更新服务器 |
Delete(ctx, serverID) | 删除服务器 |
Toggle(ctx, serverID, activate) | 切换服务器启用状态 |
ListTools(ctx, serverID, opts) | 列出与服务器关联的工具 |
ListResources(ctx, serverID, opts) | 列出与服务器关联的资源 |
ListPrompts(ctx, serverID, opts) | 列出与服务器关联的提示 |
提示服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 带有分页和筛选功能的列表提示 |
Get(ctx, promptID, args) | 获取带有参数的呈现提示(返回MCP兼容 PromptResult) |
GetNoArgs(ctx, promptID) | 获取不带参数的呈现提示符(返回MCP兼容 PromptResult) |
Create(ctx, prompt, opts) | 使用可选设置创建新提示 |
Update(ctx, promptID, prompt) | 更新提示 |
Delete(ctx, promptID) | 删除提示 |
Toggle(ctx, promptID, activate) | 切换提示活动状态 |
代理服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 列出具有跳过/限制分页和过滤功能的代理 |
Get(ctx, agentID) | 按ID获取代理 |
Create(ctx, agent, opts) | 使用可选设置创建新代理 |
Update(ctx, agentID, agent) | 更新代理 |
Delete(ctx, agentID) | 删除代理 |
Toggle(ctx, agentID, activate) | 切换代理启用状态 |
Invoke(ctx, agentName, req) | 按名称和参数调用代理 |
注: 代理使用跳过/限制(基于偏移量)分页,而不是基于光标的分页。Invoke方法使用代理名称(而不是ID)作为标识符。
团队服务
| 方法 | 说明 |
|---|---|
List(ctx, opts) | 列出具有跳过/限制分页的团队 |
Get(ctx, teamID) | 按ID获取团队 |
Create(ctx, team) | 创建新团队 |
Update(ctx, teamID, team) | 更新团队 |
Delete(ctx, teamID) | 删除团队 |
ListMembers(ctx, teamID) | 列出团队成员 |
AddMember(ctx, teamID, add) | 通过电子邮件直接添加成员 |
UpdateMember(ctx, teamID, email, update) | 更新成员角色(使用电子邮件) |
RemoveMember(ctx, teamID, email) | 删除成员(使用电子邮件) |
InviteMember(ctx, teamID, invite) | 邀请用户加入团队 |
ListInvitations(ctx, teamID) | 列出团队邀请 |
AcceptInvitation(ctx, token) | 接受邀请(使用令牌) |
CancelInvitation(ctx, invitationID) | 取消邀请 |
Discover(ctx, opts) | 发现公共团队 |
Join(ctx, teamID, req) | 申请加入公共团队 |
Leave(ctx, teamID) | 离开团队 |
ListJoinRequests(ctx, teamID) | 列出加入请求(仅限所有者) |
ApproveJoinRequest(ctx, teamID, reqID) | 批准加入请求 |
RejectJoinRequest(ctx, teamID, reqID) | 拒绝加入请求 |
注: 团队使用跳过/限制(基于偏移)分页,就像代理一样。列表返回结构化响应 {teams: [], total: N}。成员操作使用电子邮件作为标识符,而不是ID。
例子
SDK包括演示所有服务功能的工作示例程序:
- 工具/ -工具服务CRUD操作和过滤
- 资源/ -带模板的资源服务
- 网关/ -网关联合和代理
- 服务器/ -服务器管理和关联
- 提示/ -提示模板和参数
- 代理商/ -A2A代理、调用和跳过/限制分页
- 团队/ -团队管理、成员、邀请和发现
每个示例都包含一个模拟HTTP服务器,并演示了:
- 身份验证流程
- CRUD操作
- 分页模式(光标或跳过/限制)
- 过滤和查询
- 错误处理
- 服务特定功能
运行任何示例:
go run examples/tools/main.go
go run examples/agents/main.go发展
运行测试
# Unit tests
make test
# or
go test ./...
# Unit tests with coverage
make test-cover
# Integration tests (requires ContextForge running)
make integration-test-setup # Start ContextForge gateway
make integration-test # Run integration tests
make integration-test-teardown # Stop gateway
# Full integration test cycle
make integration-test-all
# Run both unit and integration tests
make test-all
# Generate HTML coverage report
make coverage集成测试配置
集成测试需要环境变量:
# Required to enable integration tests
export INTEGRATION_TESTS=true
# Optional configuration (defaults shown)
export CONTEXTFORGE_ADDR="http://localhost:8000/"
export CONTEXTFORGE_ADMIN_EMAIL="admin@test.local"
export CONTEXTFORGE_ADMIN_PASSWORD="testpassword123"建筑
# Build all packages
make build
# Build with formatting and linting
make check
# Format code
make fmt
# Lint
make vet
# Full CI pipeline
make ci可用生成目标
发展:
make deps-下载依赖项make fmt-使用gofmt格式化代码make vet-快跑兽医make lint-格式和审查make test-运行单元测试make test-verbose-运行具有详细输出的单元测试make test-cover-运行覆盖率的单元测试make build-构建所有包make clean-清理构建工件make coverage-生成HTML覆盖率报告make ci-完整的CI管道(拆卸、拆卸、测试、构建)
测试:
make integration-test-setup-启动ContextForge网关make integration-test-运行集成测试make integration-test-teardown-停止网关make integration-test-all-完整的集成测试周期make test-all-运行单元和集成测试
释放:
make goreleaser-check-验证GoRelease配置make goreleaser-snapshot-本地测试发布,不发布make release-check-验证发布先决条件make release-patch-准备补丁发布(自动增量补丁版本)make release-minor-准备次要版本(自动递增次要版本)make release-major-准备主要版本(自动递增主要版本)make release-prep VERSION=vX.Y.Z-准备发布特定版本make release-完整的发布准备工作流程
代理审查管道
该存储库包括一个上游形状的、CI辅助的采用 代理审查管道。它在以下位置安装提示资产 agents/,手册 操作员入口点 commands/review_pr.md,仅stdlib下的辅助脚本 scripts/,以及两个用于记分簿记录和 反映循环建议PR。
手动入口点
使用 commands/review_pr.md 作为人为操作的合同 /review_pr 命令。当前的集成首先是手动的:它标准化了工件名称 例如 coordinator-report.md 和 verify-feedback.json,但还没有 包括一个自动PR审查执行器。
工作流和权限
存储库现在包括:
.github/workflows/record-run.yml.github/workflows/reflect-and-propose.yml
所需的工作流权限:
contents: writepull-requests: writemodels: read
record-run.yml 设计严格:它只附加到 data/pipeline-runs.jsonl 当一个真实 coordinator-report.md 人工制品是 可用。在手动或自动审查执行器上传这些工件之前, 记分簿工作流将完全跳过,反射循环将保持不变 休眠的。
可选存储库引导
如果需要调整存储库操作设置,请使用 scripts/configure_actions_permissions.py 带有repo管理令牌。这是一个 可选的引导助手,不是正常开发或发布流程的一部分。
释放
该项目使用语义版本控制,并包括自动发布工具来简化发布过程。
先决条件:
- GoRelease -自动发布管理所需
go install github.com/goreleaser/goreleaser/v2@latest- GitHub代币 -设置
GITHUB_TOKENGitHub发布创建的环境变量
- 在以下位置创建令牌:https://github.com/settings/tokens/new - 所需范围: repo (完全访问存储库) - 添加到您的shell配置文件: export GITHUB_TOKEN=your_token_here
语义版本碰撞
发布工作流支持自动语义版本跳转:
# Patch release (0.1.0 → 0.1.1) - bug fixes, no API changes
make release-patch
# Minor release (0.1.0 → 0.2.0) - new features, backward compatible
make release-minor
# Major release (0.1.0 → 1.0.0) - breaking changes
make release-major手动版本覆盖
您还可以手动指定版本:
make release-prep VERSION=v0.2.5发布工作流
每个释放命令执行以下步骤:
- 先决条件检查:确保git工作目录干净,并且安装了goreleaser
- 版本计算:根据中的当前版本确定新版本
contextforge/version.go - 更新版本常量:更新
contextforge/version.go使用新版本 - 创建提交:提交版本更改并显示消息
release: prepare vX.Y.Z - 创建标签:为发布创建带注释的git标签
- 运行GoRelease:执行
goreleaser release --clean其中:
- 从常规提交更新CHANGELOG.md - 创建带有发布说明的GitHub发布草案
- 人工审核:在GitHub上查看发布草案,并在本地更改CHANGELOG.md
- 发布:准备就绪后,推送commit和标签,然后在GitHub上发布发布草稿
注: GoReleaser在发布之前创建一个草稿版本供审查。更改日志是使用传统提交格式从提交消息中自动生成的。
推动发布
运行release命令后,按下更改:
# Push commit and tag
git push && git push --tags
# Then create a GitHub release at:
# https://github.com/leefowlercu/go-contextforge/releases/new?tag=vX.Y.Z版本管理
- SDK版本:定义见
contextforge/version.go作为Version恒定 - 用户代理:自动包含SDK版本(
go-contextforge/vX.Y.Z) - 更新日志:使用GoReleaser从常规提交中自动生成,如下所示 保存变更日志 格式
- Git标签:使用格式
vX.Y.Z(语义版本控制v前缀) - 提交格式:所有提交都应该使用 常规承诺 格式(例如。,
feat:,fix:,docs:)
变更日志生成
该项目使用 GoRelease 根据提交消息自动生成更改日志。Changelog的生成在发布工作流程中自动进行,并在Changelog.md和GitHub发布说明中创建条目。
测试GoRelease配置:
# Validate GoReleaser configuration
make goreleaser-check
# Test release locally without publishing
make goreleaser-snapshot配置 (.goreleaser.yaml):
- 使用GitHub的原生变更日志生成
- 按常规提交类型进行组提交
- 不包括合并提交和发布准备提交
- 创建GitHub发布草案以供手动审查
提交类型映射到变更日志部分:
feat:→ Addedfix:,bug:→ Fixedrefactor:→ 改变docs:→ 文档build:,chore:→ Buildtest:,style:→ Tests
撤消发布准备
如果需要撤消发布准备(在按下之前):
# Remove the tag
git tag -d vX.Y.Z
# Reset the commit
git reset --hard HEAD~1建筑
此SDK遵循由 ,将API端点组织到逻辑服务组中:
- 客户 -HTTP客户端管理和JWT身份验证的主要入口点
- 工具服务 -所有与工具相关的操作
- 资源服务 -所有与资源相关的操作
- 网关服务 -所有与网关相关的操作
- 服务器服务 -所有与服务器相关的操作和关联
- Prompts服务 -所有及时的管理操作
- 代理服务 -所有A2A代理操作、调用和性能跟踪
- 团队服务 -团队管理、成员、邀请和发现
- 取消服务 -请求取消飞行和取消状态检查
自定义类型
- 灵活ID -处理API不一致,其中ID可能以整数或字符串形式返回
- 时间戳 -不带时区信息的API响应的自定义时间戳解析
- 标签 -使用以下方式处理标记对象
ID和Label领域;自定义JSON封送/解封送以实现API兼容性 - 指针助手 -
String(),Int(),Bool(),Time()用于处理可选字段 - 标签助手 -
NewTags(),NewTag(),TagNames()用于转换[]string和[]Tag
链接
- ContextForge存储库: https://github.com/IBM/mcp-context-forge
- MCP协议: https://modelcontextprotocol.io/
- A2A协议规范: https://a2a-protocol.org/latest/specification/
- A2A协议网站: https://a2aprotocol.ai/
已知问题
上游ContextForge API Bug
SDK集成测试目前记录了几个上游ContextForge API错误。这些bug存在于上游API中,而不是SDK实现中。跳过受影响的测试,一旦上游修复成功,将重新启用;请参阅链接的报告,了解每个问题的最新验证版本。
CONTEXTFORGE-001:切换端点返回停滞状态 这 POST /prompts/{id}/toggle 和 POST /resources/{id}/toggle 端点返回陈旧 isActive 尽管正确更新了数据库,但仍处于状态。看 docs/upstream-bugs/contextforge-001-prompt-toggle.md.
CONTEXTFORGE-002:提示API接受空模板字段 这 POST /prompts 终结点接受无需 template 字段,允许语义无效的提示。看 docs/upstream-bugs/contextforge-002-prompt-validation-missing-template.md.
CONTEXTFORGE-005:Teams API忽略用户提供的Slug字段 这 POST /teams 端点忽略 slug 字段,并始终根据团队名称自动生成。看 docs/upstream-bugs/contextforge-005-teams-slug-ignored.md.
CONTEXTFORGE-008:代理承载身份字段名称不匹配 A2A代理API仍然期望 auth_token 用于承载身份验证,而不是SDK auth_value 现场。看 docs/upstream-bugs/contextforge-008-agent-auth-field-name.md.
CONTEXTFORGE-010:团队ID筛选器返回权限错误 工具列表API仍然返回 403 对于一些应该成功的团队范围的过滤器组合。看 docs/upstream-bugs/contextforge-010-team-id-filter-permission-error.md.
在当前RC1验证中已解决: CONTEXTFORGE-004 团队端点身份验证失败不再重现。历史和已解决的报告仍在 docs/upstream-bugs/.
所有错误报告都包括根本原因分析、建议的解决方案和变通方法。
许可证
MIT许可证-请参阅 许可证 文件以获取详细信息。
