Skip to main content

add-resource

Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
cyanheads/crossref-mcp-server
آخر نشاط في المصدر
١٢ يونيو ٢٠٢٦ في ٠٣:٤٥
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٣
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
add-resource
description
Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.
metadata
{"author":"cyanheads","version":"1.4","audience":"external","type":"reference"}
## Context Resources use the `resource()` builder from `@cyanheads/mcp-ts-core`. Each resource lives in `src/mcp-server/resources/definitions/` with a `.resource.ts` suffix. The standard registration pattern uses a `definitions/index.ts` barrel that collects all resources into an `allResourceDefinitions` array for `createApp()`. Fresh scaffolds start with direct imports in `src/index.ts` — the barrel is introduced as definitions grow. Match the pattern already used by the project you're editing. **Tool coverage.** Not all MCP clients expose resources — many are tool-only (Claude Code, Cursor, most chat UIs). Before adding a resource, verify the same data is reachable via the tool surface — either through a dedicated tool, included in another tool's output, or bundled into a broader tool. A resource whose data has no tool path is invisible to a large share of agents. ## Steps 1. **Gather** the resource's URI template, purpose, and data shape from the user's request — ask only if genuinely absent 2. **Design the URI** — use `{paramName}` for path parameters (e.g., `myscheme://{itemId}/data`) 3. **Create the file** at `src/mcp-server/resources/definitions/{{resource-name}}.resource.ts` 4. **Register** the resource in the project's existing `createApp()` resource list (directly in `src/index.ts` for fresh scaffolds, or via a barrel if the repo already has one) 5. **Run `bun run devcheck`** to verify 6. **Smoke-test** with `bun run rebuild && bun run start:stdio` (or `start:http`) ## Template ```typescript /** * @fileoverview {{RESOURCE_DESCRIPTION}} * @module mcp-server/resources/definitions/{{RESOURCE_NAME}} */ import { resource, z } from '@cyanheads/mcp-ts-core'; export const {{RESOURCE_EXPORT}} = resource('{{scheme}}://{{{paramName}}}/data', { description: '{{RESOURCE_DESCRIPTION}}', mimeType: 'application/json', // size: 1024, // optional: content size in bytes, if known params: z.object({ {{paramName}}: z.string().describe('{{PARAM_DESCRIPTION}}'), }), // auth: ['resource:{{resource_name}}:read'], async handler(params, ctx) { ctx.log.debug('Fetching resource', { {{paramName}}: params.{{paramName}} }); // Pure logic — throw on failure, no try/catch return { /* resource data */ }; }, list: async (extra) => ({ resources: [ { uri: '{{scheme}}://all', name: '{{RESOURCE_LIST_NAME}}', mimeType: 'application/json', }, ], }), }); ``` ### With pagination For resources that return large result sets, include `cursor` in the URI template params and use opaque cursor pagination in the `handler`. The cursor arrives as a validated URI param. `paginateArray` requires a `RequestContext` for logging — create one from `requestContextService`: ```typescript import { extractCursor, paginateArray, requestContextService } from '@cyanheads/mcp-ts-core/utils'; // URI template: '{{scheme}}://{{{paramName}}}/items' params: z.object({ {{paramName}}: z.string().describe('{{PARAM_DESCRIPTION}}'), cursor: z.string().optional().describe('Opaque pagination cursor'), }), async handler(params, ctx) { const allItems = await fetchAllItems(params.{{paramName}}); const cursor = extractCursor({ cursor: params.cursor }); const reqCtx = requestContextService.createRequestContext({ operation: 'list-{{paramName}}', parentContext: { requestId: ctx.requestId, traceId: ctx.traceId }, }); const page = paginateArray(allItems, cursor, 20, 100, reqCtx); return { items: page.items, nextCursor: page.nextCursor, }; }, ``` ### Registration ```typescript // src/index.ts (fresh scaffold default) import { createApp } from '@cyanheads/mcp-ts-core'; import { {{RESOURCE_EXPORT}} } from './mcp-server/resources/definitions/{{resource-name}}.resource.js'; await createApp({ tools: [/* existing tools */], resources: [{{RESOURCE_EXPORT}}], prompts: [/* existing prompts */], }); ``` If the repo already uses `src/mcp-server/resources/definitions/index.ts`, add the export to that barrel instead: ```typescript export { {{RESOURCE_EXPORT}} } from './{{resource-name}}.resource.js'; ``` ### Optional: declarative `errors[]` contract Resources can opt into the same typed error contract as tools — bound to a typed `ctx.fail(reason, …)` keyed by the declared reason union: ```typescript import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors'; export const articleResource = resource('article://{pmid}', { description: 'Read an article by PMID.', errors: [ { reason: 'no_pmid_match', code: JsonRpcErrorCode.NotFound, when: 'PMID not found in the index.', recovery: 'Use pubmed_search_articles to discover valid PMIDs first.' }, { reason: 'withdrawn', code: JsonRpcErrorCode.NotFound, when: 'Article was withdrawn upstream.', recovery: 'Check PubMed directly for retraction or withdrawal notices.' }, { reason: 'upstream_throttled', code: JsonRpcErrorCode.RateLimited, when: 'Upstream PubMed quota hit.', retryable: true, recovery: 'Wait a few seconds and retry the request.' }, ], params: z.object({ pmid: z.string().describe('PubMed ID') }), async handler(params, ctx) { const article = await fetchOne(params.pmid); if (!article) throw ctx.fail('no_pmid_match', `PMID ${params.pmid} not indexed`); if (article.withdrawn) throw ctx.fail('withdrawn'); return article; }, }); ``` Without `errors[]`, the handler receives plain `Context` (no `fail` method) and throws via error factories (`notFound`, `serviceUnavailable`, …) directly. The contract is opt-in. See `skills/api-errors/SKILL.md` for the full pattern, baseline codes, and conformance rules. ### URI template variable completion Add a `complete` map to enable autocompletion for URI template variables. The SDK auto-installs `completion/complete` handling and advertises the `completions` capability when a registered resource template has completion callbacks — no other changes needed. ```typescript import { resource, z } from '@cyanheads/mcp-ts-core'; const ITEM_IDS = ['item-001', 'item-002', 'item-abc']; export const itemResource = resource('items://{itemId}', { description: 'Retrieve an item by ID.', params: z.object({ itemId: z.string().describe('Item identifier') }), handler: (params) => ({ id: params.itemId }), list: () => ({ resources: ITEM_IDS.map((id) => ({ uri: `items://${id}`, name: id })) }), // Per-variable completion callbacks — keys must match URI template variable names. complete: { itemId: async (partial) => ITEM_IDS.filter((id) => id.startsWith(partial)), }, }); ``` Only applies to templated resources (URI templates with `{variable}` syntax). Static URIs don't support completion. ### Other `resource()` options Beyond `description`, `params`, `handler`, and `list`, the builder also supports: | Field | Purpose | |:------|:--------| | `name` | Short human-readable name for `resources/list`. Defaults to a slug derived from the URI template if omitted. | | `output` | Optional Zod schema for runtime validation of the handler return value (parity with `tool()`'s `output`). | | `format` | Optional formatter mapping the handler's return to the `ReadResourceResult.contents[]` shape. Default: string passthrough; objects serialized to JSON. Override when you need to attach permissions, custom encodings, or split into multiple content items. | | `annotations` | Resource annotations (e.g., `audience`, `priority`) — see `ResourceAnnotations`. | | `title` | Human-readable display title (defaults to `name`). | | `examples` | Array of `{ name, uri }` example entries surfaced in `resources/list` for discoverability. | | `complete` | Per-variable completion callbacks for URI template variables. Keys match template variable names. Enables `completion/complete` and the `completions` capability. | ## Checklist - [ ] File created at `src/mcp-server/resources/definitions/{{resource-name}}.resource.ts` - [ ] Resource name passed to `resource()` uses a valid URI template with `{paramName}` syntax - [ ] All Zod `params` fields have `.describe()` annotations - [ ] `output` schema added if the handler returns structured data that benefits from runtime validation - [ ] JSDoc `@fileoverview` and `@module` header present - [ ] `handler(params, ctx)` is pure — throws on failure, no try/catch - [ ] If `errors[]` contract declared: every entry has a `recovery` field (≥5 words, lint-enforced) - [ ] Data is reachable via the tool surface — confirm by checking `src/mcp-server/tools/definitions/` for a tool that exposes this data, or document why this resource is resources-only - [ ] `list()` function provided if the resource is discoverable - [ ] Pagination used for large result sets (`extractCursor`/`paginateArray`) — applies to both `handler` data and `list()` catalogs with many entries - [ ] Registered in the project's existing `createApp()` resource list (directly or via barrel) - [ ] `bun run devcheck` passes - [ ] Smoke-tested with `bun run rebuild && bun run start:stdio` (or `start:http`)
عرض على GitHub