Skip to content

Commit fd91667

Browse files
committed
feat(all): add deployments tab with team info and refactor catalog tables
Split the MCP Server Catalog into 4 separate table components with a new Deployments tab that displays team ownership information. Backend changes: - Add 'github' as valid source type to validation schemas - Update mcpCatalogService to LEFT JOIN with teams table for all servers - Return team_name, team_slug, and team_id for servers with owner_team_id - Update API response schemas to include optional team fields Frontend changes: - Create McpServerTableWrapper with shared UI (tabs, search, filters) - Split into 4 table components: All, Official Registry, Manual, Deployments - Add new Deployments tab showing servers with team ownership - Display team name as clickable link to team admin page - Update types to support github source and team information - Replace single McpServerTableColumns with modular component structure The Deployments tab shows github-sourced servers with team column linking to team management pages. Team information is now available for any server with an owner_team_id, regardless of source type.
1 parent b1115bf commit fd91667

File tree

15 files changed

+3273
-648
lines changed

15 files changed

+3273
-648
lines changed

services/backend/api-spec.json

Lines changed: 1000 additions & 155 deletions
Large diffs are not rendered by default.

services/backend/api-spec.yaml

Lines changed: 687 additions & 105 deletions
Large diffs are not rendered by default.

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ export const SEARCH_SERVERS_QUERY_SCHEMA = {
115115
},
116116
source: {
117117
type: 'string',
118-
enum: ['official_registry', 'manual'],
119-
description: 'Filter by server source: official_registry or manual'
118+
enum: ['official_registry', 'manual', 'github'],
119+
description: 'Filter by server source: official_registry, manual, or github'
120120
},
121121
featured: {
122122
type: 'string',
@@ -196,8 +196,8 @@ export const LIST_SERVERS_QUERY_SCHEMA = {
196196
},
197197
source: {
198198
type: 'string',
199-
enum: ['official_registry', 'manual'],
200-
description: 'Filter by source: official_registry for servers synced from MCP Registry, manual for manually added servers'
199+
enum: ['official_registry', 'manual', 'github'],
200+
description: 'Filter by source: official_registry for servers synced from MCP Registry, manual for manually added servers, github for GitHub deployments'
201201
},
202202
auto_install_new_default_team: {
203203
type: 'boolean',
@@ -1189,9 +1189,12 @@ export const SERVER_LIST_ENTITY_SCHEMA = {
11891189
author_name: { type: 'string', nullable: true, description: 'Author name' },
11901190
organization: { type: 'string', nullable: true, description: 'Organization' },
11911191
official_name: { type: 'string', nullable: true, description: 'Official registry name' },
1192-
source: { type: 'string', enum: ['official_registry', 'manual'], description: 'Source of the MCP server' },
1192+
source: { type: 'string', enum: ['official_registry', 'manual', 'github'], description: 'Source of the MCP server' },
11931193
created_at: { type: 'string', format: 'date-time', description: 'Creation timestamp' },
1194-
updated_at: { type: 'string', format: 'date-time', description: 'Last update timestamp' }
1194+
updated_at: { type: 'string', format: 'date-time', description: 'Last update timestamp' },
1195+
team_name: { type: 'string', nullable: true, description: 'Team name (present when server belongs to a team)' },
1196+
team_slug: { type: 'string', nullable: true, description: 'Team slug (present when server belongs to a team)' },
1197+
team_id: { type: 'string', nullable: true, description: 'Team ID (present when server belongs to a team)' }
11951198
},
11961199
required: ['id', 'name', 'slug', 'description', 'language', 'runtime', 'transport_type', 'visibility', 'status', 'featured', 'requires_oauth', 'source', 'created_at', 'updated_at']
11971200
} as const;
@@ -1477,7 +1480,7 @@ export interface ListServersQueryParams {
14771480
runtime?: string;
14781481
status?: McpServerStatus;
14791482
featured?: 'true' | 'false';
1480-
source?: 'official_registry' | 'manual';
1483+
source?: 'official_registry' | 'manual' | 'github';
14811484
auto_install_new_default_team?: boolean;
14821485
search?: string;
14831486
limit?: string;
@@ -1540,7 +1543,7 @@ export interface ServerEntity {
15401543
featured: boolean;
15411544
auto_install_new_default_team: boolean;
15421545
requires_oauth: boolean;
1543-
source: 'official_registry' | 'manual';
1546+
source: 'official_registry' | 'manual' | 'github';
15441547

15451548
// Official Registry Sync Tracking
15461549
official_name: string | null;
@@ -1781,9 +1784,13 @@ export interface ServerListEntity {
17811784
author_name: string | null;
17821785
organization: string | null;
17831786
official_name: string | null;
1784-
source: 'official_registry' | 'manual';
1787+
source: 'official_registry' | 'manual' | 'github';
17851788
created_at: string;
17861789
updated_at: string;
1790+
// Team information (present when server belongs to a team)
1791+
team_name?: string;
1792+
team_slug?: string;
1793+
team_id?: string;
17871794
}
17881795

17891796
export interface ListServersSuccessResponse {
@@ -1981,6 +1988,12 @@ export function formatServerListResponse(
19811988
official_name: server.official_name || null,
19821989
source: server.source || 'manual',
19831990
created_at: server.created_at.toISOString(),
1984-
updated_at: server.updated_at.toISOString()
1991+
updated_at: server.updated_at.toISOString(),
1992+
// Include team information if present (when server belongs to a team)
1993+
...(server.team_name && {
1994+
team_name: server.team_name,
1995+
team_slug: server.team_slug,
1996+
team_id: server.team_id
1997+
})
19851998
};
19861999
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface SearchServersQueryParams extends Omit<ListServersQueryParams, 'search'
1919
q: string; // Search query is required for search endpoint
2020
tags?: string; // Optional comma-separated tags filter
2121
sort_by?: 'name' | 'github_stars'; // Optional sort parameter
22-
source?: 'official_registry' | 'manual'; // Optional source filter
22+
source?: 'official_registry' | 'manual' | 'github'; // Optional source filter
2323
}
2424

2525
export default async function searchServers(server: FastifyInstance) {

services/backend/src/services/mcpCatalogService.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export interface CreateMcpServerRequest {
120120
featured?: boolean;
121121
auto_install_new_default_team?: boolean;
122122
requires_oauth?: boolean;
123-
source?: 'official_registry' | 'manual';
123+
source?: 'official_registry' | 'manual' | 'github';
124124

125125
// Official Registry Sync Tracking
126126
official_name?: string;
@@ -185,7 +185,7 @@ export interface McpServerFilters {
185185
runtime?: string;
186186
status?: 'active' | 'deprecated' | 'maintenance' | 'disabled';
187187
featured?: boolean;
188-
source?: 'official_registry' | 'manual';
188+
source?: 'official_registry' | 'manual' | 'github';
189189
search?: string;
190190
tags?: string;
191191
}
@@ -324,15 +324,31 @@ export class McpCatalogService {
324324
}
325325

326326
// Build the query with all conditions combined
327-
const baseQuery = this.db.select().from(this.mcpServers);
327+
// Always join with teams table to get team info for servers that have owner_team_id
328+
const schema = getSchema();
329+
const { teams } = schema;
330+
331+
const baseQuery = this.db
332+
.select()
333+
.from(this.mcpServers)
334+
.leftJoin(teams, eq(this.mcpServers.owner_team_id, teams.id));
335+
328336
const queryWithConditions = whereConditions.length > 0
329337
? baseQuery.where(and(...whereConditions))
330338
: baseQuery;
331339

332340
// Apply sorting based on sortBy parameter
333-
const servers = await (sortBy === 'github_stars'
341+
const results = await (sortBy === 'github_stars'
334342
? queryWithConditions.orderBy(desc(this.mcpServers.github_stars), asc(this.mcpServers.name))
335343
: queryWithConditions.orderBy(desc(this.mcpServers.featured), asc(this.mcpServers.name)));
344+
345+
// Map the joined results to include team information
346+
const servers = results.map((row: any) => ({
347+
...row.mcpServers,
348+
team_name: row.teams?.name || null,
349+
team_slug: row.teams?.slug || null,
350+
team_id: row.teams?.id || null
351+
}));
336352

337353
this.logger.info({
338354
operation: 'get_servers_for_user',

services/frontend/src/i18n/locales/en/mcp-catalog.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export default {
2727
created: 'Created',
2828
actions: 'Actions',
2929
properties: 'Properties',
30-
details: 'Details'
30+
details: 'Details',
31+
team: 'Team'
3132
},
3233
actions: {
3334
edit: 'Edit',
@@ -871,6 +872,7 @@ export default {
871872
all: 'All',
872873
official_registry: 'Official Registry',
873874
manual: 'Manual',
875+
deployments: 'Deployments',
874876
github: 'GitHub'
875877
},
876878
status: {

services/frontend/src/types/mcp-catalog.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface McpServerSearchParams {
44
language?: string
55
runtime?: string
66
status?: 'active' | 'deprecated' | 'maintenance' | 'disabled'
7-
source?: 'official_registry' | 'manual'
7+
source?: 'official_registry' | 'manual' | 'github'
88
featured?: boolean
99
sort_by?: 'name' | 'github_stars'
1010
limit?: number

0 commit comments

Comments
 (0)