Skip to content

Commit afd54b0

Browse files
committed
📚 Update intermediate notebooks to FastMCP patterns
1 parent c805f23 commit afd54b0

File tree

2 files changed

+683
-24
lines changed

2 files changed

+683
-24
lines changed

notebooks/intermediate/06_file_operations.ipynb

Lines changed: 306 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,325 @@
88
}
99
},
1010
"source": [
11-
"# 📁 File Operations with MCP\n",
11+
"# 📁 File Operations with FastMCP\n",
1212
"\n",
13-
"Welcome to intermediate MCP development! In this notebook, you'll learn to build sophisticated file system operations that allow AI assistants to safely read, write, and manipulate files and directories.\n",
13+
"Welcome to intermediate MCP development! In this notebook, you'll learn to build a production-ready file processing server using FastMCP's modern patterns and type-safe operations.\n",
1414
"\n",
1515
"## 🎯 Learning Objectives\n",
1616
"\n",
1717
"By the end of this notebook, you will:\n",
18-
"- Create secure file reading and writing tools\n",
19-
"- Build directory navigation and management tools\n",
20-
"- Implement file search and filtering capabilities\n",
21-
"- Handle different file formats (JSON, CSV, text, images)\n",
22-
"- Apply security best practices for file operations\n",
18+
"- Build a type-safe file processing server with FastMCP\n",
19+
"- Implement file resources and prompts\n",
20+
"- Create secure file operation tools\n",
21+
"- Use Pydantic models for validation\n",
22+
"- Apply MCP best practices for file handling\n",
2323
"\n",
2424
"## 🛠️ What You'll Build\n",
2525
"\n",
26-
"1. **📖 File Reader** - Read and parse various file types\n",
27-
"2. **✍️ File Writer** - Create and update files safely\n",
28-
"3. **🗂️ Directory Manager** - Navigate and organize directories\n",
29-
"4. **🔍 File Search** - Find files by name, content, or metadata\n",
30-
"5. **🔄 File Converter** - Convert between different formats\n",
26+
"```python\n",
27+
"from mcp.server.fastmcp import FastMCP\n",
28+
"from pydantic import BaseModel\n",
3129
"\n",
32-
"## 🔐 Security Considerations\n",
30+
"# Initialize FastMCP server\n",
31+
"mcp = FastMCP(\"file_tools\")\n",
3332
"\n",
34-
"File operations require careful security considerations:\n",
35-
"- **Path validation** to prevent directory traversal attacks\n",
36-
"- **Permission checks** to ensure authorized access\n",
37-
"- **File type validation** to prevent malicious uploads\n",
38-
"- **Size limits** to prevent resource exhaustion\n",
33+
"class FileInfo(BaseModel):\n",
34+
" \"\"\"File metadata model\"\"\"\n",
35+
" path: str\n",
36+
" size: int\n",
37+
" mime_type: str\n",
38+
" hash: str\n",
39+
"\n",
40+
"@mcp.resource(\"file://{path}\")\n",
41+
"async def get_file_info(path: str) -> FileInfo:\n",
42+
" \"\"\"Get file metadata\"\"\"\n",
43+
" ...\n",
44+
"\n",
45+
"@mcp.tool()\n",
46+
"async def read_file(path: str) -> Dict:\n",
47+
" \"\"\"Read file contents safely\"\"\"\n",
48+
" ...\n",
49+
"\n",
50+
"@mcp.prompt()\n",
51+
"def error_prompt() -> str:\n",
52+
" \"\"\"Handle file errors\"\"\"\n",
53+
" return \"\"\"File operation failed...\"\"\"\n",
54+
"```\n",
55+
"\n",
56+
"## 🔐 Modern MCP Patterns\n",
57+
"\n",
58+
"FastMCP provides robust file handling through:\n",
59+
"- **Type-safe operations** with Pydantic models\n",
60+
"- **Resource providers** for file metadata\n",
61+
"- **System prompts** for error handling\n",
62+
"- **Async/await** for performance\n",
63+
"- **Structured responses** for consistency\n",
3964
"\n",
4065
"## 📚 Table of Contents\n",
4166
"\n",
42-
"1. [Security First](#security-first)\n",
43-
"2. [Basic File Operations](#basic-operations)\n",
44-
"3. [Advanced File Handling](#advanced-handling)\n",
45-
"4. [Directory Management](#directory-management)\n",
46-
"5. [File Search and Filtering](#search-filtering)\n",
47-
"6. [Production Considerations](#production)\n"
67+
"1. [FastMCP Setup](#fastmcp-setup)\n",
68+
"2. [File Resources](#file-resources)\n",
69+
"3. [File Operation Tools](#file-tools)\n",
70+
"4. [System Prompts](#system-prompts)\n",
71+
"5. [Error Handling](#error-handling)\n",
72+
"6. [Production Patterns](#production-patterns)\n"
73+
]
74+
},
75+
{
76+
"cell_type": "code",
77+
"execution_count": null,
78+
"metadata": {},
79+
"outputs": [],
80+
"source": [
81+
"# First, let's set up our FastMCP server with necessary imports\n",
82+
"from mcp.server.fastmcp import FastMCP\n",
83+
"import mcp.types as types\n",
84+
"from pydantic import BaseModel\n",
85+
"from typing import Dict, List, Optional\n",
86+
"from pathlib import Path\n",
87+
"import hashlib\n",
88+
"import mimetypes\n",
89+
"from datetime import datetime\n",
90+
"\n",
91+
"# Initialize our FastMCP server\n",
92+
"mcp = FastMCP(\"file_processor\")\n",
93+
"\n",
94+
"# Define our data models\n",
95+
"class FileInfo(BaseModel):\n",
96+
" \"\"\"File metadata model\"\"\"\n",
97+
" path: str\n",
98+
" size: int\n",
99+
" modified: float\n",
100+
" mime_type: str\n",
101+
" hash: Optional[str]\n",
102+
"\n",
103+
"class FileContent(BaseModel):\n",
104+
" \"\"\"File content model\"\"\"\n",
105+
" content: str\n",
106+
" encoding: str\n",
107+
" lines: int\n",
108+
" info: FileInfo\n",
109+
"\n",
110+
"# System prompts\n",
111+
"@mcp.prompt()\n",
112+
"def system_prompt() -> str:\n",
113+
" \"\"\"Define the file processor's role\"\"\"\n",
114+
" return \"\"\"\n",
115+
" You are a file processing assistant that helps users work with files safely.\n",
116+
" Always validate paths and file operations before execution.\n",
117+
" Never perform dangerous operations without confirmation.\n",
118+
" \"\"\"\n",
119+
"\n",
120+
"@mcp.prompt()\n",
121+
"def error_prompt() -> str:\n",
122+
" \"\"\"Handle file operation errors\"\"\"\n",
123+
" return \"\"\"\n",
124+
" I encountered an error while processing the file.\n",
125+
" This could be due to:\n",
126+
" - Invalid file path\n",
127+
" - Permission issues\n",
128+
" - File not found\n",
129+
" - Unsupported file type\n",
130+
" Please check the file and try again.\n",
131+
" \"\"\"\n",
132+
"\n",
133+
"print(\"✅ FastMCP server initialized with models and prompts!\")\n"
134+
]
135+
},
136+
{
137+
"cell_type": "code",
138+
"execution_count": null,
139+
"metadata": {},
140+
"outputs": [],
141+
"source": [
142+
"# Now let's implement our file resource provider\n",
143+
"@mcp.resource(\"file://{path}\")\n",
144+
"async def get_file_info(path: str) -> Dict:\n",
145+
" \"\"\"Get metadata about a file\"\"\"\n",
146+
" try:\n",
147+
" full_path = Path(path).resolve()\n",
148+
" \n",
149+
" if not full_path.exists():\n",
150+
" return {\"error\": \"File not found\"}\n",
151+
" \n",
152+
" stat = full_path.stat()\n",
153+
" mime_type, _ = mimetypes.guess_type(str(full_path))\n",
154+
" \n",
155+
" # Calculate file hash\n",
156+
" sha256_hash = hashlib.sha256()\n",
157+
" with open(full_path, \"rb\") as f:\n",
158+
" for byte_block in iter(lambda: f.read(4096), b\"\"):\n",
159+
" sha256_hash.update(byte_block)\n",
160+
" \n",
161+
" return FileInfo(\n",
162+
" path=str(full_path),\n",
163+
" size=stat.st_size,\n",
164+
" modified=stat.st_mtime,\n",
165+
" mime_type=mime_type or \"application/octet-stream\",\n",
166+
" hash=sha256_hash.hexdigest()\n",
167+
" ).dict()\n",
168+
" \n",
169+
" except Exception as e:\n",
170+
" return {\"error\": str(e)}\n",
171+
"\n",
172+
"# Create a test file\n",
173+
"with open(\"test.txt\", \"w\") as f:\n",
174+
" f.write(\"Hello FastMCP!\")\n",
175+
"\n",
176+
"# Test our resource\n",
177+
"import asyncio\n",
178+
"result = asyncio.run(get_file_info(\"test.txt\"))\n",
179+
"print(\"📄 File Info:\", result)\n"
180+
]
181+
},
182+
{
183+
"cell_type": "code",
184+
"execution_count": null,
185+
"metadata": {},
186+
"outputs": [],
187+
"source": [
188+
"# Let's implement our file operation tools\n",
189+
"@mcp.tool()\n",
190+
"async def read_file(file_path: str, encoding: str = \"utf-8\") -> Dict:\n",
191+
" \"\"\"\n",
192+
" Read a file safely\n",
193+
" \n",
194+
" Args:\n",
195+
" file_path: Path to the file\n",
196+
" encoding: File encoding (default: utf-8)\n",
197+
" \n",
198+
" Returns:\n",
199+
" Dictionary with file contents and metadata\n",
200+
" \"\"\"\n",
201+
" try:\n",
202+
" # Get file info from resource\n",
203+
" file_info = await get_file_info(file_path)\n",
204+
" \n",
205+
" if \"error\" in file_info:\n",
206+
" raise Exception(file_info[\"error\"])\n",
207+
" \n",
208+
" # Validate file type\n",
209+
" if not file_info[\"mime_type\"].startswith((\"text/\", \"application/\")):\n",
210+
" raise Exception(\"Unsupported file type\")\n",
211+
" \n",
212+
" # Read file\n",
213+
" with open(file_path, \"r\", encoding=encoding) as f:\n",
214+
" content = f.read()\n",
215+
" \n",
216+
" return {\n",
217+
" \"success\": True,\n",
218+
" \"data\": FileContent(\n",
219+
" content=content,\n",
220+
" encoding=encoding,\n",
221+
" lines=len(content.splitlines()),\n",
222+
" info=FileInfo(**file_info)\n",
223+
" ).dict()\n",
224+
" }\n",
225+
" \n",
226+
" except Exception as e:\n",
227+
" return {\n",
228+
" \"success\": False,\n",
229+
" \"error\": str(e),\n",
230+
" \"prompt\": \"error_prompt\"\n",
231+
" }\n",
232+
"\n",
233+
"@mcp.tool()\n",
234+
"async def write_file(file_path: str, content: str, encoding: str = \"utf-8\") -> Dict:\n",
235+
" \"\"\"\n",
236+
" Write content to a file safely\n",
237+
" \n",
238+
" Args:\n",
239+
" file_path: Path to write to\n",
240+
" content: Content to write\n",
241+
" encoding: File encoding (default: utf-8)\n",
242+
" \n",
243+
" Returns:\n",
244+
" Dictionary with operation results\n",
245+
" \"\"\"\n",
246+
" try:\n",
247+
" # Validate path\n",
248+
" full_path = Path(file_path).resolve()\n",
249+
" \n",
250+
" # Create parent directories\n",
251+
" full_path.parent.mkdir(parents=True, exist_ok=True)\n",
252+
" \n",
253+
" # Write file\n",
254+
" with open(full_path, \"w\", encoding=encoding) as f:\n",
255+
" f.write(content)\n",
256+
" \n",
257+
" # Get updated file info\n",
258+
" file_info = await get_file_info(str(full_path))\n",
259+
" \n",
260+
" return {\n",
261+
" \"success\": True,\n",
262+
" \"data\": {\n",
263+
" \"path\": str(full_path),\n",
264+
" \"size\": file_info[\"size\"],\n",
265+
" \"lines\": len(content.splitlines()),\n",
266+
" \"encoding\": encoding\n",
267+
" }\n",
268+
" }\n",
269+
" \n",
270+
" except Exception as e:\n",
271+
" return {\n",
272+
" \"success\": False,\n",
273+
" \"error\": str(e),\n",
274+
" \"prompt\": \"error_prompt\"\n",
275+
" }\n",
276+
"\n",
277+
"# Test our tools\n",
278+
"result = asyncio.run(write_file(\"example.txt\", \"Testing FastMCP tools!\\nThis is line 2.\"))\n",
279+
"print(\"✍️ Write result:\", result)\n",
280+
"\n",
281+
"result = asyncio.run(read_file(\"example.txt\"))\n",
282+
"print(\"\\n📖 Read result:\", result)\n"
283+
]
284+
},
285+
{
286+
"cell_type": "raw",
287+
"metadata": {
288+
"vscode": {
289+
"languageId": "raw"
290+
}
291+
},
292+
"source": [
293+
"# 🚀 Running the FastMCP Server\n",
294+
"\n",
295+
"Now that we have our file processing server built with FastMCP patterns, let's run it:\n",
296+
"\n",
297+
"```python\n",
298+
"if __name__ == \"__main__\":\n",
299+
" # Run the MCP server\n",
300+
" print(\"📁 Starting File Processor MCP Server...\")\n",
301+
" mcp.run(transport=\"streamable-http\")\n",
302+
"```\n",
303+
"\n",
304+
"## 🎯 Key Takeaways\n",
305+
"\n",
306+
"1. **Type Safety**\n",
307+
" - Use Pydantic models for data validation\n",
308+
" - Define clear input/output types\n",
309+
" - Catch errors early with type checking\n",
310+
"\n",
311+
"2. **Resource Providers**\n",
312+
" - Abstract file metadata access\n",
313+
" - Cache common operations\n",
314+
" - Provide consistent interfaces\n",
315+
"\n",
316+
"3. **System Prompts**\n",
317+
" - Guide AI behavior\n",
318+
" - Handle errors gracefully\n",
319+
" - Provide clear user feedback\n",
320+
"\n",
321+
"4. **Best Practices**\n",
322+
" - Always validate paths\n",
323+
" - Use async/await for I/O\n",
324+
" - Structure responses consistently\n",
325+
" - Handle errors with prompts\n",
326+
"\n",
327+
"## 🔜 Next Steps\n",
328+
"\n",
329+
"Continue to [API Integration](07_api_integration.ipynb) to learn how to connect your MCP server to external services!\n"
48330
]
49331
}
50332
],

0 commit comments

Comments
 (0)