MCP has three primitives: Tools, Resources, and Prompts. While Tools get all the attention, Resources and Prompts serve distinct purposes that are often misunderstood.
The Three Primitives
| Primitive | Control | Purpose | Client Support |
|---|
| Tools | Agent-driven | Actions with side effects | ✅ Widely supported |
| Resources | User-driven | Read-only data attachments | ⚠️ Limited support |
| Prompts | User or Agent | Instructions/templates | ⚠️ Limited support |
Resources and Prompts require client-side support. Not all MCP clients implement them. Tools are universally supported because they’re handled by LLM providers (Anthropic, OpenAI).
Resources: User-Controlled Data
What Are Resources?
Resources are read-only data that users can attach to their input. Key points:
- User decides — not the agent
- Agent cannot access resources directly — it must use Tools
- Tools control access — authentication, scopes, permissions
Real-World Examples
The best example is the @ command in Cursor, Windsurf, and Android Studio:
| Client | @ Command | What It Attaches |
|---|
| Cursor | @file.ts | File contents |
| Windsurf | @PR #123 | Pull request diff |
| Android Studio | @build.gradle | Build configuration |
When you type @file.ts, you’re adding a resource to your prompt. The user explicitly chooses what context to include.
When to Use Resources
import { Resource } from 'leanmcp';
@Resource({ description: "Current user's profile" })
async userProfile() {
return {
name: "John Doe",
role: "Developer",
preferences: { theme: "dark", language: "en" }
};
}
@Resource({ description: "Project configuration" })
async projectConfig() {
return {
name: "my-app",
version: "1.0.0",
dependencies: { ... }
};
}
Use resources for:
- Configuration files
- User profiles
- Project metadata
- Any read-only context users might want to attach
Resources are not for data the agent should fetch on its own. That’s what Tools are for.
Prompts: Instructions for Agents
What Are Prompts?
Prompts are instructions that tell the agent how to behave. They can be:
- User-added — Explicitly attached by the user
- Agent-picked — Agent selects relevant prompts when needed
Real-World Examples
The best example is Guidelines in Cursor and Windsurf:
| Client | Feature | Purpose |
|---|
| Cursor | .cursorrules | Project-specific coding guidelines |
| Windsurf | .windsurfrules | Project-specific behavior |
| Both | Guidelines panel | Agent instructions |
These are prompts — instructions that shape how the agent generates code.
When to Use Prompts
import { Prompt } from 'leanmcp';
@Prompt({ description: "Code review guidelines" })
codeReviewPrompt() {
return {
messages: [{
role: "user",
content: {
type: "text",
text: `You are a senior code reviewer. Follow these rules:
1. Check for security vulnerabilities
2. Ensure proper error handling
3. Verify edge cases are covered
4. Suggest performance improvements
5. Keep feedback constructive`
}
}]
};
}
@Prompt({ description: "API documentation writer" })
apiDocPrompt() {
return {
messages: [{
role: "user",
content: {
type: "text",
text: `Generate API documentation with:
- Clear endpoint descriptions
- Request/response examples
- Error codes and meanings
- Authentication requirements`
}
}]
};
}
Use prompts for:
- Coding standards
- Review guidelines
- Documentation templates
- Any reusable instructions
| Question | Answer |
|---|
| Who decides to use it? | Tools: Agent / Resources: User / Prompts: Both |
| Can it modify data? | Tools: Yes / Resources: No / Prompts: No |
| Needs client support? | Tools: No (LLM handles) / Resources: Yes / Prompts: Yes |
| Purpose? | Tools: Actions / Resources: Context / Prompts: Instructions |
Client Support Reality
The hard truth: Most MCP clients only support Tools.
| Client | Tools | Resources | Prompts |
|---|
| Claude Desktop | ✅ | ✅ | ✅ |
| Cursor | ✅ | ✅ (@ files) | ✅ (rules) |
| Windsurf | ✅ | ✅ (@ files) | ✅ (rules) |
| ChatGPT | ✅ | ❌ | ❌ |
| Custom clients | ✅ | Varies | Varies |
If your MCP needs broad client support, focus on Tools. Use Resources and Prompts as progressive enhancements for clients that support them.
Best Practices
For Resources
- Keep them read-only — Resources should never modify state
- Make them user-relevant — Only expose what users would want to attach
- Provide fallbacks — If resources aren’t supported, expose similar data via Tools
// Resource for clients that support it
@Resource({ description: "Project config" })
async projectConfig() {
return await this.getConfig();
}
// Tool fallback for clients that don't
@Tool({ description: "Get project config" })
async getProjectConfig() {
return await this.getConfig();
}
For Prompts
- Keep them focused — One prompt per behavior/task
- Make them reusable — Generic enough for multiple contexts
- Don’t duplicate — If it’s in the prompt, don’t repeat in tool descriptions
// ❌ BAD: Overly specific
@Prompt({ description: "Review React TypeScript code on Monday" })
// ✅ GOOD: Reusable
@Prompt({ description: "Code review guidelines" })
Summary
| Primitive | Control | Best For | Client Support |
|---|
| Tools | Agent | Actions, data access, auth | ✅ Universal |
| Resources | User | Config, profiles, context | ⚠️ Limited |
| Prompts | Both | Instructions, guidelines | ⚠️ Limited |
Key insight: Tools are LLM-focused (agent-driven). Resources and Prompts are UI-focused (user-driven). Build for Tools first, enhance with Resources/Prompts for supporting clients.