mcp-firebird

by danieleteti

Not rated
GitHub

Description

A Model Context Protocol server for Firebird 2.5 – 5.0, written in Delphi with the official `fbclient` driver. It lets an AI assistant document schemas, analyze query plans, advise on indexes (which to add and which to drop), audit schema health, and drive goal-based…

About

A Model Context Protocol server for Firebird 2.5 – 5.0, written in Delphi with the official `fbclient` driver. It lets an AI assistant document schemas, analyze query plans, advise on indexes (which to add and which to drop), audit schema health, and drive goal-based optimization. Read-only by default.

Details

Author
danieleteti
Categories
Database

Setup

Install mcp-firebird in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/danieleteti/mcp-firebird

Follow the installation instructions in the repository README, then restart your MCP client.

Ask your AI assistant why a query is slow, and get an answer worth acting on.

AModel Context Protocolserver forFirebird 2.5 to 5.0, written in Delphi against the officialfbclientdriver. Connect it to a database and an assistant can read your access plans, tell you which indexes are missing and which ones are dead weight, audit a table's health, and find the transactions left open that are holding garbage collection back.

You can also hand it a goal,"this query must stop scanning NATURAL","it has to come in under 200 ms", and let it work: it applies a change, measures it again on the database, and tries again if that was not enough. What decides the goal is met is the measurement, not the assistant.

These are not the generic index tips you can find in any article. The answers come out ofyourdatabase: the server asks Firebird for the query's execution plan (SET PLANONLY), queries the monitoring tables (MON$), and counts how many distinct values a column really holds before claiming an index on it would pay off.

Every answer arrives in three parts.Finding: what it found, and why it is a problem.SQL: the statement that fixes it, already written.Verify: how to check the fix actually worked. No tool writes to the database. The server reads, and the SQL it hands you is yours to run, when and if you decide to.

Built withmcp-server-delphi, which in turn stands onDelphiMVCFramework. This server is a complete, real-world example of what you can build with them.

- Transport:stdio (JSON-RPC 2.0, MCP protocol2025-03-26)
- Server identity:mcp-firebirdv0.4.0
- Engine support:Firebird 2.5, 3.0, 4.0, 5.0 (capability-detected at runtime)
- Safety:read-only analysis, enforced by the engine — the server attaches with a read-only transaction, so a write is refused by Firebird itself
- Freefor your own databases, at any scale, with no key and no expiry. A licence is needed only to hand the software to somebody else: resell it inside a product of yours, leave it installed at a client's, expose it as a service (
licence details)
-
Enterprise edition, sold separately: it examines the Firebirdserver, not just the database. It readsfirebird.conf, the machine's RAM and CPUs,firebird.logand the Trace API. You want it when the schema is in order and the database is still slow
-
What it does
-
Editions & licensing
-
Enterprise edition
-
How it uses mcp-server-delphi
-
Prerequisites
-
Build
-
Configuration (.env)
-
Run & verify manually
-
Install it into your AI agent: Claude Desktop · Claude Code · Gemini CLI · OpenCode · Cursor / VS Code · generic
-
Using it from Claude: worked examples
-
Tool reference
-
Testing the project
-
Troubleshooting

Tools (10 free, plus 9 Enterprise announced intools/list)

Every advisory comes with aFinding, ready-to-runSQL, and aVerifystep. Every analysis tool also closes withwhat it does not rule out: a plan cannot see contention, a snapshot cannot see accumulation, and a clean answer to a narrow question is not a clean bill of health.

Whatfb_suggest_indexesdecides, and in what order

An index is not free: it is written on every INSERT, UPDATE and DELETE into that table, for as long as it exists. So the advisor proposes thecheapest remedy that explains what the plan did, and it is willing to propose nothing at all.
- Refresh, don't build.Firebird stores an index's selectivity when the index is created and never recalculates it. An index that exists and is ignored may be ignored because the optimizer is costing it against last year's data — and answering that with a second index leaves the database paying for writes forever because a number needed refreshing. Nothing here says"the index is not used, and that is correct"until the stored figure has been compared with the real one.
- Wake, don't duplicate.AnINACTIVEindex over the column the query filters by isALTER INDEX … ACTIVE, never a second index beside the sleeping one.
- Say when the scan is right.The query is executed once and the records it read are compared with the rows it returned. A scan that keeps most of what it readsisthe correct plan, and so is a scan of a table small enough to be a handful of pages: both come back as a finding with the numbers andno SQL to run.
- Then, the right shape.UPPER(NAME) = ?cannot use a plain index at all and gets an expression index, with the expression emitted exactly as written — Firebird matches it only on an exact match.STATE = 'ACTIVE' AND TAG = ?gets an index on what it seeks by, plus the partial form on 5.0+, which indexes only the population the query looks at.ORDER BYon an unindexed column gets an index whose segments match itin its own direction— and a plain statement that no single index servesORDER BY a ASC, b DESC, rather than one that would never be used.

Every finding carries the numbers the rule was applied to, so a DBA who disagrees with a threshold can disagree with the threshold rather than with the tool. The design and the measurements behind it are indocs/superpowers/specs/2026-08-07-index-advisor.md.

Note:to measure, this toolexecutes the queryit is asked about — once, inside the read-only transaction, and only when the plan actually shows a scan or a sort. On a large table that is a full scan. It never executes anything that is not a plain SELECT, and it does not execute a statement with unbound parameters at all: those bind as NULL, match nothing, and would produce a measurement of a query nobody runs.

- optimization_goal, the goal-driven loop: set an objective, the assistant iterates thefb_tools and re-checksfb_evaluate_goaluntil it reportsmet: true(with a max-iterations / no-progress safety stop).
- health_check: guided whole-database health review.
- classify_problem: starts from the symptom, not from a tool. Places the problem on six axes (temporal shape, volume vs concurrency, locality, coordination vs execution, sensitivity to observation, environment), names the dominant class of the seven, and says which reasoning converges on it — including when the answer is that an SQL connection cannot settle it. Follows the taxonomy in Cisar & Reeves,
Practical Firebird Performance Diagnostics(IBPhoenix, 2026).

- firebird://schema: the live database schema as a single resource.

Short version:if you are using it on your own databases, it is free, and it stays free.No trial, no expiry, no licence key, no seat count, no limit on how many tables or databases you point it at. Install it, use it in production, use it every day. Nothing phones home.

The one thing you cannot do is hand it to somebody else.

MCP Firebird issource-available, not open sourceas the Open Source Initiative defines the term. Saying that plainly matters more than a badge: fromv0.2.0it is licensed under thePolyForm Internal Use License 1.0.0. Versions up to and includingv0.1.0 were released under Apache-2.0 and remain sofor everyone who received them: a licence already granted cannot be revoked, and this project does not pretend otherwise.

- Run it against any database you like: yours, your employer's, your client's. Development, staging, production, all of them.
- Run it at any scale. A hundred tables or ten thousand; one database or fifty.
- Use it in your consulting practice.Diagnose, tune, audit and support your clients' Firebird databases with it, and charge them for your time. It is your tool; keep it.
- Read the source. All of it. Learn from it, and use what you learn.
- Modify it. Fix a bug, add a detector, change a message. Run your modified build.
- Use it in a company of any size, commercial or not, for-profit or not, with no fee and no registration.

One idea, expressed three ways:letting the software out of your hands.

- Redistributing it.Publishing a fork, uploading a build, putting it on a CD, sending the binary to a customer, leaving it installed on a client's server when the engagement ends.
- Embedding it in a product you sell.Shipping it inside your ERP, your installer, your Docker image, your appliance, in source or binary form, modified or not.
- Offering it as a service.Standing it up behind an API or a hosted agent that people outside your organisation can reach.

Where the software runs, and whose database it examines, is your business. Where copies of it end up is ours.

If your case is one of these, the licence exists and it is not expensive relative to what you are building with it. Write tod.teti@bittime.it.

When you need to buy a licence: worked cases

The rule behind the table, if you would rather reason than look things up:ask where the software ends up, never what you did with it.As long as every copy of MCP Firebird stays in your hands, you owe nothing: not for the scale you run it at, not for the money it makes you, not for whose database you point it at. The moment a copy leaves, we should talk.

There is also a paidEnterprise edition, a different product, not a crippled free tier. Everything described in the rest of this README is in the free one.

The free edition is not a demo, and it is not the Enterprise edition with the good parts taken out. It is the whole of one job, done properly:it makes the database answer for itself.

It reads your schema. It explains your plans. It finds the index you are missing and the four you do not need. It catches the missing primary key, the stale statistics, the transaction pinning garbage collection since Tuesday. For most databases, most of the time, that is where the problem is, and that is where the problem gets fixed. Plenty of people will use it for years and never need anything else, and they will never be asked for a cent.

Then one day it comes back and tells you the truth:your schema is fine. Your indexes are fine. No natural scans, no external sorts, statistics fresh.And the database is still slow.

That is the line.The free edition has answered its question honestly and completely, and the answer is that the problem is not in the database. It is in the machine underneath it, and noSELECTwill ever show you that. Not because the tool is holding back, but because SQL cannot see outside its own process.

It is 2 GB of page buffers on a host with 8 GB of RAM. It isforced writesswitched off for a batch load two years ago and never switched back. It isLockHashSlotsstill at its 2010 default under four hundred connections, an index four levels deep, a bugcheck written tofirebird.logevery Tuesday at 03:00 that nobody reads.

The free edition connects to Firebird the way your application does: an ordinary SQL connection, with ordinary rights.The Enterprise edition asks for more.It attaches to the Services Manager as an administrator (which is how it streamsfirebird.logback, drives the Trace API, and reads the physical storage report), and it reads the server's own configuration and hardware. That is a different privilege, a different blast radius, and a different conversation with whoever owns the server. Hence a different product.

And it does not stop at telling you what is wrong.It runs the experiment.Capture a baseline under real load, change exactly one parameter, measure again, compare the distributions (the p95 and the p99, never the average), and keep the change or put it back. Nobody sells you a number. The database tells you the number.

Note what isnotin that table: nothing was moved out of the free edition to build the paid one. Every free tool stays free, and the ones still to be written stay on the side of the line they belong to. The boundary is not a paywall drawn through a feature list. It is the line between querying a database and administering a server, and the free edition was always on one side of it.

The hard part was never parsingfirebird.conf; anyone can parse an INI file. And nobody can honestly hand you the right value forLockHashSlots:Firebird's own documentation states no optimum for it, nor for the page cache, nor for the sort cache. What experience buys you is knowingwhichparameter your symptom implicates: that throughput collapsing under concurrency while the CPU stays calm points at the lock table and never at the page cache. That map is the product. The value at the end of it is not asserted. It is measured, on your database, under your load.

You will know when you need it, because the free edition will have told you.

The nine tools above already appear intools/listin the free edition, so your assistant can see them and say what it would do with them. Call one and it tells you how to get it.

Enterprise licences, commercial licences and support subscriptions:d.teti@bittime.it

Every tool is a plain Delphi method decorated with attributes frommcp-server-delphi. The framework turns the class into an MCP tool provider, generates the JSON-RPC schema from the attributes, and wires it to the stdio transport: no protocol code in this repo. Fromproviders/FirebirdToolsU.pas:

TFirebirdTools = class(TMCPToolProvider) public [MCPTool('fb_info', 'Engine version, dialect, charset and detected capabilities of the configured Firebird database')] function FbInfo: TMCPToolResult; [MCPTool('fb_generate_documentation', 'Markdown documentation — columns, primary key, indexes — for one table, or for the whole database when table_name is empty')] function FbGenerateDocumentation([MCPParam('Table name; leave empty for the whole database', TMCPParamPresence.Optional)] const table_name: string): TMCPToolResult; [MCPTool('fb_analyze_query', 'Returns and analyzes the access plan of a SQL query (flags NATURAL scans and external sorts)')] function FbAnalyzeQuery([MCPParam('The SQL query to analyze')] const sql: string): TMCPToolResult; end;

Prompts (providers/FirebirdPromptsU.pas) and resources (providers/FirebirdResourcesU.pas) use the same attribute approach. See themcp-server-delphirepository for the full attribute reference.

- Windows x64(the server is a native Win64 console app).
- AFirebird client library(fbclient.dll) matching, or newer than, your target server. A 5.0fbclient.dllconnects fine to 2.5-5.0 servers.
- A reachableFirebird databaseto point at.

The download ships nofbclient.dllon purpose: the right one is your server's own, and a mismatched client is worse than none. Pointfirebird.client_libat it (see below).

To build from source you also needDelphi 13 Athens(RAD Studio 37.0) withFireDAC, andDMVCFrameworkplus themcp-server-delphilibrary checked out locally. For the test matrix, the Firebird zip-kits underfb_versions/and Python 3 withpytest.

Search paths the project expects (set once inapp/MCPFirebird.dproj):

C:\DEV\mcp-server-delphi\sources <DMVCFramework>\sources (every sources subfolder DMVC needs) C:\DEV\mcp-firebird\sources C:\DEV\mcp-firebird\providers

Build the Win64 Debug app from the repo root:

_build_app.batcallsrsvars.batthenmsbuild app\MCPFirebird.dproj /t:Clean;Build /p:Config=Debug /p:Platform=Win64. The executable lands atbin\MCPFirebird.exe.

(There is a matching_build_core.batfor the DUnitX test project.)

By default the server reads its configuration from a.envfile in the same folder as the executable, so where that is depends on how you got the exe:

Then edit it. (.env.examplestarts with a dot:lsand Explorer hide it unless you ask for hidden files. It is in the zip.)

Choosing a different config folder:--env <dir>

By default the.envis read from the executable's own folder. Pass--env <dir>to read it from another folder instead. The argument is adirectory(the folder thatcontainsthe.env), not the file itself:

MCPFirebird.exe --env C:\configs\prod # reads C:\configs\prod\.env MCPFirebird.exe --env=C:\configs\prod # the --env=<dir> form also works MCPFirebird.exe --env ..\shared # relative paths resolve against the working directory MCPFirebird.exe # no argument -> reads <exe folder>\.env MCPFirebird.exe --env C:\configs\prod\.env # WRONG -> stops with an error (see below)

--envis a folder, never the.envfile.If you point it at the file (e.g....\prod\.env) the server refuses to start and prints the fix on stderr (which MCP clients surface in their server logs) instead of silently starting with an empty config:

MCPFirebird: --env must point at the FOLDER that contains the .env file, not at the file itself. got: C:\configs\prod\.env use this: C:\configs\prod

How the argument reaches the server.MCP clients don't go through a shell. They spawn the executable directly with acommandplus anargsarray, where each array element becomes one separate argument. So there is no shell quoting to worry about (paths with spaces are fine), and you write the directory as its own array element. Two equivalent forms:

Path notes (Windows):in JSON, backslashes must bedoubled("C:\\configs\\prod"), or use forward slashes, which Windows accepts and don't need escaping ("C:/configs/prod"). Prefer anabsolutepath in MCP clients: the working directory they launch with is unpredictable, so relative paths are unreliable there. Every startup logs the resolved folder tologs\MCPFirebird.NN.mcp.log:

Boot: .env directory "C:\configs\prod" (.env exists=True)

Note:logs are always written to alogs\subfolder next to theexecutable(logs\beside the exe), regardless of--env.

Claude Desktop(%APPDATA%\Claude\claude_desktop_config.json),Claude Code(.mcp.json),Cursor(.cursor/mcp.json) andVS Code(.vscode/mcp.json) all use the same shape, acommandplus anargsarray:

{ "mcpServers": { "firebird": { "command": "C:\\Tools\\MCPFirebird\\MCPFirebird.exe", "args": ["--env", "C:\\configs\\prod"] } } }

Claude Code can also add it from the CLI:

claude mcp add firebird -- "C:\Tools\MCPFirebird\MCPFirebird.exe" --env "C:\configs\prod"

Gemini CLI(~/.gemini/settings.json), samemcpServersshape:

{ "mcpServers": { "firebird": { "command": "C:\\Tools\\MCPFirebird\\MCPFirebird.exe", "args": ["--env", "C:\\configs\\prod"] } } }

OpenCode(opencode.json). Note the difference:commandis asingle arraythat already includes the arguments (there is no separateargsfield):

{ "$schema": "https://opencode.ai/config.json", "mcp": { "firebird": { "type": "local", "command": ["C:\\Tools\\MCPFirebird\\MCPFirebird.exe", "--env", "C:\\configs\\prod"], "enabled": true } } }

Serving several databases from one build

Register thesame executablemore than once with different--envfolders. Each folder holds its own.env:

{ "mcpServers": { "firebird-prod": { "command": "C:\\Tools\\MCPFirebird\\MCPFirebird.exe", "args": ["--env", "C:\\configs\\prod"] }, "firebird-test": { "command": "C:\\Tools\\MCPFirebird\\MCPFirebird.exe", "args": ["--env", "C:\\configs\\test"] } } }
C:\configs\prod\.env <- production host/port/database C:\configs\test\.env <- test host/port/database

The client then shows two independent servers (firebird-prod,firebird-test), each connected to its own database.

firebird.host=localhost firebird.port=3050 firebird.database=C:\data\MYAPP.FDB firebird.user=SYSDBA firebird.password=masterkey firebird.charset=UTF8 firebird.client_lib=C:\Program Files\Firebird\Firebird_5_0\fbclient.dll logger.config.file=loggerpro.stdio.json

Why a file and not client-passed env vars?The dotEnv strategy isfile-then-env*: the.envfile takes priority, OS environment variables are the fallback. Configuring via.envworks identically across every MCP client because it is read relative to the.exe, regardless of the client's working directory. Keep this file out of version control (it is already.gitignored): it holds credentials.

The server speaks JSON-RPC over stdin/stdout. You can smoke-test it without any MCP client by piping framed JSON lines into it. From PowerShell:

$msgs = @( '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"manual","version":"1"}}}' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"fb_info","arguments":{}}}' ) -join "n" $msgs | & .\MCPFirebird.exe`
No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.