- using-projects-meta 1.1.0 → 1.2.0: examples qualified (`victor/books`, `OpeItcLoc03/claude-skills`), `target_project` description rewrites _meta → `agenda` literal + bare-name reject; new common-mistake row; cross-ref to `concepts/projects-meta-multi-owner`. - setup-projects-meta 1.0.1 → 1.1.0: auth.toml template now reflects v2.x schema (`gitea_owners`, `agenda_tasks_repo` qualified, optional `gitea_aggregate_skip_owners`); added schema-notes block with backwards-compat for legacy `gitea_user`-only installs. - closes [update-using-projects-meta-qualified-names] + [update-setup-projects-meta-auth-toml] (last 2 of 14 multi-owner umbrella blockers). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
238 lines
14 KiB
Markdown
238 lines
14 KiB
Markdown
---
|
||
name: using-projects-meta
|
||
version: 1.2.0
|
||
description: Use when working across multiple projects on one or many machines — cross-project task aggregation (`mcp__projects-meta__tasks_*`), shared Gitea-backed wiki query / ingest (`mcp__projects-meta__knowledge_*`), or sync diagnostics (`mcp__projects-meta__meta_status`). Triggers on phrases like "across all projects", "what's on the boards", "check shared wiki", "search projects-wiki", "ingest into shared wiki", "что у меня на досках", "по всем проектам", "общая вики", "cross-project status", or any time the user wants to see / mutate state in another repo than the current cwd. v1.1.0 mandates a Step 0 freshness gate (probe `meta_status`, sync if stale, pull `projects-wiki` before shared-wiki writes) — see SKILL body. Mutation tools require two-step preview → confirm. Skip for the **current** project's tasks/wiki — those live on disk in `.tasks/` / `.wiki/`.
|
||
---
|
||
|
||
# Using the projects-meta MCP server
|
||
|
||
## Overview
|
||
|
||
`projects-meta-mcp` is a local stdio MCP server. Two responsibilities:
|
||
|
||
1. **Cross-project task aggregation** — parses `.tasks/STATUS.md` from every repo on the user's Gitea, caches them in `~/.cache/projects-mcp/tasks.json`. Read tools (`tasks_aggregate`, `tasks_search`, `tasks_get`) hit the cache. Mutations (`tasks_create`, `tasks_update`, `tasks_close`) commit back to Gitea with sha-based optimistic lock.
|
||
2. **Shared knowledge wiki** — single Gitea repo (`projects-wiki`) cloned at `~/projects/projects-wiki/` with content at `~/projects/projects-wiki/.wiki/`, structured as packages / concepts / entities / sources / raw. `knowledge_search` + `knowledge_get` for queries, `knowledge_ingest` + `knowledge_promote` for writes.
|
||
|
||
Source of truth: Gitea (`https://git.kzntsv.site`, owner `OpeItcLoc03`). Cache and clone are local convenience.
|
||
|
||
Full reference: `mcp__projects-meta__knowledge_get slug=packages/projects-meta-mcp`.
|
||
|
||
## Prerequisites
|
||
|
||
This skill assumes `mcp__projects-meta__*` tools are available. If they aren't (tools missing from the session, or calls fail with a connection error), the server isn't running for this session. Trigger the **`setup-projects-meta`** skill to clone, build, write `auth.toml`, and register `mcpServers.projects-meta` in `~/.claude.json`. It's a one-time procedure with confirmation gates.
|
||
|
||
## Local-first rule
|
||
|
||
**For the current project — read disk directly.** `.tasks/STATUS.md` and `.wiki/` files in cwd are always fresher than the MCP cache. The cache:
|
||
|
||
- May be stale (default sync runs only when triggered).
|
||
- Hides 🟢 done by default.
|
||
- May not contain locally-developed projects that aren't pushed to Gitea yet.
|
||
|
||
Use MCP only for **other** projects, **other** machines, or **shared** wiki content.
|
||
|
||
| Question | Where to read |
|
||
|---|---|
|
||
| "What's the status of *this* project?" | local `.tasks/STATUS.md` |
|
||
| "What's on all my boards?" | `mcp__projects-meta__tasks_aggregate` |
|
||
| "Has *this* project's wiki got a page on X?" | local `.wiki/index.md` + relevant file |
|
||
| "Has the **shared** wiki got a page on X?" | `mcp__projects-meta__knowledge_search` |
|
||
| "Sync state across machines?" | `mcp__projects-meta__meta_status` |
|
||
|
||
## When to use
|
||
|
||
- Cross-project task overview ("what am I working on across projects", "по всем проектам", "across the board").
|
||
- Hopping into another repo's task state without cloning it ("what's the status of project X").
|
||
- Querying the shared wiki for cross-cutting concepts (patterns, package references, design notes that apply to several repos).
|
||
- Ingesting a finished design / decision into the shared wiki so other machines / projects can see it.
|
||
- Creating a task in another project's `.tasks/STATUS.md` from the current repo (cross-project handoff).
|
||
- Sync diagnostics (when did the cache last refresh, are there errors, how many projects).
|
||
|
||
## When NOT to use
|
||
|
||
- The current project's own tasks or wiki — read disk.
|
||
- Anything inside a single project — `.tasks/<task>.md` and `.wiki/<page>.md` are always closer.
|
||
- One-off questions answered by `git log` or a single file.
|
||
- Library / framework documentation — that's `using-context7`.
|
||
- Code search — that's `Glob` / `Grep`.
|
||
|
||
## Step 0 — Freshness gate (run before any tool)
|
||
|
||
`projects-meta` is a bus between machines. Another host may have pushed minutes ago. Without this gate, reads return stale data and writes hit sha-based optimistic-lock 422s with no explanation. Mandatory pre-flight, every session, every workflow:
|
||
|
||
```
|
||
1. Call mcp__projects-meta__meta_status.
|
||
|
||
2. Branch on cache freshness:
|
||
• If cache_age_minutes > 10 OR errors_count > 0:
|
||
run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js`
|
||
(or `npm run sync` from ~/projects/.common/lib/projects-meta-mcp).
|
||
• Else: cache is fresh enough — skip sync, no need to hit the network.
|
||
|
||
3. For shared-wiki WRITES (knowledge_ingest, knowledge_promote):
|
||
ALWAYS additionally run `git -C ~/projects/projects-wiki pull --ff-only`
|
||
regardless of cache age. The MCP server uses sha-based optimistic locking;
|
||
without an up-to-date local file SHA, the commit will be rejected (422)
|
||
and the failure mode is opaque to the user.
|
||
|
||
4. For tasks-mutations (tasks_create, tasks_update, tasks_close):
|
||
sync via dist/sync.js is enough — there's no local clone of the target
|
||
tasks repo, mutations go straight through Gitea API. Sync only refreshes
|
||
the local view so you reason from current state.
|
||
|
||
5. If sync returns 401 or 403:
|
||
STOP. The Gitea token in ~/.config/projects-mcp/auth.toml is dead or
|
||
wrong-scoped. Tell the user explicitly:
|
||
"Gitea sync failed with <401|403>. Rotate gitea_token in
|
||
~/.config/projects-mcp/auth.toml (Gitea: settings/applications)
|
||
and rerun."
|
||
Do not pretend sync succeeded. Do not retry silently.
|
||
```
|
||
|
||
**Don't sync unconditionally on every call.** Network overhead + risk of 401 even on a casual "what's on my boards". The 10-minute cache window is the right balance — catches multi-machine drift without burning Gitea round-trips for back-to-back questions.
|
||
|
||
**Don't apply Step 0 to `meta_status` itself** — it's the freshness probe, not a downstream read.
|
||
|
||
## Workflow
|
||
|
||
### Read (no confirmation needed)
|
||
|
||
```
|
||
0. Run Step 0 — Freshness gate (above) first.
|
||
1. Identify what you need: cross-project tasks? shared wiki page? sync state?
|
||
2. Pick the right read tool (table below).
|
||
3. Cite the result with the source slug / project name.
|
||
```
|
||
|
||
### Mutate (always two-step)
|
||
|
||
```
|
||
0. Run Step 0 — Freshness gate (above) first.
|
||
For shared-wiki writes (knowledge_ingest, knowledge_promote): unconditional
|
||
`git -C ~/projects/projects-wiki pull --ff-only` is part of Step 0.
|
||
1. Identify the mutation: tasks_create / tasks_update / tasks_close / knowledge_ingest / knowledge_promote.
|
||
2. Call the tool WITHOUT `confirm: true` → returns a dry-run preview (the proposed file diff and the Gitea commit message).
|
||
3. Show the preview to the user. Wait for explicit "ok" / "go" / "поехали".
|
||
4. Re-call with `confirm: true` to commit.
|
||
```
|
||
|
||
**Never inline `confirm: true` on the first call.** A trigger phrase ("create a task in project X") is permission to *plan*, not to *commit*.
|
||
|
||
## Tool quick reference
|
||
|
||
### Read tools
|
||
|
||
| Tool | Required args | Purpose |
|
||
|---|---|---|
|
||
| `mcp__projects-meta__tasks_aggregate` | — | All active tasks across all cached projects |
|
||
| `mcp__projects-meta__tasks_search` | `query` | Substring search across slug + next_action |
|
||
| `mcp__projects-meta__tasks_get` | `project` | Raw STATUS.md of one project (cached snapshot) |
|
||
| `mcp__projects-meta__knowledge_search` | `query`; opt `domain`, `limit` | Shared-wiki search; auto-detects domain from cwd, pass `domain="all"` to disable |
|
||
| `mcp__projects-meta__knowledge_get` | `slug` | Full text of one wiki page (e.g. `packages/projects-meta-mcp`) |
|
||
| `mcp__projects-meta__knowledge_suggest_promote` | — | Local `.wiki/concepts/` candidates for shared-wiki promotion |
|
||
| `mcp__projects-meta__meta_status` | — | Sync diagnostics: cache age, project count, error count, page count |
|
||
|
||
### Mutation tools (need `write:repository` Gitea scope; preview → confirm)
|
||
|
||
| Tool | Required args | Effect |
|
||
|---|---|---|
|
||
| `mcp__projects-meta__tasks_create` | `target_project`, `slug`, `description`, `next_action` (+ opt `where_stopped`, `status`, `blocker`, `branch`, `source_project`) | Append block to `<target>/.tasks/STATUS.md` via Gitea commit |
|
||
| `mcp__projects-meta__tasks_update` | `target_project`, `slug` + ≥1 of `where_stopped` / `next_action` / `blocker` / `branch` / `description` / `status` | Sha-based optimistic lock; 422 on conflict |
|
||
| `mcp__projects-meta__tasks_close` | `target_project`, `slug` (+ opt `note`) | Sets task to 🟢 done; appends identity-footer |
|
||
| `mcp__projects-meta__knowledge_ingest` | `target_project`, `type`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Three commits: `<type>/<slug>.md` + `index.md` + `log.md`. `type` ∈ entities / concepts / packages / sources / raw |
|
||
| `mcp__projects-meta__knowledge_promote` | `target_project`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Move `raw/<slug>.md` → `sources/<slug>.md` with auto `raw_path` link |
|
||
|
||
`target_project` is **qualified** `<owner>/<repo>` (e.g. `victor/books`, `OpeItcLoc03/claude-skills`), or the literal `agenda` for the cross-project meta-board (resolves via `agenda_tasks_repo` in `auth.toml`). Bare names (`books`) are rejected with a hint to use the qualified form. Cross-cutting design: shared wiki → `concepts/projects-meta-multi-owner`.
|
||
|
||
## Examples
|
||
|
||
### Read example: cross-project status
|
||
|
||
User: "что у меня на досках?"
|
||
|
||
```
|
||
1. mcp__projects-meta__tasks_aggregate
|
||
→ 7 projects, 12 active tasks
|
||
|
||
2. Group by project, summarize 1 line per active task.
|
||
Cite project name; if a task is stale (cache age > 1h), flag it.
|
||
```
|
||
|
||
### Read example: shared wiki query
|
||
|
||
User: "есть ли в общей вики что-то про setup-using паттерн?"
|
||
|
||
```
|
||
1. mcp__projects-meta__knowledge_search
|
||
query: "setup-using skill pair pattern"
|
||
domain: "all"
|
||
→ hits include concepts/setup-using-skill-pair
|
||
|
||
2. mcp__projects-meta__knowledge_get
|
||
slug: "concepts/setup-using-skill-pair"
|
||
→ full text
|
||
|
||
3. Summarize, link with markdown to the slug.
|
||
```
|
||
|
||
### Mutation example: create cross-project task
|
||
|
||
User: "заведи в проекте books задачу на миграцию `settings.json`"
|
||
|
||
```
|
||
1. mcp__projects-meta__tasks_create
|
||
target_project: "victor/books"
|
||
slug: "settings-json-migration"
|
||
description: "<...>"
|
||
next_action: "<...>"
|
||
(no `confirm`)
|
||
→ preview: proposed STATUS.md diff + commit message
|
||
|
||
2. Show preview to user.
|
||
|
||
3. User: "ok, go"
|
||
|
||
4. mcp__projects-meta__tasks_create
|
||
(same args + confirm: true)
|
||
→ committed to Gitea
|
||
```
|
||
|
||
### Mutation example: closing a cross-project task
|
||
|
||
User: "close `[projects-meta-skills]` in claude-skills"
|
||
|
||
```
|
||
1. mcp__projects-meta__tasks_close
|
||
target_project: "OpeItcLoc03/claude-skills"
|
||
slug: "projects-meta-skills"
|
||
note: "<one-line summary>"
|
||
(no `confirm`)
|
||
→ preview
|
||
|
||
2. User confirms.
|
||
|
||
3. Re-call with confirm: true.
|
||
```
|
||
|
||
## Common mistakes
|
||
|
||
| Mistake | Fix |
|
||
|---|---|
|
||
| Reading current project's tasks via `tasks_get` instead of disk | Read `.tasks/STATUS.md` directly. MCP is for *other* projects. |
|
||
| Inlining `confirm: true` on the first mutation call | Always preview first; show user; only then `confirm: true`. |
|
||
| Using `knowledge_search` for the project's own wiki | The shared wiki is a separate Gitea repo. Local `.wiki/` is in cwd. |
|
||
| Acting on a stale `tasks_aggregate` without checking `meta_status` | Step 0 — Freshness gate is mandatory. If `cache_age_minutes` > 10 (or errors > 0), run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js` first. |
|
||
| Skipping `git -C ~/projects/projects-wiki pull` before `knowledge_ingest` / `knowledge_promote` | sha-based optimistic lock will reject the commit (422) and the failure is opaque. Pull is unconditional for shared-wiki writes — fast-forward is a no-op when current. |
|
||
| Treating sync 401/403 as "MCP is fine, the page just doesn't exist yet" | 401/403 means the Gitea token is dead. Stop, tell the user to rotate `gitea_token` in `~/.config/projects-mcp/auth.toml`. Never guess on stale data. |
|
||
| Calling `knowledge_ingest` with the wrong `type` | `type` must be one of `entities` / `concepts` / `packages` / `sources` / `raw`. Mis-typed pages land in the wrong section and break `index.md`. |
|
||
| Vague `knowledge_search` queries ("auth", "config") | Specific multi-word queries return targeted snippets; vague ones return noise. |
|
||
| Forgetting `domain="all"` when searching across families | Default `domain` is auto-detected from cwd; use `"all"` if the wiki page lives in a different family. |
|
||
| Passing bare project name (`target_project: "books"`) to mutation tools | v2.x rejects bare names. Use qualified `<owner>/<repo>` (e.g. `victor/books`, `OpeItcLoc03/claude-skills`). Literal `agenda` is the only exception (cross-project meta-board). |
|
||
|
||
## Red flags
|
||
|
||
- "I'll just commit it directly" → no. Mutation tools have a preview step for a reason — silent writes to another repo are a recipe for drift.
|
||
- "The cache is fresh enough" → run Step 0. The 10-minute window is the threshold; below it skip sync, above it sync. Don't eyeball this — the bus moves fast in cross-machine sessions.
|
||
- "I'll skip the pull, my last write was 30 seconds ago" → another machine pushed in between. Always pull before shared-wiki writes; the sha-lock check is your only safety net.
|
||
- "I'll skip the wiki page" → if you're answering a cross-cutting question and there's no wiki page, that's a `knowledge_ingest` candidate. Surface it to the user.
|