Skip to content

Commit a1b8088

Browse files
author
Lasim
committed
feat(all): add slug field for server identification and validation change
1 parent 210a576 commit a1b8088

File tree

14 files changed

+75
-11
lines changed

14 files changed

+75
-11
lines changed

services/backend/api-spec.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18835,6 +18835,13 @@
1883518835
"maxLength": 255,
1883618836
"description": "Server name (1-255 characters)"
1883718837
},
18838+
"slug": {
18839+
"type": "string",
18840+
"minLength": 1,
18841+
"maxLength": 255,
18842+
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
18843+
"description": "URL-friendly unique identifier (lowercase alphanumeric with hyphens, e.g., \"my-server-name\")"
18844+
},
1883818845
"description": {
1883918846
"type": "string",
1884018847
"minLength": 1,

services/backend/api-spec.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13240,6 +13240,13 @@ paths:
1324013240
minLength: 1
1324113241
maxLength: 255
1324213242
description: Server name (1-255 characters)
13243+
slug:
13244+
type: string
13245+
minLength: 1
13246+
maxLength: 255
13247+
pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
13248+
description: URL-friendly unique identifier (lowercase alphanumeric with
13249+
hyphens, e.g., "my-server-name")
1324313250
description:
1324413251
type: string
1324513252
minLength: 1

services/backend/src/routes/mcp/servers/schemas.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,13 @@ export const UPDATE_GLOBAL_SERVER_REQUEST_SCHEMA = { type: 'object',
10761076
properties: {
10771077
// All fields from SERVER_FIELDS but optional
10781078
name: SERVER_FIELDS.name,
1079+
slug: {
1080+
type: 'string',
1081+
minLength: 1,
1082+
maxLength: 255,
1083+
pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$',
1084+
description: 'URL-friendly unique identifier (lowercase alphanumeric with hyphens, e.g., "my-server-name")'
1085+
},
10791086
description: SERVER_FIELDS.description,
10801087
long_description: SERVER_FIELDS.long_description,
10811088
repository_url: SERVER_FIELDS.repository_url,

services/backend/src/routes/mcp/servers/update-global.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
// TypeScript interface for update request
1919
interface UpdateGlobalServerRequest {
2020
name?: string;
21+
slug?: string;
2122
description?: string;
2223
long_description?: string;
2324
repository_url?: string;
@@ -278,12 +279,19 @@ export default async function updateGlobalServer(server: FastifyInstance) {
278279
}, 'Failed to update global MCP server');
279280

280281
// Handle specific error cases
281-
if (error.message?.includes('UNIQUE constraint failed') ||
282+
if (error.message?.includes('UNIQUE constraint failed') ||
282283
error.message?.includes('already exists') ||
283284
error.message?.includes('duplicate')) {
285+
// Determine which field caused the conflict
286+
let conflictField = 'name or slug';
287+
if (error.message?.includes('slug')) {
288+
conflictField = 'slug';
289+
} else if (error.message?.includes('name')) {
290+
conflictField = 'name';
291+
}
284292
const errorResponse: ErrorResponse = {
285293
success: false,
286-
error: 'Server name already exists'
294+
error: `Server ${conflictField} already exists`
287295
};
288296
const jsonString = JSON.stringify(errorResponse);
289297
return reply.status(409).type('application/json').send(jsonString);

services/backend/src/services/mcpCatalogService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export interface CreateMcpServerRequest {
133133

134134
export interface UpdateMcpServerRequest {
135135
name?: string;
136+
slug?: string;
136137
description?: string;
137138
long_description?: string;
138139
repository_url?: string;
@@ -537,6 +538,7 @@ export class McpCatalogService {
537538

538539
// Only allow certain fields to be updated
539540
if (data.name !== undefined) updateData.name = data.name;
541+
if (data.slug !== undefined) updateData.slug = data.slug;
540542
if (data.description !== undefined) updateData.description = data.description;
541543
if (data.long_description !== undefined) updateData.long_description = data.long_description;
542544
if (data.repository_url !== undefined) updateData.repository_url = data.repository_url;

services/backend/tests/unit/routes/mcp/servers/update-global.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ describe('MCP Servers - Update Global', () => {
928928

929929
expect(response).toEqual({
930930
success: false,
931-
error: 'Server name already exists'
931+
error: 'Server name or slug already exists'
932932
});
933933
});
934934

services/frontend/src/components/admin/mcp-catalog/McpServerEditFormWizard.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ const initializeStorageWithData = (data: McpServerFormData) => {
302302
const formData = ref<McpServerFormData>({
303303
basic: {
304304
name: '',
305+
slug: '',
305306
description: '',
306307
long_description: '',
307308
category_id: '',

services/frontend/src/components/admin/mcp-catalog/ReviewStep.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ const formatJson = (jsonString: string) => {
127127
</dd>
128128
</div>
129129

130+
<div v-if="getBasicData().slug" class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
131+
<dt class="text-sm/6 font-medium text-gray-900">{{ t('mcpCatalog.form.review.fields.slug') }}</dt>
132+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
133+
<span class="font-mono text-xs">{{ getBasicData().slug }}</span>
134+
</dd>
135+
</div>
136+
130137
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
131138
<dt class="text-sm/6 font-medium text-gray-900">{{ t('mcpCatalog.form.review.fields.description') }}</dt>
132139
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">

services/frontend/src/components/admin/mcp-catalog/steps/BasicInfoStepEdit.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const STORAGE_KEY = 'edit_basic_data'
5858
// Default form data structure
5959
const defaultData: BasicInfoFormData = {
6060
name: '',
61+
slug: '',
6162
description: '',
6263
long_description: '',
6364
category_id: '',
@@ -190,6 +191,20 @@ onUnmounted(() => {
190191
/>
191192
</SharedFormField>
192193

194+
<!-- Slug -->
195+
<SharedFormField
196+
:label="t('mcpCatalog.form.basic.slug.label')"
197+
:description="t('mcpCatalog.form.basic.slug.description')"
198+
>
199+
<Input
200+
id="slug"
201+
:model-value="localData.slug"
202+
@update:model-value="(value) => updateField('slug', String(value))"
203+
:placeholder="t('mcpCatalog.form.basic.slug.placeholder')"
204+
class="font-mono text-sm"
205+
/>
206+
</SharedFormField>
207+
193208
<!-- Category -->
194209
<SharedFormField
195210
:label="t('mcpCatalog.form.basic.category.label')"

services/frontend/src/components/mcp-server/installation/InstallationInfo.vue

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ const formatDate = (dateString: string) => {
7373
</div>
7474
<div class="mt-6 border-t border-gray-100">
7575
<dl class="divide-y divide-gray-100">
76-
<!-- Installation Name -->
77-
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
78-
<dt class="text-sm/6 font-medium text-gray-900">{{ t('mcpInstallations.details.installationDetails.fields.installationName') }}</dt>
79-
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">
80-
{{ installation.installation_name }}
81-
</dd>
82-
</div>
83-
8476
<!-- Server Name -->
8577
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
8678
<dt class="text-sm/6 font-medium text-gray-900">{{ t('mcpInstallations.details.installationDetails.fields.server') }}</dt>

0 commit comments

Comments
 (0)