Skip to content

Commit 89b6d81

Browse files
authored
feat: Add CLAUDE.md and abstract MCP tool builder checker (#54)
1 parent ef77aba commit 89b6d81

File tree

4 files changed

+135
-5
lines changed

4 files changed

+135
-5
lines changed

CLAUDE.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build Commands
6+
7+
### Build the server binary
8+
```bash
9+
make build
10+
# Or directly:
11+
go build -o bin/snmcp cmd/streamnative-mcp-server/main.go
12+
```
13+
14+
### Run tests
15+
```bash
16+
go test -race ./...
17+
# Run a specific test:
18+
go test -race ./pkg/mcp/builders/...
19+
```
20+
21+
### Docker operations
22+
```bash
23+
make docker-build # Build local Docker image
24+
make docker-build-push # Build and push multi-platform image
25+
```
26+
27+
### License checking
28+
```bash
29+
make license-check # Check license headers
30+
make license-fix # Fix license headers
31+
```
32+
33+
## Architecture Overview
34+
35+
The StreamNative MCP Server implements the Model Context Protocol to enable AI agents to interact with Apache Kafka, Apache Pulsar, and StreamNative Cloud resources.
36+
37+
### Core Components
38+
39+
1. **Session Management** (`pkg/config/`, `pkg/kafka/`, `pkg/pulsar/`)
40+
- Three types of sessions: SNCloudSession, KafkaSession, PulsarSession
41+
- Sessions manage client connections and authentication
42+
- Sessions are created and configured based on command-line flags
43+
44+
2. **MCP Server** (`pkg/mcp/`)
45+
- Central server implementation using `mark3labs/mcp-go` library
46+
- Handles tool registration and request routing
47+
- Features can be enabled/disabled via `--features` flag
48+
49+
3. **Tool Builders** (`pkg/mcp/builders/`)
50+
- Registry pattern for registering MCP tools
51+
- Separate builders for Kafka and Pulsar operations
52+
- Each builder creates tools with specific operations (admin, client, etc.)
53+
54+
4. **PFTools** (`pkg/mcp/pftools/`)
55+
- Abstraction layer for Pulsar Functions as MCP tools
56+
- Dynamic tool generation from deployed Pulsar Functions
57+
- Circuit breaker pattern for resilience
58+
- Schema handling for input/output validation
59+
60+
### Key Design Patterns
61+
62+
1. **Builder Pattern**: Tool builders (`pkg/mcp/builders/`) register tools dynamically based on enabled features
63+
2. **Session Pattern**: Separate sessions for different services (Kafka, Pulsar, SNCloud) with lazy initialization
64+
3. **Registry Pattern**: Central registry (`pkg/mcp/builders/registry.go`) manages all tool builders
65+
4. **Circuit Breaker**: Used in PFTools for handling function invocation failures
66+
67+
## Development Guidelines
68+
69+
### Adding New Tools
70+
71+
1. Create a new builder in `pkg/mcp/builders/kafka/` or `pkg/mcp/builders/pulsar/`
72+
2. Implement the `Builder` interface with `Build()` method
73+
3. Register the builder in the appropriate tools file (e.g., `kafka_admin_*_tools.go`)
74+
4. Add feature flag support in `pkg/mcp/features.go`
75+
76+
### Session Context
77+
78+
The server maintains session context that gets passed to tools via the context:
79+
- Pulsar admin client retrieval: Use `session.GetAdminClient()` or `session.GetAdminV3Client()`
80+
- Kafka client retrieval: Use `session.GetKafkaSession()` methods
81+
- Always check for nil sessions before use
82+
83+
### Error Handling
84+
85+
- Use wrapped errors with context: `fmt.Errorf("failed to X: %w", err)`
86+
- Check session availability before operations
87+
- Return meaningful error messages for AI agent consumption
88+
89+
## Testing
90+
91+
Tests follow standard Go testing patterns:
92+
- Unit tests alongside source files (`*_test.go`)
93+
- Use `testify` for assertions
94+
- Mock external dependencies where appropriate
95+
96+
## Important Files
97+
98+
- `cmd/streamnative-mcp-server/main.go` - Entry point
99+
- `pkg/cmd/mcp/server.go` - Server setup
100+
- `pkg/mcp/server.go` - MCP server implementation
101+
- `pkg/mcp/builders/registry.go` - Tool registration
102+
- `pkg/mcp/pftools/manager.go` - Pulsar Functions management
103+
- `pkg/config/config.go` - Configuration structures
104+
105+
## Configuration
106+
107+
The server supports three modes:
108+
1. **StreamNative Cloud**: Requires `--organization` and `--key-file`
109+
2. **External Kafka**: Use `--use-external-kafka` with Kafka connection parameters
110+
3. **External Pulsar**: Use `--use-external-pulsar` with Pulsar connection parameters
111+
112+
When `--pulsar-instance` and `--pulsar-cluster` are provided together, context management tools are disabled as the context is pre-configured.

pkg/mcp/builders/base.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ import (
2626
"github.com/mark3labs/mcp-go/server"
2727
)
2828

29+
// FeatureChecker defines the interface for checking feature requirements
30+
// It provides methods to determine if required features are available
31+
type FeatureChecker interface {
32+
// HasAnyRequiredFeature checks if any of the required features are present in the provided list
33+
HasAnyRequiredFeature(features []string) bool
34+
}
35+
2936
// ToolBuilder defines the interface that all tool builders must implement
3037
// It specifies the methods required for building and managing MCP tools
3138
type ToolBuilder interface {
@@ -40,6 +47,9 @@ type ToolBuilder interface {
4047

4148
// Validate validates the builder configuration
4249
Validate(config ToolBuildConfig) error
50+
51+
// Embed FeatureChecker interface
52+
FeatureChecker
4353
}
4454

4555
// ToolBuildConfig contains all configuration information needed to build tools

pkg/mcp/builders/registry.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,8 @@ func (r *ToolRegistry) BuildAllWithFeatures(readOnly bool, features []string) ([
188188
}
189189

190190
// Check if the builder needs these features
191-
// Use interface method instead of type assertion
192-
if featureChecker, ok := builder.(interface{ HasAnyRequiredFeature([]string) bool }); ok {
193-
if !featureChecker.HasAnyRequiredFeature(features) {
194-
continue // Skip builders that don't need these features
195-
}
191+
if !builder.HasAnyRequiredFeature(features) {
192+
continue // Skip builders that don't need these features
196193
}
197194

198195
if err := builder.Validate(config); err != nil {

pkg/mcp/builders/registry_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ func (m *MockToolBuilder) Validate(config ToolBuildConfig) error {
8787
return fmt.Errorf("no matching features found")
8888
}
8989

90+
func (m *MockToolBuilder) HasAnyRequiredFeature(features []string) bool {
91+
for _, required := range m.features {
92+
for _, provided := range features {
93+
if required == provided {
94+
return true
95+
}
96+
}
97+
}
98+
return false
99+
}
100+
90101
func (m *MockToolBuilder) GetMetadata() ToolMetadata {
91102
return m.metadata
92103
}

0 commit comments

Comments
 (0)