feat: MCP view of BM answers (absolute links, no material body by default)
- jz.bm.mcp-links on the token twins and the management API: under MCP, url and *_url strings starting with a single / get the app base - material answers under MCP leave the body out unless include_body, marking it with body_omitted; write answers never echo it - jz.bm.manage keys a dispatched call by user - bm_materials and bm_material student tools Plain HTTP answers are unchanged.
This commit is contained in:
@@ -2,19 +2,84 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/bouncer"
|
||||
)
|
||||
|
||||
// viaMCP reports whether r is an MCP tool call.
|
||||
func viaMCP(r *http.Request) bool { return false }
|
||||
// The MCP view of the material answers (D-17, D-21): a material's body may
|
||||
// hold transcript text, so an answer to an MCP tool call leaves it out
|
||||
// unless the call asks for it with include_body, and a write answer never
|
||||
// echoes it. Plain HTTP answers are unchanged.
|
||||
|
||||
// includeBody reports whether r asks for the material body.
|
||||
func includeBody(r *http.Request) bool { return false }
|
||||
// viaMCP reports whether r is an MCP tool call: a request pier dispatched
|
||||
// in-process with the api_token marker. The marker lives on the context
|
||||
// only, so an HTTP request can never carry it.
|
||||
func viaMCP(r *http.Request) bool {
|
||||
_, ok := bouncer.Dispatched(r.Context(), "api_token")
|
||||
return ok
|
||||
}
|
||||
|
||||
// materialView is the answer of GET /materials/{id} for r.
|
||||
func materialView(r *http.Request, d materialDetailJSON) any { return d }
|
||||
// includeBody reports whether r asks for the material body
|
||||
// (include_body=true or 1).
|
||||
func includeBody(r *http.Request) bool {
|
||||
return flag(r, "include_body")
|
||||
}
|
||||
|
||||
// managedMaterialView is a managed material for r.
|
||||
func managedMaterialView(r *http.Request, m managedMaterialJSON, write bool) any { return m }
|
||||
// bodyOmitted reports whether leaving body out hides content.
|
||||
func bodyOmitted(body *string) bool {
|
||||
return body != nil && *body != ""
|
||||
}
|
||||
|
||||
// managedMaterialDetailView is a managed material with its files for r.
|
||||
func managedMaterialDetailView(r *http.Request, d managedMaterialDetailJSON, write bool) any { return d }
|
||||
// mcpMaterialDetailJSON is a published material under MCP without its body:
|
||||
// the list fields and the files, with body_omitted when a non-empty body was
|
||||
// left out. Body shadows the embedded one and stays nil.
|
||||
type mcpMaterialDetailJSON struct {
|
||||
materialDetailJSON
|
||||
Body *string `json:"body,omitempty"`
|
||||
BodyOmitted bool `json:"body_omitted,omitempty"`
|
||||
}
|
||||
|
||||
// mcpManagedMaterialJSON is a managed material under MCP without its body.
|
||||
type mcpManagedMaterialJSON struct {
|
||||
managedMaterialJSON
|
||||
Body *string `json:"body,omitempty"`
|
||||
BodyOmitted bool `json:"body_omitted,omitempty"`
|
||||
}
|
||||
|
||||
// mcpManagedMaterialDetailJSON is a managed material with its files under
|
||||
// MCP without its body.
|
||||
type mcpManagedMaterialDetailJSON struct {
|
||||
mcpManagedMaterialJSON
|
||||
Files []fileJSON `json:"files"`
|
||||
}
|
||||
|
||||
// materialView is the answer of GET /materials/{id} (student): unchanged
|
||||
// over plain HTTP; under MCP (mcp) without the body unless withBody.
|
||||
func materialView(d materialDetailJSON, mcp, withBody bool) any {
|
||||
if !mcp || withBody {
|
||||
return d
|
||||
}
|
||||
return mcpMaterialDetailJSON{materialDetailJSON: d, BodyOmitted: bodyOmitted(d.Body)}
|
||||
}
|
||||
|
||||
// managedMaterialView is a managed material: unchanged over plain HTTP;
|
||||
// under MCP (mcp) without the body unless withBody (a write answer passes
|
||||
// false, so it never echoes the body).
|
||||
func managedMaterialView(m managedMaterialJSON, mcp, withBody bool) any {
|
||||
if !mcp || withBody {
|
||||
return m
|
||||
}
|
||||
return mcpManagedMaterialJSON{managedMaterialJSON: m, BodyOmitted: bodyOmitted(m.Body)}
|
||||
}
|
||||
|
||||
// managedMaterialDetailView is a managed material with its files, under the
|
||||
// same rule as managedMaterialView.
|
||||
func managedMaterialDetailView(d managedMaterialDetailJSON, mcp, withBody bool) any {
|
||||
if !mcp || withBody {
|
||||
return d
|
||||
}
|
||||
return mcpManagedMaterialDetailJSON{
|
||||
mcpManagedMaterialJSON: mcpManagedMaterialJSON{managedMaterialJSON: d.managedMaterialJSON, BodyOmitted: bodyOmitted(d.Body)},
|
||||
Files: d.Files,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user