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.
Best use case
add-resource is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.
Teams using add-resource should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/add-resource/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How add-resource Compares
| Feature / Agent | add-resource | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
## 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 and is registered into `createApp()` in `src/index.ts`. Some repos later add `definitions/index.ts` barrels; follow the pattern already used by the project.
**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.
For the full `resource()` API, pagination utilities, and `Context` interface, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
## Steps
1. **Ask the user** for the resource's URI template, purpose, and data shape
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`, update that barrel instead of changing the registration style.
### 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.' },
{ reason: 'withdrawn', code: JsonRpcErrorCode.NotFound,
when: 'Article was withdrawn upstream.' },
{ reason: 'upstream_throttled', code: JsonRpcErrorCode.RateLimited,
when: 'Upstream PubMed quota hit.', retryable: true },
],
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.
### Other `resource()` options
Beyond `description`, `params`, `handler`, and `list`, the builder also supports:
| Field | Purpose |
|:------|:--------|
| `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`). |
## Checklist
- [ ] File created at `src/mcp-server/resources/definitions/{{resource-name}}.resource.ts`
- [ ] URI template uses `{paramName}` syntax for path parameters
- [ ] All Zod `params` fields have `.describe()` annotations
- [ ] JSDoc `@fileoverview` and `@module` header present
- [ ] `handler(params, ctx)` is pure — throws on failure, no try/catch
- [ ] Data is reachable via the tool surface (dedicated tool, another tool's output, or not needed for tool-only agents)
- [ ] `list()` function provided if the resource is discoverable
- [ ] Pagination used for large result sets (`extractCursor`/`paginateArray`)
- [ ] 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`)Related Skills
tool-defs-analysis
Read-only audit of MCP definition language across an existing surface — tools, resources, prompts. Walks every definition file and checks 12 categories the LLM reads to decide whether and how to call: voice & tense, internal leaks, audience leaks, defaults, recovery hints, output descriptions, cross-references, sparsity, examples, structure, mutator observability, unit-bearing numeric names. Produces grouped findings with file:line citations and a numbered options list. Use during polish, after a refactor, or before a release. Complements `field-test` (behavior testing) and `security-pass` (security audit).
setup
Post-init orientation for an MCP server built on @cyanheads/mcp-ts-core. Use after running `@cyanheads/mcp-ts-core init` to understand the project structure, conventions, and skill sync model. Also use when onboarding to an existing project for the first time.
security-pass
Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell / sampling / schema strictness / ReDoS), tenant isolation, leakage through errors and telemetry, unbounded resources, and HTTP-mode deployment surface. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.
report-issue-local
File a bug or feature request against this MCP server's own repo. Use for server-specific issues — tool logic, service integrations, config problems, or domain bugs that aren't caused by the framework.
report-issue-framework
File a bug or feature request against @cyanheads/mcp-ts-core when you hit a framework issue. Use when a builder, utility, context method, or config behaves contrary to the documented API — not for server-specific application bugs.
release-and-publish
Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.
polish-docs-meta
Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
orchestrations
Pick and run a multi-phase workflow that chains foundational task skills (`git-wrapup`, `release-and-publish`, `maintenance`, `field-test`, `setup`, etc.) end-to-end. Routes user intent to a workflow file under `workflows/` — greenfield builds, maintenance + release, field-test + fix, or known-work + release. Single source for the universal rules (no commits without authorization, no destructive git, no marketing language), the orchestrator posture (own the goal, ground sub-agents in primary sources, verify against the goal), and the sub-agent strategy (orient block, parallel fanout, isolation, normalization) that apply across every workflow. Sub-agents are an optional capability — workflows run linearly when fanout isn't available.
maintenance
Investigate, adopt, and verify dependency updates — with special handling for `@cyanheads/mcp-ts-core`. Captures what changed, understands why, cross-references against the codebase, adopts framework improvements, syncs project skills, and runs final checks. Supports two entry modes: run the full flow end-to-end, or review updates you already applied.
git-wrapup
Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
field-test
Exercise tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the catalog, runs real and adversarial inputs, and produces a tight report with concrete findings and numbered follow-up options. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface.
devcheck
Lint, format, typecheck, and verify the project is clean. Use after making changes, before committing, or when the user asks to verify quality.