pulsemcp-cms-admin-mcp-server 0.9.17 → 0.9.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -74
- package/build/index.js +53 -1
- package/build/shared/src/elicitation-config.js +69 -0
- package/build/shared/src/pulsemcp-admin-client/lib/bulk-update-tenant-servers.js +41 -0
- package/build/shared/src/pulsemcp-admin-client/lib/create-mcp-implementation.js +7 -0
- package/build/shared/src/pulsemcp-admin-client/lib/delete-api-key.js +25 -0
- package/build/shared/src/pulsemcp-admin-client/lib/delete-tenant.js +31 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-unified-mcp-server.js +5 -1
- package/build/shared/src/pulsemcp-admin-client/lib/list-tenant-servers.js +57 -0
- package/build/shared/src/pulsemcp-admin-client/lib/save-mcp-implementation.js +8 -0
- package/build/shared/src/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.js +42 -0
- package/build/shared/src/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +3 -0
- package/build/shared/src/pulsemcp-admin-client/lib/update-unified-mcp-server.js +14 -0
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +39 -0
- package/build/shared/src/server.js +20 -0
- package/build/shared/src/tools/add-servers-to-tenant.js +133 -0
- package/build/shared/src/tools/delete-api-key.js +119 -0
- package/build/shared/src/tools/delete-tenant.js +129 -0
- package/build/shared/src/tools/get-mcp-server.js +26 -8
- package/build/shared/src/tools/list-mcp-servers.js +3 -0
- package/build/shared/src/tools/list-tenant-servers.js +89 -0
- package/build/shared/src/tools/remove-servers-from-tenant.js +92 -0
- package/build/shared/src/tools/revoke-api-key.js +119 -0
- package/build/shared/src/tools/save-mcp-implementation.js +89 -2
- package/build/shared/src/tools/set-known-missing-init-tools-list.js +75 -0
- package/build/shared/src/tools/update-mcp-server.js +19 -0
- package/build/shared/src/tools.js +30 -1
- package/node_modules/@pulsemcp/mcp-elicitation/build/config.d.ts +15 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/config.js +41 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/elicitation.d.ts +24 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/elicitation.js +175 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/index.d.ts +3 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/index.js +2 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/types.d.ts +114 -0
- package/node_modules/@pulsemcp/mcp-elicitation/build/types.js +1 -0
- package/node_modules/@pulsemcp/mcp-elicitation/package.json +28 -0
- package/package.json +7 -1
- package/shared/elicitation-config.d.ts +61 -0
- package/shared/elicitation-config.js +69 -0
- package/shared/pulsemcp-admin-client/lib/bulk-update-tenant-servers.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/bulk-update-tenant-servers.js +41 -0
- package/shared/pulsemcp-admin-client/lib/create-mcp-implementation.js +7 -0
- package/shared/pulsemcp-admin-client/lib/delete-api-key.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/delete-api-key.js +25 -0
- package/shared/pulsemcp-admin-client/lib/delete-tenant.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/delete-tenant.js +31 -0
- package/shared/pulsemcp-admin-client/lib/get-unified-mcp-server.js +5 -1
- package/shared/pulsemcp-admin-client/lib/list-tenant-servers.d.ts +7 -0
- package/shared/pulsemcp-admin-client/lib/list-tenant-servers.js +57 -0
- package/shared/pulsemcp-admin-client/lib/save-mcp-implementation.js +8 -0
- package/shared/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.js +42 -0
- package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.d.ts +2 -0
- package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +3 -0
- package/shared/pulsemcp-admin-client/lib/update-unified-mcp-server.js +14 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +39 -0
- package/shared/server.d.ts +19 -1
- package/shared/server.js +20 -0
- package/shared/tools/add-servers-to-tenant.d.ts +57 -0
- package/shared/tools/add-servers-to-tenant.js +133 -0
- package/shared/tools/delete-api-key.d.ts +30 -0
- package/shared/tools/delete-api-key.js +119 -0
- package/shared/tools/delete-tenant.d.ts +36 -0
- package/shared/tools/delete-tenant.js +129 -0
- package/shared/tools/get-mcp-server.js +26 -8
- package/shared/tools/list-mcp-servers.js +3 -0
- package/shared/tools/list-tenant-servers.d.ts +45 -0
- package/shared/tools/list-tenant-servers.js +89 -0
- package/shared/tools/remove-servers-from-tenant.d.ts +42 -0
- package/shared/tools/remove-servers-from-tenant.js +92 -0
- package/shared/tools/revoke-api-key.d.ts +30 -0
- package/shared/tools/revoke-api-key.js +119 -0
- package/shared/tools/save-mcp-implementation.d.ts +9 -1
- package/shared/tools/save-mcp-implementation.js +89 -2
- package/shared/tools/set-known-missing-init-tools-list.d.ts +38 -0
- package/shared/tools/set-known-missing-init-tools-list.js +75 -0
- package/shared/tools/update-mcp-server.d.ts +6 -0
- package/shared/tools/update-mcp-server.js +19 -0
- package/shared/tools.d.ts +5 -3
- package/shared/tools.js +30 -1
- package/shared/types.d.ts +89 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { adminFetch } from './admin-fetch.js';
|
|
2
|
+
export async function deleteTenant(apiKey, baseUrl, params) {
|
|
3
|
+
const url = new URL(`/api/tenants/${encodeURIComponent(String(params.id_or_slug))}`, baseUrl);
|
|
4
|
+
if (params.force) {
|
|
5
|
+
url.searchParams.set('force', 'true');
|
|
6
|
+
}
|
|
7
|
+
const response = await adminFetch(url.toString(), {
|
|
8
|
+
method: 'DELETE',
|
|
9
|
+
headers: {
|
|
10
|
+
'X-API-Key': apiKey,
|
|
11
|
+
Accept: 'application/json',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
if (response.status === 401) {
|
|
16
|
+
throw new Error('Invalid API key');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 403) {
|
|
19
|
+
throw new Error('User lacks write privileges');
|
|
20
|
+
}
|
|
21
|
+
if (response.status === 404) {
|
|
22
|
+
throw new Error(`Tenant ${params.id_or_slug} not found`);
|
|
23
|
+
}
|
|
24
|
+
if (response.status === 422) {
|
|
25
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
26
|
+
throw new Error(`Cannot delete tenant: ${errorData.errors?.join(', ') || 'validation failed'}`);
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`Failed to delete tenant: ${response.status} ${response.statusText}`);
|
|
29
|
+
}
|
|
30
|
+
return (await response.json());
|
|
31
|
+
}
|
|
@@ -80,8 +80,12 @@ export async function getUnifiedMCPServer(apiKey, baseUrl, slug) {
|
|
|
80
80
|
updated_at: mcpServerData.updated_at,
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
// Merge MCPServer data with implementation data for complete picture
|
|
83
|
+
// Merge MCPServer data with implementation data for complete picture.
|
|
84
|
+
// Spread the search-response mcp_server first so fields only present there
|
|
85
|
+
// (e.g. owner_tenant_id/owner_tenant_slug) are preserved when the supervisor
|
|
86
|
+
// endpoint omits them. Supervisor data wins for the fields it does provide.
|
|
84
87
|
matchingImpl.mcp_server = {
|
|
88
|
+
...matchingImpl.mcp_server,
|
|
85
89
|
...mcpServerData,
|
|
86
90
|
remotes: mcpServerData.remotes,
|
|
87
91
|
tags: mcpServerData.tags,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ListTenantServersResponse } from '../../types.js';
|
|
2
|
+
export declare function listTenantServers(apiKey: string, baseUrl: string, idOrSlug: number | string, params?: {
|
|
3
|
+
status?: 'active' | 'deleted' | 'all';
|
|
4
|
+
limit?: number;
|
|
5
|
+
offset?: number;
|
|
6
|
+
}): Promise<ListTenantServersResponse>;
|
|
7
|
+
//# sourceMappingURL=list-tenant-servers.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { adminFetch } from './admin-fetch.js';
|
|
2
|
+
function mapTenantServer(row) {
|
|
3
|
+
return {
|
|
4
|
+
id: row.id,
|
|
5
|
+
tenant_id: row.tenant_id,
|
|
6
|
+
mcp_server_id: row.mcp_server_id,
|
|
7
|
+
mcp_server_slug: row.mcp_server_slug,
|
|
8
|
+
status: row.status,
|
|
9
|
+
server_json_selection: row.server_json_selection,
|
|
10
|
+
first_touched_at: row.first_touched_at,
|
|
11
|
+
touched: row.touched,
|
|
12
|
+
created_at: row.created_at,
|
|
13
|
+
updated_at: row.updated_at,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function listTenantServers(apiKey, baseUrl, idOrSlug, params) {
|
|
17
|
+
const url = new URL(`/api/tenants/${idOrSlug}/servers`, baseUrl);
|
|
18
|
+
if (params?.status) {
|
|
19
|
+
url.searchParams.append('status', params.status);
|
|
20
|
+
}
|
|
21
|
+
if (params?.limit !== undefined) {
|
|
22
|
+
url.searchParams.append('limit', params.limit.toString());
|
|
23
|
+
}
|
|
24
|
+
if (params?.offset !== undefined) {
|
|
25
|
+
url.searchParams.append('offset', params.offset.toString());
|
|
26
|
+
}
|
|
27
|
+
const response = await adminFetch(url.toString(), {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
headers: {
|
|
30
|
+
'X-API-Key': apiKey,
|
|
31
|
+
Accept: 'application/json',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
if (response.status === 401) {
|
|
36
|
+
throw new Error('Invalid API key');
|
|
37
|
+
}
|
|
38
|
+
if (response.status === 403) {
|
|
39
|
+
throw new Error('User lacks admin privileges');
|
|
40
|
+
}
|
|
41
|
+
if (response.status === 404) {
|
|
42
|
+
throw new Error(`Tenant with ID/slug ${idOrSlug} not found`);
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Failed to list tenant servers: ${response.status} ${response.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
const data = (await response.json());
|
|
47
|
+
return {
|
|
48
|
+
data: data.data.map(mapTenantServer),
|
|
49
|
+
pagination: {
|
|
50
|
+
current_page: data.meta.current_page,
|
|
51
|
+
total_pages: data.meta.total_pages,
|
|
52
|
+
total_count: data.meta.total_count,
|
|
53
|
+
has_next: data.meta.has_next,
|
|
54
|
+
limit: data.meta.limit,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -98,6 +98,14 @@ export async function saveMCPImplementation(apiKey, baseUrl, id, params) {
|
|
|
98
98
|
if (params.internal_notes !== undefined) {
|
|
99
99
|
formData.append('mcp_implementation[internal_notes]', params.internal_notes);
|
|
100
100
|
}
|
|
101
|
+
// Owner tenant linking. Slug wins over id on the Rails side when both are sent.
|
|
102
|
+
// null/empty value clears the link (Rails uses .presence semantics).
|
|
103
|
+
if (params.owner_tenant_slug !== undefined) {
|
|
104
|
+
formData.append('mcp_implementation[owner_tenant_slug]', params.owner_tenant_slug === null ? '' : params.owner_tenant_slug);
|
|
105
|
+
}
|
|
106
|
+
if (params.owner_tenant_id !== undefined) {
|
|
107
|
+
formData.append('mcp_implementation[owner_tenant_id]', params.owner_tenant_id === null ? '' : params.owner_tenant_id.toString());
|
|
108
|
+
}
|
|
101
109
|
// Remote endpoints
|
|
102
110
|
// Rails expects nested attributes to use the _attributes suffix for has_many associations
|
|
103
111
|
if (params.remote !== undefined) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SetKnownMissingInitToolsListResponse } from '../../types.js';
|
|
2
|
+
export declare function setKnownMissingInitToolsList(apiKey: string, baseUrl: string, id: number, knownMissingInitToolsList: boolean, knownMissingInitToolsListFilterTo?: string | null): Promise<SetKnownMissingInitToolsListResponse>;
|
|
3
|
+
//# sourceMappingURL=set-known-missing-init-tools-list.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { adminFetch } from './admin-fetch.js';
|
|
2
|
+
export async function setKnownMissingInitToolsList(apiKey, baseUrl, id, knownMissingInitToolsList, knownMissingInitToolsListFilterTo) {
|
|
3
|
+
const url = new URL(`/api/mcp_servers/${id}/known_missing_init_tools_list`, baseUrl);
|
|
4
|
+
const body = {
|
|
5
|
+
known_missing_init_tools_list: knownMissingInitToolsList,
|
|
6
|
+
};
|
|
7
|
+
if (knownMissingInitToolsListFilterTo !== undefined) {
|
|
8
|
+
// null is sent as JSON null; the Rails controller treats nil/blank as "clear".
|
|
9
|
+
body.known_missing_init_tools_list_filter_to = knownMissingInitToolsListFilterTo;
|
|
10
|
+
}
|
|
11
|
+
const response = await adminFetch(url.toString(), {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'X-API-Key': apiKey,
|
|
15
|
+
Accept: 'application/json',
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(body),
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
if (response.status === 401) {
|
|
22
|
+
throw new Error('Invalid API key');
|
|
23
|
+
}
|
|
24
|
+
if (response.status === 403) {
|
|
25
|
+
throw new Error('User lacks write privileges');
|
|
26
|
+
}
|
|
27
|
+
if (response.status === 404) {
|
|
28
|
+
throw new Error(`MCP server not found: ${id}`);
|
|
29
|
+
}
|
|
30
|
+
if (response.status === 400) {
|
|
31
|
+
const errBody = (await response.json().catch(() => ({})));
|
|
32
|
+
throw new Error(errBody.error ?? 'Bad request');
|
|
33
|
+
}
|
|
34
|
+
if (response.status === 422) {
|
|
35
|
+
const errBody = (await response.json().catch(() => ({})));
|
|
36
|
+
const detailStr = errBody.details?.length ? ` (${errBody.details.join(', ')})` : '';
|
|
37
|
+
throw new Error(`${errBody.error ?? 'Validation failed'}${detailStr}`);
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`Failed to set known_missing_init_tools_list: ${response.status} ${response.statusText}`);
|
|
40
|
+
}
|
|
41
|
+
return (await response.json());
|
|
42
|
+
}
|
|
@@ -47,6 +47,8 @@ export interface RailsImplementation {
|
|
|
47
47
|
mcp_server_remotes_count?: number;
|
|
48
48
|
recommended?: boolean;
|
|
49
49
|
verified_no_remote_canonicals?: boolean;
|
|
50
|
+
owner_tenant_id?: number | null;
|
|
51
|
+
owner_tenant_slug?: string | null;
|
|
50
52
|
tags?: MCPServerTag[];
|
|
51
53
|
remotes?: MCPServerRemote[];
|
|
52
54
|
created_at?: string;
|
|
@@ -74,6 +74,9 @@ export function mapToUnifiedServer(impl) {
|
|
|
74
74
|
downloads_estimate_total: mcpServer.downloads_estimate_total,
|
|
75
75
|
// Internal notes
|
|
76
76
|
internal_notes: impl.internal_notes,
|
|
77
|
+
// Owner tenant
|
|
78
|
+
owner_tenant_id: mcpServer.owner_tenant_id,
|
|
79
|
+
owner_tenant_slug: mcpServer.owner_tenant_slug,
|
|
77
80
|
// Timestamps (use implementation timestamps as they're more relevant)
|
|
78
81
|
created_at: impl.created_at,
|
|
79
82
|
updated_at: impl.updated_at,
|
|
@@ -54,6 +54,20 @@ export async function updateUnifiedMCPServer(apiKey, baseUrl, implementationId,
|
|
|
54
54
|
// Date overrides
|
|
55
55
|
if (params.created_on_override !== undefined)
|
|
56
56
|
implParams.created_on_override = params.created_on_override;
|
|
57
|
+
// Owner tenant: route to slug or id depending on the value type.
|
|
58
|
+
// String → slug, number → id, null → clear (sent as empty owner_tenant_id).
|
|
59
|
+
if (params.owner_tenant !== undefined) {
|
|
60
|
+
if (typeof params.owner_tenant === 'string') {
|
|
61
|
+
implParams.owner_tenant_slug = params.owner_tenant;
|
|
62
|
+
}
|
|
63
|
+
else if (typeof params.owner_tenant === 'number') {
|
|
64
|
+
implParams.owner_tenant_id = params.owner_tenant;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// null — clear the link
|
|
68
|
+
implParams.owner_tenant_id = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
57
71
|
// Tags
|
|
58
72
|
if (params.tags !== undefined)
|
|
59
73
|
implParams.tags = params.tags;
|
|
@@ -1045,10 +1045,49 @@ export function createMockPulseMCPAdminClient(mockData) {
|
|
|
1045
1045
|
created_at: '2024-01-01T00:00:00Z',
|
|
1046
1046
|
};
|
|
1047
1047
|
},
|
|
1048
|
+
async deleteTenant() {
|
|
1049
|
+
return { success: true, message: 'Tenant deleted' };
|
|
1050
|
+
},
|
|
1051
|
+
async deleteApiKey() {
|
|
1052
|
+
return { success: true, message: 'API key revoked' };
|
|
1053
|
+
},
|
|
1054
|
+
async listTenantServers() {
|
|
1055
|
+
return {
|
|
1056
|
+
data: [],
|
|
1057
|
+
pagination: {
|
|
1058
|
+
current_page: 1,
|
|
1059
|
+
total_pages: 0,
|
|
1060
|
+
total_count: 0,
|
|
1061
|
+
has_next: false,
|
|
1062
|
+
limit: 30,
|
|
1063
|
+
},
|
|
1064
|
+
};
|
|
1065
|
+
},
|
|
1066
|
+
async bulkUpdateTenantServers(idOrSlug) {
|
|
1067
|
+
return {
|
|
1068
|
+
status: 'success',
|
|
1069
|
+
tenant: { id: typeof idOrSlug === 'number' ? idOrSlug : 1, slug: String(idOrSlug) },
|
|
1070
|
+
added: [],
|
|
1071
|
+
removed: [],
|
|
1072
|
+
restored: [],
|
|
1073
|
+
skipped: [],
|
|
1074
|
+
unresolved_identifiers: [],
|
|
1075
|
+
};
|
|
1076
|
+
},
|
|
1048
1077
|
async recacheMCPServer(slug) {
|
|
1049
1078
|
return {
|
|
1050
1079
|
message: `Cache successfully refreshed for ${slug}.`,
|
|
1051
1080
|
};
|
|
1052
1081
|
},
|
|
1082
|
+
async setKnownMissingInitToolsList(id, knownMissingInitToolsList, knownMissingInitToolsListFilterTo) {
|
|
1083
|
+
return {
|
|
1084
|
+
id,
|
|
1085
|
+
slug: `mock-server-${id}`,
|
|
1086
|
+
known_missing_init_tools_list: knownMissingInitToolsList,
|
|
1087
|
+
known_missing_init_tools_list_filter_to: knownMissingInitToolsListFilterTo === undefined
|
|
1088
|
+
? null
|
|
1089
|
+
: (knownMissingInitToolsListFilterTo ?? null),
|
|
1090
|
+
};
|
|
1091
|
+
},
|
|
1053
1092
|
};
|
|
1054
1093
|
}
|
package/shared/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, MCPImplementation, MCPImplementationsResponse, SaveMCPImplementationParams, CreateMCPImplementationParams, Provider, ProvidersResponse, OfficialMirrorQueueStatus, OfficialMirrorQueueResponse, OfficialMirrorQueueItemDetail, OfficialMirrorQueueActionResponse, UnofficialMirror, UnofficialMirrorsResponse, CreateUnofficialMirrorParams, UpdateUnofficialMirrorParams, OfficialMirrorRest, OfficialMirrorsResponse, Tenant, TenantsResponse, McpJson, McpJsonsResponse, CreateMcpJsonParams, UpdateMcpJsonParams, UnifiedMCPServer, UnifiedMCPServersResponse, UpdateUnifiedMCPServerParams, Redirect, RedirectsResponse, RedirectStatus, CreateRedirectParams, UpdateRedirectParams, GoodJob, GoodJobsResponse, GoodJobStatus, GoodJobCronSchedule, GoodJobProcess, GoodJobStatistics, GoodJobActionResponse, GoodJobCleanupResponse, ProctorRunExamParams, ProctorRunExamResponse, ProctorSaveResultsParams, ProctorSaveResultsResponse, ProctorRunsResponse, GetProctorRunsParams, ProctorMetadataResponse, DiscoveredUrlsResponse, MarkDiscoveredUrlProcessedParams, MarkDiscoveredUrlProcessedResponse, DiscoveredUrlStats, MozMetricsResponse, MozBacklinksResponse, MozStoredMetricsResponse, CreateTenantParams, ApiKey, CreateApiKeyParams, RecacheMCPServerResponse } from './types.js';
|
|
2
|
+
import type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, MCPImplementation, MCPImplementationsResponse, SaveMCPImplementationParams, CreateMCPImplementationParams, Provider, ProvidersResponse, OfficialMirrorQueueStatus, OfficialMirrorQueueResponse, OfficialMirrorQueueItemDetail, OfficialMirrorQueueActionResponse, UnofficialMirror, UnofficialMirrorsResponse, CreateUnofficialMirrorParams, UpdateUnofficialMirrorParams, OfficialMirrorRest, OfficialMirrorsResponse, Tenant, TenantsResponse, ListTenantServersResponse, BulkUpdateTenantServersParams, BulkUpdateTenantServersResponse, McpJson, McpJsonsResponse, CreateMcpJsonParams, UpdateMcpJsonParams, UnifiedMCPServer, UnifiedMCPServersResponse, UpdateUnifiedMCPServerParams, Redirect, RedirectsResponse, RedirectStatus, CreateRedirectParams, UpdateRedirectParams, GoodJob, GoodJobsResponse, GoodJobStatus, GoodJobCronSchedule, GoodJobProcess, GoodJobStatistics, GoodJobActionResponse, GoodJobCleanupResponse, ProctorRunExamParams, ProctorRunExamResponse, ProctorSaveResultsParams, ProctorSaveResultsResponse, ProctorRunsResponse, GetProctorRunsParams, ProctorMetadataResponse, DiscoveredUrlsResponse, MarkDiscoveredUrlProcessedParams, MarkDiscoveredUrlProcessedResponse, DiscoveredUrlStats, MozMetricsResponse, MozBacklinksResponse, MozStoredMetricsResponse, CreateTenantParams, ApiKey, CreateApiKeyParams, DeleteTenantParams, DeleteTenantResponse, DeleteApiKeyResponse, RecacheMCPServerResponse, SetKnownMissingInitToolsListResponse } from './types.js';
|
|
3
3
|
export interface IPulseMCPAdminClient {
|
|
4
4
|
getPosts(params?: {
|
|
5
5
|
search?: string;
|
|
@@ -104,6 +104,14 @@ export interface IPulseMCPAdminClient {
|
|
|
104
104
|
getTenant(idOrSlug: number | string): Promise<Tenant>;
|
|
105
105
|
createTenant(params: CreateTenantParams): Promise<Tenant>;
|
|
106
106
|
createApiKey(params: CreateApiKeyParams): Promise<ApiKey>;
|
|
107
|
+
deleteTenant(params: DeleteTenantParams): Promise<DeleteTenantResponse>;
|
|
108
|
+
deleteApiKey(id: number): Promise<DeleteApiKeyResponse>;
|
|
109
|
+
listTenantServers(idOrSlug: number | string, params?: {
|
|
110
|
+
status?: 'active' | 'deleted' | 'all';
|
|
111
|
+
limit?: number;
|
|
112
|
+
offset?: number;
|
|
113
|
+
}): Promise<ListTenantServersResponse>;
|
|
114
|
+
bulkUpdateTenantServers(idOrSlug: number | string, params: BulkUpdateTenantServersParams): Promise<BulkUpdateTenantServersResponse>;
|
|
107
115
|
getMcpJsons(params?: {
|
|
108
116
|
unofficial_mirror_id?: number;
|
|
109
117
|
q?: string;
|
|
@@ -127,6 +135,7 @@ export interface IPulseMCPAdminClient {
|
|
|
127
135
|
getUnifiedMCPServer(slug: string): Promise<UnifiedMCPServer>;
|
|
128
136
|
updateUnifiedMCPServer(implementationId: number, params: UpdateUnifiedMCPServerParams): Promise<UnifiedMCPServer>;
|
|
129
137
|
recacheMCPServer(slug: string): Promise<RecacheMCPServerResponse>;
|
|
138
|
+
setKnownMissingInitToolsList(id: number, knownMissingInitToolsList: boolean, knownMissingInitToolsListFilterTo?: string | null): Promise<SetKnownMissingInitToolsListResponse>;
|
|
130
139
|
getRedirects(params?: {
|
|
131
140
|
q?: string;
|
|
132
141
|
status?: RedirectStatus;
|
|
@@ -295,6 +304,14 @@ export declare class PulseMCPAdminClient implements IPulseMCPAdminClient {
|
|
|
295
304
|
getTenant(idOrSlug: number | string): Promise<Tenant>;
|
|
296
305
|
createTenant(params: CreateTenantParams): Promise<Tenant>;
|
|
297
306
|
createApiKey(params: CreateApiKeyParams): Promise<ApiKey>;
|
|
307
|
+
deleteTenant(params: DeleteTenantParams): Promise<DeleteTenantResponse>;
|
|
308
|
+
deleteApiKey(id: number): Promise<DeleteApiKeyResponse>;
|
|
309
|
+
listTenantServers(idOrSlug: number | string, params?: {
|
|
310
|
+
status?: 'active' | 'deleted' | 'all';
|
|
311
|
+
limit?: number;
|
|
312
|
+
offset?: number;
|
|
313
|
+
}): Promise<ListTenantServersResponse>;
|
|
314
|
+
bulkUpdateTenantServers(idOrSlug: number | string, params: BulkUpdateTenantServersParams): Promise<BulkUpdateTenantServersResponse>;
|
|
298
315
|
getMcpJsons(params?: {
|
|
299
316
|
unofficial_mirror_id?: number;
|
|
300
317
|
q?: string;
|
|
@@ -318,6 +335,7 @@ export declare class PulseMCPAdminClient implements IPulseMCPAdminClient {
|
|
|
318
335
|
getUnifiedMCPServer(slug: string): Promise<UnifiedMCPServer>;
|
|
319
336
|
updateUnifiedMCPServer(implementationId: number, params: UpdateUnifiedMCPServerParams): Promise<UnifiedMCPServer>;
|
|
320
337
|
recacheMCPServer(slug: string): Promise<RecacheMCPServerResponse>;
|
|
338
|
+
setKnownMissingInitToolsList(id: number, knownMissingInitToolsList: boolean, knownMissingInitToolsListFilterTo?: string | null): Promise<SetKnownMissingInitToolsListResponse>;
|
|
321
339
|
getRedirects(params?: {
|
|
322
340
|
q?: string;
|
|
323
341
|
status?: RedirectStatus;
|
package/shared/server.js
CHANGED
|
@@ -72,8 +72,13 @@ import { getMozMetrics } from './pulsemcp-admin-client/lib/get-moz-metrics.js';
|
|
|
72
72
|
import { getMozBacklinks } from './pulsemcp-admin-client/lib/get-moz-backlinks.js';
|
|
73
73
|
import { getMozStoredMetrics } from './pulsemcp-admin-client/lib/get-moz-stored-metrics.js';
|
|
74
74
|
import { recacheMCPServer } from './pulsemcp-admin-client/lib/recache-mcp-server.js';
|
|
75
|
+
import { setKnownMissingInitToolsList } from './pulsemcp-admin-client/lib/set-known-missing-init-tools-list.js';
|
|
75
76
|
import { createTenant } from './pulsemcp-admin-client/lib/create-tenant.js';
|
|
76
77
|
import { createApiKey } from './pulsemcp-admin-client/lib/create-api-key.js';
|
|
78
|
+
import { deleteTenant } from './pulsemcp-admin-client/lib/delete-tenant.js';
|
|
79
|
+
import { deleteApiKey } from './pulsemcp-admin-client/lib/delete-api-key.js';
|
|
80
|
+
import { listTenantServers } from './pulsemcp-admin-client/lib/list-tenant-servers.js';
|
|
81
|
+
import { bulkUpdateTenantServers } from './pulsemcp-admin-client/lib/bulk-update-tenant-servers.js';
|
|
77
82
|
// PulseMCP Admin API client implementation
|
|
78
83
|
export class PulseMCPAdminClient {
|
|
79
84
|
apiKey;
|
|
@@ -200,6 +205,18 @@ export class PulseMCPAdminClient {
|
|
|
200
205
|
async createApiKey(params) {
|
|
201
206
|
return createApiKey(this.apiKey, this.baseUrl, params);
|
|
202
207
|
}
|
|
208
|
+
async deleteTenant(params) {
|
|
209
|
+
return deleteTenant(this.apiKey, this.baseUrl, params);
|
|
210
|
+
}
|
|
211
|
+
async deleteApiKey(id) {
|
|
212
|
+
return deleteApiKey(this.apiKey, this.baseUrl, id);
|
|
213
|
+
}
|
|
214
|
+
async listTenantServers(idOrSlug, params) {
|
|
215
|
+
return listTenantServers(this.apiKey, this.baseUrl, idOrSlug, params);
|
|
216
|
+
}
|
|
217
|
+
async bulkUpdateTenantServers(idOrSlug, params) {
|
|
218
|
+
return bulkUpdateTenantServers(this.apiKey, this.baseUrl, idOrSlug, params);
|
|
219
|
+
}
|
|
203
220
|
// MCP JSON REST API methods
|
|
204
221
|
async getMcpJsons(params) {
|
|
205
222
|
return getMcpJsons(this.apiKey, this.baseUrl, params);
|
|
@@ -229,6 +246,9 @@ export class PulseMCPAdminClient {
|
|
|
229
246
|
async recacheMCPServer(slug) {
|
|
230
247
|
return recacheMCPServer(this.apiKey, this.baseUrl, slug);
|
|
231
248
|
}
|
|
249
|
+
async setKnownMissingInitToolsList(id, knownMissingInitToolsList, knownMissingInitToolsListFilterTo) {
|
|
250
|
+
return setKnownMissingInitToolsList(this.apiKey, this.baseUrl, id, knownMissingInitToolsList, knownMissingInitToolsListFilterTo);
|
|
251
|
+
}
|
|
232
252
|
// Redirect REST API methods
|
|
233
253
|
async getRedirects(params) {
|
|
234
254
|
return getRedirects(this.apiKey, this.baseUrl, params);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import type { ClientFactory } from '../server.js';
|
|
3
|
+
export declare function addServersToTenant(_server: Server, clientFactory: ClientFactory): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
tenant: {
|
|
10
|
+
oneOf: {
|
|
11
|
+
type: string;
|
|
12
|
+
}[];
|
|
13
|
+
description: "Tenant ID (number) or slug (string) to add servers to.";
|
|
14
|
+
};
|
|
15
|
+
add_server_identifiers: {
|
|
16
|
+
type: string;
|
|
17
|
+
items: {
|
|
18
|
+
oneOf: {
|
|
19
|
+
type: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
description: "Array of MCP server IDs (numbers) or slugs (strings) to add to the tenant's recommendation list. Servers without an unofficial mirror are silently skipped (returned in `skipped` with reason \"no_unofficial_mirror\"). Already-active associations are skipped (reason \"already_active\").";
|
|
23
|
+
};
|
|
24
|
+
remove_server_identifiers: {
|
|
25
|
+
type: string;
|
|
26
|
+
items: {
|
|
27
|
+
oneOf: {
|
|
28
|
+
type: string;
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
31
|
+
description: "Optional array of MCP server IDs or slugs to remove from the tenant in the same call. Touched associations are soft-deleted; untouched associations are hard-deleted.";
|
|
32
|
+
};
|
|
33
|
+
restore_association_ids: {
|
|
34
|
+
type: string;
|
|
35
|
+
items: {
|
|
36
|
+
type: string;
|
|
37
|
+
};
|
|
38
|
+
description: "Optional array of TenantsMcpServer association IDs to restore from a soft-deleted state. Use list_tenant_servers with status=\"deleted\" to find these IDs.";
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
required: string[];
|
|
42
|
+
};
|
|
43
|
+
handler: (args: unknown) => Promise<{
|
|
44
|
+
content: {
|
|
45
|
+
type: string;
|
|
46
|
+
text: string;
|
|
47
|
+
}[];
|
|
48
|
+
isError?: undefined;
|
|
49
|
+
} | {
|
|
50
|
+
content: {
|
|
51
|
+
type: string;
|
|
52
|
+
text: string;
|
|
53
|
+
}[];
|
|
54
|
+
isError: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=add-servers-to-tenant.d.ts.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
tenant: 'Tenant ID (number) or slug (string) to add servers to.',
|
|
4
|
+
add_server_identifiers: 'Array of MCP server IDs (numbers) or slugs (strings) to add to the tenant\'s recommendation list. Servers without an unofficial mirror are silently skipped (returned in `skipped` with reason "no_unofficial_mirror"). Already-active associations are skipped (reason "already_active").',
|
|
5
|
+
remove_server_identifiers: 'Optional array of MCP server IDs or slugs to remove from the tenant in the same call. Touched associations are soft-deleted; untouched associations are hard-deleted.',
|
|
6
|
+
restore_association_ids: 'Optional array of TenantsMcpServer association IDs to restore from a soft-deleted state. Use list_tenant_servers with status="deleted" to find these IDs.',
|
|
7
|
+
};
|
|
8
|
+
const AddServersToTenantSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
tenant: z.union([z.number(), z.string()]).describe(PARAM_DESCRIPTIONS.tenant),
|
|
11
|
+
add_server_identifiers: z
|
|
12
|
+
.array(z.union([z.number(), z.string()]))
|
|
13
|
+
.optional()
|
|
14
|
+
.describe(PARAM_DESCRIPTIONS.add_server_identifiers),
|
|
15
|
+
remove_server_identifiers: z
|
|
16
|
+
.array(z.union([z.number(), z.string()]))
|
|
17
|
+
.optional()
|
|
18
|
+
.describe(PARAM_DESCRIPTIONS.remove_server_identifiers),
|
|
19
|
+
restore_association_ids: z
|
|
20
|
+
.array(z.number())
|
|
21
|
+
.optional()
|
|
22
|
+
.describe(PARAM_DESCRIPTIONS.restore_association_ids),
|
|
23
|
+
})
|
|
24
|
+
.refine((val) => (val.add_server_identifiers && val.add_server_identifiers.length > 0) ||
|
|
25
|
+
(val.remove_server_identifiers && val.remove_server_identifiers.length > 0) ||
|
|
26
|
+
(val.restore_association_ids && val.restore_association_ids.length > 0), {
|
|
27
|
+
message: 'Must provide at least one of add_server_identifiers, remove_server_identifiers, or restore_association_ids',
|
|
28
|
+
});
|
|
29
|
+
export function addServersToTenant(_server, clientFactory) {
|
|
30
|
+
return {
|
|
31
|
+
name: 'add_servers_to_tenant',
|
|
32
|
+
description: `Add MCP servers to a tenant's recommendation list. Optionally remove and restore associations in the same transactional call.
|
|
33
|
+
|
|
34
|
+
Servers are added with server_json_selection: "unofficial" — they always pin to the unofficial mirror's server.json. A server without an unofficial mirror cannot be added and will be reported in "skipped" with reason "no_unofficial_mirror".
|
|
35
|
+
|
|
36
|
+
This tool only changes the tenant's recommendation list (TenantsMcpServer rows). The underlying MCP server's public listing is NOT affected — public servers stay public and continue to appear in the public directory.
|
|
37
|
+
|
|
38
|
+
The response includes:
|
|
39
|
+
- added: servers newly attached to the tenant (outcome "created" or "restored_from_deleted")
|
|
40
|
+
- removed: servers detached (outcome "hard_deleted" if untouched, "soft_deleted" if the row had been customized)
|
|
41
|
+
- restored: associations restored from soft-deleted state via restore_association_ids
|
|
42
|
+
- skipped: identifiers that resolved to a server but were skipped with a reason
|
|
43
|
+
- unresolved_identifiers: identifiers that didn't match any existing MCP server
|
|
44
|
+
|
|
45
|
+
Use cases:
|
|
46
|
+
- Curate a tenant's recommended-servers list
|
|
47
|
+
- Bulk-add servers by slug or ID in one call
|
|
48
|
+
- Combine adds and removes atomically (e.g., swap one server for another)`,
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
tenant: {
|
|
53
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
54
|
+
description: PARAM_DESCRIPTIONS.tenant,
|
|
55
|
+
},
|
|
56
|
+
add_server_identifiers: {
|
|
57
|
+
type: 'array',
|
|
58
|
+
items: { oneOf: [{ type: 'number' }, { type: 'string' }] },
|
|
59
|
+
description: PARAM_DESCRIPTIONS.add_server_identifiers,
|
|
60
|
+
},
|
|
61
|
+
remove_server_identifiers: {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: { oneOf: [{ type: 'number' }, { type: 'string' }] },
|
|
64
|
+
description: PARAM_DESCRIPTIONS.remove_server_identifiers,
|
|
65
|
+
},
|
|
66
|
+
restore_association_ids: {
|
|
67
|
+
type: 'array',
|
|
68
|
+
items: { type: 'number' },
|
|
69
|
+
description: PARAM_DESCRIPTIONS.restore_association_ids,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ['tenant'],
|
|
73
|
+
},
|
|
74
|
+
handler: async (args) => {
|
|
75
|
+
try {
|
|
76
|
+
const validatedArgs = AddServersToTenantSchema.parse(args);
|
|
77
|
+
const client = clientFactory();
|
|
78
|
+
const result = await client.bulkUpdateTenantServers(validatedArgs.tenant, {
|
|
79
|
+
add_server_identifiers: validatedArgs.add_server_identifiers,
|
|
80
|
+
remove_server_identifiers: validatedArgs.remove_server_identifiers,
|
|
81
|
+
restore_association_ids: validatedArgs.restore_association_ids,
|
|
82
|
+
});
|
|
83
|
+
let content = `**Tenant servers updated** (tenant=${result.tenant.slug}, id=${result.tenant.id})\n\n`;
|
|
84
|
+
content += `- Added: ${result.added.length}\n`;
|
|
85
|
+
content += `- Removed: ${result.removed.length}\n`;
|
|
86
|
+
content += `- Restored: ${result.restored.length}\n`;
|
|
87
|
+
content += `- Skipped: ${result.skipped.length}\n`;
|
|
88
|
+
content += `- Unresolved identifiers: ${result.unresolved_identifiers.length}\n`;
|
|
89
|
+
if (result.added.length > 0) {
|
|
90
|
+
content += `\n**Added:**\n`;
|
|
91
|
+
for (const item of result.added) {
|
|
92
|
+
content += `- ${item.mcp_server_slug} (server_id=${item.mcp_server_id}, association_id=${item.association_id}, outcome=${item.outcome})\n`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (result.removed.length > 0) {
|
|
96
|
+
content += `\n**Removed:**\n`;
|
|
97
|
+
for (const item of result.removed) {
|
|
98
|
+
const assocPart = item.association_id != null ? `, association_id=${item.association_id}` : '';
|
|
99
|
+
content += `- ${item.mcp_server_slug} (server_id=${item.mcp_server_id}${assocPart}, outcome=${item.outcome})\n`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (result.restored.length > 0) {
|
|
103
|
+
content += `\n**Restored:**\n`;
|
|
104
|
+
for (const item of result.restored) {
|
|
105
|
+
content += `- ${item.mcp_server_slug} (server_id=${item.mcp_server_id}, association_id=${item.association_id})\n`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (result.skipped.length > 0) {
|
|
109
|
+
content += `\n**Skipped:**\n`;
|
|
110
|
+
for (const item of result.skipped) {
|
|
111
|
+
const label = item.mcp_server_slug || item.mcp_server_id || item.association_id;
|
|
112
|
+
content += `- ${label} — reason: ${item.reason}\n`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (result.unresolved_identifiers.length > 0) {
|
|
116
|
+
content += `\n**Unresolved identifiers:** ${result.unresolved_identifiers.join(', ')}\n`;
|
|
117
|
+
}
|
|
118
|
+
return { content: [{ type: 'text', text: content }] };
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return {
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: `Error updating tenant servers: ${error instanceof Error ? error.message : String(error)}`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
isError: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import type { ClientFactory } from '../server.js';
|
|
3
|
+
export declare function deleteApiKey(server: Server, clientFactory: ClientFactory): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
id: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: "The numeric ID of the API key to revoke.";
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
required: string[];
|
|
15
|
+
};
|
|
16
|
+
handler: (args: unknown) => Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: string;
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
isError: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
content: {
|
|
24
|
+
type: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
isError?: undefined;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=delete-api-key.d.ts.map
|