diff --git a/src/mcpd/src/repositories/mcp-server.repository.ts b/src/mcpd/src/repositories/mcp-server.repository.ts index 2e26621..731cdee 100644 --- a/src/mcpd/src/repositories/mcp-server.repository.ts +++ b/src/mcpd/src/repositories/mcp-server.repository.ts @@ -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 }); } diff --git a/src/mcpd/tests/mcp-server-repository-fields.test.ts b/src/mcpd/tests/mcp-server-repository-fields.test.ts new file mode 100644 index 0000000..843abaf --- /dev/null +++ b/src/mcpd/tests/mcp-server-repository-fields.test.ts @@ -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 }) => data); + const create = vi.fn(async ({ data }: { data: Record }) => 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' }); + }); +});