Skip to content

Commit d634e7c

Browse files
authored
Merge pull request #167 from QuantGeekDev/docs/list-resources-issue-64
docs: document ListResources + backward-compat tests
2 parents 8f21477 + caa47ac commit d634e7c

File tree

2 files changed

+802
-0
lines changed

2 files changed

+802
-0
lines changed

docs/resources.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# Resources
2+
3+
Resources are data sources that AI models can read or subscribe to. They serve as context providers in the MCP Framework.
4+
5+
## Resource Types
6+
7+
Resources can represent:
8+
- Files
9+
- API endpoints
10+
- Database queries
11+
- Real-time data streams
12+
- Configuration data
13+
14+
## Creating Resources
15+
16+
### Via CLI
17+
18+
```bash
19+
mcp add resource my-resource
20+
```
21+
22+
This generates a new resource file at `src/resources/MyResource.ts`.
23+
24+
### Required Components
25+
26+
Every resource needs metadata (URI, name, description, MIME type) and an async `read` method:
27+
28+
```typescript
29+
import { MCPResource } from "mcp-framework";
30+
31+
class ConfigResource extends MCPResource {
32+
uri = "resource://config";
33+
name = "Configuration";
34+
description = "System configuration settings";
35+
mimeType = "application/json";
36+
37+
async read() {
38+
return [
39+
{
40+
uri: this.uri,
41+
mimeType: this.mimeType,
42+
text: JSON.stringify({
43+
version: "1.0.0",
44+
environment: "production",
45+
features: ["analytics", "reporting"],
46+
}),
47+
},
48+
];
49+
}
50+
}
51+
52+
export default ConfigResource;
53+
```
54+
55+
## Resource Variations
56+
57+
### Static Resources
58+
59+
Serve fixed content like documentation:
60+
61+
```typescript
62+
class DocumentationResource extends MCPResource {
63+
uri = "resource://docs";
64+
name = "Documentation";
65+
mimeType = "text/markdown";
66+
67+
async read() {
68+
return [
69+
{
70+
uri: this.uri,
71+
mimeType: this.mimeType,
72+
text: "# API Documentation\n\nWelcome to our API...",
73+
},
74+
];
75+
}
76+
}
77+
```
78+
79+
### Dynamic Resources
80+
81+
Fetch data from external sources during read operations:
82+
83+
```typescript
84+
class MarketDataResource extends MCPResource {
85+
uri = "resource://market-data";
86+
name = "Market Data";
87+
mimeType = "application/json";
88+
89+
async read() {
90+
const data = await this.fetch("https://api.market.com/latest");
91+
return [
92+
{
93+
uri: this.uri,
94+
mimeType: this.mimeType,
95+
text: JSON.stringify(data),
96+
},
97+
];
98+
}
99+
}
100+
```
101+
102+
### Real-Time Resources
103+
104+
Use WebSocket subscriptions for continuous updates:
105+
106+
```typescript
107+
class StockTickerResource extends MCPResource {
108+
uri = "resource://stock-ticker";
109+
name = "Stock Ticker";
110+
mimeType = "application/json";
111+
private ws: WebSocket | null = null;
112+
113+
async subscribe() {
114+
this.ws = new WebSocket("wss://stocks.example.com");
115+
this.ws.on("message", this.handleUpdate);
116+
}
117+
118+
async unsubscribe() {
119+
if (this.ws) {
120+
this.ws.close();
121+
this.ws = null;
122+
}
123+
}
124+
125+
async read() {
126+
const latestData = await this.getLatestStockData();
127+
return [
128+
{
129+
uri: this.uri,
130+
mimeType: this.mimeType,
131+
text: JSON.stringify(latestData),
132+
},
133+
];
134+
}
135+
}
136+
```
137+
138+
## Caching
139+
140+
Implement TTL-based caching to reduce unnecessary data fetching:
141+
142+
```typescript
143+
class CachedResource extends MCPResource {
144+
private cache: any = null;
145+
private lastFetch: number = 0;
146+
private TTL = 60000; // 1 minute
147+
148+
async read() {
149+
if (this.cache && Date.now() - this.lastFetch < this.TTL) {
150+
return this.cache;
151+
}
152+
153+
const data = await this.fetchFreshData();
154+
this.cache = data;
155+
this.lastFetch = Date.now();
156+
return data;
157+
}
158+
}
159+
```
160+
161+
## Combining Resources with Tools
162+
163+
Resources can be used within tools for data access:
164+
165+
```typescript
166+
class DataResource extends MCPResource {
167+
uri = "resource://data";
168+
name = "Data Store";
169+
170+
async read() {
171+
return [
172+
{
173+
uri: this.uri,
174+
mimeType: "application/json",
175+
text: JSON.stringify(await this.getData()),
176+
},
177+
];
178+
}
179+
}
180+
181+
class DataProcessor extends MCPTool {
182+
async execute(input) {
183+
const resource = new DataResource();
184+
const [data] = await resource.read();
185+
return this.processData(JSON.parse(data.text));
186+
}
187+
}
188+
```
189+
190+
## Resource Discovery & Listing
191+
192+
The framework **automatically** handles the MCP `resources/list` protocol method. You do not need to implement any listing logic yourself.
193+
194+
### How It Works
195+
196+
1. Place your resource classes in `src/resources/` (or nested subdirectories)
197+
2. Export each class as the default export
198+
3. The framework discovers all resources at startup and registers them
199+
200+
When an MCP client calls `resources/list`, the framework returns the `resourceDefinition` of every registered resource — including `uri`, `name`, `description`, `mimeType`, and any optional fields like `title`, `icons`, `size`, or `annotations`.
201+
202+
### Example
203+
204+
Given these two resource files:
205+
206+
```
207+
src/resources/ConfigResource.ts
208+
src/resources/api/MarketDataResource.ts
209+
```
210+
211+
An MCP client calling `resources/list` will receive both resources automatically:
212+
213+
```json
214+
{
215+
"resources": [
216+
{
217+
"uri": "resource://config",
218+
"name": "Configuration",
219+
"description": "System configuration settings",
220+
"mimeType": "application/json"
221+
},
222+
{
223+
"uri": "resource://market-data",
224+
"name": "Market Data",
225+
"description": "Live market data",
226+
"mimeType": "application/json"
227+
}
228+
]
229+
}
230+
```
231+
232+
### Resource Templates
233+
234+
If your resource defines a `template` property, it will also appear in `resources/templates/list`:
235+
236+
```typescript
237+
class ItemResource extends MCPResource {
238+
uri = "resource://items/{id}";
239+
name = "Items";
240+
description = "Access items by ID";
241+
mimeType = "application/json";
242+
243+
protected template = {
244+
uriTemplate: "resource://items/{id}",
245+
description: "Retrieve a specific item by its ID",
246+
};
247+
248+
async read() {
249+
return [{ uri: this.uri, mimeType: this.mimeType, text: JSON.stringify({ id: "1" }) }];
250+
}
251+
}
252+
```
253+
254+
### Programmatic Registration
255+
256+
You can also register resources programmatically using `addResource()` before calling `start()`:
257+
258+
```typescript
259+
import { MCPServer } from "mcp-framework";
260+
261+
const server = new MCPServer({ name: "my-server", version: "1.0.0" });
262+
263+
server.addResource(ConfigResource);
264+
server.addResource(MarketDataResource);
265+
266+
await server.start();
267+
```
268+
269+
Programmatic and auto-discovered resources are merged. If both define the same URI, the programmatic registration takes precedence.
270+
271+
## Best Practices
272+
273+
- Follow URI naming conventions: `resource://domain/type/identifier`
274+
- Implement error handling with try-catch blocks
275+
- Use TTL-based caching to reduce unnecessary data fetching
276+
- Clean up subscriptions in `unsubscribe` methods
277+
278+
## Next Steps
279+
280+
- [Tools](./tools.md) - Learn about building tools
281+
- [Prompts](./prompts.md) - Learn about prompt templates

0 commit comments

Comments
 (0)