package mcpsrv
import (
"context"
"fmt"
"sourcecraft.dev/bigbes/sr-ht-spec/service"
)
type readInput struct {
Space string `json:"space" jsonschema:"the space to read from, written \"~owner/name\" as spec_list and spec_search report it"`
Document string `json:"document" jsonschema:"which document: its frontmatter id (\"SPEC-0007\"), or its path with or without the \".md\" extension (\"specs/0007-storage\")"`
Rev string `json:"rev,omitempty" jsonschema:"pin the read to one immutable revision, given as a git object name (lowercase hex, as returned in the rev field of any result). Omit this to read the space's approved head, which is what an agent almost always wants: the reviewed text. Branch names are not accepted."`
}
type readOutput struct {
Space string `json:"space"`
// ID is how this document is addressed: its frontmatter id when that is
// well-formed and unique in the space, otherwise its path without the
// extension.
ID string `json:"id"`
// DocID is the frontmatter id when the document has a well-formed one, and
// empty otherwise. It differs from ID exactly when the document has no
// usable id, which is the case an agent proposing an edit needs to see.
DocID string `json:"doc_id,omitempty"`
Path string `json:"path"`
// Rev is the commit this content was read at. It is immutable: pass it back
// as the rev argument to re-read these exact bytes. It is also the value
// the Phase 3 write plane takes as If-Match when it is an approved head.
Rev string `json:"rev"`
// Blob is the sha of this document's content, and changes only when the
// content does.
Blob string `json:"blob"`
// Pinned reports how Rev was chosen. False means the caller named no
// revision and this is the space's approved head as of this call — the
// reviewed, canonical text. True means the caller pinned a revision, and
// whether that revision is on the approved branch is the caller's business:
// this service does not claim it either way.
Pinned bool `json:"pinned"`
Title string `json:"title,omitempty"`
Section string `json:"section,omitempty"`
// Status is the document's authored lifecycle marker (draft, review,
// superseded). It is not approval state: a document is approved by being
// reachable from the approved ref, never by its frontmatter.
Status string `json:"status,omitempty"`
Summary string `json:"summary,omitempty"`
Tags []string `json:"tags,omitempty"`
// Markdown is the whole document, frontmatter included, exactly as stored.
// It is the text to edit and send back when proposing a change.
Markdown string `json:"markdown"`
}
func readHandler(ctx context.Context, b Backend, in readInput) (readOutput, error) {
ref, err := parseSpace(in.Space)
if err != nil {
return readOutput{}, err
}
rev, err := parseRev(in.Rev)
if err != nil {
return readOutput{}, err
}
sp, err := b.Docs.OpenSpace(ctx, ref)
if err != nil {
return readOutput{}, err
}
arc, bodies, resolved, err := archiveAt(ctx, b, sp, rev)
if err != nil {
return readOutput{}, err
}
page, err := resolvePage(arc, in.Document)
if err != nil {
return readOutput{}, err
}
body, ok := bodies[page.Path]
if !ok {
// The archive is built from these very bodies, so a page without one
// is a broken invariant rather than a missing document. Returning an
// empty markdown field would be indistinguishable from an empty
// document.
return readOutput{}, fmt.Errorf("document %s at %s in %s has no body", page.Path, resolved, ref)
}
return readOutput{
Space: ref.String(),
ID: page.ID,
DocID: page.DocID,
Path: page.Path,
Rev: resolved,
Blob: page.Blob,
Pinned: rev != service.ApprovedRev,
Title: page.Title,
Section: page.Section,
Status: string(page.Status),
Summary: page.Summary,
Tags: page.Tags,
Markdown: string(body),
}, nil
}