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.
Files changed (81) hide show
  1. package/README.md +93 -74
  2. package/build/index.js +53 -1
  3. package/build/shared/src/elicitation-config.js +69 -0
  4. package/build/shared/src/pulsemcp-admin-client/lib/bulk-update-tenant-servers.js +41 -0
  5. package/build/shared/src/pulsemcp-admin-client/lib/create-mcp-implementation.js +7 -0
  6. package/build/shared/src/pulsemcp-admin-client/lib/delete-api-key.js +25 -0
  7. package/build/shared/src/pulsemcp-admin-client/lib/delete-tenant.js +31 -0
  8. package/build/shared/src/pulsemcp-admin-client/lib/get-unified-mcp-server.js +5 -1
  9. package/build/shared/src/pulsemcp-admin-client/lib/list-tenant-servers.js +57 -0
  10. package/build/shared/src/pulsemcp-admin-client/lib/save-mcp-implementation.js +8 -0
  11. package/build/shared/src/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.js +42 -0
  12. package/build/shared/src/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +3 -0
  13. package/build/shared/src/pulsemcp-admin-client/lib/update-unified-mcp-server.js +14 -0
  14. package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +39 -0
  15. package/build/shared/src/server.js +20 -0
  16. package/build/shared/src/tools/add-servers-to-tenant.js +133 -0
  17. package/build/shared/src/tools/delete-api-key.js +119 -0
  18. package/build/shared/src/tools/delete-tenant.js +129 -0
  19. package/build/shared/src/tools/get-mcp-server.js +26 -8
  20. package/build/shared/src/tools/list-mcp-servers.js +3 -0
  21. package/build/shared/src/tools/list-tenant-servers.js +89 -0
  22. package/build/shared/src/tools/remove-servers-from-tenant.js +92 -0
  23. package/build/shared/src/tools/revoke-api-key.js +119 -0
  24. package/build/shared/src/tools/save-mcp-implementation.js +89 -2
  25. package/build/shared/src/tools/set-known-missing-init-tools-list.js +75 -0
  26. package/build/shared/src/tools/update-mcp-server.js +19 -0
  27. package/build/shared/src/tools.js +30 -1
  28. package/node_modules/@pulsemcp/mcp-elicitation/build/config.d.ts +15 -0
  29. package/node_modules/@pulsemcp/mcp-elicitation/build/config.js +41 -0
  30. package/node_modules/@pulsemcp/mcp-elicitation/build/elicitation.d.ts +24 -0
  31. package/node_modules/@pulsemcp/mcp-elicitation/build/elicitation.js +175 -0
  32. package/node_modules/@pulsemcp/mcp-elicitation/build/index.d.ts +3 -0
  33. package/node_modules/@pulsemcp/mcp-elicitation/build/index.js +2 -0
  34. package/node_modules/@pulsemcp/mcp-elicitation/build/types.d.ts +114 -0
  35. package/node_modules/@pulsemcp/mcp-elicitation/build/types.js +1 -0
  36. package/node_modules/@pulsemcp/mcp-elicitation/package.json +28 -0
  37. package/package.json +7 -1
  38. package/shared/elicitation-config.d.ts +61 -0
  39. package/shared/elicitation-config.js +69 -0
  40. package/shared/pulsemcp-admin-client/lib/bulk-update-tenant-servers.d.ts +3 -0
  41. package/shared/pulsemcp-admin-client/lib/bulk-update-tenant-servers.js +41 -0
  42. package/shared/pulsemcp-admin-client/lib/create-mcp-implementation.js +7 -0
  43. package/shared/pulsemcp-admin-client/lib/delete-api-key.d.ts +3 -0
  44. package/shared/pulsemcp-admin-client/lib/delete-api-key.js +25 -0
  45. package/shared/pulsemcp-admin-client/lib/delete-tenant.d.ts +3 -0
  46. package/shared/pulsemcp-admin-client/lib/delete-tenant.js +31 -0
  47. package/shared/pulsemcp-admin-client/lib/get-unified-mcp-server.js +5 -1
  48. package/shared/pulsemcp-admin-client/lib/list-tenant-servers.d.ts +7 -0
  49. package/shared/pulsemcp-admin-client/lib/list-tenant-servers.js +57 -0
  50. package/shared/pulsemcp-admin-client/lib/save-mcp-implementation.js +8 -0
  51. package/shared/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.d.ts +3 -0
  52. package/shared/pulsemcp-admin-client/lib/set-known-missing-init-tools-list.js +42 -0
  53. package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.d.ts +2 -0
  54. package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +3 -0
  55. package/shared/pulsemcp-admin-client/lib/update-unified-mcp-server.js +14 -0
  56. package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +39 -0
  57. package/shared/server.d.ts +19 -1
  58. package/shared/server.js +20 -0
  59. package/shared/tools/add-servers-to-tenant.d.ts +57 -0
  60. package/shared/tools/add-servers-to-tenant.js +133 -0
  61. package/shared/tools/delete-api-key.d.ts +30 -0
  62. package/shared/tools/delete-api-key.js +119 -0
  63. package/shared/tools/delete-tenant.d.ts +36 -0
  64. package/shared/tools/delete-tenant.js +129 -0
  65. package/shared/tools/get-mcp-server.js +26 -8
  66. package/shared/tools/list-mcp-servers.js +3 -0
  67. package/shared/tools/list-tenant-servers.d.ts +45 -0
  68. package/shared/tools/list-tenant-servers.js +89 -0
  69. package/shared/tools/remove-servers-from-tenant.d.ts +42 -0
  70. package/shared/tools/remove-servers-from-tenant.js +92 -0
  71. package/shared/tools/revoke-api-key.d.ts +30 -0
  72. package/shared/tools/revoke-api-key.js +119 -0
  73. package/shared/tools/save-mcp-implementation.d.ts +9 -1
  74. package/shared/tools/save-mcp-implementation.js +89 -2
  75. package/shared/tools/set-known-missing-init-tools-list.d.ts +38 -0
  76. package/shared/tools/set-known-missing-init-tools-list.js +75 -0
  77. package/shared/tools/update-mcp-server.d.ts +6 -0
  78. package/shared/tools/update-mcp-server.js +19 -0
  79. package/shared/tools.d.ts +5 -3
  80. package/shared/tools.js +30 -1
  81. 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
  }
@@ -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