copyByJSONTag previously skipped any IsZero value, which made it
impossible for tasks_update / projects_update to flip done from true
to false, reset priority/percent_done to 0, or unarchive a project.
A non-nil pointer src is now the unambiguous "caller supplied this"
signal: dereferenced values are written through even when zero, while
value-typed src fields keep the partial-update semantics. The
affected wrapper fields (Done, IsArchived, IsFavorite, Priority,
PercentDone, RepeatAfter, RepeatMode, BucketID,
CoverImageAttachmentID, ParentProjectID, Position) move to pointer
types so the JSON Schema still marks them optional.
Registers tasks, labels, teams, task_comments and task_assignees through
the MCP tool surface, completing the v1 resource list from the plan:
* tasks : create / read_one / update / delete (read_all omitted;
models.Task.ReadAll is a stub — TaskCollection is OOS)
* labels : full CRUD
* teams : full CRUD
* tasks_comments : full CRUD, install-time gated on
config.ServiceEnableTaskComments
* tasks_assignees : create / read_all / delete only (REST exposes no
read_one or update)
Per-resource input wrappers carry the path-param fields (task_id,
user_id) explicitly so MCP callers can provide them as JSON args.
installToolsForToken fans out to one installer per resource; the
generics-bound addTool keeps per-(resource, op) call sites at compile
time. The api_tokens.yml fixture extends token 11 to cover the new
scopes; token count stays at 5 for user 1 so existing token-listing
tests are unaffected.
Integration tests per resource cover tools/list visibility, at least
one successful create or read_all, and a permission denial scenario.
Wires the projects resource into the MCP server end-to-end. The five
project tools (create, read_one, read_all, update, delete) are now
visible in tools/list and dispatch through handler.Do* like the REST
layer.
- Add ProjectCreateInput / ProjectUpdateInput in inputs.go with
jsonschema tags covering only the writable fields the model honours
(title, description, identifier, hex_color, parent_project_id,
position, is_archived, is_favorite); computed fields like Owner and
MaxPermission are intentionally absent so the SDK-reflected schema
stays narrow.
- Add resources.go with a sync.Once-guarded RegisterResources(), and an
installTools helper that registers tools per (resource, op) on the
*mcp.Server via a generic addTool[In inputAdapter] helper. The
handler maps domain failures (permission denials, missing rows,
validation) to IsError tool results per the SDK convention.
- Add DispatchTyped in dispatcher.go so the AddTool handler can hand a
pre-unmarshalled wrapper to the dispatcher without a JSON
round-trip. The existing Dispatch (raw JSON path) delegates to a
shared dispatchPrepared.
- Wire RegisterResources() + installTools() into newServer() so each
new MCP session inherits the static tool set.
- Add fixture token 11 (mcp:access + projects:*) for the full-scope
integration tests; bump TestAPIToken_ReadAll's expected count.
- Refresh TestMCP_ToolsListEmpty into
TestMCP_ToolsListReturnsRegisteredResources, asserting the five
projects_* tools are present (Task 6 will introduce scope-based
filtering of this list).
- Add pkg/webtests/mcp_projects_test.go covering tools/list,
create/read_one/read_all/update/delete happy paths, schema-validation
failure on missing required title, permission denial on a forbidden
project, and nonexistent-id lookup.