diff --git a/src/cli/src/api-client.ts b/src/cli/src/api-client.ts index 9db4779..6acc36b 100644 --- a/src/cli/src/api-client.ts +++ b/src/cli/src/api-client.ts @@ -24,7 +24,10 @@ export class ApiError extends Error { function request(method: string, url: string, timeout: number, body?: unknown, token?: string): Promise> { return new Promise((resolve, reject) => { const parsed = new URL(url); - const headers: Record = { 'Content-Type': 'application/json' }; + const headers: Record = {}; + if (body !== undefined) { + headers['Content-Type'] = 'application/json'; + } if (token) { headers['Authorization'] = `Bearer ${token}`; } diff --git a/src/cli/tests/api-client.test.ts b/src/cli/tests/api-client.test.ts index ced8608..03c8f7d 100644 --- a/src/cli/tests/api-client.test.ts +++ b/src/cli/tests/api-client.test.ts @@ -21,6 +21,16 @@ beforeAll(async () => { res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 'srv-new', ...body })); }); + } else if (req.url === '/api/v1/servers/srv-1' && req.method === 'DELETE') { + // Fastify rejects empty body with Content-Type: application/json + const ct = req.headers['content-type'] ?? ''; + if (ct.includes('application/json')) { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: "Body cannot be empty when content-type is set to 'application/json'" })); + } else { + res.writeHead(204); + res.end(); + } } else if (req.url === '/api/v1/missing' && req.method === 'GET') { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); @@ -75,6 +85,12 @@ describe('ApiClient', () => { await expect(client.get('/anything')).rejects.toThrow(); }); + it('performs DELETE without Content-Type header', async () => { + const client = new ApiClient({ baseUrl: `http://localhost:${port}` }); + // Should succeed (204) because no Content-Type is sent on bodyless DELETE + await expect(client.delete('/api/v1/servers/srv-1')).resolves.toBeUndefined(); + }); + it('sends Authorization header when token provided', async () => { // We need a separate server to check the header let receivedAuth = ''; diff --git a/src/mcpd/src/repositories/project.repository.ts b/src/mcpd/src/repositories/project.repository.ts index ffee081..43f7861 100644 --- a/src/mcpd/src/repositories/project.repository.ts +++ b/src/mcpd/src/repositories/project.repository.ts @@ -1,11 +1,11 @@ import type { PrismaClient, Project } from '@prisma/client'; export interface ProjectWithRelations extends Project { - servers: Array<{ id: string; server: { id: string; name: string } }>; + servers: Array<{ id: string; projectId: string; serverId: string; server: Record & { id: string; name: string } }>; } const PROJECT_INCLUDE = { - servers: { include: { server: { select: { id: true, name: true } } } }, + servers: { include: { server: true } }, } as const; export interface IProjectRepository {