mirror of
https://github.com/zhigang1992/pocketbase-mcp.git
synced 2026-01-12 09:14:16 +08:00
First commit to public
This commit is contained in:
130
.gitignore
vendored
Normal file
130
.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
21
LICENSE copy
Normal file
21
LICENSE copy
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
285
README.md
285
README.md
@@ -1 +1,284 @@
|
||||
# pocketbase-mcp
|
||||
# PocketBase MCP Server
|
||||
|
||||
This is an MCP server that interacts with a PocketBase instance. It allows you to fetch, list, create, update, and manage records and files in your PocketBase collections.
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Clone the repository (if you haven't already):**
|
||||
```bash
|
||||
git clone <repository_url>
|
||||
cd pocketbase-mcp
|
||||
```
|
||||
2. **Install dependencies:**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
3. **Build the server:**
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
This compiles the TypeScript code to JavaScript in the `build/` directory and makes the entry point executable.
|
||||
|
||||
## Configuration
|
||||
|
||||
This server requires the following environment variables to be set:
|
||||
|
||||
- `POCKETBASE_API_URL`: The URL of your PocketBase instance (e.g., `http://127.0.0.1:8090`). Defaults to `http://127.0.0.1:8090` if not set.
|
||||
- `POCKETBASE_ADMIN_TOKEN`: An admin authentication token for your PocketBase instance. **This is required.** You can generate this from your PocketBase admin UI.
|
||||
|
||||
These variables need to be configured when adding the server to Cline (see Cline Installation section).
|
||||
|
||||
## Available Tools
|
||||
|
||||
The server provides the following tools:
|
||||
|
||||
- **fetch_record**: Fetch a single record from a PocketBase collection by ID.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the record to fetch."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **list_records**: List records from a PocketBase collection. Supports pagination, filtering, sorting, and expanding relations.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"page": {
|
||||
"type": "number",
|
||||
"description": "Page number (defaults to 1).",
|
||||
"minimum": 1
|
||||
},
|
||||
"perPage": {
|
||||
"type": "number",
|
||||
"description": "Items per page (defaults to 25).",
|
||||
"minimum": 1,
|
||||
"maximum": 100
|
||||
},
|
||||
"filter": {
|
||||
"type": "string",
|
||||
"description": "Filter string for the PocketBase query."
|
||||
},
|
||||
"sort": {
|
||||
"type": "string",
|
||||
"description": "Sort string for the PocketBase query (e.g., \\"fieldName,-otherFieldName\\")."
|
||||
},
|
||||
"expand": {
|
||||
"type": "string",
|
||||
"description": "Expand string for the PocketBase query (e.g., \\"relation1,relation2.subRelation\\")."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **create_record**: Create a new record in a PocketBase collection.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "The data for the new record.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection",
|
||||
"data"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **update_record**: Update an existing record in a PocketBase collection.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the record to update."
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "The data to update.",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection",
|
||||
"id",
|
||||
"data"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **get_collection_schema**: Get the schema of a PocketBase collection.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **upload_file**: Upload a file to a specific field in a PocketBase collection record.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"recordId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the record to upload the file to."
|
||||
},
|
||||
"fileField": {
|
||||
"type": "string",
|
||||
"description": "The name of the file field in the PocketBase collection."
|
||||
},
|
||||
"fileContent": {
|
||||
"type": "string",
|
||||
"description": "The content of the file to upload."
|
||||
},
|
||||
"fileName": {
|
||||
"type": "string",
|
||||
"description": "The name of the file."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"fileContent",
|
||||
"fileName"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **list_collections**: List all collections in the PocketBase instance.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
- **download_file**: Get the download URL for a file stored in a PocketBase collection record.
|
||||
- *Input Schema*:
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collection": {
|
||||
"type": "string",
|
||||
"description": "The name of the PocketBase collection."
|
||||
},
|
||||
"recordId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the record to download the file from."
|
||||
},
|
||||
"fileField": {
|
||||
"type": "string",
|
||||
"description": "The name of the file field in the PocketBase collection."
|
||||
},
|
||||
"downloadPath": {
|
||||
"type": "string",
|
||||
"description": "The path where the downloaded file should be saved (Note: This tool currently returns the URL, download must be handled separately)."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"downloadPath"
|
||||
]
|
||||
}
|
||||
```
|
||||
*Note: This tool returns the file URL. The actual download needs to be performed by the client using this URL.*
|
||||
|
||||
## Cline Installation
|
||||
|
||||
To use this server with Cline, you need to add it to your MCP settings file (`cline_mcp_settings.json`).
|
||||
|
||||
1. **Locate your Cline MCP settings file:**
|
||||
* Typically found at `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` on Linux/macOS.
|
||||
* Or `~/Library/Application Support/Claude/claude_desktop_config.json` if using the Claude desktop app on macOS.
|
||||
|
||||
2. **Edit the file and add the following configuration under the `mcpServers` key.** Replace `/path/to/pocketbase-mcp` with the actual absolute path to this project directory on your system. Also, replace `<YOUR_POCKETBASE_API_URL>` and `<YOUR_POCKETBASE_ADMIN_TOKEN>` with your actual PocketBase URL and admin token.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
// ... other servers might be listed here ...
|
||||
|
||||
"pocketbase-mcp": {
|
||||
"command": "node",
|
||||
"args": ["/path/to/pocketbase-mcp/build/index.js"],
|
||||
"env": {
|
||||
"POCKETBASE_API_URL": "<YOUR_POCKETBASE_API_URL>", // e.g., "http://127.0.0.1:8090"
|
||||
"POCKETBASE_ADMIN_TOKEN": "<YOUR_POCKETBASE_ADMIN_TOKEN>"
|
||||
},
|
||||
"disabled": false, // Ensure it's enabled
|
||||
"autoApprove": [] // Default auto-approve settings
|
||||
}
|
||||
|
||||
// ... other servers might be listed here ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Save the settings file.** Cline should automatically detect the changes and connect to the server. You can then use the tools listed above.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `@modelcontextprotocol/sdk`
|
||||
- `pocketbase`
|
||||
- `typescript`
|
||||
- `ts-node` (dev dependency)
|
||||
- `@types/node` (dev dependency)
|
||||
|
||||
398
build/index.js
Executable file
398
build/index.js
Executable file
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/env node
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
||||
import PocketBase from 'pocketbase';
|
||||
const API_URL = process.env.POCKETBASE_API_URL || 'http://127.0.0.1:8090';
|
||||
const ADMIN_TOKEN = process.env.POCKETBASE_ADMIN_TOKEN;
|
||||
if (!ADMIN_TOKEN) {
|
||||
throw new Error('POCKETBASE_ADMIN_TOKEN environment variable is required');
|
||||
}
|
||||
class PocketBaseServer {
|
||||
constructor() {
|
||||
this.server = new Server({
|
||||
name: 'pocketbase-mcp',
|
||||
version: '0.1.0',
|
||||
}, {
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
});
|
||||
this.pb = new PocketBase(API_URL);
|
||||
if (ADMIN_TOKEN) {
|
||||
this.pb.authStore.save(ADMIN_TOKEN, null);
|
||||
}
|
||||
this.setupToolHandlers();
|
||||
// Error handling
|
||||
this.server.onerror = (error) => { console.error('[MCP Error]', error); };
|
||||
process.on('SIGINT', async () => {
|
||||
await this.server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
setupToolHandlers() {
|
||||
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: 'fetch_record',
|
||||
description: 'Fetch a single record from a PocketBase collection by ID.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to fetch.',
|
||||
},
|
||||
},
|
||||
required: ['collection', 'id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'list_records',
|
||||
description: 'List records from a PocketBase collection. Supports pagination using `page` and `perPage` parameters.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
description: 'Page number (defaults to 1).',
|
||||
minimum: 1
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
description: 'Items per page (defaults to 25).',
|
||||
minimum: 1,
|
||||
maximum: 100
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
description: 'Filter string for the PocketBase query.'
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
description: 'Sort string for the PocketBase query (e.g., "fieldName,-otherFieldName").'
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
description: 'Expand string for the PocketBase query (e.g., "relation1,relation2.subRelation").'
|
||||
}
|
||||
},
|
||||
required: ['collection'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'create_record',
|
||||
description: 'Create a new record in a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'The data for the new record.',
|
||||
additionalProperties: true
|
||||
},
|
||||
},
|
||||
required: ['collection', 'data'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'update_record',
|
||||
description: 'Update an existing record in a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to update.',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'The data to update.',
|
||||
additionalProperties: true
|
||||
},
|
||||
},
|
||||
required: ['collection', 'id', 'data'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_collection_schema',
|
||||
description: 'Get the schema of a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
},
|
||||
required: ['collection'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'upload_file',
|
||||
description: 'Upload a file to a PocketBase collection record.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to upload the file to.',
|
||||
},
|
||||
fileField: {
|
||||
type: 'string',
|
||||
description: 'The name of the file field in the PocketBase collection.',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
description: 'The content of the file to upload.',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
description: 'The name of the file.',
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"fileContent",
|
||||
"fileName"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'list_collections',
|
||||
description: 'List all collections in the PocketBase instance.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'download_file',
|
||||
description: 'Download a file from a PocketBase collection record.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to download the file from.',
|
||||
},
|
||||
fileField: {
|
||||
type: 'string',
|
||||
description: 'The name of the file field in the PocketBase collection.',
|
||||
},
|
||||
downloadPath: {
|
||||
type: 'string',
|
||||
description: 'The path where the downloaded file should be saved.',
|
||||
},
|
||||
},
|
||||
required: [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"downloadPath"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
}));
|
||||
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
try {
|
||||
const { name, arguments: args } = request.params;
|
||||
switch (name) {
|
||||
case 'fetch_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('id' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection or id");
|
||||
}
|
||||
const { collection, id } = args;
|
||||
const record = await this.pb.collection(collection).getOne(id);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'list_records': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection");
|
||||
}
|
||||
const { collection, page, perPage, filter, sort, expand } = args;
|
||||
const result = await this.pb.collection(collection).getList(page || 1, perPage || 25, {
|
||||
filter,
|
||||
sort,
|
||||
expand
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
content: result,
|
||||
text: JSON.stringify(result, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'create_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('data' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection or data");
|
||||
}
|
||||
const { collection, data } = args;
|
||||
const record = await this.pb.collection(collection).create(data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'update_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('id' in args) || !('data' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, id, or data");
|
||||
}
|
||||
const { collection, id, data } = args;
|
||||
const record = await this.pb.collection(collection).update(id, data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'get_collection_schema': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection");
|
||||
}
|
||||
const { collection } = args;
|
||||
const schema = await this.pb.collections.getOne(collection);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(schema, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'upload_file': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('recordId' in args) || !('fileField' in args) || !('fileContent' in args) || !('fileName' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, recordId, fileField, fileContent, or fileName");
|
||||
}
|
||||
const { collection, recordId, fileField, fileContent, fileName } = args;
|
||||
// Create a Blob from the file content
|
||||
const blob = new Blob([fileContent]);
|
||||
// Create a FormData object and append the file
|
||||
const formData = new FormData();
|
||||
formData.append(fileField, blob, fileName);
|
||||
// Update the record with the file
|
||||
const record = await this.pb.collection(collection).update(recordId, formData);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'download_file': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('recordId' in args) || !('fileField' in args) || !('downloadPath' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, recordId, fileField, or downloadPath");
|
||||
}
|
||||
const { collection, recordId, fileField, downloadPath } = args;
|
||||
// Fetch the record
|
||||
const record = await this.pb.collection(collection).getOne(recordId);
|
||||
// Get the file URL
|
||||
const fileUrl = this.pb.getFileUrl(record, record[fileField]);
|
||||
// Return the file URL to the user. They can use this URL to download the file.
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: fileUrl
|
||||
}
|
||||
]
|
||||
};
|
||||
// The following code is not possible because we cannot download files within the MCP server
|
||||
// // Download the file content
|
||||
// const response = await fetch(fileUrl);
|
||||
// const fileContent = await response.text();
|
||||
// // Save the file using the write_to_file tool
|
||||
// await write_to_file({ path: downloadPath, content: fileContent });
|
||||
// return {
|
||||
// content: [
|
||||
// {
|
||||
// type: 'text',
|
||||
// text: `File downloaded to ${downloadPath}`,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
}
|
||||
case 'list_collections': {
|
||||
const result = await this.pb.collections.getFullList({ sort: '-created' });
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(result, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: errorMessage,
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
async run() {
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
console.error('PocketBase MCP server running on stdio');
|
||||
}
|
||||
}
|
||||
const server = new PocketBaseServer();
|
||||
server.run().catch(console.error);
|
||||
1397
package-lock.json
generated
Normal file
1397
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "pocketbase-mcp",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"@types/node": "^22.13.10",
|
||||
"axios": "^1.8.3",
|
||||
"pocketbase": "^0.25.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
}
|
||||
440
src/index.ts
Normal file
440
src/index.ts
Normal file
@@ -0,0 +1,440 @@
|
||||
#!/usr/bin/env node
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ErrorCode,
|
||||
ListToolsRequestSchema,
|
||||
McpError,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const API_URL = process.env.POCKETBASE_API_URL || 'http://127.0.0.1:8090';
|
||||
const ADMIN_TOKEN = process.env.POCKETBASE_ADMIN_TOKEN;
|
||||
|
||||
if (!ADMIN_TOKEN) {
|
||||
throw new Error('POCKETBASE_ADMIN_TOKEN environment variable is required');
|
||||
}
|
||||
|
||||
interface ToolError {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: string
|
||||
}],
|
||||
isError: true
|
||||
}
|
||||
|
||||
class PocketBaseServer {
|
||||
private server: Server;
|
||||
private pb: PocketBase;
|
||||
|
||||
constructor() {
|
||||
this.server = new Server(
|
||||
{
|
||||
name: 'pocketbase-mcp',
|
||||
version: '0.1.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.pb = new PocketBase(API_URL);
|
||||
if (ADMIN_TOKEN) {
|
||||
this.pb.authStore.save(ADMIN_TOKEN, null);
|
||||
}
|
||||
|
||||
this.setupToolHandlers();
|
||||
|
||||
// Error handling
|
||||
this.server.onerror = (error: Error) => { console.error('[MCP Error]', error) };
|
||||
process.on('SIGINT', async () => {
|
||||
await this.server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private setupToolHandlers() {
|
||||
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: 'fetch_record',
|
||||
description: 'Fetch a single record from a PocketBase collection by ID.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to fetch.',
|
||||
},
|
||||
},
|
||||
required: ['collection', 'id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'list_records',
|
||||
description: 'List records from a PocketBase collection. Supports pagination using `page` and `perPage` parameters.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
description: 'Page number (defaults to 1).',
|
||||
minimum: 1
|
||||
},
|
||||
perPage: {
|
||||
type: 'number',
|
||||
description: 'Items per page (defaults to 25).',
|
||||
minimum: 1,
|
||||
maximum: 100
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
description: 'Filter string for the PocketBase query.'
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
description: 'Sort string for the PocketBase query (e.g., "fieldName,-otherFieldName").'
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
description: 'Expand string for the PocketBase query (e.g., "relation1,relation2.subRelation").'
|
||||
}
|
||||
},
|
||||
required: ['collection'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'create_record',
|
||||
description: 'Create a new record in a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'The data for the new record.',
|
||||
additionalProperties: true
|
||||
},
|
||||
},
|
||||
required: ['collection', 'data'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'update_record',
|
||||
description: 'Update an existing record in a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to update.',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'The data to update.',
|
||||
additionalProperties: true
|
||||
},
|
||||
},
|
||||
required: ['collection', 'id', 'data'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_collection_schema',
|
||||
description: 'Get the schema of a PocketBase collection.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
},
|
||||
required: ['collection'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'upload_file',
|
||||
description: 'Upload a file to a PocketBase collection record.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to upload the file to.',
|
||||
},
|
||||
fileField: {
|
||||
type: 'string',
|
||||
description: 'The name of the file field in the PocketBase collection.',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
description: 'The content of the file to upload.',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
description: 'The name of the file.',
|
||||
}
|
||||
},
|
||||
required: [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"fileContent",
|
||||
"fileName"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'list_collections',
|
||||
description: 'List all collections in the PocketBase instance.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'download_file',
|
||||
description: 'Download a file from a PocketBase collection record.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'The name of the PocketBase collection.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
description: 'The ID of the record to download the file from.',
|
||||
},
|
||||
fileField: {
|
||||
type: 'string',
|
||||
description: 'The name of the file field in the PocketBase collection.',
|
||||
},
|
||||
downloadPath: {
|
||||
type: 'string',
|
||||
description: 'The path where the downloaded file should be saved.',
|
||||
},
|
||||
},
|
||||
required: [
|
||||
"collection",
|
||||
"recordId",
|
||||
"fileField",
|
||||
"downloadPath"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
}));
|
||||
|
||||
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
try {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
switch (name) {
|
||||
case 'fetch_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('id' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection or id");
|
||||
}
|
||||
const { collection, id } = args as { collection: string; id: string; };
|
||||
const record = await this.pb.collection(collection).getOne(id);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'list_records': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection");
|
||||
}
|
||||
const { collection, page, perPage, filter, sort, expand } = args as { collection: string; page?: number; perPage?: number; filter?: string; sort?: string; expand?: string; };
|
||||
const result = await this.pb.collection(collection).getList(page || 1, perPage || 25, {
|
||||
filter,
|
||||
sort,
|
||||
expand
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
content: result,
|
||||
text: JSON.stringify(result, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'create_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('data' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection or data");
|
||||
}
|
||||
const { collection, data } = args as {collection: string, data: any};
|
||||
const record = await this.pb.collection(collection).create(data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'update_record': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('id' in args) || !('data' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, id, or data");
|
||||
}
|
||||
const { collection, id, data } = args as {collection: string, id: string, data: any};
|
||||
const record = await this.pb.collection(collection).update(id, data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'get_collection_schema': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection");
|
||||
}
|
||||
const { collection } = args as { collection: string };
|
||||
const schema = await this.pb.collections.getOne(collection);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(schema, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'upload_file': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('recordId' in args) || !('fileField' in args) || !('fileContent' in args) || !('fileName' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, recordId, fileField, fileContent, or fileName");
|
||||
}
|
||||
const { collection, recordId, fileField, fileContent, fileName } = args as { collection: string; recordId: string; fileField: string; fileContent: string; fileName: string };
|
||||
|
||||
// Create a Blob from the file content
|
||||
const blob = new Blob([fileContent]);
|
||||
|
||||
// Create a FormData object and append the file
|
||||
const formData = new FormData();
|
||||
formData.append(fileField, blob, fileName);
|
||||
|
||||
// Update the record with the file
|
||||
const record = await this.pb.collection(collection).update(recordId, formData);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(record, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'download_file': {
|
||||
if (!args || typeof args !== 'object' || !('collection' in args) || !('recordId' in args) || !('fileField' in args) || !('downloadPath' in args)) {
|
||||
throw new McpError(ErrorCode.InvalidParams, "Missing collection, recordId, fileField, or downloadPath");
|
||||
}
|
||||
const { collection, recordId, fileField, downloadPath } = args as { collection: string; recordId: string; fileField: string; downloadPath: string; };
|
||||
|
||||
// Fetch the record
|
||||
const record = await this.pb.collection(collection).getOne(recordId);
|
||||
|
||||
// Get the file URL
|
||||
const fileUrl = this.pb.getFileUrl(record, record[fileField]);
|
||||
|
||||
// Return the file URL to the user. They can use this URL to download the file.
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: fileUrl
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// The following code is not possible because we cannot download files within the MCP server
|
||||
// // Download the file content
|
||||
// const response = await fetch(fileUrl);
|
||||
// const fileContent = await response.text();
|
||||
|
||||
// // Save the file using the write_to_file tool
|
||||
// await write_to_file({ path: downloadPath, content: fileContent });
|
||||
|
||||
// return {
|
||||
// content: [
|
||||
// {
|
||||
// type: 'text',
|
||||
// text: `File downloaded to ${downloadPath}`,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
}
|
||||
case 'list_collections': {
|
||||
const result = await this.pb.collections.getFullList({ sort: '-created' });
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(result, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
`Unknown tool: ${name}`
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: errorMessage,
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run() {
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
console.error('PocketBase MCP server running on stdio');
|
||||
}
|
||||
}
|
||||
|
||||
const server = new PocketBaseServer();
|
||||
server.run().catch(console.error);
|
||||
70
task.md
Normal file
70
task.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Task List
|
||||
|
||||
This file is used to track tasks. Tasks are marked with `[ ]` for incomplete and `[X]` for completed. The next task to be taken is the first one marked with `[ ]`. If that task has subtasks do the one with [ ], only marking the parent task as done when all subtasks for that parent task are with [X] marked.
|
||||
|
||||
**IMPORTANT: After completing each task or subtask, update this file immediately to reflect the changes (mark tasks as complete and add new tasks as needed).**
|
||||
|
||||
## General Tasks
|
||||
|
||||
- [X] **Enhance `list_records` tool:**
|
||||
- [X] Add support for filtering records based on field values.
|
||||
- [X] Implement pagination options for listing large datasets.
|
||||
- [X] Allow sorting of records by specific fields.
|
||||
- [X] Implement support for expanding relations in list results.
|
||||
- [ ] **Collection Management Tools:**
|
||||
- [X] Add a tool to list all collections in the PocketBase instance (`list_collections`).
|
||||
- [X] Implement a tool to get schema of a specific collection (`get_collection_schema`).
|
||||
- [ ] Implement tools for Collection Management:
|
||||
- [ ] Create and manage collections with custom schemas (`create_collection`, `update_collection`)
|
||||
- [ ] Migrate collection schemas with data preservation (`migrate_collection_schema`)
|
||||
- [ ] Advanced index management tools:
|
||||
- [ ] Create indexes (`create_index`)
|
||||
- [ ] Delete indexes (`delete_index`)
|
||||
- [ ] List indexes (`list_indexes`)
|
||||
- [ ] Implement schema validation and type safety (This might be more of an implementation detail within other tools, but could be a task to ensure it's properly handled)
|
||||
|
||||
- [ ] **File Handling Tools:**
|
||||
- [ ] Implement a tool to upload files to a PocketBase collection with file fields (`upload_file`).
|
||||
- [ ] Add a tool to download files from a PocketBase collection (`download_file`).
|
||||
|
||||
- [ ] **Record Operations:**
|
||||
- [ ] Implement a tool to delete a record from a PocketBase collection (`delete_record`).
|
||||
- [ ] Enhance Record Operations:
|
||||
- [ ] Implement advanced querying with aggregation (`list_records` with aggregation support)
|
||||
- [ ] Implement batch import/export capabilities:
|
||||
- [ ] Batch record import (`batch_import_records`)
|
||||
- [ ] Batch record export (`batch_export_records`)
|
||||
|
||||
- [ ] **User Management Tools:**
|
||||
- [ ] Implement User Management Tools:
|
||||
- [ ] User authentication and token management tools: (`create_user_token`, `verify_user_token`)
|
||||
- [ ] User account creation and management tools: (`create_user`, `update_user`, `delete_user`, `list_users`)
|
||||
- [ ] Password management tools: (`update_user_password`, `reset_user_password`)
|
||||
- [ ] Role-based access control tools (This might be complex and could be broken down further if needed)
|
||||
- [ ] Session handling (This might be more of an implementation detail, but could be a task if specific tools are needed)
|
||||
|
||||
- [ ] **Database Operation Tools:**
|
||||
- [ ] Implement Database Operation Tools:
|
||||
- [ ] Database backup and restore tools: (`backup_database`, `restore_database`)
|
||||
- [ ] Multiple export formats (`export_database_json`, `export_database_csv`)
|
||||
- [ ] Data migration tools (This is a broad task and might need further definition)
|
||||
- [ ] Index optimization tools (`optimize_indexes`)
|
||||
- [ ] Batch operations (This might overlap with Batch Record Operations, needs clarification)
|
||||
|
||||
- [ ] **Realtime Subscription Tools (Advanced):**
|
||||
- [ ] Explore adding tools to subscribe to realtime events for specific collections (`subscribe_collection`).
|
||||
- [ ] Implement a tool to unsubscribe from realtime events (`unsubscribe_collection`).
|
||||
- [ ] **Admin Authentication Tool (Optional):**
|
||||
- [ ] If not using impersonate token, add a tool to authenticate as admin and obtain an admin token (`admin_auth`).
|
||||
- [ ] **Documentation:**
|
||||
- [ ] Create a README.md file for the `pocketbase-mcp` server.
|
||||
- [ ] Document each tool with clear descriptions, input schemas, and example usage, including code examples.
|
||||
- [ ] **Improve Error Handling:**
|
||||
- [ ] Implement more specific error handling for PocketBase API interactions.
|
||||
- [ ] Provide more informative error messages to the user through the MCP tool responses, including PocketBase error details when available.
|
||||
- [ ] Handle rate limiting and retry mechanisms for API requests.
|
||||
- [ ] **Add Input Validation:**
|
||||
- [ ] Validate input parameters for all tools to ensure they conform to the input schemas.
|
||||
- [ ] Return specific error messages for invalid input, clearly indicating the invalid fields and expected format.
|
||||
- [ ] Validate data types and formats for create and update operations based on collection schema.
|
||||
- [ ] Add documentation on setting up authentication and environment variables.
|
||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"outDir": "build",
|
||||
"rootDir": "src",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user