fix: review fixes for MCP and API key feature

- Remove spurious ALTER TABLE client_targets DROP DEFAULT from migration
  (Prisma schema drift side-effect unrelated to this PR)
- Surface revoke errors in ApiKeysPage UI via deleteApiKey.isError
- Fix UpdateProjectInput.color type to allow null, removing unsafe cast
  in the update_project MCP tool handler
This commit is contained in:
simon.franken
2026-03-16 15:35:06 +01:00
parent 64211e6a49
commit a7ab55932f
4 changed files with 13 additions and 15 deletions

View File

@@ -1,6 +1,3 @@
-- AlterTable
ALTER TABLE "client_targets" ALTER COLUMN "working_days" DROP DEFAULT;
-- CreateTable -- CreateTable
CREATE TABLE "api_keys" ( CREATE TABLE "api_keys" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,

View File

@@ -139,15 +139,8 @@ function buildMcpServer(user: AuthenticatedUser): McpServer {
clientId: z.string().uuid().optional().describe('Move project to a different client'), clientId: z.string().uuid().optional().describe('Move project to a different client'),
}, },
}, },
async (args) => { async ({ id, name, description, color, clientId }) => {
const { id, ...rest } = args as { const project = await projectService.update(id, userId, { name, description, color, clientId });
id: string;
name?: string;
description?: string;
color?: string | null;
clientId?: string;
};
const project = await projectService.update(id, userId, rest as import('../types').UpdateProjectInput);
return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] }; return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] };
} }
); );

View File

@@ -31,7 +31,7 @@ export interface CreateProjectInput {
export interface UpdateProjectInput { export interface UpdateProjectInput {
name?: string; name?: string;
description?: string; description?: string;
color?: string; color?: string | null;
clientId?: string; clientId?: string;
} }

View File

@@ -49,8 +49,8 @@ export function ApiKeysPage() {
try { try {
await deleteApiKey.mutateAsync(id); await deleteApiKey.mutateAsync(id);
setRevokeConfirmId(null); setRevokeConfirmId(null);
} catch (err) { } catch (_err) {
// error is surfaced inline via mutation state // error rendered below the table row via deleteApiKey.error
} }
} }
@@ -81,6 +81,14 @@ export function ApiKeysPage() {
</div> </div>
)} )}
{deleteApiKey.isError && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
{deleteApiKey.error instanceof Error
? deleteApiKey.error.message
: "Failed to revoke API key"}
</div>
)}
{isLoading ? ( {isLoading ? (
<div className="text-center py-12 text-gray-400 text-sm">Loading...</div> <div className="text-center py-12 text-gray-400 text-sm">Loading...</div>
) : !apiKeys || apiKeys.length === 0 ? ( ) : !apiKeys || apiKeys.length === 0 ? (