pulsemcp-cms-admin-mcp-server 0.6.2 → 0.6.3
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/build/index.js +9 -1
- package/build/local/src/index.integration-with-mock.js +9 -1
- package/build/shared/src/pulsemcp-admin-client/lib/create-mcp-implementation.js +179 -0
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +27 -0
- package/build/shared/src/server.js +6 -2
- package/build/shared/src/tools/save-mcp-implementation.js +157 -92
- package/package.json +1 -1
- package/shared/index.d.ts +1 -1
- package/shared/pulsemcp-admin-client/lib/create-mcp-implementation.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/create-mcp-implementation.js +179 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +27 -0
- package/shared/server.d.ts +7 -2
- package/shared/server.js +6 -2
- package/shared/tools/save-mcp-implementation.d.ts +12 -13
- package/shared/tools/save-mcp-implementation.js +157 -92
- package/shared/types.d.ts +34 -0
package/build/index.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
2
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
6
|
import { createMCPServer } from '../shared/index.js';
|
|
4
7
|
import { logServerStart, logError } from '../shared/logging.js';
|
|
8
|
+
// Read version from package.json
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
11
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
12
|
+
const VERSION = packageJson.version;
|
|
5
13
|
// Validate required environment variables before starting
|
|
6
14
|
function validateEnvironment() {
|
|
7
15
|
const required = [
|
|
@@ -33,7 +41,7 @@ async function main() {
|
|
|
33
41
|
// Validate environment variables first
|
|
34
42
|
validateEnvironment();
|
|
35
43
|
// Create server using factory
|
|
36
|
-
const { server, registerHandlers } = createMCPServer();
|
|
44
|
+
const { server, registerHandlers } = createMCPServer({ version: VERSION });
|
|
37
45
|
// Register all handlers (resources and tools)
|
|
38
46
|
await registerHandlers(server);
|
|
39
47
|
// Start server
|
|
@@ -7,11 +7,19 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Mock data is passed via the PULSEMCP_MOCK_DATA environment variable.
|
|
9
9
|
*/
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
10
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
14
|
// IMPORTANT: This uses the package name pattern, not a relative path
|
|
12
15
|
import { createMCPServer } from 'pulsemcp-cms-admin-mcp-server-shared';
|
|
13
16
|
// Import the mock client factory from the shared module
|
|
14
17
|
import { createMockPulseMCPAdminClient } from '../../shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js';
|
|
18
|
+
// Read version from package.json
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
21
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
22
|
+
const VERSION = packageJson.version;
|
|
15
23
|
async function main() {
|
|
16
24
|
const transport = new StdioServerTransport();
|
|
17
25
|
// Parse mock data from environment variable
|
|
@@ -26,7 +34,7 @@ async function main() {
|
|
|
26
34
|
}
|
|
27
35
|
// Create client factory that returns our mock
|
|
28
36
|
const clientFactory = () => createMockPulseMCPAdminClient(mockData);
|
|
29
|
-
const { server, registerHandlers } = createMCPServer();
|
|
37
|
+
const { server, registerHandlers } = createMCPServer({ version: VERSION });
|
|
30
38
|
await registerHandlers(server, clientFactory);
|
|
31
39
|
await server.connect(transport);
|
|
32
40
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
export async function createMCPImplementation(apiKey, baseUrl, params) {
|
|
2
|
+
const url = new URL(`/api/implementations`, baseUrl);
|
|
3
|
+
// Build form data for the POST request
|
|
4
|
+
const formData = new URLSearchParams();
|
|
5
|
+
// Required fields for creation
|
|
6
|
+
formData.append('mcp_implementation[name]', params.name);
|
|
7
|
+
formData.append('mcp_implementation[type]', params.type);
|
|
8
|
+
// Optional fields
|
|
9
|
+
if (params.short_description !== undefined) {
|
|
10
|
+
formData.append('mcp_implementation[short_description]', params.short_description);
|
|
11
|
+
}
|
|
12
|
+
if (params.description !== undefined) {
|
|
13
|
+
formData.append('mcp_implementation[description]', params.description);
|
|
14
|
+
}
|
|
15
|
+
if (params.status !== undefined) {
|
|
16
|
+
formData.append('mcp_implementation[status]', params.status);
|
|
17
|
+
}
|
|
18
|
+
if (params.slug !== undefined) {
|
|
19
|
+
formData.append('mcp_implementation[slug]', params.slug);
|
|
20
|
+
}
|
|
21
|
+
if (params.url !== undefined) {
|
|
22
|
+
// Backend expects 'marketing_url' field, but tool exposes it as 'url' for better UX
|
|
23
|
+
formData.append('mcp_implementation[marketing_url]', params.url);
|
|
24
|
+
}
|
|
25
|
+
if (params.provider_name !== undefined) {
|
|
26
|
+
formData.append('mcp_implementation[provider_name]', params.provider_name);
|
|
27
|
+
}
|
|
28
|
+
if (params.github_stars !== undefined) {
|
|
29
|
+
formData.append('mcp_implementation[github_stars]', params.github_stars === null ? '' : params.github_stars.toString());
|
|
30
|
+
}
|
|
31
|
+
if (params.classification !== undefined) {
|
|
32
|
+
formData.append('mcp_implementation[classification]', params.classification);
|
|
33
|
+
}
|
|
34
|
+
if (params.implementation_language !== undefined) {
|
|
35
|
+
formData.append('mcp_implementation[implementation_language]', params.implementation_language);
|
|
36
|
+
}
|
|
37
|
+
if (params.mcp_server_id !== undefined) {
|
|
38
|
+
formData.append('mcp_implementation[mcp_server_id]', params.mcp_server_id === null ? '' : params.mcp_server_id.toString());
|
|
39
|
+
}
|
|
40
|
+
if (params.mcp_client_id !== undefined) {
|
|
41
|
+
formData.append('mcp_implementation[mcp_client_id]', params.mcp_client_id === null ? '' : params.mcp_client_id.toString());
|
|
42
|
+
}
|
|
43
|
+
// Provider creation/linking
|
|
44
|
+
if (params.provider_id !== undefined) {
|
|
45
|
+
formData.append('mcp_implementation[provider_id]', params.provider_id.toString());
|
|
46
|
+
}
|
|
47
|
+
if (params.provider_slug !== undefined) {
|
|
48
|
+
formData.append('mcp_implementation[provider_slug]', params.provider_slug);
|
|
49
|
+
}
|
|
50
|
+
if (params.provider_url !== undefined) {
|
|
51
|
+
formData.append('mcp_implementation[provider_url]', params.provider_url);
|
|
52
|
+
}
|
|
53
|
+
// GitHub repository fields
|
|
54
|
+
if (params.github_owner !== undefined) {
|
|
55
|
+
formData.append('mcp_implementation[github_owner]', params.github_owner);
|
|
56
|
+
}
|
|
57
|
+
if (params.github_repo !== undefined) {
|
|
58
|
+
formData.append('mcp_implementation[github_repo]', params.github_repo);
|
|
59
|
+
}
|
|
60
|
+
if (params.github_subfolder !== undefined) {
|
|
61
|
+
formData.append('mcp_implementation[github_subfolder]', params.github_subfolder);
|
|
62
|
+
}
|
|
63
|
+
// Package registry fields
|
|
64
|
+
if (params.package_registry !== undefined) {
|
|
65
|
+
formData.append('mcp_implementation[package_registry]', params.package_registry);
|
|
66
|
+
}
|
|
67
|
+
if (params.package_name !== undefined) {
|
|
68
|
+
formData.append('mcp_implementation[package_name]', params.package_name);
|
|
69
|
+
}
|
|
70
|
+
// Flags
|
|
71
|
+
if (params.recommended !== undefined) {
|
|
72
|
+
formData.append('mcp_implementation[recommended]', params.recommended.toString());
|
|
73
|
+
}
|
|
74
|
+
// Date overrides
|
|
75
|
+
if (params.created_on_override !== undefined) {
|
|
76
|
+
formData.append('mcp_implementation[created_on_override]', params.created_on_override);
|
|
77
|
+
}
|
|
78
|
+
// Tags
|
|
79
|
+
if (params.tags !== undefined) {
|
|
80
|
+
if (params.tags.length > 0) {
|
|
81
|
+
params.tags.forEach((tagSlug, index) => {
|
|
82
|
+
formData.append(`mcp_implementation[tags][${index}]`, tagSlug);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Empty array explicitly provided - send empty array marker to Rails
|
|
87
|
+
formData.append('mcp_implementation[tags]', '[]');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Internal notes
|
|
91
|
+
if (params.internal_notes !== undefined) {
|
|
92
|
+
formData.append('mcp_implementation[internal_notes]', params.internal_notes);
|
|
93
|
+
}
|
|
94
|
+
// Remote endpoints
|
|
95
|
+
// Rails expects nested attributes to use the _attributes suffix for has_many associations
|
|
96
|
+
if (params.remote !== undefined) {
|
|
97
|
+
if (params.remote.length > 0) {
|
|
98
|
+
params.remote.forEach((remote, index) => {
|
|
99
|
+
if (remote.id !== undefined) {
|
|
100
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][id]`, remote.id.toString());
|
|
101
|
+
}
|
|
102
|
+
if (remote.url_direct !== undefined) {
|
|
103
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][url_direct]`, remote.url_direct);
|
|
104
|
+
}
|
|
105
|
+
if (remote.url_setup !== undefined) {
|
|
106
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][url_setup]`, remote.url_setup);
|
|
107
|
+
}
|
|
108
|
+
if (remote.transport !== undefined) {
|
|
109
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][transport]`, remote.transport);
|
|
110
|
+
}
|
|
111
|
+
if (remote.host_platform !== undefined) {
|
|
112
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][host_platform]`, remote.host_platform);
|
|
113
|
+
}
|
|
114
|
+
if (remote.host_infrastructure !== undefined) {
|
|
115
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][host_infrastructure]`, remote.host_infrastructure);
|
|
116
|
+
}
|
|
117
|
+
if (remote.authentication_method !== undefined) {
|
|
118
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][authentication_method]`, remote.authentication_method);
|
|
119
|
+
}
|
|
120
|
+
if (remote.cost !== undefined) {
|
|
121
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][cost]`, remote.cost);
|
|
122
|
+
}
|
|
123
|
+
if (remote.status !== undefined) {
|
|
124
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][status]`, remote.status);
|
|
125
|
+
}
|
|
126
|
+
if (remote.display_name !== undefined) {
|
|
127
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][display_name]`, remote.display_name);
|
|
128
|
+
}
|
|
129
|
+
if (remote.internal_notes !== undefined) {
|
|
130
|
+
formData.append(`mcp_implementation[remote_attributes][${index}][internal_notes]`, remote.internal_notes);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Canonical URLs
|
|
136
|
+
// Rails expects nested attributes to use the _attributes suffix for has_many associations
|
|
137
|
+
if (params.canonical !== undefined) {
|
|
138
|
+
if (params.canonical.length > 0) {
|
|
139
|
+
params.canonical.forEach((canonicalUrl, index) => {
|
|
140
|
+
formData.append(`mcp_implementation[canonical_attributes][${index}][url]`, canonicalUrl.url);
|
|
141
|
+
formData.append(`mcp_implementation[canonical_attributes][${index}][scope]`, canonicalUrl.scope);
|
|
142
|
+
if (canonicalUrl.note !== undefined) {
|
|
143
|
+
formData.append(`mcp_implementation[canonical_attributes][${index}][note]`, canonicalUrl.note);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const response = await fetch(url.toString(), {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
headers: {
|
|
151
|
+
'X-API-Key': apiKey,
|
|
152
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
153
|
+
Accept: 'application/json',
|
|
154
|
+
},
|
|
155
|
+
body: formData.toString(),
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
if (response.status === 401) {
|
|
159
|
+
throw new Error('Invalid API key');
|
|
160
|
+
}
|
|
161
|
+
if (response.status === 403) {
|
|
162
|
+
throw new Error('User lacks admin privileges');
|
|
163
|
+
}
|
|
164
|
+
if (response.status === 422) {
|
|
165
|
+
const errorData = (await response.json());
|
|
166
|
+
// Handle both array format and single error string format from Rails
|
|
167
|
+
// Also handle empty arrays - an empty array should fall back to the default message
|
|
168
|
+
const errors = errorData.errors && errorData.errors.length > 0
|
|
169
|
+
? errorData.errors
|
|
170
|
+
: errorData.error
|
|
171
|
+
? [errorData.error]
|
|
172
|
+
: ['Unknown validation error'];
|
|
173
|
+
throw new Error(`Validation failed: ${errors.join(', ')}`);
|
|
174
|
+
}
|
|
175
|
+
throw new Error(`Failed to create MCP implementation: ${response.status} ${response.statusText}`);
|
|
176
|
+
}
|
|
177
|
+
const data = await response.json();
|
|
178
|
+
return data;
|
|
179
|
+
}
|
|
@@ -315,6 +315,33 @@ export function createMockPulseMCPAdminClient(mockData) {
|
|
|
315
315
|
};
|
|
316
316
|
return updatedImpl;
|
|
317
317
|
},
|
|
318
|
+
async createMCPImplementation(params) {
|
|
319
|
+
// Create a new implementation with mock data
|
|
320
|
+
const newImpl = {
|
|
321
|
+
id: Math.floor(Math.random() * 10000) + 1000,
|
|
322
|
+
name: params.name,
|
|
323
|
+
type: params.type,
|
|
324
|
+
status: params.status || 'draft',
|
|
325
|
+
slug: params.slug || params.name.toLowerCase().replace(/\s+/g, '-'),
|
|
326
|
+
short_description: params.short_description,
|
|
327
|
+
description: params.description,
|
|
328
|
+
classification: params.classification,
|
|
329
|
+
implementation_language: params.implementation_language,
|
|
330
|
+
url: params.url,
|
|
331
|
+
provider_name: params.provider_name,
|
|
332
|
+
github_stars: params.github_stars,
|
|
333
|
+
mcp_server_id: params.mcp_server_id,
|
|
334
|
+
mcp_client_id: params.mcp_client_id,
|
|
335
|
+
github_owner: params.github_owner,
|
|
336
|
+
github_repo: params.github_repo,
|
|
337
|
+
github_subfolder: params.github_subfolder,
|
|
338
|
+
internal_notes: params.internal_notes,
|
|
339
|
+
canonical: params.canonical,
|
|
340
|
+
created_at: new Date().toISOString(),
|
|
341
|
+
updated_at: new Date().toISOString(),
|
|
342
|
+
};
|
|
343
|
+
return newImpl;
|
|
344
|
+
},
|
|
318
345
|
async sendEmail(params) {
|
|
319
346
|
if (mockData.errors?.sendEmail) {
|
|
320
347
|
throw mockData.errors.sendEmail;
|
|
@@ -72,6 +72,10 @@ export class PulseMCPAdminClient {
|
|
|
72
72
|
const { saveMCPImplementation } = await import('./pulsemcp-admin-client/lib/save-mcp-implementation.js');
|
|
73
73
|
return saveMCPImplementation(this.apiKey, this.baseUrl, id, params);
|
|
74
74
|
}
|
|
75
|
+
async createMCPImplementation(params) {
|
|
76
|
+
const { createMCPImplementation } = await import('./pulsemcp-admin-client/lib/create-mcp-implementation.js');
|
|
77
|
+
return createMCPImplementation(this.apiKey, this.baseUrl, params);
|
|
78
|
+
}
|
|
75
79
|
async sendEmail(params) {
|
|
76
80
|
const { sendEmail } = await import('./pulsemcp-admin-client/lib/send-email.js');
|
|
77
81
|
return sendEmail(this.apiKey, this.baseUrl, params);
|
|
@@ -187,10 +191,10 @@ export class PulseMCPAdminClient {
|
|
|
187
191
|
return updateUnifiedMCPServer(this.apiKey, this.baseUrl, implementationId, params);
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
|
-
export function createMCPServer() {
|
|
194
|
+
export function createMCPServer(options) {
|
|
191
195
|
const server = new Server({
|
|
192
196
|
name: 'pulsemcp-cms-admin',
|
|
193
|
-
version:
|
|
197
|
+
version: options.version,
|
|
194
198
|
}, {
|
|
195
199
|
capabilities: {
|
|
196
200
|
tools: {},
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
// Parameter descriptions - single source of truth
|
|
3
3
|
const PARAM_DESCRIPTIONS = {
|
|
4
|
-
id: 'The ID of the MCP implementation to
|
|
5
|
-
name: '
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
id: 'The ID of the MCP implementation to update. Omit this field to CREATE a new implementation instead of updating an existing one.',
|
|
5
|
+
name: 'Name of the MCP implementation. Required when creating a new implementation.',
|
|
6
|
+
type: 'Implementation type - "server" for MCP servers, "client" for MCP clients. Required when creating a new implementation.',
|
|
7
|
+
short_description: 'Short description (brief summary)',
|
|
8
|
+
description: 'Full description (detailed documentation)',
|
|
9
9
|
status: 'Publication status - "draft", "live", or "archived"',
|
|
10
|
-
slug: '
|
|
11
|
-
url: '
|
|
12
|
-
provider_name: '
|
|
13
|
-
github_stars: '
|
|
10
|
+
slug: 'URL-friendly slug identifier',
|
|
11
|
+
url: 'URL to the implementation (GitHub repo, npm package, etc.)',
|
|
12
|
+
provider_name: 'Provider/author name',
|
|
13
|
+
github_stars: 'GitHub star count (integer, or null if unknown)',
|
|
14
14
|
classification: 'Implementation classification - "official", "community", or "reference"',
|
|
15
|
-
implementation_language: '
|
|
15
|
+
implementation_language: 'Programming language (e.g., "TypeScript", "Python", "Go")',
|
|
16
16
|
mcp_server_id: 'ID of associated MCP server record (null to unlink)',
|
|
17
17
|
mcp_client_id: 'ID of associated MCP client record (null to unlink)',
|
|
18
18
|
// Provider creation/linking
|
|
@@ -31,11 +31,11 @@ const PARAM_DESCRIPTIONS = {
|
|
|
31
31
|
internal_notes: 'Admin-only notes. Not displayed publicly. Used for tracking submission sources, reviewer comments, etc.',
|
|
32
32
|
};
|
|
33
33
|
const SaveMCPImplementationSchema = z.object({
|
|
34
|
-
id: z.number().describe(PARAM_DESCRIPTIONS.id),
|
|
34
|
+
id: z.number().optional().describe(PARAM_DESCRIPTIONS.id),
|
|
35
35
|
name: z.string().optional().describe(PARAM_DESCRIPTIONS.name),
|
|
36
|
+
type: z.enum(['server', 'client']).optional().describe(PARAM_DESCRIPTIONS.type),
|
|
36
37
|
short_description: z.string().optional().describe(PARAM_DESCRIPTIONS.short_description),
|
|
37
38
|
description: z.string().optional().describe(PARAM_DESCRIPTIONS.description),
|
|
38
|
-
type: z.enum(['server', 'client']).optional().describe(PARAM_DESCRIPTIONS.type),
|
|
39
39
|
status: z.enum(['draft', 'live', 'archived']).optional().describe(PARAM_DESCRIPTIONS.status),
|
|
40
40
|
slug: z.string().optional().describe(PARAM_DESCRIPTIONS.slug),
|
|
41
41
|
url: z.string().optional().describe(PARAM_DESCRIPTIONS.url),
|
|
@@ -94,11 +94,30 @@ const SaveMCPImplementationSchema = z.object({
|
|
|
94
94
|
export function saveMCPImplementation(_server, clientFactory) {
|
|
95
95
|
return {
|
|
96
96
|
name: 'save_mcp_implementation',
|
|
97
|
-
description: `
|
|
97
|
+
description: `Create or update an MCP implementation. This tool replicates the "Save Changes" functionality from the PulseMCP Admin panel.
|
|
98
|
+
|
|
99
|
+
**Creating a new implementation:**
|
|
100
|
+
- Omit the \`id\` field to create a new implementation
|
|
101
|
+
- Required fields for creation: \`name\`, \`type\` (either "server" or "client")
|
|
102
|
+
|
|
103
|
+
**Updating an existing implementation:**
|
|
104
|
+
- Provide the \`id\` field to update an existing implementation
|
|
105
|
+
- Only provided fields will be updated; omitted fields remain unchanged
|
|
106
|
+
|
|
107
|
+
All business logic from the Rails controller is applied (validation, associations, callbacks).
|
|
98
108
|
|
|
99
|
-
|
|
109
|
+
Example request (CREATE new implementation):
|
|
110
|
+
{
|
|
111
|
+
"name": "My New MCP Server",
|
|
112
|
+
"type": "server",
|
|
113
|
+
"short_description": "A new MCP server for doing cool things",
|
|
114
|
+
"classification": "community",
|
|
115
|
+
"implementation_language": "TypeScript",
|
|
116
|
+
"github_owner": "myorg",
|
|
117
|
+
"github_repo": "my-mcp-server"
|
|
118
|
+
}
|
|
100
119
|
|
|
101
|
-
Example request (
|
|
120
|
+
Example request (UPDATE existing implementation):
|
|
102
121
|
{
|
|
103
122
|
"id": 11371,
|
|
104
123
|
"name": "GitHub MCP Server",
|
|
@@ -125,30 +144,6 @@ Example request (with remote endpoints - new remote):
|
|
|
125
144
|
]
|
|
126
145
|
}
|
|
127
146
|
|
|
128
|
-
Example request (updating existing remote by ID):
|
|
129
|
-
{
|
|
130
|
-
"id": 11371,
|
|
131
|
-
"remote": [
|
|
132
|
-
{
|
|
133
|
-
"id": 123,
|
|
134
|
-
"url_direct": "https://updated-api.example.com/mcp",
|
|
135
|
-
"status": "beta"
|
|
136
|
-
}
|
|
137
|
-
]
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
Example request (with canonical URLs):
|
|
141
|
-
{
|
|
142
|
-
"id": 11371,
|
|
143
|
-
"canonical": [
|
|
144
|
-
{
|
|
145
|
-
"url": "https://github.com/owner/repo",
|
|
146
|
-
"scope": "url",
|
|
147
|
-
"note": "Official GitHub repository"
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
}
|
|
151
|
-
|
|
152
147
|
Example response:
|
|
153
148
|
{
|
|
154
149
|
"id": 11371,
|
|
@@ -161,15 +156,15 @@ Example response:
|
|
|
161
156
|
}
|
|
162
157
|
|
|
163
158
|
Important notes:
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
- The ID parameter is required to identify which implementation to update
|
|
159
|
+
- Omit \`id\` to CREATE, provide \`id\` to UPDATE
|
|
160
|
+
- When creating: \`name\` and \`type\` are required
|
|
161
|
+
- When updating: only provided fields will be changed
|
|
168
162
|
- Setting mcp_server_id or mcp_client_id to null will unlink the association
|
|
169
163
|
- Remote endpoints are for MCP servers only and configure how they can be accessed
|
|
170
164
|
- Canonical URLs help identify the authoritative source for the implementation
|
|
171
165
|
|
|
172
166
|
Use cases:
|
|
167
|
+
- Create new MCP implementation entries
|
|
173
168
|
- Update draft implementations before publishing
|
|
174
169
|
- Change implementation status (draft → live, live → archived)
|
|
175
170
|
- Update metadata (stars, language, classification)
|
|
@@ -307,69 +302,139 @@ Use cases:
|
|
|
307
302
|
description: PARAM_DESCRIPTIONS.internal_notes,
|
|
308
303
|
},
|
|
309
304
|
},
|
|
310
|
-
required: ['id'],
|
|
311
305
|
},
|
|
312
306
|
handler: async (args) => {
|
|
313
307
|
const validatedArgs = SaveMCPImplementationSchema.parse(args);
|
|
314
308
|
const client = clientFactory();
|
|
315
309
|
try {
|
|
316
|
-
const { id, ...
|
|
317
|
-
//
|
|
318
|
-
|
|
310
|
+
const { id, type, ...restParams } = validatedArgs;
|
|
311
|
+
// Determine if this is a create or update operation
|
|
312
|
+
const isCreate = id === undefined;
|
|
313
|
+
if (isCreate) {
|
|
314
|
+
// CREATE mode - validate required fields
|
|
315
|
+
if (!validatedArgs.name) {
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: 'text',
|
|
320
|
+
text: 'Error: "name" is required when creating a new MCP implementation (when "id" is omitted).',
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
isError: true,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
if (!type) {
|
|
327
|
+
return {
|
|
328
|
+
content: [
|
|
329
|
+
{
|
|
330
|
+
type: 'text',
|
|
331
|
+
text: 'Error: "type" is required when creating a new MCP implementation (when "id" is omitted). Use "server" or "client".',
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
isError: true,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
// Create new implementation
|
|
338
|
+
const createParams = {
|
|
339
|
+
name: validatedArgs.name,
|
|
340
|
+
type: type,
|
|
341
|
+
...restParams,
|
|
342
|
+
};
|
|
343
|
+
const implementation = await client.createMCPImplementation(createParams);
|
|
344
|
+
// Format the response for MCP
|
|
345
|
+
let content = `Successfully created new MCP implementation!\n\n`;
|
|
346
|
+
content += `**Name:** ${implementation.name}\n`;
|
|
347
|
+
content += `**ID:** ${implementation.id}\n`;
|
|
348
|
+
content += `**Slug:** ${implementation.slug}\n`;
|
|
349
|
+
content += `**Type:** ${implementation.type}\n`;
|
|
350
|
+
content += `**Status:** ${implementation.status}\n`;
|
|
351
|
+
if (implementation.classification) {
|
|
352
|
+
content += `**Classification:** ${implementation.classification}\n`;
|
|
353
|
+
}
|
|
354
|
+
if (implementation.implementation_language) {
|
|
355
|
+
content += `**Language:** ${implementation.implementation_language}\n`;
|
|
356
|
+
}
|
|
357
|
+
if (implementation.provider_name) {
|
|
358
|
+
content += `**Provider:** ${implementation.provider_name}\n`;
|
|
359
|
+
}
|
|
360
|
+
if (implementation.url) {
|
|
361
|
+
content += `**URL:** ${implementation.url}\n`;
|
|
362
|
+
}
|
|
363
|
+
if (implementation.created_at) {
|
|
364
|
+
content += `**Created:** ${new Date(implementation.created_at).toLocaleDateString()}\n`;
|
|
365
|
+
}
|
|
319
366
|
return {
|
|
320
367
|
content: [
|
|
321
368
|
{
|
|
322
369
|
type: 'text',
|
|
323
|
-
text:
|
|
370
|
+
text: content,
|
|
324
371
|
},
|
|
325
372
|
],
|
|
326
373
|
};
|
|
327
374
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
content += `**
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
content += `**
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
375
|
+
else {
|
|
376
|
+
// UPDATE mode
|
|
377
|
+
const params = { type, ...restParams };
|
|
378
|
+
// Only update if there are actual changes
|
|
379
|
+
if (Object.keys(params).filter((k) => params[k] !== undefined)
|
|
380
|
+
.length === 0) {
|
|
381
|
+
return {
|
|
382
|
+
content: [
|
|
383
|
+
{
|
|
384
|
+
type: 'text',
|
|
385
|
+
text: 'No changes provided. Please specify at least one field to update.',
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
const updateParams = params;
|
|
391
|
+
const implementation = await client.saveMCPImplementation(id, updateParams);
|
|
392
|
+
// Format the response for MCP
|
|
393
|
+
let content = `Successfully updated MCP implementation!\n\n`;
|
|
394
|
+
content += `**Name:** ${implementation.name}\n`;
|
|
395
|
+
content += `**ID:** ${implementation.id}\n`;
|
|
396
|
+
content += `**Slug:** ${implementation.slug}\n`;
|
|
397
|
+
content += `**Type:** ${implementation.type}\n`;
|
|
398
|
+
content += `**Status:** ${implementation.status}\n`;
|
|
399
|
+
if (implementation.classification) {
|
|
400
|
+
content += `**Classification:** ${implementation.classification}\n`;
|
|
401
|
+
}
|
|
402
|
+
if (implementation.implementation_language) {
|
|
403
|
+
content += `**Language:** ${implementation.implementation_language}\n`;
|
|
404
|
+
}
|
|
405
|
+
if (implementation.provider_name) {
|
|
406
|
+
content += `**Provider:** ${implementation.provider_name}\n`;
|
|
407
|
+
}
|
|
408
|
+
if (implementation.url) {
|
|
409
|
+
content += `**URL:** ${implementation.url}\n`;
|
|
410
|
+
}
|
|
411
|
+
if (implementation.github_stars !== undefined) {
|
|
412
|
+
content += `**GitHub Stars:** ${implementation.github_stars}\n`;
|
|
413
|
+
}
|
|
414
|
+
if (implementation.mcp_server_id) {
|
|
415
|
+
content += `**Linked MCP Server ID:** ${implementation.mcp_server_id}\n`;
|
|
416
|
+
}
|
|
417
|
+
if (implementation.mcp_client_id) {
|
|
418
|
+
content += `**Linked MCP Client ID:** ${implementation.mcp_client_id}\n`;
|
|
419
|
+
}
|
|
420
|
+
if (implementation.updated_at) {
|
|
421
|
+
content += `**Updated:** ${new Date(implementation.updated_at).toLocaleDateString()}\n`;
|
|
422
|
+
}
|
|
423
|
+
content += `\n**Fields updated:**\n`;
|
|
424
|
+
Object.keys(params)
|
|
425
|
+
.filter((k) => params[k] !== undefined)
|
|
426
|
+
.forEach((field) => {
|
|
427
|
+
content += `- ${field}\n`;
|
|
428
|
+
});
|
|
429
|
+
return {
|
|
430
|
+
content: [
|
|
431
|
+
{
|
|
432
|
+
type: 'text',
|
|
433
|
+
text: content,
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
};
|
|
360
437
|
}
|
|
361
|
-
content += `\n**Fields updated:**\n`;
|
|
362
|
-
Object.keys(params).forEach((field) => {
|
|
363
|
-
content += `- ${field}\n`;
|
|
364
|
-
});
|
|
365
|
-
return {
|
|
366
|
-
content: [
|
|
367
|
-
{
|
|
368
|
-
type: 'text',
|
|
369
|
-
text: content,
|
|
370
|
-
},
|
|
371
|
-
],
|
|
372
|
-
};
|
|
373
438
|
}
|
|
374
439
|
catch (error) {
|
|
375
440
|
return {
|
package/package.json
CHANGED
package/shared/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { registerTools, createRegisterTools } from './tools.js';
|
|
2
|
-
export { createMCPServer, type ClientFactory, type IPulseMCPAdminClient, PulseMCPAdminClient, } from './server.js';
|
|
2
|
+
export { createMCPServer, type CreateMCPServerOptions, type ClientFactory, type IPulseMCPAdminClient, PulseMCPAdminClient, } from './server.js';
|
|
3
3
|
export type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, } from './types.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MCPImplementation, CreateMCPImplementationParams } from '../../types.js';
|
|
2
|
+
export declare function createMCPImplementation(apiKey: string, baseUrl: string, params: CreateMCPImplementationParams): Promise<MCPImplementation>;
|
|
3
|
+
//# sourceMappingURL=create-mcp-implementation.d.ts.map
|