Files
mcpctl/docs/proposals.md
Michal 56735a5290 docs: skills + revisions + proposals reference, plus cheatsheet update
Phase 7 of the Skills + Revisions + Proposals work — documentation
pass for the surface added in PR-1 through PR-6. Reference material
only; no code changes.

## What's added

- `docs/skills.md` — skill model, scoping rules, CLI surface, the
  `mcpctl config claude --project` setup flow, metadata schema (with
  the deferred-execution note for hooks/mcpServers/postInstall), the
  on-disk state file shape, atomic install mechanics, failure
  semantics, and what's deferred.
- `docs/revisions.md` — ResourceRevision model, semver auto-bump
  rules, contentHash diff key (cross-resource sync), CLI for history
  / diff / restore, RBAC, audit emission, storage growth note.
- `docs/proposals.md` — ResourceProposal model, the reviewer flow
  (CLI + web UI), atomic-approval mechanics, the propose_prompt /
  propose_skill MCP tools, the propose-learnings global skill that
  steers Claude toward engaging with them, and the deferred legacy
  PromptRequest cutover.

## What's edited

- Top-level `CLAUDE.md` — resource cheatsheet adds `skill`, `proposal`,
  `revision` with cross-references to the new docs. The legacy
  `promptrequest` entry stays (still on the legacy code path) but
  notes that new work should use `proposal`.

## What's NOT in this PR

- The PromptRequest → ResourceProposal cutover migration. Both run
  side-by-side today; the focused cutover PR will rename + backfill +
  drop. Keeping that out of PR-7 means review can stay on docs.
- Bundle-backup / `mcpctl apply -f` skill support (deferred from PR-3).
- `metadata.hooks` / `metadata.mcpServers` / `metadata.postInstall`
  execution (deferred from PR-5).
- Existing-page UI migration to Tailwind (deferred from PR-6 — old
  inline-styled pages coexist fine inside the new Layout).

These are tracked as future PRs; each is its own focused change.

## Verification

`pnpm test:run` whole monorepo: 162 test files / 2157 tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:58:04 +01:00

5.3 KiB

Resource Proposals

A proposal is a pending change to a Prompt or Skill, submitted by either a Claude Code session (via the propose_prompt / propose_skill MCP tools) or a human (via the web UI / CLI). Reviewers triage the queue and either approve — at which point the proposal becomes a real prompt or skill — or reject with a note.

This is the path by which Claude proposes back to mcpd: things the session learned that future sessions would benefit from. The propose-learnings global skill (seeded by mcpd at startup) explains the discipline to Claude.

Model

ResourceProposal shares the schema's discriminator pattern with ResourceRevision — single table, resourceType field disambiguates prompts vs skills.

Field Purpose
resourceType 'prompt' | 'skill'.
name Proposed resource name.
body Proposed body ({ content, priority?, metadata?, … }).
projectId / agentId Scope of the proposal (XOR; null/null = global).
createdBySession mcplocal session that proposed (when from Claude).
createdByUserId User who proposed (when via UI/CLI).
status 'pending''approved' | 'rejected'.
reviewerNote Set on approval or rejection.
approvedRevisionId Set when approved — points at the resulting revision.

Two unique constraints — (resourceType, name, projectId) and (resourceType, name, agentId) — mirror the Prompt / Skill scoping rules. The same ?? '' workaround for nullable-FK lookups applies.

Reviewer flow

CLI

mcpctl review pending                          # list pending
mcpctl review next                             # show oldest pending
mcpctl review show <id>                        # full detail
mcpctl review diff <id>                        # before/after diff
mcpctl review approve <id>                     # POST /proposals/:id/approve
mcpctl review reject <id> --reason "explain"   # rejected with note

Web UI

/proposals shows a Pending / Approved / Rejected tab view; the sidebar nav badge polls every 30 s and shows the pending count in amber. Click a row to see the full body, the diff against the current resource (if any), and approve / reject controls.

Approval is atomic

Approval runs in a single Prisma transaction:

  1. Look up the pending proposal.
  2. Dispatch by resourceType to the registered handler (PromptService or SkillService registers itself at construction).
  3. Handler upserts the underlying resource — creating it if new, or updating + auto-bumping patch semver if it exists.
  4. Handler records a ResourceRevision linking back to the proposal.
  5. Proposal status flips to approved, approvedRevisionId set.

If any step fails, the transaction rolls back and the proposal stays pending. There is no half-approved state.

Claude side: propose_prompt and propose_skill

Both tools are registered by the gate plugin in mcplocal. They post to /api/v1/proposals with the appropriate resourceType.

The propose-learnings global skill (seeded by mcpd) tells Claude when to use them:

  • propose_prompt for project-specific knowledge — gotchas, conventions, hidden constraints. Cheap to add, easy to reject.
  • propose_skill for cross-cutting knowledge — debugging discipline, release hygiene, security review style. Larger blast radius; lean toward propose_prompt unless you have a clear cross-project reason.

The gate-encouragement-propose system prompt (priority 10, sits in the gating bundle) is the trigger that makes Claude actually consider proposing. Without that, the tools exist but Claude rarely engages.

Backwards compat

PR-1 / PR-2 deferred the cutover from the prompt-only PromptRequest table to ResourceProposal. Both run side-by-side today:

  • mcplocal's propose_prompt still POSTs to the legacy /api/v1/projects/:name/promptrequests URL.
  • mcplocal's propose_skill (newer) POSTs to /api/v1/proposals directly.
  • The legacy /api/v1/promptrequests* routes remain in mcpd.
  • mcpctl approve promptrequest <name> still works.

A focused follow-up PR will:

  1. Migrate existing PromptRequest rows into ResourceProposal (resourceType=prompt).
  2. Rename PromptRequest to _PromptRequest_legacy.
  3. Update mcplocal's propose_prompt to use /api/v1/proposals.
  4. Keep the legacy URL as a thin translation shim through one release.
  5. Drop _PromptRequest_legacy after that.

This stays separate so the cutover is reviewable independently of the larger Skills + Revisions + Proposals work.

RBAC

Proposals piggyback on the prompts permission for now — anyone with view:prompts can read the queue, anyone with edit:prompts can approve or reject. Splitting out a dedicated proposals permission (or a "reviewer" role) is straightforward if granularity becomes useful.

Audit emission

Proposal create / approve / reject events flow through the existing audit pipeline. Approval events also reference the resulting revision id, so you can join "proposal approved at T" against "revision X created at T" without polling.