|
| 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