§ คู่มือเริ่มต้น

เชื่อม Claude Code กับ paper.farzai.com

≈ 30 seconds · or 3 manual steps

ใช้ทางลัดให้ Claude เซ็ตให้ในไม่กี่วินาที — หรือทำตามขั้นตอนละเอียดด้านล่าง

§สารบัญ

§ ทางลัด

ให้ Claude ตั้งค่าให้

paste this into Claude Code

ถ้ามี Claude Code ติดตั้งอยู่แล้ว วาง prompt นี้ลงไปได้เลย Claude จะเรียก claude mcp add แล้วเปิด browser ให้คุณ approve OAuth — ไม่ต้องคัดลอก token เอง

→ paste into Claude Code
Set up paper.farzai.com for Claude Code. Re-paste this prompt anytime to update skills and references.

1. Register the MCP server (skip with a one-liner note if it's already registered):

   claude mcp add --transport http --scope user paper-personal https://paper.farzai.com/api/mcp

   If the command prints an OAuth URL, show it to me and wait until I confirm I've approved in the browser. The consent screen lets me pick which vaults the connector can access — no vault id needed up front. (If the connection is already approved, Claude Code will just say "already exists". That's fine, move on.)

2. Install or update every paper skill. The curl flags overwrite local copies, so this also doubles as the update flow:

   for skill in paper-ask paper-contribute paper-help paper-research paper-save; do
     mkdir -p ~/.claude/skills/$skill
     curl -fsSL https://paper.farzai.com/docs/skills/$skill.md -o ~/.claude/skills/$skill/SKILL.md
   done
   
   mkdir -p ~/.claude/skills/paper-research/references
   curl -fsSL https://paper.farzai.com/docs/skills/paper-research/references/program.md \
     -o ~/.claude/skills/paper-research/references/program.md

3. Tell me to restart Claude Code so the new/updated skills register.

4. After restart, suggest I run /paper-help — it should call get_skill_versions and report every paper-* skill "in sync" with the server.
§หรือทำตามขั้นตอนละเอียดทีละข้อ
01

เชื่อมด้วย claude mcp add (Claude Code · CLI · Desktop)

ถ้ามี Claude Code หรือ Claude Desktop (CLI mode) ติดตั้งอยู่แล้ว รันคำสั่งนี้:

terminal — claude mcp add
claude mcp add --transport http --scope user paper-personal https://paper.farzai.com/api/mcp

Claude Code จะพิมพ์ URL ออกมา — เปิดใน browser แล้ว Approve หน้าจอ consent จะถามว่าจะให้ Claude เข้าถึง vault ไหนบ้าง (ติ๊กได้หลาย vault พร้อมกัน) เมื่อ approve เสร็จ Claude จะใช้งานได้ทันที (OAuth handle credential ให้เอง — ไม่ต้องคัดลอก token)

ไม่ต้องเตรียม vault ID — URL /api/mcp เปิดทุก vault ที่คุณ approve. Claude จะเรียก tool list_my_vaults ก่อนเพื่อหา vault id ที่ใช้ได้ แล้วใส่ vault id ลงในแต่ละ tool call เอง

จะติดตั้ง global vault ด้วยให้รัน: claude mcp add --transport http --scope user paper-global https://paper.farzai.com/api/mcp/global

ทางเลือก — pin connector กับ vault เดียว

ถ้าอยาก isolate connector ไว้กับ vault เดียว (เช่นใช้ paper-personal-work / paper-personal-home แยกกัน) ใช้ URL /api/mcp/v/<vault-id> แทน

terminal — claude mcp add (per-vault)
claude mcp add --transport http --scope user paper-personal https://paper.farzai.com/api/mcp/v/YOUR_VAULT_ID

หา vault id ได้ที่ /app/vaults — UUID ใน URL ของ vault. URL แบบนี้จะ pin connector กับ vault นั้นเท่านั้น คนละ connector ต่อ vault

02

หรือผ่าน Claude Desktop Connectors directory (เร็วๆ นี้)

⏳ Connector listing ของ paper.farzai.com ยังอยู่ระหว่างรอ Anthropic อนุมัติเข้าไดเรกทอรี ระหว่างนี้ใช้ขั้นตอน 01 ด้านบนได้เลย — ผลลัพธ์เหมือนกัน. หัวข้อนี้เก็บไว้สำหรับอนาคต เมื่อ listing เปิดแล้ว

เมื่อ listing เปิด: ใน Claude Desktop เปิด Customize Connectors แล้วค้นหา paper.farzai.com ใน Connectors directory กด Install แล้วทำตามขั้นตอนใน popup ของ Claude

คุณยังไม่ต้องสมัครสมาชิกก่อนก็ได้ — Anthropic จะเปิดหน้า paper.farzai.com ให้สมัครและล็อกอินครั้งเดียว จากนั้นจะให้คุณเลือก vault ที่ต้องการให้ Claude เข้าถึง กด Approve แล้ว Claude Desktop จะกลับมาพร้อมใช้งานทันที

03

ติดตั้ง skill (ทางเลือก)

Skills เป็นไฟล์ markdown ที่ Claude Code โหลดเข้ามาเป็น shortcut — เช่น /paper-save เซฟแชทเข้า vault, /paper-ask ค้นและสรุปจาก vault, /paper-contribute ส่ง PR เข้า global vault

วิธีติดตั้งแบบเร็ว (ใช้ curl):

install paper-* skills
# Install or update all paper skills + reference files.
# Re-run anytime to update to the latest server versions (curl with -o overwrites).
# Skill list comes from the SSR loader — `/paper-help` calls get_skill_versions
# after install to detect any drift between local cache and server canonical.
for skill in paper-ask paper-contribute paper-help paper-research paper-save; do
  mkdir -p ~/.claude/skills/$skill
  curl -fsSL https://paper.farzai.com/docs/skills/$skill.md -o ~/.claude/skills/$skill/SKILL.md
done

mkdir -p ~/.claude/skills/paper-research/references
curl -fsSL https://paper.farzai.com/docs/skills/paper-research/references/program.md \
  -o ~/.claude/skills/paper-research/references/program.md

# Then restart Claude Code so the new/updated skills register.

ดูรายละเอียดทุก skill และเนื้อในไฟล์ทั้งหมดได้ที่ § Skills ด้านล่าง

§รายละเอียด OAuth (สำหรับ power user) — OAuth flow internals

ขั้น 01–02 ด้านบนใช้ claude mcp add ซึ่ง handle ทุกอย่างให้แล้ว ส่วนนี้อธิบายว่า Claude Code กับ paper.farzai.com คุยกันยังไง — ใช้สำหรับ debug หรือ self-hosted client ที่ต้อง implement OAuth flow เอง

paper.farzai.com เป็น OAuth 2.1 authorization server ตามมาตรฐาน — รองรับ Dynamic Client Registration (RFC 7591), PKCE S256 (plain rejected) และ refresh-token rotation ทุกครั้งที่ใช้

Discovery
/.well-known/oauth-authorization-server
DCR
POST /oauth/register
Authorize
/oauth/authorize (code + PKCE)
Token
/oauth/token
Revoke
/oauth/revoke

Claude Code จะ register client ผ่าน DCR endpoint อัตโนมัติครั้งแรกที่ใช้ จากนั้นเปิด browser ที่/oauth/authorize ให้คุณ approve scope แล้วเก็บ access/refresh token ลงเครื่อง (ไม่ต้องคัดลอกเอง)

ถ้าต้องการเชื่อมหลาย endpoint พร้อมกัน (personal vault + global community vault):

terminal — claude mcp add (dual)
claude mcp add --transport http --scope user paper-personal https://paper.farzai.com/api/mcp
claude mcp add --transport http --scope user paper-global https://paper.farzai.com/api/mcp/global

จัดการ client ที่เคย approve และดู refresh-token activity ทั้งหมดได้ที่ /app/connections (revoke แต่ละ client ได้ทันที — token + grant chain จะถูก revoke พร้อมกัน)

§ Skills

Skills สำหรับ Claude Code

Skill คือไฟล์ markdown ที่ Claude Code โหลดเข้ามาเป็น shortcut — เช่น /paper-save สำหรับเซฟแชทเข้า vault แต่ละ skill ด้านล่างก็อปปี้ลงไฟล์ ~/.claude/skills/<ชื่อ-skill>/SKILL.md ได้เลย หรือใช้ curl ดึง raw markdown จาก /docs/skills/<ชื่อ>.md

คลิกที่ชื่อ skill เพื่อขยายดูเนื้อ markdown เต็ม

§

paper-ask

v1.1.0

triggers /paper-ask · what do I know about · search my vault for · from my notes · based on my vault · ask my paper vault

~/.claude/skills/paper-ask/SKILL.md
---
name: paper-ask
version: 1.1.0
description: /paper-ask, what do I know about, search my vault for, from my notes, based on my vault, ask my paper vault
allowed-tools: Read Glob Grep
---

# paper-ask

Answer a question by searching the user's `paper-*` MCP vault, reading relevant notes, and synthesizing an answer with `[[wikilink]]` citations.

This is the read-side counterpart to `paper-save`. It treats the vault as the source of truth for "what do I know" questions and refuses to hallucinate when the vault is empty on a topic.

## When to invoke

- `/paper-ask <question>`
- "what do I know about X"
- "search my vault for X"
- "from my notes, …"
- "based on my vault, …"

## Server selection

Same as `paper-save`. Pattern-match `mcp__paper-[a-z0-9-]+__`. If both personal and global are bound, default to the last-used server. Allow explicit override:

- `paper-ask global <question>` → forces global vault
- `paper-ask personal <question>` → forces personal vault

If neither was used in this session yet, default to personal.

## Three modes

| Mode | Tool budget | When to use |
|---|---|---|
| `quick` | 1× `search_notes` → return top 3 with snippets | Browsing, "what notes do I have on X" |
| `standard` (default) | 1× `search_notes` + 3-5× `read_note` → synthesize with citations | Most questions |
| `deep` | search + read + `get_backlinks` per top hit → multi-source synthesis with gap analysis | "Compare …", "Everything I know about …" |
| `exhaustive` | uncapped — explicit opt-in | User explicitly says "uncapped" or "exhaustive" |

Mode selection heuristic:
- If user prefixes with `quick `, `deep `, or `exhaustive ` → use that mode.
- Otherwise default to `standard`.

### Cross-vault deep mode (both personal and global bound)

When both servers are bound AND mode is `deep`:
- Cap total reads to **12** (≤ 6 per side).
- Cap total `get_backlinks` calls to **4**.
- Search both sides in parallel: `mcp__paper-personal__search_notes` + `mcp__paper-global__search_notes`.
- Label each citation with its source: `[[Title]] (global)` vs `[[Title]] (personal)`.

If the user wants no caps, they explicitly say "exhaustive" — no caps apply.

## Workflow

### 1. Search (no separate probe)

Do NOT probe `get_vault` first — that tool exists only on personal servers, not on the global vault (`/api/mcp/global`), so a probe breaks every global-mode query. Confirm the binding with the first real call instead: `search_notes({ query: <user question>, limit: 10 })`. A `401` on this call triggers the OAuth-refresh flow in the error matrix below.

If search returns `search_unavailable: true` (Meilisearch is down on the server side), warn the user once:

> Note: full-text search is degraded. Falling back to folder listing — results may be less precise.

Then call `list_notes({ folder: "notes" })` and use simple title-substring match.

### 2. Quick mode

Return the top 3 results with their snippets. Format:

```
- [[Title 1]] — <snippet>
- [[Title 2]] — <snippet>
- [[Title 3]] — <snippet>
```

Stop here.

### 3. Standard mode

For the top 3-5 search hits, call `read_note({ idOrPath })` and load the body + frontmatter.

Synthesize an answer (3-8 sentences) where every factual claim cites a wikilink: `[[Title of source]]`.

Format:
```
<concise synthesis with [[Title 1]] and [[Title 2]] citations>

Sources:
- [[Title 1]] (created YYYY-MM-DD)
- [[Title 2]] (created YYYY-MM-DD)
```

### 4. Deep mode

Standard mode +:
- For each of the top 3 hits, also call `get_backlinks({ noteId })` to find related notes.
- Read up to 2 additional backlinked notes per source.
- Identify contradictions across sources — flag them as `> ⚠ Contradiction: [[A]] says X, [[B]] says Y`.
- End with a "Gaps" section listing things the vault doesn't cover.

### 5. Empty-vault handling

If `search_notes` returns 0 hits AND `list_notes` for the most plausible folder is also empty:

> Your vault doesn't have any notes on this topic. I can answer from general knowledge if you want, but the answer wouldn't be grounded in your second brain. Say "use general knowledge" to proceed, or "save this answer" if you'd like me to write a starter note first.

Do NOT silently fall through to training-data answers when the user scoped the question to "my vault".

## Citation rule

Every factual claim cites the source note as a wikilink: `[[Title]]`. This is the form that resolves on paper.farzai.com via `src/lib/markdown/wikilinks.ts`.

Do not paraphrase a single source as if it were multiple. If only one note discusses the topic, the answer should cite that one note multiple times — not pretend to draw from many.

## Error matrix

| Error | Action |
|---|---|
| `401 unauthorized` | Check the `WWW-Authenticate` header. If `resource_metadata=...` is present, Claude Code handles the OAuth refresh — retry the call once. If the retry also 401s, direct the user to `/app/connections` to reconnect. |
| `429 rate_limited` | Wait, retry once. If still 429, downgrade mode (deep → standard, standard → quick). |
| `search_unavailable: true` | Warn once, fall back to `list_notes` + title-substring match. |
| `note_not_found` mid-read | Skip that result, continue with the remaining. |

## Examples

User: "/paper-ask deep what do I know about cache invalidation"

You:
1. `search_notes({ query: "cache invalidation", limit: 10 })` → 6 hits.
2. Read top 3 hits via `read_note`.
3. For each, `get_backlinks` → 2 more relevant notes read.
4. Synthesize:
   > Three approaches surface in my notes: TTL-based ([[Cache TTL strategies]]), event-driven ([[Event-sourced caching]]), and lazy ([[Lazy invalidation patterns]]). [[Event-sourced caching]] notes a contradiction with [[Cache TTL strategies]] about staleness windows — see ⚠ below.
   > ...
5. Output the synthesis + sources + gaps.

User: "what does the global vault say about MCP" (both servers bound, `paper-global` last used)

You:
1. Default server = `paper-global`.
2. `mcp__paper-global__search_notes({ query: "MCP", limit: 10 })`.
3. Mode default = standard.
4. Read top 3, synthesize, label each citation with `(global)`.

หรือดึงผ่าน curl -s /docs/skills/paper-ask.md (ดูที่ /docs/skills/paper-ask.md)

§

paper-contribute

v1.1.0

triggers /paper-contribute · submit this to global · propose to global vault · open PR to global · edit a global note · send to global · contribute to paper global

~/.claude/skills/paper-contribute/SKILL.md
---
name: paper-contribute
version: 1.1.0
description: /paper-contribute, submit this to global, propose to global vault, open PR to global, edit a global note, send to global, contribute to paper global
allowed-tools: Read Glob Grep
---

# paper-contribute

Open, track, and rebase contributions (PRs) against the global vault at {{BASE_URL}}/global.

The global vault is the community knowledge base. It does not accept direct writes — every change is a `proposal` reviewed by an admin. This skill teaches Claude the PR workflow so a user can propose new notes, edit existing global notes, respond to admin feedback, and resolve conflicts — all from chat.

## When to invoke

- `/paper-contribute`
- "submit this to global"
- "propose to global vault"
- "open PR to global"
- "edit a global note"
- "send to global"
- "contribute to paper global"

## Prerequisite

The `paper-global` MCP server (URL contains `/api/mcp/global`) must be bound and exposed in the current session. Detect by matching `mcp__paper-[a-z0-9-]+__propose_global_note`.

If not bound:

> I don't see a `paper-global` MCP server. To contribute to the global vault, run `claude mcp add paper-global {{BASE_URL}}/api/mcp/global` — Claude Code will open the browser for OAuth approval. See {{BASE_URL}}/docs/mcp-setup for the full guide.
>
> (If you only have `paper-personal` bound, you can still open a *new-note* PR without adding a second server — see **Shortcut** below.)

## Shortcut: submit straight from the personal vault

If the note already lives in the user's personal vault and they just want to open a **new-note** PR, the personal server exposes `submit_to_global` — it snapshots the note and opens the global contribution in one call, with no `paper-global` binding required:

```
submit_to_global({
  noteId: <id or path of the personal note>,
  targetPath: "notes/<topic>/<slug>",   // optional; defaults to the note's own path
})
→ { contributionId, url: "/global/contributions/<id>", status: "open" }
```

Output `{{BASE_URL}}` + the returned `url`. Prefer this path for "submit my note X to global" when `paper-global` is NOT bound and the content is an unchanged personal note.

Use the full PR workflow below (which DOES need `paper-global`) when the user wants to: edit an existing global note, list/track/comment on PRs, rebase a conflict, or close a PR — `submit_to_global` only covers the initial new-note submission.

## Sub-commands

### 1. Propose a new note

Triggers: "propose", "submit this", "new global note about …"

Workflow:
1. Pick a title (ask the user if unclear).
2. Build a path slug from the title: `notes/<topic>/<slug>.md` — keep it scoped, not at the root.
3. Resolve wikilinks against the **global** vault first: `mcp__paper-global__search_notes({ query: "X", limit: 5 })`. Same rules as `paper-save` — keep on exact match, annotate `_(new)_` otherwise.
4. Build frontmatter (title, type, tags, source).
5. Show a preview: path + frontmatter + first 200 chars of body.
6. On confirm, call:
   ```
   propose_global_note({
     kind: "new_note",
     title: "…",
     path: "notes/…/slug.md",
     body: "…",
     frontmatter: { … },
     message: "<one-line PR description>"
   })
   ```
7. On success, output `{{BASE_URL}}/global/contributions/<id>` and explain the admin will review.

### 2. Propose an edit to an existing global note

Triggers: "edit global note", "fix the global note on X"

Workflow:
1. Find the target: `search_notes({ query: "X" })` or take the user-supplied path.
2. Read it: `read_note({ idOrPath })` — keep `id` AND `currentRevisionId` (you'll need both).
3. Show the user the current body and ask what to change.
4. Build the edited body. Compare against the source via diff in the chat.
5. On confirm, call:
   ```
   propose_global_note({
     kind: "edit_note",
     targetNoteId: <id from read>,
     baseRevisionId: <currentRevisionId from read>,
     title: <new or unchanged>,
     path: <new or unchanged>,
     body: <new body>,
     frontmatter: <merged frontmatter>,
     message: "<one-line summary of the change>"
   })
   ```

### 3. List my open PRs

Triggers: "my contributions", "my PRs", "what have I submitted"

Call `list_my_contributions({ status: "open" })`. Format as a table:

| PR | Status | Path | Updated |
|---|---|---|---|
| #42 | open | notes/research/mcp.md | 2 days ago |

Suggest follow-ups: "Want to check status on #42?" or "Want to comment on #42?"

### 4. Status check

Triggers: "status of PR #X", "what's happening with #X"

Call `get_contribution({ id: <X> })`. Show:
- Status (open / merged / rejected / conflict)
- Admin comments (most recent first)
- Diff preview if `kind: 'edit_note'`

### 5. Respond to a comment

Triggers: "reply to #X", "respond to the admin on #X"

Call `add_contribution_comment({ id: <X>, body: <user's reply> })`.

### 6. Rebase on conflict

When `get_contribution` returns `status: 'conflict'`, the response includes three-way merge data:
```json
{
  "baseRevisionId": "rev_…",
  "currentRevisionId": "rev_…",
  "currentBody": "…",
  "currentFrontmatter": { … }
}
```

Workflow:
1. Show the user the conflict clearly:
   ```
   This PR is based on revision <baseRevisionId>, but the global note has moved forward to <currentRevisionId>. Below are the three versions.

   YOUR PROPOSED CHANGES:
   <pr.proposedBody>

   CURRENT GLOBAL VAULT VERSION:
   <currentBody>
   ```
2. Help the user write a merged body that includes both their intent and the upstream change.
3. On confirm, call:
   ```
   update_proposal({
     id: <X>,
     rebaseOnto: <currentRevisionId>,
     body: <merged body>,
     frontmatter: <merged frontmatter>
   })
   ```
4. The PR status flips back to `open` for re-review.

### 7. Close own PR

Triggers: "close #X", "withdraw #X"

Call `close_contribution({ id: <X>, reason: <user-supplied or "withdrawn"> })`.

## Error matrix

| Error | Action |
|---|---|
| `401 unauthorized` | Check the `WWW-Authenticate` header. If `resource_metadata=...` is present, Claude Code handles the OAuth refresh — retry the call once. If the retry also 401s, direct the user to `/app/connections` to reconnect. |
| `429 rate_limited` | Wait `Retry-After`, retry once. |
| `contribution_already_open` (single-flight invariant — at most one open PR per (vault, path)) | Call `list_my_contributions` to find the existing PR. Offer to `update_proposal` it instead. |
| `revision_conflict` (PR's `baseRevisionId` is stale) | Trigger the rebase flow above. |
| `email_not_verified` | Tell the user to verify their email at /app/profile. |
| `target_not_found` (on edit_note) | The global note was deleted. Suggest a `new_note` PR instead. |

## Workflow from `paper-save` global-mode redirect

When invoked because `paper-save` was called against the global server:

1. Use the chat content `paper-save` had identified.
2. Apply the same wikilink resolution + duplicate-detection logic.
3. Skip directly to step 1 of this skill — propose a new note.

## Example session

User: "submit my MCP research note to global"

You:
1. Detect: `paper-global` is bound. ✓
2. Find the source note in personal vault: `mcp__paper-personal__read_note({ idOrPath: "notes/research/mcp.md" })`.
3. Resolve wikilinks against global: `mcp__paper-global__search_notes` for each `[[…]]`.
4. Build the proposal. Show preview.
5. User confirms.
6. Call `propose_global_note({ kind: "new_note", title: "Model Context Protocol overview", path: "notes/protocols/mcp.md", body, frontmatter, message: "Initial MCP overview note" })`.
7. Output: "PR #57 opened at {{BASE_URL}}/global/contributions/57. The admin will review."

หรือดึงผ่าน curl -s /docs/skills/paper-contribute.md (ดูที่ /docs/skills/paper-contribute.md)

§

paper-help

v1.1.0

triggers /paper-help · /paper-update · /paper-sync · what paper skills can I use · list paper skills · help with paper · paper skill catalog · update paper skills · sync paper skills · update all paper skills · อัพเดต paper skills · อัพเดทสกิล paper

~/.claude/skills/paper-help/SKILL.md
---
name: paper-help
version: 1.1.0
description: /paper-help, /paper-update, /paper-sync, what paper skills can I use, list paper skills, help with paper, paper skill catalog, update paper skills, sync paper skills, update all paper skills, อัพเดต paper skills, อัพเดทสกิล paper
allowed-tools: Read Glob Grep
---

# paper-help

Meta-skill that lists every `paper-*` skill the user has installed, plus everything published on paper.farzai.com, and helps install or update them.

This skill is the entry point for someone who has just added a `paper-*` MCP server and wants to discover what they can now do.

## When to invoke

**Catalog / discovery**
- `/paper-help`
- "what paper skills can I use"
- "list paper skills"
- "help with paper" (only when the user has not specified a more targeted skill)

**Update / sync** (jumps straight to Workflow step 4 — installs every skill via `get_skill_versions`)
- `/paper-update`
- `/paper-sync`
- "update paper skills"
- "sync paper skills"
- "update all paper skills"
- "อัพเดต paper skills" / "อัพเดทสกิล paper"

When invoked via an update trigger, skip step 2 (catalog rendering) — go to step 4 with `install all` semantics and report which skills were refreshed at which version.

## Workflow

### 1. List server-side canonical versions

Call `get_skill_versions` (available on both `mcp__paper-personal__*` and `mcp__paper-global__*` endpoints). It returns:

```json
{
  "skills": [
    { "name": "paper-save", "version": "1.0.0", "description": "...", "installUrl": "/docs/skills/paper-save.md", "references": [] },
    { "name": "paper-ask", "version": "1.0.0", "description": "...", "installUrl": "/docs/skills/paper-ask.md", "references": [] },
    { "name": "paper-help", "version": "1.0.0", "description": "...", "installUrl": "/docs/skills/paper-help.md", "references": [] },
    { "name": "paper-contribute", "version": "1.0.0", "description": "...", "installUrl": "/docs/skills/paper-contribute.md", "references": [] }
  ]
}
```

### 2. Show the catalog

For each skill, show:
- Name + version
- One-line description (the trigger phrases — they explain the skill better than any prose)
- Which servers it works against: personal, global, or both

| Skill | Personal | Global | Notes |
|---|---|---|---|
| `paper-save` | yes | redirects → `paper-contribute` | direct-write for personal; offers PR redirect for global |
| `paper-ask` | yes | yes | read-only search + synthesis |
| `paper-help` | yes | yes | this skill |
| `paper-contribute` | n/a | yes | PR workflow against the global vault |

### 3. Detect stale local copies

For each skill the user has installed locally (under `~/.claude/skills/<name>/SKILL.md`):
- Compare `frontmatter.version` in the local file against the server-canonical version from `get_skill_versions`.
- Flag any that are behind:

> ⚠ paper-save local version is 0.9.1 but server is 1.0.0. Run `/paper-help install paper-save` to update.

### 4. Install on request

When the user says "install paper-X" or "update paper-X":
1. Fetch `https://paper.farzai.com/docs/skills/<name>.md` (or the appropriate base URL from `installUrl`).
2. Write to `~/.claude/skills/<name>/SKILL.md`. Create the folder if missing.
3. For each reference file listed under `references[]`, fetch `https://paper.farzai.com/docs/skills/<name>/references/<file>.md` and write to `~/.claude/skills/<name>/references/<file>.md`.
4. Tell the user to restart Claude Code so it discovers the change.

If the user says "install all" or "/paper-help install":
- Loop through every skill returned by `get_skill_versions` and install each.

### 5. Server discovery

If no `mcp__paper-*__get_skill_versions` is available (user has no `paper-*` server bound yet), point them to https://paper.farzai.com/docs/mcp-setup.

## Self-documenting contract

This skill teaches Claude how the registry works:
- The `version:` frontmatter in `src/lib/skills/templates/<name>/SKILL.md` on the server is canonical.
- The user-installed `~/.claude/skills/<name>/SKILL.md` is a cached copy.
- `get_skill_versions` is the truth source for the latest version.
- Drift = user's local copy is older than the server's. The fix is to re-install via this skill.

## Error matrix

| Error | Action |
|---|---|
| `401 unauthorized` from any paper-* tool | Check the `WWW-Authenticate` header. If `resource_metadata=...` is present, Claude Code handles the OAuth refresh — retry once. If the retry also 401s, direct the user to `/app/connections` to reconnect. |
| `get_skill_versions` not available | User has no `paper-*` server bound. Point to https://paper.farzai.com/docs/mcp-setup. |
| Network fetch fails when installing | Tell the user the URL and ask them to paste the contents manually. |
| File-write permission denied | Tell the user the target path and ask them to create it manually. |

## Example session

User: "/paper-help"

You:
1. Call `mcp__paper-personal__get_skill_versions` (or `mcp__paper-global__get_skill_versions`, whichever is bound).
2. Read `~/.claude/skills/paper-save/SKILL.md` → frontmatter version is `0.9.1`. Server says `1.0.0`. Flag stale.
3. Output catalog table. Note stale skill. Ask whether to update.

User: "update paper-save"

You:
1. Fetch `https://paper.farzai.com/docs/skills/paper-save.md`.
2. Write to `~/.claude/skills/paper-save/SKILL.md` (overwriting).
3. "Done — restart Claude Code to pick up the new version."

หรือดึงผ่าน curl -s /docs/skills/paper-help.md (ดูที่ /docs/skills/paper-help.md)

§

paper-research

v0.2.0

triggers Autonomous research loop that files results into a paper.farzai.com vault via the bound MCP server. Takes a topic · runs iterative WebSearch + WebFetch queries · extracts claims and entities · then files structured source notes · topic pages for recurring concepts · and a synthesis note using create_collection / create_note / add_to_collection. Searches the vault for prior knowledge before the web. When invoked without a topic · calls suggest_research_topics to surface the vault's frontier. Output goes into the vault · not the chat. Triggers on "/paper-research" · "paper-research" · "research [topic] into paper" · "deep dive [topic]" · "investigate [topic] and file" · "research and file to paper" · "go research [topic]" · "build a research session on [topic]".

~/.claude/skills/paper-research/SKILL.md
---
name: paper-research
version: 0.2.0
description: Autonomous research loop that files results into a paper.farzai.com vault via the bound MCP server. Takes a topic, runs iterative WebSearch + WebFetch queries, extracts claims and entities, then files structured source notes, topic pages for recurring concepts, and a synthesis note using create_collection / create_note / add_to_collection. Searches the vault for prior knowledge before the web. When invoked without a topic, calls suggest_research_topics to surface the vault's frontier. Output goes into the vault, not the chat. Triggers on "/paper-research", "paper-research", "research [topic] into paper", "deep dive [topic]", "investigate [topic] and file", "research and file to paper", "go research [topic]", "build a research session on [topic]".
allowed-tools: Read Write Glob Grep WebFetch WebSearch
---

# paper-research: Autonomous Research Loop → paper.farzai.com

You are a research agent. You take a topic, run iterative web searches, extract structured findings, and file everything into a paper.farzai.com vault via the bound MCP server. The user gets vault pages and a `/app/vaults/<vaultId>/research/<topic-slug>` URL, not a chat answer.

This skill is a port of the claude-obsidian `autoresearch` pattern to the paper.farzai.com MCP surface. The configurable program in `references/program.md` controls max rounds, max pages, confidence rules, and source-preference rules.

---

## Before You Start

1. Read `references/program.md` — it sets the loop constraints (max rounds, max pages per session, confidence scoring rules, source-preference rules). Treat any value in that file as authoritative; the defaults below are last-resort fallbacks.

2. Detect which MCP server you are bound to. paper.farzai.com exposes two:
   - **personal vault** server (URL contains `/api/mcp/v/<vaultId>`) — has `create_note`, `create_collection`, `add_to_collection`. This is the target for research sessions.
   - **global vault** server (URL contains `/api/mcp/global`) — read-only writes (PR-only). Research sessions do NOT file directly to global; if only `paper-global` is bound, ask the user to also bind a personal server (`paper-personal`) and stop.

3. Probe `get_vault` on the personal server. On 401, tell the user to reconnect at `/app/connections` (Claude Code's OAuth flow will issue a new access token) and stop.

4. Confirm the topic with the user before kicking off the loop. The topic becomes the **topic-slug** — a lowercase kebab-case identifier no longer than 60 characters, derived from the topic phrase. Examples:
   - "Model Context Protocol" → `model-context-protocol`
   - "How LLMs handle long contexts" → `long-context-handling`

   **4a — No topic given.** If the user invoked `/paper-research` with no topic, ask the server which notes are at the frontier of the vault:

   ```
   suggest_research_topics({ limit: 5 })
   → { suggestions: [{ path, title, out, in, score }, …] }
   ```

   Present the candidate list as a numbered prompt:

   > Frontier notes in your vault. Which one should I research?
   > 1. <Title> — /<path>
   > 2. ...
   > Type a number 1-5, type a free-text topic to override, or "cancel".

   - Pick 1-5 → use the selected note's title as the topic.
   - Free text → use that.
   - "cancel" → ask: "What topic should I research?"

   **4b — Explicit topic given.** Use it verbatim; skip 4a.

---

## Path Conventions (load-bearing)

paper.farzai.com enforces a strict path schema on notes (lowercased, `[a-z0-9-_.]` per segment, max depth 8). The skill MUST file research notes into the following paths:

| Kind | Path |
|---|---|
| Source note | `research/<topic-slug>/sources/<source-slug>` |
| Synthesis note | `research/<topic-slug>/synthesis` |
| Topic page | `topics/<topic-slug>` (vault-root, cap-independent) |
| Research collection | slug `research-<topic-slug>` (NO slashes — collection slugs are flat) |

The `/app/vaults/<vaultId>/research` view filters collections by the `research-` slug prefix. The detail page at `/app/vaults/<vaultId>/research/<topic-slug>` looks up the collection by slug `research-<topic-slug>` and lists every note under `research/<topic-slug>/sources/`.

**Soft cap**: the server rejects new source notes with error code `research_session_full` once a topic has 25 active source notes. Plan ahead — pick the 5-15 best sources per session, not 25+.

---

## Frontmatter Contract

paper.farzai.com validates frontmatter via a known-keys allowlist (`type`, `tags`, `status`, `aliases`, `description`, `canonical`, `created`, `updated`, `published`, `publishedAt`, `extra`). Unknown keys are NOT rejected — they are bucketed into `extra` automatically. Use this to attach research metadata.

### Source note frontmatter

```yaml
type: source
tags: [research, <topic-tag>]
description: <one-line description>
canonical: <original URL>
created: <ISO-8601 UTC>
# Unknown keys land in `extra`:
topic: <topic-slug>
url: <original URL>
retrieved_at: <ISO-8601 UTC>
confidence: high | medium | low
source_type: paper | docs | post | spec | repo | thread | other
```

`canonical: <url>` makes the source's origin visible in the editor metadata panel; `url:` (in extra) lets downstream queries find it.

### Synthesis note frontmatter

```yaml
type: note
tags: [research, synthesis, <topic-tag>]
description: <one-line summary of the synthesis>
created: <ISO-8601 UTC>
# In extra:
topic: <topic-slug>
sources: <count>
rounds: <count>
status: developing | complete
```

### Topic page frontmatter

```yaml
type: concept   # or 'entity' — both valid FrontmatterSchema values
tags: [topic, research, <topic-tag>]
description: <one-line definition>
created: <ISO-8601 UTC>
# In extra:
first_seen_in: <session-topic-slug>
mentioned_in: [<source-slug-1>, <source-slug-2>, ...]
mention_count: <number>
```

---

## Workflow

### Step 1 — Create the collection

```
create_collection({
  slug: "research-<topic-slug>",
  name: "Research: <Topic Display Name>",
  description: "Autoresearch session on <topic>",
})
```

Error handling: if `slug_taken` comes back, the session already exists. Ask the user whether to (a) resume by adding more sources to the existing topic, or (b) pick a new topic-slug.

### Step 2 — Search vault for prior knowledge

Before WebSearch, ask the server what the vault already knows. This gives Round 1 a context primer + lets the synthesis link back to existing notes.

```
search_notes({ query: "<topic display name>", tags: ["research"], limit: 20 })
search_notes({ query: "<topic display name>", limit: 20 })   # broad fallback
```

Filter hits the resolver considers relevant. Store them as `priorHits` for Step 5. If `search_unavailable: true` (Meili down), proceed with web. If 0 hits across both queries, log "no prior knowledge found, proceeding with web search" and move on — this is not an error.

### Step 3 — Decompose the topic

Break the topic into **3-5 distinct search angles**. Each angle is one facet — e.g., for "Model Context Protocol" you might pick:
1. Official spec + primitives
2. Reference implementations (SDKs, transports)
3. Use cases + ecosystem (which agents use it, why)
4. Comparisons to similar protocols (LSP, OpenAI tool-use, etc.)
5. Critiques + open issues

Skip angles that overlap; aim for breadth.

### Step 4 — Search & fetch (rounds)

For each round (max 3, per `program.md`):

```
For each angle:
  - WebSearch 2-3 queries → pick top 2-3 results
  - WebFetch each top result
  - Extract from each fetch: key claims, entities, concepts, open questions
```

Round 1 is broad. Round 2 fills gaps + contradictions identified in Round 1. Round 3 (optional) is a final pass for remaining major gaps.

Stop when the **per-program max page count** is hit OR every angle has at least one usable source OR three rounds have run.

### Step 5 — File source notes + track mentions

While extracting sources, keep an in-memory tally of every `[[X]]` mention across source bodies. Step 6 uses this to decide which wikilinks deserve a standalone topic page:

```
mentionCounts = new Map()   // X → { count, sources: [source-slug, ...] }
for each source filed:
  for each [[X]] in source body:
    mentionCounts[X] ??= { count: 0, sources: [] }
    mentionCounts[X].count++
    mentionCounts[X].sources.push(<source-slug>)
```

For every distinct source kept after deduplication:

```
create_note({
  path: "research/<topic-slug>/sources/<source-slug>",
  title: "<Source Title>",
  body: <body — see below>,
  frontmatter: <source-frontmatter — see above>,
})
```

The **source-slug** is a stable kebab-case derivative of the source title, ≤40 chars. Example: "Anthropic Introducing MCP" → `anthropic-introducing-mcp`.

**Source note body** (markdown):

```markdown
# <Source Title>

> [!source] <url>

## Summary
<2-4 sentence summary in declarative present tense>

## Key Claims
- <claim 1>
- <claim 2>
- ...

## Entities Mentioned
- [[<Entity 1>]]
- ...

## Concepts
- [[<Concept 1>]]
- ...

## Notes
<your extracted notes — verbatim quotes are fine; mark with > blockquote>
```

Wikilinks (`[[X]]`) inside source notes are intentionally "speculative" — they may not resolve yet. The synthesis step will create or link the canonical pages.

If `create_note` returns `research_session_full`, stop adding new sources. The remaining sources go into the synthesis's "Sources not filed" section.

If `create_note` returns `path_conflict`, the source already exists — read it with `read_note` and skip (idempotency: re-running the skill on the same topic should not duplicate).

### Step 6 — File substantive topic pages

For every `[[X]]` that crossed the threshold (per `references/program.md`, default **≥ 3 mentions across sources**):

```
threshold = (program.md value, default 3)
for X, info in mentionCounts:
  if info.count < threshold: skip
  slug = slugify(X, max=40)
  existing = read_note({ path: `topics/${slug}` })    # may 404
  if existing: skip — link will still resolve via Phase 1 backfill
  else:
    create_note({
      path: `topics/${slug}`,
      title: X,
      body: <stub body — see below>,
      frontmatter: { type: 'concept', tags: ['topic', 'research', topic-slug],
                     description: <one-line definition>, ... },
    })
  add_to_collection({ collectionId, noteId: created.id })
```

**Topic stub body**:

```markdown
# <X>

> [!stub] Drafted by paper-research session on [[research/<topic-slug>/synthesis|<Topic Display Name>]]

Mentioned in: [[<source-1>]], [[<source-2>]], [[<source-3>]]

<one-paragraph definition synthesized from sources>
```

Idempotency: re-running the skill on the same topic should not duplicate topic pages — `read_note` checks first, returning the existing note when present. Phase 1's wikilink backfill means source notes' `[[X]]` already resolve to the topic page once it's filed.

`topics/<slug>` lives at vault root → does NOT count toward `research_session_full` cap (which is scoped to `research/<slug>/sources/*`).

### Step 7 — File the synthesis

After all sources are filed (or capped):

```
create_note({
  path: "research/<topic-slug>/synthesis",
  title: "Synthesis: <Topic Display Name>",
  body: <synthesis-body — see below>,
  frontmatter: <synthesis-frontmatter — see above>,
})
```

**Synthesis body** (markdown):

```markdown
# Synthesis: <Topic Display Name>

## Overview
<2-3 sentence high-level summary>

## Key Findings
- <finding 1> — see [[<source-title-1>]]
- <finding 2> — see [[<source-title-2>]]
- ...

## Key Entities
- [[<Entity>]]: <role / significance>

## Key Concepts
- [[<Concept>]]: <one-line definition>

## Contradictions
- [[<Source A>]] says X. [[<Source B>]] says Y. <which is more credible + why>

## Open Questions
- <question 1>
- <question 2>

## Related Notes in Vault
<emit this section only if priorHits from Step 2 has entries>
- [[<prior-note-title>]] · <one-line context on why this is related>
- ...

## Sources
- [[<source-title-1>]] · <author/org>, <date>
- [[<source-title-2>]] · <author/org>, <date>
```

Every claim cites the source via `[[<source-title>]]`. This is how the synthesis ties together — the wikilinks resolve to the source notes you filed in Step 5, and paper.farzai.com's backlinks panel will surface the synthesis on every source note. Topic-page wikilinks (`[[Concept]]`) filed in Step 6 also resolve automatically thanks to Phase 1 backfill.

If `path_conflict` comes back on the synthesis, the synthesis already exists. Use `update_note` (must supply `expectedRevisionId` from `read_note`) to refresh it.

### Step 8 — Add everything to the collection

For every filed note (sources + synthesis):

```
add_to_collection({ collectionId: <id from Step 1>, noteId: <id> })
```

`add_to_collection` is idempotent — safe to re-run without checking.

### Step 9 — Report back

Output to the user:

```
Research complete: <Topic>

  → <baseUrl>/app/vaults/<vaultId>/research/<topic-slug>

Rounds: <n>     Sources filed: <n>     Topic pages filed: <n>     Synthesis: <yes/no>

Key findings:
- <finding 1>
- <finding 2>
- <finding 3>

Related notes in vault: <n>
Open questions filed: <n>
```

Substitute `<baseUrl>` with `{{BASE_URL}}` — the setup page rewrites this placeholder at install time.

---

## Error Matrix

| Error code | Cause | Fix |
|---|---|---|
| `401` | OAuth grant expired/revoked | Reconnect at `/app/connections` |
| `429` (rate_limited) | Too many calls — `create_note` is capped at 60/min | Sleep 5s, retry once |
| `slug_taken` | Collection already exists | Resume or pick new topic-slug |
| `path_conflict` | Note already exists at that path | `read_note` then either skip (idempotent) or `update_note` |
| `path_invalid` | Path violates `[a-z0-9-_.]/...` schema | Re-slugify the source title |
| `research_session_full` | ≥ 25 active source notes for this topic | Stop adding sources; mention skipped sources in synthesis Open Questions |
| `revision_conflict` (update_note) | Stale `expectedRevisionId` | Re-read the note, retry once |
| `note_not_found` | Note id wrong (or deleted) | Re-resolve via `read_note({ idOrPath })` |
| `collection_not_found` | Collection id stale | Re-resolve via `list_collections` |

---

## Idempotency

Re-running `/paper-research <same-topic>` should be safe:

1. Step 1 returns `slug_taken` → ask user before continuing.
2. Step 5 returns `path_conflict` on already-filed sources → `read_note` then skip; do not duplicate.
3. Step 6 returns `path_conflict` on already-filed topic pages → `read_note` then skip (resolution will pick them up via Phase 1 backfill).
4. Step 7 returns `path_conflict` on the synthesis → use `update_note` to refresh (read first to get `expectedRevisionId`).
5. Step 8 is idempotent server-side (no error on duplicate).

The user can re-run mid-session if WebFetch errors out — only new sources will be filed.

---

## When Global Submission Is Available

If BOTH `paper-personal` AND `paper-global` MCP servers are bound, after Step 7 ask:

> "Want to submit the synthesis to the global vault as a PR? (y/n)"

On `y`, invoke `/paper-contribute` with the synthesis content. Source notes stay personal (raw evidence is not for the commons); only the curated synthesis is a candidate global contribution.

If only `paper-personal` is bound, skip this prompt silently.

---

## Constraints

Always respect `references/program.md`:
- Max rounds per topic (default: 3)
- Max pages per session (default: 15)
- Source-preference rules
- Confidence-scoring rules

If a program constraint conflicts with completeness, respect the constraint and note what was left out in the synthesis's "Open Questions" section.

If the program file is missing or malformed, fall back to the defaults above and warn the user once at the start of the session.

หรือดึงผ่าน curl -s /docs/skills/paper-research.md (ดูที่ /docs/skills/paper-research.md)

§

paper-save

v1.1.0

triggers /paper-save · save this to paper · save this conversation · save to my vault · file this in paper · file to vault · log decision · save meeting notes

~/.claude/skills/paper-save/SKILL.md
---
name: paper-save
version: 1.1.0
description: /paper-save, save this to paper, save this conversation, save to my vault, file this in paper, file to vault, log decision, save meeting notes
allowed-tools: Read Glob Grep
---

# paper-save

Save the current chat — an insight, decision, synthesis, or research summary — into a `paper-*` MCP vault on paper.farzai.com.

This skill is the bridge between an ad-hoc Claude Code conversation and a durable note in the user's second brain. It detects which `paper-*` MCP server is bound, asks once when ambiguous, and resolves wikilinks before writing so the user does not end up with orphan references.

## When to invoke

Any phrase containing one of:
- `/paper-save`
- "save this to paper" / "save to my vault" / "save this conversation"
- "file this in paper" / "file to vault"
- "log decision" / "save meeting notes" (route to the matching note type in step 3 — `decision`, or `reference` for meeting notes)

If the user says only "save this" without context, ask one clarifying question — vault name or topic — before proceeding.

## Server selection

Each `paper-*` MCP server represents one vault. The binding name itself (`mcp__paper-personal__*`, `mcp__paper-work__*`, etc.) is the only identifier you need.

Pattern-match exposed tools against `mcp__paper-[a-z0-9-]+__`. If more than one matches, ask:

> Which vault should I save to? I see: `paper-personal`, `paper-work`. (Reply with the name.)

Remember the answer for the rest of the session. Do not write a config file.

To distinguish personal vs global servers:
- Personal vault URLs contain `/api/mcp/v/<uuid>` and expose `create_note` / `update_note`.
- Global vault URLs contain `/api/mcp/global` and expose `propose_global_note` instead. `create_note` is NOT available there.

If the bound server is the global vault, see the **Global vault redirect** section below.

## Workflow

### 1. Health check

Call `get_vault` (the cheapest tool). Use it to confirm the binding works, and keep its `id` field — that is the **vaultId** you need to build the note URL in step 8.

- On `401 unauthorized`: check the `WWW-Authenticate` response header. If it carries `resource_metadata=...`, Claude Code will handle the OAuth refresh automatically — retry the call once. If the retry also 401s, stop and direct the user to `{{BASE_URL}}/app/connections` to reconnect.
- On `429 rate_limited`: wait the `Retry-After` seconds, retry once. If it fails again, stop and report.

### 2. Scan the chat for save-worthy content

Skim the recent conversation. Identify the substantive content:

- Insights you arrived at (not the mechanical Q&A around them)
- Decisions (with rationale)
- Syntheses (multi-source distillations)
- Research summaries (with sources cited)
- Code patterns or architecture notes

Skip:
- Pure tool-call echoes
- Boilerplate Claude scaffolding
- Conversation niceties

### 3. Determine note type and folder

| Type | Folder | Frontmatter additions |
|---|---|---|
| research | `notes/research/` | `confidence: low|medium|high` |
| how-to | `notes/how-to/` | — |
| decision | `notes/decisions/` | `status: proposed|accepted|rejected`, `decision_date: YYYY-MM-DD` |
| idea | `notes/ideas/` | — |
| reference | `notes/reference/` | — |

If unclear, ask the user once.

### 4. Resolve intended wikilinks

For every `[[Foo]]` you plan to write into the body, call `search_notes({ query: "Foo", limit: 5 })` first.

- Exact title match: keep `[[Foo]]` as-is. It will resolve.
- Fuzzy match: use the exact resolved title. `[[Foo Bar Baz]]` not `[[Foo]]`.
- No match: write `[[Foo]] _(new)_` so the user knows it's an unresolved link. Do NOT silently drop it — the absence of links is worse than orphans for a knowledge graph.

### 5. Build the frontmatter

The server validates frontmatter against a known-keys allowlist (`type`, `tags`, `status`, `aliases`, `description`, `canonical`, `created`, `updated`, `published`, `publishedAt`, `extra`). Unknown keys are NOT rejected — they are auto-bucketed into `extra`. The note **title is a top-level `create_note` argument, not a frontmatter key** — do not duplicate it in frontmatter.

Allowlist fields:
```yaml
type: research|how-to|decision|idea|reference
tags: [array, of, kebab-case, tags]
description: "<one-line description>"
created: <ISO 8601 of "now">
updated: <same as created>
```

Metadata that lands in `extra` (no special syntax needed — just add the keys):
```yaml
source: claude-code
related: [related-note-slug-1, related-note-slug-2]   # plain strings; put the actual [[wikilinks]] in the body
```

Type-specific fields per the table above (`confidence` for research, `status` + `decision_date` for decision) also bucket into `extra`, except `status`, which is an allowlist key.

### 6. Preview before writing

Show the user:
- Path (e.g. `notes/research/cache-invalidation` — no `.md` extension; the server strips a trailing `.md` if you add one)
- Frontmatter block
- First 200 characters of the body
- Resolved wikilinks count

Ask for confirmation. Do not silently write.

### 7. Duplicate detection

Before `create_note`, call `list_notes({ folder })` and check whether a note with the same title already exists.

If yes, offer two options:
- **Append**: read the existing note via `read_note`, append the new content under a `## <date>` heading, call `update_note` with the `expectedRevisionId` from the read.
  - On `revision_conflict`: re-read, re-merge, retry once. If still conflicting, ask the user to resolve manually.
- **Create with suffix**: `<title> (continued)` or similar.

### 8. Write the note

Call `create_note` (or `update_note` for append).

On success:
- Output the note URL: `{{BASE_URL}}/app/vaults/<vaultId>/notes/<id>` — use the `vaultId` you kept from `get_vault` in step 1 and the `id` returned by `create_note`. (There is no `/app/notes/<id>` route; the vault segment is required.)
- Suggest 1-2 follow-ups: "Want me to publish this?", "Should I link it from `[[Related]]`?"

### 9. Optional: add to a collection

If the user mentioned project context (e.g. "save this from my MCP investigation"), call `list_collections` and offer `add_to_collection`. Skip if no obvious match.

## Global vault redirect

When the bound server is `paper-global` (URL contains `/api/mcp/global`):

> The MCP server you're connected to (`paper-global`) accepts contributions, not direct writes.
> Did you mean to (a) save this to your personal vault instead, or (b) propose this as a PR to the global vault?
> If (a), connect to `paper-personal` and re-run. If (b), I'll invoke /paper-contribute now.

Wait for explicit confirmation. Do not silently redirect.

## Error matrix

| Error | Action |
|---|---|
| `401 unauthorized` | Check the `WWW-Authenticate` header. If `resource_metadata=...` is present, Claude Code handles the OAuth refresh — retry once. If the retry also 401s, direct the user to `/app/connections` to reconnect. |
| `429 rate_limited` | Wait `Retry-After`, retry once. If still 429, stop. |
| `revision_conflict` | Re-read the target note, re-merge, retry once. If still conflicting, stop and ask the user. |
| `note_not_found` (on update) | Fall back to `create_note`. |
| `path_conflict` | Append `-2`, `-3`, … to the slug until unique. |
| Missing MCP tools (no `mcp__paper-*` server bound) | Point to {{BASE_URL}}/docs/mcp-setup. |

## Examples

User: "save this analysis of cache invalidation strategies"

You:
1. Probe `get_vault` → ok.
2. Scan chat — the substantive content is the four-strategy comparison.
3. Folder: `notes/research/`. Type: `research`. Confidence: `medium` (analysis based on 3 sources).
4. Resolve `[[Cache stampede]]` via `search_notes` → not found → write `[[Cache stampede]] _(new)_`.
5. Build frontmatter (no `title` key — it's a `create_note` arg), show preview.
6. User confirms → `create_note({ path: "notes/research/cache-invalidation", title: "Cache invalidation strategies", body, frontmatter })`.
7. Output `{{BASE_URL}}/app/vaults/<vaultId>/notes/<id>` + offer to publish.

หรือดึงผ่าน curl -s /docs/skills/paper-save.md (ดูที่ /docs/skills/paper-save.md)

§ ทำอะไรต่อ

ขั้นถัดไป

  • ·เผยแพร่โน้ต — บอก Claude ว่า "เผยแพร่ [ชื่อโน้ต]" Claude จะเรียก publish_note ให้ — ได้ URL สาธารณะกลับมา
  • ·ติดตั้ง skill — เพิ่ม skill ทั้ง 5 ตัว (/paper-ask, /paper-contribute, /paper-help, /paper-research, /paper-save) ลงใน ~/.claude/skills/ เพื่อใช้ shortcut ที่ทำให้ Claude เขียน/ค้น/PR/วิจัย/สรุปการประชุม/ทำ ADR เข้า vault ได้ทันที (ดู § Skills)
  • ·ส่งเข้า global vault — "ส่ง [ชื่อโน้ต] เข้า global vault" จะเปิด PR ให้ admin review
  • ·จัดการ Connections — ดูแอป Claude ที่เชื่อมอยู่ และ revoke เมื่อต้องการที่ /app/connections

§ แก้ปัญหา

Troubleshooting

§401 unauthorized

Claude Desktop / Code จะรีเฟรช OAuth access token ให้อัตโนมัติ — ถ้ายัง 401 ซ้ำให้เปิดหน้า /app/connections แล้ว reconnect ใหม่ (grant อาจถูก revoke หรือ idle เกิน 30 วัน — กรณีนี้รัน claude mcp add ซ้ำเพื่อขอ approve ใหม่)

§429 rate_limited

ค่าเริ่มต้น 60 req/min ต่อ token — รอประมาณ 1 นาทีแล้วลองใหม่. ดูค่า cap ต่อ tool และตัว cheap-read cluster (300/min) ที่ docs mcp-tools.md § Rate Limits

§Token idle expiry

OAuth grant ที่ไม่ถูกใช้นาน 30 วัน จะถูก revoke อัตโนมัติ — รัน claude mcp add ซ้ำเพื่อ approve connection ใหม่

§Skill ไม่ทำงานหลังติดตั้ง

Claude Code โหลด skill ตอนเริ่มต้น — ปิดและเปิดใหม่หลัง curl เสร็จ ถ้ายังไม่เจอ ตรวจว่าไฟล์อยู่ที่ ~/.claude/skills/<name>/SKILL.md (มี SKILL.md เป็นชื่อไฟล์ ไม่ใช่ <name>.md)