Skip to content

Commit 45f7088

Browse files
committed
feat(backend): add tags filtering to MCP servers API and implement endpoint to retrieve unique tags
1 parent 756e607 commit 45f7088

File tree

9 files changed

+10224
-15750
lines changed

9 files changed

+10224
-15750
lines changed

package-lock.json

Lines changed: 9738 additions & 15726 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/backend/api-spec.json

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14337,6 +14337,15 @@
1433714337
"required": false,
1433814338
"description": "Filter by auto-install flag"
1433914339
},
14340+
{
14341+
"schema": {
14342+
"type": "string"
14343+
},
14344+
"in": "query",
14345+
"name": "tags",
14346+
"required": false,
14347+
"description": "Filter by tags (comma-separated list, e.g., \"mcp,web-scraping\"). Servers matching ANY of the provided tags will be returned."
14348+
},
1434014349
{
1434114350
"schema": {
1434214351
"type": "string",
@@ -14783,6 +14792,123 @@
1478314792
}
1478414793
}
1478514794
},
14795+
"/api/mcp/servers/tags": {
14796+
"get": {
14797+
"summary": "Get all unique tags",
14798+
"tags": [
14799+
"MCP Servers"
14800+
],
14801+
"description": "Retrieve all unique tags from MCP servers. Authentication is required. Results are filtered based on user permissions - users see tags from global servers plus their team servers, while global admins see tags from all servers.",
14802+
"security": [
14803+
{
14804+
"cookieAuth": []
14805+
}
14806+
],
14807+
"responses": {
14808+
"200": {
14809+
"description": "Tags retrieved successfully",
14810+
"content": {
14811+
"application/json": {
14812+
"schema": {
14813+
"type": "object",
14814+
"properties": {
14815+
"success": {
14816+
"type": "boolean",
14817+
"description": "Indicates successful tags retrieval"
14818+
},
14819+
"data": {
14820+
"type": "object",
14821+
"properties": {
14822+
"tags": {
14823+
"type": "array",
14824+
"items": {
14825+
"type": "string"
14826+
},
14827+
"description": "Array of unique tags sorted alphabetically"
14828+
},
14829+
"total": {
14830+
"type": "number",
14831+
"description": "Total number of unique tags"
14832+
}
14833+
},
14834+
"required": [
14835+
"tags",
14836+
"total"
14837+
],
14838+
"additionalProperties": false
14839+
}
14840+
},
14841+
"required": [
14842+
"success",
14843+
"data"
14844+
],
14845+
"additionalProperties": false,
14846+
"description": "Tags retrieved successfully"
14847+
}
14848+
}
14849+
}
14850+
},
14851+
"401": {
14852+
"description": "Unauthorized - Authentication required",
14853+
"content": {
14854+
"application/json": {
14855+
"schema": {
14856+
"type": "object",
14857+
"properties": {
14858+
"success": {
14859+
"type": "boolean",
14860+
"default": false,
14861+
"description": "Indicates the operation failed"
14862+
},
14863+
"error": {
14864+
"type": "string",
14865+
"description": "Error message describing what went wrong"
14866+
},
14867+
"details": {
14868+
"description": "Additional error details"
14869+
}
14870+
},
14871+
"required": [
14872+
"success",
14873+
"error"
14874+
],
14875+
"description": "Unauthorized - Authentication required"
14876+
}
14877+
}
14878+
}
14879+
},
14880+
"500": {
14881+
"description": "Internal Server Error",
14882+
"content": {
14883+
"application/json": {
14884+
"schema": {
14885+
"type": "object",
14886+
"properties": {
14887+
"success": {
14888+
"type": "boolean",
14889+
"default": false,
14890+
"description": "Indicates the operation failed"
14891+
},
14892+
"error": {
14893+
"type": "string",
14894+
"description": "Error message describing what went wrong"
14895+
},
14896+
"details": {
14897+
"description": "Additional error details"
14898+
}
14899+
},
14900+
"required": [
14901+
"success",
14902+
"error"
14903+
],
14904+
"description": "Internal Server Error"
14905+
}
14906+
}
14907+
}
14908+
}
14909+
}
14910+
}
14911+
},
1478614912
"/api/mcp/servers/global": {
1478714913
"post": {
1478814914
"summary": "Create global MCP server (Global Admin only)",

services/backend/api-spec.yaml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10043,6 +10043,13 @@ paths:
1004310043
name: auto_install_new_default_team
1004410044
required: false
1004510045
description: Filter by auto-install flag
10046+
- schema:
10047+
type: string
10048+
in: query
10049+
name: tags
10050+
required: false
10051+
description: Filter by tags (comma-separated list, e.g., "mcp,web-scraping").
10052+
Servers matching ANY of the provided tags will be returned.
1004610053
- schema:
1004710054
type: string
1004810055
enum:
@@ -10376,6 +10383,88 @@ paths:
1037610383
- success
1037710384
- error
1037810385
description: Internal Server Error
10386+
/api/mcp/servers/tags:
10387+
get:
10388+
summary: Get all unique tags
10389+
tags:
10390+
- MCP Servers
10391+
description: Retrieve all unique tags from MCP servers. Authentication is
10392+
required. Results are filtered based on user permissions - users see
10393+
tags from global servers plus their team servers, while global admins
10394+
see tags from all servers.
10395+
security:
10396+
- cookieAuth: []
10397+
responses:
10398+
"200":
10399+
description: Tags retrieved successfully
10400+
content:
10401+
application/json:
10402+
schema:
10403+
type: object
10404+
properties:
10405+
success:
10406+
type: boolean
10407+
description: Indicates successful tags retrieval
10408+
data:
10409+
type: object
10410+
properties:
10411+
tags:
10412+
type: array
10413+
items:
10414+
type: string
10415+
description: Array of unique tags sorted alphabetically
10416+
total:
10417+
type: number
10418+
description: Total number of unique tags
10419+
required:
10420+
- tags
10421+
- total
10422+
additionalProperties: false
10423+
required:
10424+
- success
10425+
- data
10426+
additionalProperties: false
10427+
description: Tags retrieved successfully
10428+
"401":
10429+
description: Unauthorized - Authentication required
10430+
content:
10431+
application/json:
10432+
schema:
10433+
type: object
10434+
properties:
10435+
success:
10436+
type: boolean
10437+
default: false
10438+
description: Indicates the operation failed
10439+
error:
10440+
type: string
10441+
description: Error message describing what went wrong
10442+
details:
10443+
description: Additional error details
10444+
required:
10445+
- success
10446+
- error
10447+
description: Unauthorized - Authentication required
10448+
"500":
10449+
description: Internal Server Error
10450+
content:
10451+
application/json:
10452+
schema:
10453+
type: object
10454+
properties:
10455+
success:
10456+
type: boolean
10457+
default: false
10458+
description: Indicates the operation failed
10459+
error:
10460+
type: string
10461+
description: Error message describing what went wrong
10462+
details:
10463+
description: Additional error details
10464+
required:
10465+
- success
10466+
- error
10467+
description: Internal Server Error
1037910468
/api/mcp/servers/global:
1038010469
post:
1038110470
summary: Create global MCP server (Global Admin only)

services/backend/src/routes/mcp/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import deleteCategory from './categories/delete';
99
import listServers from './servers/list';
1010
import getServer from './servers/get';
1111
import searchServers from './servers/search';
12+
import getTags from './servers/tags';
1213
import createGlobalServer from './servers/create-global';
1314
import updateGlobalServer from './servers/update-global';
1415
import deleteGlobalServer from './servers/delete-global';
@@ -38,6 +39,7 @@ export default async function mcpRoutes(server: FastifyInstance) {
3839
await server.register(listServers);
3940
await server.register(getServer);
4041
await server.register(searchServers);
42+
await server.register(getTags);
4143

4244
// Global server management (global_admin only)
4345
await server.register(createGlobalServer);

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ export const SEARCH_SERVERS_QUERY_SCHEMA = {
109109
type: 'boolean',
110110
description: 'Filter by auto-install flag'
111111
},
112+
tags: {
113+
type: 'string',
114+
description: 'Filter by tags (comma-separated list, e.g., "mcp,web-scraping"). Servers matching ANY of the provided tags will be returned.'
115+
},
112116
sort_by: {
113117
type: 'string',
114118
enum: ['name', 'github_stars'],
@@ -1008,6 +1012,34 @@ export const UPDATE_GLOBAL_SERVER_REQUEST_SCHEMA = {
10081012

10091013
export const UPDATE_GLOBAL_SERVER_SUCCESS_RESPONSE_SCHEMA = createSuccessResponseSchema(SERVER_ENTITY_SCHEMA, 'server update');
10101014

1015+
export const GET_TAGS_SUCCESS_RESPONSE_SCHEMA = {
1016+
type: 'object',
1017+
properties: {
1018+
success: {
1019+
type: 'boolean',
1020+
description: 'Indicates successful tags retrieval'
1021+
},
1022+
data: {
1023+
type: 'object',
1024+
properties: {
1025+
tags: {
1026+
type: 'array',
1027+
items: { type: 'string' },
1028+
description: 'Array of unique tags sorted alphabetically'
1029+
},
1030+
total: {
1031+
type: 'number',
1032+
description: 'Total number of unique tags'
1033+
}
1034+
},
1035+
required: ['tags', 'total'],
1036+
additionalProperties: false
1037+
}
1038+
},
1039+
required: ['success', 'data'],
1040+
additionalProperties: false
1041+
} as const;
1042+
10111043
// =============================================================================
10121044
// TYPESCRIPT INTERFACES - CONSOLIDATED
10131045
// =============================================================================
@@ -1265,6 +1297,14 @@ export interface ListServersSuccessResponse {
12651297
};
12661298
}
12671299

1300+
export interface GetTagsSuccessResponse {
1301+
success: boolean;
1302+
data: {
1303+
tags: string[];
1304+
total: number;
1305+
};
1306+
}
1307+
12681308
// =============================================================================
12691309
// UTILITY FUNCTIONS
12701310
// =============================================================================

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
// TypeScript interface for search query params
1717
interface SearchServersQueryParams extends Omit<ListServersQueryParams, 'search'> {
1818
q: string; // Search query is required for search endpoint
19+
tags?: string; // Optional comma-separated tags filter
1920
sort_by?: 'name' | 'github_stars'; // Optional sort parameter
2021
}
2122

@@ -61,7 +62,8 @@ export default async function searchServers(server: FastifyInstance) {
6162
language: queryParams.language,
6263
runtime: queryParams.runtime,
6364
status: queryParams.status,
64-
featured: queryParams.featured
65+
featured: queryParams.featured,
66+
tags: queryParams.tags
6567
},
6668
sortBy: queryParams.sort_by,
6769
pagination: {
@@ -107,7 +109,8 @@ export default async function searchServers(server: FastifyInstance) {
107109
language: queryParams.language,
108110
runtime: queryParams.runtime,
109111
status: status,
110-
featured: featured
112+
featured: featured,
113+
tags: queryParams.tags
111114
};
112115

113116
// Get servers using the service (which handles permission filtering)
@@ -127,6 +130,7 @@ export default async function searchServers(server: FastifyInstance) {
127130
operation: 'search_mcp_servers',
128131
userId: request.user!.id,
129132
query: queryParams.q,
133+
tags: queryParams.tags,
130134
sortBy: sortBy,
131135
totalResults: total,
132136
returnedResults: paginatedServers.length,

0 commit comments

Comments
 (0)