ddg-search-mcp - #10
Conversation
83d4af6 to
b760db5
Compare
Add MCP configuration management Add MCP logging infrastructure Add MCP signal handling for graceful shutdown Add OpenSpec change documentation for MCP server Update Go dependencies for MCP server Add MCP server tests Update MCP server implementation Update OpenSpec tasks documentation Refactor Perplexity client implementation Update MCP server tools implementation Update MCP server core Update command implementations Update OpenSpec documentation Fix SSE endpoint routing and add streaming support Add HTTP SSE transport tests Update Perplexity integration Update E2E tests and main command Add SSE test plan and update tasks docs: update manual test plans for /mcp endpoint migration Update stage8, stage9, and stage10 manual test plans to reflect the new Streamable HTTP transport endpoint (/mcp) instead of the deprecated /sse and /message endpoints. Changes: - stage8-manual-test-plan.md: Updated all test cases to use /mcp endpoint with POST for JSON-RPC requests and GET with Accept: text/event-stream for SSE connections. Removed session ID handling as it's no longer needed in the new transport. - stage9-manual-test-plan.md: Updated TLS/mTLS test cases to use /mcp endpoint with GET and Accept: text/event-stream header. - stage10-manual-test-plan.md: Verified as already correct for new transport. Fix README inconsistencies Archive ddg-search-mcp-server change and sync specs Add TLS tests and archive ddg-search-mcp-server change Update MCP server implementation Update mise configuration Update config test file Update dump test file Refactor search test files to internal
b760db5 to
07364bd
Compare
|
| streamableHTTPServer *server.StreamableHTTPServer | ||
| logger *slog.Logger | ||
| config *Config | ||
| appConfig any // Holds full application configuration (mcpconfig.Config) |
There was a problem hiding this comment.
appConfig any breaks type safety.
The entire TLS configuration path in buildTLSConfig() (lines 437-503) uses a chain of interface type assertions (tlsConfigGetter, tlsFields) to extract config fields.
Similarly, ReloadTLS() (lines 164-213) does the same.
The Server struct doesn't know what its config is, yet deeply depends on
its shape.
Recommendation: Define an explicit interface that mcpconfig.Config satisfies:
type AppConfig interface {k
GetTLSConfig() *TLSSettings // concrete type, not any
}
Or simply accept *mcpconfig.Config directly — these packages are in the same module.
| } | ||
|
|
||
| // GetEnabled returns whether TLS is enabled. | ||
| func (t *TLSConfig) GetEnabled() bool { |
There was a problem hiding this comment.
This adds 6 getter methods (GetEnabled, GetCertFile, GetKeyFile, GetMinVersion, GetMTLSEnabled, GetMTLSCAFile) on an already-exported struct with exported fields.
These exist solely to satisfy the inline interfaces in server.go. If the types were used directly, these would be unnecessary.
| serverCtx, serverCancel := context.WithCancel(context.Background()) | ||
|
|
||
| go func() { | ||
| defer serverCancel() |
There was a problem hiding this comment.
The serverCancel is only called when Serve returns.
But in waitForShutdown (line 173), when shutdown signal arrives, nothing cancels serverCtx — the code just waits on shutdown.Wait().
The HTTP server is never told to stop. The Serve method does
select { case <-ctx.Done(): ... }
but ctx is serverCtx, which is never cancelled from outside.
Recommendation: Cancel serverCtx when shutdown is initiated.
| case <-shutdown.ShutdownChan(): | ||
| logger.Info("Shutting down...") | ||
| case err := <-serverErr: | ||
| if err != nil && err.Error() != "MCP server error: context canceled" { |
There was a problem hiding this comment.
This is fragile string matching. Use errors.Is(err, context.Canceled) or a sentinel error.
| defer h.mu.Unlock() | ||
|
|
||
| select { | ||
| case <-h.shutdownChan: |
There was a problem hiding this comment.
Both branches return context.Background(). This method never returns a context that gets cancelled on shutdown.
If the intent is to provide a cancellable context, it should use context.WithCancel tied to the shutdown channel.
Cancelling serverCtx on shutdown solves the shutdown problem without needing this method.
| v.SetConfigFile(configFilePath) | ||
| } else { | ||
| // File doesn't exist, skip config file loading | ||
| skipConfigFile = true |
There was a problem hiding this comment.
When DDG_SEARCH_CONFIG_FILE points to a non-existent file, the code silently skips it and uses defaults.
If a user explicitly sets config file, they expect that file to be used. Silently falling back to defaults hides misconfiguration
| logger *slog.Logger, | ||
| ) { | ||
| go reloadableCfg.WatchSignalsWithCallback(shutdown.ShutdownChan(), func() { | ||
| currentCfg := reloadableCfg.Get() |
There was a problem hiding this comment.
After SIGHUP reload, the callback reads currentCfg but never updates the server’s appConfig pointer.
Since tools and ReloadTLS() read configuration from mcpServer state, they keep using the initial config loaded at startup, so runtime config changes (e.g., Perplexity enable/token, search defaults, TLS file path changes) are silently ignored.
| func (r *ReloadableConfig) Reload() error { | ||
| r.logger.Info("Reloading configuration") | ||
|
|
||
| newCfg, err := Load() |
There was a problem hiding this comment.
This reads default locations/environment, even when startup used --config and LoadFromFile(...).
In that case, SIGHUP reload pulls a different config source, so services launched with a custom config file can unexpectedly revert to defaults or unrelated settings.
Also note: --log-level override is currently startup-only and will also be lost on reload.
| // Build TLS config | ||
| config := &tls.Config{ | ||
| Certificates: []tls.Certificate{cert}, | ||
| MinVersion: tls.VersionTLS12, // Minimum TLS 1.2 |
There was a problem hiding this comment.
This hardcodes MinVersion: tls.VersionTLS12 and never uses the configured server.tls.min_version value, even though the interface exposes GetMinVersion().
| // Make the request | ||
| resp, err := c.Do(ctx, c.httpClient.R(). | ||
| SetBody(reqBody). | ||
| SetContext(ctx). |
There was a problem hiding this comment.
Can be removed because Do() at client.go:63 calls req.SetContext(ctx) again.
No description provided.