Plan 6 is now deployed; replace the _routing_pending placeholder in the routing MCP entry with a real headers block carrying X-Hyperguild-Mode: client-local. The pod treats absent or unknown values as client-local, so this is forward-compat for future modes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
func runMode(ctx context.Context, args []string, _ io.Reader, stdout, stderr io.Writer) error {
|
|
fs := flag.NewFlagSet("mode", flag.ContinueOnError)
|
|
fs.SetOutput(stderr)
|
|
out := fs.String("out", ".mcp.json", "output file path")
|
|
force := fs.Bool("force", false, "overwrite an existing file")
|
|
// Pull the first positional (mode name) out so flags after it still parse
|
|
// with stdlib flag (which stops at the first non-flag arg).
|
|
if len(args) < 1 {
|
|
return errors.New("name required (cloud|client-local|sovereign)")
|
|
}
|
|
name := args[0]
|
|
if err := fs.Parse(args[1:]); err != nil {
|
|
return fmt.Errorf("parse flags: %w", err)
|
|
}
|
|
|
|
brainURL := os.Getenv("BRAIN_URL")
|
|
if brainURL == "" {
|
|
brainURL = defaultBrainURL
|
|
}
|
|
|
|
var doc map[string]any
|
|
switch name {
|
|
case "cloud":
|
|
doc = modeCloud(brainURL)
|
|
case "client-local":
|
|
doc = modeClientLocal(brainURL)
|
|
case "sovereign":
|
|
doc = modeSovereign(brainURL)
|
|
default:
|
|
return fmt.Errorf("unknown mode: %s (expected cloud|client-local|sovereign)", name)
|
|
}
|
|
|
|
if !*force {
|
|
if _, err := os.Stat(*out); err == nil {
|
|
return fmt.Errorf("%s exists (use --force to overwrite)", *out)
|
|
}
|
|
}
|
|
|
|
body, err := json.MarshalIndent(doc, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("marshal mode doc: %w", err)
|
|
}
|
|
if err := os.WriteFile(*out, append(body, '\n'), 0o644); err != nil {
|
|
return fmt.Errorf("write %s: %w", *out, err)
|
|
}
|
|
fmt.Fprintf(stdout, "wrote %s (mode: %s)\n", *out, name) //nolint:errcheck
|
|
return nil
|
|
}
|
|
|
|
func modeCloud(brainURL string) map[string]any {
|
|
return map[string]any{
|
|
"mcpServers": map[string]any{
|
|
"brain": map[string]any{
|
|
"url": brainURL + "/mcp",
|
|
"description": "Brain MCP — knowledge query, write, ingestion, session log",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func modeClientLocal(brainURL string) map[string]any {
|
|
return map[string]any{
|
|
"mcpServers": map[string]any{
|
|
"brain": map[string]any{
|
|
"url": brainURL + "/mcp",
|
|
"description": "Brain MCP — knowledge query, write, ingestion, session log",
|
|
},
|
|
"routing": map[string]any{
|
|
"url": "http://koala:30310/mcp",
|
|
"description": "Mode 2 routing pod — routes skill calls to LiteLLM/local",
|
|
"headers": map[string]any{
|
|
"X-Hyperguild-Mode": "client-local",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func modeSovereign(brainURL string) map[string]any {
|
|
return map[string]any{
|
|
"_mode_note": "Sovereign mode primarily uses Crush + LiteLLM. This .mcp.json is provided as Claude Code fallback (e.g. emergency offline editing).",
|
|
"mcpServers": map[string]any{
|
|
"brain": map[string]any{
|
|
"url": brainURL + "/mcp",
|
|
"description": "Brain MCP — knowledge query, write, ingestion, session log",
|
|
},
|
|
},
|
|
}
|
|
}
|