Skip to main content
Glama
fadlee

PocketBase MCP Server

by fadlee
analysis.ts5.46 kB
import type PocketBase from "pocketbase"; import type { ToolHandler, AnalyzeCollectionDataArgs, QueryCollectionArgs } from "../../types/index.js"; import { handlePocketBaseError } from "../../utils/errors.js"; import { createJsonResponse } from "../../utils/response.js"; import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; /** * Analyze data patterns and provide insights about a collection */ export function createAnalyzeCollectionDataHandler(pb: PocketBase): ToolHandler { return async (args: AnalyzeCollectionDataArgs) => { try { const { collection, options = {} } = args; const sampleSize = options.sampleSize || 100; // Get collection info and records const collectionInfo = await pb.collections.getOne(collection); const records = await pb .collection(collection) .getList(1, sampleSize); // Initialize analysis structure const analysis = { collectionName: collection, recordCount: records.totalItems, fields: [] as any[], insights: [] as string[], }; if (records.items.length === 0) { analysis.insights.push("No records available for analysis"); return createJsonResponse(analysis); } // Analyze each field const fields = collectionInfo.fields || []; for (const field of fields) { if (options.fields && !options.fields.includes(field.name)) { continue; } const fieldAnalysis = { name: field.name, type: field.type, nonNullValues: 0, uniqueValues: new Set(), min: null as any, max: null as any, }; // Analyze field values for (const record of records.items) { const value = record[field.name]; if (value !== null && value !== undefined) { fieldAnalysis.nonNullValues++; fieldAnalysis.uniqueValues.add(JSON.stringify(value)); // For numeric fields, track min/max if (field.type === "number") { if (fieldAnalysis.min === null || value < fieldAnalysis.min) { fieldAnalysis.min = value; } if (fieldAnalysis.max === null || value > fieldAnalysis.max) { fieldAnalysis.max = value; } } } } // Process analysis results const processedAnalysis = { ...fieldAnalysis, uniqueValueCount: fieldAnalysis.uniqueValues.size, fillRate: `${( (fieldAnalysis.nonNullValues / records.items.length) * 100 ).toFixed(2)}%`, uniqueValues: undefined, // Remove the Set before serializing }; analysis.fields.push(processedAnalysis); // Generate insights if ( processedAnalysis.uniqueValueCount === records.items.length && records.items.length > 5 ) { analysis.insights.push( `Field '${field.name}' contains all unique values, consider using it as an identifier.` ); } if (processedAnalysis.nonNullValues === 0) { analysis.insights.push( `Field '${field.name}' has no values. Consider removing it or ensuring it's populated.` ); } } return createJsonResponse(analysis); } catch (error: unknown) { throw handlePocketBaseError("analyze collection data", error); } }; } /** * Advanced query with filtering, sorting, and aggregation */ export function createQueryCollectionHandler(pb: PocketBase): ToolHandler { return async (args: QueryCollectionArgs) => { try { const collection = pb.collection(args.collection); const options: any = {}; if (args.filter) options.filter = args.filter; if (args.sort) options.sort = args.sort; if (args.expand) options.expand = args.expand; const records = await collection.getList(1, 100, options); const result: any = { items: records.items }; if (args.aggregate) { const aggregations: any = {}; for (const [name, expr] of Object.entries(args.aggregate)) { const [func, field] = (expr as string).split("("); const cleanField = field.replace(")", ""); switch (func) { case "sum": aggregations[name] = records.items.reduce( (sum: number, record: any) => sum + (record[cleanField] || 0), 0 ); break; case "avg": aggregations[name] = records.items.reduce( (sum: number, record: any) => sum + (record[cleanField] || 0), 0 ) / records.items.length; break; case "count": aggregations[name] = records.items.length; break; default: throw new McpError( ErrorCode.InvalidParams, `Unsupported aggregation function: ${func}` ); } } result.aggregations = aggregations; } return createJsonResponse(result); } catch (error: unknown) { throw handlePocketBaseError("query collection", error); } }; }

Implementation Reference

Latest Blog Posts

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