verae-mcp-timestamping-serv.../docs/superpowers/plans/2026-09-29-go-mcp-timestamping-server.md
George Lambert 3e16839367 docs: implementation plan for Go MCP server and pkg/tsnats
Eight TDD tasks: digest module, tsnats client, VeraeTime, FileLedger,
MCP HTTP server, NATS bridge/stdio, cutover docs, shadow unit. Spec
already includes AI MCP vs bulk NATS split.
2026-09-29 11:41:55 -04:00

9.2 KiB
Raw Blame History

Go MCP Timestamping Server + pkg/tsnats Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Ship a Go MCP timestamping server (full tool parity with Python) plus importable pkg/tsnats for bulk NATS clients, with shadow deploy docs for ns1 :8001.

Architecture: Official go-sdk/mcp for AI-facing Streamable HTTP/stdio; FileLedger + VeraeTime HTTPS; optional NATS upstream via pkg/tsnats. Job-complete stays core NATS only (no JetStream).

Tech Stack: Go 1.22+, github.com/modelcontextprotocol/go-sdk, github.com/nats-io/nats.go, net/http, stdio.

Spec: docs/superpowers/specs/2026-09-29-go-mcp-timestamping-server-design.md

Global Constraints

  • Digests only: refuse content/bytes/file/data/path/body/payload/blob/archive.
  • Global stamp restamp: false by default; poll /api/status/{jobId} when needed.
  • Never log JWT or password values.
  • No JetStream (or other persistent stream) for job-complete / jobId notification.
  • MCP tools: stamp, verify, hash_report, signup, login, session, create_user.
  • pkg/tsnats subjects: verae.ts.request, verae.ts.verify, verae.hash.report; subscribe verae.ts.completed.
  • Shadow default port 8001; Python keeps 8000.
  • Work in /Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server-go.
  • READMEs follow Standard Readme when touched.
  • Frequent commits; one logical change each.

File map

Path Responsibility
go.mod / go.sum Module + deps
cmd/verae-mcp-timestamping-server/main.go Flags, env, serve
internal/digest/ Normalize SHA-256 + forbidden keys
internal/config/ Env loading
pkg/tsnats/ Importable NATS client
internal/veraetime/ Global HTTPS + auth.properties
internal/ledger/ FileLedger local/shared
internal/natsbridge/ MCP upstream wrapping tsnats
internal/tools/ MCP tool registration/handlers
internal/server/ MCP server wiring (HTTP + stdio)
docs/cutover.md Shadow + cutover checklist
README.md Standard Readme

Task 1: Module scaffold + digest package

Files:

  • Create: go.mod, internal/digest/digest.go, internal/digest/digest_test.go, .gitignore
  • Modify: README.md (Install already present; keep Standard Readme)

Interfaces:

  • Produces: NormalizeSHA256(s string) (string, error), AssertDigestOnly(args map[string]any) error, ForbiddenKeys

  • Step 1: Write failing tests

// internal/digest/digest_test.go
func TestNormalizeSHA256_StripPrefix(t *testing.T) {
    h := strings.Repeat("a", 64)
    got, err := NormalizeSHA256("sha256:" + h)
    if err != nil || got != h {
        t.Fatalf("got %q err %v", got, err)
    }
}
func TestAssertDigestOnly_RejectsContent(t *testing.T) {
    err := AssertDigestOnly(map[string]any{"sha256": strings.Repeat("a", 64), "content": "x"})
    if err == nil {
        t.Fatal("expected error")
    }
}
  • Step 2: Run tests — expect fail

Run: go test ./internal/digest/ -count=1
Expected: build/package errors

  • Step 3: Implement digest + go mod init
go mod init git.georgelambert.org/marchon/verae-mcp-timestamping-server-go

Implement normalize (lower hex, strip sha256:) and forbidden-key check. Add .gitignore for bin/, *.exe, .venv/.

  • Step 4: Tests pass

Run: go test ./internal/digest/ -count=1

  • Step 5: Commit
git add go.mod internal/digest .gitignore
git commit -m "feat: module scaffold and digest normalize/guard"

Task 2: pkg/tsnats client

Files:

  • Create: pkg/tsnats/client.go, pkg/tsnats/client_test.go, pkg/tsnats/subjects.go

Interfaces:

  • Consumes: internal/digest (or duplicate thin normalize in pkg to avoid internal import — Ruling: put a small NormalizeSHA256 copy in pkg/tsnats or export via a public pkg/digest; prefer pkg/digest moved from internal if needed)
  • Produces:
    • type Client struct
    • Connect(url string, opts ...Option) (*Client, error)
    • Stamp(ctx, sha256 string, opts StampOptions) ([]Receipt, error) → verae.ts.request
    • Verify(ctx, receiptID string, opts VerifyOptions) (VerifyResult, error)
    • HashReport(ctx, sha256 string, opts ReportOptions) ([]Receipt, error)
    • SubscribeCompleted(ctx, func(CompletedEvent)) (Subscription, error) → core subscribe verae.ts.completed

Ruling for Task 2: Move digest helpers to pkg/digest so both pkg/tsnats and internal/ can import them (Go forbids internal/ import from pkg/). Update Task 1 packages in the same commit if still only local, or do a quick move commit first.

  • Step 1: Write failing client tests with nats-server or mock Conn

Prefer natsserver.RunServer from github.com/nats-io/nats-server/v2/test or inject a Requester interface:

type Requester interface {
    Request(ctx context.Context, subj string, data []byte) ([]byte, error)
}

Test: Stamp sends JSON {"hash":"...","scope":"global"} on verae.ts.request and parses receipt list.

  • Step 2: Run — expect fail

  • Step 3: Implement subjects + Client

Subjects constants exactly: verae.ts.request, verae.ts.verify, verae.hash.report, verae.ts.completed. Digests only. Timeout default 2s. No JetStream APIs in this package.

  • Step 4: Tests pass

Run: go test ./pkg/tsnats/ -count=1

  • Step 5: Commit
git commit -m "feat: pkg/tsnats digests-only NATS timestamp client"

Task 3: VeraeTime HTTPS client + auth.properties

Files:

  • Create: internal/veraetime/client.go, authprops.go, *_test.go

Interfaces:

  • Produces: Stamp(digest), Verify(receiptID), HashReport(digest), Signup, Login, Session, CreateUser

  • Token order: ctor → VERAE_GLOBAL_TOKEN → auth.properties

  • Stamp body: data, digest, hashAlg: SHA256, restamp: false

  • Step 1: Fake HTTP round-tripper tests (existing certificate path + 401 → AuthError)

  • Step 2: Fail

  • Step 3: Implement

  • Step 4: Pass — go test ./internal/veraetime/ -count=1

  • Step 5: Commit — feat: VeraeTime client and auth.properties


Task 4: FileLedger (local/shared)

Files:

  • Create: internal/ledger/file.go, file_test.go

Interfaces:

  • Stamp(digest, scope, cubeID, orgID) ([]Receipt, error) — idempotent for same hash+org+scope

  • Verify, HashReport against file

  • Path mode 0600; parent dir 0700

  • Step 1–5: TDD; commit feat: FileLedger for local and shared scopes


Task 5: MCP tools + Streamable HTTP server

Files:

  • Create: internal/tools/register.go, handlers; internal/server/server.go; cmd/verae-mcp-timestamping-server/main.go
  • Depend: go-sdk mcp.NewStreamableHTTPHandler, mcp.AddTool

Routing:

  • stamp scope global → veraetime; local|shared → ledger

  • verify / hash_report want_scope=global or CID-like → veraetime; else ledger

  • auth tools → veraetime

  • Step 1: Integration test httptest initialize → tools/list → tools/call stamp local

  • Step 2: Fail

  • Step 3: Wire server + main flags (-transport, -host, -port, env overrides); default shadow-friendly :8001 in examples only — code default may match Python 8000 with README documenting 8001 for shadow

  • Step 4: go test ./... and go build -o bin/verae-mcp-timestamping-server ./cmd/verae-mcp-timestamping-server

  • Step 5: Commit — feat: MCP Streamable HTTP server with full tool set


Task 6: internal/natsbridge + stdio transport

Files:

  • Create: internal/natsbridge/bridge.go wrapping pkg/tsnats

  • Modify: config + main to enable when VERAE_NATS_URL set (ledger backend switch or dual)

  • Add stdio serve path via go-sdk stdio transport

  • Step 1–5: TDD with fake tsnats; commit feat: optional NATS upstream and stdio transport


Task 7: Docs — cutover, README Usage for tsnats, example

Files:

  • Create: docs/cutover.md, examples/tsnats-bulk/main.go (optional small example)
  • Modify: README.md — Install/Usage for binary + pkg/tsnats import snippet; Standard Readme
client, err := tsnats.Connect(os.Getenv("NATS_URL"))
receipts, err := client.Stamp(ctx, digest, tsnats.StampOptions{Scope: "global"})
  • Step 1–5: Write docs; go test ./...; commit docs: cutover checklist and tsnats usage

Task 8: Shadow unit file + local parity script

Files:

  • Create: deploy/verae-mcp-timestamping-go.service (PORT=8001)

  • Create: scripts/parity_smoke.sh — tools/list against :8000 and :8001 when both up

  • Do not install on ns1 unless user says publish/deploy.

  • Commit chore: shadow systemd unit and parity smoke script


Self-review (plan vs spec)

Spec item Task
Go MCP binary full tools Task 5–6
FileLedger Task 4
VeraeTime + auth.properties Task 3
pkg/tsnats Task 2
natsbridge reuses tsnats Task 6
No JetStream job-complete Tasks 2, 6, Global Constraints
Shadow :8001 + cutover Tasks 7–8
Digests only Task 1 (+ enforced everywhere)