MCP Resources & Prompts Explained | Beyond MCP Tools | FDE Full Course #15
While Model Context Protocol (MCP) Tools allow Large Language Models to trigger active computations and mutations, real-world AI applications require structured context and reusable workflows. This chapter expands beyond MCP Tools to explore MCP Resources (application-controlled read-only context) and MCP Prompts (user-driven parameterized templates). Using a practical To-Do assistant built with Spring AI, we break down how to centralize data, documentation, and prompt engineering into a decoupled MCP architecture.
1. Why Do We Need Resources and Prompts? Beyond MCP Tools
In standard Model Context Protocol setups, developers often start exclusively with Tools. While tools provide agentic execution (such as create_task, list_tasks, and complete_task), relying solely on tools presents key challenges:
- Context Bloat & Token Inefficiency: Forcing an LLM to call a tool or stuffing static company guidelines into every prompt wastes valuable context window tokens and increases latency.
- Scattered Business Logic: Storing prompt templates and company handbooks directly inside client applications causes fragmentation across multiple microservices.
- The Need for Decoupled Data: An MCP Server should act as a single centralized knowledge and capability hub that any client (web app, CLI, IDE, or mobile agent) can query consistently.
MCP solves this by defining Three Core Primitives:
- Tools: Dynamic execution (LLM decides when and how to invoke).
- Resources: Passive read-only data and documentation (Application or User injects as context).
- Prompts: Pre-engineered reusable workflows (User selects to orchestrate LLM behavior).
2. Deep Dive into MCP Resources: Definition, Anatomy, and URI vs. URL
An MCP Resource represents any data or document exposed by an MCP Server that an AI client can discover and attach as context. Unlike tools, resources are read-only and do not execute external mutations.
Anatomy of an MCP Resource
Every resource registered on an MCP server contains five fundamental attributes:
Key Distinction: URI vs. URL
A common point of confusion in MCP is whether a resource URI functions like a standard web URL.
Note: A URI is an internal identifier recognized by the MCP Server. Passing
company/coding-guidelinesdoes not trigger an external HTTP GET request over the public internet. Instead, the MCP Client passes this URI to the MCP Server viaresources/read, and the server resolves where that document lives internally.
Core Resource Endpoints
The protocol defines two primitive JSON-RPC operations for resources:
resources/list: Discovers available resources, returning names, descriptions, URIs, and MIME types.resources/read: Retrieves the raw content of a specific resource by supplying its uniqueuri.
3. Static vs. Dynamic Resources
Resources can serve static documents or dynamically generated database snapshots:
A. Static Resources
Fixed files, markdown policies, or in-memory strings that remain constant across requests.
- Examples: Company code-review guidelines, company working hours policy, architectural guidelines.
- URI Structure:
guidelines/coding,policies/remote-work.
B. Dynamic Resources
Parameterized resources that query backing databases or third-party APIs at runtime.
- Examples: Customer profiles, order records, live task boards.
- URI Structure:
user/{id}/profile,orders/{orderId}/status.
4. Control Paradigms: LLM-Controlled vs. Application-Controlled
The fundamental architectural difference between Tools and Resources lies in who controls the invocation:
Why Resources Are Application-Controlled
Rather than allowing the LLM to autonomously guess which documents to read, the surrounding client application frequently injects the appropriate resource:
- User Interface Attachment (e.g., ChatGPT with Google Drive):
The UI queries
resources/listand presents a dropdown or picker. When the user attaches a document, the application fetches it viaresources/readand appends it to the prompt. - Active Context in IDEs (e.g., Cursor IDE):
When editing
Order.java, the IDE knows the active window context. When the user asks "Explain this function", the application automatically fetchessource/Order.javaand provides it to the model. - RAG & Vector Similarity Search: An application matches user queries against a vector store and pulls relevant chunk URIs without requiring LLM function calling.
- LLM Fallback (Hybrid Model):
If explicit UI/IDE context is absent, the application can expose resources to the model as tools (e.g., a tool named
get_guidelines(uri)), allowing the model to request resources when needed.
5. Implementing MCP Resources in Spring AI
In the lecture demonstration, Aditya Tandon implements a task-scheduling system where the server hosts scheduling guidelines and the client reads them before prompting the LLM.
Server Implementation (TaskResources.java)
The server exposes an internal policy as an MCP Resource using Spring AI annotations:
package com.coderarmy.taskserver;
import org.springframework.ai.mcp.annotation.McpResource;
import org.springframework.stereotype.Component;
@Component
public class TaskResources {
@McpResource(
name = "Task Guidelines",
description = "Guidelines for managing and prioritizing tasks",
uri = "task/guidelines",
mimeType = "text/plain"
)
public String getGuidelines() {
return """
Task Management Guidelines:
1. Deep work tasks must preferably be scheduled before 12:00 PM.
2. Meetings should preferably be scheduled after 2:00 PM.
3. Urgent and high-priority tasks must be completed on the same day.
4. Routine administrative tasks should be batched together.
5. Take regular 15-minute breaks between intense deep-work blocks.
""";
}
}
Client Implementation (TaskClientService.java)
The client uses McpSyncClient to fetch the resource content via ReadResourceRequest and attaches it into the system prompt:
package com.coderarmy.taskclient;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceRequest;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.SyncMcpClient;
import org.springframework.stereotype.Service;
@Service
public class TaskClientService {
private final ChatClient chatClient;
private final SyncMcpClient mcpClient;
public TaskClientService(ChatClient.Builder builder, SyncMcpClient mcpClient) {
this.chatClient = builder.build();
this.mcpClient = mcpClient;
}
public String askWithGuidelines(String userQuestion) {
// 1. Fetch resource from MCP Server via URI
ReadResourceRequest request = new ReadResourceRequest("task/guidelines");
ReadResourceResult result = mcpClient.readResource(request);
String guidelinesContent = ((TextResourceContents) result.contents().getFirst()).text();
// 2. Inject guidelines into the System Prompt
String systemPrompt = """
You are an expert Task Management Assistant.
Adhere strictly to the following task management guidelines:
%s
""".formatted(guidelinesContent);
// 3. Query LLM with injected context & active tools
return chatClient.prompt()
.system(systemPrompt)
.user(userQuestion)
.call()
.content();
}
}
6. Deep Dive into MCP Prompts: Reusable Parameterized Workflows
The Problem MCP Prompts Solve
Asking end users to write complex, perfectly engineered prompts leads to inconsistency, omitted edge cases, and user friction.
The MCP Solution
Store structured, production-tested prompt templates directly on the MCP server:
- Centralized Optimization: The prompt engineering team maintains high-performance system instructions on the server.
- Client Agnostic: Web dashboards, CLI tools, and chat extensions invoke identical prompt logic.
- Runtime Parameters: Prompts accept dynamic arguments (e.g.,
availableHours = 4).
Core Prompt Endpoints
prompts/list: Returns all available prompt templates, descriptions, and expected parameters.prompts/get: Returns the fully formatted prompt text, populating any parameters supplied in the request.
7. Implementing MCP Prompts in Spring AI
Server Implementation (TaskPrompts.java)
The server registers a parameterized prompt using @McpPrompt and @McpPromptParam:
package com.coderarmy.taskserver;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
import org.springframework.ai.mcp.annotation.McpPrompt;
import org.springframework.ai.mcp.annotation.McpPromptParam;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class TaskPrompts {
@McpPrompt(
name = "plan_day",
description = "Plan pending tasks based on available hours and schedule guidelines"
)
public GetPromptResult planDay(
@McpPromptParam(
name = "availableHours",
description = "Number of available hours for work today",
required = true
) String availableHours
) {
String template = """
Help me plan my pending tasks for today.
1. First, check all pending tasks using the available 'list_tasks' tool.
2. Prioritize high-focus creative or deep-work tasks first.
3. I have %s hours available today.
4. Provide a structured, realistic hourly breakdown including break buffers.
""".formatted(availableHours);
PromptMessage message = new PromptMessage(Role.USER, new TextContent(template));
return new GetPromptResult("Plan my day with pending tasks", List.of(message));
}
}
Client Execution Flow (PlanController.java & Service)
When the user clicks "Plan My Day" and inputs 4 hours, the client fetches the template from the server and submits it to the model:
// Client fetching and executing the prompt
public String executePlanWorkflow(int hours) {
// 1. Request compiled prompt from MCP Server
GetPromptRequest promptRequest = new GetPromptRequest(
"plan_day",
Map.of("availableHours", String.valueOf(hours))
);
GetPromptResult promptResult = mcpClient.getPrompt(promptRequest);
PromptMessage userMessage = promptResult.messages().getFirst();
String generatedPromptText = ((TextContent) userMessage.content()).text();
// 2. Pass generated prompt to the LLM (with tools enabled)
return chatClient.prompt()
.user(generatedPromptText)
.call()
.content();
}
8. Complete Flow Walkthrough: The Multi-Pillar To-Do Assistant
Here is the complete runtime workflow connecting Prompts, Resources, and Tools during a single user interaction:
Example Output Generated in Postman Demo:
When prompted with pending tasks ("Record FDE Lecture", "Read about Docker", "Attend College Seminar"):
- 12:00 PM – 1:30 PM: Record FDE Lecture (90 min, highest focus scheduled early).
- 1:30 PM – 1:45 PM: Buffer break.
- 1:45 PM – 3:00 PM: Read about Docker.
- 3:00 PM – 4:00 PM: Attend College Seminar.
9. Comprehensive Architectural Comparison
10. Summary and Key Takeaways
- Beyond Tool Calling: An MCP Server is more than a function-calling proxy; it serves as a complete ecosystem encompassing Tools (actions), Resources (data), and Prompts (workflows).
- Resources Provide Read-Only Context: Resources expose documents, policies, or database queries via unique URIs without running mutations or modifying server state.
- URIs Are Not Necessarily Web URLs: A resource URI is an internal identifier that the MCP server understands (such as
task/guidelines), rather than an open web address. - Application-Driven Injection: Unlike Tools (which the LLM autonomously chooses), Resources are typically application-controlled—selected based on UI attachments, open IDE file tabs, or RAG similarity matches.
- Static vs. Dynamic Data: Resources can represent static markdown documents or dynamically query databases using URI route parameters (e.g.,
user/{id}/profile). - Prompts Centralize Workflow Engineering: Storing prompt templates on the MCP server ensures consistent, optimized prompt logic across diverse client apps while sparing users from writing lengthy instructions.
- Dynamic Parameterization: Prompts accept runtime variables (such as available working hours), enabling reusable templates with flexible constraints.
- Decoupled Architecture: Hosting tools, resources, and prompts on a central MCP server allows multiple distinct AI clients to share business logic, guidelines, and toolkits without code duplication.