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.
Files changed (69) hide show
  1. package/README.md +169 -0
  2. package/build/index.js +48 -0
  3. package/build/shared/src/index.js +2 -0
  4. package/build/shared/src/logging.js +34 -0
  5. package/build/shared/src/pulsemcp-admin-client/lib/create-post.js +69 -0
  6. package/build/shared/src/pulsemcp-admin-client/lib/get-author-by-slug.js +26 -0
  7. package/build/shared/src/pulsemcp-admin-client/lib/get-authors.js +38 -0
  8. package/build/shared/src/pulsemcp-admin-client/lib/get-mcp-client-by-slug.js +26 -0
  9. package/build/shared/src/pulsemcp-admin-client/lib/get-mcp-server-by-slug.js +26 -0
  10. package/build/shared/src/pulsemcp-admin-client/lib/get-post.js +26 -0
  11. package/build/shared/src/pulsemcp-admin-client/lib/get-posts.js +62 -0
  12. package/build/shared/src/pulsemcp-admin-client/lib/update-post.js +75 -0
  13. package/build/shared/src/pulsemcp-admin-client/lib/upload-image.js +36 -0
  14. package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +158 -0
  15. package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.js +1 -0
  16. package/build/shared/src/server.js +71 -0
  17. package/build/shared/src/tools/draft-newsletter-post.js +225 -0
  18. package/build/shared/src/tools/get-authors.js +115 -0
  19. package/build/shared/src/tools/get-newsletter-post.js +127 -0
  20. package/build/shared/src/tools/get-newsletter-posts.js +127 -0
  21. package/build/shared/src/tools/update-newsletter-post.js +232 -0
  22. package/build/shared/src/tools/upload-image.js +106 -0
  23. package/build/shared/src/tools.js +58 -0
  24. package/build/shared/src/types.js +1 -0
  25. package/package.json +43 -0
  26. package/shared/index.d.ts +4 -0
  27. package/shared/index.js +2 -0
  28. package/shared/logging.d.ts +20 -0
  29. package/shared/logging.js +34 -0
  30. package/shared/pulsemcp-admin-client/lib/create-post.d.ts +3 -0
  31. package/shared/pulsemcp-admin-client/lib/create-post.js +69 -0
  32. package/shared/pulsemcp-admin-client/lib/get-author-by-slug.d.ts +3 -0
  33. package/shared/pulsemcp-admin-client/lib/get-author-by-slug.js +26 -0
  34. package/shared/pulsemcp-admin-client/lib/get-authors.d.ts +6 -0
  35. package/shared/pulsemcp-admin-client/lib/get-authors.js +38 -0
  36. package/shared/pulsemcp-admin-client/lib/get-mcp-client-by-slug.d.ts +3 -0
  37. package/shared/pulsemcp-admin-client/lib/get-mcp-client-by-slug.js +26 -0
  38. package/shared/pulsemcp-admin-client/lib/get-mcp-server-by-slug.d.ts +3 -0
  39. package/shared/pulsemcp-admin-client/lib/get-mcp-server-by-slug.js +26 -0
  40. package/shared/pulsemcp-admin-client/lib/get-post.d.ts +3 -0
  41. package/shared/pulsemcp-admin-client/lib/get-post.js +26 -0
  42. package/shared/pulsemcp-admin-client/lib/get-posts.d.ts +8 -0
  43. package/shared/pulsemcp-admin-client/lib/get-posts.js +62 -0
  44. package/shared/pulsemcp-admin-client/lib/update-post.d.ts +3 -0
  45. package/shared/pulsemcp-admin-client/lib/update-post.js +75 -0
  46. package/shared/pulsemcp-admin-client/lib/upload-image.d.ts +3 -0
  47. package/shared/pulsemcp-admin-client/lib/upload-image.js +36 -0
  48. package/shared/pulsemcp-admin-client/pulsemcp-admin-client.d.ts +3 -0
  49. package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.d.ts +27 -0
  50. package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +158 -0
  51. package/shared/pulsemcp-admin-client/pulsemcp-admin-client.js +1 -0
  52. package/shared/server.d.ts +71 -0
  53. package/shared/server.js +71 -0
  54. package/shared/tools/draft-newsletter-post.d.ts +97 -0
  55. package/shared/tools/draft-newsletter-post.js +225 -0
  56. package/shared/tools/get-authors.d.ts +34 -0
  57. package/shared/tools/get-authors.js +115 -0
  58. package/shared/tools/get-newsletter-post.d.ts +30 -0
  59. package/shared/tools/get-newsletter-post.js +127 -0
  60. package/shared/tools/get-newsletter-posts.d.ts +42 -0
  61. package/shared/tools/get-newsletter-posts.js +127 -0
  62. package/shared/tools/update-newsletter-post.d.ts +92 -0
  63. package/shared/tools/update-newsletter-post.js +232 -0
  64. package/shared/tools/upload-image.d.ts +38 -0
  65. package/shared/tools/upload-image.js +106 -0
  66. package/shared/tools.d.ts +15 -0
  67. package/shared/tools.js +58 -0
  68. package/shared/types.d.ts +100 -0
  69. 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
@@ -0,0 +1,2 @@
1
+ export { registerTools, createRegisterTools } from './tools.js';
2
+ export { createMCPServer, PulseMCPAdminClient, } from './server.js';
@@ -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,3 @@
1
+ import type { Post, CreatePostParams } from '../../types.js';
2
+ export declare function createPost(apiKey: string, baseUrl: string, params: CreatePostParams): Promise<Post>;
3
+ //# sourceMappingURL=create-post.d.ts.map
@@ -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,3 @@
1
+ import type { Author } from '../../types.js';
2
+ export declare function getAuthorBySlug(apiKey: string, baseUrl: string, slug: string): Promise<Author>;
3
+ //# sourceMappingURL=get-author-by-slug.d.ts.map
@@ -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,6 @@
1
+ import type { AuthorsResponse } from '../../types.js';
2
+ export declare function getAuthors(apiKey: string, baseUrl: string, params?: {
3
+ search?: string;
4
+ page?: number;
5
+ }): Promise<AuthorsResponse>;
6
+ //# sourceMappingURL=get-authors.d.ts.map
@@ -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,3 @@
1
+ import type { MCPClient } from '../../types.js';
2
+ export declare function getMCPClientBySlug(apiKey: string, baseUrl: string, slug: string): Promise<MCPClient>;
3
+ //# sourceMappingURL=get-mcp-client-by-slug.d.ts.map
@@ -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,3 @@
1
+ import type { MCPServer } from '../../types.js';
2
+ export declare function getMCPServerBySlug(apiKey: string, baseUrl: string, slug: string): Promise<MCPServer>;
3
+ //# sourceMappingURL=get-mcp-server-by-slug.d.ts.map
@@ -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,3 @@
1
+ import type { Post } from '../../types.js';
2
+ export declare function getPost(apiKey: string, baseUrl: string, slug: string): Promise<Post>;
3
+ //# sourceMappingURL=get-post.d.ts.map
@@ -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,3 @@
1
+ import type { Post, UpdatePostParams } from '../../types.js';
2
+ export declare function updatePost(apiKey: string, baseUrl: string, slug: string, params: UpdatePostParams): Promise<Post>;
3
+ //# sourceMappingURL=update-post.d.ts.map
@@ -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,3 @@
1
+ import type { ImageUploadResponse } from '../../types.js';
2
+ export declare function uploadImage(apiKey: string, baseUrl: string, postSlug: string, fileName: string, fileData: Buffer): Promise<ImageUploadResponse>;
3
+ //# sourceMappingURL=upload-image.d.ts.map
@@ -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,3 @@
1
+ export type { IPulseMCPAdminClient } from '../server.js';
2
+ export type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, } from '../types.js';
3
+ //# sourceMappingURL=pulsemcp-admin-client.d.ts.map
@@ -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