Skip to content

Commit 3fa709f

Browse files
refactor(mcp_server): convert to interface-based design
- Hide all concrete types behind interfaces except config structures - Make factory functions return interface types - Create hierarchical schema interface instead of flat schema - Add nolint comments for driver import - Remove unused imports and ensure clean API 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Benevolent General Kroll Who cannot spell <[email protected]>
1 parent 9632c6d commit 3fa709f

File tree

4 files changed

+313
-201
lines changed

4 files changed

+313
-201
lines changed

pkg/mcp_server/backend.go

Lines changed: 186 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package mcp_server
22

33
import (
44
"context"
5-
"database/sql/driver"
5+
"database/sql/driver" //nolint:unused
66
)
77

88
// Backend defines the interface for executing queries from MCP clients.
@@ -11,11 +11,11 @@ import (
1111
type Backend interface {
1212
// Execute runs a query and returns the results.
1313
// The query string and parameters are provided by the MCP client.
14-
Execute(ctx context.Context, query string, params map[string]interface{}) (*QueryResult, error)
14+
Execute(ctx context.Context, query string, params map[string]interface{}) (QueryResult, error)
1515

1616
// GetSchema returns metadata about available resources and their structure.
1717
// This is used by MCP clients to understand what data is available.
18-
GetSchema(ctx context.Context) (*Schema, error)
18+
GetSchema(ctx context.Context) (SchemaProvider, error)
1919

2020
// Ping verifies the backend connection is active.
2121
Ping(ctx context.Context) error
@@ -25,84 +25,84 @@ type Backend interface {
2525
}
2626

2727
// QueryResult represents the result of a query execution.
28-
type QueryResult struct {
29-
// Columns contains metadata about each column in the result set.
30-
Columns []ColumnInfo `json:"columns"`
28+
type QueryResult interface {
29+
// GetColumns returns metadata about each column in the result set.
30+
GetColumns() []ColumnInfo
3131

32-
// Rows contains the actual data returned by the query.
33-
Rows [][]interface{} `json:"rows"`
32+
// GetRows returns the actual data returned by the query.
33+
GetRows() [][]interface{}
3434

35-
// RowsAffected indicates the number of rows affected by DML operations.
36-
RowsAffected int64 `json:"rows_affected"`
35+
// GetRowsAffected returns the number of rows affected by DML operations.
36+
GetRowsAffected() int64
3737

38-
// ExecutionTime is the time taken to execute the query in milliseconds.
39-
ExecutionTime int64 `json:"execution_time_ms"`
38+
// GetExecutionTime returns the time taken to execute the query in milliseconds.
39+
GetExecutionTime() int64
4040
}
4141

4242
// ColumnInfo provides metadata about a result column.
43-
type ColumnInfo struct {
44-
// Name is the column name as returned by the query.
45-
Name string `json:"name"`
43+
type ColumnInfo interface {
44+
// GetName returns the column name as returned by the query.
45+
GetName() string
4646

47-
// Type is the data type of the column (e.g., "string", "int64", "float64").
48-
Type string `json:"type"`
47+
// GetType returns the data type of the column (e.g., "string", "int64", "float64").
48+
GetType() string
4949

50-
// Nullable indicates whether the column can contain null values.
51-
Nullable bool `json:"nullable"`
50+
// IsNullable indicates whether the column can contain null values.
51+
IsNullable() bool
5252
}
5353

54-
// Schema represents the metadata structure of available resources.
55-
type Schema struct {
56-
// Providers lists all available providers (e.g., aws, google, azure).
57-
Providers []Provider `json:"providers"`
54+
// SchemaProvider represents the metadata structure of available resources.
55+
type SchemaProvider interface {
56+
// GetProviders returns all available providers (e.g., aws, google, azure).
57+
GetProviders() []Provider
5858
}
5959

6060
// Provider represents a StackQL provider with its services and resources.
61-
type Provider struct {
62-
// Name is the provider identifier (e.g., "aws", "google").
63-
Name string `json:"name"`
61+
type Provider interface {
62+
// GetName returns the provider identifier (e.g., "aws", "google").
63+
GetName() string
6464

65-
// Version is the provider version.
66-
Version string `json:"version"`
65+
// GetVersion returns the provider version.
66+
GetVersion() string
6767

68-
// Services lists all services available in this provider.
69-
Services []Service `json:"services"`
68+
// GetServices returns all services available in this provider.
69+
GetServices() []Service
7070
}
7171

7272
// Service represents a service within a provider.
73-
type Service struct {
74-
// Name is the service identifier (e.g., "ec2", "compute").
75-
Name string `json:"name"`
73+
type Service interface {
74+
// GetName returns the service identifier (e.g., "ec2", "compute").
75+
GetName() string
7676

77-
// Resources lists all resources available in this service.
78-
Resources []Resource `json:"resources"`
77+
// GetResources returns all resources available in this service.
78+
GetResources() []Resource
7979
}
8080

8181
// Resource represents a queryable resource.
82-
type Resource struct {
83-
// Name is the resource identifier (e.g., "instances", "buckets").
84-
Name string `json:"name"`
82+
type Resource interface {
83+
// GetName returns the resource identifier (e.g., "instances", "buckets").
84+
GetName() string
8585

86-
// Methods lists the available operations for this resource.
87-
Methods []string `json:"methods"`
86+
// GetMethods returns the available operations for this resource.
87+
GetMethods() []string
8888

89-
// Fields describes the available fields in this resource.
90-
Fields []Field `json:"fields"`
89+
// GetFields returns the available fields in this resource.
90+
GetFields() []Field
9191
}
9292

9393
// Field represents a field within a resource.
94-
type Field struct {
95-
// Name is the field identifier.
96-
Name string `json:"name"`
94+
type Field interface {
95+
// GetName returns the field identifier.
96+
GetName() string
9797

98-
// Type is the field data type.
99-
Type string `json:"type"`
98+
// GetType returns the field data type.
99+
GetType() string
100100

101-
// Required indicates if this field is mandatory for certain operations.
102-
Required bool `json:"required"`
101+
// IsRequired indicates if this field is mandatory for certain operations.
102+
IsRequired() bool
103103

104-
// Description provides human-readable documentation for the field.
105-
Description string `json:"description,omitempty"`
104+
// GetDescription returns human-readable documentation for the field.
105+
GetDescription() string
106106
}
107107

108108
// BackendError represents an error that occurred in the backend.
@@ -122,6 +122,140 @@ func (e *BackendError) Error() string {
122122
}
123123

124124
// Ensure BackendError implements the driver.Valuer interface for database compatibility
125-
func (e *BackendError) Value() (driver.Value, error) {
125+
func (e *BackendError) Value() (driver.Value, error) { //nolint:unused
126126
return e.Message, nil
127+
}
128+
129+
// Private implementations of interfaces
130+
131+
type queryResult struct {
132+
Columns []ColumnInfo `json:"columns"`
133+
Rows [][]interface{} `json:"rows"`
134+
RowsAffected int64 `json:"rows_affected"`
135+
ExecutionTime int64 `json:"execution_time_ms"`
136+
}
137+
138+
func (qr *queryResult) GetColumns() []ColumnInfo { return qr.Columns }
139+
func (qr *queryResult) GetRows() [][]interface{} { return qr.Rows }
140+
func (qr *queryResult) GetRowsAffected() int64 { return qr.RowsAffected }
141+
func (qr *queryResult) GetExecutionTime() int64 { return qr.ExecutionTime }
142+
143+
type columnInfo struct {
144+
Name string `json:"name"`
145+
Type string `json:"type"`
146+
Nullable bool `json:"nullable"`
147+
}
148+
149+
func (ci *columnInfo) GetName() string { return ci.Name }
150+
func (ci *columnInfo) GetType() string { return ci.Type }
151+
func (ci *columnInfo) IsNullable() bool { return ci.Nullable }
152+
153+
type schemaProvider struct {
154+
Providers []Provider `json:"providers"`
155+
}
156+
157+
func (sp *schemaProvider) GetProviders() []Provider { return sp.Providers }
158+
159+
type provider struct {
160+
Name string `json:"name"`
161+
Version string `json:"version"`
162+
Services []Service `json:"services"`
163+
}
164+
165+
func (p *provider) GetName() string { return p.Name }
166+
func (p *provider) GetVersion() string { return p.Version }
167+
func (p *provider) GetServices() []Service { return p.Services }
168+
169+
type service struct {
170+
Name string `json:"name"`
171+
Resources []Resource `json:"resources"`
172+
}
173+
174+
func (s *service) GetName() string { return s.Name }
175+
func (s *service) GetResources() []Resource { return s.Resources }
176+
177+
type resource struct {
178+
Name string `json:"name"`
179+
Methods []string `json:"methods"`
180+
Fields []Field `json:"fields"`
181+
}
182+
183+
func (r *resource) GetName() string { return r.Name }
184+
func (r *resource) GetMethods() []string { return r.Methods }
185+
func (r *resource) GetFields() []Field { return r.Fields }
186+
187+
type field struct {
188+
Name string `json:"name"`
189+
Type string `json:"type"`
190+
Required bool `json:"required"`
191+
Description string `json:"description,omitempty"`
192+
}
193+
194+
func (f *field) GetName() string { return f.Name }
195+
func (f *field) GetType() string { return f.Type }
196+
func (f *field) IsRequired() bool { return f.Required }
197+
func (f *field) GetDescription() string { return f.Description }
198+
199+
// Factory functions
200+
201+
// NewQueryResult creates a new QueryResult instance.
202+
func NewQueryResult(columns []ColumnInfo, rows [][]interface{}, rowsAffected, executionTime int64) QueryResult {
203+
return &queryResult{
204+
Columns: columns,
205+
Rows: rows,
206+
RowsAffected: rowsAffected,
207+
ExecutionTime: executionTime,
208+
}
209+
}
210+
211+
// NewColumnInfo creates a new ColumnInfo instance.
212+
func NewColumnInfo(name, colType string, nullable bool) ColumnInfo {
213+
return &columnInfo{
214+
Name: name,
215+
Type: colType,
216+
Nullable: nullable,
217+
}
218+
}
219+
220+
// NewSchemaProvider creates a new SchemaProvider instance.
221+
func NewSchemaProvider(providers []Provider) SchemaProvider {
222+
return &schemaProvider{
223+
Providers: providers,
224+
}
225+
}
226+
227+
// NewProvider creates a new Provider instance.
228+
func NewProvider(name, version string, services []Service) Provider {
229+
return &provider{
230+
Name: name,
231+
Version: version,
232+
Services: services,
233+
}
234+
}
235+
236+
// NewService creates a new Service instance.
237+
func NewService(name string, resources []Resource) Service {
238+
return &service{
239+
Name: name,
240+
Resources: resources,
241+
}
242+
}
243+
244+
// NewResource creates a new Resource instance.
245+
func NewResource(name string, methods []string, fields []Field) Resource {
246+
return &resource{
247+
Name: name,
248+
Methods: methods,
249+
Fields: fields,
250+
}
251+
}
252+
253+
// NewField creates a new Field instance.
254+
func NewField(name, fieldType string, required bool, description string) Field {
255+
return &field{
256+
Name: name,
257+
Type: fieldType,
258+
Required: required,
259+
Description: description,
260+
}
127261
}

0 commit comments

Comments
 (0)