pulsemcp-cms-admin-mcp-server 0.0.2
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 +169 -0
- package/build/index.js +48 -0
- package/build/shared/src/index.js +2 -0
- package/build/shared/src/logging.js +34 -0
- package/build/shared/src/pulsemcp-admin-client/lib/create-post.js +69 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-author-by-slug.js +26 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-authors.js +38 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-mcp-client-by-slug.js +26 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-mcp-server-by-slug.js +26 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-post.js +26 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-posts.js +62 -0
- package/build/shared/src/pulsemcp-admin-client/lib/update-post.js +75 -0
- package/build/shared/src/pulsemcp-admin-client/lib/upload-image.js +36 -0
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +158 -0
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.js +1 -0
- package/build/shared/src/server.js +71 -0
- package/build/shared/src/tools/draft-newsletter-post.js +225 -0
- package/build/shared/src/tools/get-authors.js +115 -0
- package/build/shared/src/tools/get-newsletter-post.js +127 -0
- package/build/shared/src/tools/get-newsletter-posts.js +127 -0
- package/build/shared/src/tools/update-newsletter-post.js +232 -0
- package/build/shared/src/tools/upload-image.js +106 -0
- package/build/shared/src/tools.js +58 -0
- package/build/shared/src/types.js +1 -0
- package/package.json +43 -0
- package/shared/index.d.ts +4 -0
- package/shared/index.js +2 -0
- package/shared/logging.d.ts +20 -0
- package/shared/logging.js +34 -0
- package/shared/pulsemcp-admin-client/lib/create-post.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/create-post.js +69 -0
- package/shared/pulsemcp-admin-client/lib/get-author-by-slug.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/get-author-by-slug.js +26 -0
- package/shared/pulsemcp-admin-client/lib/get-authors.d.ts +6 -0
- package/shared/pulsemcp-admin-client/lib/get-authors.js +38 -0
- package/shared/pulsemcp-admin-client/lib/get-mcp-client-by-slug.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/get-mcp-client-by-slug.js +26 -0
- package/shared/pulsemcp-admin-client/lib/get-mcp-server-by-slug.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/get-mcp-server-by-slug.js +26 -0
- package/shared/pulsemcp-admin-client/lib/get-post.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/get-post.js +26 -0
- package/shared/pulsemcp-admin-client/lib/get-posts.d.ts +8 -0
- package/shared/pulsemcp-admin-client/lib/get-posts.js +62 -0
- package/shared/pulsemcp-admin-client/lib/update-post.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/update-post.js +75 -0
- package/shared/pulsemcp-admin-client/lib/upload-image.d.ts +3 -0
- package/shared/pulsemcp-admin-client/lib/upload-image.js +36 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.d.ts +3 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.d.ts +27 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +158 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.js +1 -0
- package/shared/server.d.ts +71 -0
- package/shared/server.js +71 -0
- package/shared/tools/draft-newsletter-post.d.ts +97 -0
- package/shared/tools/draft-newsletter-post.js +225 -0
- package/shared/tools/get-authors.d.ts +34 -0
- package/shared/tools/get-authors.js +115 -0
- package/shared/tools/get-newsletter-post.d.ts +30 -0
- package/shared/tools/get-newsletter-post.js +127 -0
- package/shared/tools/get-newsletter-posts.d.ts +42 -0
- package/shared/tools/get-newsletter-posts.js +127 -0
- package/shared/tools/update-newsletter-post.d.ts +92 -0
- package/shared/tools/update-newsletter-post.js +232 -0
- package/shared/tools/upload-image.d.ts +38 -0
- package/shared/tools/upload-image.js +106 -0
- package/shared/tools.d.ts +15 -0
- package/shared/tools.js +58 -0
- package/shared/types.d.ts +100 -0
- package/shared/types.js +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pulsemcp-cms-admin-mcp-server",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Local implementation of PulseMCP CMS Admin MCP server",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pulsemcp-cms-admin-mcp-server": "./build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build/**/*.js",
|
|
12
|
+
"build/**/*.d.ts",
|
|
13
|
+
"shared/**/*.js",
|
|
14
|
+
"shared/**/*.d.ts",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc && npm run build:integration",
|
|
19
|
+
"build:integration": "tsc -p tsconfig.integration.json",
|
|
20
|
+
"start": "node build/index.js",
|
|
21
|
+
"dev": "tsx src/index.ts",
|
|
22
|
+
"predev": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
|
|
23
|
+
"prebuild": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
|
|
24
|
+
"prepublishOnly": "node prepare-publish.js && node ../scripts/prepare-npm-readme.js",
|
|
25
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
26
|
+
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"format:check": "prettier --check .",
|
|
29
|
+
"stage-publish": "npm version"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
33
|
+
"zod": "^3.24.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.10.6",
|
|
37
|
+
"tsx": "^4.19.4",
|
|
38
|
+
"typescript": "^5.7.3"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [],
|
|
41
|
+
"author": "PulseMCP",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { registerTools, createRegisterTools } from './tools.js';
|
|
2
|
+
export { createMCPServer, type ClientFactory, type IPulseMCPAdminClient, PulseMCPAdminClient, } from './server.js';
|
|
3
|
+
export type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
package/shared/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for consistent output across MCP servers
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Log server startup message
|
|
6
|
+
*/
|
|
7
|
+
export declare function logServerStart(serverName: string, transport?: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Log an error with context
|
|
10
|
+
*/
|
|
11
|
+
export declare function logError(context: string, error: unknown): void;
|
|
12
|
+
/**
|
|
13
|
+
* Log a warning
|
|
14
|
+
*/
|
|
15
|
+
export declare function logWarning(context: string, message: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Log debug information (only in development)
|
|
18
|
+
*/
|
|
19
|
+
export declare function logDebug(context: string, message: string): void;
|
|
20
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for consistent output across MCP servers
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Log server startup message
|
|
6
|
+
*/
|
|
7
|
+
export function logServerStart(serverName, transport = 'stdio') {
|
|
8
|
+
console.error(`MCP server ${serverName} running on ${transport}`);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Log an error with context
|
|
12
|
+
*/
|
|
13
|
+
export function logError(context, error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
16
|
+
console.error(`[ERROR] ${context}: ${message}`);
|
|
17
|
+
if (stack) {
|
|
18
|
+
console.error(stack);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Log a warning
|
|
23
|
+
*/
|
|
24
|
+
export function logWarning(context, message) {
|
|
25
|
+
console.error(`[WARN] ${context}: ${message}`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Log debug information (only in development)
|
|
29
|
+
*/
|
|
30
|
+
export function logDebug(context, message) {
|
|
31
|
+
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
|
32
|
+
console.error(`[DEBUG] ${context}: ${message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export async function createPost(apiKey, baseUrl, params) {
|
|
2
|
+
const url = new URL('/posts', baseUrl);
|
|
3
|
+
// Build form data for the POST request
|
|
4
|
+
const formData = new URLSearchParams();
|
|
5
|
+
// Required fields
|
|
6
|
+
formData.append('post[title]', params.title);
|
|
7
|
+
formData.append('post[body]', params.body);
|
|
8
|
+
formData.append('post[slug]', params.slug);
|
|
9
|
+
formData.append('post[author_id]', params.author_id.toString());
|
|
10
|
+
// Optional fields
|
|
11
|
+
if (params.status)
|
|
12
|
+
formData.append('post[status]', params.status);
|
|
13
|
+
if (params.category)
|
|
14
|
+
formData.append('post[category]', params.category);
|
|
15
|
+
if (params.image_url)
|
|
16
|
+
formData.append('post[image_url]', params.image_url);
|
|
17
|
+
if (params.preview_image_url)
|
|
18
|
+
formData.append('post[preview_image_url]', params.preview_image_url);
|
|
19
|
+
if (params.share_image)
|
|
20
|
+
formData.append('post[share_image]', params.share_image);
|
|
21
|
+
if (params.title_tag)
|
|
22
|
+
formData.append('post[title_tag]', params.title_tag);
|
|
23
|
+
if (params.short_title)
|
|
24
|
+
formData.append('post[short_title]', params.short_title);
|
|
25
|
+
if (params.short_description)
|
|
26
|
+
formData.append('post[short_description]', params.short_description);
|
|
27
|
+
if (params.description_tag)
|
|
28
|
+
formData.append('post[description_tag]', params.description_tag);
|
|
29
|
+
if (params.last_updated)
|
|
30
|
+
formData.append('post[last_updated]', params.last_updated);
|
|
31
|
+
if (params.table_of_contents)
|
|
32
|
+
formData.append('post[table_of_contents]', JSON.stringify(params.table_of_contents));
|
|
33
|
+
// Handle arrays for featured servers/clients
|
|
34
|
+
if (params.featured_mcp_server_ids) {
|
|
35
|
+
params.featured_mcp_server_ids.forEach((id) => {
|
|
36
|
+
formData.append('post[featured_mcp_server_ids][]', id.toString());
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (params.featured_mcp_client_ids) {
|
|
40
|
+
params.featured_mcp_client_ids.forEach((id) => {
|
|
41
|
+
formData.append('post[featured_mcp_client_ids][]', id.toString());
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const response = await fetch(url.toString(), {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'X-API-Key': apiKey,
|
|
48
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
49
|
+
Accept: 'application/json',
|
|
50
|
+
},
|
|
51
|
+
body: formData.toString(),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
if (response.status === 401) {
|
|
55
|
+
throw new Error('Invalid API key');
|
|
56
|
+
}
|
|
57
|
+
if (response.status === 403) {
|
|
58
|
+
throw new Error('User lacks admin privileges');
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 422) {
|
|
61
|
+
const errorData = (await response.json());
|
|
62
|
+
const errors = errorData.errors || ['Validation failed'];
|
|
63
|
+
throw new Error(`Validation failed: ${errors.join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Failed to create post: ${response.status} ${response.statusText}`);
|
|
66
|
+
}
|
|
67
|
+
const data = await response.json();
|
|
68
|
+
return data;
|
|
69
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function getAuthorBySlug(apiKey, baseUrl, slug) {
|
|
2
|
+
// Use the supervisor endpoint which supports JSON
|
|
3
|
+
const url = new URL(`/supervisor/authors/${slug}`, baseUrl);
|
|
4
|
+
const response = await fetch(url.toString(), {
|
|
5
|
+
method: 'GET',
|
|
6
|
+
headers: {
|
|
7
|
+
'X-API-Key': apiKey,
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
if (response.status === 401) {
|
|
13
|
+
throw new Error('Invalid API key');
|
|
14
|
+
}
|
|
15
|
+
if (response.status === 403) {
|
|
16
|
+
throw new Error('User lacks admin privileges');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 404) {
|
|
19
|
+
throw new Error(`Author not found: ${slug}`);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Failed to fetch author: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
// The supervisor endpoint returns the author object directly
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export async function getAuthors(apiKey, baseUrl, params) {
|
|
2
|
+
// Use the supervisor endpoint which supports JSON
|
|
3
|
+
const url = new URL('/supervisor/authors', baseUrl);
|
|
4
|
+
// Add query parameters if provided
|
|
5
|
+
if (params?.search) {
|
|
6
|
+
url.searchParams.append('search', params.search);
|
|
7
|
+
}
|
|
8
|
+
if (params?.page) {
|
|
9
|
+
url.searchParams.append('page', params.page.toString());
|
|
10
|
+
}
|
|
11
|
+
const response = await fetch(url.toString(), {
|
|
12
|
+
method: 'GET',
|
|
13
|
+
headers: {
|
|
14
|
+
'X-API-Key': apiKey,
|
|
15
|
+
Accept: 'application/json',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
if (response.status === 401) {
|
|
20
|
+
throw new Error('Invalid API key');
|
|
21
|
+
}
|
|
22
|
+
if (response.status === 403) {
|
|
23
|
+
throw new Error('User lacks admin privileges');
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`Failed to fetch authors: ${response.status} ${response.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const data = (await response.json());
|
|
28
|
+
return {
|
|
29
|
+
authors: data.data || [],
|
|
30
|
+
pagination: data.meta
|
|
31
|
+
? {
|
|
32
|
+
current_page: data.meta.current_page,
|
|
33
|
+
total_pages: data.meta.total_pages,
|
|
34
|
+
total_count: data.meta.total_count,
|
|
35
|
+
}
|
|
36
|
+
: undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function getMCPClientBySlug(apiKey, baseUrl, slug) {
|
|
2
|
+
// Use the supervisor endpoint which supports JSON
|
|
3
|
+
const url = new URL(`/supervisor/mcp_clients/${slug}`, baseUrl);
|
|
4
|
+
const response = await fetch(url.toString(), {
|
|
5
|
+
method: 'GET',
|
|
6
|
+
headers: {
|
|
7
|
+
'X-API-Key': apiKey,
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
if (response.status === 401) {
|
|
13
|
+
throw new Error('Invalid API key');
|
|
14
|
+
}
|
|
15
|
+
if (response.status === 403) {
|
|
16
|
+
throw new Error('User lacks admin privileges');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 404) {
|
|
19
|
+
throw new Error(`MCP client not found: ${slug}`);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Failed to fetch MCP client: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
// The supervisor endpoint returns the MCP client object directly
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function getMCPServerBySlug(apiKey, baseUrl, slug) {
|
|
2
|
+
// Use the supervisor endpoint which supports JSON
|
|
3
|
+
const url = new URL(`/supervisor/mcp_servers/${slug}`, baseUrl);
|
|
4
|
+
const response = await fetch(url.toString(), {
|
|
5
|
+
method: 'GET',
|
|
6
|
+
headers: {
|
|
7
|
+
'X-API-Key': apiKey,
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
if (response.status === 401) {
|
|
13
|
+
throw new Error('Invalid API key');
|
|
14
|
+
}
|
|
15
|
+
if (response.status === 403) {
|
|
16
|
+
throw new Error('User lacks admin privileges');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 404) {
|
|
19
|
+
throw new Error(`MCP server not found: ${slug}`);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Failed to fetch MCP server: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
// The supervisor endpoint returns the MCP server object directly
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function getPost(apiKey, baseUrl, slug) {
|
|
2
|
+
// Use the supervisor endpoint which supports JSON and returns full post data including body
|
|
3
|
+
const url = new URL(`/supervisor/posts/${slug}`, baseUrl);
|
|
4
|
+
const response = await fetch(url.toString(), {
|
|
5
|
+
method: 'GET',
|
|
6
|
+
headers: {
|
|
7
|
+
'X-API-Key': apiKey,
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
if (response.status === 401) {
|
|
13
|
+
throw new Error('Invalid API key');
|
|
14
|
+
}
|
|
15
|
+
if (response.status === 403) {
|
|
16
|
+
throw new Error('User lacks admin privileges');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 404) {
|
|
19
|
+
throw new Error(`Post not found: ${slug}`);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Failed to fetch post: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
// The supervisor endpoint returns the full post object with all fields including body
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PostsResponse } from '../../types.js';
|
|
2
|
+
export declare function getPosts(apiKey: string, baseUrl: string, params?: {
|
|
3
|
+
search?: string;
|
|
4
|
+
sort?: string;
|
|
5
|
+
direction?: 'asc' | 'desc';
|
|
6
|
+
page?: number;
|
|
7
|
+
}): Promise<PostsResponse>;
|
|
8
|
+
//# sourceMappingURL=get-posts.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export async function getPosts(apiKey, baseUrl, params) {
|
|
2
|
+
const url = new URL('/posts', baseUrl);
|
|
3
|
+
// Add query parameters if provided
|
|
4
|
+
if (params?.search) {
|
|
5
|
+
url.searchParams.append('search', params.search);
|
|
6
|
+
}
|
|
7
|
+
if (params?.sort) {
|
|
8
|
+
url.searchParams.append('sort', params.sort);
|
|
9
|
+
}
|
|
10
|
+
if (params?.direction) {
|
|
11
|
+
url.searchParams.append('direction', params.direction);
|
|
12
|
+
}
|
|
13
|
+
if (params?.page) {
|
|
14
|
+
url.searchParams.append('page', params.page.toString());
|
|
15
|
+
}
|
|
16
|
+
const response = await fetch(url.toString(), {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'X-API-Key': apiKey,
|
|
20
|
+
Accept: 'application/json',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
if (response.status === 401) {
|
|
25
|
+
throw new Error('Invalid API key');
|
|
26
|
+
}
|
|
27
|
+
if (response.status === 403) {
|
|
28
|
+
throw new Error('User lacks admin privileges');
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Failed to fetch posts: ${response.status} ${response.statusText}`);
|
|
31
|
+
}
|
|
32
|
+
// Parse the JSON response
|
|
33
|
+
const data = (await response.json());
|
|
34
|
+
// Handle the Rails JSON structure with data and meta
|
|
35
|
+
if (data.data && data.meta) {
|
|
36
|
+
return {
|
|
37
|
+
posts: data.data.map((post) => ({
|
|
38
|
+
id: post.id,
|
|
39
|
+
slug: post.slug,
|
|
40
|
+
title: post.title,
|
|
41
|
+
short_title: post.short_title,
|
|
42
|
+
short_description: post.short_description,
|
|
43
|
+
category: post.category,
|
|
44
|
+
status: post.status,
|
|
45
|
+
author_id: post.author_id,
|
|
46
|
+
created_at: post.created_at,
|
|
47
|
+
updated_at: post.updated_at,
|
|
48
|
+
last_updated: post.last_updated,
|
|
49
|
+
})),
|
|
50
|
+
pagination: {
|
|
51
|
+
current_page: data.meta.current_page,
|
|
52
|
+
total_pages: data.meta.total_pages,
|
|
53
|
+
total_count: data.meta.total_count,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Fallback for unexpected response format
|
|
58
|
+
return {
|
|
59
|
+
posts: [],
|
|
60
|
+
pagination: undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export async function updatePost(apiKey, baseUrl, slug, params) {
|
|
2
|
+
const url = new URL(`/posts/${slug}`, baseUrl);
|
|
3
|
+
// Build form data for the PUT request
|
|
4
|
+
const formData = new URLSearchParams();
|
|
5
|
+
// Add all provided fields
|
|
6
|
+
if (params.title !== undefined)
|
|
7
|
+
formData.append('post[title]', params.title);
|
|
8
|
+
if (params.body !== undefined)
|
|
9
|
+
formData.append('post[body]', params.body);
|
|
10
|
+
if (params.slug !== undefined)
|
|
11
|
+
formData.append('post[slug]', params.slug);
|
|
12
|
+
if (params.author_id !== undefined)
|
|
13
|
+
formData.append('post[author_id]', params.author_id.toString());
|
|
14
|
+
if (params.status !== undefined)
|
|
15
|
+
formData.append('post[status]', params.status);
|
|
16
|
+
if (params.category !== undefined)
|
|
17
|
+
formData.append('post[category]', params.category);
|
|
18
|
+
if (params.image_url !== undefined)
|
|
19
|
+
formData.append('post[image_url]', params.image_url);
|
|
20
|
+
if (params.preview_image_url !== undefined)
|
|
21
|
+
formData.append('post[preview_image_url]', params.preview_image_url);
|
|
22
|
+
if (params.share_image !== undefined)
|
|
23
|
+
formData.append('post[share_image]', params.share_image);
|
|
24
|
+
if (params.title_tag !== undefined)
|
|
25
|
+
formData.append('post[title_tag]', params.title_tag);
|
|
26
|
+
if (params.short_title !== undefined)
|
|
27
|
+
formData.append('post[short_title]', params.short_title);
|
|
28
|
+
if (params.short_description !== undefined)
|
|
29
|
+
formData.append('post[short_description]', params.short_description);
|
|
30
|
+
if (params.description_tag !== undefined)
|
|
31
|
+
formData.append('post[description_tag]', params.description_tag);
|
|
32
|
+
if (params.last_updated !== undefined)
|
|
33
|
+
formData.append('post[last_updated]', params.last_updated);
|
|
34
|
+
if (params.table_of_contents !== undefined)
|
|
35
|
+
formData.append('post[table_of_contents]', JSON.stringify(params.table_of_contents));
|
|
36
|
+
// Handle arrays for featured servers/clients
|
|
37
|
+
if (params.featured_mcp_server_ids !== undefined) {
|
|
38
|
+
params.featured_mcp_server_ids.forEach((id) => {
|
|
39
|
+
formData.append('post[featured_mcp_server_ids][]', id.toString());
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (params.featured_mcp_client_ids !== undefined) {
|
|
43
|
+
params.featured_mcp_client_ids.forEach((id) => {
|
|
44
|
+
formData.append('post[featured_mcp_client_ids][]', id.toString());
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const response = await fetch(url.toString(), {
|
|
48
|
+
method: 'PUT',
|
|
49
|
+
headers: {
|
|
50
|
+
'X-API-Key': apiKey,
|
|
51
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
52
|
+
Accept: 'application/json',
|
|
53
|
+
},
|
|
54
|
+
body: formData.toString(),
|
|
55
|
+
});
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
if (response.status === 401) {
|
|
58
|
+
throw new Error('Invalid API key');
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 403) {
|
|
61
|
+
throw new Error('User lacks admin privileges');
|
|
62
|
+
}
|
|
63
|
+
if (response.status === 404) {
|
|
64
|
+
throw new Error(`Post not found: ${slug}`);
|
|
65
|
+
}
|
|
66
|
+
if (response.status === 422) {
|
|
67
|
+
const errorData = (await response.json());
|
|
68
|
+
const errors = errorData.errors || ['Validation failed'];
|
|
69
|
+
throw new Error(`Validation failed: ${errors.join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Failed to update post: ${response.status} ${response.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export async function uploadImage(apiKey, baseUrl, postSlug, fileName, fileData) {
|
|
2
|
+
const url = new URL('/upload_image', baseUrl);
|
|
3
|
+
// Create form data for multipart upload
|
|
4
|
+
const formData = new FormData();
|
|
5
|
+
// Create a blob from the buffer
|
|
6
|
+
const blob = new Blob([fileData], { type: 'image/png' }); // Default to PNG, adjust as needed
|
|
7
|
+
// Add file to form data
|
|
8
|
+
formData.append('file', blob, fileName);
|
|
9
|
+
// Add folder path that includes the post slug
|
|
10
|
+
formData.append('folder', `newsletter/${postSlug}`);
|
|
11
|
+
// Add the full filepath
|
|
12
|
+
formData.append('filepath', `newsletter/${postSlug}/${fileName}`);
|
|
13
|
+
const response = await fetch(url.toString(), {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {
|
|
16
|
+
'X-API-Key': apiKey,
|
|
17
|
+
// Don't set Content-Type for FormData - let the browser set it with boundary
|
|
18
|
+
},
|
|
19
|
+
body: formData,
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
if (response.status === 401) {
|
|
23
|
+
throw new Error('Invalid API key');
|
|
24
|
+
}
|
|
25
|
+
if (response.status === 403) {
|
|
26
|
+
throw new Error('User lacks admin privileges');
|
|
27
|
+
}
|
|
28
|
+
if (response.status === 422) {
|
|
29
|
+
const errorData = await response.text();
|
|
30
|
+
throw new Error(`Validation failed: ${errorData}`);
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Failed to upload image: ${response.status} ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
const data = (await response.json());
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IPulseMCPAdminClient } from '../server.js';
|
|
2
|
+
import type { Post, ImageUploadResponse, Author, MCPServer, MCPClient } from '../types.js';
|
|
3
|
+
interface MockData {
|
|
4
|
+
posts?: Post[];
|
|
5
|
+
postsBySlug?: Record<string, Post>;
|
|
6
|
+
createPostResponse?: Post;
|
|
7
|
+
updatePostResponse?: Post;
|
|
8
|
+
uploadImageResponse?: ImageUploadResponse;
|
|
9
|
+
authors?: Author[];
|
|
10
|
+
authorsBySlug?: Record<string, Author>;
|
|
11
|
+
mcpServersBySlug?: Record<string, MCPServer>;
|
|
12
|
+
mcpClientsBySlug?: Record<string, MCPClient>;
|
|
13
|
+
errors?: {
|
|
14
|
+
getPosts?: Error;
|
|
15
|
+
getPost?: Error;
|
|
16
|
+
createPost?: Error;
|
|
17
|
+
updatePost?: Error;
|
|
18
|
+
uploadImage?: Error;
|
|
19
|
+
getAuthors?: Error;
|
|
20
|
+
getAuthorBySlug?: Error;
|
|
21
|
+
getMCPServerBySlug?: Error;
|
|
22
|
+
getMCPClientBySlug?: Error;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare function createMockPulseMCPAdminClient(mockData: MockData): IPulseMCPAdminClient;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=pulsemcp-admin-client.integration-mock.d.ts.map
|