feat: redesign mcp surface as 8 ref-first tools#170
Merged
Conversation
ulascanzorer
approved these changes
Jul 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Task Reference: [PYZ-290]
Replaces the 6-tool MCP surface (
piyaz_project/task/edge/query/context/analyze) with 8 ref-first tools (piyaz_workspace/search/get/create/edit/link/map/activity) in one breaking change. Refs (PYZ-42,PYZ) are first-class on every parameter with server-side resolution (org-bounded, candidate lists on ambiguity, near-miss copy on a miss; the near-miss lookup runs only when the ref misses). Task editing becomes operation-based (str_replace/append/seton text, by-id ops on collections,ifUpdatedAtcompare-and-swap) andoverwriteArraysis removed.piyaz_editreads the task rowFOR UPDATE, so concurrent edits to the same task serialize and a staleifUpdatedAtfails deterministically; edit ops enforce the create-path per-field size caps, tag writes return the same variant/taxonomy hints as the create path, linkupdatepatches only the supplied fields, anddelete_taskrejectsifUpdatedAt.set prUrlguarantees apull_request-kind link at that URL on any host (one shared helper serves the web and MCP paths): a same-URL link of another kind is converted in place keeping its label, retries are no-ops, a task can carry multiple PR links, andnullclears them; status hints derive the prior status and links from the write transaction instead of extra reads. Task creation becomes an idempotent batch (1-25 tasks + edges, exact-title dedup; duplicate edges within one payload dedupe to their first occurrence; assignees are membership-checked once per batch over the to-create items, before any write). New read capabilities: cross-project filtered search with keyset cursors (forged non-UUID cursor ids degrade to page 1), single-field reads (fields=[...], framed with the untrusted-content notice), arecordlens, 1-2 hopneighborswalks, and a keyset activity feed withsincefor resume. Context lenses now rendercategory, work-so-far execution records, assignees (agent lens), andrelates_toneighbors; lens bundles width-cap edge/prerequisite/downstream/related lists and upstream record blocks with narrowing guidance.piyaz_workspacegainsmembers(one team's directory — name, user UUID, role — the UUID source for assigneeIds/assignee ops/filters, backed by the membership-gatedteam_members_visibleSECURITY DEFINER read) andrename_category/delete_category(vocabulary edits that cascade to task rows atomically, reusing the web data functions; plainupdate categories=[...]replaces the list without touching tasks and now says so). Every list-shaped response is budgeted with truncation guidance, includingpiyaz_workspaceteams/projects/members backstops. MCP gets dedicated rate-limit tiers (RATE_LIMIT_MCP100/60,RATE_LIMIT_MCP_HEAVY20/60 enforced per tool shape inwrapTool; heavy covers deep lenses, overview, critical_path, hops-2 neighbors, task- or edge-heavy batches, and the category cascades) and a structured per-call log line. Task statuses and the heavy lenses derive from single shared lists (TASK_STATUSESinlib/types.ts,DEEP_LENSESin the schema module). Plugin doctrine (all four brands) and generated docs are rewritten against the new surface.The write surface is now project-lifecycle-phase aware (
brainstorming → decomposing → active → archived). Task/edge writes in abrainstormingproject and execution-status flips (in_progress/in_review/done) in adecomposingproject return advisory_hintsnaming the exact promote call;activeis silent.archivedis hard read-only on the task surface:piyaz_create,piyaz_edit(includingdelete_task preview=false; preview stays a read),piyaz_link, and the category cascades throw a newProjectArchivedErrorin the data layer before any write, translated to an error naming the reopen call. Reads andpiyaz_workspace updatestay allowed (the un-archive path); metadata-only updates and identifier renames on an archived project carry the read-only hint.piyaz_workspace update status=...returns transition hints for forward skips, backward moves, and the archived read-only consequence, ordered by a sharedPROJECT_STATUS_ORDERinlib/types.ts. Status reads cost zero extra queries: the task access gate joinsprojects(single-row PK join) forprojectStatus/projectIdentifier, andupdateProject/getProjectCategories/createTasksBatch/applyTaskEdit/createEdgesurface the phase from reads they already perform. The previously deaddecomposingphase is wired into the decompose and onboarding agents (flip at task-creation start,activeon validation,decomposingcounts as resume mode); lifecycle.md gains a project-lifecycle section and the schema.describe()prose documents the phases for docs:gen.Type of change
Testing
bun run devbun run lint)bun run typecheck)bun test), including a 17-test MCP read-surface regression suite over the InMemoryTransport client (tests/regression/) and a concurrency test proving one winner + oneStaleWriteErroron contendedifUpdatedAteditspiyaz_workspace action='update'covered end to end: field persistence, no-op rejection, malformed identifier fails before any write, identifier-rename taskRef cascade, rename+fields in one call, rename conflict (clean 23505), member-role rename refusalmemberscovered: directory with roles and assignment steering, foreign-team 404 shape, multi-team ambiguity;rename_category/delete_categorycovered: task cascade, unknown/colliding names, uncategorizeset prUrlcontract covered: kind forced topull_requeston non-GitHub hosts, same-URL conversion keeps the user label, second URL keeps both PR links, null clears; batch assignee checks covered for persist and non-member fail-before-any-writeNotes for reviewer
bun run db:rls: three new SECURITY DEFINER functions indocker/rls-functions.sql(activity_actors_for_project_visible,current_user_profile,team_members_visible— reintroduced with a TS caller and a drop-first guard against the legacy signature).lib/graph/tool-handlers.tsand its tests are deleted; assertions ported totests/graph/tools-{read,write}.test.ts. New transport tests cover the InMemoryTransport pair and the route's 401/413 boundary.createTaskwas refactored onto acreateTaskTxseam for the batch path; the untouched pre-existing task test suite is the behavior-identity proof.wrangler.jsoncadds rate-limit namespaces 1003/1004 (prod) and 2003/2004 (dev).docs-sync.ymlnow watcheslib/graph/tools/**(the deletedtool-handlers.tspath is gone).Docs impact
Generated MCP tool reference changes from 6 pages to 8 (Docs Sync opens the PR automatically). The 6 orphan pages
reference/tools/{project,task,edge,query,context,analyze}.mdxin piyaz-docs need manual deletion;meta.jsonis regenerated.BREAKING CHANGE: piyaz_project/task/edge/query/context/analyze are removed; the MCP surface is now piyaz_workspace/search/get/create/edit/link/map/activity. Once this deploys to prod, plugins still on 0.2.x call the removed tools and their skills carry stale prompts against the old surface. Users must update their plugin to 0.3.0.