- Public signup no longer requires a caller bearer (POST /auth/signup) - create_user remains admin-only (POST /auth/users) - Optional VERAE_MCP_EDGE_URL bills global stamps via zap.verae.ai - Clearer public auth errors and instructions for Grok/clients
109 lines
3.4 KiB
Go
109 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/edgebill"
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/embednats"
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/ledger"
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/server"
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/tools"
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/veraetime"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
func main() {
|
|
transport := flag.String("transport", "streamable-http", "MCP transport: streamable-http or stdio")
|
|
host := flag.String("host", "127.0.0.1", "HTTP bind host")
|
|
port := flag.Int("port", 8000, "HTTP bind port (shadow often uses 8001)")
|
|
flag.Parse()
|
|
|
|
cfg := server.LoadConfig(*transport, *host, *port)
|
|
|
|
led, err := ledger.Open(cfg.LedgerPath)
|
|
if err != nil {
|
|
log.Fatalf("ledger: %v", err)
|
|
}
|
|
global := veraetime.NewClient(cfg.GlobalURL, veraetime.WithAuthPath(cfg.AuthPath))
|
|
keeper := veraetime.NewKeeper(global, cfg.AuthPath)
|
|
edge := edgebill.FromEnv()
|
|
if edge != nil {
|
|
log.Printf("MCP global stamps bill via edge %s (channel=%s)", edge.BaseURL, edge.Channel)
|
|
}
|
|
|
|
emb, err := embednats.Start(embednats.Options{
|
|
Listen: cfg.NATSListen,
|
|
Ledger: led,
|
|
Global: global,
|
|
Keeper: keeper,
|
|
RequireBearer: cfg.RequireBearer,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("embednats: %v", err)
|
|
}
|
|
if emb != nil {
|
|
defer emb.Shutdown()
|
|
log.Printf("embedded NATS listening on %s", emb.ClientURL())
|
|
}
|
|
|
|
switch cfg.Transport {
|
|
case "streamable-http", "http":
|
|
srv, err := server.New(server.Options{
|
|
Ledger: led,
|
|
Global: global,
|
|
Keeper: keeper,
|
|
Edge: edge,
|
|
AuthPath: cfg.AuthPath,
|
|
GlobalURL: cfg.GlobalURL,
|
|
RequireBearer: cfg.RequireBearer,
|
|
// Stateful sessions + JSON responses match public mcp.verae.ai clients
|
|
// (Mcp-Session-Id after initialize). Stateless broke tools/call for those clients.
|
|
JSONResponse: true,
|
|
Stateless: false,
|
|
// DEC-654: the HTTP transport is the public server (mcp.verae.ai) unless VERAE_MCP_TRUSTED_LOCAL=1.
|
|
Public: os.Getenv("VERAE_MCP_TRUSTED_LOCAL") != "1",
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("server: %v", err)
|
|
}
|
|
handler := server.WithCORS(srv.Handler(), cfg.CORS)
|
|
httpSrv := &http.Server{Addr: cfg.Addr(), Handler: handler}
|
|
go func() {
|
|
log.Printf("verae-mcp-timestamping-server listening on http://%s/mcp", cfg.Addr())
|
|
if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("listen: %v", err)
|
|
}
|
|
}()
|
|
waitSignal()
|
|
_ = httpSrv.Shutdown(context.Background())
|
|
case "stdio":
|
|
mcpServer := tools.NewServer(&tools.Deps{
|
|
Ledger: led,
|
|
Global: global,
|
|
Keeper: keeper,
|
|
Edge: edge,
|
|
RequireBearer: cfg.RequireBearer,
|
|
})
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
if err := mcpServer.Run(ctx, &mcp.StdioTransport{}); err != nil {
|
|
fmt.Fprintf(os.Stderr, "stdio: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
default:
|
|
log.Fatalf("unknown transport %q (use streamable-http or stdio)", cfg.Transport)
|
|
}
|
|
}
|
|
|
|
func waitSignal() {
|
|
ch := make(chan os.Signal, 1)
|
|
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
|
|
<-ch
|
|
}
|