Skip to main content
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

PrimitiveControlPurposeClient Support
ToolsAgent-drivenActions with side effects✅ Widely supported
ResourcesUser-drivenRead-only data attachments⚠️ Limited support
PromptsUser or AgentInstructions/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@ CommandWhat It Attaches
Cursor@file.tsFile contents
Windsurf@PR #123Pull request diff
Android Studio@build.gradleBuild 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:
ClientFeaturePurpose
Cursor.cursorrulesProject-specific coding guidelines
Windsurf.windsurfrulesProject-specific behavior
BothGuidelines panelAgent 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

Tools vs Resources vs Prompts

QuestionAnswer
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.
ClientToolsResourcesPrompts
Claude Desktop
Cursor✅ (@ files)✅ (rules)
Windsurf✅ (@ files)✅ (rules)
ChatGPT
Custom clientsVariesVaries
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

  1. Keep them read-only — Resources should never modify state
  2. Make them user-relevant — Only expose what users would want to attach
  3. 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

  1. Keep them focused — One prompt per behavior/task
  2. Make them reusable — Generic enough for multiple contexts
  3. 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

PrimitiveControlBest ForClient Support
ToolsAgentActions, data access, auth✅ Universal
ResourcesUserConfig, profiles, context⚠️ Limited
PromptsBothInstructions, 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.