A reference manual for people who design and build MCP (Model Context Protocol) ecosystems
A reference manual for people who design and build MCP (Model Context Protocol) ecosystems
A reference manual for people who design and build MCP (Model Context Protocol) ecosystems
Your First Connection
Your First Connection
Your First Connection
The "Hello World" that actually does something • Building a bridge between Claude and your file system • That magical moment when it just works
The "Hello World" that actually does something • Building a bridge between Claude and your file system • That magical moment when it just works
The "Hello World" that actually does something • Building a bridge between Claude and your file system • That magical moment when it just works
The Moment Everything Clicks
The Moment Everything Clicks
Remember the first time you wrote code that actually worked? That little rush when "Hello World" appeared on your screen? With MCP, that moment is even better—because your first connection doesn't just print text. It gives AI actual capabilities. In the next 30 minutes, you'll build something real: an MCP server that lets Claude organize your downloads folder, search your notes, or analyze your code. No toy examples. No fictional scenarios. Real code that solves real problems.
Remember the first time you wrote code that actually worked? That little rush when "Hello World" appeared on your screen? With MCP, that moment is even better—because your first connection doesn't just print text. It gives AI actual capabilities. In the next 30 minutes, you'll build something real: an MCP server that lets Claude organize your downloads folder, search your notes, or analyze your code. No toy examples. No fictional scenarios. Real code that solves real problems.
The Moment
# What you'll need (all free and open source) Prerequisites: - Node.js 18 or higher (check with: node --version) - A text editor (VS Code, Sublime, anything works) - Claude Desktop or another MCP client - 30 minutes and some curiosity
Starting Simple: The Note Keeper
Starting Simple: The Note Keeper
We'll build a personal note-taking server. Why? Because everyone takes notes, and having Claude help organize them is genuinely useful.
We'll build a personal note-taking server. Why? Because everyone takes notes, and having Claude help organize them is genuinely useful.
Note-server.js
Note-server.js
// note-server.js - Your first MCP server import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import fs from 'fs/promises'; import path from 'path'; import os from 'os'; // Where we'll store notes (in your home directory) const NOTES_DIR = path.join(os.homedir(), '.mcp-notes'); // Create the notes directory if it doesn't exist await fs.mkdir(NOTES_DIR, { recursive: true }); // Create an MCP server const server = new Server( { name: 'note-keeper', version: '1.0.0', }, { capabilities: { tools: {}, // We'll add tools here }, } ); // Tool 1: Create a new note server.setRequestHandler('tools/call', async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'create_note': { const { title, content } = args; const filename = `${Date.now()}-${title.replace(/[^a-z0-9]/gi, '-')}.md`; const filepath = path.join(NOTES_DIR, filename); await fs.writeFile(filepath, `# ${title}\n\n${content}\n\nCreated: ${new Date().toISOString()}`); return { content: [{ type: 'text', text: `Created note: ${title}` }] }; } case 'list_notes': { const files = await fs.readdir(NOTES_DIR); const notes = files.filter(f => f.endsWith('.md')); return { content: [{ type: 'text', text: `Found ${notes.length} notes:\n${notes.map(n => `- ${n}`).join('\n')}` }] }; } case 'read_note': { const { filename } = args; const filepath = path.join(NOTES_DIR, filename); const content = await fs.readFile(filepath, 'utf-8'); return { content: [{ type: 'text', text: content }] }; } default: throw new Error(`Unknown tool: ${name}`); } }); // List available tools server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'create_note', description: 'Create a new note', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Title of the note' }, content: { type: 'string', description: 'Content of the note' } }, required: ['title', 'content'] } }, { name: 'list_notes', description: 'List all notes', inputSchema: { type: 'object', properties: {} } }, { name: 'read_note', description: 'Read a specific note', inputSchema: { type: 'object', properties: { filename: { type: 'string', description: 'Filename of the note to read' } }, required: ['filename'] } } ] }; }); // Start the server const transport = new StdioServerTransport(); await server.connect(transport); console.error('Note Keeper MCP Server running...');




visualization
6.1 Live Server Builder
6.1 Live Server Builder
Making It Real: Connect to Claude
Making It Real: Connect to Claude
Now for the magic moment—connecting your server to Claude Desktop:
Now for the magic moment—connecting your server to Claude Desktop:

Claude Desktop Log:
[2024-03-15 10:30:45]
Connecting to MCP servers...
[2024-03-15 10:30:45]
Connected to note-keeper
[2024-03-15 10:30:45]
Available tools: create_note, list_notes, read_note
[2024-03-15 10:30:46]
Ready!

Claude Desktop Log:
[2024-03-15 10:30:45]
Connecting to MCP servers...
[2024-03-15 10:30:45]
Connected to note-keeper
[2024-03-15 10:30:45]
Available tools: create_note, list_notes, read_note
[2024-03-15 10:30:46]
Ready!

Claude Desktop Log:
[2024-03-15 10:30:45]
Connecting to MCP servers...
[2024-03-15 10:30:45]
Connected to note-keeper
[2024-03-15 10:30:45]
Available tools: create_note, list_notes, read_note
[2024-03-15 10:30:46]
Ready!






6.2 Connection Visualizer
Your First Conversation
Your First Conversation
you
Hey Claude, can you help me keep track of some ideas?
Claude
I'd be happy to help you track ideas! I can see you have a note-keeping system
available. Would you like me to create a new note for your ideas?
you
Yes! Let's create a note about MCP project ideas.
Claude
I'll create that note for you right now.
[Claude uses the create_note tool]
Created note: MCP-project-ideas
Would you like to add some initial ideas to this note?
you
Add these:
1) Recipe organizer that reads from cooking websites
2) Email summarizer that creates daily digests
3) Code documentation generator
Claude
[Updates the note with your ideas]
Your ideas have been saved! You now have these MCP project concepts documented:
- Recipe organizer that reads from cooking websites
- Email summarizer that creates daily digests
- Code documentation generator
Would you like me to help you elaborate on any of these ideas?
you
Hey Claude, can you help me keep track of some ideas?
Claude
I'd be happy to help you track ideas! I can see you have a note-keeping system
available. Would you like me to create a new note for your ideas?
you
Yes! Let's create a note about MCP project ideas.
Claude
I'll create that note for you right now.
[Claude uses the create_note tool]
Created note: MCP-project-ideas
Would you like to add some initial ideas to this note?
you
Add these:
1) Recipe organizer that reads from cooking websites
2) Email summarizer that creates daily digests
3) Code documentation generator
Claude
[Updates the note with your ideas]
Your ideas have been saved! You now have these MCP project concepts documented:
- Recipe organizer that reads from cooking websites
- Email summarizer that creates daily digests
- Code documentation generator
Would you like me to help you elaborate on any of these ideas?
you
Hey Claude, can you help me keep track of some ideas?
Claude
I'd be happy to help you track ideas! I can see you have a note-keeping system
available. Would you like me to create a new note for your ideas?
you
Yes! Let's create a note about MCP project ideas.
Claude
I'll create that note for you right now.
[Claude uses the create_note tool]
Created note: MCP-project-ideas
Would you like to add some initial ideas to this note?
you
Add these:
1) Recipe organizer that reads from cooking websites
2) Email summarizer that creates daily digests
3) Code documentation generator
Claude
[Updates the note with your ideas]
Your ideas have been saved! You now have these MCP project concepts documented:
- Recipe organizer that reads from cooking websites
- Email summarizer that creates daily digests
- Code documentation generator
Would you like me to help you elaborate on any of these ideas?




6.3 Conversation Simulator
6.3 Conversation Simulator
Level Up: Adding Search
Level Up: Adding Search
Let's make our server more useful by adding search:
Let's make our server more useful by adding search:
Search Tools
Search Tools
// Add this tool to your server case 'search_notes': { const { query } = args; const files = await fs.readdir(NOTES_DIR); const results = []; for (const file of files) { if (!file.endsWith('.md')) continue; const content = await fs.readFile( path.join(NOTES_DIR, file), 'utf-8' ); // Simple search - check if query appears in content if (content.toLowerCase().includes(query.toLowerCase())) { // Extract title from content const titleMatch = content.match(/^# (.+)$/m); const title = titleMatch ? titleMatch[1] : file; // Find the line containing the query const lines = content.split('\n'); const matchingLine = lines.find(line => line.toLowerCase().includes(query.toLowerCase()) ); results.push({ filename: file, title, excerpt: matchingLine || 'Match found in file', }); } } return { content: [{ type: 'text', text: results.length > 0 ? `Found ${results.length} matches:\n\n${results.map(r => `**${r.title}** (${r.filename})\n> ${r.excerpt}` ).join('\n\n')}` : `No notes found matching "${query}"` }] }; } // Don't forget to add it to tools/list: { name: 'search_notes', description: 'Search through all notes', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search term to look for in notes' } }, required: ['query'] } }
you
Find my notes about Python
Claude
Let me search your notes for "Python".
[Claude uses search_notes tool]
Found 3 matches:
Learning Python
Started learning Python basics today
1709823456-Learning-Python.md
Project Ideas
Build a Python script to organize photos
1709834567-Project-Ideas.md
Book List
"Automate the Boring Stuff with Python" - recommended by Sarah
1709845678-Book-List.md
you
Find my notes about Python
Claude
Let me search your notes for "Python".
[Claude uses search_notes tool]
Found 3 matches:
Learning Python
Started learning Python basics today
1709823456-Learning-Python.md
Project Ideas
Build a Python script to organize photos
1709834567-Project-Ideas.md
Book List
"Automate the Boring Stuff with Python" - recommended by Sarah
1709845678-Book-List.md
you
Find my notes about Python
Claude
Let me search your notes for "Python".
[Claude uses search_notes tool]
Found 3 matches:
Learning Python
Started learning Python basics today
1709823456-Learning-Python.md
Project Ideas
Build a Python script to organize photos
1709834567-Project-Ideas.md
Book List
"Automate the Boring Stuff with Python" - recommended by Sarah
1709845678-Book-List.md















6.4 Feature Workshop
6.4 Feature Workshop
Real-World Example: The Download Organizer
Real-World Example: The Download Organizer
Let's build something everyone needs—a download folder organizer:
Let's build something everyone needs—a download folder organizer:
Download Organizer Code
Download Organizer Code
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import fs from 'fs/promises'; import path from 'path'; import os from 'os'; const DOWNLOADS_DIR = path.join(os.homedir(), 'Downloads'); // File type categories const FILE_CATEGORIES = { images: ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp'], documents: ['.pdf', '.doc', '.docx', '.txt', '.odt'], videos: ['.mp4', '.avi', '.mkv', '.mov', '.wmv'], audio: ['.mp3', '.wav', '.flac', '.aac', '.ogg'], archives: ['.zip', '.rar', '.7z', '.tar', '.gz'], code: ['.js', '.py', '.html', '.css', '.json', '.xml'], }; const server = new Server( { name: 'download-organizer', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Analyze downloads folder async function analyzeDownloads() { const files = await fs.readdir(DOWNLOADS_DIR); const analysis = { total: files.length, byCategory: {}, oldestFile: null, largestFile: null, totalSize: 0, }; for (const category of Object.keys(FILE_CATEGORIES)) { analysis.byCategory[category] = 0; } analysis.byCategory.other = 0; for (const file of files) { const filepath = path.join(DOWNLOADS_DIR, file); const stat = await fs.stat(filepath); if (!stat.isFile()) continue; // Categorize file const ext = path.extname(file).toLowerCase(); let categorized = false; for (const [category, extensions] of Object.entries(FILE_CATEGORIES)) { if (extensions.includes(ext)) { analysis.byCategory[category]++; categorized = true; break; } } if (!categorized) { analysis.byCategory.other++; } // Track statistics analysis.totalSize += stat.size; if (!analysis.oldestFile || stat.mtime < analysis.oldestFile.date) { analysis.oldestFile = { name: file, date: stat.mtime }; } if (!analysis.largestFile || stat.size > analysis.largestFile.size) { analysis.largestFile = { name: file, size: stat.size }; } } return analysis; } // Tool implementations server.setRequestHandler('tools/call', async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'analyze_downloads': { const analysis = await analyzeDownloads(); const report = `# Downloads Folder Analysis Total files: ${analysis.total}
By Category
By Category
${Object.entries(analysis.byCategory) .map(([cat, count]) => `- ${cat}: ${count} files`) .join('\n')}
Statistics
Statistics
- Total size: ${(analysis.totalSize / 1024 / 1024).toFixed(2)} MB - Oldest file: ${analysis.oldestFile?.name || 'None'} (${analysis.oldestFile?.date.toLocaleDateString() || 'N/A'}) - Largest file: ${analysis.largestFile?.name || 'None'} (${((analysis.largestFile?.size || 0) / 1024 / 1024).toFixed(2)} MB)`; return { content: [{ type: 'text', text: report }] }; } case 'organize_downloads': { const { dryRun = true } = args; const moves = []; const files = await fs.readdir(DOWNLOADS_DIR); for (const file of files) { const filepath = path.join(DOWNLOADS_DIR, file); const stat = await fs.stat(filepath); if (!stat.isFile()) continue; const ext = path.extname(file).toLowerCase(); for (const [category, extensions] of Object.entries(FILE_CATEGORIES)) { if (extensions.includes(ext)) { const targetDir = path.join(DOWNLOADS_DIR, category); const targetPath = path.join(targetDir, file); moves.push({ from: file, to: `${category}/${file}`, category }); if (!dryRun) { await fs.mkdir(targetDir, { recursive: true }); await fs.rename(filepath, targetPath); } break; } } } const summary = dryRun ? `Would organize ${moves.length} files:\n${moves.map(m => `- ${m.from} → ${m.to}`).join('\n')}` : `Organized ${moves.length} files into categories`; return { content: [{ type: 'text', text: summary }] }; } case 'find_old_downloads': { const { days = 30 = args; const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - days); const files = await fs.readdir(DOWNLOADS_DIR); const oldFiles = []; for (const file of files) { const filepath = path.join(DOWNLOADS_DIR, file); const stat = await fs.stat(filepath); if (stat.isFile() && stat.mtime < cutoffDate) { oldFiles.push({ name: file, date: stat.mtime, size: stat.size, ageInDays: Math.floor((Date.now() - stat.mtime) / (1000 * 60 * 60 * 24)) }); } } oldFiles.sort((a, b) => a.date - b.date); const report = oldFiles.length > 0 ? `Found ${oldFiles.length} files older than ${days} days:\n\n${oldFiles.map(f => `- ${f.name} (${f.ageInDays} days old, ${(f.size / 1024).toFixed(1)} KB)` ).join('\n')}` : `No files older than ${days} days found`; return { content: [{ type: 'text', text: report }] }; } default: throw new Error(`Unknown tool: ${name}`); } }); // Tool definitions server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'analyze_downloads', description: 'Analyze the contents of your Downloads folder', inputSchema: { type: 'object', properties: {} } }, { name: 'organize_downloads', description: 'Organize downloads into folders by file type', inputSchema: { type: 'object', properties: { dryRun: { type: 'boolean', description: 'If true, show what would be done without moving files', default: true } } } }, { name: 'find_old_downloads', description: 'Find files older than specified days', inputSchema: { type: 'object', properties: { days: { type: 'number', description: 'Files older than this many days', default: 30 } } } } ] }; }); const transport = new StdioServerTransport(); await server.connect(transport); console.error('Download Organizer MCP Server running...');
Now you can have conversations like:
Now you can have conversations like:
you
My downloads folder is a mess. Can you help?
Claude
I'll help you analyze and organize your downloads folder. Let me first
see what's in there.
[Claude uses analyze_downloads]
Downloads Folder Analysis
Total files: 247
-- By Category --
- images: 89 files
- documents: 43 files
- videos: 12 files
- audio: 23 files
- archives: 31 files
- code: 15 files
- other: 34 files
-- Statistics --
- Total size: 3,421.56 MB
- Oldest file: old-presentation.ppt (1/15/2023)
- Largest file: conference-recording.mp4 (892.34 MB)
You have quite a collection!
Would you like me to:
1.Organize these files into category folders?
2. Find old files you might want to clean up?
3. Both?
you
Let's find files older than 60 days first
Claude
[Claude uses find_old_downloads with days=60]
Found 73 files older than 60 days:
Old-presentation.ppt (423 days old, 2,341.2 KB)
Tax-docs-2023.zip (287 days old, 15,234.1 KB)
Meeting-notes-jan.pdf (198 days old, 234.5 KB)
(showing first few, 70 more)
These files are taking up about 1.2 GB.
Would you like me to:
- Move them to an "Archive" folder?
- See the complete list?
- organize your current files by type?
you
My downloads folder is a mess. Can you help?
Claude
I'll help you analyze and organize your downloads folder. Let me first
see what's in there.
[Claude uses analyze_downloads]
Downloads Folder Analysis
Total files: 247
-- By Category --
- images: 89 files
- documents: 43 files
- videos: 12 files
- audio: 23 files
- archives: 31 files
- code: 15 files
- other: 34 files
-- Statistics --
- Total size: 3,421.56 MB
- Oldest file: old-presentation.ppt (1/15/2023)
- Largest file: conference-recording.mp4 (892.34 MB)
You have quite a collection!
Would you like me to:
1.Organize these files into category folders?
2. Find old files you might want to clean up?
3. Both?
you
Let's find files older than 60 days first
Claude
[Claude uses find_old_downloads with days=60]
Found 73 files older than 60 days:
Old-presentation.ppt (423 days old, 2,341.2 KB)
Tax-docs-2023.zip (287 days old, 15,234.1 KB)
Meeting-notes-jan.pdf (198 days old, 234.5 KB)
(showing first few, 70 more)
These files are taking up about 1.2 GB.
Would you like me to:
- Move them to an "Archive" folder?
- See the complete list?
- organize your current files by type?
you
My downloads folder is a mess. Can you help?
Claude
I'll help you analyze and organize your downloads folder. Let me first
see what's in there.
[Claude uses analyze_downloads]
Downloads Folder Analysis
Total files: 247
-- By Category --
- images: 89 files
- documents: 43 files
- videos: 12 files
- audio: 23 files
- archives: 31 files
- code: 15 files
- other: 34 files
-- Statistics --
- Total size: 3,421.56 MB
- Oldest file: old-presentation.ppt (1/15/2023)
- Largest file: conference-recording.mp4 (892.34 MB)
You have quite a collection!
Would you like me to:
1.Organize these files into category folders?
2. Find old files you might want to clean up?
3. Both?
you
Let's find files older than 60 days first
Claude
[Claude uses find_old_downloads with days=60]
Found 73 files older than 60 days:
Old-presentation.ppt (423 days old, 2,341.2 KB)
Tax-docs-2023.zip (287 days old, 15,234.1 KB)
Meeting-notes-jan.pdf (198 days old, 234.5 KB)
(showing first few, 70 more)
These files are taking up about 1.2 GB.
Would you like me to:
- Move them to an "Archive" folder?
- See the complete list?
- organize your current files by type?

Images
Documents
Videos


Images
Documents
Videos
6.5 Downloads Dashboard
6.5 Downloads Dashboard
Debugging: When Things Don't Work
Debugging: When Things Don't Work
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:
Debug Environment
// Add logging to your server for debugging const DEBUG = process.env.DEBUG === 'true'; function log(...args) { if (DEBUG) { console.error('[DEBUG]', new Date().toISOString(), ...args); } } server.setRequestHandler('tools/call', async (request) => { log('Tool call received:', request.params.name); try { // Your tool implementation const result = await handleTool(request); log('Tool call successful:', request.params.name); return result; } catch (error) { log('Tool call failed:', request.params.name, error); throw error; } }); // Run with debugging: // DEBUG=true node your-server.js
Common Issues and Fixes
Common Issues and Fixes
# Issue 1: "Connection refused" # Fix: Check your path in claude_desktop_config.json { "mcpServers": { "your-server": { "command": "node", "args": ["/absolute/path/to/your-server.js"] # Must be absolute! } } } # Issue 2: "Module not found" # Fix: Install dependencies cd your-server-directory npm install @modelcontextprotocol/sdk # Issue 3: "Permission denied" # Fix: Make your script executable chmod +x your-server.js # Issue 4: Server crashes silently # Fix: Add error handling process.on('uncaughtException', (error) => { console.error('Uncaught exception:', error); process.exit(1); });
Beyond Basic: Adding Resources
Beyond Basic: Adding Resources
So far we've used Tools (actions). Let's add Resources (data that Claude can read):
So far we've used Tools (actions). Let's add Resources (data that Claude can read):
Add Resources
// Add to your note server server.setRequestHandler('resources/list', async () => { return { resources: [ { uri: 'notes://recent', name: 'Recent Notes', description: 'Notes from the last 7 days', mimeType: 'text/plain' } ] }; }); server.setRequestHandler('resources/read', async (request) => { const { uri } = request.params; if (uri === 'notes://recent') { const files = await fs.readdir(NOTES_DIR); const recentNotes = []; const cutoff = Date.now() - (7 * 24 * 60 * 60 * 1000); // 7 days for (const file of files) { if (!file.endsWith('.md')) continue; const filepath = path.join(NOTES_DIR, file); const stat = await fs.stat(filepath); // Check if file was created in last 7 days const timestamp = parseInt(file.split('-')[0]); if (timestamp > cutoff) { const content = await fs.readFile(filepath, 'utf-8'); recentNotes.push(content); } } return { contents: [{ uri: uri, mimeType: 'text/plain', text: recentNotes.join('\n\n---\n\n') }] }; } throw new Error(`Unknown resource: ${uri}`); });
Now Claude can proactively reference your recent notes:
Now Claude can proactively reference your recent notes:
you
What have I been working on lately?
Claude
Let me check your recent notes to see what you've been working on.
[Claude reads the notes://recent resource]
Based on your recent notes, you've been working on several things:
MCP Integration Project
3 days ago
You're building a note-keeping system with search functionality
Learning Rust
5 days ago
Planning a trip to Portland with a list of restaurants and hiking trails
Book Reading List
6 days ago
Added "The Pragmatic Programmer" and
"Designing Data-Intensive Applications"
Would you like me to help you continue with any of these projects?
you
What have I been working on lately?
Claude
Let me check your recent notes to see what you've been working on.
[Claude reads the notes://recent resource]
Based on your recent notes, you've been working on several things:
MCP Integration Project
3 days ago
You're building a note-keeping system with search functionality
Learning Rust
5 days ago
Planning a trip to Portland with a list of restaurants and hiking trails
Book Reading List
6 days ago
Added "The Pragmatic Programmer" and
"Designing Data-Intensive Applications"
Would you like me to help you continue with any of these projects?
you
What have I been working on lately?
Claude
Let me check your recent notes to see what you've been working on.
[Claude reads the notes://recent resource]
Based on your recent notes, you've been working on several things:
MCP Integration Project
3 days ago
You're building a note-keeping system with search functionality
Learning Rust
5 days ago
Planning a trip to Portland with a list of restaurants and hiking trails
Book Reading List
6 days ago
Added "The Pragmatic Programmer" and
"Designing Data-Intensive Applications"
Would you like me to help you continue with any of these projects?

Tools
Resources

Tools
Resources

Tools
Resources
6.6 Conversation Simulator
The Power of Composition
The Power of Composition
The real magic happens when you combine MCP servers:
The real magic happens when you combine MCP servers:
Bash Composition
# Your claude_desktop_config.json can have multiple servers { "mcpServers": { "note-keeper": { "command": "node", "args": ["/path/to/note-server.js"] }, "download-organizer": { "command": "node", "args": ["/path/to/download-organizer.js"] }, "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "your-token" } } } }
Now Claude can proactively reference your recent notes:
Now Claude can proactively reference your recent notes:
you
Can you check my GitHub PRs and create a note about what I need to review?
Claude
I'll check your GitHub PRs and create a summary note for you.
[Claude uses GitHub server to list PRs]
[Claude uses note-keeper to create a note]
I've created a note titled "GitHub-PR-Review-2024-03-15" with the following PRs that need your attention:
PR #234: Update authentication flow
Waiting for your review
Last updated: 2 hours ago
3 files changed, 2 comments
PR #229: Fix memory leak in worker process
Approved by TeamLead, waiting for your approval
Last updated: Yesterday
5 files changed
The note has been saved to your notes system. Would you like me to analyze the changes in any of these PRs?
you
Can you check my GitHub PRs and create a note about what I need to review?
Claude
I'll check your GitHub PRs and create a summary note for you.
[Claude uses GitHub server to list PRs]
[Claude uses note-keeper to create a note]
I've created a note titled "GitHub-PR-Review-2024-03-15" with the following PRs that need your attention:
PR #234: Update authentication flow
Waiting for your review
Last updated: 2 hours ago
3 files changed, 2 comments
PR #229: Fix memory leak in worker process
Approved by TeamLead, waiting for your approval
Last updated: Yesterday
5 files changed
The note has been saved to your notes system. Would you like me to analyze the changes in any of these PRs?
you
Can you check my GitHub PRs and create a note about what I need to review?
Claude
I'll check your GitHub PRs and create a summary note for you.
[Claude uses GitHub server to list PRs]
[Claude uses note-keeper to create a note]
I've created a note titled "GitHub-PR-Review-2024-03-15" with the following PRs that need your attention:
PR #234: Update authentication flow
Waiting for your review
Last updated: 2 hours ago
3 files changed, 2 comments
PR #229: Fix memory leak in worker process
Approved by TeamLead, waiting for your approval
Last updated: Yesterday
5 files changed
The note has been saved to your notes system. Would you like me to analyze the changes in any of these PRs?




6.7 Server Composer
Your MCP Toolkit
Your MCP Toolkit
Here's a practical toolkit for building MCP servers:
Here's a practical toolkit for building MCP servers:
mcp-toolkit.js
mcp-toolkit.js
// mcp-toolkit.js - Reusable utilities // 1. Error handling wrapper export function withErrorHandling(handler) { return async (request) => { try { return await handler(request); } catch (error) { console.error('Error in handler:', error); return { content: [{ type: 'text', text: `Error: ${error.message}` }] }; } }; } // 2. Input validation export function validateInput(schema, input) { for (const [key, rules] of Object.entries(schema)) { if (rules.required && !(key in input)) { throw new Error(`Missing required field: ${key}`); } if (key in input && rules.type) { const actualType = typeof input[key]; if (actualType !== rules.type) { throw new Error(`${key} must be ${rules.type}, got ${actualType}`); } } } return true; } // 3. Pagination helper export function paginate(items, page = 1, perPage = 10) { const start = (page - 1) * perPage; const end = start + perPage; return { items: items.slice(start, end), totalItems: items.length, totalPages: Math.ceil(items.length / perPage), currentPage: page, hasNext: end < items.length, hasPrev: page > 1 }; } // 4. Rate limiting export class RateLimiter { constructor(maxRequests = 10, windowMs = 60000) { this.maxRequests = maxRequests; this.windowMs = windowMs; this.requests = new Map(); } check(clientId) { const now = Date.now(); const userRequests = this.requests.get(clientId) || []; // Remove old requests const validRequests = userRequests.filter( time => now - time < this.windowMs ); if (validRequests.length >= this.maxRequests) { throw new Error('Rate limit exceeded'); } validRequests.push(now); this.requests.set(clientId, validRequests); return true; } } // 5. Simple caching export class Cache { constructor(ttlMs = 300000) { // 5 minutes default this.cache = new Map(); this.ttlMs = ttlMs; } get(key) { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() - entry.timestamp > this.ttlMs) { this.cache.delete(key); return null; } return entry.value; } set(key, value) { this.cache.set(key, { value, timestamp: Date.now() }); } }





6.8 Conversation Simulator
6.8 Conversation Simulator
What You've Built
What You've Built
Take a moment to appreciate what you've accomplished:
Take a moment to appreciate what you've accomplished:
Built a working MCP server that solves real problems
Connected it to Claude Desktop and had actual conversations
Added search capabilities to make it more useful
Created a download organize that can clean up your files
Learned debugging techniques for when things go wrong
Understand Resources vs Tools and when to use each
Can combine multiple servers for complex workflows
Next Steps: Where to Go From Here
Next Steps: Where to Go From Here
Here's a practical toolkit for building MCP servers:
Here's a practical toolkit for building MCP servers:
mcp-toolkit.js
mcp-toolkit.js
// Your journey continues... const nextProjects = [ { name: "Email Digest Server", difficulty: "Medium", description: "Read emails and create daily summaries", learnings: ["Email APIs", "Scheduling", "Summarization"] }, { name: "Code Analyzer", difficulty: "Medium", description: "Analyze code quality and suggest improvements", learnings: ["AST parsing", "Code metrics", "Best practices"] }, { name: "Personal Finance Tracker", difficulty: "Hard", description: "Track expenses and generate reports", learnings: ["Data persistence", "Calculations", "Visualizations"] }, { name: "Smart Home Controller", difficulty: "Hard", description: "Control IoT devices through Claude", learnings: ["IoT protocols", "Device APIs", "Security"] } ]; // Join the community const resources = { documentation: "https://modelcontextprotocol.io/docs", github: "https://github.com/modelcontextprotocol", discord: "MCP Community Discord", examples: "https://github.com/modelcontextprotocol/servers" };
YOU ARE HERE NOW!




YOU ARE HERE NOW!




YOU ARE HERE NOW!




6.9 Your MCP Journey Map
6.9 Your MCP Journey Map