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:
@@ -163,7 +163,8 @@ func newManagedMaterialJSON(m models.Material) managedMaterialJSON {
|
||||
}
|
||||
|
||||
// ManagedMaterials answers GET /materials: every material, drafts
|
||||
// included, newest first.
|
||||
// included, newest first. An MCP call gets the bodies only with
|
||||
// include_body (see managedMaterialView).
|
||||
func (h *Handlers) ManagedMaterials(w http.ResponseWriter, r *http.Request) {
|
||||
db, ok := h.db(w, r)
|
||||
if !ok {
|
||||
@@ -174,9 +175,9 @@ func (h *Handlers) ManagedMaterials(w http.ResponseWriter, r *http.Request) {
|
||||
writeOpaque500(w)
|
||||
return
|
||||
}
|
||||
out := make([]managedMaterialJSON, 0, len(rows))
|
||||
out := make([]any, 0, len(rows))
|
||||
for _, m := range rows {
|
||||
out = append(out, newManagedMaterialJSON(m))
|
||||
out = append(out, managedMaterialView(newManagedMaterialJSON(m), viaMCP(r), includeBody(r)))
|
||||
}
|
||||
writeData(w, out)
|
||||
}
|
||||
@@ -326,15 +327,16 @@ func (d materialDraft) apply(tx *gorm.DB, m *models.Material, created bool, now
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeManaged answers with the material id, reloaded with its course and
|
||||
// tags, plus any extra top-level keys.
|
||||
func writeManaged(w http.ResponseWriter, db *gorm.DB, id uint, status int, extra map[string]any) {
|
||||
// writeManaged answers a write of r with the material id, reloaded with its
|
||||
// course and tags, plus any extra top-level keys. Under MCP the answer never
|
||||
// echoes the body.
|
||||
func writeManaged(w http.ResponseWriter, r *http.Request, db *gorm.DB, id uint, status int, extra map[string]any) {
|
||||
var out models.Material
|
||||
if err := preloadMaterial(db).First(&out, id).Error; err != nil {
|
||||
writeOpaque500(w)
|
||||
return
|
||||
}
|
||||
payload := map[string]any{"data": newManagedMaterialJSON(out)}
|
||||
payload := map[string]any{"data": managedMaterialView(newManagedMaterialJSON(out), viaMCP(r), false)}
|
||||
for k, v := range extra {
|
||||
payload[k] = v
|
||||
}
|
||||
@@ -373,7 +375,7 @@ func (h *Handlers) UpsertMaterial(w http.ResponseWriter, r *http.Request) {
|
||||
if created {
|
||||
status = http.StatusCreated
|
||||
}
|
||||
writeManaged(w, db, m.ID, status, map[string]any{"created": created})
|
||||
writeManaged(w, r, db, m.ID, status, map[string]any{"created": created})
|
||||
}
|
||||
|
||||
// UpdateMaterial answers PUT /materials/{id}: the same body as POST, applied
|
||||
@@ -423,7 +425,7 @@ func (h *Handlers) UpdateMaterial(w http.ResponseWriter, r *http.Request) {
|
||||
case taken:
|
||||
writeValidation(w, map[string][]string{"title": {"Another material already has this title and course."}})
|
||||
default:
|
||||
writeManaged(w, db, m.ID, http.StatusOK, nil)
|
||||
writeManaged(w, r, db, m.ID, http.StatusOK, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,12 +448,13 @@ func (h *Handlers) ManagedMaterial(w http.ResponseWriter, r *http.Request) {
|
||||
writeNotFound(w)
|
||||
return
|
||||
}
|
||||
h.writeMaterialDetail(w, db, uint(id), http.StatusOK)
|
||||
h.writeMaterialDetail(w, r, db, uint(id), http.StatusOK)
|
||||
}
|
||||
|
||||
// writeMaterialDetail answers with the material id as GET /materials/{id}
|
||||
// shows it, or 404 when there is none.
|
||||
func (h *Handlers) writeMaterialDetail(w http.ResponseWriter, db *gorm.DB, id uint, status int) {
|
||||
// writeMaterialDetail answers r with the material id as GET /materials/{id}
|
||||
// shows it, or 404 when there is none. Under MCP a GET carries the body only
|
||||
// with include_body, and any other method's answer never does.
|
||||
func (h *Handlers) writeMaterialDetail(w http.ResponseWriter, r *http.Request, db *gorm.DB, id uint, status int) {
|
||||
var m models.Material
|
||||
res := preloadMaterial(db).Where("jz_bm_materials.id = ?", id).Limit(1).Find(&m)
|
||||
if res.Error != nil {
|
||||
@@ -475,7 +478,7 @@ func (h *Handlers) writeMaterialDetail(w http.ResponseWriter, db *gorm.DB, id ui
|
||||
}
|
||||
out.Files = append(out.Files, fileJSON{ID: f.ID, URL: f.URL(), Name: f.FileName, Size: f.FileSize, Mime: f.ContentType})
|
||||
}
|
||||
wire.WriteJSON(w, status, map[string]any{"data": out})
|
||||
wire.WriteJSON(w, status, map[string]any{"data": managedMaterialDetailView(out, viaMCP(r), r.Method == http.MethodGet && includeBody(r))})
|
||||
}
|
||||
|
||||
// DeleteMaterial answers DELETE /materials/{id}.
|
||||
|
||||
Reference in New Issue
Block a user