This file provides guidance to Claude Code when working with code in this repository.
- Project Overview
- Project Principles
- High-Level Architecture
- Subsystems Reference
- Conventions & Patterns
- Code Organization Principles
- Testing Approach
- Development Commands
Go SDK for the IBM ContextForge MCP Gateway API, providing an idiomatic client library for programmatically managing MCP resources and A2A agents.
Key Features:
- Service-oriented architecture following
google/go-githubpatterns - Full CRUD operations for Tools, Resources, Gateways, Servers, Prompts, Agents, and Teams
- JWT bearer token authentication
- Cursor-based and offset-based pagination support
- Rate limit tracking and error handling
- Three-state semantics for partial updates (essential for Terraform providers)
This SDK follows these guiding principles:
- google/go-github Pattern Adherence: Follow the service-oriented architecture established by google/go-github for consistent, idiomatic Go SDK design
- Three-State Semantics: Support nil (omit), empty (clear), and value (set) states for all optional fields to enable partial updates
- Context-First API: All API methods accept
context.Contextas first parameter for cancellation and timeout support - Explicit Over Implicit: Prefer explicit type definitions over interface{} where possible; use custom types for API inconsistencies
- Test-Driven Development: Unit tests with HTTP mocking for all service methods; integration tests against real API
The SDK follows the service-oriented architecture pattern established by google/go-github:
- Single
Clientstruct: Manages HTTP communication, JWT authentication, and rate limit tracking - Service structs:
ToolsService,ResourcesService,GatewaysService,ServersService,PromptsService,AgentsService,TeamsService(each embeds sharedservicestruct) - Context support: All API methods accept
context.Contextfor cancellation and timeouts
Client Management (contextforge/contextforge.go):
NewClient(httpClient, address, bearerToken): Factory for authenticated clients (returns*Client, error)- Accepts address as string parameter (e.g.,
"https://api.example.com/") - Automatically appends trailing slash if missing
- Returns error for invalid URL formats
- Accepts address as string parameter (e.g.,
NewRequest(): Creates HTTP requests with proper headers and authenticationDo(): Executes requests with context support, error handling, and rate limit tracking- Thread-safe rate limit tracking using
sync.MutexandrateLimitsmap
Service Pattern:
- Each service embeds
servicestruct with pointer to parentClient - Services instantiated once during client creation via
commonservice pattern - All services reused throughout client lifetime
Custom Types (contextforge/types.go):
FlexibleID: Handles API inconsistencies where IDs may be integers or stringsTimestamp: Custom timestamp parsing for API responses without timezone informationTag: Handles tag objects withIDandLabelfields; custom JSON marshal/unmarshal for API compatibility (accepts strings, returns objects)
Pointer Helpers (contextforge/pointers.go):
- Creating pointers:
String(),Int(),Int64(),Bool(),Float64(),Time() - Safe dereferencing:
StringValue(),IntValue(),Int64Value(),BoolValue(),Float64Value(),TimeValue() - Tag helpers:
NewTag(),NewTags(),TagNames()for converting between[]stringand[]Tag - Enables three-state semantics for optional fields
Rate Limiting:
- Tracked per-endpoint path in
Client.rateLimits - Parses headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - Custom
RateLimitErrortype for 429 responses
Pagination Patterns: The SDK supports two pagination patterns based on API endpoint design:
- Cursor-based (Tools, Resources, Gateways, Servers, Prompts): Uses
ListOptionswithLimitandCursorfields. Next cursor fromX-Next-Cursorheader. - Offset-based (Agents, Teams): Uses service-specific options with
SkipandLimitfields for offset pagination.
Error Handling (contextforge/errors.go):
ErrorResponse: Standard API error with message and error detailsRateLimitError: Specialized error for rate limitingCheckResponse(): Validates HTTP responses and returns typed errors- URL sanitization to prevent token leakage in error messages
Additional Core Files:
contextforge/doc.go- Package-level documentationcontextforge/version.go- SDK version constant used in User-Agent headerdocs/three-state-system.md- Comprehensive guide to three-state semantics patterndocs/terraform-provider-usage.md- Guide for building Terraform providers with the SDK
This SDK does not have separate subsystems. All functionality is contained within the contextforge/ package.
Related Documentation:
docs/three-state-system.md- Guide to three-state semantics for optional fieldsdocs/terraform-provider-usage.md- Building Terraform providers with this SDKdocs/upstream-bugs/- Documentation of known upstream API bugs
Some API endpoints require request body wrapping (e.g., {"tool": {...}}). Check upstream schema/router definitions for the target tag, or existing service implementations, for wrapping requirements.
Field Naming Conventions:
- Create types use snake_case for some fields (e.g.,
mime_type,endpoint_url) - Read/Update types use camelCase (e.g.,
mimeType,endpointUrl) - The SDK handles these inconsistencies internally
Prompt ID Type:
- Prompt IDs are
stringtype (changed fromintin v1.0.0) - All PromptService methods accept
promptID string
The SDK uses a three-state semantics pattern for optional fields in update operations:
- nil pointer/slice - Field omitted from request (don't update existing value)
- Pointer to zero value or empty slice - Field cleared/set to empty
- Pointer to value or populated slice - Field set to specific value
// Update only name, leave other fields unchanged
update := &contextforge.ResourceUpdate{
Name: contextforge.String("new-name"),
// Description is nil - won't be sent to API
}
// Clear all tags vs don't update tags
update1 := &contextforge.ResourceUpdate{Tags: nil} // Tags unchanged
update2 := &contextforge.ResourceUpdate{Tags: []string{}} // Clears all tagsDocumentation:
- docs/three-state-system.md - Comprehensive guide
- docs/terraform-provider-usage.md - Terraform provider integration
ContextForge implements TWO SEPARATE APIs:
1. REST API (Management Operations) - IMPLEMENT IN SDK
- Purpose: Manage tools, resources, servers, gateways, prompts, agents, teams
- Authentication: JWT bearer tokens
- Examples:
GET /tools,POST /resources,PUT /servers/{id}
2. JSON-RPC API (MCP Protocol) - DO NOT IMPLEMENT
- Purpose: MCP protocol compliance for client communication
- Endpoint:
POST /rpc(single endpoint for all JSON-RPC methods) - Methods:
initialize,tools/list,tools/call,resources/read, etc.
3. SSE/Streaming Endpoints - DO NOT IMPLEMENT
GET /servers/{id}/sse- SSE connection for MCP protocol transportPOST /resources/subscribe/{id}- SSE streaming for change notifications
go-contextforge/
├── contextforge/ # Main SDK package
│ ├── contextforge.go # Client, NewClient(), request handling
│ ├── types.go # All type definitions
│ ├── pointers.go # Pointer helper functions
│ ├── errors.go # Error types and handling
│ ├── version.go # SDK version constant
│ ├── doc.go # Package documentation
│ ├── tools.go # ToolsService
│ ├── resources.go # ResourcesService
│ ├── gateways.go # GatewaysService
│ ├── servers.go # ServersService
│ ├── prompts.go # PromptsService
│ ├── agents.go # AgentsService
│ ├── teams.go # TeamsService
│ └── *_test.go # Unit tests for each file
├── test/integration/ # Integration tests
│ ├── helpers.go # Test utilities and cleanup
│ └── *_integration_test.go
├── examples/ # Working examples for each service
├── docs/ # Additional documentation
│ ├── upstream-bugs/ # Known API bug reports
│ └── *.md # Guides and references
└── scripts/ # Build and release scripts
When implementing new services for additional ContextForge API resources:
- Create service file:
contextforge/<service>.go - Define service struct:
type <Service>Service service - Add service field to
Clientstruct intypes.go - Initialize service in
newClient()incontextforge.go:c.<Service> = (*<Service>Service)(&c.common) - Implement service methods with signature:
(ctx context.Context, ...) (*ReturnType, *Response, error) - Add unit tests in
contextforge/<service>_test.go - Add integration tests in
test/integration/<service>_integration_test.go - Add helper functions to
test/integration/helpers.gofor test data generation and cleanup
Follow existing service implementations as patterns:
- ToolsService - Wrapped create/update, nested toggle response
- ResourcesService - Wrapped create, unwrapped update
- GatewaysService - Complex types with authentication fields
- ServersService - Direct toggle response, association endpoints
- PromptsService - String IDs, API case inconsistencies
- AgentsService - Offset-based pagination, A2A protocol
- TeamsService - Offset-based pagination, wrapped list response
Located in contextforge/*_test.go:
- Use
httptest.NewServerfor HTTP mocking - Table-driven tests for most functions
- Setup/teardown helpers:
setup(),testMethod(),testURLParseError(),testJSONMarshal() - Test edge cases, error scenarios, and happy paths
Located in test/integration/*_test.go:
- Build tag:
//go:build integration(required for all integration test files) - Run against real ContextForge gateway
- Environment configuration:
INTEGRATION_TESTS=true(required to run)CONTEXTFORGE_ADDR(default:http://localhost:8000/)CONTEXTFORGE_ADMIN_EMAIL(default:admin@test.local)CONTEXTFORGE_ADMIN_PASSWORD(default:testpassword123)
- JWT authentication via login endpoint
- Automatic cleanup using
t.Cleanup()
Some integration tests are skipped due to confirmed bugs in the upstream ContextForge API (confirmed in both v0.8.0 and v1.0.0-BETA-1). See docs/upstream-bugs/ for detailed bug reports.
| Bug ID | Summary | Skipped Tests |
|---|---|---|
| CONTEXTFORGE-001 | Toggle endpoints return stale state | 4 tests |
| CONTEXTFORGE-002 | API accepts empty template string | 1 test |
| CONTEXTFORGE-003 | Toggle returns 400 instead of 404 | 1 test |
| CONTEXTFORGE-005 | Teams slug field ignored | 2 tests |
| CONTEXTFORGE-007 | Gateway tags not persisted | 2 tests |
| CONTEXTFORGE-008 | Agent bearer auth requires auth_token | 1 test |
| CONTEXTFORGE-009 | Tag filtering returns empty results | 5 tests |
| CONTEXTFORGE-010 | Team ID filter returns 403 | 1 test |
Resolved Issues (tests re-enabled):
| Bug ID | Summary | Resolution |
|---|---|---|
| CONTEXTFORGE-004 | Teams endpoints reject valid auth | Fixed in ContextForge v1.0.0-RC1 |
| CONTEXTFORGE-006 | 422 for validation errors | By design (FastAPI standard) |
To re-enable a skipped test once the upstream bug is fixed:
- Verify the bug is fixed in the ContextForge version you're testing against
- Remove the
t.Skip()line from the test - Run the test to confirm it passes
- Update the bug report in
docs/upstream-bugs/with resolution details
Run make help to see all available targets.
Unit Tests:
make test # Run unit tests
make test-verbose # Run unit tests with verbose output
make test-cover # Run unit tests with coverage
make coverage # Generate HTML coverage reportIntegration Tests:
make integration-test-all # Full cycle: setup, test, teardown
make integration-test-setup # Start ContextForge gateway
make integration-test # Run integration tests
make integration-test-teardown # Stop gateway
make test-all # Run both unit and integration testsBuilding:
make build # Build all packages
make build-all # Build packages + examples
make examples # Build all example programsCode Quality:
make fmt # Format code with gofmt
make vet # Run go vet
make lint # Format + vet
make check # Lint + test
make ci # Full CI pipeline (deps, lint, test, build)Dependencies:
make deps # Download dependencies
make tidy # Tidy go.mod and go.sum
make update-deps # Update dependencies to latest versions
make clean # Clean build artifacts and test cacheThis is an SDK library, not a standalone application. To use it:
import "github.com/leefowlercu/go-contextforge/contextforge"
client, err := contextforge.NewClient(nil, "http://localhost:8000/", "your-jwt-token")Examples: Run any example program to see SDK usage:
go run examples/tools/main.go
go run examples/prompts/main.go
go run examples/agents/main.goThe project uses semantic versioning with automated release tooling.
Prerequisites:
- GoReleaser:
go install github.com/goreleaser/goreleaser/v2@latest - GitHub Token: Set
GITHUB_TOKENenvironment variable
Release Commands:
make release-patch # 0.1.0 → 0.1.1 (bug fixes)
make release-minor # 0.1.0 → 0.2.0 (new features)
make release-major # 0.1.0 → 1.0.0 (breaking changes)
make release-prep VERSION=vX.Y.Z # Manual version
make goreleaser-check # Validate GoReleaser config
make goreleaser-snapshot # Test release locallyRelease Process:
- Checks git working directory is clean
- Updates
contextforge/version.gowith new version - Creates commit:
release: prepare vX.Y.Z - Creates annotated git tag
- Runs GoReleaser to update CHANGELOG.md and create draft GitHub release
- Review and publish the draft release
Undoing a release (before pushing):
git tag -d vX.Y.Z
git reset --hard HEAD~1