MCP

Intent Surfaces

A reference manual for people who design and build MCP (Model Context Protocol) ecosystems

MCP

Intent Surfaces

A reference manual for people who design and build MCP (Model Context Protocol) ecosystems

MCP

Intent Surfaces

A reference manual for people who design and build MCP (Model Context Protocol) ecosystems

Resources: The Library of Context

Resources: The Library of Context

Resources: The Library of Context

Teaching AI to read your world • The difference between showing and doing • Building context that scales

Teaching AI to read your world • The difference between showing and doing • Building context that scales

Teaching AI to read your world • The difference between showing and doing • Building context that scales

The Read-Only Revolution

The Read-Only Revolution

In 2023, a fascinating pattern emerged in the early MCP community. Developers kept trying to make Tools do everything—read files, write files, delete files, all in one. Then someone asked a simple question on the community Discord:

In 2023, a fascinating pattern emerged in the early MCP community. Developers kept trying to make Tools do everything—read files, write files, delete files, all in one. Then someone asked a simple question on the community Discord:

"What if reading and writing were fundamentally different operations?"

"What if reading and writing were fundamentally different operations?"

This sparked the insight that became Resources: AI needs to observe far more than it needs to act.

This sparked the insight that became Resources: AI needs to observe far more than it needs to act.

Think about your own workflow. For every file you modify, how many do you read? For every database record you update, how many do you query? The ratio is often 100:1 or higher. Resources embrace this reality.

Think about your own workflow. For every file you modify, how many do you read? For every database record you update, how many do you query? The ratio is often 100:1 or higher. Resources embrace this reality.

Revelation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// The revelation: Separate reading from writing

// ❌ Old approach: Everything is a tool
{
  name: 'file_operation',
  inputSchema: {
    operation: { enum: ['read', 'write', 'delete'] },
    path: { type: 'string' },
    content: { type: 'string' }  // Only used for write
  }
}

// ✅ New approach: Resources for reading, Tools for writing
// Resource:
{
  uri: 'file:///project/src',
  name: 'Source Code',
  mimeType: 'text/directory'
}

// Tool:
{
  name: 'write_file',
  inputSchema: {
    path: { type: 'string' },
    content: { type: 'string' }
  }
}

This separation unlocked something powerful: Resources can be cached, prefetched, indexed, and optimized in ways that Tools cannot.

This separation unlocked something powerful: Resources can be cached, prefetched, indexed, and optimized in ways that Tools cannot.

Cache

Permissions

Cache

Permissions

8.1 Resource vs Tool Visualizer

8.1 Resource vs Tool Visualizer

The URI: Your Resource's Address

The URI: Your Resource's Address

The MCP community learned from decades of web architecture: URIs (Uniform Resource Identifiers) are incredibly powerful. Here's how successful MCP resources use them:

The MCP community learned from decades of web architecture: URIs (Uniform Resource Identifiers) are incredibly powerful. Here's how successful MCP resources use them:

URI Pattern

URI Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Community-evolved URI patterns

// 1. Hierarchical Resources (filesystem-like)
'file:///home/user/documents/report.md'
'file:///home/user/documents/'  // Directory listing

// 2. Database Resources  
'postgres://database/users/123'  // Specific record
'postgres://database/users?status=active'  // Query

// 3. API Resources
'github://repos/owner/name/issues/42'
'github://repos/owner/name/issues?state=open'

// 4. Virtual Resources (computed on-demand)
'system://memory'  // Current memory usage
'system://cpu'     // CPU statistics
'analytics://dashboard/weekly-summary'

// 5. Composite Resources
'project://my-app/overview'  // Combines multiple data sources

// Implementation showing URI best practices
class ResourceProvider {
  async resolveUri(uri) {
    const parsed = new URL(uri);
    
    switch (parsed.protocol) {
      case 'file:':
        return this.handleFileResource(parsed.pathname, parsed.search);
        
      case 'postgres:':
        return this.handleDatabaseResource(
          parsed.hostname,  // database name
          parsed.pathname,  // table/record path
          parsed.search     // query parameters
        );
        
      case 'analytics:':
        return this.handleAnalyticsResource(parsed.pathname);
        
      default:
        throw new Error(`Unsupported protocol: ${parsed.protocol}`);
    }
  }
  
  async handleFileResource(path, queryString) {
    const params = new URLSearchParams(queryString);
    
    // Support filtering via query params
    if (params.has('extension')) {
      return this.listFilesByExtension(path, params.get('extension'));
    }
    
    if (params.has('modified')) {
      return this.listRecentlyModified(path, params.get('modified'));
    }
    
    // Default: return file content or directory listing
    const stats = await fs.stat(path);
    
    if (stats.isDirectory()) {
      const entries = await fs.readdir(path, { withFileTypes: true });
      return {
        mimeType: 'application/json',
        data: entries.map(entry => ({
          name: entry.name,
          type: entry.isDirectory() ? 'directory' : 'file',
          uri: `file://${path}/${entry.name}`
        }))
      };
    } else {
      const content = await fs.readFile(path, 'utf-8');
      return {
        mimeType: mime.lookup(path) || 'text/plain',
        data: content
      };
    }
  }
}

Resources That Tell Stories

Resources That Tell Stories

The best resources don't just provide data—they provide context and narrative. Here's a real example evolved from community feedback:

The best resources don't just provide data—they provide context and narrative. Here's a real example evolved from community feedback:

Code Context Server

Code Context Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
// code-context-server.js - A resource that understands your code

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import * as ts from 'typescript';
import * as babel from '@babel/parser';
import path from 'path';
import fs from 'fs/promises';

class CodeContextProvider {
  constructor(projectRoot) {
    this.projectRoot = projectRoot;
    this.cache = new Map();
  }
  
  // Provide rich context about a code file
  async getFileContext(filePath) {
    const content = await fs.readFile(filePath, 'utf-8');
    const ext = path.extname(filePath);
    
    let context = {
      uri: `file://${filePath}`,
      content: content,
      metadata: {
        lines: content.split('\n').length,
        size: content.length,
        lastModified: (await fs.stat(filePath)).mtime
      }
    };
    
    // Language-specific analysis
    if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
      context.analysis = await this.analyzeJavaScript(content, filePath);
    } else if (['.py'].includes(ext)) {
      context.analysis = await this.analyzePython(content);
    }
    
    // Find related files
    context.related = await this.findRelatedFiles(filePath);
    
    // Extract TODOs and FIXMEs
    context.annotations = this.extractAnnotations(content);
    
    return context;
  }
  
  async analyzeJavaScript(content, filePath) {
    const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
    
    try {
      const ast = babel.parse(content, {
        sourceType: 'module',
        plugins: [
          'jsx',
          isTypeScript && 'typescript',
          'classProperties',
          'asyncGenerators',
          'objectRestSpread'
        ].filter(Boolean)
      });
      
      const analysis = {
        imports: [],
        exports: [],
        functions: [],
        classes: [],
        complexity: 0
      };
      
      // Traverse AST to extract structure
      babel.traverse(ast, {
        ImportDeclaration(path) {
          analysis.imports.push({
            source: path.node.source.value,
            specifiers: path.node.specifiers.map(spec => spec.local.name)
          });
        },
        
        ExportNamedDeclaration(path) {
          if (path.node.declaration) {
            analysis.exports.push({
              type: 'named',
              name: path.node.declaration.id?.name || 'anonymous'
            });
          }
        },
        
        FunctionDeclaration(path) {
          analysis.functions.push({
            name: path.node.id?.name || 'anonymous',
            params: path.node.params.length,
            async: path.node.async,
            lines: path.node.loc.end.line - path.node.loc.start.line
          });
          analysis.complexity += this.calculateComplexity(path);
        },
        
        ClassDeclaration(path) {
          analysis.classes.push({
            name: path.node.id?.name || 'anonymous',
            methods: path.node.body.body
              .filter(member => member.type === 'ClassMethod')
              .map(method => method.key.name)
          });
        }
      });
      
      return analysis;
    } catch (error) {
      console.error('Failed to parse JavaScript:', error);
      return { parseError: error.message };
    }
  }
  
  async findRelatedFiles(filePath) {
    const related = [];
    const fileName = path.basename(filePath, path.extname(filePath));
    const dirPath = path.dirname(filePath);
    
    // Look for test files
    const testPatterns = [
      `${fileName}.test.*`,
      `${fileName}.spec.*`,
      `__tests__/${fileName}.*`
    ];
    
    // Look for related files (same name, different extension)
    const files = await fs.readdir(dirPath);
    
    for (const file of files) {
      const baseName = path.basename(file, path.extname(file));
      
      // Test files
      if (file.includes('.test.') || file.includes('.spec.')) {
        if (baseName.includes(fileName)) {
          related.push({
            type: 'test',
            uri: `file://${path.join(dirPath, file)}`
          });
        }
      }
      
      // Style files
      if (baseName === fileName && ['.css', '.scss', '.module.css'].includes(path.extname(file))) {
        related.push({
          type: 'style',
          uri: `file://${path.join(dirPath, file)}`
        });
      }
      
      // Component files
      if (baseName === fileName && file !== path.basename(filePath)) {
        related.push({
          type: 'related',
          uri: `file://${path.join(dirPath, file)}`
        });
      }
    }
    
    return related;
  }
  
  extractAnnotations(content) {
    const annotations = [];
    const lines = content.split('\n');
    
    const patterns = [
      { pattern: /\/\/\s*(TODO|FIXME|HACK|XXX|NOTE):?\s*(.+)/, type: 'comment' },
      { pattern: /#\s*(TODO|FIXME|HACK|XXX|NOTE):?\s*(.+)/, type: 'comment' },
      { pattern: /@(todo|deprecated|throws|param|returns)/, type: 'jsdoc' }
    ];
    
    lines.forEach((line, index) => {
      for (const { pattern, type } of patterns) {
        const match = line.match(pattern);
        if (match) {
          annotations.push({
            line: index + 1,
            type: type,
            tag: match[1],
            text: match[2] || line.trim(),
            severity: match[1] === 'FIXME' ? 'high' : 'normal'
          });
        }
      }
    });
    
    return annotations;
  }
  
  calculateComplexity(path) {
    let complexity = 1;
    
    path.traverse({
      IfStatement() { complexity++; },
      ForStatement() { complexity++; },
      WhileStatement() { complexity++; },
      DoWhileStatement() { complexity++; },
      SwitchCase() { complexity++; },
      CatchClause() { complexity++; },
      ConditionalExpression() { complexity++; },
      LogicalExpression({ node }) {
        if (node.operator === '&&' || node.operator === '||') {
          complexity++;
        }
      }
    });
    
    return complexity;
  }
}

// MCP Server implementation
const server = new Server(
  {
    name: 'code-context',
    version: '1.0.0',
  },
  {
    capabilities: {
      resources: {}
    }
  }
);

const projectRoot = process.argv[2] || process.cwd();
const provider = new CodeContextProvider(projectRoot);

// List available resources
server.setRequestHandler('resources/list', async () => {
  // Scan for code files
  const codeFiles = await scanForCodeFiles(projectRoot);
  
  return {
    resources: [
      {
        uri: `code:///${projectRoot}`,
        name: 'Project Overview',
        mimeType: 'application/json',
        description: 'Overview of the entire project structure'
      },
      ...codeFiles.slice(0, 100).map(file => ({
        uri: `code://file${file}`,
        name: path.relative(projectRoot, file),
        mimeType: 'application/json',
        description: 'Rich context about this code file'
      })),
      {
        uri: `code:///search`,
        name: 'Code Search',
        mimeType: 'application/json',
        description: 'Search across all code files'
      }
    ]
  };
});

// Read specific resources
server.setRequestHandler('resources/read', async (request) => {
  const { uri } = request.params;
  const parsed = new URL(uri);
  
  if (parsed.pathname === '/' && parsed.hostname === '') {
    // Project overview
    const overview = await generateProjectOverview(projectRoot);
    
    return {
      contents: [{
        uri: uri,
        mimeType: 'application/json',
        text: JSON.stringify(overview, null, 2)
      }]
    };
  }
  
  if (parsed.pathname.startsWith('/file')) {
    // Specific file context
    const filePath = parsed.pathname.substring(5); // Remove '/file' prefix
    const context = await provider.getFileContext(filePath);
    
    return {
      contents: [{
        uri: uri,
        mimeType: 'application/json', 
        text: JSON.stringify(context, null, 2)
      }]
    };
  }
  
  if (parsed.pathname === '/search') {
    // Search functionality
    const query = parsed.searchParams.get('q');
    const type = parsed.searchParams.get('type') || 'all';
    
    const results = await searchCode(projectRoot, query, type);
    
    return {
      contents: [{
        uri: uri,
        mimeType: 'application/json',
        text: JSON.stringify(results, null, 2)
      }]
    };
  }
  
  throw new Error(`Unknown resource: ${uri}`);
});

// Helper functions
async function scanForCodeFiles(dir, files = []) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  
  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    
    if (entry.isDirectory()) {
      // Skip common directories
      if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
        await scanForCodeFiles(fullPath, files);
      }
    } else if (entry.isFile()) {
      // Include code files
      if (/\.(js|jsx|ts|tsx|py|java|cpp|c|h|go|rs|rb|php)$/.test(entry.name)) {
        files.push(fullPath);
      }
    }
  }
  
  return files;
}

async function generateProjectOverview(projectRoot) {
  const files = await scanForCodeFiles(projectRoot);
  
  const overview = {
    root: projectRoot,
    statistics: {
      totalFiles: files.length,
      byLanguage: {},
      totalLines: 0
    },
    structure: {},
    dependencies: await analyzeDependencies(projectRoot),
    recentActivity: await getRecentActivity(projectRoot)
  };
  
  // Analyze files
  for (const file of files) {
    const ext = path.extname(file);
    const content = await fs.readFile(file, 'utf-8');
    const lines = content.split('\n').length;
    
    overview.statistics.totalLines += lines;
    overview.statistics.byLanguage[ext] = (overview.statistics.byLanguage[ext] || 0) + 1;
  }
  
  return overview;
}

const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`Code Context Server running for: ${projectRoot}`);

This tool demonstrates several key principles:

This tool demonstrates several key principles:

Rich Context

Don't just return file contents—provide analysis

Relationships

Show how files relate to each other

Search Capability

Resources can accept query parameters

Performance

Cache expensive computations

Progressive Disclosure

Overview → File → Specific analysis

File relation

Code Structure

ANNOTATIONS

File relation

Code Structure

ANNOTATIONS

File relation

Code Structure

ANNOTATIONS

8.2 Tool Design Challenge

8.2 Tool Design Challenge

The Subscription Pattern

The Subscription Pattern

One of the most powerful MCP features discovered by the community is resource subscriptions—the ability for Claude to watch resources for changes:

One of the most powerful MCP features discovered by the community is resource subscriptions—the ability for Claude to watch resources for changes:

MCP Feature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
One of the most powerful MCP features discovered by the community is resource subscriptions—the ability for Claude to watch resources for changes:// Resource subscriptions: Real-time awareness

class SubscribableResourceProvider {
  constructor() {
    this.subscribers = new Map();
    this.watchers = new Map();
  }
  
  // Allow clients to subscribe to resources
  async subscribe(uri, subscriber) {
    if (!this.subscribers.has(uri)) {
      this.subscribers.set(uri, new Set());
      
      // Start watching this resource
      this.startWatching(uri);
    }
    
    this.subscribers.get(uri).add(subscriber);
    
    // Return current value immediately
    return await this.readResource(uri);
  }
  
  async unsubscribe(uri, subscriber) {
    const subs = this.subscribers.get(uri);
    if (subs) {
      subs.delete(subscriber);
      
      // Stop watching if no more subscribers
      if (subs.size === 0) {
        this.stopWatching(uri);
        this.subscribers.delete(uri);
      }
    }
  }
  
  // Example: Watch file system resources
  startWatching(uri) {
    if (uri.startsWith('file://')) {
      const filePath = uri.substring(7);
      
      const watcher = fs.watch(filePath, async (eventType, filename) => {
        // Resource changed, notify subscribers
        const newContent = await this.readResource(uri);
        
        for (const subscriber of this.subscribers.get(uri) || []) {
          subscriber.notify({
            uri: uri,
            changeType: eventType,
            contents: newContent
          });
        }
      });
      
      this.watchers.set(uri, watcher);
    }
  }
  
  stopWatching(uri) {
    const watcher = this.watchers.get(uri);
    if (watcher) {
      watcher.close();
      this.watchers.delete(uri);
    }
  }
}

// Real-world example: Log file monitoring
class LogMonitorResource {
  async provideResource(uri) {
    if (uri === 'logs://application/current') {
      const logPath = '/var/log/app.log';
      
      // Return recent logs
      const content = await this.getTailContent(logPath, 1000);
      
      return {
        uri: uri,
        mimeType: 'text/plain',
        content: content,
        subscription: {
          supported: true,
          events: ['append', 'rotate']
        }
      };
    }
  }
  
  async getTailContent(filePath, bytes) {
    const stats = await fs.stat(filePath);
    const start = Math.max(0, stats.size - bytes);
    
    const stream = fs.createReadStream(filePath, {
      start: start
    });
    
    let content = '';
    for await (const chunk of stream) {
      content += chunk;
    }
    
    return content;
  }
  
  // Watch for new log entries
  watchLog(filePath, callback) {
    let size = fs.statSync(filePath).size;
    
    return fs.watch(filePath, async (eventType) => {
      if (eventType === 'change') {
        const newSize = fs.statSync(filePath).size;
        
        if (newSize > size) {
          // Read new content
          const stream = fs.createReadStream(filePath, {
            start: size,
            end: newSize
          });
          
          let newContent = '';
          for await (const chunk of stream) {
            newContent += chunk;
          }
          
          callback({
            type: 'append',
            content: newContent
          });
          
          size = newSize;
        } else if (newSize < size) {
          // Log rotated
          callback({
            type: 'rotate',
            content: await this.getTailContent(filePath, 1000)
          });
          
          size = newSize;
        }
      }
    });
  }
}

Database

Database

api docs

api docs

fodlers

fodlers

8.3 Subscription Simulatort

8.3 Subscription Simulatort

Efficient Resource Design

Efficient Resource Design

The difference between a good tool and a great tool is how it fails:

The difference between a good tool and a great tool is how it fails:

Lazy Resource Provider

Lazy Resource Provider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Pattern 1: Lazy Loading
class LazyResourceProvider {
  async listResources() {
    // Don't compute everything upfront
    return [
      {
        uri: 'expensive://computation',
        name: 'Complex Analysis',
        mimeType: 'application/json',
        // Just describe what's available
        metadata: {
          estimatedTime: '2-5 seconds',
          requiresComputation: true
        }
      }
    ];
  }
  
  async readResource(uri) {
    // Only compute when actually requested
    if (uri === 'expensive://computation') {
      return await this.performExpensiveComputation();
    }
  }
}

// Pattern 2: Progressive Detail
class ProgressiveResourceProvider {
  async readResource(uri) {
    const url = new URL(uri);
    const detail = url.searchParams.get('detail') || 'summary';
    
    switch (detail) {
      case 'summary':
        // Fast, lightweight response
        return {
          mimeType: 'application/json',
          data: await this.getSummary()
        };
        
      case 'detailed':
        // More computation, more data
        return {
          mimeType: 'application/json',
          data: await this.getDetailedAnalysis()
        };
        
      case 'full':
        // Everything, might be slow
        return {
          mimeType: 'application/json',
          data: await this.getFullAnalysis()
        };
    }
  }
}

// Pattern 3: Smart Caching
class CachedResourceProvider {
  constructor() {
    this.cache = new Map();
    this.cacheMetadata = new Map();
  }
  
  async readResource(uri) {
    // Check cache validity
    const cached = this.cache.get(uri);
    const metadata = this.cacheMetadata.get(uri);
    
    if (cached && metadata) {
      // Different cache strategies for different resources
      const cacheStrategy = this.getCacheStrategy(uri);
      
      switch (cacheStrategy.type) {
        case 'time-based':
          if (Date.now() - metadata.timestamp < cacheStrategy.ttl) {
            return cached;
          }
          break;
          
        case 'content-based':
          const currentHash = await this.computeHash(uri);
          if (currentHash === metadata.hash) {
            return cached;
          }
          break;
          
        case 'hybrid':
          // Use time-based for fast check, content-based for accuracy
          if (Date.now() - metadata.timestamp < cacheStrategy.softTtl) {
            return cached;
          }
          
          const currentHash = await this.computeHash(uri);
          if (currentHash === metadata.hash) {
            // Content unchanged, refresh timestamp
            metadata.timestamp = Date.now();
            return cached;
          }
          break;
      }
    }
    
    // Cache miss or invalid, compute fresh
    const fresh = await this.computeResource(uri);
    
    this.cache.set(uri, fresh);
    this.cacheMetadata.set(uri, {
      timestamp: Date.now(),
      hash: await this.computeHash(uri),
      accessCount: 0
    });
    
    // Implement cache eviction
    this.evictIfNeeded();
    
    return fresh;
  }
  
  evictIfNeeded() {
    const maxSize = 100; // Max cache entries
    
    if (this.cache.size > maxSize) {
      // LRU eviction based on access patterns
      const entries = Array.from(this.cacheMetadata.entries())
        .sort((a, b) => {
          const scoreA = a[1].accessCount / (Date.now() - a[1].timestamp);
          const scoreB = b[1].accessCount / (Date.now() - b[1].timestamp);
          return scoreA - scoreB;
        });
      
      // Evict lowest scoring entries
      const toEvict = entries.slice(0, this.cache.size - maxSize);
      
      for (const [uri] of toEvict) {
        this.cache.delete(uri);
        this.cacheMetadata.delete(uri);
      }
    }
  }
}

// Pattern 4: Batch Loading
class BatchResourceProvider {
  constructor() {
    this.batchQueue = [];
    this.batchTimer = null;
  }
  
  async readResource(uri) {
    return new Promise((resolve, reject) => {
      this.batchQueue.push({ uri, resolve, reject });
      
      if (!this.batchTimer) {
        this.batchTimer = setTimeout(() => this.processBatch(), 10);
      }
    });
  }
  
  async processBatch() {
    const batch = this.batchQueue.splice(0);
    this.batchTimer = null;
    
    // Group by resource type for efficient loading
    const grouped = batch.reduce((acc, item) => {
      const type = new URL(item.uri).protocol;
      acc[type] = acc[type] || [];
      acc[type].push(item);
      return acc;
    }, {});
    
    // Process each group in parallel
    await Promise.all(
      Object.entries(grouped).map(async ([type, items]) => {
        const uris = items.map(item => item.uri);
        const results = await this.batchLoad(type, uris);
        
        items.forEach((item, index) => {
          item.resolve(results[index]);
        });
      })
    );
  }
  
  async batchLoad(type, uris) {
    switch (type) {
      case 'database:':
        // Single query for multiple records
        const ids = uris.map(uri => new URL(uri).pathname);
        return await this.db.query(
          'SELECT * FROM records WHERE id IN (?)',
          [ids]
        );
        
      case 'api:':
        // Batch API request
        return await this.api.batchGet(uris);
        
      default:
        // Fallback to individual loads
        return await Promise.all(uris.map(uri => this.loadSingle(uri)));
    }
  }
}

resource

control hub

resource

control hub

resource

control hub

8.4 Performance Profiler

8.4 Performance Profiler

Resources That Scale

Resources That Scale

As MCP adoption grew, the community discovered patterns for building resources that scale from personal use to enterprise deployments:

As MCP adoption grew, the community discovered patterns for building resources that scale from personal use to enterprise deployments:

Enterpirse Ready

Enterpirse Ready

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Enterprise-ready resource provider

class ScalableResourceProvider {
  constructor(config) {
    this.config = config;
    
    // Connection pooling for databases
    this.dbPool = createPool({
      max: config.maxConnections || 20,
      idleTimeoutMillis: 30000
    });
    
    // Distributed cache (Redis)
    this.cache = new Redis({
      cluster: config.cacheCluster,
      retry_strategy: (options) => Math.min(options.attempt * 100, 3000)
    });
    
    // Rate limiting per client
    this.rateLimiter = new RateLimiter({
      points: config.rateLimit || 100,
      duration: 60 // per minute
    });
    
    // Metrics collection
    this.metrics = new MetricsCollector(config.metrics);
  }
  
  async readResource(uri, context) {
    const startTime = Date.now();
    
    try {
      // Check rate limits
      await this.rateLimiter.consume(context.clientId);
      
      // Try distributed cache first
      const cached = await this.cache.get(uri);
      if (cached) {
        this.metrics.increment('cache.hits');
        return JSON.parse(cached);
      }
      
      this.metrics.increment('cache.misses');
      
      // Route to appropriate handler
      const result = await this.routeRequest(uri, context);
      
      // Cache successful results
      if (result) {
        await this.cache.set(
          uri,
          JSON.stringify(result),
          'EX',
          this.getCacheTTL(uri)
        );
      }
      
      return result;
      
    } catch (error) {
      this.metrics.increment('errors', { error: error.code });
      throw error;
      
    } finally {
      this.metrics.histogram('request.duration', Date.now() - startTime);
    }
  }
  
  async routeRequest(uri, context) {
    const url = new URL(uri);
    
    // Apply access control
    if (!await this.checkAccess(url, context)) {
      throw new Error('Access denied');
    }
    
    // Route based on protocol
    switch (url.protocol) {
      case 'db:':
        return await this.handleDatabaseResource(url, context);
        
      case 'api:':
        return await this.handleAPIResource(url, context);
        
      case 'compute:':
        return await this.handleComputeResource(url, context);
        
      default:
        throw new Error(`Unknown protocol: ${url.protocol}`);
    }
  }
  
  async handleDatabaseResource(url, context) {
    const [database, table, id] = url.pathname.split('/').filter(Boolean);
    
    // Use connection from pool
    const client = await this.dbPool.connect();
    
    try {
      // Apply row-level security
      const query = `
        SELECT * FROM ${table}
        WHERE id = $1
          AND organization_id = $2
          AND ($3 = ANY(allowed_roles) OR allowed_roles IS NULL)
      `;
      
      const result = await client.query(query, [
        id,
        context.organizationId,
        context.userRole
      ]);
      
      return {
        mimeType: 'application/json',
        data: result.rows[0] || null
      };
      
    } finally {
      client.release();
    }
  }
  
  async handleComputeResource(url, context) {
    const computation = url.pathname.substring(1);
    
    // Check if computation is already running
    const existingJob = await this.cache.get(`job:${computation}:${context.organizationId}`);
    
    if (existingJob) {
      const job = JSON.parse(existingJob);
      
      if (job.status === 'completed') {
        return job.result;
      } else {
        return {
          mimeType: 'application/json',
          data: {
            status: 'processing',
            progress: job.progress,
            estimatedCompletion: job.estimatedCompletion
          }
        };
      }
    }
    
    // Start new computation job
    const jobId = await this.startComputeJob(computation, context);
    
    return {
      mimeType: 'application/json',
      data: {
        status: 'started',
        jobId: jobId,
        pollUri: `compute://${computation}?jobId=${jobId}`
      }
    };
  }
}

// Community pattern: Resource middleware
class ResourceMiddleware {
  constructor() {
    this.middlewares = [];
  }
  
  use(middleware) {
    this.middlewares.push(middleware);
  }
  
  async process(uri, context, next) {
    let index = 0;
    
    const dispatch = async () => {
      if (index >= this.middlewares.length) {
        return await next();
      }
      
      const middleware = this.middlewares[index++];
      return await middleware(uri, context, dispatch);
    };
    
    return await dispatch();
  }
}

// Example middleware: Audit logging
async function auditMiddleware(uri, context, next) {
  const startTime = Date.now();
  const result = await next();
  
  await auditLog.record({
    timestamp: new Date(),
    userId: context.userId,
    organizationId: context.organizationId,
    resource: uri,
    duration: Date.now() - startTime,
    success: true
  });
  
  return result;
}

// Example middleware: Transform responses
async function transformMiddleware(uri, context, next) {
  const result = await next();
  
  // Add metadata to all responses
  return {
    ...result,
    metadata: {
      timestamp: new Date().toISOString(),
      version: '1.0',
      cacheable: true
    }
  };
}

resource

load

control hub

resource

load

control hub

resource

load

control hub

8.4 Scale Simulator

8.4 Scale Simulator

The Semantic Resource

The Semantic Resource

The community's latest innovation is semantic resources—resources that understand meaning, not just data:

The community's latest innovation is semantic resources—resources that understand meaning, not just data:

Testing Pattern MCP

Testing Pattern MCP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Testing patterns for MCP tools
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestClient } from '@modelcontextprotocol/sdk/testing';

describe('GitAnalytics Tool', () => {
  let client;
  let testRepoPath;
  
  beforeEach(async () => {
    // Create test repository
    testRepoPath = await createTestRepo();
    
    // Create test client
    client = new TestClient({
      command: 'node',
      args: ['./git-analytics-server.js', testRepoPath]
    });
    
    await client.start();
  });
  
  afterEach(async () => {
    await client.stop();
    await cleanupTestRepo(testRepoPath);
  });
  
  describe('analyze_commit_patterns', () => {
    it('should analyze commit patterns correctly', async () => {
      // Create test commits
      await createTestCommits(testRepoPath, [
        { message: 'Initial commit', date: '2024-03-01T09:00:00Z' },
        { message: 'Add feature', date: '2024-03-01T14:00:00Z' },
        { message: 'Fix bug', date: '2024-03-02T10:00:00Z' }
      ]);
      
      // Call tool
      const result = await client.callTool('analyze_commit_patterns', {
        days: 30
      });
      
      // Verify results
      expect(result.content[0].text).toContain('Total commits: 3');
      expect(result.content[0].text).toContain('9:00 - 1 commits');
      expect(result.content[0].text).toContain('14:00 - 1 commits');
    });
    
    it('should handle empty repository gracefully', async () => {
      const result = await client.callTool('analyze_commit_patterns', {
        days: 30
      });
      
      expect(result.content[0].text).toContain('Total commits: 0');
    });
  });
  
  describe('error handling', () => {
    it('should provide helpful error for non-git directory', async () => {
      // Point to non-git directory
      const nonGitClient = new TestClient({
        command: 'node',
        args: ['./git-analytics-server.js', '/tmp']
      });
      
      await nonGitClient.start();
      
      const result = await nonGitClient.callTool('analyze_commit_patterns');
      
      expect(result.content[0].text).toContain('Not a git repository');
      
      await nonGitClient.stop();
    });
  });
});

// Integration test example
describe('Multi-tool Integration', () => {
  it('should work with bookmark manager', async () => {
    // Start both servers
    const gitClient = await TestClient.start('./git-analytics-server.js');
    const bookmarkClient = await TestClient.start('./bookmark-manager.js');
    
    // Simulate workflow
    const gitInsights = await gitClient.callTool('get_productivity_insights');
    
    // Extract recommendation from insights
    const recommendation = extractRecommendation(gitInsights);
    
    // Save as bookmark
    const bookmark = await bookmarkClient.callTool('add_bookmark', {
      url: 'https://example.com/productivity-tips',
      title: 'Productivity Tips',
      tags: ['productivity', 'automated']
    });
    
    expect(bookmark.content[0].text).toContain('Added bookmark');
  });
});

node cluster

node cluster

highlighted node

highlighted node

8.5 Knowledge Explorer

8.5 Knowledge Explorer

The Tool Maker's Mindset

The Tool Maker's Mindset

Let's be honest—things don't always work the first time. Here's how to debug MCP connections:

Let's be honest—things don't always work the first time. Here's how to debug MCP connections:

Do’s

Design for caching

Resources should be cacheable by default

Use standard MIME types

Help Claude understand your data format

Provide metadata

Include size, update time, and other helpful context

Support query parameters

Make resources filterable and searchable

Version your schemas

Resources evolve, plan for it

❌ DON'Ts

Mix reading and writing

That's what Tools are for

Compute everything eagerly

Lazy loading improves performance

Ignore errors

Graceful degradation is better than crashes

Forget about scale

Personal tools often become team tools

Skimp on documentation

Good descriptions help Claude help users

File Resource

file:///home/user/documents/report.md

System Resource

system://cpu

API Resource

github://repos/owner/name/issues?state=open

Composite Resource

project://my-app/overview

File Resource

file:///home/user/documents/report.md

System Resource

system://cpu

API Resource

github://repos/owner/name/issues?state=open

Composite Resource

project://my-app/overview

File Resource

file:///home/user/documents/report.md

System Resource

system://cpu

API Resource

github://repos/owner/name/issues?state=open

Composite Resource

project://my-app/overview

8.6 Tool Design Challenge

8.6 Tool Design Challenge

Building Resources That Matter

Building Resources That Matter

The best resources solve real problems. Here's a final example that brings together everything we've learned:

The best resources solve real problems. Here's a final example that brings together everything we've learned:

Learning Tracker

Learning Tracker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Personal Learning Tracker - A resource that helps Claude help you learn

class LearningTrackerResource {
  constructor() {
    this.db = new Database('./learning-tracker.db');
    this.initSchema();
  }
  
  async provideResources() {
    return [
      {
        uri: 'learning://progress/overview',
        name: 'Learning Progress Overview',
        mimeType: 'application/json',
        description: 'Your overall learning progress across all topics'
      },
      {
        uri: 'learning://topics/{topic}',
        name: 'Topic Progress',
        mimeType: 'application/json', 
        description: 'Detailed progress for a specific topic'
      },
      {
        uri: 'learning://recommendations',
        name: 'Learning Recommendations',
        mimeType: 'application/json',
        description: 'Personalized recommendations based on your progress'
      },
      {
        uri: 'learning://insights/weekly',
        name: 'Weekly Learning Insights',
        mimeType: 'application/json',
        description: 'Analysis of your learning patterns'
      }
    ];
  }
  
  async readResource(uri, context) {
    const url = new URL(uri);
    
    switch (url.pathname) {
      case '/progress/overview':
        return await this.getProgressOverview(context.userId);
        
      case '/recommendations':
        return await this.getRecommendations(context.userId);
        
      case '/insights/weekly':
        return await this.getWeeklyInsights(context.userId);
        
      default:
        if (url.pathname.startsWith('/topics/')) {
          const topic = url.pathname.substring(8);
          return await this.getTopicProgress(context.userId, topic);
        }
    }
  }
  
  async getProgressOverview(userId) {
    const topics = await this.db.query(`
      SELECT 
        topic,
        COUNT(DISTINCT concept) as concepts_learned,
        AVG(confidence) as avg_confidence,
        MAX(last_reviewed) as last_activity,
        SUM(practice_time) as total_time
      FROM learning_progress
      WHERE user_id = ?
      GROUP BY topic
      ORDER BY last_activity DESC
    `, [userId]);
    
    const streaks = await this.calculateStreaks(userId);
    const momentum = await this.calculateMomentum(userId);
    
    return {
      mimeType: 'application/json',
      data: {
        summary: {
          totalTopics: topics.length,
          totalConcepts: topics.reduce((sum, t) => sum + t.concepts_learned, 0),
          currentStreak: streaks.current,
          longestStreak: streaks.longest,
          momentum: momentum
        },
        topics: topics.map(t => ({
          ...t,
          masteryLevel: this.calculateMastery(t.avg_confidence, t.concepts_learned),
          nextReview: this.calculateNextReview(t.last_activity, t.avg_confidence)
        }))
      }
    };
  }
  
  async getRecommendations(userId) {
    // Analyze learning patterns
    const patterns = await this.analyzeLearningPatterns(userId);
    
    // Get current progress
    const progress = await this.getProgressOverview(userId);
    
    // Generate personalized recommendations
    const recommendations = [];
    
    // 1. Spaced repetition recommendations
    const dueForReview = await this.getDueForReview(userId);
    if (dueForReview.length > 0) {
      recommendations.push({
        type: 'review',
        priority: 'high',
        title: 'Time for Review',
        description: `${dueForReview.length} concepts are due for review to maintain retention`,
        concepts: dueForReview.slice(0, 5)
      });
    }
    
    // 2. Weak area strengthening
    const weakAreas = progress.data.topics
      .filter(t => t.avg_confidence < 0.6)
      .slice(0, 3);
      
    if (weakAreas.length > 0) {
      recommendations.push({
        type: 'strengthen',
        priority: 'medium',
        title: 'Strengthen Foundations',
        description: 'These topics need more practice',
        topics: weakAreas
      });
    }
    
    // 3. Learning path suggestions
    const nextTopics = await this.suggestNextTopics(userId, patterns);
    if (nextTopics.length > 0) {
      recommendations.push({
        type: 'explore',
        priority: 'medium',
        title: 'Ready for New Challenges',
        description: 'Based on your progress, you\'re ready for these topics',
        topics: nextTopics
      });
    }
    
    // 4. Optimal learning time
    if (patterns.bestTimeOfDay) {
      recommendations.push({
        type: 'timing',
        priority: 'low',
        title: 'Optimal Learning Time',
        description: `Your data shows you learn best around ${patterns.bestTimeOfDay}`,
        data: patterns.timeAnalysis
      });
    }
    
    return {
      mimeType: 'application/json',
      data: {
        recommendations: recommendations,
        learningVelocity: patterns.velocity,
        strengths: patterns.strengths,
        improvementAreas: patterns.improvements
      }
    };
  }
}

// This resource enables conversations like:
/*
You: How's my Python learning going?

Claude: Let me check your learning progress.

[Claude reads learning://progress/overview]
[Claude reads learning://topics/python]

Your Python journey is going well! Here's what I see:

📊 **Progress Overview**:
- Concepts learned: 47
- Average confidence: 78%
- Current streak: 12 days 🔥
- Total practice time: 18.5 hours

💪 **Strengths**:
- Functions and control flow (92% confidence)
- Data structures (85% confidence)
- File handling (81% confidence)

🎯 **Areas for Improvement**:
- Decorators (45% confidence)
- Async programming (52% confidence)
- Class inheritance (58% confidence)

[Claude reads learning://recommendations]

📚 **Recommendations**:
1. **Review Session Needed**: 5 concepts are due for review today to maintain your retention
   - List comprehensions (last reviewed 7 days ago)
   - Lambda functions (last reviewed 9 days ago)
   
2. **Strengthen Decorators**: Your confidence is lowest here. Try:
   - Building a simple @timer decorator
   - Creating a @cache decorator
   - Understanding @property

3. **Optimal Learning Time**: Your data shows you retain information best between 9-11 AM

Would you like me to create a practice session for decorators or help you review the concepts due today?
*/

Answers feel limited.

Answers feel rich + precise.

Answers feel limited.

Answers feel rich + precise.

Answers feel limited.

Answers feel rich + precise.

8.7 Resource Impact Visualizer

8.7 Resource Impact Visualizer

Your Resource Journey

Your Resource Journey

Resources are the eyes through which AI sees your world. Every resource you build expands what's possible:

Resources are the eyes through which AI sees your world. Every resource you build expands what's possible:

Today: Claude reads your files and databases

Today: Claude reads your files and databases

Tomorrow: Claude understands your entire digital context

Tomorrow: Claude understands your entire digital context

The Future: Claude becomes a true extension of your capabilities

The Future: Claude becomes a true extension of your capabilities

8.8 Resource Builder Toolkit

8.8 Resource Builder Toolkit