Skip to main content
Glama
by vibase-ai

@vibase/core

Easily read and mutate Postgres data using MCP.

Overview

@vibase/core provides the foundational components for creating MCP (Model Context Protocol) servers on top of Postgres using simple configuration files.

Related MCP server: Simple PostgreSQL MCP Server

Features

  • YAML Configuration: Define MCP tools using simple YAML configuration files

  • PostgreSQL Support: Direct SQL execution against PostgreSQL databases with connection pooling

  • Safe SQL Generation: Uses parameterized queries for safe SQL query construction

  • Type Safety: Full TypeScript support with Zod validation

  • Connection Management: Automatic connection pooling and cleanup

Installation

npm install -g @vibase/core

Quick Start

  1. Create a YAML configuration file (tools.yaml):

sources: todo_db: kind: postgres connection_string: postgres://user:password@localhost:5432/todo_management tools: get_boards: kind: postgres source: todo_db description: Retrieve all todo boards statement: SELECT id, name, description FROM boards ORDER BY created_at DESC; parameters: [] search_tasks: kind: postgres source: todo_db description: Search tasks by title or description parameters: - name: search_term type: string description: Search term to match against task title or description required: true statement: | SELECT t.id, t.title, t.priority, s.name as stage_name FROM tasks t JOIN stages s ON t.stage_id = s.id WHERE t.title ILIKE '%' || $1 || '%' OR t.description ILIKE '%' || $1 || '%';
  1. Create and run the MCP server:

npx @vibase/core tools.yaml --http 5555

Your MCP server now runs over streamable HTTP on http://localhost:5555 You can view options with --help

API Reference

Use Vibase with any TypeScript MCP server.

Configuration

loadConfigFromYaml(configPath: string): ToolboxConfig

Loads and validates configuration from a YAML file.

Parameters:

  • configPath: Path to the YAML configuration file

Returns:

  • ToolboxConfig: Validated configuration object

Throws:

  • Configuration validation errors

  • File not found errors

  • YAML parsing errors

loadConfigFromYamlString(yamlContent: string): ToolboxConfig

Loads and validates configuration from a YAML string.

Parameters:

  • yamlContent: YAML configuration as a string

Returns:

  • ToolboxConfig: Validated configuration object

Throws:

  • Configuration validation errors

  • YAML parsing errors

Server Creation

createMcpServerFromConfig(config: ToolboxConfig, options?: ServerOptions): { server: McpServer; cleanup: () => Promise<void>; plugins: PluginRegistry }

Creates an MCP server from a validated configuration object.

Parameters:

  • config: Validated configuration object

  • options: Optional server configuration options

Returns:

  • server: The MCP server instance

  • cleanup: Async function to clean up database connections

  • plugins: Plugin registry for registering custom plugins

addToolsToMcpServer(server: McpServer, config: ToolboxConfig): { cleanup: () => Promise<void>; plugins: PluginRegistry }

Adds tools from configuration to an existing MCP server. This is useful when you want to add tools to a server that was created separately.

Parameters:

  • server: Existing MCP server instance

  • config: Validated configuration object

Returns:

  • cleanup: Async function to clean up database connections

  • plugins: Plugin registry for registering custom plugins

Example:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { addToolsToMcpServer, loadConfigFromYaml } from "@vibase/core"; // Create server manually const server = new McpServer({ name: "My Custom Server", version: "1.0.0", }); // Load configuration and add tools const config = loadConfigFromYaml("./config.yaml"); const { cleanup, plugins } = addToolsToMcpServer(server, config); // Register plugins if needed const authPlugin = { name: "auth", callbacks: { beforeQuery: (context) => { return { pgSettings: new Map([["role", "authenticated"]]) }; } } }; plugins.register(authPlugin); // Connect transport and handle cleanup process.on("SIGINT", async () => { await cleanup(); process.exit(0); });

Configuration Format

Sources

Define database connections:

sources: source-name: kind: postgres connection_string: postgres://user:password@host:port/database

Tools

Define SQL-based tools:

tools: tool-name: kind: postgres source: source-name # References a source description: Human-readable description parameters: - name: param_name type: string | number | boolean description: Parameter description required: true | false # Optional, defaults to true statement: SELECT * FROM table WHERE column = $1;

Plugins API

@vibase/core includes a plugin system that allows you to modify query execution behavior. The most common use case is implementing authentication with JWT tokens and PostgreSQL Row Level Security (RLS).

Plugin Registration

import { createMcpServerFromConfig, loadConfigFromYaml } from "@vibase/core"; // Create server and get plugins registry const config = loadConfigFromYaml("./config.yaml"); const { server, plugins } = createMcpServerFromConfig(config); // Register a plugin const myPlugin = { name: "my-plugin", callbacks: { beforeQuery: (context) => { // Plugin logic here return { pgSettings: new Map([["role", "authenticated"]]) }; } } }; plugins.register(myPlugin);

JWT Authentication Plugin Example

Here's how you can implement JWT bearer token authentication:

import jwt from "jsonwebtoken"; function createBearerAuthPlugin(jwtSecret: string) { return { name: "bearer-auth", callbacks: { beforeQuery: ({ extra }) => { const pgSettings = new Map(); try { // Extract JWT from authInfo.token or Authorization header let token = extra?.authInfo?.token; if (!token) { const authHeader = extra?.requestInfo?.headers?.authorization; if (authHeader?.startsWith('Bearer ')) { token = authHeader.replace(/^Bearer\s+/i, ''); } } if (!token) { // No token - set anonymous role pgSettings.set('role', 'anonymous'); return { pgSettings }; } // Verify and decode JWT const decoded = jwt.verify(token, jwtSecret); // Set PostgreSQL session variables for RLS pgSettings.set('role', 'authenticated'); pgSettings.set('jwt.claims.sub', decoded.sub); pgSettings.set('jwt.claims.role', decoded.role); return { pgSettings }; } catch (error) { // JWT verification failed - set anonymous role pgSettings.set('role', 'anonymous'); return { pgSettings }; } } } }; } // Register the plugin const authPlugin = createBearerAuthPlugin(process.env.JWT_SECRET); plugins.register(authPlugin);

How Plugins Work

  1. Plugin Structure: Plugins are objects with a name and callbacks for different hooks

  2. Plugin Registration: Use plugins.register(plugin) to register a complete plugin

  3. Hook Execution: Plugin callbacks run before each query (more hooks coming soon)

  4. Context Access: Callbacks receive context including tool name, parsed arguments, and request metadata

  5. Session Variables: Plugins return pgSettings Maps that become PostgreSQL session variables

  6. Automatic Transactions: When pgSettings are present, queries run in transactions with variables applied

  7. RLS Integration: SQL queries can access variables via current_setting('jwt.claims.sub')

Plugin Management

// Get all registered plugins const allPlugins = plugins.getRegisteredPlugins(); // Get specific plugin by name const authPlugin = plugins.getPlugin("bearer-auth");

Row Level Security Integration

With the authentication plugin, your SQL queries can reference JWT claims:

-- Users can only see their own todos CREATE POLICY user_todos ON todos FOR ALL USING (user_id = current_setting('jwt.claims.sub')::uuid); -- Query automatically filtered by RLS SELECT * FROM todos; -- Only returns current user's todos

Plugin Context

Plugin callbacks receive a context object with:

  • toolName: Name of the tool being executed

  • toolConfig: Tool configuration from YAML

  • pool: Database connection pool

  • parsedArgs: Validated tool arguments

  • extra: MCP request metadata (authInfo, requestInfo, etc.)

  • query: The SQL query being executed

Security Considerations

  • Always use parameterized queries (enforced by this package)

  • Store database credentials securely (consider environment variables)

  • Limit database user permissions to only what's needed

  • Use SSL connections for production databases

  • Validate and sanitize user input before using in queries

Contributing

Contributions are welcome!

License

Apache 2.0

-
security - not tested
A
license - permissive license
-
quality - not tested

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/vibase-ai/vibase'

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