Skip to content

Commit 468967d

Browse files
author
Lasim
committed
refactor(backend): improve user configuration validation logic
1 parent e79efc0 commit 468967d

File tree

4 files changed

+40
-12
lines changed

4 files changed

+40
-12
lines changed

services/backend/src/routes/mcp/user-configurations/create.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export default async function createUserConfigRoute(server: FastifyInstance) {
2323
}>('/teams/:teamId/mcp/installations/:installationId/user-configs', {
2424
preValidation: [
2525
requireAuthenticationAny(),
26-
requireOAuthScope('mcp:read'),
2726
requireTeamPermission('mcp.installations.edit')
2827
],
2928
schema: {

services/backend/src/routes/mcp/user-configurations/schemas.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,30 @@ export interface UpdateUserEnvRouteRequest {
390390
// Helper function to format user config response
391391
// eslint-disable-next-line @typescript-eslint/no-explicit-any
392392
export function formatUserConfigResponse(config: any): UserConfigData {
393+
// Helper to safely handle JSON parsing - config might already be parsed by service layer
394+
const safeJsonParse = (value: any, defaultValue: any) => {
395+
if (value === null || value === undefined || value === '') {
396+
return defaultValue
397+
}
398+
// If it's already parsed (object/array), return as-is
399+
if (typeof value !== 'string') {
400+
return value
401+
}
402+
// If it's a string, try to parse it
403+
try {
404+
return JSON.parse(value)
405+
} catch {
406+
return defaultValue
407+
}
408+
}
409+
393410
return {
394411
id: config.id,
395412
installation_id: config.installation_id,
396413
user_id: config.user_id,
397414
device_id: config.device_id || undefined,
398-
user_args: config.user_args ? JSON.parse(config.user_args) : undefined,
399-
user_env: config.user_env ? JSON.parse(config.user_env) : undefined,
415+
user_args: safeJsonParse(config.user_args, undefined),
416+
user_env: safeJsonParse(config.user_env, undefined),
400417
created_at: config.created_at.toISOString(),
401418
updated_at: config.updated_at.toISOString(),
402419
last_used_at: config.last_used_at ? config.last_used_at.toISOString() : undefined

services/backend/src/routes/mcp/user-configurations/update.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export default async function updateUserConfigurationRoute(fastify: FastifyInsta
4141
} catch (error) {
4242
fastify.log.error({
4343
operation: 'update_user_configuration',
44-
error,
44+
error: error instanceof Error ? {
45+
name: error.name,
46+
message: error.message,
47+
stack: error.stack
48+
} : { errorType: typeof error, errorValue: error },
4549
teamId,
4650
installationId,
4751
configId,
@@ -66,7 +70,7 @@ export default async function updateUserConfigurationRoute(fastify: FastifyInsta
6670
}
6771

6872
return reply.status(500).send({
69-
error: 'Internal server error'
73+
error: error instanceof Error ? error.message : 'Internal server error'
7074
});
7175
}
7276
}

services/backend/src/services/mcpUserConfigurationService.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { eq, and, desc } from 'drizzle-orm';
2+
import { eq, and, desc, ne } from 'drizzle-orm';
33
import { mcpUserConfigurations, mcpServerInstallations, mcpServers } from '../db/schema.sqlite';
44
import type { AnyDatabase } from '../db';
55
import type { FastifyBaseLogger } from 'fastify';
@@ -321,7 +321,7 @@ export class McpUserConfigurationService {
321321
eq(mcpUserConfigurations.user_id, userId),
322322
eq(mcpUserConfigurations.device_id, data.device_id),
323323
// Exclude current configuration
324-
eq(mcpUserConfigurations.id, configId)
324+
ne(mcpUserConfigurations.id, configId)
325325
)
326326
)
327327
.limit(1);
@@ -452,12 +452,20 @@ export class McpUserConfigurationService {
452452

453453
private validateUserEnv(userEnv: Record<string, string>, schema: any[]): void {
454454
// Validate user environment variables against schema
455-
const requiredVars = schema.filter((envVar: any) => envVar.required);
456-
457-
for (const requiredVar of requiredVars) {
458-
if (!userEnv[requiredVar.name] || userEnv[requiredVar.name].trim() === '') {
459-
throw new Error(`Required environment variable '${requiredVar.name}' is missing or empty`);
455+
// Only validate the fields that are actually being sent, not all required fields
456+
for (const [envVarName, envVarValue] of Object.entries(userEnv)) {
457+
const schemaEntry = schema.find((envVar: any) => envVar.name === envVarName)
458+
459+
if (schemaEntry) {
460+
// If this field is required and the sent value is empty, throw error
461+
if (schemaEntry.required && (!envVarValue || envVarValue.trim() === '')) {
462+
throw new Error(`Required environment variable '${envVarName}' is missing or empty`)
463+
}
464+
465+
// Additional type validation can be added here in the future
466+
// e.g., if (schemaEntry.type === 'number' && isNaN(Number(envVarValue)))
460467
}
468+
// Note: We don't validate fields that aren't in the schema - allowing flexibility
461469
}
462470
}
463471

0 commit comments

Comments
 (0)