Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Cyanheads Add Resource

ASecurity

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

76 stars
0 votes
0 copies
0 views
Added 9/22/2026
ai-agentstypescriptapi

Works with

claude codecursorcliapimcp

Security Analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned 9/22/2026

$npx -y skills add jiayaoqijia/cryptoskill --skill cyanheads-add-resource --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cyanheads Add Resource?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Cyanheads Add Resource
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jiayaoqijia-cyanheads-add-resource/badge)](https://www.skillsdirectory.com/skills/jiayaoqijia-cyanheads-add-resource)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
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.6"
  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 `framework-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. |
| `cacheHint` | `{ ttlMs?, cacheScope? }` — how long a client may cache this resource's `resources/read` result on protocol revision 2026-07-28. See below. |

### Cache hints (2026-07-28)

A resource that serves slow-changing data can declare how long its `resources/read` result stays fresh. `ttlMs` is the lifetime in milliseconds (a non-negative safe integer); `cacheScope` is `'private'` (only the requesting client may cache it) or `'public'` (shared caches may too).

```typescript
export const referenceTable = resource('reference://units', {
  description: 'Unit conversion reference table.',
  cacheHint: { ttlMs: 86_400_000, cacheScope: 'public' },
  handler: () => UNIT_TABLE,
});
```

Resolution is per field, most specific first: the resource's own `cacheHint`, then the `resources/read` entry of `createApp({ cacheHints })`, then the SDK defaults (`ttlMs: 0`, `cacheScope: 'private'`). So a resource that names only a scope still inherits the server-wide lifetime.

Set a server-wide policy for the list operations alongside it — those results the SDK builds itself, so a resource cannot speak for them:

```typescript
await createApp({
  cacheHints: {
    'tools/list': { ttlMs: 3_600_000, cacheScope: 'public' },
    'resources/read': { ttlMs: 60_000 },
  },
  resources: [referenceTable],
});
```

Cacheable operations are `tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, `resources/read`, and `server/discover`. Responses to 2025-era clients are unaffected — the hint only fills fields the 2026-07-28 revision defines.

## 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
- [ ] `cacheHint` set if the data is slow-changing and worth caching (`ttlMs` a non-negative safe integer)
- [ ] 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`)

Attribution

jiayaoqijiajiayaoqijia
View sourceSee grades on GitHubMore from jiayaoqijia →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698461 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →