Documentation Index
Fetch the complete documentation index at: https://isthmus.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
Isthmus follows hexagonal architecture (ports & adapters). The core business logic is decoupled from infrastructure, making it straightforward to add new database backends or change the transport layer.
Directory structure
cmd/isthmus/ → Binary entrypoint (single binary, stdio + HTTP transports)
internal/
core/
port/ → Interfaces: SchemaExplorer, QueryExecutor, QueryAuditor
domain/ → SQL validation (pg_query AST), cardinality, column masking (MaskType)
service/ → Application services: QueryService
adapter/
mcp/ → MCP server factory + tool definitions (discover, describe_table, query)
postgres/ → PostgreSQL implementation of ports (pgxpool, information_schema, pg_stats)
audit/ → File-based audit logging (NDJSON)
config/ → Environment variable + CLI flag loading
policy/ → Policy engine: YAML loading, context enrichment, column masking
telemetry/ → OpenTelemetry: OTLP gRPC providers, metric instruments
Everything is under internal/ — Go’s convention enforces module privacy. There is no pkg/ directory.
Ports (interfaces)
The core defines three port interfaces that adapters implement:
// Schema discovery and table analysis
type SchemaExplorer interface {
ListSchemas(ctx context.Context) ([]SchemaInfo, error)
ListTables(ctx context.Context) ([]TableInfo, error)
DescribeTable(ctx context.Context, schema, tableName string) (*TableDetail, error)
Discover(ctx context.Context) (*DiscoveryResult, error)
}
// SQL execution
type QueryExecutor interface {
Execute(ctx context.Context, sql string) ([]map[string]any, error)
}
// Audit trail
type QueryAuditor interface {
Record(ctx context.Context, entry AuditEntry)
Close() error
}
Data flow
- The MCP client sends a tool call (e.g.
describe_table) over stdio or HTTP
- The MCP adapter routes it to the appropriate service
- The service calls the port interface (e.g.
SchemaExplorer.DescribeTable)
- The Postgres adapter executes SQL against the database
- The policy engine enriches the response and applies column masks (if configured)
- The result is serialized to JSON and returned to the client
- If OpenTelemetry is enabled, spans and metrics are recorded at each step
Dependency injection
All dependencies are wired at startup in cmd/isthmus/main.go. There is no global state, service locator, or runtime reflection:
// Adapters
pool := postgres.NewPool(ctx, cfg.DatabaseURL)
explorer := postgres.NewExplorer(pool, cfg.Schemas)
executor := postgres.NewExecutor(pool, cfg.ReadOnly, cfg.MaxRows, cfg.QueryTimeout)
// Optional decorators
if cfg.PolicyFile != "" {
pol, _ := policy.LoadFromFile(cfg.PolicyFile)
masks := policy.MaskSpec(pol.Context)
explorer = policy.NewPolicyExplorer(explorer, pol, masks)
}
// Services
querySvc := service.NewQueryService(validator, executor, auditor, logger)
// Server
mcpServer := mcp.NewServer(ver, explorer, querySvc, logger)
Adding a new database adapter
To add support for a new database (e.g. MySQL):
- Create
internal/adapter/mysql/
- Implement
port.SchemaExplorer and port.QueryExecutor
- Wire the new adapter in
cmd/isthmus/main.go based on the connection string scheme
- Add integration tests using testcontainers
The MCP layer, services, domain validation, and policy engine all work unchanged — they only depend on port interfaces.
Testing
Isthmus uses no mocks. Integration tests use testcontainers-go to spin up real PostgreSQL containers:
go test -race -count=1 ./... # All tests (needs Docker)
go test -short -race -count=1 ./... # Unit tests only
go test ./internal/core/domain/... # Domain tests only
go test ./internal/adapter/postgres/... # Postgres adapter tests
Domain tests (SQL validation, cardinality classification) run without Docker. Adapter and E2E tests require Docker for testcontainers.