## Summary
- Fixes migration 008 failing with "column contains null values" error
- Adds COALESCE protection to handle 126 servers without status fields
- Includes test utilities to reproduce and verify fixes with production
data
## Problem
Migration 008 was failing in production because:
- 126 out of 781 servers have no `status` field in their JSON
- When extracting `rec.value->>'status'`, these became NULL values
- The `ALTER TABLE servers ALTER COLUMN status SET NOT NULL` constraint
then failed
## Solution
1. **Prevent NULLs during migration**: Use COALESCE to provide 'active'
default when status is NULL/empty
2. **Enhanced debugging**: Added RAISE NOTICE statements to log
problematic values
3. **Safety check**: Double-check that throws exception if any NULLs
remain
4. **Test utilities**: Created mirror_data tools to test migrations with
real production data
## Testing
Created and used `scripts/mirror_data/` tools to:
- Fetch all 781 servers from production API
- Load them into a test database
- Verify the migration fix works correctly
### Results:
- **Before fix**: 126 NULL status values created during migration
- **After fix**: 0 NULL status values, all servers have valid status
- Migration completes successfully with NOT NULL constraint
## Test plan
- [x] Tested locally with production data using mirror_data tools
- [x] Verified 0 NULL values after migration (vs 126 before)
- [x] Confirmed NOT NULL constraint adds successfully
- [ ] Deploy to staging and verify migration succeeds
- [ ] Monitor production deployment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR tries to ensure sane defaults for entries that for one
reason or another have NULL values for some of their properties.
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
---------
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
## Problem
Production deployment is failing with CrashLoopBackOff. Migration 008
fails with:
```
ERROR: column "status" of relation "servers" contains null values (SQLSTATE 23502)
```
## Root Cause
The migration extracts the `status` field from an unexpected location in
the data structure.
### Actual Data Structure (Before Migration)
In the current format, `status` is at the **top level** of the JSON, not
nested in official metadata:
```json
{
"name": "com.example/server",
"version": "1.0.0",
"status": "active", ← STATUS IS HERE (top level)
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "uuid",
"publishedAt": "2025-09-01T00:00:00Z",
"isLatest": true
// NO STATUS FIELD HERE
}
}
}
```
For 120 out of 765 servers, the status field is **missing entirely**:
```json
{
"name": "com.biodnd/agent-ip",
"version": "0.1.2",
// ← NO STATUS FIELD AT ALL
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "uuid",
"publishedAt": "2025-09-23T09:47:07Z",
"isLatest": true
// STILL NO STATUS FIELD HERE
}
}
}
```
### Original Migration Logic
```sql
IF official_meta IS NOT NULL THEN
UPDATE servers
SET
status = (official_meta->>'status'), -- Extracts NULL for all servers
```
**Result:**
- All 765 servers would have NULL status extracted
- Because `official_meta->>'status'` returns NULL (field doesn't exist
there)
- The migration tries to add `NOT NULL` constraint → fails
### Fixed Migration Logic
```sql
IF official_meta IS NOT NULL THEN
UPDATE servers
SET
status = COALESCE(NULLIF(rec.value->>'status', 'null'), 'active'),
```
**Result:**
- Extracts from top-level JSON where status actually exists
- `NULLIF(rec.value->>'status', 'null')` handles edge case of literal
string "null"
- `COALESCE(..., 'active')` provides fallback when status is missing
- All servers get valid status value
### Impact Breakdown
**645 servers with `"status": "active"`:**
- Before fix: Would extract NULL → migration fails
- After fix: Extracts "active" correctly
**120 servers with missing status field:**
- Before fix: Would extract NULL → migration fails
- After fix: Defaults to 'active' via COALESCE
## Example Servers with Missing Status
(First 9 discovered during investigation)
- com.biodnd/agent-ip
- io.github.jkakar/cookwith-mcp
- com.joelverhagen.mcp/Knapcode.SampleMcpServer
- io.github.Lyellr88/marm-mcp-server
- com.biodnd/agent-press
- io.github.timheuer/sampledotnetmcpserver
- io.github.CodeCraftersLLC/local-voice-mcp
- com.biodnd/agent-fin
- io.github.ruvnet/ruv-swarm
## Testing
Verified against production data (765 total servers):
- ✅ All servers have official metadata
- ✅ None have status in official metadata (expected)
- ✅ 645 have "status": "active" at top level
- ✅ 120 have missing status field (no field present)
- ✅ Fix extracts from correct location for all cases
## Impact
This unblocks production deployment. Once merged and released as v1.2.1,
the migration will:
1. Extract status from the correct location (top-level JSON)
2. Handle missing status fields with sensible defaults
3. Successfully add NOT NULL constraint
4. Allow pods to start normally
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)