Skip to main content
Glama

Advanced PocketBase MCP Server

agent-best-practices.ts38.9 kB
/** * PocketBase MCP Server - Best Practices Implementation * * This implementation follows Cloudflare's official MCP best practices: * - Uses the official Cloudflare Agents SDK * - Proper tool registration patterns from @cloudflare/mcp-server-cloudflare * - Individual Zod schemas for better LLM understanding * - Proper error handling and state management * - Follows the exact patterns from Context7 documentation */ import { Agent } from "agents"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import PocketBase from 'pocketbase'; import { StripeService } from './services/stripe.js'; import { EmailService } from './services/email.js'; // Environment interface following Cloudflare patterns interface Env { POCKETBASE_URL?: string; POCKETBASE_ADMIN_EMAIL?: string; POCKETBASE_ADMIN_PASSWORD?: string; STRIPE_SECRET_KEY?: string; SENDGRID_API_KEY?: string; EMAIL_SERVICE?: string; SMTP_HOST?: string; } // Agent state interface following best practices interface State { pocketbaseInitialized: boolean; isAuthenticated: boolean; discoveryMode: boolean; customHeaders: Record<string, string>; realtimeSubscriptions: string[]; lastActivityTime: number; } // Individual Zod schemas following Cloudflare MCP server patterns // This approach provides better LLM understanding and schema reusability /** Collection name schema */ export const CollectionNameSchema = z .string() .min(1) .max(100) .regex(/^[a-zA-Z][a-zA-Z0-9_]*$/) .describe('The name of the PocketBase collection (alphanumeric, underscore, must start with letter)'); /** Record ID schema */ export const RecordIdSchema = z .string() .min(1) .max(15) .regex(/^[a-zA-Z0-9]+$/) .describe('The unique identifier for a PocketBase record'); /** Record data schema */ export const RecordDataSchema = z .record(z.unknown()) .describe('JSON object containing the record data fields'); /** Query filter schema */ export const QueryFilterSchema = z .string() .optional() .describe('PocketBase filter query string (e.g., "status = true && created >= @now")'); /** Sort criteria schema */ export const SortCriteriaSchema = z .string() .optional() .describe('Sort criteria (e.g., "-created,+name" for descending created, ascending name)'); /** Page number schema */ export const PageNumberSchema = z .number() .int() .positive() .optional() .describe('Page number for pagination (starting from 1)'); /** Records per page schema */ export const PerPageSchema = z .number() .int() .min(1) .max(500) .optional() .describe('Number of records per page (1-500)'); /** Email address schema */ export const EmailAddressSchema = z .string() .email() .describe('Valid email address'); /** Email template schema */ export const EmailTemplateSchema = z .string() .min(1) .describe('Name of the email template to use'); /** Stripe amount schema */ export const StripeAmountSchema = z .number() .int() .positive() .describe('Amount in cents (e.g., 2000 for $20.00)'); /** Currency code schema */ export const CurrencyCodeSchema = z .string() .length(3) .regex(/^[A-Z]{3}$/) .describe('Three-letter currency code (e.g., USD, EUR, GBP)'); /** * PocketBase MCP Agent following Cloudflare best practices * * Key improvements: * - Individual Zod schemas for better LLM understanding * - Proper error handling patterns from official Cloudflare MCP servers * - Standard tool registration structure * - Efficient state management with Agent class */ export class PocketBaseMCPAgentBestPractices extends Agent<Env, State> { server = new McpServer({ name: "pocketbase-server", version: "0.1.0", }); // Initial state following Agent patterns initialState: State = { pocketbaseInitialized: false, isAuthenticated: false, discoveryMode: false, customHeaders: {}, realtimeSubscriptions: [], lastActivityTime: Date.now() }; // Private instances private pb?: PocketBase; private stripeService?: StripeService; private emailService?: EmailService; /** * Initialize the agent - called automatically by the Agents framework */ async init(): Promise<void> { await this.initializePocketBase(); await this.initializeServices(); this.registerTools(); this.registerResources(); this.registerPrompts(); } /** * Initialize PocketBase connection */ private async initializePocketBase(): Promise<void> { try { const pocketbaseUrl = this.env.POCKETBASE_URL; if (!pocketbaseUrl) { this.setState({ ...this.state, discoveryMode: true }); return; } // Custom headers from state const options: any = {}; if (Object.keys(this.state.customHeaders).length > 0) { // Apply custom headers if any are set options.headers = { ...options.headers, ...this.state.customHeaders }; } this.pb = new PocketBase(pocketbaseUrl, options); // Authenticate if credentials are provided const adminEmail = this.env.POCKETBASE_ADMIN_EMAIL; const adminPassword = this.env.POCKETBASE_ADMIN_PASSWORD; if (adminEmail && adminPassword) { try { await this.pb.collection('_superusers').authWithPassword(adminEmail, adminPassword); this.setState({ ...this.state, pocketbaseInitialized: true, isAuthenticated: true }); } catch (authError: any) { console.warn('Admin authentication failed:', authError.message); this.setState({ ...this.state, pocketbaseInitialized: true, isAuthenticated: false }); } } else { this.setState({ ...this.state, pocketbaseInitialized: true, isAuthenticated: false }); } } catch (error: any) { console.error('PocketBase initialization failed:', error.message); this.setState({ ...this.state, discoveryMode: true }); } } /** * Initialize additional services */ private async initializeServices(): Promise<void> { if (!this.pb) return; // Initialize Stripe service if (this.env.STRIPE_SECRET_KEY) { try { this.stripeService = new StripeService(this.pb); } catch (error) { console.warn('Stripe service initialization failed:', error); } } // Initialize Email service if (this.env.EMAIL_SERVICE || this.env.SMTP_HOST) { try { this.emailService = new EmailService(this.pb); } catch (error) { console.warn('Email service initialization failed:', error); } } } /** * Register all MCP tools following Cloudflare patterns */ private registerTools(): void { // PocketBase CRUD operations this.registerPocketBaseTools(); // Stripe tools if (this.stripeService) { this.registerStripeTools(); } // Email tools if (this.emailService) { this.registerEmailTools(); } // Utility tools this.registerUtilityTools(); } /** * Register PocketBase CRUD tools */ private registerPocketBaseTools(): void { // List collections this.server.tool( 'pocketbase_list_collections', 'List all available PocketBase collections with their schemas and metadata', {}, async () => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } const collections = await this.pb.collections.getFullList(200); return { content: [{ type: 'text', text: JSON.stringify({ success: true, collections: collections.map(col => ({ id: col.id, name: col.name, type: col.type, schema: col.schema, listRule: col.listRule, viewRule: col.viewRule, createRule: col.createRule, updateRule: col.updateRule, deleteRule: col.deleteRule })) }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create record this.server.tool( 'pocketbase_create_record', 'Create a new record in a PocketBase collection with specified data', { collection: CollectionNameSchema, data: RecordDataSchema }, async ({ collection, data }) => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } const record = await this.pb.collection(collection).create(data); return { content: [{ type: 'text', text: JSON.stringify({ success: true, record: { id: record.id, ...record } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Get record this.server.tool( 'pocketbase_get_record', 'Retrieve a specific record by ID from a PocketBase collection', { collection: CollectionNameSchema, id: RecordIdSchema }, async ({ collection, id }) => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } const record = await this.pb.collection(collection).getOne(id); return { content: [{ type: 'text', text: JSON.stringify({ success: true, record }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // List records with advanced filtering this.server.tool( 'pocketbase_list_records', 'List records from a PocketBase collection with optional filtering, sorting, and pagination', { collection: CollectionNameSchema, filter: QueryFilterSchema, sort: SortCriteriaSchema, page: PageNumberSchema, perPage: PerPageSchema }, async ({ collection, filter, sort, page, perPage }) => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } const options: any = {}; if (filter) options.filter = filter; if (sort) options.sort = sort; if (page) options.page = page; if (perPage) options.perPage = perPage; const records = await this.pb.collection(collection).getList( page || 1, perPage || 30, options ); return { content: [{ type: 'text', text: JSON.stringify({ success: true, page: records.page, perPage: records.perPage, totalItems: records.totalItems, totalPages: records.totalPages, items: records.items }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Update record this.server.tool( 'pocketbase_update_record', 'Update an existing record in a PocketBase collection with new data', { collection: CollectionNameSchema, id: RecordIdSchema, data: RecordDataSchema }, async ({ collection, id, data }) => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } const record = await this.pb.collection(collection).update(id, data); return { content: [{ type: 'text', text: JSON.stringify({ success: true, record }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Delete record this.server.tool( 'pocketbase_delete_record', 'Delete a specific record by ID from a PocketBase collection', { collection: CollectionNameSchema, id: RecordIdSchema }, async ({ collection, id }) => { try { if (!this.pb) { return this.createErrorResponse('PocketBase not initialized'); } await this.pb.collection(collection).delete(id); return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: `Record ${id} deleted successfully from ${collection}` }) }] }; } catch (error) { return this.createErrorResponse(error); } } ); } /** * Register Stripe payment tools */ private registerStripeTools(): void { if (!this.stripeService) return; // Create Payment Intent this.server.tool( 'stripe_create_payment', 'Create a new Stripe payment intent for processing payments', { amount: StripeAmountSchema, currency: CurrencyCodeSchema, description: z.string().optional().describe('Optional description for the payment') }, async ({ amount, currency, description }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const paymentIntent = await this.stripeService.createPaymentIntent({ amount, currency, description }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, paymentIntent: { paymentIntentId: paymentIntent.paymentIntentId, clientSecret: paymentIntent.clientSecret } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Product this.server.tool( 'stripe_create_product', 'Create a new product in Stripe for selling', { name: z.string().describe('Product name'), description: z.string().optional().describe('Product description'), price: StripeAmountSchema.describe('Price in cents'), currency: CurrencyCodeSchema.optional().describe('Currency code'), interval: z.enum(['month', 'year', 'week', 'day']).optional().describe('Billing interval for subscriptions') }, async ({ name, description, price, currency, interval }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const product = await this.stripeService.createProduct({ name, description, price, currency, interval }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, product }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Customer this.server.tool( 'stripe_create_customer', 'Create a new customer in Stripe', { email: EmailAddressSchema, name: z.string().optional().describe('Customer name'), metadata: z.record(z.string()).optional().describe('Custom metadata') }, async ({ email, name, metadata }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const customer = await this.stripeService.createCustomer({ email, name, metadata }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, customer }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Retrieve Customer this.server.tool( 'stripe_get_customer', 'Retrieve a customer from Stripe by ID', { customerId: z.string().describe('Stripe customer ID') }, async ({ customerId }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const customer = await this.stripeService.retrieveCustomer(customerId); return { content: [{ type: 'text', text: JSON.stringify({ success: true, customer }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Update Customer this.server.tool( 'stripe_update_customer', 'Update an existing customer in Stripe', { customerId: z.string().describe('Stripe customer ID'), email: EmailAddressSchema.optional(), name: z.string().optional().describe('Customer name'), metadata: z.record(z.string()).optional().describe('Custom metadata') }, async ({ customerId, email, name, metadata }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const customer = await this.stripeService.updateCustomer(customerId, { email, name, metadata }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, customer }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Cancel Subscription this.server.tool( 'stripe_cancel_subscription', 'Cancel a Stripe subscription', { subscriptionId: z.string().describe('Stripe subscription ID'), cancelAtPeriodEnd: z.boolean().optional().describe('Whether to cancel at period end or immediately') }, async ({ subscriptionId, cancelAtPeriodEnd }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const subscription = await this.stripeService.cancelSubscription(subscriptionId, cancelAtPeriodEnd); return { content: [{ type: 'text', text: JSON.stringify({ success: true, subscription }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Checkout Session this.server.tool( 'stripe_create_checkout_session', 'Create a Stripe Checkout session for payment', { priceId: z.string().describe('Stripe price ID'), successUrl: z.string().url().describe('Success redirect URL'), cancelUrl: z.string().url().describe('Cancel redirect URL'), customerId: z.string().optional().describe('Stripe customer ID'), mode: z.enum(['payment', 'subscription', 'setup']).optional().describe('Checkout mode') }, async ({ priceId, successUrl, cancelUrl, customerId, mode }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const session = await this.stripeService.createCheckoutSession({ priceId, successUrl, cancelUrl, customerId, mode }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, session: { id: session.sessionId, url: session.url } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Payment Method this.server.tool( 'stripe_create_payment_method', 'Create a new payment method in Stripe', { type: z.enum(['card', 'us_bank_account', 'sepa_debit']).describe('Payment method type') }, async ({ type }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const paymentMethod = await this.stripeService.createPaymentMethod({ type }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, paymentMethod }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // List Payment Methods this.server.tool( 'stripe_list_payment_methods', 'List payment methods for a customer', { customerId: z.string().describe('Stripe customer ID'), type: z.string().optional().describe('Payment method type filter') }, async ({ customerId, type }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const paymentMethods = await this.stripeService.listPaymentMethods(customerId, type); return { content: [{ type: 'text', text: JSON.stringify({ success: true, paymentMethods }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Setup Intent this.server.tool( 'stripe_create_setup_intent', 'Create a Setup Intent for saving payment methods', { customerId: z.string().describe('Stripe customer ID'), paymentMethodTypes: z.array(z.string()).optional().describe('Allowed payment method types') }, async ({ customerId, paymentMethodTypes }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const setupIntent = await this.stripeService.createSetupIntent({ customerId, paymentMethodTypes }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, setupIntent }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Payment Link this.server.tool( 'stripe_create_payment_link', 'Create a payment link for products', { priceId: z.string().describe('Stripe price ID'), quantity: z.number().optional().describe('Quantity of the product'), metadata: z.record(z.string()).optional().describe('Custom metadata') }, async ({ priceId, quantity, metadata }) => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const paymentLink = await this.stripeService.createPaymentLink({ lineItems: [{ price: priceId, quantity: quantity || 1 }], metadata }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, paymentLink }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Sync Products this.server.tool( 'stripe_sync_products', 'Sync Stripe products with PocketBase database', {}, async () => { try { if (!this.stripeService) { return this.createErrorResponse('Stripe service not available'); } const result = await this.stripeService.syncProducts(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, syncResult: result }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); } /** * Register email tools */ private registerEmailTools(): void { if (!this.emailService) return; // Send Templated Email this.server.tool( 'email_send_templated', 'Send a templated email using the configured email service', { template: EmailTemplateSchema, to: EmailAddressSchema, from: EmailAddressSchema.optional(), subject: z.string().optional().describe('Custom email subject (overrides template subject)'), variables: z.record(z.unknown()).optional().describe('Template variables for personalization') }, async ({ template, to, from, subject, variables }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.sendTemplatedEmail({ template, to, from, customSubject: subject, variables }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, emailLog: { id: result.id, to: result.to, subject: result.subject, status: result.status, sentAt: result.created } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Send Custom Email this.server.tool( 'email_send_custom', 'Send a custom email with specified content', { to: EmailAddressSchema, from: EmailAddressSchema.optional(), subject: z.string().describe('Email subject'), htmlBody: z.string().optional().describe('HTML email body'), textBody: z.string().optional().describe('Plain text email body') }, async ({ to, from, subject, htmlBody, textBody }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.sendCustomEmail({ to, from, subject, html: htmlBody || '', text: textBody }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, emailLog: { id: result.id, to: result.to, subject: result.subject, status: result.status, sentAt: result.created } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Email Template this.server.tool( 'email_create_template', 'Create a new email template in the database', { name: z.string().describe('Template name/identifier'), subject: z.string().describe('Email subject'), htmlBody: z.string().describe('HTML template body'), textBody: z.string().optional().describe('Plain text template body'), variables: z.array(z.string()).optional().describe('List of template variables') }, async ({ name, subject, htmlBody, textBody, variables }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const template = await this.emailService.createTemplate({ name, subject, htmlContent: htmlBody, textContent: textBody, variables }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, template }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Get Email Template this.server.tool( 'email_get_template', 'Retrieve an email template by name', { name: EmailTemplateSchema }, async ({ name }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const template = await this.emailService.getTemplate(name); return { content: [{ type: 'text', text: JSON.stringify({ success: true, template }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Update Email Template this.server.tool( 'email_update_template', 'Update an existing email template', { name: EmailTemplateSchema, subject: z.string().optional().describe('Email subject'), htmlBody: z.string().optional().describe('HTML template body'), textBody: z.string().optional().describe('Plain text template body'), variables: z.array(z.string()).optional().describe('List of template variables') }, async ({ name, subject, htmlBody, textBody, variables }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const template = await this.emailService.updateTemplate(name, { subject, htmlContent: htmlBody, textContent: textBody, variables }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, template }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Test Email Connection this.server.tool( 'email_test_connection', 'Test the email service connection and configuration', {}, async () => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.testConnection(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, connectionTest: result }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Send Enhanced Templated Email this.server.tool( 'email_send_enhanced_templated', 'Send a templated email with enhanced features (tracking, scheduling, etc.)', { template: EmailTemplateSchema, to: EmailAddressSchema, from: EmailAddressSchema.optional(), subject: z.string().optional().describe('Custom email subject'), variables: z.record(z.unknown()).optional().describe('Template variables'), trackOpens: z.boolean().optional().describe('Enable open tracking'), trackClicks: z.boolean().optional().describe('Enable click tracking'), tags: z.array(z.string()).optional().describe('Email tags for categorization') }, async ({ template, to, from, subject, variables, trackOpens, trackClicks, tags }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.sendEnhancedTemplatedEmail({ template, to, from, customSubject: subject, variables, trackingSettings: trackOpens || trackClicks ? { openTracking: trackOpens, clickTracking: trackClicks } : undefined, categories: tags }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, emailLog: { id: result.id, to: result.to, subject: result.subject, status: result.status, sentAt: result.created } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Schedule Templated Email this.server.tool( 'email_schedule_templated', 'Schedule a templated email to be sent at a specific time', { template: EmailTemplateSchema, to: EmailAddressSchema, from: EmailAddressSchema.optional(), subject: z.string().optional().describe('Custom email subject'), variables: z.record(z.unknown()).optional().describe('Template variables'), scheduledFor: z.string().describe('ISO 8601 datetime string for when to send'), timezone: z.string().optional().describe('Timezone for scheduling (e.g., "America/New_York")') }, async ({ template, to, from, subject, variables, scheduledFor, timezone }) => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.scheduleTemplatedEmail({ template, to, from, customSubject: subject, variables, sendAt: new Date(scheduledFor), categories: timezone ? [timezone] : undefined }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, scheduledEmail: { id: result.id, to: result.to, subject: result.subject, status: result.status, createdAt: result.created } }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); // Create Default Templates this.server.tool( 'email_create_default_templates', 'Create a set of default email templates for common use cases', {}, async () => { try { if (!this.emailService) { return this.createErrorResponse('Email service not available'); } const result = await this.emailService.createDefaultTemplates(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, createdTemplates: result }, null, 2) }] }; } catch (error) { return this.createErrorResponse(error); } } ); } /** * Register utility tools */ private registerUtilityTools(): void { this.server.tool( 'pocketbase_get_status', 'Get the current status and configuration of the PocketBase MCP server', {}, async () => { return { content: [{ type: 'text', text: JSON.stringify({ success: true, status: { state: this.state, capabilities: { pocketbaseUrl: Boolean(this.env.POCKETBASE_URL), hasAdminAuth: Boolean(this.env.POCKETBASE_ADMIN_EMAIL), hasStripe: Boolean(this.env.STRIPE_SECRET_KEY), hasEmail: Boolean(this.env.EMAIL_SERVICE || this.env.SMTP_HOST) }, timestamp: new Date().toISOString() } }, null, 2) }] }; } ); } /** * Register MCP resources */ private registerResources(): void { // Resources would be registered here with proper callback functions // Example: this.server.resource('name', 'uri', async (uri) => { ... }); } /** * Register MCP prompts */ private registerPrompts(): void { // Prompts would be registered here with proper callback functions // Example: this.server.prompt('name', 'description', async (extra) => { ... }); } /** * Create standardized error response following Cloudflare patterns */ private createErrorResponse(error: unknown): { content: Array<{ type: 'text'; text: string }> } { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: errorMessage, timestamp: new Date().toISOString() }) }] }; } /** * Handle state updates (called by Agents framework) */ onStateUpdate(state: State | undefined, source: any): void { console.log('State updated:', { state, source }); } } export default PocketBaseMCPAgentBestPractices;

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/DynamicEndpoints/advanced-pocketbase-mcp-server'

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