MCP Protocol Implementation
This document describes how the Natural Language Agent implements the Model Context Protocol (MCP).
Protocol Version
This server is dual-era: it implements both 2024-11-05 (a
connection-scoped initialize handshake) and the current revision,
2026-07-28 (stateless, per-request negotiation), on both transports at
the same time.
A request whose params._meta carries
io.modelcontextprotocol/protocolVersion is served under the modern,
2026-07-28 model: see MCP Specification
Compliance for the full negotiation rules,
required Streamable HTTP headers, and what was deliberately not adopted
from that revision.
Every other request, including every initialize handshake, is served
under this server's original, legacy behavior, unchanged: the server
negotiates the version during the handshake and never simply echoes the
version a client requests. A client that asks for a revision the server
does not implement receives 2024-11-05 in the initialize response,
and it should then decide whether to continue or disconnect. A client
that omits protocolVersion also receives 2024-11-05. An initialize
request whose parameters are malformed, such as a protocolVersion that
is not a string, fails with a -32602 Invalid params error on both
transports.
Transport Modes
The server supports two transport modes:
1. stdio Mode (Default)
Used by: Claude Desktop and other MCP clients
Communication: JSON-RPC 2.0 over standard input/output
How it works:
- Server reads JSON-RPC messages from
stdin - Server writes JSON-RPC responses to
stdout - Errors and diagnostics written to
stderr
Starting in stdio mode:
./bin/pgedge-postgres-mcp
2. HTTP/HTTPS Mode
Used by: Web applications, direct API access, custom integrations
Communication: JSON-RPC 2.0 over HTTP/HTTPS with streaming support
Endpoints:
POST /mcp/v1- JSON-RPC endpointGET /health- Health check endpoint
How it works:
- Client sends HTTP POST request with JSON-RPC payload
- Server processes request and returns JSON-RPC response
- Supports HTTP/1.1 with keep-alive
- Streaming responses for real-time data
Starting in HTTP mode:
./bin/pgedge-postgres-mcp -http -addr ":8080"
MCP Capabilities
The server advertises these capabilities during initialization:
{
"capabilities": {
"tools": {},
"resources": {},
"prompts": {}
}
}
Tools
Five callable functions for database interaction and management:
- query_database - Execute SQL queries against PostgreSQL
- get_schema_info - Get detailed database schema information
- similarity_search - Advanced hybrid search combining vector similarity with BM25 and MMR
- generate_embedding - Generate vector embeddings from text using AI models
- read_resource - Read MCP resources by URI
Note: Database connection is configured at server startup via environment variables, config file, or command-line flags. There are no runtime connection management tools.
For detailed tool documentation, see Tools Documentation.
Resources
Read-only resources for system information:
- pg://system_info - PostgreSQL version and system information
For detailed resource documentation, see Resources Documentation.
Prompts
Custom prompts for common database tasks (extensible).
JSON-RPC Messages
Request Format
{
"jsonrpc": "2.0",
"id": 1,
"method": "method_name",
"params": {
"param1": "value1",
"param2": "value2"
}
}
Response Format
Success:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"data": "..."
}
}
Error:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid Request"
}
}
MCP Methods
Initialize
Establish connection and negotiate capabilities.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
}
},
"clientInfo": {
"name": "my-client",
"version": "1.0.0"
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {},
"resources": {},
"prompts": {}
},
"serverInfo": {
"name": "pgedge-postgres-mcp",
"version": "1.0.0-alpha2"
}
}
}
List Tools
Get available tools.
Request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "query_database",
"description": "Execute a natural language query...",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Natural language question..."
}
},
"required": ["query"]
}
}
// ... more tools
]
}
}
Call Tool
Execute a tool.
Request:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {
"query": "Show me all active users"
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Natural Language Query: Show me all active users\n\nGenerated SQL:\nSELECT * FROM users WHERE status = 'active';\n\nResults:\n..."
}
]
}
}
List Resources
Get available resources.
Request:
{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/list",
"params": {}
}
Response:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"resources": [
{
"uri": "pg://system_info",
"name": "postgresql_system_info",
"description": "Returns PostgreSQL version...",
"mimeType": "application/json"
}
// ... more resources
]
}
}
Read Resource
Read a specific resource.
Request:
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/read",
"params": {
"uri": "pg://system_info"
}
}
Response:
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"contents": [
{
"uri": "pg://system_info",
"mimeType": "application/json",
"text": "{\"version\":\"PostgreSQL 16.0\",\"os\":\"Linux\",\"arch\":\"x86_64\"}"
}
]
}
}
Error Codes
Standard JSON-RPC error codes:
| Code | Message | Meaning |
|---|---|---|
| -32700 | Parse error | Invalid JSON |
| -32601 | Method not found | Method doesn't exist |
| -32602 | Invalid params | Invalid method parameters, or (modern requests only) an unknown resource URI in resources/read |
| -32603 | Internal error | Server error |
Codes specific to a resource lookup:
| Code | Message | Meaning |
|---|---|---|
| -32002 | Resource not found | Unknown resource URI in resources/read, on a legacy (pre-2026-07-28) request |
Codes specific to the modern (2026-07-28) protocol, defined by the
spec's reserved range for protocol-level errors; see MCP Specification
Compliance:
| Code | Message | Meaning |
|---|---|---|
| -32020 | HeaderMismatch | A Streamable HTTP header does not match the request body, or a required one is missing |
| -32021 | MissingRequiredClientCapability | The request needs a client capability not declared in _meta.clientCapabilities (not currently reachable: this server requires no client capability for anything it implements) |
| -32022 | UnsupportedProtocolVersion | The requested _meta.protocolVersion is not one this server implements |
Streaming (HTTP Mode)
In HTTP mode, the server supports streaming responses for tools that generate large outputs.
How it works:
- Client sends request to
/mcp/v1 - Server processes request
- Server streams response chunks
- Client receives progressive updates
Example - Streaming Query Results:
The query_database tool can stream results as they're generated:
const response = await fetch('https://localhost:8080/mcp/v1', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'query_database',
arguments: { query: 'Show all users' }
}
})
});
// Stream response
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Process chunk
console.log(new TextDecoder().decode(value));
}
Authentication (HTTP Mode)
HTTP mode uses Bearer token authentication.
Request with authentication:
curl -X POST https://localhost:8080/mcp/v1 \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{...}'
Unauthenticated request returns 401:
{
"error": "Unauthorized"
}
For token management, see Authentication Guide.
Protocol Extensions
Multi-Database Support
The query_database tool supports querying multiple databases:
Temporary connection:
{
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {
"query": "Show users at postgres://otherhost/otherdb"
}
}
}
Change default connection:
{
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {
"query": "set default database to postgres://newhost/newdb"
}
}
}
Read-Only Transaction Enforcement
All queries via query_database are executed in read-only transactions:
BEGIN;
SET TRANSACTION READ ONLY;
-- User's query here
COMMIT;
This prevents data modification while allowing full read access.
Implementation Details
Server Lifecycle
-
Startup:
- Parse configuration
- Connect to PostgreSQL
- Load database metadata (tables, columns, types)
- Initialize tool and resource registries
- Start transport (stdio or HTTP)
-
Request Handling:
- Receive JSON-RPC message
- Validate request format
- Route to appropriate handler
- Execute tool/resource
- Return JSON-RPC response
-
Shutdown:
- Close database connections
- Clean up resources
- Exit gracefully
Metadata Loading
On startup, the server loads:
- All tables and views (excluding system schemas)
- Column names and data types
- Nullability constraints
- Table and column comments from
pg_description
This metadata is used for:
- Schema information tools
- Natural language to SQL conversion
- Query validation
Error Handling
The server provides detailed error messages in development:
- Database connection errors
- SQL syntax errors
- Permission errors
- API key errors
In production, errors are logged but sanitized in responses.
Testing MCP Protocol
With MCP Inspector
npx @modelcontextprotocol/inspector /path/to/bin/pgedge-postgres-mcp
The MCP Inspector provides a web UI for testing:
- Initialize connection
- List tools and resources
- Call tools interactively
- View request/response JSON
With curl (HTTP mode)
# Start server
./bin/pgedge-postgres-mcp -http -no-auth
# Initialize
curl -X POST http://localhost:8080/mcp/v1 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "curl", "version": "1.0"}
}
}'
# List tools
curl -X POST http://localhost:8080/mcp/v1 \
-H "Content-Type": "application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'
Integration Tests
The project includes comprehensive integration tests for MCP protocol compliance:
go test ./test/... -v -run TestMCPCompliance
Tests verify:
- Protocol version negotiation
- Tool and resource registration
- Request/response format
- Error handling
- Streaming support (HTTP mode)
For more testing information, see Testing Guide.