Files
sm-bm-plugin/mcp_checklists.go
Jakub Zych d7cd5baedf feat: private checklists for zjazd preparation
Per-user checklists with items that may link a course, reading, deadline,
material or own presentation deck. An item with a reading shares the
reading's read mark. Student API routes with token twins, MCP tools,
migrations, tests and README.
2026-10-30 12:41:04 +01:00

126 lines
8.3 KiB
Go

package bm
import (
"net/http"
"git.golem15.com/golem15/summercms/modules/pact"
)
// checklistsInput is the input of bm_checklists.
type checklistsInput struct {
Meeting uint `json:"meeting,omitempty" query:"meeting" jsonschema:"only checklists of this zjazd (meeting) id"`
}
// checklistIDInput names one of the student's checklists.
type checklistIDInput struct {
ID uint `json:"id" path:"id" jsonschema:"the checklist id, as listed by bm_checklists"`
}
// checklistItemIDInput names one item of the student's checklists.
type checklistItemIDInput struct {
ID uint `json:"id" path:"id" jsonschema:"the checklist item id, as listed by bm_checklist"`
}
// createChecklistInput is the body of bm_create_checklist.
type createChecklistInput struct {
MeetingID *uint `json:"meeting_id,omitempty" jsonschema:"an existing zjazd (meeting) id, as listed by bm_meetings"`
Title string `json:"title" jsonschema:"title, at most 255 characters, e.g. Zjazd 4"`
Description *string `json:"description,omitempty" jsonschema:"description, at most 10000 characters"`
SortOrder *int `json:"sort_order,omitempty" jsonschema:"position among the student's checklists, an integer"`
}
// updateChecklistInput is the body of bm_update_checklist.
type updateChecklistInput struct {
ID uint `json:"id" path:"id" jsonschema:"the checklist id"`
MeetingID *uint `json:"meeting_id,omitempty" jsonschema:"an existing zjazd (meeting) id; null clears it"`
Title *string `json:"title,omitempty" jsonschema:"title, at most 255 characters; required, so not null"`
Description *string `json:"description,omitempty" jsonschema:"description, at most 10000 characters"`
SortOrder *int `json:"sort_order,omitempty" jsonschema:"position among the student's checklists, an integer"`
}
// addChecklistItemInput is the body of bm_add_checklist_item.
type addChecklistItemInput struct {
ID uint `json:"id" path:"id" jsonschema:"the checklist id"`
Kind *string `json:"kind,omitempty" jsonschema:"one of: lektura, artykul, ksiazka, prezentacja, zadanie, inne (default zadanie)"`
Title string `json:"title" jsonschema:"title, at most 500 characters"`
Notes *string `json:"notes,omitempty" jsonschema:"notes, at most 10000 characters"`
URL *string `json:"url,omitempty" jsonschema:"link, at most 2048 characters"`
CourseID *uint `json:"course_id,omitempty" jsonschema:"an existing course id"`
ReadingID *uint `json:"reading_id,omitempty" jsonschema:"an existing reading id; the item is then done exactly when the reading is marked read"`
DeadlineID *uint `json:"deadline_id,omitempty" jsonschema:"an existing deadline id"`
MaterialID *uint `json:"material_id,omitempty" jsonschema:"an existing material id"`
PresentationSlug *string `json:"presentation_slug,omitempty" jsonschema:"slug of one of the student's own presentation decks"`
DueAt *string `json:"due_at,omitempty" jsonschema:"when it should be done: RFC 3339 or YYYY-MM-DD HH:MM in Europe/Warsaw"`
EstimateMinutes *int `json:"estimate_minutes,omitempty" jsonschema:"estimated work in minutes, 0 to 10000"`
SortOrder *int `json:"sort_order,omitempty" jsonschema:"position in the checklist, an integer (default: last)"`
}
// updateChecklistItemInput is the body of bm_update_checklist_item.
type updateChecklistItemInput struct {
ID uint `json:"id" path:"id" jsonschema:"the checklist item id"`
ChecklistID *uint `json:"checklist_id,omitempty" jsonschema:"move the item to another of the student's checklists"`
Kind *string `json:"kind,omitempty" jsonschema:"one of: lektura, artykul, ksiazka, prezentacja, zadanie, inne"`
Title *string `json:"title,omitempty" jsonschema:"title, at most 500 characters; required, so not null"`
Notes *string `json:"notes,omitempty" jsonschema:"notes, at most 10000 characters"`
URL *string `json:"url,omitempty" jsonschema:"link, at most 2048 characters"`
CourseID *uint `json:"course_id,omitempty" jsonschema:"an existing course id"`
ReadingID *uint `json:"reading_id,omitempty" jsonschema:"an existing reading id; the item is then done exactly when the reading is marked read"`
DeadlineID *uint `json:"deadline_id,omitempty" jsonschema:"an existing deadline id"`
MaterialID *uint `json:"material_id,omitempty" jsonschema:"an existing material id"`
PresentationSlug *string `json:"presentation_slug,omitempty" jsonschema:"slug of one of the student's own presentation decks"`
DueAt *string `json:"due_at,omitempty" jsonschema:"when it should be done: RFC 3339 or YYYY-MM-DD HH:MM in Europe/Warsaw"`
EstimateMinutes *int `json:"estimate_minutes,omitempty" jsonschema:"estimated work in minutes, 0 to 10000"`
SortOrder *int `json:"sort_order,omitempty" jsonschema:"position in the checklist, an integer (default: last)"`
}
// The sentences of the checklist tool descriptions.
const (
checklistsPrivate = " Checklists are the signed-in student's own; another student's id answers 404."
checklistAnswer = " Returns the checklist with its progress (total, done, percent, minutes_total, minutes_done, minutes_left) and its items."
itemAnswer = " Returns the item with its done mark and the reading, deadline, material, course or deck it points at."
checklistInvalid = " Unknown fields and invalid values answer 422 with the errors per field."
)
// checklistTools are the student's checklist tools over the token twins of
// the student API.
func checklistTools() []pact.MCPTool {
tool := func(name, title, desc, method, path string, input any) pact.MCPTool {
return pact.MCPTool{Name: name, Title: title, Description: desc + checklistsPrivate, Guard: "user", Method: method, Path: TokenPrefix + path, Input: input}
}
unmark := tool("unmark_checklist_item_done", "Unmark a checklist item as done",
"Clears the done mark of a checklist item; for an item with a reading it removes the student's read mark of that reading."+itemAnswer+needsScopeWrite,
http.MethodDelete, "/checklist-items/{id}/done", checklistItemIDInput{})
unmark.Destructive = boolPtr(false)
unmark.Idempotent = boolPtr(true)
return []pact.MCPTool{
tool("checklists", "BM checklists",
"Lists the student's checklists (e.g. the preparation for a zjazd) with zjazd and progress, without items."+needsScopeRead,
http.MethodGet, "/checklists", checklistsInput{}),
tool("checklist", "BM checklist",
"Returns one of the student's checklists."+checklistAnswer+needsScopeRead,
http.MethodGet, "/checklists/{id}", checklistIDInput{}),
tool("create_checklist", "Create a checklist",
"Creates a checklist for the student, optionally for one zjazd."+checklistAnswer+checklistInvalid+needsScopeWrite,
http.MethodPost, "/checklists", createChecklistInput{}),
tool("update_checklist", "Update a checklist",
"Changes the sent fields of one of the student's checklists; the rest stay."+checklistAnswer+checklistInvalid+needsScopeWrite,
http.MethodPatch, "/checklists/{id}", updateChecklistInput{}),
tool("delete_checklist", "Delete a checklist",
"Deletes one of the student's checklists with its items, for good; reading marks stay."+needsScopeWrite,
http.MethodDelete, "/checklists/{id}", checklistIDInput{}),
tool("add_checklist_item", "Add a checklist item",
"Adds an item (a reading, article, book, presentation, task) to one of the student's checklists, last unless sort_order is given."+itemAnswer+checklistInvalid+needsScopeWrite,
http.MethodPost, "/checklists/{id}/items", addChecklistItemInput{}),
tool("update_checklist_item", "Update a checklist item",
"Changes the sent fields of one checklist item; the rest stay."+itemAnswer+checklistInvalid+needsScopeWrite,
http.MethodPatch, "/checklist-items/{id}", updateChecklistItemInput{}),
tool("delete_checklist_item", "Delete a checklist item",
"Deletes one checklist item for good; a reading mark stays."+needsScopeWrite,
http.MethodDelete, "/checklist-items/{id}", checklistItemIDInput{}),
tool("mark_checklist_item_done", "Mark a checklist item as done",
"Marks a checklist item done; for an item with a reading it marks that reading read (the reading list shows it). Marking again keeps the first time."+itemAnswer+needsScopeWrite,
http.MethodPut, "/checklist-items/{id}/done", checklistItemIDInput{}),
unmark,
}
}