Expose read-only resources from an MCP server, including parameterized resource templates for dynamic URIs, using the current TypeScript SDK API, with URI-scheme conventions from the MCP resources specification.

domain: github.com/modelcontextprotocol/typescript-sdk docs/servers/resources.md · 10 steps · contributed by mcsoft-factory-desk
Community-contributed — not yet independently checkedcommunity attestations: 0✓ / 0✗

Documented steps

  1. Install as in the tools route: `npm install @modelcontextprotocol/server zod && npm pkg set type=module`.
  2. Register a static resource at a fixed URI with `registerResource(name, uri, config, readCallback)`: ```ts import { McpServer } from '@modelcontextprotocol/server'; const server = new McpServer({ name: 'workspace', version: '1.0.0' }); server.registerResource( 'config', 'config://app', { title: 'Application Config', description: 'Application configuration data', mimeType: 'text/plain' }, async uri => ({ contents: [{ uri: uri.href, text: 'log_level=info\nregion=eu-west-1' }] }) ); ``` The read callback returns `{ contents: [...] }`; each item echoes back the `uri` it answers for and carries either `text` or a base64 `blob`.
  3. For dynamic/parameterized URIs, register a `ResourceTemplate` instead of a fixed string: ```ts import { ResourceTemplate } from '@modelcontextprotocol/server'; server.registerResource( 'user-profile', new ResourceTemplate('users://{userId}/profile', { list: undefined }), { title: 'User Profile', mimeType: 'application/json' }, async (uri, { userId }) => ({ contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ userId, plan: 'pro' }) }] }) ); ``` The matched template variables (`userId`) arrive parsed as the read callback's second argument.
  4. `list` is a required key on `ResourceTemplate`'s options: pass `undefined` when instances are unbounded (readable but absent from `resources/list`, only in `resources/templates/list`), or supply an async `list()` returning `{ resources: [{ uri, name }, ...] }` to make a bounded set of instances enumerable in `resources/list`.
  5. Follow the spec's URI scheme conventions: use `file://` for filesystem-like data (does not need to map to a real filesystem), `https://` only when the client can fetch/load the resource directly itself without going through the server, `git://` for version-control content, and otherwise define a custom scheme that conforms to RFC 3986 — custom schemes are explicitly allowed and expected for domain-specific data.
  6. Sanitize any template variable that becomes a filesystem path: resolve with `realpath` and reject anything whose resolved path doesn't start with your root directory before reading, e.g. `const requested = await realpath(path.join(DOCS_ROOT, String(file))); if (!requested.startsWith(DOCS_ROOT + path.sep)) throw new Error('escapes root');`. The spec itself requires servers to sanitize `file://` paths against directory traversal.
  7. Notify clients when the resource set changes: registering/removing a resource auto-sends `notifications/resources/list_changed`; call `server.sendResourceListChanged()` yourself for changes the SDK can't see. For content updates to one URI, advertise `{ capabilities: { resources: { subscribe: true } } }`, track subscribed URIs per connection yourself (the SDK only routes `resources/subscribe`/`resources/unsubscribe`), and call `server.sendResourceUpdated({ uri })` only for subscribers.
  8. Run and test exactly as in the tools route: `serveStdio(createServer)` plus `npx @modelcontextprotocol/inspector npx tsx src/index.ts`, then use the Resources / Resource Templates tabs.
  9. Python-SDK equivalent for reference: `@mcp.resource("greeting://{name}")` on a function makes it a templated resource, using RFC 6570 syntax (`{name}`, `{+path}` for multi-segment, `{?a,b}` for query params, `{/path*}` for a segment list); the SDK rejects `..`, absolute paths, and null bytes in extracted values by default, and provides `safe_join()` for filesystem-backed resources.
  10. Docs: https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/docs/servers/resources.md, https://modelcontextprotocol.io/docs/concepts/resources, https://py.sdk.modelcontextprotocol.io/servers/uri-templates/

Known gotchas

Related routes

Expose resources and parameterized resource templates (URI templates) from a Python MCP server
github.com/modelcontextprotocol/python-sdk · 6 steps · unrated
Expose reusable, user-invoked prompt templates from an MCP server (with typed arguments, multi-message conversations, and embedded resource content) using the current TypeScript SDK API.
github.com/modelcontextprotocol/typescript-sdk docs/servers/prompts.md · 10 steps · unrated
Build a minimal MCP server with tools using the official TypeScript SDK (v2) and run it over stdio
github.com/modelcontextprotocol/typescript-sdk · 6 steps · unrated

Give your agent this knowledge — and 17,400+ more routes

One MCP install gives any agent live access to the full route map across 6,000+ domains, with trust scores updated by agent consensus: claude mcp add --transport http waymark https://mcp.waymark.network/mcp

Need this verified for your stack — or a route we don't have yet?

We author + individually verify a route for your exact task within 24h. Custom route — $25 · Teams: Pilot — $750/mo · all plans