fix(store): handle NULL JSONB columns in MCP server scan (#40)

* fix(store): handle NULL JSONB columns in MCP server scan

Scan JSONB nullable columns (args, headers, env, settings) into *[]byte
instead of directly into json.RawMessage to prevent silent scan failures
when database values are SQL NULL. Also initialize result slices with
make() to return empty JSON arrays instead of null.

* fix(store): keep settings scanning direct since column is NOT NULL
This commit is contained in:
Thieu Nguyen authored and GitHub committed 2026-03-02 16:45:02 +07:00
1 parent 53db5165a3
commit 846c217cf9
3 files changed
+27 -8

No files matched your search

+7
View File
@@ -55,6 +55,13 @@ func derefUUID(u *uuid.UUID) uuid.UUID {
return *u
}
func derefBytes(b *[]byte) []byte {
if b == nil {
return nil
}
return *b
}
// --- JSON helpers ---
func jsonOrEmpty(data []byte) []byte {
+11 -3
View File
@@ -74,9 +74,10 @@ func (s *PGMCPServerStore) GetServerByName(ctx context.Context, name string) (*s
func (s *PGMCPServerStore) scanServer(row *sql.Row) (*store.MCPServerData, error) {
var srv store.MCPServerData
var displayName, command, url, apiKey, toolPrefix *string
var args, headers, env *[]byte
err := row.Scan(
&srv.ID, &srv.Name, &displayName, &srv.Transport, &command,
&srv.Args, &url, &srv.Headers, &srv.Env,
&args, &url, &headers, &env,
&apiKey, &toolPrefix, &srv.TimeoutSec,
&srv.Settings, &srv.Enabled, &srv.CreatedBy, &srv.CreatedAt, &srv.UpdatedAt,
)
@@ -87,6 +88,9 @@ func (s *PGMCPServerStore) scanServer(row *sql.Row) (*store.MCPServerData, error
srv.Command = derefStr(command)
srv.URL = derefStr(url)
srv.ToolPrefix = derefStr(toolPrefix)
srv.Args = derefBytes(args)
srv.Headers = derefBytes(headers)
srv.Env = derefBytes(env)
if apiKey != nil && *apiKey != "" && s.encKey != "" {
decrypted, err := crypto.Decrypt(*apiKey, s.encKey)
if err != nil {
@@ -110,13 +114,14 @@ func (s *PGMCPServerStore) ListServers(ctx context.Context) ([]store.MCPServerDa
}
defer rows.Close()
var result []store.MCPServerData
result := make([]store.MCPServerData, 0)
for rows.Next() {
var srv store.MCPServerData
var displayName, command, url, apiKey, toolPrefix *string
var args, headers, env *[]byte
if err := rows.Scan(
&srv.ID, &srv.Name, &displayName, &srv.Transport, &command,
&srv.Args, &url, &srv.Headers, &srv.Env,
&args, &url, &headers, &env,
&apiKey, &toolPrefix, &srv.TimeoutSec,
&srv.Settings, &srv.Enabled, &srv.CreatedBy, &srv.CreatedAt, &srv.UpdatedAt,
); err != nil {
@@ -126,6 +131,9 @@ func (s *PGMCPServerStore) ListServers(ctx context.Context) ([]store.MCPServerDa
srv.Command = derefStr(command)
srv.URL = derefStr(url)
srv.ToolPrefix = derefStr(toolPrefix)
srv.Args = derefBytes(args)
srv.Headers = derefBytes(headers)
srv.Env = derefBytes(env)
if apiKey != nil && *apiKey != "" && s.encKey != "" {
if decrypted, err := crypto.Decrypt(*apiKey, s.encKey); err == nil {
srv.APIKey = decrypted
+9 -5
View File
@@ -113,15 +113,16 @@ func (s *PGMCPServerStore) ListAccessible(ctx context.Context, agentID uuid.UUID
}
defer rows.Close()
var result []store.MCPAccessInfo
result := make([]store.MCPAccessInfo, 0)
for rows.Next() {
var srv store.MCPServerData
var displayName, command, url, apiKey, toolPrefix *string
var toolAllowJSON, toolDenyJSON []byte
var args, headers, env *[]byte
var toolAllowJSON, toolDenyJSON *[]byte
if err := rows.Scan(
&srv.ID, &srv.Name, &displayName, &srv.Transport, &command,
&srv.Args, &url, &srv.Headers, &srv.Env,
&args, &url, &headers, &env,
&apiKey, &toolPrefix, &srv.TimeoutSec,
&srv.Settings, &srv.Enabled, &srv.CreatedBy, &srv.CreatedAt, &srv.UpdatedAt,
&toolAllowJSON, &toolDenyJSON,
@@ -132,6 +133,9 @@ func (s *PGMCPServerStore) ListAccessible(ctx context.Context, agentID uuid.UUID
srv.Command = derefStr(command)
srv.URL = derefStr(url)
srv.ToolPrefix = derefStr(toolPrefix)
srv.Args = derefBytes(args)
srv.Headers = derefBytes(headers)
srv.Env = derefBytes(env)
if apiKey != nil && *apiKey != "" && s.encKey != "" {
if decrypted, err := crypto.Decrypt(*apiKey, s.encKey); err == nil {
srv.APIKey = decrypted
@@ -142,10 +146,10 @@ func (s *PGMCPServerStore) ListAccessible(ctx context.Context, agentID uuid.UUID
info := store.MCPAccessInfo{Server: srv}
if toolAllowJSON != nil {
json.Unmarshal(toolAllowJSON, &info.ToolAllow)
json.Unmarshal(*toolAllowJSON, &info.ToolAllow)
}
if toolDenyJSON != nil {
json.Unmarshal(toolDenyJSON, &info.ToolDeny)
json.Unmarshal(*toolDenyJSON, &info.ToolDeny)
}
result = append(result, info)
}