[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GVCJ2D0BQAZ07Z22ESRARS":3,"skill-same:01M6GVCJ2D0BQAZ07Z22ESRARS":103},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":16,"sourceHtmlUrl":17,"categories":18,"keywords":32,"publishedAt":56,"linkOnly":57,"markdownContent":58,"markdownBody":59,"frontMatter":60,"refName":66,"commitSha":67,"skillDirectory":68,"forksCount":69,"repositoryHtmlUrl":70,"repositoryHomepageUrl":6,"ownerHtmlUrl":71,"ownerBlogUrl":72,"ownerDescription":73,"updatedAt":74,"install":75,"resources":88},"01M6GVCJ2D0BQAZ07Z22ESRARS","api-mirror",null,"Stand up a persistent, self-refreshing local mirror of a bulk upstream dataset with the MirrorService (@cyanheads\u002Fmcp-ts-core\u002Fmirror). Use when a server wraps a large or slow API and should query a synced local index (embedded SQLite + FTS5) instead of paginating the live API per request.","jiayaoqijia\u002Fcryptoskill","jiayaoqijia","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1453879?v=4","01M6GMYTX7Q7S504VC6E756ENR","YQ","01M6GMYV1QKJ1CC75MTTGT9PR0",81,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fmcp-servers\u002Fcyanheads-api-mirror\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fmcp-servers\u002Fcyanheads-api-mirror\u002FSKILL.md",[19,24,27],{"categoryNo":20,"categoryName":21,"taxonomyType":22,"taxonomyTypeDesc":23},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":25,"categoryName":26,"taxonomyType":22,"taxonomyTypeDesc":23},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":28,"categoryName":29,"taxonomyType":30,"taxonomyTypeDesc":31},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55],"api","api mirror","mirror","self","when","synced","request","fts5","with","bulk","dataset","live","local","MCP","upstream","query","reference","up","use","sqlite","server","index","core","2026-11-10 10:12:30",false,"---\nname: api-mirror\ndescription: >\n  Stand up a persistent, self-refreshing local mirror of a bulk upstream dataset with the MirrorService (@cyanheads\u002Fmcp-ts-core\u002Fmirror). Use when a server wraps a large or slow API and should query a synced local index (embedded SQLite + FTS5) instead of paginating the live API per request.\nmetadata:\n  author: cyanheads\n  version: \"1.5\"\n  audience: external\n  type: reference\n---\n\n## Context\n\nThe MirrorService owns the source-agnostic half of a local mirror — the embedded store, the sync-state machine, the runner — so a server supplies only the two parts that are irreducibly per-source: the **ingester** (a `sync` generator) and the **schema**. It targets the embedded-SQLite tier (~10⁴–10⁷ rows). Node\u002FBun only: `bun:sqlite` is built-in on Bun, `better-sqlite3` is an optional peer dependency on Node; the store is unavailable on Workers (no SQLite, no persistent filesystem).\n\nImport from `@cyanheads\u002Fmcp-ts-core\u002Fmirror`.\n\n## The shape\n\n```ts\nimport { defineMirror, sqliteMirrorStore } from '@cyanheads\u002Fmcp-ts-core\u002Fmirror';\n\nconst papers = defineMirror({\n  name: 'arxiv-papers',\n  store: sqliteMirrorStore({\n    path: config.mirrorPath,\n    table: 'papers',                                 \u002F\u002F primary table; FTS index is `papers_fts`\n    primaryKey: 'id',\n    columns: { id: 'TEXT', title: 'TEXT', authors: 'TEXT', abstract: 'TEXT', updated: 'TEXT' },\n    fts: ['title', 'authors', 'abstract'],          \u002F\u002F opt-in FTS5 external-content index\n    indexes: [{ columns: ['updated'] }],\n  }),\n  \u002F\u002F The ingester — the one part that is always server-specific.\n  async *sync({ mode, cursor, checkpoint, signal }) {\n    for await (const page of harvestPages({ resumeFrom: cursor, since: checkpoint, signal })) {\n      yield {\n        records: page.rows,             \u002F\u002F objects keyed by declared column\n        tombstones: page.deletedIds,    \u002F\u002F primary-key values to delete\n        cursor: page.token,             \u002F\u002F volatile resume position (see below)\n        checkpoint: page.maxStamp,      \u002F\u002F durable high-water mark (see below)\n      };\n    }\n  },\n});\n\nawait papers.runSync({ mode: 'init', signal: AbortSignal.timeout(3_600_000) }); \u002F\u002F full; resumes on interrupt\nawait papers.runSync({ mode: 'refresh' });                                       \u002F\u002F incremental\nconst { rows, total } = await papers.query({ match: 'transformers', limit: 10, offset: 0 });\nconst status = await papers.status();   \u002F\u002F { status, ready, checkpoint, total, ... }\n```\n\n## cursor vs. checkpoint — the core distinction\n\nTwo resume dimensions, deliberately separate. Conflating them silently corrupts resume for token-paged sources.\n\n| | `cursor` | `checkpoint` |\n|---|---|---|\n| Meaning | Volatile intra-run resume position (e.g. an OAI-PMH resumption token, a page token) | Durable incremental high-water mark (e.g. the max record datestamp) |\n| Lifetime | One run; may expire; **cleared on completion** | Persists; **advances monotonically, only on success** |\n| Used for | Resuming an interrupted `init` | Seeding the next `refresh` |\n\nWhy they can't merge: during a from-scratch init the records aren't ordered by the high-water field, so the max-so-far is not a valid resume position — only the cursor is. After a completed init the cursor is meaningless, but the high-water mark is the correct refresh seed. The framework persists both per page and threads the right one back into `sync()` per mode. **The checkpoint must be lexicographically monotonic** (ISO 8601 works); the runner advances the stored checkpoint only when a page's value compares greater.\n\n## What you own vs. what the framework owns\n\n| Framework | Server |\n|---|---|\n| Cross-runtime SQLite handle, WAL + `busy_timeout`; an open waits out another connection's lock for `busyTimeoutMs` | The `sync` generator (the ingester) |\n| `mirror_sync_state` + cursor\u002Fcheckpoint state machine | Translating your query syntax → FTS5 `match` |\n| `runSync({ init \\| refresh })`, per-page persist, resume | Mapping upstream records → row objects |\n| Schema gen (columns + FTS + tokenizer + triggers) | Migration *content* (the `up` functions) |\n| `schema_version` + migration *runner* | Scheduling + init\u002Frefresh bootstrap (see below) |\n| Generic `query()` + the raw-handle escape hatch | Server-specific access paths via the raw handle |\n\nA store that cannot be opened or initialized (a lock held past `busyTimeoutMs`, an unreadable or corrupted file, a parent directory that cannot be created, a migration that throws) rejects with `DatabaseError` (`-32010`). The caller sees the store's file name and a `data.recovery.hint`, never its directory; the driver or filesystem error stays on `cause` for the log.\n\n## Querying\n\n`query({ match?, filters?, sort?, limit, offset })` covers the common case:\n\n- `match` — an FTS5 `MATCH` expression (only when the store declares `fts` columns). Translate your own query grammar to FTS5 before calling.\n- `filters` — `[{ column, op, value }]`, AND-combined, over declared columns. `op` ∈ `eq|ne|gt|gte|lt|lte|in` (`in` takes an array).\n- `sort` — `{ column, direction }` or `'relevance'` (FTS bm25; requires `match`). Defaults to insertion order.\n\nFor access paths the generic query can't express — junction tables for index-backed multi-value filtering, denormalized counters, bespoke `bm25` weighting — use the **raw handle**: `const db = await mirror.raw();` then run prepared statements against your own auxiliary tables (declare them via a migration). Add the auxiliary DDL in a `migrations` step; maintain it from your `sync` mapping or SQL triggers.\n\nA migration runs identically on first creation and on upgrade: a fresh database runs every migration up to `version` right after the declarative DDL, an existing one runs only those above its stored version. So `up()` must tolerate a database that already has the current declarative shape — `CREATE TABLE IF NOT EXISTS` for auxiliary objects, never an `ALTER` that assumes an older layout of a declared column.\n\n## Readiness — key off the completion marker, not live status\n\n`status().ready` is `true` once a full sync has **ever completed** (`completedAt != null`), not when `status === 'complete'`. The dataset stays transactionally queryable during a refresh, so a mirror mid-refresh — or one whose last refresh failed — is still ready and should keep serving. Gate the mirror read path on `await mirror.ready()`; fall back to the live API only when it is `false` (cold, never-completed init).\n\n## Scheduling and bootstrap (server-owned)\n\nThe service owns `runSync` + state; it does not schedule. Wire \"self-refreshing\" yourself:\n\n- **Refresh** — register `runSync({ mode: 'refresh' })` on a cron via `schedulerService` from `@cyanheads\u002Fmcp-ts-core\u002Futils`, inside `setup()`. Gate on transport (HTTP) when stdio operators run it out-of-band.\n- **Off the serving thread** — SQLite calls are synchronous, so each write transaction a sync runs blocks the event loop until it commits. A refresh whose pages or post-sync recomputes take seconds stalls every request, `\u002Fhealthz` included, for that long. Run such a scheduled sync in a child process instead: spawn `process.execPath` with `process.execArgv` and a compiled job entry in `dist\u002F`, relay its log lines, and in `teardown` stop it with SIGTERM, then SIGKILL after a grace period. In-process scheduling suits mirrors whose transactions stay short.\n- **Init** — run out-of-band (a CLI script \u002F one-shot), never on startup: a full init can take hours and must not block the server. It is idempotent and resumable — re-running after an interrupt continues from the persisted cursor.\n\n### Shipping the mirror CLI in a production Docker image\n\nThe scaffold `Dockerfile` copies only `dist\u002F` to the runtime stage. A mirror lifecycle script (`mirror:init`, `mirror:refresh`, `mirror:verify`) that imports through the `@\u002F` path alias fails under `docker exec` — `@\u002F` resolves to `src\u002F` via the source `tsconfig.json`, and `src\u002F` never reaches the image.\n\nOn the Bun runtime image (`oven\u002Fbun`), two stanzas fix it — no build change, no `rootDir` surgery, and the `mirror:*` package scripts stay identical between a dev checkout and the image.\n\nAdd the following to the runtime stage of `Dockerfile`, after the `COPY --from=build ...\u002Fdist .\u002Fdist` line:\n\n```dockerfile\n# Copy mirror lifecycle scripts. The shared context shim (_mirror-context.ts)\n# is imported by the three named scripts, so it must travel with them.\nCOPY --from=build \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-init.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-refresh.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-verify.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F_mirror-context.ts \\\n                  .\u002Fscripts\u002F\n\n# Bun honors tsconfig `paths` at runtime — map `@\u002F` to the compiled `.\u002Fdist\u002F`\n# so the .ts scripts resolve their alias imports against the build output.\n# In a dev checkout the source tsconfig.json maps @\u002F* → .\u002Fsrc\u002F*; in the image\n# this emitted one maps @\u002F* → .\u002Fdist\u002F*. Same `bun run mirror:*` command, both\n# environments — the only lever is which tsconfig.json is on disk.\nRUN echo '{\"compilerOptions\":{\"baseUrl\":\".\",\"paths\":{\"@\u002F*\":[\".\u002Fdist\u002F*\"]}}}' > tsconfig.json\n```\n\n**Caveat:** this relies on Bun's runtime `paths` resolution. A Node runtime image (no native `.ts` execution) needs the scripts compiled into `dist\u002F` instead — a separate tsconfig pass with a different `rootDir` is required in that case.\n\n**`package.json` `files[]`:** add `scripts\u002F_mirror-context.ts` and the three named lifecycle scripts so the npm tarball and `.mcpb` bundle carry them. They resolve `@\u002F` only where a `tsconfig.json` maps it (a dev checkout, or the image above). An npm install ships no such mapping, so an npm-installed server cannot run them: document building the mirror from a checkout or the image.\n\n## Checklist\n\n- [ ] `defineMirror({ name, store, sync })`; the server holds the instance (one per mirror)\n- [ ] `sqliteMirrorStore` spec declares `primaryKey`, `columns`, and (if searching) `fts`\n- [ ] `sync` yields `{ records, tombstones?, cursor?, checkpoint? }` per page; checkpoint is lexicographically monotonic\n- [ ] Read path gated on `await mirror.ready()` with a live fallback when not ready\n- [ ] `better-sqlite3` added as a peer dependency for Node deployments; mirror disabled on Workers\n- [ ] Refresh wired via `schedulerService` in `setup()`; init runs out-of-band\n- [ ] `bun run devcheck` passes\n","## Context\n\nThe MirrorService owns the source-agnostic half of a local mirror — the embedded store, the sync-state machine, the runner — so a server supplies only the two parts that are irreducibly per-source: the **ingester** (a `sync` generator) and the **schema**. It targets the embedded-SQLite tier (~10⁴–10⁷ rows). Node\u002FBun only: `bun:sqlite` is built-in on Bun, `better-sqlite3` is an optional peer dependency on Node; the store is unavailable on Workers (no SQLite, no persistent filesystem).\n\nImport from `@cyanheads\u002Fmcp-ts-core\u002Fmirror`.\n\n## The shape\n\n```ts\nimport { defineMirror, sqliteMirrorStore } from '@cyanheads\u002Fmcp-ts-core\u002Fmirror';\n\nconst papers = defineMirror({\n  name: 'arxiv-papers',\n  store: sqliteMirrorStore({\n    path: config.mirrorPath,\n    table: 'papers',                                 \u002F\u002F primary table; FTS index is `papers_fts`\n    primaryKey: 'id',\n    columns: { id: 'TEXT', title: 'TEXT', authors: 'TEXT', abstract: 'TEXT', updated: 'TEXT' },\n    fts: ['title', 'authors', 'abstract'],          \u002F\u002F opt-in FTS5 external-content index\n    indexes: [{ columns: ['updated'] }],\n  }),\n  \u002F\u002F The ingester — the one part that is always server-specific.\n  async *sync({ mode, cursor, checkpoint, signal }) {\n    for await (const page of harvestPages({ resumeFrom: cursor, since: checkpoint, signal })) {\n      yield {\n        records: page.rows,             \u002F\u002F objects keyed by declared column\n        tombstones: page.deletedIds,    \u002F\u002F primary-key values to delete\n        cursor: page.token,             \u002F\u002F volatile resume position (see below)\n        checkpoint: page.maxStamp,      \u002F\u002F durable high-water mark (see below)\n      };\n    }\n  },\n});\n\nawait papers.runSync({ mode: 'init', signal: AbortSignal.timeout(3_600_000) }); \u002F\u002F full; resumes on interrupt\nawait papers.runSync({ mode: 'refresh' });                                       \u002F\u002F incremental\nconst { rows, total } = await papers.query({ match: 'transformers', limit: 10, offset: 0 });\nconst status = await papers.status();   \u002F\u002F { status, ready, checkpoint, total, ... }\n```\n\n## cursor vs. checkpoint — the core distinction\n\nTwo resume dimensions, deliberately separate. Conflating them silently corrupts resume for token-paged sources.\n\n| | `cursor` | `checkpoint` |\n|---|---|---|\n| Meaning | Volatile intra-run resume position (e.g. an OAI-PMH resumption token, a page token) | Durable incremental high-water mark (e.g. the max record datestamp) |\n| Lifetime | One run; may expire; **cleared on completion** | Persists; **advances monotonically, only on success** |\n| Used for | Resuming an interrupted `init` | Seeding the next `refresh` |\n\nWhy they can't merge: during a from-scratch init the records aren't ordered by the high-water field, so the max-so-far is not a valid resume position — only the cursor is. After a completed init the cursor is meaningless, but the high-water mark is the correct refresh seed. The framework persists both per page and threads the right one back into `sync()` per mode. **The checkpoint must be lexicographically monotonic** (ISO 8601 works); the runner advances the stored checkpoint only when a page's value compares greater.\n\n## What you own vs. what the framework owns\n\n| Framework | Server |\n|---|---|\n| Cross-runtime SQLite handle, WAL + `busy_timeout`; an open waits out another connection's lock for `busyTimeoutMs` | The `sync` generator (the ingester) |\n| `mirror_sync_state` + cursor\u002Fcheckpoint state machine | Translating your query syntax → FTS5 `match` |\n| `runSync({ init \\| refresh })`, per-page persist, resume | Mapping upstream records → row objects |\n| Schema gen (columns + FTS + tokenizer + triggers) | Migration *content* (the `up` functions) |\n| `schema_version` + migration *runner* | Scheduling + init\u002Frefresh bootstrap (see below) |\n| Generic `query()` + the raw-handle escape hatch | Server-specific access paths via the raw handle |\n\nA store that cannot be opened or initialized (a lock held past `busyTimeoutMs`, an unreadable or corrupted file, a parent directory that cannot be created, a migration that throws) rejects with `DatabaseError` (`-32010`). The caller sees the store's file name and a `data.recovery.hint`, never its directory; the driver or filesystem error stays on `cause` for the log.\n\n## Querying\n\n`query({ match?, filters?, sort?, limit, offset })` covers the common case:\n\n- `match` — an FTS5 `MATCH` expression (only when the store declares `fts` columns). Translate your own query grammar to FTS5 before calling.\n- `filters` — `[{ column, op, value }]`, AND-combined, over declared columns. `op` ∈ `eq|ne|gt|gte|lt|lte|in` (`in` takes an array).\n- `sort` — `{ column, direction }` or `'relevance'` (FTS bm25; requires `match`). Defaults to insertion order.\n\nFor access paths the generic query can't express — junction tables for index-backed multi-value filtering, denormalized counters, bespoke `bm25` weighting — use the **raw handle**: `const db = await mirror.raw();` then run prepared statements against your own auxiliary tables (declare them via a migration). Add the auxiliary DDL in a `migrations` step; maintain it from your `sync` mapping or SQL triggers.\n\nA migration runs identically on first creation and on upgrade: a fresh database runs every migration up to `version` right after the declarative DDL, an existing one runs only those above its stored version. So `up()` must tolerate a database that already has the current declarative shape — `CREATE TABLE IF NOT EXISTS` for auxiliary objects, never an `ALTER` that assumes an older layout of a declared column.\n\n## Readiness — key off the completion marker, not live status\n\n`status().ready` is `true` once a full sync has **ever completed** (`completedAt != null`), not when `status === 'complete'`. The dataset stays transactionally queryable during a refresh, so a mirror mid-refresh — or one whose last refresh failed — is still ready and should keep serving. Gate the mirror read path on `await mirror.ready()`; fall back to the live API only when it is `false` (cold, never-completed init).\n\n## Scheduling and bootstrap (server-owned)\n\nThe service owns `runSync` + state; it does not schedule. Wire \"self-refreshing\" yourself:\n\n- **Refresh** — register `runSync({ mode: 'refresh' })` on a cron via `schedulerService` from `@cyanheads\u002Fmcp-ts-core\u002Futils`, inside `setup()`. Gate on transport (HTTP) when stdio operators run it out-of-band.\n- **Off the serving thread** — SQLite calls are synchronous, so each write transaction a sync runs blocks the event loop until it commits. A refresh whose pages or post-sync recomputes take seconds stalls every request, `\u002Fhealthz` included, for that long. Run such a scheduled sync in a child process instead: spawn `process.execPath` with `process.execArgv` and a compiled job entry in `dist\u002F`, relay its log lines, and in `teardown` stop it with SIGTERM, then SIGKILL after a grace period. In-process scheduling suits mirrors whose transactions stay short.\n- **Init** — run out-of-band (a CLI script \u002F one-shot), never on startup: a full init can take hours and must not block the server. It is idempotent and resumable — re-running after an interrupt continues from the persisted cursor.\n\n### Shipping the mirror CLI in a production Docker image\n\nThe scaffold `Dockerfile` copies only `dist\u002F` to the runtime stage. A mirror lifecycle script (`mirror:init`, `mirror:refresh`, `mirror:verify`) that imports through the `@\u002F` path alias fails under `docker exec` — `@\u002F` resolves to `src\u002F` via the source `tsconfig.json`, and `src\u002F` never reaches the image.\n\nOn the Bun runtime image (`oven\u002Fbun`), two stanzas fix it — no build change, no `rootDir` surgery, and the `mirror:*` package scripts stay identical between a dev checkout and the image.\n\nAdd the following to the runtime stage of `Dockerfile`, after the `COPY --from=build ...\u002Fdist .\u002Fdist` line:\n\n```dockerfile\n# Copy mirror lifecycle scripts. The shared context shim (_mirror-context.ts)\n# is imported by the three named scripts, so it must travel with them.\nCOPY --from=build \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-init.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-refresh.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F\u003Cyour>-mirror-verify.ts \\\n                  \u002Fusr\u002Fsrc\u002Fapp\u002Fscripts\u002F_mirror-context.ts \\\n                  .\u002Fscripts\u002F\n\n# Bun honors tsconfig `paths` at runtime — map `@\u002F` to the compiled `.\u002Fdist\u002F`\n# so the .ts scripts resolve their alias imports against the build output.\n# In a dev checkout the source tsconfig.json maps @\u002F* → .\u002Fsrc\u002F*; in the image\n# this emitted one maps @\u002F* → .\u002Fdist\u002F*. Same `bun run mirror:*` command, both\n# environments — the only lever is which tsconfig.json is on disk.\nRUN echo '{\"compilerOptions\":{\"baseUrl\":\".\",\"paths\":{\"@\u002F*\":[\".\u002Fdist\u002F*\"]}}}' > tsconfig.json\n```\n\n**Caveat:** this relies on Bun's runtime `paths` resolution. A Node runtime image (no native `.ts` execution) needs the scripts compiled into `dist\u002F` instead — a separate tsconfig pass with a different `rootDir` is required in that case.\n\n**`package.json` `files[]`:** add `scripts\u002F_mirror-context.ts` and the three named lifecycle scripts so the npm tarball and `.mcpb` bundle carry them. They resolve `@\u002F` only where a `tsconfig.json` maps it (a dev checkout, or the image above). An npm install ships no such mapping, so an npm-installed server cannot run them: document building the mirror from a checkout or the image.\n\n## Checklist\n\n- [ ] `defineMirror({ name, store, sync })`; the server holds the instance (one per mirror)\n- [ ] `sqliteMirrorStore` spec declares `primaryKey`, `columns`, and (if searching) `fts`\n- [ ] `sync` yields `{ records, tombstones?, cursor?, checkpoint? }` per page; checkpoint is lexicographically monotonic\n- [ ] Read path gated on `await mirror.ready()` with a live fallback when not ready\n- [ ] `better-sqlite3` added as a peer dependency for Node deployments; mirror disabled on Workers\n- [ ] Refresh wired via `schedulerService` in `setup()`; init runs out-of-band\n- [ ] `bun run devcheck` passes\n",{"name":5,"metadata":61,"description":65},{"type":49,"author":62,"version":63,"audience":64},"cyanheads","1.5","external","Stand up a persistent, self-refreshing local mirror of a bulk upstream dataset with the MirrorService (@cyanheads\u002Fmcp-ts-core\u002Fmirror). Use when a server wraps a large or slow API and should query a synced local index (embedded SQLite + FTS5) instead of paginating the live API per request.\n","main","3df1d0d67b30ff15236561f8c0f9f06d33f42dfd","skills\u002Fmcp-servers\u002Fcyanheads-api-mirror",17,"https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia","https:\u002F\u002Feth2030.com","Hardcore infra. Building @alt-research and applied cryptography.","2026-11-12 02:28:42",{"source":8,"skillName":5,"commands":76,"prompt":86,"zipAvailable":87},[77,80,83],{"tool":78,"command":79},"npx","npx skills add jiayaoqijia\u002Fcryptoskill --skill api-mirror",{"tool":81,"command":82},"bunx","bunx skills add jiayaoqijia\u002Fcryptoskill --skill api-mirror",{"tool":84,"command":85},"pnpm","pnpm dlx skills add jiayaoqijia\u002Fcryptoskill --skill api-mirror","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fmcp-servers\u002Fcyanheads-api-mirror\u002FSKILL.md",true,[89,93,99],{"path":90,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":92,"sourceHtmlUrl":16},"LICENSE","许可证",11317,{"path":94,"resourceType":95,"resourceTypeDesc":96,"sizeBytes":97,"sourceHtmlUrl":98},"SOURCE.md","REFERENCE","参考文档",369,"https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fmcp-servers\u002Fcyanheads-api-mirror\u002FSOURCE.md",{"path":100,"resourceType":95,"resourceTypeDesc":96,"sizeBytes":101,"sourceHtmlUrl":102},"TRUST.auto.yaml",2141,"https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fmcp-servers\u002Fcyanheads-api-mirror\u002FTRUST.auto.yaml",[104,133,154,172,185,195],{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":108,"licenseFileUrl":109,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":56},"01M712C436EQYE43342Q4E7GG1","recipe-performance","Diagnose a specific MetaMask user flow's real-app performance and Sentry critical-user-flow telemetry with deterministic mm-harness recipes. Use for CUF boundaries, cold\u002Fwarm\u002Fbackground-resume behavior, latency regressions, lifecycle tags, duplicate or blocking network requests, long tasks, layout\u002Fpaint cost, screen redraws, React rerenders, or browser trace analysis where tests alone cannot prove the real app.","MIT","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fwallets\u002Fmetamask-official-recipe-performance\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fwallets\u002Fmetamask-official-recipe-performance\u002FSKILL.md",[],[113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132],"performance","recipe performance","recipe","mm","sentry","harness","app","deterministic","recipes","flow","cold","critical","analysis","tasks","react","browser","layout","screen","network","metamask",{"skillNo":134,"name":135,"nameZh":6,"description":136,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":137,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":56},"01M70XT2F8A5CQZ81JBP98KSW3","mexc-spot-websocket","MEXC Spot WebSocket workflows for public streams, protobuf handling, depth maintenance, listen keys, and private streams without exposing API keys.","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-spot-websocket\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-spot-websocket\u002FSKILL.md",[],[141,142,143,33,144,145,146,147,148,149,150,151,152,153],"mexc spot websocket","spot","websocket","workflows","public","listen","streams","depth","keys","for","api keys","maintenance","protobuf",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":158,"sourceHtmlUrl":159,"categories":160,"keywords":161,"publishedAt":56},"01M70XT0MBXN3WZY1BKN0CPNKR","mexc-spot-rest","MEXC Spot REST workflows for public data, signed account requests, orders, wallet\u002Fadmin actions, and secure signing without exposing API keys.","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-spot-rest\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-spot-rest\u002FSKILL.md",[],[162,142,163,164,165,166,167,151,150,149,168,169,170,145,171,144,33],"mexc spot rest","REST","admin","signing","Wallet","requests","actions","secure","data","orders",{"skillNo":173,"name":174,"nameZh":6,"description":175,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":176,"sourceHtmlUrl":177,"categories":178,"keywords":179,"publishedAt":56},"01M70XSYSVZ2N0N91XCAR2CAK0","mexc-futures-websocket","MEXC Futures WebSocket workflows for public streams, private account\u002Forder\u002Fposition streams, ping\u002Fpong, subscriptions, and depth maintenance.","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-futures-websocket\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fmncrftfrcnm-mexc-futures-websocket\u002FSKILL.md",[],[180,181,143,182,183,152,150,184,148,147,145,144],"mexc futures websocket","futures","position","ping","subscriptions",{"skillNo":186,"name":187,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":108,"licenseFileUrl":188,"sourceHtmlUrl":189,"categories":190,"keywords":191,"publishedAt":56},"01M6H0ZWHVE5W04WG2DKQE2SQC","0xklen-version-and-refresh-sanctions-lists","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fpayments\u002F0xklen-version-and-refresh-sanctions-lists\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fpayments\u002F0xklen-version-and-refresh-sanctions-lists\u002FSKILL.md",[],[192,193,194],"refresh","0xklen version and refresh sanctions lists","version",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":108,"licenseFileUrl":199,"sourceHtmlUrl":200,"categories":201,"keywords":202,"publishedAt":56},"01M6H0Z28Q3R8J0S799GJQWXM3","verify-tests-cover-changed-lines","Use when a PR adds code that the existing suite may not exercise. Measures patch coverage — the share of newly changed lines hit by tests — before approving.","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fpayments\u002F0xklen-verify-tests-cover-changed-lines\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fpayments\u002F0xklen-verify-tests-cover-changed-lines\u002FSKILL.md",[],[203,204,205,206,207,208,209,51,210,211,212],"tests","cover","verify tests cover changed lines","verify","coverage","pr","exercise","patch","code","share"]