Skip to main content
Glama

PocketBase MCP Server

by fadlee
MIT License
0
developer-guide.md9.52 kB
# Developer Guide: Adding New Tools This guide provides technical documentation for developers who want to extend the PocketBase MCP Server with new tools. ## Architecture Overview The server follows a modular architecture with clear separation of concerns: ``` src/ ├── tools/ │ ├── schemas/ # Tool input schemas (validation) │ ├── handlers/ # Tool implementation logic │ └── index.ts # Tool registry ├── utils/ # Shared utilities ├── types/ # TypeScript type definitions └── server.ts # Main server configuration ``` ## Step-by-Step Guide to Add a New Tool ### 1. Define the Tool Schema Create or update a schema file in `src/tools/schemas/`: ```typescript // src/tools/schemas/example.ts export const myNewToolSchema = { type: "object" as const, properties: { collection: { type: "string" as const, description: "Collection name" }, options: { type: "object" as const, properties: { limit: { type: "number" as const } } } }, required: ["collection" as const] }; ``` ### 2. Implement the Tool Handler Create or update a handler file in `src/tools/handlers/`: ```typescript // src/tools/handlers/example.ts import type { PocketBase } from 'pocketbase'; import { createJsonResponse } from '../../utils/response.js'; import { handlePocketBaseError } from '../../utils/errors.js'; export function createMyNewToolHandler(pb: PocketBase) { return async (args: { collection: string; options?: { limit?: number } }) => { try { const { collection, options = {} } = args; const { limit = 50 } = options; // Your tool implementation here const result = await pb.collection(collection).getList(1, limit); return createJsonResponse({ success: true, data: result, message: `Retrieved ${result.items.length} items from ${collection}` }); } catch (error) { return handlePocketBaseError(error, 'Failed to execute my new tool'); } }; } ``` ### 3. Export Schema and Handler Update the respective index files: ```typescript // src/tools/schemas/index.ts export { myNewToolSchema } from './example.js'; // src/tools/handlers/index.ts export { createMyNewToolHandler } from './example.js'; ``` ### 4. Register the Tool Add the tool to the main server configuration: ```typescript // src/server.ts import { myNewToolSchema, createMyNewToolHandler } from './tools/index.js'; // In the tools array: { name: "my_new_tool", description: "Description of what this tool does", inputSchema: myNewToolSchema, handler: createMyNewToolHandler(pb) } ``` ## Best Practices ### Error Handling Always use the standardized error handling utility: ```typescript import { handlePocketBaseError } from '../../utils/errors.js'; try { // Your logic here } catch (error) { return handlePocketBaseError(error, 'Custom error message'); } ``` ### Response Formatting Use the response utilities for consistent output: ```typescript import { createJsonResponse, createTextResponse } from '../../utils/response.js'; // For JSON responses return createJsonResponse({ data: result }); // For text responses return createTextResponse('Operation completed successfully'); ``` ### Type Safety Define proper TypeScript types: ```typescript interface MyToolArgs { collection: string; options?: { limit?: number; sort?: string; }; } export function createMyNewToolHandler(pb: PocketBase) { return async (args: MyToolArgs) => { // Implementation }; } ``` ### Schema Validation Ensure your schema properly validates input: ```typescript export const myToolSchema = { type: "object" as const, properties: { required_field: { type: "string" as const, description: "This field is required" }, optional_field: { type: "number" as const, description: "This field is optional", minimum: 1, maximum: 1000 } }, required: ["required_field" as const] }; ``` ## Testing Your New Tool 1. **Build the project**: ```bash npm run build ``` 2. **Test the server**: ```bash node build/src/index.js --help ``` 3. **Verify tool registration**: The tool should appear in the MCP tool list when connected to your MCP client. ## Complete Example: Adding a `count_records` Tool Here's a complete walkthrough of adding a new tool that counts records in a collection: ### 1. Define the Schema ```typescript // src/tools/schemas/analysis.ts export const countRecordsSchema = { type: 'object', properties: { collection: { type: 'string', description: 'Collection name to count records from' }, filter: { type: 'string', description: 'Optional filter to apply when counting' } }, required: ['collection'] } as const; ``` ## File Handling Tools Implementation The recently added file handling tools (`upload_file`, `download_file`, and `upload_file_from_url`) demonstrate advanced patterns: ### Key Implementation Details 1. **File Upload with FormData**: Uses `FormData` and `Blob` for file uploads 2. **File URL Generation**: Uses `pb.files.getUrl()` for download URLs 3. **File Upload from URL**: Downloads files from URLs and uploads to PocketBase 4. **MIME Type Handling**: Automatic file extension detection from MIME types 5. **Type Safety**: Proper TypeScript interfaces for file operations 6. **Error Handling**: Validates file field existence and content ### File Upload Pattern ```typescript // Create a Blob from file content const blob = new Blob([fileContent]); // Use FormData for file upload const formData = new FormData(); formData.append(fileField, blob, fileName); // Update record with file const record = await pb.collection(collection).update(recordId, formData); ``` ### File Download Pattern ```typescript // Get record with file field const record = await pb.collection(collection).getOne(recordId); // Generate download URL const fileUrl = pb.files.getUrl(record, fileName); ``` ### File Upload from URL Pattern ```typescript // Download file from URL const response = await fetch(url); const arrayBuffer = await response.arrayBuffer(); // Get MIME type and determine file extension const mimeType = response.headers.get('content-type') || 'application/octet-stream'; const extension = getExtensionFromMimeType(mimeType); const fileName = customFileName || `file_${Date.now()}${extension}`; // Create blob and upload const blob = new Blob([arrayBuffer], { type: mimeType }); const formData = new FormData(); formData.append(fileField, blob, fileName); const record = await pb.collection(collection).update(recordId, formData); ``` ### MIME Type to Extension Mapping ```typescript function getExtensionFromMimeType(mimeType: string): string { const mimeToExt: Record<string, string> = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif', 'image/webp': '.webp', 'application/pdf': '.pdf', 'text/plain': '.txt', 'application/json': '.json', 'text/csv': '.csv', // Add more mappings as needed }; return mimeToExt[mimeType] || ''; } ``` ## Example: Complete Tool Implementation Here's a complete example of adding a `count_records` tool: ```typescript // src/tools/schemas/record.ts (add to existing file) export const countRecordsSchema = { type: "object" as const, properties: { collection: { type: "string" as const, description: "Collection name to count records in" }, filter: { type: "string" as const, description: "Optional filter expression" } }, required: ["collection" as const] }; // src/tools/handlers/record.ts (add to existing file) export function createCountRecordsHandler(pb: PocketBase) { return async (args: { collection: string; filter?: string }) => { try { const { collection, filter } = args; const result = await pb.collection(collection).getList(1, 1, { filter: filter || '', skipTotal: false }); return createJsonResponse({ collection, totalCount: result.totalItems, filter: filter || 'none' }); } catch (error) { return handlePocketBaseError(error, `Failed to count records in ${args.collection}`); } }; } // src/server.ts (add to tools array) { name: "count_records", description: "Count the total number of records in a collection with optional filtering", inputSchema: countRecordsSchema, handler: createCountRecordsHandler(pb) } ``` ##### Debugging Tips - Use `console.log()` for debugging during development - Check the MCP server logs in your MCP client's developer console - Validate your schema using online JSON Schema validators - Test with minimal examples before adding complex logic ## Code Quality Guidelines ### File Organization - Keep related schemas in the same file (e.g., all record-related schemas in `record.ts`) - Group handlers by functionality - Use descriptive file and function names ### Documentation - Add JSDoc comments to all exported functions - Include usage examples in complex schemas - Document any non-obvious business logic ### Performance - Use appropriate PocketBase query options (limit, fields, etc.) - Implement proper error boundaries - Consider caching for expensive operations ### Security - Validate all input parameters - Use proper authentication when required - Sanitize user-provided filter expressions

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fadlee/pocketbase-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server