agent-best-practices.js•44.5 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';
// 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 {
server = new McpServer({
name: "pocketbase-server",
version: "0.1.0",
});
// Initial state following Agent patterns
initialState = {
pocketbaseInitialized: false,
isAuthenticated: false,
discoveryMode: false,
customHeaders: {},
realtimeSubscriptions: [],
lastActivityTime: Date.now()
};
// Private instances
pb;
stripeService;
emailService;
/**
* Initialize the agent - called automatically by the Agents framework
*/
async init() {
await this.initializePocketBase();
await this.initializeServices();
this.registerTools();
this.registerResources();
this.registerPrompts();
}
/**
* Initialize PocketBase connection
*/
async initializePocketBase() {
try {
const pocketbaseUrl = this.env.POCKETBASE_URL;
if (!pocketbaseUrl) {
this.setState({
...this.state,
discoveryMode: true
});
return;
}
// Custom headers from state
const options = {};
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) {
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) {
console.error('PocketBase initialization failed:', error.message);
this.setState({
...this.state,
discoveryMode: true
});
}
}
/**
* Initialize additional services
*/
async initializeServices() {
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
*/
registerTools() {
// 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
*/
registerPocketBaseTools() {
// 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 = {};
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
*/
registerStripeTools() {
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
*/
registerEmailTools() {
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
*/
registerUtilityTools() {
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
*/
registerResources() {
// Resources would be registered here with proper callback functions
// Example: this.server.resource('name', 'uri', async (uri) => { ... });
}
/**
* Register MCP prompts
*/
registerPrompts() {
// Prompts would be registered here with proper callback functions
// Example: this.server.prompt('name', 'description', async (extra) => { ... });
}
/**
* Create standardized error response following Cloudflare patterns
*/
createErrorResponse(error) {
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, source) {
console.log('State updated:', { state, source });
}
}
export default PocketBaseMCPAgentBestPractices;