fix(servers): persist secretDelivery and entrypoint
Some checks failed
CI/CD / lint (pull_request) Successful in 1m29s
CI/CD / typecheck (pull_request) Successful in 1m18s
CI/CD / test (pull_request) Successful in 1m26s
CI/CD / smoke (pull_request) Failing after 1m59s
CI/CD / build (pull_request) Successful in 6m42s
CI/CD / publish (pull_request) Has been skipped
Some checks failed
CI/CD / lint (pull_request) Successful in 1m29s
CI/CD / typecheck (pull_request) Successful in 1m18s
CI/CD / test (pull_request) Successful in 1m26s
CI/CD / smoke (pull_request) Failing after 1m59s
CI/CD / build (pull_request) Successful in 6m42s
CI/CD / publish (pull_request) Has been skipped
`mcpctl patch server my-grafana secretDelivery=injector` printed "patched server 'my-grafana'" and changed nothing. The repository maps update/create fields explicitly, one by one, so a new column silently does nothing until it is added there — and the silence is total: the API returns 200, the CLI reports success, and `get -o yaml` still shows the old value. Caught by trying to migrate a real server, not by any test. Adds the two fields to both create and update, plus tests that assert the mapping directly. Those tests fail against the unfixed repository (3 of 4) — verified before keeping them. This class of bug will recur: the mapping is manual and nothing links a schema column to it. The tests at least make the next omission loud for these two fields. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vybEitX4FykeMatKe5Xki
This commit is contained in:
@@ -34,6 +34,8 @@ export class McpServerRepository implements IMcpServerRepository {
|
||||
env: data.env,
|
||||
healthCheck: (data.healthCheck ?? Prisma.JsonNull) as Prisma.InputJsonValue,
|
||||
volumes: data.volumes,
|
||||
secretDelivery: data.secretDelivery,
|
||||
entrypoint: (data.entrypoint ?? Prisma.DbNull) as Prisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -53,6 +55,8 @@ export class McpServerRepository implements IMcpServerRepository {
|
||||
if (data.env !== undefined) updateData['env'] = data.env;
|
||||
if (data.healthCheck !== undefined) updateData['healthCheck'] = (data.healthCheck ?? Prisma.JsonNull) as Prisma.InputJsonValue;
|
||||
if (data.volumes !== undefined) updateData['volumes'] = data.volumes;
|
||||
if (data.secretDelivery !== undefined) updateData['secretDelivery'] = data.secretDelivery;
|
||||
if (data.entrypoint !== undefined) updateData['entrypoint'] = (data.entrypoint ?? Prisma.JsonNull) as Prisma.InputJsonValue;
|
||||
|
||||
return this.prisma.mcpServer.update({ where: { id }, data: updateData });
|
||||
}
|
||||
|
||||
48
src/mcpd/tests/mcp-server-repository-fields.test.ts
Normal file
48
src/mcpd/tests/mcp-server-repository-fields.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* The server repository maps update/create fields explicitly, field by field.
|
||||
* That means a new column silently does nothing until it is added here — and
|
||||
* the failure is invisible: `mcpctl patch server x secretDelivery=injector`
|
||||
* returns "patched" while the value never changes.
|
||||
*
|
||||
* Caught exactly that way in production. These assert the mapping instead.
|
||||
*/
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { McpServerRepository } from '../src/repositories/mcp-server.repository.js';
|
||||
import type { PrismaClient } from '@prisma/client';
|
||||
|
||||
function prismaSpy() {
|
||||
const update = vi.fn(async ({ data }: { data: Record<string, unknown> }) => data);
|
||||
const create = vi.fn(async ({ data }: { data: Record<string, unknown> }) => data);
|
||||
return { spy: { mcpServer: { update, create } } as unknown as PrismaClient, update, create };
|
||||
}
|
||||
|
||||
describe('McpServerRepository field mapping', () => {
|
||||
it('persists secretDelivery on update', async () => {
|
||||
const { spy, update } = prismaSpy();
|
||||
await new McpServerRepository(spy).update('id1', { secretDelivery: 'injector' });
|
||||
expect(update.mock.calls[0]?.[0].data).toMatchObject({ secretDelivery: 'injector' });
|
||||
});
|
||||
|
||||
it('persists entrypoint on update', async () => {
|
||||
const { spy, update } = prismaSpy();
|
||||
await new McpServerRepository(spy).update('id1', { entrypoint: ['/bin/x', '--flag'] });
|
||||
expect(update.mock.calls[0]?.[0].data).toMatchObject({ entrypoint: ['/bin/x', '--flag'] });
|
||||
});
|
||||
|
||||
it('leaves both untouched when not supplied', async () => {
|
||||
const { spy, update } = prismaSpy();
|
||||
await new McpServerRepository(spy).update('id1', { description: 'x' });
|
||||
const data = update.mock.calls[0]?.[0].data ?? {};
|
||||
expect(data).not.toHaveProperty('secretDelivery');
|
||||
expect(data).not.toHaveProperty('entrypoint');
|
||||
});
|
||||
|
||||
it('persists secretDelivery on create', async () => {
|
||||
const { spy, create } = prismaSpy();
|
||||
await new McpServerRepository(spy).create({
|
||||
name: 'x', description: '', transport: 'STDIO', replicas: 1, env: [], volumes: [],
|
||||
secretDelivery: 'injector',
|
||||
} as never);
|
||||
expect(create.mock.calls[0]?.[0].data).toMatchObject({ secretDelivery: 'injector' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user