Skip to content
SHAMPOO

Tool catalogue

107 documented tools.

Derived from the canonical SHAMPOO tool documentation (the same source the implementation is gated against), so these pages and the software cannot drift apart.

10 tools

Tasks and notes

Everyday work: create, assign, query, digest and archive tasks and notes.

archive_done_tasks

Write · Advanced

Sweeps done tasks whose last update is older than N days into archived. A maintenance pass for keeping the live task lists quiet without losing history.

Full documentation →

Arguments, result and boundaries
Arguments
Optional: `older_than_days`. - `older_than_days`. Note: Non-negative age threshold in days (default 7). Only done tasks of type task older than this move.
Result
JSON object with archived (how many moved) and threshold_days (the applied threshold).
Boundaries
Only done tasks visible under the caller's project grants are candidates; notes, live statuses, and foreign projects are never touched. With no visible scope the sweep succeeds with archived 0.
Lifecycle
Each move travels through the canonical mutation path and records an audit/history event, so archived content keeps its full trail and can be distinguished from cancelled or live work.
Errors
Bad types are rejected (strict validator). A negative threshold is rejected. Failures move nothing (atomic).
Example
```json { "older_than_days": 7 } ``` Result shape: ```json { "archived": 4, "threshold_days": 7 } ```

assign_task

Write · Ordinary

Sets who should do a task or note. Assignment is a coordination hint only: the assignee value is stored verbatim and grants nothing, while the calling principal is recorded alongside as shared_by.

Full documentation →

Arguments, result and boundaries
Arguments
Required: `task_id`. Optional: `assignee`. - `task_id`: UUID of the task to assign (required). - `assignee`: Who should do it (free-form). Note: Free-form verbatim hint, never authority. Empty or omitted unassigns, clearing both assignee and shared_by.
Result
JSON object with task_id, assignee (string or null when unassigned), and shared_by (the calling principal, or null when unassigned).
Boundaries
The caller must reach the task under their project grants; anything else answers Task not found. The assignee value itself is never scope-checked.
Lifecycle
Applies the assignment through the canonical mutation path and records an audit/history event. Reassigning overwrites the previous hint; unassigning clears both coordination fields while history is preserved.
Errors
Missing task_id is rejected by validation. Unknown arguments are rejected (strict validator). Unknown or out-of-scope tasks answer not-found. Failures leave nothing half-applied (atomic).
Example
```json { "assignee": "fred", "task_id": "<uuid>" } ``` Result shape: ```json { "assignee": "fred", "shared_by": "homer", "task_id": "<uuid>" } ```

bump_overdue_priority

Write · Advanced

Raises every past-due live task below the target priority up to that priority. A maintenance pass for making overdue work surface again in priority order.

Full documentation →

Arguments, result and boundaries
Arguments
Optional: `target_priority`. - `target_priority`. Note: Must be low, medium, high, or critical (default high). Only tasks strictly below it move. Targeting low is a no-op reporting bumped 0 with an explanatory message.
Result
JSON object with bumped (how many raised) and target_priority. Targeting the bottom rung returns bumped 0 with a message instead.
Boundaries
Only past-due live tasks (type task) visible under the caller's project grants are candidates; finished content and foreign projects are never touched. With no visible scope the sweep succeeds with bumped 0.
Lifecycle
Each raise travels through the canonical mutation path and records an audit/history event, so the escalation stays attributable.
Errors
Bad types are rejected (strict validator). An unknown priority name is rejected with Invalid priority. Failures raise nothing (atomic).
Example
```json { "target_priority": "high" } ``` Result shape: ```json { "bumped": 2, "target_priority": "high" } ```

create_task_or_note

Write · Ordinary

Creates one task or note. The everyday write entry point: a task is actionable work tracked to done, a note is durable long-form content. Call it when new work, findings, or decisions must persist beyond the current session.

Full documentation →

Arguments, result and boundaries
Arguments
Required: `title`. Optional: `assignee`, `description`, `due_date`, `notes`, `parent_id`, `priority`, `project`, `recurring`, `reminder_at`, `section`, `type`. - `title`: Task title (required). - `assignee`. Note: Accepted by the schema but intentionally not stored at creation (oracle parity). Use assign_task afterwards, which records shared_by as the calling principal. - `description`: Primary task/note body and main long-form content. Note: Primary long-form content. notes is only for auxiliary or machine-readable metadata. - `due_date`: YYYY-MM-DD format or empty to skip. - `notes`: Secondary/internal notes or machine-readable metadata. - `parent_id`: UUID of parent task (for subtasks). - `priority`: low | medium | high | critical. - `project`: Project tag for grouping. - `recurring`: JSON config for recurrence (e.g. '{"every":"week","day":"monday"}'). - `reminder_at`: ISO datetime for reminder (e.g. '2026-03-15T14:00:00'). - `section`: inbox | today | next | someday | waiting. - `type`: task | note.
Result
JSON object with task_id (new UUID), title, type, and status (always not_started on creation).
Boundaries
Stored within the selected profile boundary only; visible only to principals whose project grants cover the given project. Untagged (no project) content lives in the untagged enclave and fails closed for principals without that grant. Creation never crosses profiles.
Lifecycle
Creates the task and records an audit/history event through the canonical mutation path. New tasks start not_started. No undo tool exists; later movement uses update_task (archive/cancel), which preserves history per the no-delete invariant.
Errors
Missing title is rejected by validation. Unknown arguments are rejected (strict validator). Out-of-scope project values deny generically. If the operation fails, nothing is partially created (atomic).
Example
```json { "project": "shared-ops", "section": "next", "title": "Draft Paul pack README", "type": "task" } ``` Result shape: ```json { "status": "not_started", "task_id": "<uuid>", "title": "Draft Paul pack README", "type": "task" } ``` task_id is assigned by the server; the placeholder stands for the returned UUID.

find_by_title

Read · Ordinary

Finds tasks, notes, and entities from a remembered phrase when the exact title is unknown. Scores title, description, notes, project, entity names, observations, and entity types, ranking live work above finished work under the memory_lookup_v2 contract.

Full documentation →

Arguments, result and boundaries
Arguments
Required: `title_fragment`. Optional: `limit`. - `title_fragment`. - `limit`. Note: Clamped to 1..100 (default 20); candidate pools scale from it internally.
Result
JSON object with matches (task/note entries or entity entries, each carrying score, matched_in, surface_scores, primary_surface, confidence, and ranking_contract_version), count, query, hidden_low_confidence_count, ranking_contract_version, lookup_strategy (fts_prefilter or full_scan_fallback), markdown, and message. A blank fragment returns matches [] with the message Empty title fragment.
Boundaries
Only content inside the caller's project grants is loaded and scored; foreign projects never appear, even as low-confidence hints. Finished entries stay reachable but always rank below live work.
Lifecycle
Pure read; scoring and ranking leave no trace on the matched content.
Errors
title_fragment is enforced-required at call time (missing input is rejected by validation) even though the display schema carries no required list. Unknown arguments and bad types are rejected (strict validator).
Example
```json { "limit": 5, "title_fragment": "Paul pack README" } ``` Result shape: ```json { "count": 1, "hidden_low_confidence_count": 0, "lookup_strategy": "fts_prefilter", "matches": [ { "confidence": "high", "id": "<uuid>", "kind": "task", "primary_surface": "title", "ranking_contract_version": "memory_lookup_v2", "score": 0.9, "title": "Draft Paul pack README" } ], "query": "Paul pack README", "ranking_contract_version": "memory_lookup_v2" } ```

query_tasks

Read · Ordinary

Lists live tasks and notes with combined filters, full-text search, sorting, and paging. The default workhorse view: finished work stays out unless explicitly asked for, and every non-empty page ships with a markdown rendering.

Full documentation →

Arguments, result and boundaries
Arguments
Optional: `include_completed`, `limit`, `offset`, `overdue_only`, `parent_id`, `priority`, `project`, `search`, `section`, `sort_by`, `sort_order`, `status`, `summary_only`, `type`. - `include_completed`. - `limit`. - `offset`. - `overdue_only`. Note: Shows only past-due content and always excludes finished content, even with include_completed. - `parent_id`. - `priority`. - `project`. - `search`. Note: Re-ranks matches by relevance and adds a per-item rank; sort order has little visible effect on that path. - `section`. - `sort_by`. Note: Allowlist: created_at, updated_at, due_date, priority, status, title, project, section, type. Anything else is rejected; each field has its own sensible default direction. - `sort_order`. - `status`. Note: Passing an explicit status (or include_completed) opts back in to done, archived, and cancelled content; otherwise it is excluded. - `summary_only`. - `type`.
Result
JSON object with tasks (full entries, or summaries with summary_only), count, total, offset, limit, and markdown. Paging adds has_more and next_offset when content remains. Empty results return tasks [], count 0, total, and the message No tasks match.
Boundaries
Only content inside the caller's project grants is ever searched or counted; anything else reads as no match. Out-of-scope project filters yield the empty shape, never a denial.
Lifecycle
Pure read; nothing is created, moved, or annotated as a side effect.
Errors
Unknown arguments and bad types are rejected (strict validator). Unknown sort_by or sort_order values are rejected with an invalid_sort message naming the allowlist.
Example
```json { "limit": 5, "section": "today" } ``` Result shape: ```json { "count": 1, "limit": 5, "markdown": "| # | Title | Status | Priority | Section | Due | Project | Created | Notes |", "offset": 0, "tasks": [ { "id": "<uuid>", "priority": "high", "section": "today", "status": "not_started", "title": "Draft Paul pack README" } ], "total": 1 } ```

ready_context

Read · Ordinary

Returns deterministic ready/prime context: live tasks scored into ready states with reasons, urgency, blockers, and provenance. The cross-project answer to what should be worked next, meant to be consulted before broad memory search.

Full documentation →

Arguments, result and boundaries
Arguments
Optional: `include_readings`, `limit`, `mode`. - `include_readings`. Note: Threaded into record building; off by default. - `limit`. Note: Clamped to 1..100 (default 12). - `mode`. Note: ready (scored records), suggested (task-shaped candidates), or prime (compact session boot pack). Anything else returns an error naming the valid modes.
Result
Versioned under contract_version ready_context.v1. ready returns mode, count, truncated, and items; suggested returns mode, count, and task-shaped items; prime returns the mandate, guidance, today_used, items_empty, top_ready_items, blocked_or_waiting, cleanup_candidates, explicit_exclusions, risk_or_escalation_items, evidence_refs, and mode. An invalid mode returns error plus valid_modes.
Boundaries
Only live content inside the caller's project grants is scored; finished content appears solely as review-only candidates or explicit exclusions. No visible scope reads as the empty shape for the requested mode, never a denial.
Lifecycle
Pure read; scoring, sorting, and sort_position stamping leave the underlying tasks untouched.
Errors
Unknown arguments and bad types are rejected (strict validator). An unknown mode is rejected with an error plus the valid mode list.
Example
```json { "limit": 12, "mode": "ready" } ``` Result shape: ```json { "contract_version": "ready_context.v1", "count": 2, "items": [], "mode": "ready", "truncated": false } ```

task_digest

Read · Ordinary

Renders the session-start digest: status counts plus the active today, inbox, and next sections, with past-due items highlighted first. A quick orientation read before choosing what to work on.

Full documentation →

Arguments, result and boundaries
Arguments
Optional: `include_overdue`, `limit`. - `include_overdue`. Note: When true (default), up to 10 past-due items lead the digest. - `limit`. Note: Caps the active section listing.
Result
JSON object with digest (markdown text starting with ## Task Digest), active_count, and overdue_count. An empty or out-of-scope view returns the bare header with both counts zero.
Boundaries
Only tasks inside the caller's project grants are counted and listed; notes are never part of the digest. No grants at all reads as an empty digest, never a denial.
Lifecycle
Pure read; rendering the digest changes nothing.
Errors
Unknown arguments and bad types are rejected (strict validator).
Example
```json { "include_overdue": true, "limit": 20 } ``` Result shape: ```json { "active_count": 3, "digest": "## Task Digest", "overdue_count": 1 } ```

update_task

Write · Ordinary

Updates one task or note in place. Only the supplied non-empty fields change, so it is the everyday tool for moving work through status and section, retitling, reprioritizing, rescheduling, or rehoming content across projects.

Full documentation →

Arguments, result and boundaries
Arguments
Required: `task_id`. Optional: `description`, `due_date`, `notes`, `parent_id`, `priority`, `project`, `recurring`, `reminder_at`, `section`, `status`, `title`, `type`. - `task_id`: UUID of the task to update (required). - `description`: New main task/note body. - `due_date`: YYYY-MM-DD or "CLEAR" to remove. - `notes`: New auxiliary/internal notes or "CLEAR" to remove. - `parent_id`: Parent UUID or "CLEAR" to remove. Note: The named parent must be visible to the caller; CLEAR detaches. - `priority`: low | medium | high | critical. - `project`: Project tag or "CLEAR" to remove. Note: Moving to a named project or to untagged (CLEAR) each needs the write grant for the destination. - `recurring`: JSON config or "CLEAR" to remove. - `reminder_at`: ISO datetime or "CLEAR" to remove. - `section`: inbox | today | next | someday | waiting. - `status`: not_started | in_progress | done | archived | cancelled. - `title`: New title. Note: Empty strings are ignored; only non-empty values are applied, except CLEAR which nulls the field. - `type`: task | note.
Result
JSON object with updated (the task UUID) and fields (the applied field names plus updated_at).
Boundaries
The target task must be visible under the caller's project grants; anything else answers Task not found, never distinguishing missing from foreign. Project and parent moves are each grant-checked at the destination.
Lifecycle
Applies the field changes through the canonical mutation path and records an audit/history event. Status moves (including to archived or cancelled) preserve history per the no-delete invariant.
Errors
Missing task_id is rejected by validation. Unknown arguments are rejected (strict validator). Bad enum, date, or recurrence values are rejected. Unknown or out-of-scope tasks and parents answer not-found. A call with no effective fields answers No fields to update. Failures leave nothing half-applied (atomic).
Example
```json { "status": "in_progress", "task_id": "<uuid>" } ``` Result shape: ```json { "fields": [ "status", "updated_at" ], "updated": "<uuid>" } ```

upsert_note_by_title_project

Write · Ordinary

Creates a note or updates the existing note with the same normalized title within the same project. The idempotent write surface for durable research and decision notes: repeated runs converge on one note instead of creating near-duplicates.

Full documentation →

Arguments, result and boundaries
Arguments
Required: `title`. Optional: `description`, `notes`, `priority`, `project`, `section`, `update_if_found`. - `title`: Note title (required). - `description`: Primary long-form note body. Note: Primary long-form content. notes is only for auxiliary or machine-readable metadata. - `notes`: Optional auxiliary/internal metadata. - `priority`: Priority for new notes; updates only when explicitly set. - `project`: Project tag for matching and grouping. Note: Empty or absent means untagged, which needs an explicit untagged grant. - `section`: Section for new notes; updates only when explicitly set. Note: Defaults to next and priority to medium on creation; on update they change only when explicitly set. - `update_if_found`: When false, return the existing row without mutation. Note: When false, a matched note is returned untouched with action existing.
Result
JSON object with task_id, title, type (always note), action (created, updated, or existing), and matched_on (always normalized_title_project). Creation also returns status not_started; updates return the changed fields list.
Boundaries
Matching and creation stay inside the selected profile boundary and the caller's project grants. Out-of-scope projects deny with a generic scope message; untagged writes fail closed without the untagged grant.
Lifecycle
Creates the note or applies the supplied fields through the canonical mutation path, recording an audit/history event. Matching is by normalized title plus project. No undo tool exists; later movement uses update_task, which preserves history per the no-delete invariant.
Errors
Blank title is rejected. Unknown arguments are rejected (strict validator). Out-of-scope projects deny generically. If the operation fails, nothing is partially created (atomic).
Example
```json { "description": "Chosen because WAL tolerates concurrent writers.", "project": "shared-ops", "title": "Bus Timeout Decision" } ``` Result shape: ```json { "action": "created", "matched_on": "normalized_title_project", "status": "not_started", "task_id": "<uuid>", "title": "Bus Timeout Decision", "type": "note" } ``` task_id is assigned by the server; the placeholder stands for the returned UUID.