pulsemcp-cms-admin-mcp-server 0.9.17 → 0.9.24
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/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 +29 -0
- package/build/shared/src/server.js +16 -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/update-mcp-server.js +19 -0
- package/build/shared/src/tools.js +22 -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/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 +29 -0
- package/shared/server.d.ts +17 -1
- package/shared/server.js +16 -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/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 +22 -1
- package/shared/types.d.ts +83 -0
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
// Hosts that must never appear as canonical URLs. Source-code hosts belong in
|
|
3
|
+
// `source_code_location`; aggregators are not authoritative. Mirrors the rule
|
|
4
|
+
// in agents/skills/server-discovery/identify-remote-canonical-url/SKILL.md.
|
|
5
|
+
export const BLOCKLISTED_CANONICAL_HOSTS = [
|
|
6
|
+
'github.com',
|
|
7
|
+
'gitlab.com',
|
|
8
|
+
'bitbucket.org',
|
|
9
|
+
'smithery.ai',
|
|
10
|
+
'glama.ai',
|
|
11
|
+
];
|
|
12
|
+
export function findBlocklistedCanonicalHost(url) {
|
|
13
|
+
let host;
|
|
14
|
+
try {
|
|
15
|
+
host = new URL(url).hostname.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
for (const blocked of BLOCKLISTED_CANONICAL_HOSTS) {
|
|
21
|
+
if (host === blocked || host.endsWith(`.${blocked}`)) {
|
|
22
|
+
return host;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
2
27
|
// Parameter descriptions - single source of truth
|
|
3
28
|
const PARAM_DESCRIPTIONS = {
|
|
4
29
|
id: 'The ID of the MCP implementation to update. Omit this field to CREATE a new implementation instead of updating an existing one.',
|
|
@@ -26,11 +51,12 @@ const PARAM_DESCRIPTIONS = {
|
|
|
26
51
|
// Remote endpoints
|
|
27
52
|
remote: 'Array of remote endpoint configurations for MCP servers. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all. Each remote can have: id (existing remote ID or blank for new), url_direct, url_setup, transport (e.g., "sse"), host_platform (e.g., "smithery"), host_infrastructure (e.g., "cloudflare"), authentication_method (e.g., "open"), cost (e.g., "free"), status (defaults to "live"), display_name, and internal_notes.',
|
|
28
53
|
// Canonical URLs
|
|
29
|
-
canonical: 'Array of canonical URL configurations. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain", or "url"), and optional note for additional context.',
|
|
54
|
+
canonical: 'Array of canonical URL configurations. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain", or "url"), and optional note for additional context. The host must NOT be one of: github.com, gitlab.com, bitbucket.org, smithery.ai, glama.ai (or any subdomain of those) — source-code repos belong in `source_code_location`, and aggregator listings are not authoritative. Calls with blocklisted canonical hosts are rejected with `CANONICAL_BLOCKLISTED_HOST`.',
|
|
30
55
|
// Flags
|
|
31
56
|
verified_no_remote_canonicals: 'Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)',
|
|
32
57
|
// Other fields
|
|
33
58
|
internal_notes: 'Admin-only notes. Not displayed publicly. Used for tracking submission sources, reviewer comments, etc.',
|
|
59
|
+
owner_tenant: 'Owner tenant of this server. Pass a tenant slug (e.g. "gram-recommended") or numeric tenant ID to link, or null to clear the link. Omit to leave unchanged. Slugs are preferred for readability; the Rails backend resolves them to a tenant ID. An unknown slug returns a validation error.',
|
|
34
60
|
};
|
|
35
61
|
const SaveMCPImplementationSchema = z.object({
|
|
36
62
|
id: z.number().optional().describe(PARAM_DESCRIPTIONS.id),
|
|
@@ -97,6 +123,10 @@ const SaveMCPImplementationSchema = z.object({
|
|
|
97
123
|
.describe(PARAM_DESCRIPTIONS.verified_no_remote_canonicals),
|
|
98
124
|
// Other fields
|
|
99
125
|
internal_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.internal_notes),
|
|
126
|
+
owner_tenant: z
|
|
127
|
+
.union([z.string(), z.number(), z.null()])
|
|
128
|
+
.optional()
|
|
129
|
+
.describe(PARAM_DESCRIPTIONS.owner_tenant),
|
|
100
130
|
});
|
|
101
131
|
export function saveMCPImplementation(_server, clientFactory) {
|
|
102
132
|
return {
|
|
@@ -314,13 +344,48 @@ Important notes:
|
|
|
314
344
|
type: 'string',
|
|
315
345
|
description: PARAM_DESCRIPTIONS.internal_notes,
|
|
316
346
|
},
|
|
347
|
+
// Owner tenant
|
|
348
|
+
owner_tenant: {
|
|
349
|
+
oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'null' }],
|
|
350
|
+
description: PARAM_DESCRIPTIONS.owner_tenant,
|
|
351
|
+
},
|
|
317
352
|
},
|
|
318
353
|
},
|
|
319
354
|
handler: async (args) => {
|
|
320
355
|
const validatedArgs = SaveMCPImplementationSchema.parse(args);
|
|
356
|
+
if (validatedArgs.canonical) {
|
|
357
|
+
for (const entry of validatedArgs.canonical) {
|
|
358
|
+
const blockedHost = findBlocklistedCanonicalHost(entry.url);
|
|
359
|
+
if (blockedHost) {
|
|
360
|
+
return {
|
|
361
|
+
content: [
|
|
362
|
+
{
|
|
363
|
+
type: 'text',
|
|
364
|
+
text: `CANONICAL_BLOCKLISTED_HOST: ${blockedHost} is not a valid canonical URL host. Use the actual project homepage instead.`,
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
isError: true,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
321
372
|
const client = clientFactory();
|
|
322
373
|
try {
|
|
323
|
-
const { id, type, ...restParams } = validatedArgs;
|
|
374
|
+
const { id, type, owner_tenant, ...restParams } = validatedArgs;
|
|
375
|
+
// Translate owner_tenant (string slug | number id | null clear) into the
|
|
376
|
+
// explicit owner_tenant_slug / owner_tenant_id fields the admin client
|
|
377
|
+
// forwards to Rails.
|
|
378
|
+
if (owner_tenant !== undefined) {
|
|
379
|
+
if (typeof owner_tenant === 'string') {
|
|
380
|
+
restParams.owner_tenant_slug = owner_tenant;
|
|
381
|
+
}
|
|
382
|
+
else if (typeof owner_tenant === 'number') {
|
|
383
|
+
restParams.owner_tenant_id = owner_tenant;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
restParams.owner_tenant_id = null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
324
389
|
// Determine if this is a create or update operation
|
|
325
390
|
const isCreate = id === undefined;
|
|
326
391
|
if (isCreate) {
|
|
@@ -386,6 +451,15 @@ Important notes:
|
|
|
386
451
|
content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
|
|
387
452
|
});
|
|
388
453
|
}
|
|
454
|
+
const createOwnerSlug = implementation.mcp_server?.owner_tenant_slug;
|
|
455
|
+
const createOwnerId = implementation.mcp_server?.owner_tenant_id;
|
|
456
|
+
if (createOwnerSlug || createOwnerId) {
|
|
457
|
+
content += `**Owner Tenant:** ${createOwnerSlug ?? '(no slug)'}`;
|
|
458
|
+
if (createOwnerId) {
|
|
459
|
+
content += ` (id: ${createOwnerId})`;
|
|
460
|
+
}
|
|
461
|
+
content += '\n';
|
|
462
|
+
}
|
|
389
463
|
if (implementation.created_at) {
|
|
390
464
|
content += `**Created:** ${new Date(implementation.created_at).toLocaleDateString()}\n`;
|
|
391
465
|
}
|
|
@@ -459,6 +533,19 @@ Important notes:
|
|
|
459
533
|
content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
|
|
460
534
|
});
|
|
461
535
|
}
|
|
536
|
+
const updateOwnerSlug = implementation.mcp_server?.owner_tenant_slug;
|
|
537
|
+
const updateOwnerId = implementation.mcp_server?.owner_tenant_id;
|
|
538
|
+
if (updateOwnerSlug || updateOwnerId) {
|
|
539
|
+
content += `**Owner Tenant:** ${updateOwnerSlug ?? '(no slug)'}`;
|
|
540
|
+
if (updateOwnerId) {
|
|
541
|
+
content += ` (id: ${updateOwnerId})`;
|
|
542
|
+
}
|
|
543
|
+
content += '\n';
|
|
544
|
+
}
|
|
545
|
+
else if (implementation.mcp_server?.owner_tenant_id === null &&
|
|
546
|
+
implementation.mcp_server?.owner_tenant_slug === null) {
|
|
547
|
+
content += `**Owner Tenant:** (none)\n`;
|
|
548
|
+
}
|
|
462
549
|
if (implementation.updated_at) {
|
|
463
550
|
content += `**Updated:** ${new Date(implementation.updated_at).toLocaleDateString()}\n`;
|
|
464
551
|
}
|
|
@@ -183,6 +183,12 @@ export declare function updateMCPServer(_server: Server, clientFactory: ClientFa
|
|
|
183
183
|
type: string;
|
|
184
184
|
description: "Admin-only internal notes";
|
|
185
185
|
};
|
|
186
|
+
owner_tenant: {
|
|
187
|
+
oneOf: {
|
|
188
|
+
type: string;
|
|
189
|
+
}[];
|
|
190
|
+
description: "Owner tenant of this server. Pass a tenant slug (e.g. \"gram-recommended\") or numeric tenant ID to link, or null to clear the link. Omit to leave unchanged. Slugs are preferred for readability; the Rails backend resolves them to a tenant ID. An unknown slug returns a validation error.";
|
|
191
|
+
};
|
|
186
192
|
};
|
|
187
193
|
required: string[];
|
|
188
194
|
};
|
|
@@ -22,6 +22,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
22
22
|
canonical_urls: 'Authoritative URLs for the server. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all.',
|
|
23
23
|
remotes: 'Remote endpoints for the server. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all.',
|
|
24
24
|
internal_notes: 'Admin-only internal notes',
|
|
25
|
+
owner_tenant: 'Owner tenant of this server. Pass a tenant slug (e.g. "gram-recommended") or numeric tenant ID to link, or null to clear the link. Omit to leave unchanged. Slugs are preferred for readability; the Rails backend resolves them to a tenant ID. An unknown slug returns a validation error.',
|
|
25
26
|
};
|
|
26
27
|
const CanonicalUrlSchema = z.object({
|
|
27
28
|
url: z.string().describe('The canonical URL'),
|
|
@@ -95,6 +96,10 @@ const UpdateMCPServerSchema = z.object({
|
|
|
95
96
|
.describe(PARAM_DESCRIPTIONS.canonical_urls),
|
|
96
97
|
remotes: z.array(RemoteEndpointSchema).optional().describe(PARAM_DESCRIPTIONS.remotes),
|
|
97
98
|
internal_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.internal_notes),
|
|
99
|
+
owner_tenant: z
|
|
100
|
+
.union([z.string(), z.number(), z.null()])
|
|
101
|
+
.optional()
|
|
102
|
+
.describe(PARAM_DESCRIPTIONS.owner_tenant),
|
|
98
103
|
});
|
|
99
104
|
export function updateMCPServer(_server, clientFactory) {
|
|
100
105
|
return {
|
|
@@ -284,6 +289,10 @@ Create new provider:
|
|
|
284
289
|
description: PARAM_DESCRIPTIONS.remotes,
|
|
285
290
|
},
|
|
286
291
|
internal_notes: { type: 'string', description: PARAM_DESCRIPTIONS.internal_notes },
|
|
292
|
+
owner_tenant: {
|
|
293
|
+
oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'null' }],
|
|
294
|
+
description: PARAM_DESCRIPTIONS.owner_tenant,
|
|
295
|
+
},
|
|
287
296
|
},
|
|
288
297
|
required: ['implementation_id'],
|
|
289
298
|
},
|
|
@@ -335,6 +344,16 @@ Create new provider:
|
|
|
335
344
|
if (server.verified_no_remote_canonicals !== undefined) {
|
|
336
345
|
content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
|
|
337
346
|
}
|
|
347
|
+
if (server.owner_tenant_slug || server.owner_tenant_id) {
|
|
348
|
+
content += `**Owner Tenant:** ${server.owner_tenant_slug ?? '(no slug)'}`;
|
|
349
|
+
if (server.owner_tenant_id) {
|
|
350
|
+
content += ` (id: ${server.owner_tenant_id})`;
|
|
351
|
+
}
|
|
352
|
+
content += '\n';
|
|
353
|
+
}
|
|
354
|
+
else if (server.owner_tenant_id === null && server.owner_tenant_slug === null) {
|
|
355
|
+
content += `**Owner Tenant:** (none)\n`;
|
|
356
|
+
}
|
|
338
357
|
if (server.updated_at) {
|
|
339
358
|
content += `**Updated:** ${server.updated_at}\n`;
|
|
340
359
|
}
|
package/shared/tools.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ import { ClientFactory } from './server.js';
|
|
|
18
18
|
* - official_queue / official_queue_readonly: Official mirror queue tools (list, get, approve, reject, unlink)
|
|
19
19
|
* - unofficial_mirrors / unofficial_mirrors_readonly: Unofficial mirror CRUD tools
|
|
20
20
|
* - official_mirrors_readonly: Official mirrors read-only tools (REST API)
|
|
21
|
-
* - tenants / tenants_readonly: Tenant management tools (CRUD + API key provisioning)
|
|
21
|
+
* - tenants / tenants_readonly: Tenant management tools (CRUD + API key provisioning, including revoke_api_key for rolling keys after re-issuance)
|
|
22
|
+
* - tenants_destructive: Destructive tenant tools (delete_tenant, delete_api_key). NOT enabled by default — operators must opt in via TOOL_GROUPS. Each tool requires MCP elicitation user approval before execution.
|
|
22
23
|
* - mcp_jsons / mcp_jsons_readonly: MCP JSON configuration tools
|
|
23
24
|
* - mcp_servers / mcp_servers_readonly: Unified MCP server tools (abstracted interface)
|
|
24
25
|
* - redirects / redirects_readonly: URL redirect management tools
|
|
@@ -28,7 +29,7 @@ import { ClientFactory } from './server.js';
|
|
|
28
29
|
* - moz / moz_readonly: MOZ SEO metrics tools (live URL metrics, backlinks, stored historical data)
|
|
29
30
|
* - notifications: Notification email tools (send_impl_posted_notif). Separated from server_directory so notification capability can be granted independently.
|
|
30
31
|
*/
|
|
31
|
-
export type ToolGroup = 'newsletter' | 'newsletter_readonly' | 'server_directory' | 'server_directory_readonly' | 'official_queue' | 'official_queue_readonly' | 'unofficial_mirrors' | 'unofficial_mirrors_readonly' | 'official_mirrors' | 'official_mirrors_readonly' | 'tenants' | 'tenants_readonly' | 'mcp_jsons' | 'mcp_jsons_readonly' | 'mcp_servers' | 'mcp_servers_readonly' | 'redirects' | 'redirects_readonly' | 'good_jobs' | 'good_jobs_readonly' | 'proctor' | 'proctor_readonly' | 'discovered_urls' | 'discovered_urls_readonly' | 'moz' | 'moz_readonly' | 'notifications';
|
|
32
|
+
export type ToolGroup = 'newsletter' | 'newsletter_readonly' | 'server_directory' | 'server_directory_readonly' | 'official_queue' | 'official_queue_readonly' | 'unofficial_mirrors' | 'unofficial_mirrors_readonly' | 'official_mirrors' | 'official_mirrors_readonly' | 'tenants' | 'tenants_readonly' | 'tenants_destructive' | 'mcp_jsons' | 'mcp_jsons_readonly' | 'mcp_servers' | 'mcp_servers_readonly' | 'redirects' | 'redirects_readonly' | 'good_jobs' | 'good_jobs_readonly' | 'proctor' | 'proctor_readonly' | 'discovered_urls' | 'discovered_urls_readonly' | 'moz' | 'moz_readonly' | 'notifications';
|
|
32
33
|
/**
|
|
33
34
|
* Parse enabled tool groups from environment variable or parameter
|
|
34
35
|
* @param enabledGroupsParam - Comma-separated list of tool groups (e.g., "newsletter,server_directory_readonly")
|
|
@@ -60,8 +61,9 @@ export declare function parseEnabledToolGroups(enabledGroupsParam?: string): Too
|
|
|
60
61
|
* - unofficial_mirrors: Unofficial mirror CRUD tools (read + write)
|
|
61
62
|
* - unofficial_mirrors_readonly: Unofficial mirror tools (read only)
|
|
62
63
|
* - official_mirrors_readonly: Official mirrors REST API tools (read only)
|
|
63
|
-
* - tenants: Tenant management tools including API key provisioning (read + write)
|
|
64
|
+
* - tenants: Tenant management tools including API key provisioning + revoke_api_key (read + write). revoke_api_key is gated by MCP elicitation user approval before execution.
|
|
64
65
|
* - tenants_readonly: Tenant tools (read only)
|
|
66
|
+
* - tenants_destructive: Destructive tenant tools (delete_tenant, delete_api_key). NOT enabled by default; operators must opt in via TOOL_GROUPS. Each tool requires MCP elicitation user approval before execution.
|
|
65
67
|
* - mcp_jsons: MCP JSON configuration tools (read + write)
|
|
66
68
|
* - mcp_jsons_readonly: MCP JSON tools (read only)
|
|
67
69
|
* - mcp_servers: Unified MCP server tools with abstracted interface (read + write)
|
package/shared/tools.js
CHANGED
|
@@ -31,6 +31,12 @@ import { getTenants } from './tools/get-tenants.js';
|
|
|
31
31
|
import { getTenant } from './tools/get-tenant.js';
|
|
32
32
|
import { createTenant } from './tools/create-tenant.js';
|
|
33
33
|
import { createApiKey } from './tools/create-api-key.js';
|
|
34
|
+
import { revokeApiKey } from './tools/revoke-api-key.js';
|
|
35
|
+
import { deleteTenant } from './tools/delete-tenant.js';
|
|
36
|
+
import { deleteApiKey } from './tools/delete-api-key.js';
|
|
37
|
+
import { listTenantServers } from './tools/list-tenant-servers.js';
|
|
38
|
+
import { addServersToTenant } from './tools/add-servers-to-tenant.js';
|
|
39
|
+
import { removeServersFromTenant } from './tools/remove-servers-from-tenant.js';
|
|
34
40
|
// MCP JSON tools
|
|
35
41
|
import { getMcpJsons } from './tools/get-mcp-jsons.js';
|
|
36
42
|
import { getMcpJson } from './tools/get-mcp-json.js';
|
|
@@ -178,6 +184,19 @@ const ALL_TOOLS = [
|
|
|
178
184
|
{ factory: getTenant, groups: ['tenants'], isWriteOperation: false },
|
|
179
185
|
{ factory: createTenant, groups: ['tenants'], isWriteOperation: true },
|
|
180
186
|
{ factory: createApiKey, groups: ['tenants'], isWriteOperation: true },
|
|
187
|
+
// revoke_api_key lives in the regular `tenants` group (not `tenants_destructive`) so
|
|
188
|
+
// it's exposed to read-write tenant management workflows like sub-registry credential
|
|
189
|
+
// re-issuance. The tool gates each call with MCP elicitation; if the connected client
|
|
190
|
+
// supports neither native elicitation nor an HTTP fallback, the elicitation library
|
|
191
|
+
// throws at runtime rather than silently destroying the key.
|
|
192
|
+
{ factory: revokeApiKey, groups: ['tenants'], isWriteOperation: true },
|
|
193
|
+
// Destructive tenant tools — opt-in only, NOT in BASE_TOOL_GROUPS
|
|
194
|
+
{ factory: deleteTenant, groups: ['tenants_destructive'], isWriteOperation: true },
|
|
195
|
+
{ factory: deleteApiKey, groups: ['tenants_destructive'], isWriteOperation: true },
|
|
196
|
+
// Tenant -> recommended MCP server association tools
|
|
197
|
+
{ factory: listTenantServers, groups: ['tenants'], isWriteOperation: false },
|
|
198
|
+
{ factory: addServersToTenant, groups: ['tenants'], isWriteOperation: true },
|
|
199
|
+
{ factory: removeServersFromTenant, groups: ['tenants'], isWriteOperation: true },
|
|
181
200
|
// MCP JSON tools (CRUD) (also in server_directory)
|
|
182
201
|
{
|
|
183
202
|
factory: getMcpJsons,
|
|
@@ -277,6 +296,7 @@ const VALID_TOOL_GROUPS = [
|
|
|
277
296
|
'official_mirrors_readonly',
|
|
278
297
|
'tenants',
|
|
279
298
|
'tenants_readonly',
|
|
299
|
+
'tenants_destructive',
|
|
280
300
|
'mcp_jsons',
|
|
281
301
|
'mcp_jsons_readonly',
|
|
282
302
|
'mcp_servers',
|
|
@@ -382,8 +402,9 @@ function shouldIncludeTool(toolDef, enabledGroups) {
|
|
|
382
402
|
* - unofficial_mirrors: Unofficial mirror CRUD tools (read + write)
|
|
383
403
|
* - unofficial_mirrors_readonly: Unofficial mirror tools (read only)
|
|
384
404
|
* - official_mirrors_readonly: Official mirrors REST API tools (read only)
|
|
385
|
-
* - tenants: Tenant management tools including API key provisioning (read + write)
|
|
405
|
+
* - tenants: Tenant management tools including API key provisioning + revoke_api_key (read + write). revoke_api_key is gated by MCP elicitation user approval before execution.
|
|
386
406
|
* - tenants_readonly: Tenant tools (read only)
|
|
407
|
+
* - tenants_destructive: Destructive tenant tools (delete_tenant, delete_api_key). NOT enabled by default; operators must opt in via TOOL_GROUPS. Each tool requires MCP elicitation user approval before execution.
|
|
387
408
|
* - mcp_jsons: MCP JSON configuration tools (read + write)
|
|
388
409
|
* - mcp_jsons_readonly: MCP JSON tools (read only)
|
|
389
410
|
* - mcp_servers: Unified MCP server tools with abstracted interface (read + write)
|
package/shared/types.d.ts
CHANGED
|
@@ -126,6 +126,8 @@ export interface MCPServer {
|
|
|
126
126
|
mcp_server_remotes_count?: number;
|
|
127
127
|
recommended?: boolean;
|
|
128
128
|
verified_no_remote_canonicals?: boolean;
|
|
129
|
+
owner_tenant_id?: number | null;
|
|
130
|
+
owner_tenant_slug?: string | null;
|
|
129
131
|
tags?: MCPServerTag[];
|
|
130
132
|
remotes?: MCPServerRemote[];
|
|
131
133
|
}
|
|
@@ -196,6 +198,8 @@ export interface SaveMCPImplementationParams {
|
|
|
196
198
|
implementation_language?: string;
|
|
197
199
|
mcp_server_id?: number | null;
|
|
198
200
|
mcp_client_id?: number | null;
|
|
201
|
+
owner_tenant_id?: number | null;
|
|
202
|
+
owner_tenant_slug?: string | null;
|
|
199
203
|
provider_id?: string | number;
|
|
200
204
|
provider_slug?: string;
|
|
201
205
|
provider_url?: string;
|
|
@@ -247,6 +251,8 @@ export interface CreateMCPImplementationParams {
|
|
|
247
251
|
remote?: RemoteEndpointParams[];
|
|
248
252
|
canonical?: CanonicalUrlParams[];
|
|
249
253
|
internal_notes?: string;
|
|
254
|
+
owner_tenant_id?: number | null;
|
|
255
|
+
owner_tenant_slug?: string | null;
|
|
250
256
|
}
|
|
251
257
|
export interface Provider {
|
|
252
258
|
id: number;
|
|
@@ -427,6 +433,68 @@ export interface TenantsResponse {
|
|
|
427
433
|
limit?: number;
|
|
428
434
|
};
|
|
429
435
|
}
|
|
436
|
+
export interface TenantServer {
|
|
437
|
+
id: number;
|
|
438
|
+
tenant_id: number;
|
|
439
|
+
mcp_server_id: number;
|
|
440
|
+
mcp_server_slug: string;
|
|
441
|
+
status: 'active' | 'deleted';
|
|
442
|
+
server_json_selection: string;
|
|
443
|
+
first_touched_at?: string | null;
|
|
444
|
+
touched: boolean;
|
|
445
|
+
created_at?: string;
|
|
446
|
+
updated_at?: string;
|
|
447
|
+
}
|
|
448
|
+
export interface ListTenantServersResponse {
|
|
449
|
+
data: TenantServer[];
|
|
450
|
+
pagination: {
|
|
451
|
+
current_page: number;
|
|
452
|
+
total_pages: number;
|
|
453
|
+
total_count: number;
|
|
454
|
+
has_next: boolean;
|
|
455
|
+
limit: number;
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
export interface BulkUpdateTenantServersParams {
|
|
459
|
+
add_server_identifiers?: Array<number | string>;
|
|
460
|
+
remove_server_identifiers?: Array<number | string>;
|
|
461
|
+
restore_association_ids?: number[];
|
|
462
|
+
}
|
|
463
|
+
export interface BulkUpdateAddedItem {
|
|
464
|
+
mcp_server_id: number;
|
|
465
|
+
mcp_server_slug: string;
|
|
466
|
+
association_id: number;
|
|
467
|
+
outcome: 'created' | 'restored_from_deleted';
|
|
468
|
+
}
|
|
469
|
+
export interface BulkUpdateRemovedItem {
|
|
470
|
+
mcp_server_id: number;
|
|
471
|
+
mcp_server_slug: string;
|
|
472
|
+
association_id: number | null;
|
|
473
|
+
outcome: 'hard_deleted' | 'soft_deleted';
|
|
474
|
+
}
|
|
475
|
+
export interface BulkUpdateRestoredItem {
|
|
476
|
+
mcp_server_id: number;
|
|
477
|
+
mcp_server_slug: string;
|
|
478
|
+
association_id: number;
|
|
479
|
+
}
|
|
480
|
+
export interface BulkUpdateSkippedItem {
|
|
481
|
+
mcp_server_id?: number;
|
|
482
|
+
mcp_server_slug?: string;
|
|
483
|
+
association_id?: number;
|
|
484
|
+
reason: 'no_unofficial_mirror' | 'already_active' | string;
|
|
485
|
+
}
|
|
486
|
+
export interface BulkUpdateTenantServersResponse {
|
|
487
|
+
status: 'success';
|
|
488
|
+
tenant: {
|
|
489
|
+
id: number;
|
|
490
|
+
slug: string;
|
|
491
|
+
};
|
|
492
|
+
added: BulkUpdateAddedItem[];
|
|
493
|
+
removed: BulkUpdateRemovedItem[];
|
|
494
|
+
restored: BulkUpdateRestoredItem[];
|
|
495
|
+
skipped: BulkUpdateSkippedItem[];
|
|
496
|
+
unresolved_identifiers: Array<number | string>;
|
|
497
|
+
}
|
|
430
498
|
export interface McpJson {
|
|
431
499
|
id: number;
|
|
432
500
|
mcp_servers_unofficial_mirror_id: number;
|
|
@@ -529,6 +597,8 @@ export interface UnifiedMCPServer {
|
|
|
529
597
|
downloads_estimate_last_30_days?: number;
|
|
530
598
|
downloads_estimate_total?: number;
|
|
531
599
|
internal_notes?: string;
|
|
600
|
+
owner_tenant_id?: number | null;
|
|
601
|
+
owner_tenant_slug?: string | null;
|
|
532
602
|
created_at?: string;
|
|
533
603
|
updated_at?: string;
|
|
534
604
|
created_on_override?: string;
|
|
@@ -575,6 +645,7 @@ export interface UpdateUnifiedMCPServerParams {
|
|
|
575
645
|
canonical_urls?: CanonicalUrl[];
|
|
576
646
|
remotes?: RemoteEndpoint[];
|
|
577
647
|
internal_notes?: string;
|
|
648
|
+
owner_tenant?: string | number | null;
|
|
578
649
|
}
|
|
579
650
|
export type RedirectStatus = 'draft' | 'active' | 'paused' | 'archived';
|
|
580
651
|
export interface Redirect {
|
|
@@ -805,6 +876,18 @@ export interface CreateApiKeyParams {
|
|
|
805
876
|
name?: string;
|
|
806
877
|
permission_level?: ApiKeyPermissionLevel;
|
|
807
878
|
}
|
|
879
|
+
export interface DeleteTenantParams {
|
|
880
|
+
id_or_slug: number | string;
|
|
881
|
+
force?: boolean;
|
|
882
|
+
}
|
|
883
|
+
export interface DeleteTenantResponse {
|
|
884
|
+
success: boolean;
|
|
885
|
+
message: string;
|
|
886
|
+
}
|
|
887
|
+
export interface DeleteApiKeyResponse {
|
|
888
|
+
success: boolean;
|
|
889
|
+
message: string;
|
|
890
|
+
}
|
|
808
891
|
export interface RecacheMCPServerResponse {
|
|
809
892
|
message: string;
|
|
810
893
|
}
|