verae-mcp-timestamping-serv.../cmd/verae-mcp-timestamping-server/main.go
George Lambert e04fe6e2c4 feat: embedded NATS with VeraeTime handlers and stdio MCP
Start in-process nats-server on VERAE_NATS_LISTEN (default 127.0.0.1:4223;
empty disables). Queue-subscribe verae.ts.request|verify|hash.report with
ledger/veraetime routing and authctx.Resolve from authorization/access_token.
Publish verae.ts.completed on core NATS only (no JetStream). Wire embed into
main alongside Streamable HTTP; stdio MCP via go-sdk StdioTransport.
2026-09-29 12:11:47 -04:00

98 lines
2.8 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/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)
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,
AuthPath: cfg.AuthPath,
GlobalURL: cfg.GlobalURL,
RequireBearer: cfg.RequireBearer,
JSONResponse: true,
Stateless: true,
})
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,
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
}