Send global stamps as a verae-meta-git/1 history.

Optional metadata or restamp builds the same commit history as node-red. A call that omits both stays digest-only with restamp false. Local and shared stamps reject metadata.
This commit is contained in:
Cursor Agent 2026-10-11 05:37:24 +00:00
commit deb0e251de
No known key found for this signature in database
3 changed files with 608 additions and 11 deletions

View file

@ -49,7 +49,11 @@ func (d *Deps) stamp(ctx context.Context, req *mcp.CallToolRequest, in StampArgs
if err != nil {
return nil, ReceiptsOut{}, err
}
rec, err := d.Global.Stamp(ctx, in.SHA256, tok)
rec, err := d.Global.StampWithMeta(ctx, in.SHA256, tok, veraetime.StampMeta{
Metadata: in.Metadata,
Restamp: in.Restamp,
Message: in.Message,
})
if err != nil {
return nil, ReceiptsOut{}, err
}
@ -61,6 +65,9 @@ func (d *Deps) stamp(ctx context.Context, req *mcp.CallToolRequest, in StampArgs
}
return nil, ReceiptsOut{Receipts: []ReceiptDTO{fromGlobal(rec)}}, nil
case "local", "shared":
if len(in.Metadata) > 0 || in.Restamp {
return nil, ReceiptsOut{}, fmt.Errorf("metadata history is stored on global stamps")
}
if d.Ledger == nil {
return nil, ReceiptsOut{}, fmt.Errorf("ledger not configured")
}
@ -98,7 +105,21 @@ func (d *Deps) stampBatch(ctx context.Context, req *mcp.CallToolRequest, in Stam
if err != nil {
return nil, StampBatchOut{}, err
}
br, err := d.Global.StampBatch(ctx, in.SHA256s, tok)
var metas []veraetime.StampMeta
if len(in.Metadata) > 0 || in.Restamp {
if len(in.Metadata) > 0 && len(in.Metadata) != len(in.SHA256s) {
return nil, StampBatchOut{}, fmt.Errorf("metadata must have one entry per digest")
}
metas = make([]veraetime.StampMeta, len(in.SHA256s))
for i := range in.SHA256s {
metas[i].Restamp = in.Restamp
metas[i].Message = in.Message
if i < len(in.Metadata) {
metas[i].Metadata = in.Metadata[i]
}
}
}
br, err := d.Global.StampBatchWithMeta(ctx, in.SHA256s, tok, metas)
if err != nil {
return nil, StampBatchOut{}, err
}
@ -108,6 +129,9 @@ func (d *Deps) stampBatch(ctx context.Context, req *mcp.CallToolRequest, in Stam
}
return nil, out, nil
case "local", "shared":
if len(in.Metadata) > 0 || in.Restamp {
return nil, StampBatchOut{}, fmt.Errorf("metadata history is stored on global stamps")
}
if d.Ledger == nil {
return nil, StampBatchOut{}, fmt.Errorf("ledger not configured")
}

View file

@ -27,7 +27,7 @@ func Register(s *mcp.Server, d *Deps) {
}
mcp.AddTool(s, &mcp.Tool{
Name: "stamp",
Description: "Commit a SHA-256. scope=local is the leaf file ledger; scope=global posts digest and restamp=false to api.veraetime.net and reuses the first CID.",
Description: "Commit a SHA-256. scope=local is the leaf file ledger. scope=global posts the digest to api.veraetime.net and reuses the first CID unless restamp is true. Optional metadata is sent as a verae-meta-git/1 history; blank values do not delete fields.",
}, d.stamp)
mcp.AddTool(s, &mcp.Tool{
Name: "stamp_batch",

View file

@ -2,10 +2,13 @@ package tools
// StampArgs is the stamp tool input.
type StampArgs struct {
SHA256 string `json:"sha256" jsonschema:"SHA-256 digest (64 hex; optional sha256: prefix)"`
Scope string `json:"scope,omitempty" jsonschema:"local, shared, or global (default local)"`
CubeID string `json:"cube_id,omitempty" jsonschema:"optional cube id"`
OrgID string `json:"org_id,omitempty" jsonschema:"optional org id"`
SHA256 string `json:"sha256" jsonschema:"SHA-256 digest (64 hex; optional sha256: prefix)"`
Scope string `json:"scope,omitempty" jsonschema:"local, shared, or global (default local)"`
CubeID string `json:"cube_id,omitempty" jsonschema:"optional cube id"`
OrgID string `json:"org_id,omitempty" jsonschema:"optional org id"`
Metadata map[string]any `json:"metadata,omitempty" jsonschema:"optional plain object or a verae-meta-git/1 history. Blank values do not delete fields. Global scope only."`
Restamp bool `json:"restamp,omitempty" jsonschema:"when true, append metadata onto the previous global stamp for this digest"`
Message string `json:"message,omitempty" jsonschema:"optional history commit message"`
}
// VerifyArgs is the verify tool input.
@ -85,8 +88,11 @@ type CreateUserOut struct {
// StampBatchArgs is the stamp_batch tool input (DEC-659).
type StampBatchArgs struct {
SHA256s []string `json:"sha256s" jsonschema:"SHA-256 digests (64 hex each), 1..500"`
Scope string `json:"scope,omitempty" jsonschema:"global (default; charged per new digest to your Verae account) or local"`
SHA256s []string `json:"sha256s" jsonschema:"SHA-256 digests (64 hex each), 1..500"`
Scope string `json:"scope,omitempty" jsonschema:"global (default; charged per new digest to your Verae account) or local"`
Metadata []map[string]any `json:"metadata,omitempty" jsonschema:"optional history patch per digest, same length as sha256s. Global scope only."`
Restamp bool `json:"restamp,omitempty" jsonschema:"when true, append each metadata patch onto the previous global stamp"`
Message string `json:"message,omitempty" jsonschema:"optional history commit message for every item"`
}
// BatchItemDTO is one item of a stamp_batch answer.