{
  "name": "ai-knowledge-lab",
  "protocol": "discover-then-execute",
  "skill": "/SKILL.md",
  "registry": "/mcp/tools.json",
  "updated": "2026-09-21T06:40:05Z",
  "meta": [
    "lab_discover",
    "lab_execute"
  ],
  "tools": [
    {
      "name": "lab_discover",
      "kind": "meta",
      "description": "List available Lab capabilities. Call before lab_execute. Catalog grows without changing SKILL.md.",
      "invoke": {
        "method": "GET",
        "path": "/api/discover.json"
      }
    },
    {
      "name": "lab_execute",
      "kind": "meta",
      "description": "Execute a discovered tool by name. Resolve schema from /mcp/tools.json, then hit static.path / path_template / action.",
      "invoke": {
        "method": "RESOLVE",
        "registry": "/mcp/tools.json",
        "note": "Substitute path_template args; GET for read tools; follow action.url / mailto for engage tools."
      }
    },
    {
      "name": "lab_hello",
      "kind": "engage",
      "description": "Announce that an agent connected. One anonymous GET that increments a public counter shown on the home page, so the lab can tell whether agents actually use this protocol. No identity, no payload, no lab-side logging. Call once per session. Read the count without incrementing at https://abacus.jasoncameron.dev/get/chengguruchun-lab/agent-connect.",
      "invoke": {
        "method": "GET",
        "url": "https://abacus.jasoncameron.dev/hit/chengguruchun-lab/agent-connect"
      }
    },
    {
      "name": "lab_list_entries",
      "kind": "read",
      "description": "List knowledge entries from catalog: Articles, Diverse Lab, Times (Hot Words), etc. Filter with type. Projects live under /api/projects.json (+ projects-research / projects-personal); Videos under /api/videos.json (page-only, not in catalog).",
      "invoke": {
        "method": "GET",
        "path": "/api/catalog.json",
        "alt_by_type": "/api/{type-slug}.json"
      }
    },
    {
      "name": "lab_list_articles",
      "kind": "read",
      "description": "List articles only (shorthand for catalog filter Articles).",
      "invoke": {
        "method": "GET",
        "path": "/api/articles.json"
      }
    },
    {
      "name": "lab_list_projects_research",
      "kind": "read",
      "description": "List curated research GitHub repos (static snapshot for agents).",
      "invoke": {
        "method": "GET",
        "path": "/api/projects-research.json"
      }
    },
    {
      "name": "lab_list_projects_personal",
      "kind": "read",
      "description": "List top personal non-fork GitHub repos excluding *.github.io (static snapshot).",
      "invoke": {
        "method": "GET",
        "path": "/api/projects-personal.json"
      }
    },
    {
      "name": "lab_list_projects",
      "kind": "read",
      "description": "Projects overview (research + personal sections).",
      "invoke": {
        "method": "GET",
        "path": "/api/projects.json"
      }
    },
    {
      "name": "lab_get_entry",
      "kind": "read",
      "description": "Fetch one entry's canonical Markdown. Prefer the catalog item's `content` or `markdown` field (always a working path). The path_template `/content/{collection}/{id}.md` only works when id equals the file stem. Times ids look like `times-2026-09-w3` while files are `/content/times/2026-09-w3.md` — use catalog `markdown` or `slug` (e.g. slug=2026-09-w3 → /content/times/{slug}.md).",
      "invoke": {
        "method": "GET",
        "path_template": "/content/{collection}/{id}.md",
        "collections": {
          "Articles": "articles",
          "Diverse Lab": "diverse",
          "Times": "times"
        },
        "resolve_collection_via": "/api/catalog.json",
        "prefer_catalog_fields": [
          "content",
          "markdown"
        ],
        "slug_field": "slug",
        "notes": "Times: id times-YYYY-MM-wN ↔ file /content/times/YYYY-MM-wN.md via markdown or slug."
      }
    },
    {
      "name": "lab_search_entries",
      "kind": "read",
      "description": "Client-filter catalog by keyword against title, excerpt, tags.",
      "invoke": {
        "method": "GET",
        "path": "/api/catalog.json",
        "client_filter": true
      }
    },
    {
      "name": "lab_list_tags",
      "kind": "read",
      "description": "List tags and entry ids under each tag.",
      "invoke": {
        "method": "GET",
        "path": "/api/tags.json"
      }
    },
    {
      "name": "lab_get_about",
      "kind": "read",
      "description": "Owner stance and The Road Here (bio route). HTML page; no private contact beyond published footer.",
      "invoke": {
        "method": "GET",
        "path": "/about/"
      }
    },
    {
      "name": "lab_follow_github",
      "kind": "engage",
      "description": "Follow / star the owner's GitHub (chengguruchun). Opens the profile for the agent or human to complete follow.",
      "invoke": {
        "method": "ACTION",
        "action": "open",
        "url": "https://github.com/chengguruchun",
        "note": "Prefer Follow on the profile; site repo: https://github.com/chengguruchun/chengguruchun.github.io"
      }
    },
    {
      "name": "lab_subscribe_email",
      "kind": "engage",
      "description": "Subscribe to article updates by email. Opens a mailto draft to the owner with a Subscribe subject.",
      "invoke": {
        "method": "ACTION",
        "action": "mailto",
        "to": "chengguruchun@163.com",
        "subject_template": "Subscribe · AI Knowledge Lab",
        "body_template": "Please add me to article updates.\\n\\nName: {from_name}\\nNote: {note}",
        "note": "P0: mailto handshake. Future: dedicated subscribe endpoint without changing SKILL.md."
      }
    },
    {
      "name": "lab_submit_issue",
      "kind": "contribute",
      "description": "Propose an idea / fix / knowledge patch like a GitHub issue. Human reviews; accepted items may merge into the Lab.",
      "invoke": {
        "method": "ACTION",
        "action": "open",
        "url_template": "https://github.com/chengguruchun/chengguruchun.github.io/issues/new?title={title}&body={body}",
        "note": "P0: GitHub Issues as intake. Review → merge into content/tools. Future: /api/issues without SKILL edits."
      }
    },
    {
      "name": "lab_get_llms_txt",
      "kind": "read",
      "description": "Machine-oriented site map (llms.txt).",
      "invoke": {
        "method": "GET",
        "path": "/llms.txt"
      }
    },
    {
      "name": "lab_subscribe_rss",
      "kind": "engage",
      "description": "Subscribe to Lab content via real RSS at /feed.xml (auto-rebuilt by scripts/build_feeds.py + GitHub Action on content push).",
      "invoke": {
        "method": "GET",
        "path": "/feed.xml"
      }
    },
    {
      "name": "lab_watch_repo",
      "kind": "engage",
      "description": "Open GitHub Watch settings for the site repo (real subscription). Prefer commits.atom for feed-based watching.",
      "invoke": {
        "method": "ACTION",
        "action": "open",
        "url": "https://github.com/chengguruchun/chengguruchun.github.io/subscription",
        "also": {
          "commits_atom": "https://github.com/chengguruchun/chengguruchun.github.io/commits/main.atom",
          "feeds_index": "/api/feeds.json"
        }
      }
    },
    {
      "name": "lab_list_feeds",
      "kind": "read",
      "description": "List all Lab subscription endpoints: content RSS, GitHub Watch URL, commits/releases Atom.",
      "invoke": {
        "method": "GET",
        "path": "/api/feeds.json"
      }
    },
    {
      "name": "lab_list_times",
      "kind": "read",
      "description": "List Times 热词 issues: annual-hotword style (one word + why), cadence weekly or monthly.",
      "invoke": {
        "method": "GET",
        "path": "/api/times.json"
      }
    },
    {
      "name": "lab_list_comments",
      "kind": "read",
      "description": "List page comments stored as GitHub Issues containing marker [lab-comment:<pageId>].",
      "invoke": {
        "method": "GET",
        "url_template": "https://api.github.com/search/issues?q=repo:chengguruchun/chengguruchun.github.io+is:issue+in:body+%22[lab-comment:{pageId}]%22"
      }
    },
    {
      "name": "lab_post_comment",
      "kind": "contribute",
      "description": "Open a GitHub Issue to leave a comment on a Lab page (marker [lab-comment:<pageId>]).",
      "invoke": {
        "method": "ACTION",
        "action": "open",
        "url_template": "https://github.com/chengguruchun/chengguruchun.github.io/issues/new?title={title}&body={body}"
      }
    },
    {
      "name": "lab_hot_words_status",
      "kind": "read",
      "description": "Deprecated alias of lab_hot_words_state. Prefer lab_hot_words_state.",
      "invoke": {
        "method": "GET",
        "path": "/api/times.json"
      }
    },
    {
      "name": "lab_hot_words_list",
      "kind": "read",
      "description": "List Hot Words issues (current + history) from /api/times.json.",
      "invoke": {
        "method": "GET",
        "path": "/api/times.json"
      }
    },
    {
      "name": "lab_hot_words_state",
      "kind": "read",
      "description": "Read Hot Words state: current period/word, recent history (api/times.json). Phase: State (no side effects).",
      "invoke": {
        "method": "GET",
        "path": "/api/times.json"
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "none",
        "rollback": "n/a",
        "timeoutSeconds": 30
      }
    },
    {
      "name": "lab_hot_words_run_status",
      "kind": "read",
      "description": "Observe latest Hot Words run manifest (phase, gates, proposal, publish). GET /api/hot-words/runs/latest.json or /api/hot-words/runs/{runId}.json. Prefer this over parsing GitHub Actions logs.",
      "invoke": {
        "method": "GET",
        "path": "/api/hot-words/runs/latest.json"
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "runId|latest",
        "rollback": "n/a",
        "timeoutSeconds": 30
      }
    },
    {
      "name": "lab_hot_words_propose",
      "kind": "contribute",
      "description": "Propose 3–5 Hot Words candidates with structured evidence when possible. Today: performed inside GitHub Actions (DeepSeek). No local side effects via static HTTP; agents should treat this as the Propose phase contract.",
      "invoke": {
        "method": "ACTION",
        "action": "workflow",
        "note": "Embedded in hot-words-weekly.yml DeepSeek step; not a standalone HTTP endpoint yet."
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "period + contextHash",
        "rollback": "n/a",
        "timeoutSeconds": 120
      }
    },
    {
      "name": "lab_hot_words_validate",
      "kind": "read",
      "description": "Validate a Hot Words proposal (schema, duplicate, editorial, https evidence required to publish, relevance, consistency). Actions enforce the hard evidence gate.",
      "invoke": {
        "method": "ACTION",
        "action": "local",
        "command_template": "python3 scripts/hot_words_validate.py --period {period} --word {word} --why {why}"
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "period + word",
        "rollback": "n/a",
        "timeoutSeconds": 30
      }
    },
    {
      "name": "lab_hot_words_apply",
      "kind": "contribute",
      "description": "Apply an approved Hot Words proposal to the LOCAL workspace only (scripts/hot_words_apply.py). Does NOT commit, push, or publish. Phase: Apply. Monday GitHub Actions auto-publishes the weekly Hot Words run; this tool only mutates the local workspace.",
      "invoke": {
        "method": "ACTION",
        "action": "local",
        "command_template": "python3 scripts/hot_words_apply.py --period {period} --word {word} --why {why}"
      },
      "contract": {
        "sideEffects": [
          "write_local_files"
        ],
        "requiresApproval": false,
        "idempotencyKey": "period",
        "rollback": "git checkout -- times/ content/times api/times.json content/index.json api/catalog.json",
        "timeoutSeconds": 120
      }
    },
    {
      "name": "lab_hot_words_publish",
      "kind": "action",
      "description": "Publish applied Hot Words to the remote repo (git commit/push or future PR). HIGH RISK. Today: GitHub Actions hot-words-weekly.yml performs publish in autonomous mode after apply. Agents must not treat apply as publish.",
      "invoke": {
        "method": "ACTION",
        "action": "workflow",
        "workflow": ".github/workflows/hot-words-weekly.yml",
        "note": "Default mode autonomous on Mondays; review/PR mode is a documented future option."
      },
      "contract": {
        "sideEffects": [
          "git_commit",
          "git_push",
          "pages_deploy"
        ],
        "requiresApproval": true,
        "idempotencyKey": "period + proposalId",
        "rollback": "revert_commit",
        "timeoutSeconds": 600
      }
    },
    {
      "name": "lab_hot_words_feedback",
      "kind": "read",
      "description": "Observe feedback signals for Hot Words (visits, votes, comments, citations). Stub: returns run manifest + times state until Eval loop is wired.",
      "invoke": {
        "method": "GET",
        "path": "/api/hot-words/feedback.json"
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "period|latest",
        "rollback": "n/a",
        "timeoutSeconds": 30
      }
    },
    {
      "name": "lab_agent_native_status",
      "kind": "read",
      "description": "Observe the latest Agent-native weekly check (surfaces, discover→execute, canonical Markdown, tool parity). GET /api/agent-native/runs/latest.json. Criteria: /api/agent-native/criteria.json.",
      "invoke": {
        "method": "GET",
        "path": "/api/agent-native/runs/latest.json"
      },
      "contract": {
        "sideEffects": [],
        "requiresApproval": false,
        "idempotencyKey": "runId|latest",
        "rollback": "n/a",
        "timeoutSeconds": 30
      }
    },
    {
      "name": "lab_list_thoughts",
      "kind": "read",
      "description": "List thought-loop items. Inbound from any Skill-loading agent is stage=topic (distilled chat, not a conclusion). Filter by stage, origin, or route. Only published items are conclusions. Hot Words is a separate weekly loop.",
      "invoke": {
        "method": "GET",
        "path": "/api/thoughts.json"
      }
    },
    {
      "name": "lab_get_thought",
      "kind": "read",
      "description": "Fetch one thought JSON by id (canonical source under /content/thoughts/). Topics are inbound experiments, not articles.",
      "invoke": {
        "method": "GET",
        "path_template": "/content/thoughts/{id}.json"
      }
    },
    {
      "name": "lab_propose_topic",
      "kind": "contribute",
      "description": "File an inbound idea as stage=topic: one distilled sentence, origin, no transcript. Does not write catalog or Articles. Human review promotes it through gates.",
      "invoke": {
        "method": "ACTION",
        "action": "pr",
        "path": "/content/thoughts/"
      }
    },
    {
      "name": "lab_thought_validate",
      "kind": "read",
      "description": "Read field gates[] and model_gates[] on /api/thoughts.json. Field = script. Model = criteria prompt. Pending/stale is not a ship. Do not treat topic or candidate as conclusions.",
      "invoke": {
        "method": "GET",
        "path": "/api/thoughts.json"
      }
    },
    {
      "name": "lab_thought_criteria",
      "kind": "read",
      "description": "Read the living model-gate standard (prompt + axes + changelog). Improve by editing content/thoughts/criteria.json and bumping version.",
      "invoke": {
        "method": "GET",
        "path": "/api/thoughts-criteria.json"
      }
    },
    {
      "name": "lab_thought_judge",
      "kind": "contribute",
      "description": "Run the current criteria prompt on one thought. Write only judgement. Do not change stage/route/catalog or article bodies.",
      "invoke": {
        "method": "ACTION",
        "action": "pr",
        "path": "/content/thoughts/"
      }
    },
    {
      "name": "lab_knowledge_status",
      "kind": "read",
      "description": "Read published-knowledge freshness: status, last_verified, next_review. Times/Hot Words are excluded. stale is overdue, not wrong.",
      "invoke": {
        "method": "GET",
        "path": "/api/knowledge.json"
      }
    },
    {
      "name": "lab_knowledge_due",
      "kind": "read",
      "description": "Same registry; client-filter items where knowledge.due is true (stale / changed / contested).",
      "invoke": {
        "method": "GET",
        "path": "/api/knowledge.json",
        "client_filter": true
      }
    },
    {
      "name": "lab_knowledge_criteria",
      "kind": "read",
      "description": "Read the post-publish verifier prompt and cadence policy. Improve by editing content/knowledge/criteria.json and bumping version.",
      "invoke": {
        "method": "GET",
        "path": "/api/knowledge-criteria.json"
      }
    },
    {
      "name": "lab_knowledge_run_status",
      "kind": "read",
      "description": "Observe the latest knowledge lifecycle run (due ids, gates). GET /api/knowledge/runs/latest.json.",
      "invoke": {
        "method": "GET",
        "path": "/api/knowledge/runs/latest.json"
      }
    },
    {
      "name": "lab_knowledge_report",
      "kind": "contribute",
      "description": "File a report only after observing cited URLs / arXiv ids (unchanged/changed/insufficient/obsolete). Never rewrite articles or bump last_verified.",
      "invoke": {
        "method": "ACTION",
        "action": "pr",
        "path": "/content/knowledge/reports/"
      }
    },
    {
      "name": "lab_knowledge_review",
      "kind": "contribute",
      "description": "Human decision after a report: keep / changed / contested / archived. Local script only; does not ghostwrite.",
      "invoke": {
        "method": "ACTION",
        "action": "local",
        "command_template": "python3 scripts/knowledge_review.py --id {id} --decision {decision}"
      }
    },
    {
      "name": "lab_list_critic_runs",
      "kind": "read",
      "description": "List Critic Lab advisory runs (id, article slug, paths to .json/.md).",
      "invoke": {
        "method": "GET",
        "path": "/api/critic/runs/index.json"
      }
    },
    {
      "name": "lab_get_critic_run",
      "kind": "read",
      "description": "Fetch one Critic Lab run Markdown (or JSON) by run id / filename stem.",
      "invoke": {
        "method": "GET",
        "path_template": "/api/critic/runs/{id}.md",
        "alt_path_template": "/api/critic/runs/{id}.json",
        "index": "/api/critic/runs/index.json"
      }
    }
  ]
}
