How to Register Resources and Prompts in MCP Servers: A Complete Implementation Guide
You register resources and prompts in an MCP server by instantiating an McpServer with the appropriate capabilities, then calling server.registerResource() for URI-addressable content and server.registerPrompt() for reusable prompt templates, typically orchestrated through modular helper functions during server initialization.
The Model Context Protocol (MCP) enables AI clients to discover and interact with server-side capabilities through standardized resources and prompts. In the modelcontextprotocol/servers repository, the registration architecture separates concerns cleanly: the server factory creates the instance, while dedicated modules handle the attachment of specific resources and prompts. This pattern ensures maintainable code as you extend the server with custom functionality.
MCP Server Registration Architecture
The registration flow is orchestrated in src/everything/server/index.ts, where the server bootstrap follows a strict sequence. First, the McpServer is instantiated with capability declarations (lines 45-74). Then, the server registers its core components: tools (lines 81-82), resources via registerResources(server) (lines 84-85), prompts via registerPrompts(server) (lines 87-88), and finally subscription handlers (lines 90-91).
This separation keeps the bootstrap code clean. The createServer factory function handles the heavy lifting, while individual registration modules contain the specific implementation details for each resource or prompt type.
How to Register Resources in MCP Servers
Resources in MCP are URI-addressable pieces of content—such as files, API responses, or generated data—that clients can read and optionally subscribe to updates. The registerResources function in src/everything/resources/index.ts serves as the entry point, delegating to specialized handlers for dynamic templates and static files.
// src/everything/resources/index.ts
export const registerResources = (server: McpServer) => {
registerResourceTemplates(server); // dynamic text/blob resources
registerFileResources(server); // static files from the repo
};
Dynamic Resource Templates
Dynamic resources use the ResourceTemplate class to handle URI patterns with variables. In src/everything/resources/templates.ts (lines 71-90), each template registers a handler that generates content on-the-fly based on the URI parameters.
// src/everything/resources/templates.ts (excerpt)
server.registerResource(
"Dynamic Text Resource",
new ResourceTemplate(textUriTemplate, {
list: undefined,
complete: { resourceId: resourceIdForResourceTemplateCompleter },
}),
{ mimeType: "text/plain", description: "Plaintext dynamic resource…" },
async (uri, variables) => ({
contents: [textResource(uri, parseResourceId(uri, variables))],
})
);
The ResourceTemplate constructor accepts a URI template string (e.g., resources://text/{resourceId}), completion handlers for argument suggestions, and metadata. The async handler receives the parsed URI and variables, returning the resource content with the appropriate MIME type.
Static File Resources
For static content, registerFileResources in src/everything/resources/files.ts exposes existing files from the repository. These resources use server.registerResource with fixed URIs rather than templates, serving markdown, images, or other assets directly without dynamic generation logic.
How to Register Prompts in MCP Servers
Prompts are reusable templates that help AI assistants interact with your server effectively. The registerPrompts function in src/everything/prompts/index.ts aggregates all prompt registrations, calling specialized functions for each prompt type.
// src/everything/prompts/index.ts
export const registerPrompts = (server: McpServer) => {
registerSimplePrompt(server);
registerArgumentsPrompt(server);
registerPromptWithCompletions(server);
registerEmbeddedResourcePrompt(server);
};
Simple Prompts
Simple prompts require no arguments and return fixed messages. The implementation in src/everything/prompts/simple.ts (lines 9-28) demonstrates the basic server.registerPrompt signature: a name, metadata object, and a generator function returning the message list.
Prompts with Arguments
For interactive prompts, registerArgumentsPrompt in src/everything/prompts/args.ts (lines 11-40) shows how to define required and optional parameters using a schema. The generator receives the parsed arguments and constructs dynamic messages based on user input.
Prompts with Completions
Argument autocompletion is implemented in src/everything/prompts/completions.ts. These prompts use the completable property in the args schema to provide valid values as the user types, improving the client-side experience when selecting parameters.
Embedded Resource Prompts
Advanced prompts can include resource links generated by templates. registerEmbeddedResourcePrompt in src/everything/prompts/resource.ts creates prompts that reference dynamic resources, allowing clients to fetch related content automatically when using the prompt.
All prompts follow the same registration pattern: server.registerPrompt(name, metadata, generator), where the generator is a function that returns a message list (and optionally resource references).
Practical Implementation Examples
Minimal Server Setup
This example shows the canonical pattern for initializing a server with resources and prompts enabled:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerResources } from "./everything/resources/index.js";
import { registerPrompts } from "./everything/prompts/index.js";
export const createSimpleServer = () => {
const server = new McpServer(
{ name: "demo", title: "Demo Server", version: "1.0.0" },
{ capabilities: { resources: { listChanged: true }, prompts: { listChanged: true } } }
);
// Register core artifacts
registerResources(server);
registerPrompts(server);
return server;
};
Custom Resource Template
Implement a dynamic CSV resource that generates content based on a URI parameter:
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
export const registerMyCsvResource = (server: McpServer) => {
const csvTemplate = "demo://csv/{resourceId}";
server.registerResource(
"Demo CSV Resource",
new ResourceTemplate(csvTemplate, {
list: undefined,
complete: { resourceId: (value) => /^[0-9]+$/.test(value) ? [value] : [] },
}),
{ mimeType: "text/csv", description: "CSV file generated on‑demand." },
async (uri, vars) => ({
contents: [
{
uri: uri.toString(),
mimeType: "text/csv",
text: `id,generated_at\n${vars.resourceId},${new Date().toISOString()}`,
},
],
})
);
};
Custom Prompt with Arguments
Create a prompt that accepts user input and generates a personalized greeting:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export const registerHelloPrompt = (server: McpServer) => {
const schema = {
name: z.string().describe("Your name"),
};
server.registerPrompt(
"hello-prompt",
{ title: "Hello Prompt", description: "Greets the user", argsSchema: schema },
(args) => ({
messages: [
{
role: "user",
content: { type: "text", text: `Hello, ${args?.name ?? "friend"}!` },
},
],
})
);
};
Summary
- Registration Flow: The
McpServerinstance is created insrc/everything/server/index.tswith capabilities declared, then resources and prompts are registered viaregisterResources(server)andregisterPrompts(server). - Dynamic Resources: Use
ResourceTemplatewith URI patterns and async handlers insrc/everything/resources/templates.tsto generate content on-demand. - Static Resources: Implement file-based resources in
src/everything/resources/files.tsusing fixed URI registration. - Prompt Types: Simple, argument-based, completion-enabled, and embedded-resource prompts are all registered via
server.registerPrompt()insrc/everything/prompts/index.ts. - Capabilities: Enable
resources.listChangedandprompts.listChangedin the server constructor to support subscription notifications.
Frequently Asked Questions
What is the difference between static file resources and dynamic resource templates in MCP servers?
Static file resources expose existing files with fixed URIs and pre-defined content, while dynamic resource templates use the ResourceTemplate class to handle variable URI patterns and generate content programmatically when accessed. Static resources are defined in src/everything/resources/files.ts, whereas dynamic templates are implemented in src/everything/resources/templates.ts.
How do I add argument autocompletion to a registered prompt?
Include a completable property in your prompt's args schema that defines completion functions for specific arguments, as demonstrated in src/everything/prompts/completions.ts. These functions receive the current input value and return an array of valid suggestions, enabling the client to provide autocomplete functionality during prompt invocation.
Can I register additional resources after the MCP server has started?
While the standard pattern registers all resources during initialization in src/everything/server/index.ts, the SDK supports adding resources conditionally after the client's initialized notification via the server.oninitialized callback. However, for predictable client behavior, register static capabilities during construction and use the listChanged capability to notify clients of dynamic updates.
What capabilities must be declared when creating an McpServer to support resources and prompts?
You must declare capabilities.resources and capabilities.prompts in the McpServer constructor options, optionally setting listChanged: true for each to enable subscription notifications. Without these capability declarations, clients cannot discover or interact with your registered resources and prompts according to the MCP specification.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →