You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(mcp): HTTP header support and runtime deps for stdio MCPs (#16)
Two related features for remote/local MCP server support:
1. HTTP headers for MCP HTTP upstreams
- Add `Headers map[string]string` to MCPUpstreamRow/Opts/UpstreamConfig
- New DB migration adds nullable `headers` JSON column to mcp_upstreams
- HTTPUpstream applies configured headers on every request (Send + Stop)
- CLI: `sluice mcp add --header "KEY=VAL"` (repeatable via flag.Func)
- Reject --header on non-HTTP transports to fail loud on misuse
2. Vault template substitution in env/header values
- New `{vault:<name>}` substring template form, alongside the existing
whole-value `vault:<name>` prefix form
- Shared via resolveVaultMap helper for both env and header values
- Enables e.g. `--header "Authorization=Bearer {vault:github_pat}"`
- Binding templates keep their `{value}` syntax (binding has an
implicit single credential); doc comment explains the difference
3. Runtime dependencies in Docker image
- Install nodejs + npm (npx) and python3 to the alpine image
- Download uv/uvx as static musl binaries from astral.sh (pipx on
alpine installs to /root/.local/bin which the sluice user cannot
access; direct binary install avoids that)
- Enables stdio MCP servers distributed via npx or uvx to run inside
the sluice container without a separate sidecar
Image size grows from ~30MB to ~220MB (nodejs+python+uv).
Tests cover: whole-value form, template form, multiple substitutions,
missing credential errors, for both env and headers.
envStr:=fs.String("env", "", "comma-separated KEY=VAL environment variables (VAL may be vault:<name> for the whole value, or contain {vault:<name>} substrings for templated substitution)")
255
256
timeout:=fs.Int("timeout", 120, "upstream timeout in seconds")
256
257
transport:=fs.String("transport", "stdio", "transport type: stdio, http, or websocket")
258
+
headers:=make(map[string]string)
259
+
fs.Func("header", "HTTP header to send on every request to an http upstream (repeatable, format: KEY=VAL; VAL may be vault:<name> for the whole value, or contain {vault:<name>} substrings for templated substitution, e.g. \"Authorization=Bearer {vault:github_pat}\")", func(sstring) error {
260
+
parts:=strings.SplitN(s, "=", 2)
261
+
iflen(parts) !=2 {
262
+
returnfmt.Errorf("invalid header format %q (expected KEY=VAL)", s)
0 commit comments