Skip to main content
Glama
Nam088

Multi-Database MCP Server

by Nam088
plugin-base.ts3.67 kB
import type { IPlugin, PluginConfig, PluginContext } from './types.js'; import { PluginMode } from './types.js'; import type { CallToolResult, ServerRequest, ServerNotification, } from '@modelcontextprotocol/sdk/types.js'; import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'; import type { ZodRawShape } from 'zod'; /** * Tool schema definition */ export type ToolSchema = { description: string; inputSchema: ZodRawShape; }; /** * Tool handler function type */ export type ToolHandler<TArgs = Record<string, unknown>> = ( args: TArgs, ) => CallToolResult | Promise<CallToolResult>; /** * Abstract base class for plugins * Provides common functionality and enforces interface implementation */ export abstract class PluginBase implements IPlugin { abstract readonly metadata: PluginConfig; protected context?: PluginContext; protected config: Record<string, unknown>; constructor(config?: Record<string, unknown>) { this.config = config ?? {}; } /** * Initialize plugin * Override this to add custom initialization logic */ initialize(context: PluginContext): Promise<void> | void { this.context = context; } /** * Register tools, resources, prompts * Must be implemented by subclasses */ abstract register(context: PluginContext): Promise<void> | void; /** * Cleanup resources * Override this to add custom cleanup logic */ cleanup(): Promise<void> | void { // Override this method to add custom cleanup logic } /** * Health check * Override this to add custom health checks */ healthCheck(): Promise<boolean> | boolean { return true; } /** * Get plugin context */ protected getContext(): PluginContext { if (!this.context) { throw new Error(`Plugin ${this.metadata.name} not initialized`); } return this.context; } /** * Get plugin configuration */ protected getConfig<T = Record<string, unknown>>(): T { return (this.config || {}) as T; } /** * Check if plugin is in readonly mode */ protected isReadonly(): boolean { return this.metadata.mode === PluginMode.READONLY; } /** * Check if plugin is in full mode */ protected isFullMode(): boolean { return this.metadata.mode === PluginMode.FULL; } /** * Register a tool with automatic mode checking * Write tools are automatically skipped in readonly mode * @param options Tool registration options * @param options.context Plugin context * @param options.name Tool name * @param options.schema Tool schema with description and inputSchema * @param options.handler Tool handler function * @param options.isWriteTool Whether this is a write operation (default: false) */ protected registerTool<TArgs = Record<string, unknown>>(options: { context: PluginContext; name: string; schema: ToolSchema; handler: ToolHandler<TArgs>; isWriteTool?: boolean; }): void { const { context, name, schema, handler, isWriteTool = false } = options; // Skip write tools in readonly mode if (isWriteTool && this.isReadonly()) { console.warn(`[WARN] [${this.metadata.name}] Skipping write tool '${name}' in readonly mode`); return; } // Wrap handler to match MCP SDK signature: (args, extra) => ... // The SDK expects both args and extra, but our handler only needs args context.server.registerTool( name, schema, ( args: { [x: string]: unknown }, _extra: RequestHandlerExtra<ServerRequest, ServerNotification>, ) => { return handler(args as TArgs); }, ); } }

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/Nam088/mcp-server'

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