import { z } from 'zod';
import { pocketBaseService, PocketBaseError } from '../pocketbase-service.js';
import { logger } from '../utils/logger.js';
import { ListRecordsParamsSchema, GetRecordParamsSchema, CreateRecordParamsSchema, UpdateRecordParamsSchema, DeleteRecordParamsSchema, } from '../types/mcp.js';
// Helper function to create tool results
function createResult(text, isError = false, meta) {
const result = {
content: [{ type: 'text', text }],
isError,
};
if (meta !== undefined) {
result._meta = meta;
}
return result;
}
// Helper function to validate parameters
function validateParams(schema, params) {
try {
return schema.parse(params);
}
catch (error) {
if (error instanceof z.ZodError) {
const errors = error.errors.map((err) => `${err.path.join('.')}: ${err.message}`);
throw new Error(`Invalid parameters:\n${errors.join('\n')}`);
}
throw error;
}
}
// List records tool
export const listRecordsTool = {
name: 'pb_records_list',
description: 'List records from a collection with optional filtering and pagination',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
page: {
type: 'number',
minimum: 1,
default: 1,
description: 'Page number',
},
perPage: {
type: 'number',
minimum: 1,
maximum: 500,
default: 30,
description: 'Items per page',
},
sort: {
type: 'string',
description: 'Sort criteria (e.g., "created", "-updated", "title")',
},
filter: {
type: 'string',
description: 'Filter criteria (e.g., "status = true")',
},
expand: {
type: 'string',
description: 'Relations to expand (e.g., "author,category")',
},
fields: {
type: 'string',
description: 'Fields to return (e.g., "id,title,created")',
},
},
required: ['collection'],
},
};
export async function handleListRecords(params) {
try {
const { collection, page, perPage, sort, filter, expand, fields, } = validateParams(ListRecordsParamsSchema, params);
logger.info('Processing list records request', {
collection,
page,
perPage,
sort,
filter,
expand,
fields,
});
const result = await pocketBaseService.listRecords(collection, {
page,
perPage,
sort,
filter,
expand,
fields,
});
const response = {
success: true,
collection,
pagination: {
page: result.page,
perPage: result.perPage,
totalItems: result.totalItems,
totalPages: result.totalPages,
},
records: result.items,
};
return createResult(`Found ${result.items.length} records in collection '${collection}' ` +
`(page ${page} of ${result.totalPages})`, false, response);
}
catch (error) {
logger.error('List records failed', error);
if (error instanceof PocketBaseError) {
return createResult(`List records failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`List records failed: ${message}`, true);
}
}
// Get record tool
export const getRecordTool = {
name: 'pb_records_get',
description: 'Get a specific record by ID from a collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
id: {
type: 'string',
description: 'Record ID',
},
expand: {
type: 'string',
description: 'Relations to expand (e.g., "author,category")',
},
fields: {
type: 'string',
description: 'Fields to return (e.g., "id,title,created")',
},
},
required: ['collection', 'id'],
},
};
export async function handleGetRecord(params) {
try {
const { collection, id, expand, fields } = validateParams(GetRecordParamsSchema, params);
logger.info('Processing get record request', { collection, id, expand, fields });
const record = await pocketBaseService.getRecord(collection, id, { expand, fields });
const result = {
success: true,
collection,
record,
};
return createResult(`Record retrieved from collection '${collection}': ${id}`, false, result);
}
catch (error) {
logger.error('Get record failed', error);
if (error instanceof PocketBaseError) {
return createResult(`Get record failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Get record failed: ${message}`, true);
}
}
// Create record tool
export const createRecordTool = {
name: 'pb_records_create',
description: 'Create a new record in a collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
data: {
type: 'object',
description: 'Record data to create',
},
expand: {
type: 'string',
description: 'Relations to expand in response',
},
fields: {
type: 'string',
description: 'Fields to return in response',
},
},
required: ['collection', 'data'],
},
};
export async function handleCreateRecord(params) {
try {
const { collection, data, expand, fields } = validateParams(CreateRecordParamsSchema, params);
logger.info('Processing create record request', { collection, data });
const record = await pocketBaseService.createRecord(collection, data, {
expand,
fields,
});
const result = {
success: true,
collection,
record,
};
return createResult(`Record created in collection '${collection}': ${record.id}`, false, result);
}
catch (error) {
logger.error('Create record failed', error);
if (error instanceof PocketBaseError) {
return createResult(`Create record failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Create record failed: ${message}`, true);
}
}
// Update record tool
export const updateRecordTool = {
name: 'pb_records_update',
description: 'Update an existing record in a collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
id: {
type: 'string',
description: 'Record ID to update',
},
data: {
type: 'object',
description: 'Record data to update',
},
expand: {
type: 'string',
description: 'Relations to expand in response',
},
fields: {
type: 'string',
description: 'Fields to return in response',
},
},
required: ['collection', 'id', 'data'],
},
};
export async function handleUpdateRecord(params) {
try {
const { collection, id, data, expand, fields } = validateParams(UpdateRecordParamsSchema, params);
logger.info('Processing update record request', { collection, id, data });
const record = await pocketBaseService.updateRecord(collection, id, data, {
expand,
fields,
});
const result = {
success: true,
collection,
record,
};
return createResult(`Record updated in collection '${collection}': ${id}`, false, result);
}
catch (error) {
logger.error('Update record failed', error);
if (error instanceof PocketBaseError) {
return createResult(`Update record failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Update record failed: ${message}`, true);
}
}
// Delete record tool
export const deleteRecordTool = {
name: 'pb_records_delete',
description: 'Delete a record from a collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
id: {
type: 'string',
description: 'Record ID to delete',
},
},
required: ['collection', 'id'],
},
};
export async function handleDeleteRecord(params) {
try {
const { collection, id } = validateParams(DeleteRecordParamsSchema, params);
logger.info('Processing delete record request', { collection, id });
const success = await pocketBaseService.deleteRecord(collection, id);
if (success) {
return createResult(`Record deleted from collection '${collection}': ${id}`, false, {
success: true,
collection,
deletedId: id,
});
}
return createResult('Failed to delete record', true);
}
catch (error) {
logger.error('Delete record failed', error);
if (error instanceof PocketBaseError) {
return createResult(`Delete record failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Delete record failed: ${message}`, true);
}
}
// Bulk create records tool
export const bulkCreateRecordsTool = {
name: 'pb_records_bulk_create',
description: 'Create multiple records in a collection at once',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name or ID',
},
records: {
type: 'array',
items: {
type: 'object',
},
description: 'Array of record data objects to create',
},
},
required: ['collection', 'records'],
},
};
export async function handleBulkCreateRecords(params) {
try {
const { collection, records } = params;
logger.info('Processing bulk create records request', {
collection,
count: records.length,
});
const createdRecords = [];
const errors = [];
for (let i = 0; i < records.length; i++) {
const recordData = records[i];
if (!recordData)
continue;
try {
const record = await pocketBaseService.createRecord(collection, recordData);
createdRecords.push(record);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
errors.push({
index: i,
data: recordData,
error: errorMessage,
});
}
}
const result = {
success: errors.length === 0,
collection,
totalRequested: records.length,
created: createdRecords.length,
failed: errors.length,
createdRecords,
errors,
};
const message = errors.length === 0
? `Successfully created ${createdRecords.length} records in collection '${collection}'`
: `Created ${createdRecords.length} records, ${errors.length} failed in collection '${collection}'`;
return createResult(message, errors.length > 0, result);
}
catch (error) {
logger.error('Bulk create records failed', error);
if (error instanceof PocketBaseError) {
return createResult(`Bulk create records failed: ${error.message}`, true, { error: error.message, status: error.status });
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Bulk create records failed: ${message}`, true);
}
}
// Export all record tools and handlers
export const recordTools = [
listRecordsTool,
getRecordTool,
createRecordTool,
updateRecordTool,
deleteRecordTool,
bulkCreateRecordsTool,
];
export const recordHandlers = {
pb_records_list: handleListRecords,
pb_records_get: handleGetRecord,
pb_records_create: handleCreateRecord,
pb_records_update: handleUpdateRecord,
pb_records_delete: handleDeleteRecord,
pb_records_bulk_create: handleBulkCreateRecords,
};
//# sourceMappingURL=records.js.map