Changelog
All notable changes to the pgEdge Natural Language Agent will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Security
-
Login rate limiting no longer counts against an address the caller can choose. The server previously read the leftmost entry of
X-Forwarded-For, which is the part of that header a caller sets for itself, so rotating the value defeatedrate_limit_max_attemptsentirely, and sending someone else's address had their attempts counted against them. The reverse proxy configurations in these docs appended to the header rather than replacing it, so this needed no misconfiguration to reproduce. Client addresses now come from the network connection by default, and a newhttp.client_ipblock opts into reading a forwarding header, which is honoured only on connections from an address listed inclient_ip.trusted_proxies;X-Forwarded-Foris read from right to left, past trusted entries, and every instance of the header is treated as one list. Candidate addresses that do not parse are discarded rather than becoming rate limiter keys, and IPv6 addresses are no longer counted twice under bracketed and unbracketed forms. Note that account lockout is keyed on the username and was unaffected. The nginx examples in the documentation now setX-Forwarded-Forto$remote_addrrather than$proxy_add_x_forwarded_for. -
Upgraded
github.com/jackc/pgx/v5from 5.7.6 to 5.10.0, resolving three advisories against the PostgreSQL driver. Two are memory-safety issues (GO-2026-4771/CVE-2026-33815 and GO-2026-4772/CVE-2026-33816, fixed in 5.9.0), whichgovulncheckreports at package level, meaning it finds no call path to them from this code. The third is a SQL injection through placeholder confusion with dollar-quoted string literals (GO-2026-5004/CVE-2026-41889, fixed in 5.9.2), whichgovulncheckreports at symbol level with a reachable path from the metadata loader into the driver's SQL sanitiser, making it the one of the three that demonstrably affects this project. -
Upgraded
golang.org/x/textfrom 0.35.0 to 0.40.0 (GO-2026-5970, an infinite loop on invalid input, reachable through connection setup) andgithub.com/yuin/goldmarkfrom 1.7.8 to 1.7.17 (GO-2026-5320, cross-site scripting, reachable through the chat client's markdown rendering).golang.org/x/netmoves from 0.51.0 to 0.57.0, clearing a DNS message parsing panic (GO-2026-5942) that was only ever reachable at module level.golang.org/x/crypto,golang.org/x/sync,golang.org/x/sysandgolang.org/x/termmove with them.
After these upgrades govulncheck reports no reachable vulnerability in any
dependency. The findings that remain are in the Go standard library and are
resolved by building with Go 1.26.5 or later; the continuous integration
workflows and container images track the latest 1.26 patch release, so they
pick that up without a change here.
-
The system prompt now states that everything returned by a tool is untrusted content: query results, table and column names, document text and search results are data to report to the user, never instructions to follow. Retrieved content is written by whoever populated the database rather than by the person asking the question, so a document can carry instructions of its own; one that asks an assistant to copy it into the table it came from is read again by the next session that searches for it. The rule applies in read-only mode and otherwise. It is a mitigation and not a control, since a model can be argued out of any instruction, which is why the documentation now states plainly that the measure which actually prevents such a document propagating itself is the absence of write access.
-
Custom tools now advertise whether they can modify the database, using the same
readOnlyHintanddestructiveHintMCP annotations already set onquery_database. Anypl-doorpl-functool is treated as capable of writing, as is asqltool whose statement is not plainly a read; on a connection that does not permit writes every custom tool is advertised as read-only. A statement that cannot be classified is assumed to write, so an incorrect guess costs a confirmation prompt rather than an unannounced write. -
The CLI and the web client now ask for confirmation before any tool call that the server advertises as capable of writing, rather than only before a write through
query_database. A custom tool that modified data previously executed without a prompt in either client, because the check was keyed to a single tool name. Statements passed toquery_databaseare still classified individually, so an ordinary read on a write-enabled connection is not interrupted, and a tool that advertises no annotation is not treated as a write, so the built-in read-only tools are unaffected. The confirmation wording is now neutral, since the subject may be a tool call rather than a SQL statement.
Note that the untrusted content rule above applies only to the CLI, which is the only client that sends a system prompt; the web client sends none, so it receives neither that rule nor the pre-existing read-only safety instructions.
- Provider API keys are no longer disclosed in error output. When an LLM or
embedding provider rejects a credential it commonly quotes that credential
back in its error body: OpenAI's authentication failure names the key it
was given, partially masked but with its opening characters intact. The
shared pgEdge LLM library relays a provider's message verbatim into the
error it returns, and that error reached the
/api/llm/response body, the trace file, and the CLI's own output. All three are now redacted, with both the configured credentials and anything matching a known key format replaced by[REDACTED]; the rest of the message survives, so the provider, status code and reason are still reported. The/api/llm/filtering is best-effort: it inspects each response write on its own, so a credential split across two writes would evade it.
The durable fix belongs in the provider client library, so that a credential never reaches an error value at all. What is added here is a filter over text that should not have contained a credential in the first place: it recognises the formats this project handles and the values it was configured with, so it is a safety net rather than a guarantee. See Security for the limits.
Fixed
make testnow runs every package that has tests. Ten packages were absent from the server target and so were never exercised by the suite or by continuous integration:api,compactor,conversations,definitions,httperror,llmtracing,logging,prompts,searchandtsv. All of them pass; they were simply never run.
Added
-
A
make vulnchecktarget runsgovulncheckover the module, using call-graph analysis to prioritize known vulnerabilities that this code can actually reach over every advisory affecting a dependency. See Development for details. -
Database configuration now accepts
sslcert,sslkey, andsslrootcertfields, letting the server authenticate to PostgreSQL with a client certificate instead of, or alongside, a password. Configure them withdatabases[].sslcert,databases[].sslkey, anddatabases[].sslrootcertin the configuration file, the-db-sslcert,-db-sslkey, and-db-sslrootcertCLI flags, or thePGEDGE_DB_SSLCERT/PGSSLCERT,PGEDGE_DB_SSLKEY/PGSSLKEY, andPGEDGE_DB_SSLROOTCERT/PGSSLROOTCERTenvironment variables.sslcertandsslkeymust be set together.sslrootcerttakes effect only undersslmoderequire,verify-ca, orverify-full; underdisable,allow, orprefer(the default) pgx never checks the server certificate against it and silently ignores the value, matching libpq andpsql. In HTTP mode, changing any of these fields and reloading the configuration (SIGHUP) now closes pooled per-token connections so they reconnect with the new certificate settings. -
A configurable per-attempt timeout bounds each individual HTTP attempt to an LLM or embedding provider, so a single slow attempt becomes retryable instead of consuming the whole request budget; the knowledgebase embedding path honours the same setting. Configure it with
per_attempt_timeoutin thellmandembeddingconfig sections,embedding_per_attempt_timeoutin theknowledgebasesection, or thePGEDGE_LLM_PER_ATTEMPT_TIMEOUT,PGEDGE_EMBEDDING_PER_ATTEMPT_TIMEOUT, andPGEDGE_KB_EMBEDDING_PER_ATTEMPT_TIMEOUTenvironment variables (default 60 seconds; set the corresponding environment variable to 0 to disable the cap). -
Similarity search now validates the query embedding dimension against the target vector column before querying, returning a clear error on a mismatch instead of a raw database error.
-
Similarity search now supports pgvector
halfveccolumns; it detects the column type and casts the query vector accordingly (requires pgvector 0.7.0 or later). -
The web client now uses the provider display name reported by the proxy, falling back to its built-in labels when none is supplied.
-
Each built-in tool, resource, and prompt can now be enabled or disabled via an environment variable in addition to the
builtinssection of the configuration file. The variables arePGEDGE_BUILTIN_TOOL_*,PGEDGE_BUILTIN_RESOURCE_*, andPGEDGE_BUILTIN_PROMPT_*; see the configuration reference for the complete list. This is useful in containerized deployments where editing the configuration file is awkward. (#139)
Changed
-
The LLM provider clients (Anthropic, OpenAI, and Ollama) now use the shared
pgedge-go-llm-liblibrary instead of hand-rolled HTTP wire code; approximately 1500 lines of provider-specific code are removed frominternal/chat/. Behaviour is preserved; theLLMClientinterface is unchanged. -
Anthropic prompt caching now covers both the tools block and the system prompt (the library exposes a
WithSystemCachingbuilder alongsideWithToolCaching). Long system prompts no longer pay full input-token cost on every turn. -
OpenAI models that require the Responses API (
gpt-5-*,o1-*,o3-*) are now supported transparently; the library routes them to/v1/responsesautomatically based on the model name. -
Embedding provider clients (Voyage, OpenAI, Ollama) now use the shared
pgedge-go-llm-liblibrary instead of hand-rolled HTTP wire code. Approximately 1100 lines of provider-specific code are removed frominternal/embedding/. TheProviderinterface andNewProviderfactory are preserved; tool consumers (search_knowledgebase, generate_embedding, similarity_search) compile unchanged. -
Provider.Dimensions()is now lazily populated from the first successfulEmbedcall; it returns 0 before any embedding has been generated (previously the value was hard-coded per known model). -
Refactored
Client.LoadMetadataForininternal/database/connection.go. The CTE-based metadata query now lives ininternal/database/load_metadata.sqland is loaded via//go:embed; the per-row scan and the grouping/transform logic are split intoscanMetadataRowandbuildTableInfoininternal/database/metadata.go.buildTableInfois pure and is covered by table-driven unit tests that do not require a live database. No behavior change. (#153) -
The built-in
pg://system_inforesource now uses the machine-safe namepostgresql_system_info(previously"PostgreSQL System Information"). The new name matches the identifier pattern enforced by Anthropic's tool-name validation (^[a-zA-Z0-9_-]{1,128}$), so the resource no longer breaks interoperability when a downstream MCP client forwards built-in capability names as provider tool names. The resource URI is unchanged. (#139) -
The KB Builder (formerly
cmd/kb-builderand theinternal/kb*packages) has moved to a standalone project atpgedge-ai-kb. The binary is renamed frompgedge-nla-kb-buildertopgedge-ai-kb-builder. The MCP server itself is unaffected; it continues to consume a pre-builtkb.dbat runtime. The Docker build now downloadskb.dbfromhttps://github.com/pgEdge/pgedge-ai-kb/releases/download/kb-latest/kb.dbby default; passKB_SOURCEto override. Thepgedge-nla-kb-builder_*release archives are no longer published from this repository. -
The LLM HTTP proxy is now provided by
pgedge-go-llm-lib'sllm/proxypackage, mounted at/api/llm/. The endpoints moved from/api/llm/{providers,models,chat}to/api/llm/v1/*, and the request/response wire format now uses typed content blocks (see the library'sllm.ChatRequestandllm.ContentBlock).internal/llmproxy/is deleted; tracing plumbs through proxy hooks via the newinternal/llmtracingpackage. -
A streaming chat endpoint
/api/llm/v1/chat/stream(SSE) is now exposed alongside the non-streaming endpoint. The non-streaming/v1/chatendpoint remains available for callers that prefer it. -
The web chat interface now consumes the streaming endpoint
/api/llm/v1/chat/stream(Server-Sent Events) and renders the assistant response incrementally as chunks arrive. The non-streaming endpoint stays available for callers that prefer it. A newweb/src/utils/sseChat.jshelper handles the SSE parsing and assembles the final response into the same shape the non-streaming endpoint returns, so the agentic chat loop is unchanged. -
The tools
search_knowledgebase,generate_embedding, andsimilarity_searchnow construct their embedding client directly viallm.NewClientrather than going through the oldembedding.NewProviderwrapper. Theinternal/embedding/package is deleted entirely. -
The temporary
chat.LLMClientinterface andlibClientadapter added in the first migration PR are removed. The CLI chat client now consumespgedge-go-llm-lib'sllm.ClientAPI directly.internal/chat/llm.goandinternal/chat/llm_translate.goare deleted; messages and content blocks flow through the chat package asllm.Messageandllm.ContentBlockrather than chat-package wrapper types. The library'sllm.ClientAPI itself is unchanged. The CLI's debug-mode HTTP tracing still injects viallm.Options.HTTPClient. -
Saved conversations from earlier versions are migrated on load: messages with a plain-string
contentfield are wrapped as a single text content block, and tool-result messages saved with role"user"are promoted to role"tool"to match the library's expected shape. The on-disk JSON written by this and later versions uses the typed content-block format directly. -
The web client now groups the conversation-level Save and Delete actions in a new menu in the status banner header, alongside the database switcher and connection details, rather than placing them next to the message input. This keeps the destructive Delete action away from the Send button and clarifies that the actions affect the whole conversation. Deleting a conversation now requires confirmation via a dialog instead of a browser prompt. (#73)
-
Custom
pl-functools now fail immediately, with an explanation, on a database connection that does not permit writes. Such a tool creates and drops a temporary function, which a read-only transaction cannot do, so it previously failed partway through with an opaque permissions error that invited disabling read-only mode as the remedy. Use apl-dotool on a read-only connection. -
The read-only statement guard no longer rejects a query merely for mentioning
transaction_read_onlyordefault_transaction_read_onlyinside a string literal or a comment, so an ordinary lookup such asSELECT * FROM config WHERE key = 'transaction_read_only'is now permitted. A rejection additionally requires a construct capable of changing a setting.
Security
-
Read-only connections no longer accept more than one SQL statement per request.
query_databasepreviously executed any statement that did not begin withSELECT,WITH,TABLE, orVALUESthrough pgx'sExec, which falls back to the PostgreSQL simple query protocol whenever no bind parameters are supplied. That protocol accepts several semicolon-separated statements in one message, so a caller could append their own statements to a request, includingSET TRANSACTION READ WRITEorCOMMIT; BEGIN READ WRITE, and then write to the database. On a read-only connection every statement now runs through the extended query protocol, which carries exactly one statement per message and rejects anything else. Note that a leading comment was enough to reach the vulnerable path, so no unusual keyword was required. Connections configured withallow_writes: truekeep the previous behaviour, including support for multi-statement scripts. -
The read-only statement guard now recognises the transaction access mode, which it previously ignored altogether: it matched only the literal strings
transaction_read_onlyanddefault_transaction_read_only, and neverREAD WRITE. It now also rejectsSET SESSION CHARACTERISTICS,RESET ALL,DISCARD, transaction control statements,SET ROLE,SET SESSION AUTHORIZATION,ALTER ROLE,ALTER USER,ALTER DATABASE, and operations whose effects fall outside the transaction and which a read-only transaction therefore does not prevent:DOblocks,COPY ... TO PROGRAM, the server-side file functions, anddblink. Statements are matched after comments and literals have been stripped, so a comment can no longer be used to split a keyword, and the guard now runs on thecount_rowswhereargument and theexecute_explainquery as well as onquery_database. Rejected statements are logged in full, since a rejection records an attempt to escape read-only mode. -
The session-level
default_transaction_read_onlysetting is now re-applied when a pooled connection is released, and a connection whose state cannot be confirmed is discarded rather than reused. Previously the setting was applied only when the connection was established, so aRESET ALLorDISCARD ALLpersisted on that pooled connection for every later caller that received it. This was not a route throughquery_database, which set the access mode on each of its own transactions and so remained protected; it mattered for any path that did not, such as the custom tool executor'spl-functype, and it left the session-level layer disabled for the rest of that connection's life. -
The custom tools framework now has end-to-end test coverage. It is invisible until an operator sets
custom_definitions_path, so none of its three tool types had ever been exercised through the MCP protocol, which is how a tool type came to depend on a single guardrail without anyone noticing.TestCustomToolsRespectReadOnlyenables the framework the way an operator would and checks each type against a read-only connection:sqlandpl-dotools read successfully and are refused when they write, and apl-functool is refused up front. Each case also verifies out of band that the database was not modified and that no temporary function survived. -
Custom
pl-dotools no longer interpolate arguments between fixed dollar-quote delimiters. The wrapper used$mcp_custom_tool$and$mcp_args$, and JSON encoding does not escape a dollar sign, so an argument value containing either delimiter closed the quoting early and had the remainder of the value parsed as SQL. Combined with the simple query protocol used for these statements, that was a complete bypass of read-only mode that the statement guard never saw. Delimiters are now generated per invocation, forpl-functools as well, and an argument that carries one is refused. -
Read-only transactions now request their access mode as part of
BEGINrather than issuingSET TRANSACTION READ ONLYas a following statement, inquery_database,count_rows,execute_explain, and the custom tool executor. The transaction is therefore never briefly writable, and the mode cannot fail to apply independently of the transaction starting.
Fixed
-
Metadata loader no longer emits duplicate column entries for a column that participates in more than one foreign-key constraint. The
fk_columnsCTE produced one row per foreign key, so the downstream LEFT JOIN multiplied the per-column rows;get_schema_infoconsequently listed the affected column once per foreign key. The CTE now aggregates every reference into one ordered, de-duplicated array per column, andColumnInfo.ForeignKeyRefsis a[]stringso all references are surfaced (comma-separated in thefk_refoutput column) rather than silently discarding all but one. (#171) -
The edit and delete icons in the conversation history list no longer overlap the conversation title; the list item now reserves enough space for both controls so long titles ellipsize cleanly. (#73)
-
Every HTTP error response is now a consistent JSON object (
{"error": "..."}) with an appropriate status code, including framework-level cases that previously bypassed the normal handlers and returned a plaintext or empty body: an unknown route (404), a method mismatch (405), an oversized request body (413, distinguished from other body-read failures), and a panic inside a handler (500; previously the connection was simply closed with no response at all). A sharedinternal/httperrorhelper backs the new panic recovery and 404 catch-all middleware, as well as the handlers that previously wrote plaintext errors viahttp.Error(/mcp/v1,/api/chat/compact,/api/openapi.json, and the session-auth wrapper). Request bodies on/api/chat/compact,/api/databases/select, and the/api/conversations*endpoints are now also capped at 10MB, matching the existing/mcp/v1limit. The HTTP server now setsReadHeaderTimeout,ReadTimeout, andIdleTimeoutto guard against slow-header and slow-body attacks; these fire before a request reaches a handler, so (unlike the cases above) there is no response body to produce. Every 405 response now also sets theAllowheader naming the supported method(s), per RFC 7231 §6.5.5.GET /api/databases's 405 uses the sharedinternal/httperrorwriter;POST /api/databases/select's 405 uses the endpoint's own documented{"success": false, "error": "..."}shape instead, matching its other error responses (400, 404, 403) rather than the bare{"error": "..."}it previously returned only for that one status code. (#189) -
Tool and resource responses now show the operator-configured database display name instead of the raw connection details. Previously,
query_database,get_schema_info,execute_explain,count_rows, andsimilarity_searchonly masked the password in the connection string they showed the caller, leaving the real host, port, and database name visible;pg://system_infowas worse, reporting the live-resolved server address frominet_server_addr(), which can be an internal-only address (a container or pod IP) that differs from, and may be unreachable via, the address the operator actually configured. A newClient.DisplayName()now backs every one of these responses with the connection's configuredname(falling back to a password-masked connection string when none is configured);pg://system_infogains aconnection_namefield and itshost/portfields now reflect the configured values rather than a live-resolved one. Ad-hoc connection strings a caller types inline (thepostgres://...mini-DSL supported byquery_database) are intentionally left as-is, since echoing back what the caller themselves supplied is not a leak. (#187) -
The token and user file watchers now detect changes delivered through an atomically-swapped symlink, such as a Kubernetes-projected Secret or ConfigMap volume, or any tool that renames a new version into place. Previously the watcher matched events by exact filename and only handled
Write/Create, so a symlink swap on a different directory entry (for example Kubernetes' own..datasymlink) never triggered a reload and updates only took effect on restart. The watcher now reacts to any event in the watched directory and re-resolves and hashes the watched path's content to decide whether a reload is warranted, catching changes that never touch the watched filename directly while still ignoring unrelated activity elsewhere in the directory. (#186) -
Metadata loader now tolerates tables with zero columns (e.g.
CREATE TABLE foo()). The query LEFT JOINs against the per-column catalog, so a zero-column table produced a row whosecolumn_name,data_type, andis_nullablewere all NULL; the scan declared those targets as plainstringand aborted withcannot scan NULL into *string, failing the entire metadata load and surfacing as the misleadingno database connection configured for this tokenerror. The three columns are now scanned assql.NullStringand zero-column tables appear in the metadata with an emptyColumnsslice. (#126) -
HTTP transport now returns
202 Acceptedwith an empty body for JSON-RPC notifications, per JSON-RPC 2.0 §4.1 and the MCP streamable HTTP transport spec. Previously, the server replied to notifications with a200 OKresponse that had noidfield, which is itself not a valid JSON-RPC message and caused strict clients (such as the .NET MCP SDK) to throw on every notification. Unknown notification methods are now also acknowledged silently rather than receiving a-32601error reply. (#142) -
Stdio transport now correctly distinguishes JSON-RPC notifications (no
idmember) from requests with an explicit"id": null(per JSON-RPC 2.0 §4.1). A request with"id": nulltargeting an unknown method previously matched the samereq.ID == nilguard used to suppress notification replies and was silently dropped; it now receives the required-32601 Method not foundresponse. The hardcodednotifications/initializedcase was likewise affected and is now filtered uniformly with all other notifications at the read loop, using the samehasIDFieldraw-bytes probe introduced for the HTTP transport in #142. (#152) -
JSON-RPC response now always serializes the
idfield, including when it is null. Per JSON-RPC 2.0 §5.1, the response object MUST include the id member; the value is the originating request's id, or null when the id cannot be determined (parse error / invalid request) or when the request itself used"id": null. TheJSONRPCResponse.IDJSON tag previously usedomitempty, which caused Go's encoder to drop the field for nil interface values — producing a response without anidfield, which is itself a malformed JSON-RPC body. This affects both the HTTP and stdio transports. (#152) -
Database switching via
select_database_connectionnow persists correctly in HTTP mode for unbound API tokens.GetAccessibleDatabasespreviously returned only the first configured database for unbound tokens, causinggetClientto silently override the user's selection on every subsequent tool call. The method now returns all databases, matching the behavior ofCanAccessDatabase. (#117) -
Added a JSON-RPC
pinghandler on both stdio and HTTP transports so MCP clients that issuepingduring initialization or health checks receive a compliant{}result instead of a-32601 Method not founderror. The stdio handler suppresses responses to notification-style pings (noid) per JSON-RPC 2.0 §4.1. (#167)
Added
-
The installer detects running Postgres instances and offers to connect to them, with automatic database listing.
-
Added
--detect/-Detectflag for non-interactive auto-connection to detected Postgres instances. -
The installer detects previous installations and offers to update the binary or reconfigure the database connection instead of re-running the full install flow.
-
Schema metadata cache now refreshes automatically based on a configurable TTL. The
metadata_ttldatabase option controls how long cached metadata remains valid (default: 5 minutes). This fixes an issue whereget_schema_inforeturned stale results when tables were created outside the MCP server or when using read-only database connections. -
HTTP authentication is now configurable in Docker deployments via the
PGEDGE_AUTH_ENABLEDenvironment variable. Auth remains enabled by default; setPGEDGE_AUTH_ENABLED=falseonly in trusted local development environments (for example, when connecting Claude throughmcp-remotewith a fixed bearer token and needing access to multiple databases). The setting is honored by both the single-database and multi-database initialization paths. (#167) -
Google Gemini is now a supported LLM provider. Configure via
gemini_api_key/gemini_api_key_filein the config file or via thePGEDGE_GEMINI_API_KEY/GEMINI_API_KEYenvironment variables.
Fixed
- Fixed port detection on Windows; the installer now reliably detects Postgres instances on all network addresses.
1.0.0 - 2026-03-27
Changed
-
Docker container now defaults to stdio mode instead of HTTP mode. HTTP mode requires setting
PGEDGE_HTTP_ENABLED=true. This allows the Docker image to work with stdio-based MCP clients such as the Docker Desktop MCP Toolkit, Claude Code, and Claude Desktop. -
Docker init script output now goes to
stderrinstead ofstdout; this keepsstdoutclean for the MCP protocol in stdio mode. -
User and token initialization (
INIT_USERS,INIT_TOKENS) now only runs when HTTP mode is enabled. Stdio mode does not use HTTP authentication. -
Quickstart demo files (
docker-compose.yml,.env.example,pgedge-ait-demo.sh) are now served from the GitHub repository instead ofdownloads.pgedge.com. The Northwind example database download is unchanged.
Fixed
-
Queries with trailing semicolons no longer produce a SQL syntax error when the server auto-appends a
LIMITclause. The server now strips trailing semicolons before appendingLIMIT/OFFSET. -
MCP tools (
query_database,count_rows,get_schema_info) now load metadata synchronously on the first call instead of returning a "database is still initializing" error. This eliminates the unnecessary LLM retry that previously occurred on every first tool call. -
Database connection timeout now defaults to 10 seconds instead of blocking for 60+ seconds when a target host is unreachable. A new
connect_timeoutconfiguration option allows customization of the timeout duration.
Security
-
The server now rejects queries that reference the
transaction_read_onlyordefault_transaction_read_onlysettings when the database connection is in read-only mode. This prevents single-statement bypass attacks (such as PL/pgSQLDOblocks withset_config()) that could circumvent theSET TRANSACTION READ ONLYguardrail. -
The system prompt sent to all LLM providers (Anthropic, OpenAI, Ollama) now includes explicit safety instructions that forbid attempts to bypass read-only mode when the database connection does not allow writes.
Added
-
MCP tool selection guidance for AI agents. The server now sends a server-level
instructionsfield during the MCP initialize handshake, directing agents to prefer MCP tools overpsqland shell commands. Tool descriptions include explicit "use this instead of..." language to steer tool selection. A new documentation page covers recommendedCLAUDE.mdand.cursorrulesconfiguration for reinforcing tool preference. -
Cursor IDE plugin manifest and setup guide.
-
OpenAPI 3.0.3 specification and interactive API browser. The server now provides a programmatic OpenAPI specification covering all REST endpoints. The specification is available at the
/api/openapi.jsonendpoint (no authentication required), through the-openapiCLI flag, and as a static file in the documentation. Usemake openapito regenerate the static copy. The MkDocs site includes a ReDoc-powered interactive API browser under For Developers. API responses include an RFC 8631Linkheader for automatic discovery by tools such asrestish. -
Write query confirmation in the CLI and Web UI. When a database has write access enabled, the user is prompted to confirm DDL and DML queries before the server executes the queries. Declining a query returns an error to the LLM without executing the query.
-
MCP tool annotations on the
query_databasetool. The server setsdestructiveHintandreadOnlyHintannotations per the MCP 2025-03-26 specification; third-party MCP clients that support annotations can use the annotations to prompt for user confirmation. -
Partitioned table support in
get_schema_info. The tool now recognizes partitioned parent tables (shown asPARTITIONED TABLE) and hides child partitions by default. Use the newinclude_partitionsparameter to reveal child partitions when needed. This reduces context window usage for databases with daily or other time-based partitioning schemes. -
Example DBA toolkit as a drop-in YAML custom definitions file (
examples/pgedge-postgres-mcp-dba.yaml). The toolkit provides three pl-do tools:get_top_queriesfor top resource-consuming query analysis,analyze_db_healthfor seven-category database health checks, andrecommend_indexesfor two-tier index recommendations with optional HypoPG simulation. -
Multi-host database connection support for high availability and failover. The
hostsarray replaces the singlehostandportfields when connecting to multiple PostgreSQL servers. The server generates a libpq-compatible multi-host connection string and passes the list to pgx for automatic failover. -
The
target_session_attrsoption controls read-write routing for multi-host connections. Accepted values includeany,read-write,read-only,primary,standby, andprefer-standby. -
Pool health check and connection lifetime settings for database connections. The
pool_health_check_periodoption sets the interval for background health checks; thepool_max_conn_lifetimeoption sets the maximum age of a pooled connection before the server closes the connection. -
The
PGEDGE_DB_HOSTSenvironment variable configures multi-host connections as a comma-separated list ofhost:portpairs. -
The
--db-hostsCLI flag specifies multiple database hosts as a comma-separatedhost:portlist. The--db-target-session-attrsflag sets the session routing attribute for multi-host connections. -
Configuration validation rejects entries that specify both the single-host
hostfield and the multi-hosthostsarray. The validator also checks thattarget_session_attrscontains a recognized value. -
The web client displays multi-host connection details in the database selector, showing each configured host and port alongside the connection status.
-
GitHub Codespaces demo environment for one-click evaluation of the MCP server in a browser-based development environment.
-
One-command installers for Claude Code and Claude Desktop that automate binary download, configuration generation, and client registration.
-
--max-retriesflag for the kb-builder controls how many times transient embedding API errors are retried. The default is 5; set to 0 for unlimited retries. Backoff is capped at 60 seconds. -
Connection status field (
connectedorunavailable) in thelist_database_connectionstool response; databases with statusunavailableare connected on demand when selected. -
Trace file logging for deep diagnostics of MCP interactions. Enable with
-trace-file <path>, thetrace_fileconfiguration option, or thePGEDGE_TRACE_FILEenvironment variable. The server writes JSONL entries for tool calls, resource reads, prompt executions, HTTP requests, LLM interactions, database switches, configuration reloads, and session events.
Internal
- Replaced the CGO SQLite driver with a pure Go driver, enabling fully static binaries without a C compiler dependency.
Improved
-
Expanded the configuration reference in
configuration.mdwith database connection options (allow_writes,allow_llm_switching,allowed_pl_languages, pool settings, and access control), LLM proxy options, and previously undocumented CLI flags (-debug,-db-*, user management flags). Added missing entries to the environment variable reference and example configuration file. -
Comprehensive documentation expansion to improve Context7 benchmark coverage across all ten benchmark categories:
-
New
row-level-security.mdguide covering PostgreSQL RLS/CLS integration with the MCP server, including per-user database connections, session variable patterns, column-level security with grants and views, and a multi-tenant worked example. -
New
distributed-deployment.mdguide covering multi-instance deployment with shared filesystem and object storage patterns, nginx and AWS ALB load balancer configuration, Docker Compose multi-instance example, Kubernetes deployment with ConfigMap and init containers, and knowledge base synchronization. -
New
custom-knowledgebase-tutorial.mdwith an end-to-end tutorial for building custom knowledge bases from domain documentation, including schema documentation patterns, business rules glossaries, KB builder configuration, and the internal SQLite database schema. -
New
client-examples.mdwith complete Python and JavaScript client implementations covering authentication, schema retrieval, query execution, database switching, TSV parsing, knowledgebase search, token lifecycle management, and error handling with automatic retry. -
New
error-reference.mddocumenting all HTTP status codes, JSON-RPC error codes, authentication errors, tool-specific errors, database access errors, and troubleshooting steps. -
Expanded
claude_desktop.mdwith a getting started guide, build instructions, YAML configuration examples, natural language query flow explanation, command-line flags reference, setup verification checklist, and detailed troubleshooting. -
Expanded
authentication.mdwith a database access control section documentingavailable_to_usersauthorization, a per-token database binding section, an authorization model summary, and a token lifecycle management section covering expiration detection, automatic re-authentication, and best practices for programmatic clients. -
Expanded
api-reference.mdwith schema retrieval examples includingcurlcommands with authentication, TSV response parsing in Python and JavaScript, query execution examples with comprehensive error handling and retry logic, a query error reference table, and result format documentation. -
Expanded
deploy_docker.mdwith a complete consolidateddocker-compose.yml, a full environment variable reference, a quick start guide, and Docker health check documentation. -
Expanded
multiple_db_config.mdwith Python and JavaScript client integration examples, access denied error handling, and a configuration settings reference clarifying the relationship betweenllm_connection_selectionandallow_llm_switching.
-
Fixed
-
The server no longer exits when configured databases are unreachable at startup (#82). In STDIO mode, each database connection is now attempted independently; failures are logged as warnings and the server starts with whichever databases are reachable. Unreachable databases are connected on demand when a tool or the user selects them. The
list_database_connectionstool reports each database asconnectedorunavailable. -
Closed database clients are no longer returned from the client manager cache. Previously, background cleanup or database switching could close a client while tool registries still held a reference, causing intermittent "Connection pool not found" errors. Retrieval points now check a closed flag and transparently create a fresh client when needed.
-
The
default_transaction_read_onlysession parameter is now set with aSETcommand after connection instead of as a startup parameter. Connection poolers such as PgBouncer and HAProxy do not support arbitrary startup parameters; the previous approach caused connections to fail with an "unsupported startup parameter" error. -
Ollama embedding generation no longer retries or fails the entire batch when a chunk exceeds the model's context length. The builder detects the error immediately, progressively truncates the text at word boundaries (75 %, 50 %, 25 %), and skips the chunk with a warning if all attempts fail.
-
Custom
pl-doandpl-functools no longer appear intools/listwhen their language is not inallowed_pl_languages. Previously the language check only happened at execution time; the server now filters PL tools at registration time so clients only see tools they can use. -
Fixed PL/Perl custom tools (
pl-funcandpl-do) failing with "Unable to load JSON.pm into plperl" when using trustedplperl. Trustedplperlcannot load external Perl modules, so the wrapper now uses PostgreSQL'sjsonb_each_text()via SPI to parse arguments instead ofJSON.pm. Untrustedplperlucontinues to useJSON.pmas before. -
Fixed Web GUI losing connection when switching between databases. The server now returns proper JSON error responses when the database is temporarily unavailable during switching, and the client handles these transient states gracefully with automatic retry logic instead of showing a disconnection error.
-
SIGHUP configuration reload now invalidates stale database connections. Previously, reloading the configuration did not close connections whose parameters had changed, leaving the server with outdated connection settings until restart.
-
The
-add-user,-add-token, and related user/token management commands now respect theuser_fileandtoken_filepaths from the server configuration file. Previously, these commands used hardcoded default paths regardless of the configuration, which could cause users or tokens to be added to the wrong file when custom paths were configured. The commands use the priority order: CLI flag > config file > default path. When-user-fileor-token-fileis explicitly provided on the command line, no configuration file is required (except for-add-tokenwhich needs database names from the config). This allows Docker containers and scripts to use these commands without a configuration file by specifying paths directly.
1.0.0-beta3 - 2026-01-21
Added
Custom Tools
- New custom tools feature for defining database operations as callable MCP tools via YAML configuration
- Three tool types are supported:
sql: Execute parameterized SQL queries with$1,$2, etc. placeholderspl-do: Execute PL/* DO blocks (anonymous functions) with automatic result handling viaset_config/current_settingpl-func: Create temporary PL/* functions with proper RETURN types
- Security controls via
allowed_pl_languagesconfiguration per database to restrict which procedural languages can be used - Language support includes plpgsql, plpython3u, plv8, and plperl with
automatic code wrapping and
mcp_return()helper function - Configurable per-tool timeout support
- Comprehensive validation of tool definitions at startup
LLM Database Connection Switching
- New
list_database_connectionstool allows LLMs to discover available database connections - New
select_database_connectiontool allows LLMs to switch between databases during a conversation - New
llm_connection_selectionconfiguration option to enable/disable the feature (disabled by default for security) - New
allow_llm_switchingper-database option to exclude specific connections from LLM switching (defaults to true when feature is enabled) - Real-time UI updates in web client when LLM switches databases
- CLI notification message when LLM switches databases
Prompt Argument Types
- Prompt arguments now support a
typefield with valuesstring(default) orboolean - Boolean arguments render as toggle switches in the web GUI instead of text fields
- Custom prompts in YAML can specify argument types for improved UI rendering
Fixed
-
The conversation history panel is now expanded by default when the web GUI loads, improving accessibility to past conversations.
-
Fixed Web GUI database switching causing JSON parse error and disconnect loop. The
selectDatabasefunction inuseDatabases.jsnow checksresponse.okbefore parsing the response as JSON; the auth middleware and database API handlers now return consistent JSON error responses instead of plain text. -
Improved login error messages in the web GUI. Authentication failures now display user-friendly messages like "Invalid username or password. Please try again." instead of technical RPC error codes.
-
Standardized default configuration file paths for consistency. All config files now use the
postgres-mcpprefix and search/etc/pgedge/first:- Config:
postgres-mcp.yaml(previouslypgedge-postgres-mcp.yaml) - Tokens:
postgres-mcp-tokens.yaml(previouslypgedge-postgres-mcp-tokens.yaml) - Users:
postgres-mcp-users.yaml(previouslypgedge-postgres-mcp-users.yaml) - Secret:
postgres-mcp.secret(previouslypgedge-postgres-mcp.secret)
- Config:
-
Improved error messages when the MCP server is unavailable. The web GUI now displays user-friendly messages for 502/503/504 errors instead of showing raw HTML error pages from the proxy.
-
Fixed DDL and DML statements silently failing when
allow_writesis enabled. Thequery_databasetool now usestx.Exec()for DDL (CREATE, DROP, ALTER, TRUNCATE) and DML (INSERT, UPDATE, DELETE) statements instead oftx.Query(), which could cause statements to not execute properly due to pgx's prepared statement caching behavior. DML statements with RETURNING clauses continue to usetx.Query()to capture returned rows.
1.0.0-beta2 - 2026-01-13
Added
Write Access Mode
- New
allow_writesconfiguration option for database connections- Disabled by default (read-only mode) for safety
- When enabled, allows the LLM to execute DDL (CREATE, DROP, ALTER) and DML (INSERT, UPDATE, DELETE) statements
- Automatic schema metadata refresh after DDL operations to keep
get_schema_inforesults current
- Visual warnings for write-enabled databases:
- Web client: Prominent amber warning banner when connected to a write-enabled database
- Web client: Warning chip indicator in database selector popover
- CLI:
[WRITE-ENABLED]indicator in/list databasesoutput - CLI: Warning message when switching to a write-enabled database
- Added
allow_writesfield topg://system_inforesource output - Updated
query_databasetool description to dynamically indicate write access status
Token Management
- New
count_rowstool for lightweight row counting before querying large tables - Pagination support (
offsetparameter) inquery_databasetool for paging through large result sets - Truncation detection in query results (fetches limit+1 rows to show "more data available" indicator)
Configuration Templates
- Added example configuration files in
examples/directory:pgedge-postgres-mcp-http.yaml.example- MCP server HTTP mode configpgedge-postgres-mcp-stdio.yaml.example- MCP server stdio mode configpgedge-nla-cli-http.yaml.example- CLI client HTTP mode configpgedge-nla-cli-stdio.yaml.example- CLI client stdio mode configpostgres-mcp-users.yaml.example- User authentication templatepostgres-mcp-tokens.yaml.example- Token authentication template
CLI Features
- Added
-mcp-server-configcommand line flag for specifying the MCP server config file path in stdio mode
CI/CD
- Claude PR review GitHub Action workflow for automated code reviews
- CodeRabbit configuration for additional PR analysis
Knowledgebase Builder
-
Hybrid chunking algorithm for improved RAG quality:
- Two-pass algorithm: Pass 1 splits at semantic boundaries, Pass 2 merges undersized chunks
- Structural element preservation: Code blocks, tables, lists, and blockquotes are kept intact when possible
- Full heading hierarchy tracking: Chunks include breadcrumb context (e.g., "API Reference > Authentication > OAuth")
- Smart splitting for oversized elements: Large code blocks split at line boundaries, tables at row boundaries, paragraphs at sentence boundaries
- Chunk metadata now includes
HeadingPath(full hierarchy) andElementTypes(structural element types in chunk)
-
Maintains Ollama compatibility with existing size limits (300 words / 3000 chars)
Changed
CLI Command Consistency
- Simplified LLM command names:
/set llm-provider→/set provider/set llm-model→/set model/show llm-provider→/show provider/show llm-model→/show model
- Moved standalone listing commands under
/list:/tools→/list tools/resources→/list resources/prompts→/list prompts
- Added
/list providerscommand to list available LLM providers - Reorganized
/helpoutput into logical sections
Token Efficiency
- Query results now returned in TSV format instead of JSON for better token efficiency
- Custom SQL resource data returned in TSV format
get_schema_infotool returns results in TSV format with additional relevant information and supports more targeted calls- Removed redundant resource for retrieving schema info
Model Selection
- Model family matching when reloading saved conversations (handles
date-suffixed model names like
claude-opus-4-5-20251101) - Web UI now uses family matching for model selection persistence
- CLI now restores database preference on load
- Added debug messages for model loading troubleshooting
Documentation
- Comprehensive documentation restructuring for online publication
- Added configuration setup instructions to README Web Client and CLI sections
- Added Quickstart guide
- Updated security documentation
- Added conversations API and database selection API documentation
- Fixed various documentation formatting issues and environment variable references
Docker
- Renamed
mcp-servertopostgres-mcpin Docker configuration (#12)
Fixed
- CLI preference saving now works correctly
- Fixed test expecting wrong number of resources (1 instead of 2)
- Updated tests to expect 7 tools after count_rows addition
- Various typo fixes in documentation and configuration
1.0.0-beta1 - 2025-12-15
Changed
This release marks the transition from alpha to beta status, indicating the software is now feature-complete and ready for broader testing.
Internal
- Updated Claude Code configuration
1.0.0-alpha6 - 2025-12-12
Added
CLI Features
- Added
noneauthentication mode for CLI client to connect to servers with authentication disabled (-mcp-auth-mode none)
Knowledgebase
- Release workflow now builds kb.db with embeddings from all three providers (OpenAI, Voyage AI, Ollama)
Changed
Naming
- Renamed server binary from
pgedge-postgres-mcptopgedge-nla-svr
Knowledgebase Builder
- Reduced chunk sizes to avoid hitting Ollama model token limits (250 words target, 300 max)
- Added character-based chunk limiting (3000 chars max) for technical content with high character-to-word ratios (XML/SGML)
- Improved markdown cleanup when building knowledgebase (removes images, link URLs, simplifies table borders)
- Added ASCII table border simplification to reduce token usage
Fixed
- Fixed lint warnings in test files (unused types and unusedwrite warnings)
- Fixed tests that failed without database connection
- Fixed git branch handling when building knowledgebase (uses checkout -B to handle behind branches)
- Improved git pull handling when checking out branches for knowledgebase building
Infrastructure
- Added Claude Code instructions file for development workflow
1.0.0-alpha5 - 2025-12-11
Added
CLI Features
- Ability to cancel in-flight LLM queries by pressing Escape key
- Support for enabling/disabling colorization via configuration
- Terminal sanitization at startup to recover from broken terminal states
Web UI
- Login page animation
Changed
Cross-Platform Compatibility
- Refactored syscall package usage into platform-specific files for proper cross-platform support (darwin, linux, windows)
- Improved terminal raw mode handling in escape key detection
UI Improvements
- Updated web UI styling to better match pgEdge Cloud design
- Removed unnecessary checks for LLM environment variables
Fixed
Critical Bug Fixes
- CLI Output Bug: Fixed staircase indentation issue where CLI output progressively indented to the right
- Terminal State: Fixed terminal being left in broken state after CLI exit due to raw mode not being properly restored
- Compaction Bug: Fixed tool_use and tool_result messages being separated during conversation compaction, which caused Anthropic API errors (400 Bad Request with "tool_use_id not found")
Other Fixes
- Fixed first load of a conversation not displaying correctly in web UI
- Fixed broken documentation URLs in README after docs restructuring
Infrastructure
- GitHub Actions workflow improvements:
- Build kb.db using goreleaser
- Include kb.db in kb-builder archive
- Use token to pull private repos
- Create bin directory before using it in release workflow
- Fix build command issues
- Fix dirty git state error in workflow
- Use architecture-specific runners for release builds
1.0.0-alpha4 - 2025-12-08
Added
Conversation History
- Server-side conversation storage using SQLite database for persistent chat history
- REST API endpoints for conversation CRUD operations
(
/api/conversations/*) - Web client conversation panel with list, load, rename, and delete functionality
- CLI conversation history commands (
/history,/new,/save) when running in HTTP mode with authentication - Automatic provider/model restoration when loading saved conversations
- Database connection tracking per conversation
- History replay with muted colors when loading CLI conversations
- Auto-save behavior in web client after first assistant response
Configuration
- Configuration options to selectively enable/disable built-in tools,
resources, and prompts via the
builtinssection in the config file - Disabled features are not advertised to the LLM and return errors if called directly
- The
read_resourcetool is always enabled as it's required for listing resources
LLM Provider Improvements
- Dynamic model retrieval for Anthropic provider - available models are now fetched from the API instead of being hardcoded
- Display client and server version numbers in CLI startup banner
Build & Release
- GitHub Actions workflow for automated release artifact generation using goreleaser
- Local verification script for goreleaser artifacts
1.0.0-alpha3 - 2025-12-03
Added
- Web client documentation with screenshots demonstrating all UI features
- Documentation comparing RAG (Retrieval-Augmented Generation) and MCP approaches
- Optional Docker container variant with pre-built knowledgebase database included
Changed
Naming
- Renamed the server to pgEdge MCP Server (from pgEdge NLA Server)
Knowledgebase System
search_knowledgebasetool now accepts arrays for product and version filters, allowing searches across multiple products/versions in a single query- Parameter names changed from
project_name/project_versiontoproject_names/project_versions(arrays of strings) - Added
list_productsparameter to discover available products and versions before searching - Improved
search_knowledgebasetool prompt with:- Critical warning about exact product name matching at the top
- Step-by-step workflow guidance (discover products first, then search)
- Troubleshooting section for zero-result scenarios
- Updated examples showing realistic product names
Fixed
- Docker Compose health check now uses correctly renamed binary
1.0.0-alpha2 - 2025-11-27
Added
Token Usage Optimization
- Smart auto-summary mode for
get_schema_infotool when database has >10 tables - New
compactparameter forget_schema_infoto return minimal output (table names + column names only) - Token estimation and tracking for individual tool calls (visible in debug mode)
- Resource URI display in activity log for
read_resourcecalls - Proactive compaction triggered by token count threshold (15,000 tokens)
- Rate limit handling with automatic 60-second pause and retry
Prompt Improvements
- Added
<fresh_data_required>guidance to prompts to prevent LLM from using stale information when database state may have changed - Updated
explore-databaseprompt with rate limit awareness and tool call budget guidance - Enhanced prompts guide LLMs to minimize tool calls for token efficiency
Multiple Database Support
- Configure multiple PostgreSQL database connections with unique names
- Per-user access control via
available_to_usersconfiguration field - Automatic default database selection based on user accessibility
- Runtime database switching in both CLI and Web clients
- Database selection persistence across sessions via user preferences
- CLI commands:
/list databases,/show database,/set database <name> - Web UI database selector in status banner with connection details
- Database switching disabled during LLM query processing to prevent data consistency issues
- Improved error messages when no databases are accessible to a user
- API token database binding via
-token-databaseflag or interactive prompt during token creation
Knowledgebase System
- Complete knowledgebase system with SQLite backend for offline documentation search
search_knowledgebaseMCP tool for semantic similarity search across pre-built documentation- KB builder utility for creating knowledgebase from markdown, HTML, SGML, and DocBook XML sources
- Support for multiple embedding providers (Voyage AI, OpenAI, Ollama) in knowledgebase
- Project name and version filtering for targeted documentation search
- Independent API key configuration for knowledgebase (separate from embedding and LLM sections)
- DocBook XML format support for PostGIS and similar documentation
- Optional project version field in documentation sources
LLM Provider Management
- Dynamic Ollama model selection with automatic fallback to available models
- Per-provider model persistence in CLI (remembers last-used model for each provider)
- Per-provider model persistence in Web UI (using localStorage)
- Automatic preference validation and sanitization on load
- Default provider priority order (Anthropic → OpenAI → Ollama)
- Preferred Ollama models list with tool-calling support verification
- Runtime model validation against provider APIs before selection
- Provider selection now validates that provider is actually configured
- Filtered out Claude Opus models from Anthropic (causes tool-calling errors)
- Filtered out embedding, audio, and image models from OpenAI model list
Security & Authentication
- Rate limiting for failed authentication attempts (configurable window and max attempts)
- Account lockout after repeated failed login attempts
- Per-IP rate limiting to prevent brute force attacks
Tools, Resources, and Prompts
- Support for custom user-defined prompts in
examples/pgedge-postgres-mcp-custom.yaml - Support for custom user-defined resources in custom definitions file
- New
execute_explaintool for query performance analysis - Enhanced tool descriptions with usage examples and best practices
- Added a schema-design prompt for helping design database schemas
Changed
Naming & Organization
- Renamed the project to pgEdge Natural Language Agent
- Renamed all binaries and configuration files for consistency:
- Server:
pgedge-pg-mcp-svr->pgedge-postgres-mcp - CLI:
pgedge-pg-mcp-cli->pgedge-nla-cli - Web UI:
pgedge-mcp-web->pgedge-nla-web - KB Builder:
kb-builder->pgedge-nla-kb-builder
- Server:
- Default server configuration files now use
pgedge-postgres-mcp-*.yamlnaming - Default CLI configuration files now uses
pgedge-nla-cli.yamlnaming - Custom definitions file:
pgedge-postgres-mcp-custom.yaml - Updated all documentation and examples to reflect new naming
Configuration
- Reduced default similarity_search token budget from 2500 to 1000
- Default OpenAI model changed from
gpt-5-maintogpt-5.1 - Independent API key configuration for knowledgebase, embedding, and LLM sections
- Support for KB-specific environment variables:
PGEDGE_KB_VOYAGE_API_KEY,PGEDGE_KB_OPENAI_API_KEY
UI/UX Improvements
- Enhanced LLM system prompts for better tool usage guidance
- CLI now saves current model when switching providers
- Web UI correctly remembers per-provider model selections
- Improved error messages and warnings for invalid configurations
- CLI
/list tools,/list resources, and/list promptscommands now sort output alphabetically - Web UI favicon added
- Web UI: Moved Clear button from floating position to bottom toolbar (next to Settings)
- Web UI: Added Save Chat button to export conversation history as Markdown
- Web UI: Improved light mode contrast with gray page background for paper effect
Fixed
- Critical: Fixed Voyage AI API response parsing (was expecting flat
embeddingfield, actual API returnsdata[].embedding) - Security: Custom HTTP handlers (
/api/chat/compact,/api/llm/chat) now require authentication when auth is enabled (provider/model listing endpoints remain public for login page) - CLI no longer randomly switches to wrong provider/model on startup
- Invalid provider/model combinations in preferences now automatically corrected with warnings
- Web UI model selection now persists correctly across provider switches
- Applied consistent code formatting with
gofmt - Removed unused kb-dedup utility
- Fixed gocritic lint warnings
- Fixed data race in rate limiter tests
Infrastructure
- Docker images updated to Go 1.24
- CI/CD workflows upgraded to Go 1.24 with PostgreSQL 18 testing support
- Start scripts refactored with variable references for improved maintainability
1.0.0-alpha1 - 2025-11-21
Added
Core Features
- Model Context Protocol (MCP) server implementation
- PostgreSQL database connectivity with read-only transaction enforcement
- Support for stdio and HTTP/HTTPS transport modes
- TLS support with certificate and key configuration
- Hot-reload capability for authentication files (tokens and users)
- Automatic detection and handling of configuration file changes
MCP Tools (5)
query_database- Execute SQL queries in read-only transactionsget_schema_info- Retrieve database schema informationhybrid_search- Advanced search combining BM25 and MMR algorithmsgenerate_embeddings- Create vector embeddings for semantic searchread_resource- Access MCP resources programmatically
MCP Resources (3)
pg://stat/activity- Current database connections and activitypg://stat/database- Database-level statisticspg://version- PostgreSQL version information
MCP Prompts (3)
- Semantic search setup workflow
- Database exploration guide
- Query diagnostics helper
CLI Client
- Production-ready command-line chat interface
- Support for multiple LLM providers (Anthropic, OpenAI, Ollama)
- Anthropic prompt caching (90% cost reduction)
- Dual mode support (stdio subprocess or HTTP API)
- Persistent command history with readline support
- Bash-like Ctrl-R reverse incremental search
- Runtime configuration with slash commands
- User preferences persistence
- Debug mode with LLM token usage logging
- PostgreSQL-themed UI with animations
Web Client
- Modern React-based web interface
- AI-powered chat for natural language database interaction
- Real-time PostgreSQL system information display
- Light/dark theme support with system preference detection
- Responsive design for desktop and mobile
- Token usage display for LLM interactions
- Chat history with prefix-based search
- Message persistence and state management
- Debug mode with toggle in preferences popover
- Markdown rendering for formatted responses
- Inline code block rendering
- Auto-scroll with smart positioning
Authentication & Security
- Token-based authentication with SHA256 hashing
- User-based authentication with password hashing
- API token management with expiration support
- File permission enforcement (0600 for sensitive files)
- Per-token connection isolation
- Input validation and sanitization
- Secure password storage in
.pgpassfiles - TLS/HTTPS support for encrypted communications
Docker Support
- Complete Docker Compose deployment configuration
- Multi-stage Docker builds for optimized images
- Container health checks
- Volume management for persistent data
- Environment-based configuration
- CI/CD pipeline for Docker builds
Infrastructure
- Comprehensive CI/CD with GitHub Actions
- Automated testing for server, CLI client, and web client
- Docker build and deployment validation
- Documentation build verification
- Code linting and formatting checks
- Integration tests with real PostgreSQL databases
LLM Proxy
- JSON-RPC proxy for LLM interactions from web clients
- Support for multiple LLM providers
- Request/response logging
- Error handling and status reporting
- Dynamic model name loading for Anthropic
- Improved tool call parsing for Ollama
Documentation
- Comprehensive user guide covering all features
- Configuration examples for server, tokens, and clients
- API reference documentation
- Architecture and internal design documentation
- Security best practices guide
- Troubleshooting guide with common issues
- Docker deployment guide
- Building chat clients tutorial with Python examples
- Query examples demonstrating common use cases
- CI/CD pipeline documentation
- Testing guide for contributors