Skip to content

Commit da58750

Browse files
Add proxy to docs mcp server (#21)
1 parent 237c602 commit da58750

File tree

5 files changed

+476
-32
lines changed

5 files changed

+476
-32
lines changed

internal/tiger/cmd/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ func outputTable(cfg *config.Config, cmd *cobra.Command) error {
136136
fmt.Fprintf(out, " API URL: %s\n", cfg.APIURL)
137137
fmt.Fprintf(out, " Console URL: %s\n", cfg.ConsoleURL)
138138
fmt.Fprintf(out, " Gateway URL: %s\n", cfg.GatewayURL)
139+
fmt.Fprintf(out, " Docs MCP: %t\n", cfg.DocsMCP)
140+
fmt.Fprintf(out, " Docs MCP URL: %s\n", cfg.DocsMCPURL)
139141
fmt.Fprintf(out, " Project ID: %s\n", valueOrEmpty(cfg.ProjectID))
140142
fmt.Fprintf(out, " Service ID: %s\n", valueOrEmpty(cfg.ServiceID))
141143
fmt.Fprintf(out, " Output: %s\n", cfg.Output)
@@ -151,6 +153,8 @@ func outputJSON(cfg *config.Config, cmd *cobra.Command) error {
151153
"api_url": cfg.APIURL,
152154
"console_url": cfg.ConsoleURL,
153155
"gateway_url": cfg.GatewayURL,
156+
"docs_mcp": cfg.DocsMCP,
157+
"docs_mcp_url": cfg.DocsMCPURL,
154158
"project_id": cfg.ProjectID,
155159
"service_id": cfg.ServiceID,
156160
"output": cfg.Output,
@@ -170,6 +174,8 @@ func outputYAML(cfg *config.Config, cmd *cobra.Command) error {
170174
"api_url": cfg.APIURL,
171175
"console_url": cfg.ConsoleURL,
172176
"gateway_url": cfg.GatewayURL,
177+
"docs_mcp": cfg.DocsMCP,
178+
"docs_mcp_url": cfg.DocsMCPURL,
173179
"project_id": cfg.ProjectID,
174180
"service_id": cfg.ServiceID,
175181
"output": cfg.Output,

internal/tiger/cmd/mcp.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,19 @@ func startStdioServer(ctx context.Context) error {
145145
defer stop()
146146

147147
// Create MCP server
148-
server, err := mcp.NewServer()
148+
server, err := mcp.NewServer(ctx)
149149
if err != nil {
150150
return fmt.Errorf("failed to create MCP server: %w", err)
151151
}
152152

153153
// Start the stdio transport
154154
if err := server.StartStdio(ctx); !errors.Is(err, context.Canceled) {
155-
return err
155+
return fmt.Errorf("failed to start MCP server: %w", err)
156+
}
157+
158+
// Close the MCP server when finished
159+
if err := server.Close(); err != nil {
160+
return fmt.Errorf("failed to close MCP server: %w", err)
156161
}
157162
return nil
158163
}
@@ -161,16 +166,16 @@ func startStdioServer(ctx context.Context) error {
161166
func startHTTPServer(ctx context.Context, host string, port int) error {
162167
logging.Info("Starting Tiger MCP server", zap.String("transport", "http"))
163168

169+
// Setup graceful shutdown handling
170+
ctx, stop := signalContext(ctx)
171+
defer stop()
172+
164173
// Create MCP server
165-
server, err := mcp.NewServer()
174+
server, err := mcp.NewServer(ctx)
166175
if err != nil {
167176
return fmt.Errorf("failed to create MCP server: %w", err)
168177
}
169178

170-
// Setup graceful shutdown handling
171-
ctx, stop := signalContext(ctx)
172-
defer stop()
173-
174179
// Find available port and get the listener
175180
listener, actualPort, err := getListener(host, port)
176181
if err != nil {
@@ -181,7 +186,8 @@ func startHTTPServer(ctx context.Context, host string, port int) error {
181186
if actualPort != port {
182187
logging.Info("Specified port was busy, using alternative port",
183188
zap.Int("requested_port", port),
184-
zap.Int("actual_port", actualPort))
189+
zap.Int("actual_port", actualPort),
190+
)
185191
}
186192

187193
address := fmt.Sprintf("%s:%d", host, actualPort)
@@ -212,7 +218,15 @@ func startHTTPServer(ctx context.Context, host string, port int) error {
212218

213219
// Shutdown server gracefully
214220
logging.Info("Shutting down HTTP server...")
215-
return httpServer.Shutdown(context.Background())
221+
if err := httpServer.Shutdown(context.Background()); err != nil {
222+
return fmt.Errorf("failed to shut down HTTP server: %w", err)
223+
}
224+
225+
// Close the MCP server when finished
226+
if err := server.Close(); err != nil {
227+
return fmt.Errorf("failed to close MCP server: %w", err)
228+
}
229+
return nil
216230
}
217231

218232
// getListener finds an available port starting from the specified port and returns the listener

internal/tiger/config/config.go

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"strconv"
78

89
"github.com/spf13/pflag"
910
"github.com/spf13/viper"
@@ -13,19 +14,23 @@ type Config struct {
1314
APIURL string `mapstructure:"api_url" yaml:"api_url"`
1415
ConsoleURL string `mapstructure:"console_url" yaml:"console_url"`
1516
GatewayURL string `mapstructure:"gateway_url" yaml:"gateway_url"`
17+
DocsMCP bool `mapstructure:"docs_mcp" yaml:"docs_mcp"`
18+
DocsMCPURL string `mapstructure:"docs_mcp_url" yaml:"docs_mcp_url"`
1619
ProjectID string `mapstructure:"project_id" yaml:"project_id"`
1720
ServiceID string `mapstructure:"service_id" yaml:"service_id"`
1821
Output string `mapstructure:"output" yaml:"output"`
1922
Analytics bool `mapstructure:"analytics" yaml:"analytics"`
2023
PasswordStorage string `mapstructure:"password_storage" yaml:"password_storage"`
21-
ConfigDir string `mapstructure:"config_dir" yaml:"-"`
2224
Debug bool `mapstructure:"debug" yaml:"debug"`
25+
ConfigDir string `mapstructure:"config_dir" yaml:"-"`
2326
}
2427

2528
const (
2629
DefaultAPIURL = "https://console.cloud.timescale.com/public/api/v1"
2730
DefaultConsoleURL = "https://console.cloud.timescale.com"
2831
DefaultGatewayURL = "https://console.cloud.timescale.com/api"
32+
DefaultDocsMCP = true
33+
DefaultDocsMCPURL = "https://mcp.tigerdata.com/docs"
2934
DefaultOutput = "table"
3035
DefaultAnalytics = true
3136
DefaultPasswordStorage = "keyring"
@@ -47,6 +52,8 @@ func SetupViper(configDir string) error {
4752
viper.SetDefault("api_url", DefaultAPIURL)
4853
viper.SetDefault("console_url", DefaultConsoleURL)
4954
viper.SetDefault("gateway_url", DefaultGatewayURL)
55+
viper.SetDefault("docs_mcp", DefaultDocsMCP)
56+
viper.SetDefault("docs_mcp_url", DefaultDocsMCPURL)
5057
viper.SetDefault("project_id", "")
5158
viper.SetDefault("service_id", "")
5259
viper.SetDefault("output", DefaultOutput)
@@ -96,6 +103,8 @@ func (c *Config) Save() error {
96103
viper.Set("api_url", c.APIURL)
97104
viper.Set("console_url", c.ConsoleURL)
98105
viper.Set("gateway_url", c.GatewayURL)
106+
viper.Set("docs_mcp", c.DocsMCP)
107+
viper.Set("docs_mcp_url", c.DocsMCPURL)
99108
viper.Set("project_id", c.ProjectID)
100109
viper.Set("service_id", c.ServiceID)
101110
viper.Set("output", c.Output)
@@ -118,6 +127,14 @@ func (c *Config) Set(key, value string) error {
118127
c.ConsoleURL = value
119128
case "gateway_url":
120129
c.GatewayURL = value
130+
case "docs_mcp":
131+
b, err := setBool("docs_mcp", value)
132+
if err != nil {
133+
return err
134+
}
135+
c.DocsMCP = b
136+
case "docs_mcp_url":
137+
c.DocsMCPURL = value
121138
case "project_id":
122139
c.ProjectID = value
123140
case "service_id":
@@ -128,33 +145,37 @@ func (c *Config) Set(key, value string) error {
128145
}
129146
c.Output = value
130147
case "analytics":
131-
if value == "true" {
132-
c.Analytics = true
133-
} else if value == "false" {
134-
c.Analytics = false
135-
} else {
136-
return fmt.Errorf("invalid analytics value: %s (must be true or false)", value)
137-
}
138-
case "debug":
139-
if value == "true" {
140-
c.Debug = true
141-
} else if value == "false" {
142-
c.Debug = false
143-
} else {
144-
return fmt.Errorf("invalid debug value: %s (must be true or false)", value)
148+
b, err := setBool("analytics", value)
149+
if err != nil {
150+
return err
145151
}
152+
c.Analytics = b
146153
case "password_storage":
147154
if value != "keyring" && value != "pgpass" && value != "none" {
148155
return fmt.Errorf("invalid password_storage value: %s (must be keyring, pgpass, or none)", value)
149156
}
150157
c.PasswordStorage = value
158+
case "debug":
159+
b, err := setBool("debug", value)
160+
if err != nil {
161+
return err
162+
}
163+
c.Debug = b
151164
default:
152165
return fmt.Errorf("unknown configuration key: %s", key)
153166
}
154167

155168
return c.Save()
156169
}
157170

171+
func setBool(key, val string) (bool, error) {
172+
b, err := strconv.ParseBool(val)
173+
if err != nil {
174+
return false, fmt.Errorf("invalid %s value: %s (must be true or false)", key, val)
175+
}
176+
return b, nil
177+
}
178+
158179
func (c *Config) Unset(key string) error {
159180
switch key {
160181
case "api_url":
@@ -163,6 +184,10 @@ func (c *Config) Unset(key string) error {
163184
c.ConsoleURL = DefaultConsoleURL
164185
case "gateway_url":
165186
c.GatewayURL = DefaultGatewayURL
187+
case "docs_mcp":
188+
c.DocsMCP = DefaultDocsMCP
189+
case "docs_mcp_url":
190+
c.DocsMCPURL = DefaultDocsMCPURL
166191
case "project_id":
167192
c.ProjectID = ""
168193
case "service_id":
@@ -171,10 +196,10 @@ func (c *Config) Unset(key string) error {
171196
c.Output = DefaultOutput
172197
case "analytics":
173198
c.Analytics = DefaultAnalytics
174-
case "debug":
175-
c.Debug = DefaultDebug
176199
case "password_storage":
177200
c.PasswordStorage = DefaultPasswordStorage
201+
case "debug":
202+
c.Debug = DefaultDebug
178203
default:
179204
return fmt.Errorf("unknown configuration key: %s", key)
180205
}
@@ -186,6 +211,8 @@ func (c *Config) Reset() error {
186211
c.APIURL = DefaultAPIURL
187212
c.ConsoleURL = DefaultConsoleURL
188213
c.GatewayURL = DefaultGatewayURL
214+
c.DocsMCP = DefaultDocsMCP
215+
c.DocsMCPURL = DefaultDocsMCPURL
189216
c.ProjectID = ""
190217
c.ServiceID = ""
191218
c.Output = DefaultOutput

0 commit comments

Comments
 (0)