pulsemcp-cms-admin-mcp-server 0.9.10 → 0.9.12
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 +24 -7
- package/build/shared/src/exam-result-store.js +2 -1
- package/build/shared/src/pulsemcp-admin-client/lib/get-moz-backlinks.js +39 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-moz-metrics.js +36 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-moz-stored-metrics.js +39 -0
- package/build/shared/src/pulsemcp-admin-client/lib/upload-image.js +1 -1
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +26 -0
- package/build/shared/src/server.js +13 -0
- package/build/shared/src/tools/get-exam-result.js +9 -2
- package/build/shared/src/tools/get-moz-backlinks.js +102 -0
- package/build/shared/src/tools/get-moz-metrics.js +95 -0
- package/build/shared/src/tools/get-moz-stored-metrics.js +119 -0
- package/build/shared/src/tools/run-exam-for-mirror.js +7 -5
- package/build/shared/src/tools.js +13 -0
- package/package.json +1 -1
- package/shared/exam-result-store.js +2 -1
- package/shared/pulsemcp-admin-client/lib/get-moz-backlinks.d.ts +7 -0
- package/shared/pulsemcp-admin-client/lib/get-moz-backlinks.js +39 -0
- package/shared/pulsemcp-admin-client/lib/get-moz-metrics.d.ts +6 -0
- package/shared/pulsemcp-admin-client/lib/get-moz-metrics.js +36 -0
- package/shared/pulsemcp-admin-client/lib/get-moz-stored-metrics.d.ts +8 -0
- package/shared/pulsemcp-admin-client/lib/get-moz-stored-metrics.js +39 -0
- package/shared/pulsemcp-admin-client/lib/upload-image.js +1 -1
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +26 -0
- package/shared/server.d.ts +31 -1
- package/shared/server.js +13 -0
- package/shared/tools/get-exam-result.js +9 -2
- package/shared/tools/get-moz-backlinks.d.ts +41 -0
- package/shared/tools/get-moz-backlinks.js +102 -0
- package/shared/tools/get-moz-metrics.d.ts +35 -0
- package/shared/tools/get-moz-metrics.js +95 -0
- package/shared/tools/get-moz-stored-metrics.d.ts +45 -0
- package/shared/tools/get-moz-stored-metrics.js +119 -0
- package/shared/tools/run-exam-for-mirror.js +7 -5
- package/shared/tools.d.ts +4 -1
- package/shared/tools.js +13 -0
- package/shared/types.d.ts +45 -0
|
@@ -66,6 +66,10 @@ import { getProctorMetadata } from './tools/get-proctor-metadata.js';
|
|
|
66
66
|
import { listDiscoveredUrls } from './tools/list-discovered-urls.js';
|
|
67
67
|
import { markDiscoveredUrlProcessed } from './tools/mark-discovered-url-processed.js';
|
|
68
68
|
import { getDiscoveredUrlStats } from './tools/get-discovered-url-stats.js';
|
|
69
|
+
// MOZ tools
|
|
70
|
+
import { getMozMetrics } from './tools/get-moz-metrics.js';
|
|
71
|
+
import { getMozBacklinks } from './tools/get-moz-backlinks.js';
|
|
72
|
+
import { getMozStoredMetrics } from './tools/get-moz-stored-metrics.js';
|
|
69
73
|
const ALL_TOOLS = [
|
|
70
74
|
// Newsletter tools (all are write operations except get_newsletter_posts/post and get_authors)
|
|
71
75
|
{ factory: getNewsletterPosts, groups: ['newsletter'], isWriteOperation: false },
|
|
@@ -242,6 +246,10 @@ const ALL_TOOLS = [
|
|
|
242
246
|
isWriteOperation: true,
|
|
243
247
|
},
|
|
244
248
|
{ factory: getDiscoveredUrlStats, groups: ['discovered_urls'], isWriteOperation: false },
|
|
249
|
+
// MOZ tools (all read-only - live metrics, backlinks, and stored data)
|
|
250
|
+
{ factory: getMozMetrics, groups: ['moz'], isWriteOperation: false },
|
|
251
|
+
{ factory: getMozBacklinks, groups: ['moz'], isWriteOperation: false },
|
|
252
|
+
{ factory: getMozStoredMetrics, groups: ['moz'], isWriteOperation: false },
|
|
245
253
|
];
|
|
246
254
|
/**
|
|
247
255
|
* All valid tool groups (base groups and their _readonly variants)
|
|
@@ -271,6 +279,8 @@ const VALID_TOOL_GROUPS = [
|
|
|
271
279
|
'proctor_readonly',
|
|
272
280
|
'discovered_urls',
|
|
273
281
|
'discovered_urls_readonly',
|
|
282
|
+
'moz',
|
|
283
|
+
'moz_readonly',
|
|
274
284
|
'notifications',
|
|
275
285
|
];
|
|
276
286
|
/**
|
|
@@ -289,6 +299,7 @@ const BASE_TOOL_GROUPS = [
|
|
|
289
299
|
'good_jobs',
|
|
290
300
|
'proctor',
|
|
291
301
|
'discovered_urls',
|
|
302
|
+
'moz',
|
|
292
303
|
'notifications',
|
|
293
304
|
];
|
|
294
305
|
/**
|
|
@@ -374,6 +385,8 @@ function shouldIncludeTool(toolDef, enabledGroups) {
|
|
|
374
385
|
* - proctor_readonly: Proctor tools (read only - get_exam_result, list_proctor_runs, and get_proctor_metadata)
|
|
375
386
|
* - discovered_urls: Discovered URL management tools for processing URLs into MCP implementations (read + write)
|
|
376
387
|
* - discovered_urls_readonly: Discovered URL tools (read only - list and stats)
|
|
388
|
+
* - moz: MOZ SEO metrics tools - live URL metrics, backlinks, and stored historical data (all read-only)
|
|
389
|
+
* - moz_readonly: MOZ tools (read only - same as moz since all tools are read-only)
|
|
377
390
|
* - notifications: Notification email tools - send_impl_posted_notif (write-only, no readonly variant)
|
|
378
391
|
*
|
|
379
392
|
* @param clientFactory - Factory function that creates client instances
|
package/package.json
CHANGED
|
@@ -21,7 +21,8 @@ export function extractExamId(line) {
|
|
|
21
21
|
*/
|
|
22
22
|
export function extractStatus(line) {
|
|
23
23
|
const data = line.data;
|
|
24
|
-
|
|
24
|
+
const result = data?.result;
|
|
25
|
+
return (result?.status || data?.status || line.status || 'unknown');
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Maximum number of results to keep on disk. Oldest results are evicted
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MozBacklinksResponse } from '../../types.js';
|
|
2
|
+
export declare function getMozBacklinks(apiKey: string, baseUrl: string, params: {
|
|
3
|
+
url: string;
|
|
4
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
5
|
+
limit?: number;
|
|
6
|
+
}): Promise<MozBacklinksResponse>;
|
|
7
|
+
//# sourceMappingURL=get-moz-backlinks.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export async function getMozBacklinks(apiKey, baseUrl, params) {
|
|
2
|
+
const apiUrl = new URL('/api/moz/backlinks', baseUrl);
|
|
3
|
+
apiUrl.searchParams.append('url', params.url);
|
|
4
|
+
if (params.scope) {
|
|
5
|
+
apiUrl.searchParams.append('scope', params.scope);
|
|
6
|
+
}
|
|
7
|
+
if (params.limit !== undefined) {
|
|
8
|
+
apiUrl.searchParams.append('limit', params.limit.toString());
|
|
9
|
+
}
|
|
10
|
+
const response = await fetch(apiUrl.toString(), {
|
|
11
|
+
method: 'GET',
|
|
12
|
+
headers: {
|
|
13
|
+
'X-API-Key': apiKey,
|
|
14
|
+
Accept: 'application/json',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
if (response.status === 401) {
|
|
19
|
+
throw new Error('Invalid API key');
|
|
20
|
+
}
|
|
21
|
+
if (response.status === 403) {
|
|
22
|
+
throw new Error('User lacks admin privileges');
|
|
23
|
+
}
|
|
24
|
+
if (response.status === 400) {
|
|
25
|
+
const errorData = (await response.json());
|
|
26
|
+
throw new Error(errorData.error || 'Bad request');
|
|
27
|
+
}
|
|
28
|
+
if (response.status === 429) {
|
|
29
|
+
throw new Error('MOZ API rate limit exceeded. Please try again later.');
|
|
30
|
+
}
|
|
31
|
+
if (response.status === 502) {
|
|
32
|
+
const errorData = (await response.json());
|
|
33
|
+
throw new Error(errorData.error || 'MOZ API error');
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Failed to fetch MOZ backlinks: ${response.status} ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const data = (await response.json());
|
|
38
|
+
return data.data;
|
|
39
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MozMetricsResponse } from '../../types.js';
|
|
2
|
+
export declare function getMozMetrics(apiKey: string, baseUrl: string, params: {
|
|
3
|
+
url: string;
|
|
4
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
5
|
+
}): Promise<MozMetricsResponse>;
|
|
6
|
+
//# sourceMappingURL=get-moz-metrics.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export async function getMozMetrics(apiKey, baseUrl, params) {
|
|
2
|
+
const apiUrl = new URL('/api/moz/metrics', baseUrl);
|
|
3
|
+
apiUrl.searchParams.append('url', params.url);
|
|
4
|
+
if (params.scope) {
|
|
5
|
+
apiUrl.searchParams.append('scope', params.scope);
|
|
6
|
+
}
|
|
7
|
+
const response = await fetch(apiUrl.toString(), {
|
|
8
|
+
method: 'GET',
|
|
9
|
+
headers: {
|
|
10
|
+
'X-API-Key': apiKey,
|
|
11
|
+
Accept: 'application/json',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
if (response.status === 401) {
|
|
16
|
+
throw new Error('Invalid API key');
|
|
17
|
+
}
|
|
18
|
+
if (response.status === 403) {
|
|
19
|
+
throw new Error('User lacks admin privileges');
|
|
20
|
+
}
|
|
21
|
+
if (response.status === 400) {
|
|
22
|
+
const errorData = (await response.json());
|
|
23
|
+
throw new Error(errorData.error || 'Bad request');
|
|
24
|
+
}
|
|
25
|
+
if (response.status === 429) {
|
|
26
|
+
throw new Error('MOZ API rate limit exceeded. Please try again later.');
|
|
27
|
+
}
|
|
28
|
+
if (response.status === 502) {
|
|
29
|
+
const errorData = (await response.json());
|
|
30
|
+
throw new Error(errorData.error || 'MOZ API error');
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Failed to fetch MOZ metrics: ${response.status} ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
const data = (await response.json());
|
|
35
|
+
return data.data;
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MozStoredMetricsResponse } from '../../types.js';
|
|
2
|
+
export declare function getMozStoredMetrics(apiKey: string, baseUrl: string, params: {
|
|
3
|
+
server_id: string;
|
|
4
|
+
canonical_id?: number;
|
|
5
|
+
limit?: number;
|
|
6
|
+
offset?: number;
|
|
7
|
+
}): Promise<MozStoredMetricsResponse>;
|
|
8
|
+
//# sourceMappingURL=get-moz-stored-metrics.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export async function getMozStoredMetrics(apiKey, baseUrl, params) {
|
|
2
|
+
const apiUrl = new URL('/api/moz/stored_metrics', baseUrl);
|
|
3
|
+
apiUrl.searchParams.append('server_id', params.server_id);
|
|
4
|
+
if (params.canonical_id !== undefined) {
|
|
5
|
+
apiUrl.searchParams.append('canonical_id', params.canonical_id.toString());
|
|
6
|
+
}
|
|
7
|
+
if (params.limit !== undefined) {
|
|
8
|
+
apiUrl.searchParams.append('limit', params.limit.toString());
|
|
9
|
+
}
|
|
10
|
+
if (params.offset !== undefined) {
|
|
11
|
+
apiUrl.searchParams.append('offset', params.offset.toString());
|
|
12
|
+
}
|
|
13
|
+
const response = await fetch(apiUrl.toString(), {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
headers: {
|
|
16
|
+
'X-API-Key': apiKey,
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
if (response.status === 401) {
|
|
22
|
+
throw new Error('Invalid API key');
|
|
23
|
+
}
|
|
24
|
+
if (response.status === 403) {
|
|
25
|
+
throw new Error('User lacks admin privileges');
|
|
26
|
+
}
|
|
27
|
+
if (response.status === 400) {
|
|
28
|
+
const errorData = (await response.json());
|
|
29
|
+
throw new Error(errorData.error || 'Bad request');
|
|
30
|
+
}
|
|
31
|
+
if (response.status === 404) {
|
|
32
|
+
const errorData = (await response.json());
|
|
33
|
+
throw new Error(errorData.error || 'Server not found');
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Failed to fetch MOZ stored metrics: ${response.status} ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const data = (await response.json());
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
@@ -3,7 +3,7 @@ export async function uploadImage(apiKey, baseUrl, postSlug, fileName, fileData)
|
|
|
3
3
|
// Create form data for multipart upload
|
|
4
4
|
const formData = new FormData();
|
|
5
5
|
// Create a blob from the buffer
|
|
6
|
-
const blob = new Blob([fileData], { type: 'image/png' });
|
|
6
|
+
const blob = new Blob([new Uint8Array(fileData)], { type: 'image/png' });
|
|
7
7
|
// Add file to form data
|
|
8
8
|
formData.append('file', blob, fileName);
|
|
9
9
|
// Add folder and filepath as documented in the API
|
|
@@ -997,5 +997,31 @@ export function createMockPulseMCPAdminClient(mockData) {
|
|
|
997
997
|
errored_today: 0,
|
|
998
998
|
};
|
|
999
999
|
},
|
|
1000
|
+
async getMozMetrics() {
|
|
1001
|
+
return {
|
|
1002
|
+
metrics: { page_authority: 50, domain_authority: 60 },
|
|
1003
|
+
raw_response: {},
|
|
1004
|
+
processed_at: new Date().toISOString(),
|
|
1005
|
+
};
|
|
1006
|
+
},
|
|
1007
|
+
async getMozBacklinks() {
|
|
1008
|
+
return {
|
|
1009
|
+
backlinks: [],
|
|
1010
|
+
raw_response: {},
|
|
1011
|
+
processed_at: new Date().toISOString(),
|
|
1012
|
+
};
|
|
1013
|
+
},
|
|
1014
|
+
async getMozStoredMetrics() {
|
|
1015
|
+
return {
|
|
1016
|
+
data: [],
|
|
1017
|
+
meta: {
|
|
1018
|
+
current_page: 1,
|
|
1019
|
+
total_pages: 0,
|
|
1020
|
+
total_count: 0,
|
|
1021
|
+
has_next: false,
|
|
1022
|
+
limit: 30,
|
|
1023
|
+
},
|
|
1024
|
+
};
|
|
1025
|
+
},
|
|
1000
1026
|
};
|
|
1001
1027
|
}
|
package/shared/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, MCPImplementation, MCPImplementationsResponse, SaveMCPImplementationParams, CreateMCPImplementationParams, Provider, ProvidersResponse, OfficialMirrorQueueStatus, OfficialMirrorQueueResponse, OfficialMirrorQueueItemDetail, OfficialMirrorQueueActionResponse, UnofficialMirror, UnofficialMirrorsResponse, CreateUnofficialMirrorParams, UpdateUnofficialMirrorParams, OfficialMirrorRest, OfficialMirrorsResponse, Tenant, TenantsResponse, McpJson, McpJsonsResponse, CreateMcpJsonParams, UpdateMcpJsonParams, UnifiedMCPServer, UnifiedMCPServersResponse, UpdateUnifiedMCPServerParams, Redirect, RedirectsResponse, RedirectStatus, CreateRedirectParams, UpdateRedirectParams, GoodJob, GoodJobsResponse, GoodJobStatus, GoodJobCronSchedule, GoodJobProcess, GoodJobStatistics, GoodJobActionResponse, GoodJobCleanupResponse, ProctorRunExamParams, ProctorRunExamResponse, ProctorSaveResultsParams, ProctorSaveResultsResponse, ProctorRunsResponse, GetProctorRunsParams, ProctorMetadataResponse, DiscoveredUrlsResponse, MarkDiscoveredUrlProcessedParams, MarkDiscoveredUrlProcessedResponse, DiscoveredUrlStats } from './types.js';
|
|
2
|
+
import type { Post, PostsResponse, CreatePostParams, UpdatePostParams, ImageUploadResponse, Author, AuthorsResponse, MCPServer, MCPClient, MCPImplementation, MCPImplementationsResponse, SaveMCPImplementationParams, CreateMCPImplementationParams, Provider, ProvidersResponse, OfficialMirrorQueueStatus, OfficialMirrorQueueResponse, OfficialMirrorQueueItemDetail, OfficialMirrorQueueActionResponse, UnofficialMirror, UnofficialMirrorsResponse, CreateUnofficialMirrorParams, UpdateUnofficialMirrorParams, OfficialMirrorRest, OfficialMirrorsResponse, Tenant, TenantsResponse, McpJson, McpJsonsResponse, CreateMcpJsonParams, UpdateMcpJsonParams, UnifiedMCPServer, UnifiedMCPServersResponse, UpdateUnifiedMCPServerParams, Redirect, RedirectsResponse, RedirectStatus, CreateRedirectParams, UpdateRedirectParams, GoodJob, GoodJobsResponse, GoodJobStatus, GoodJobCronSchedule, GoodJobProcess, GoodJobStatistics, GoodJobActionResponse, GoodJobCleanupResponse, ProctorRunExamParams, ProctorRunExamResponse, ProctorSaveResultsParams, ProctorSaveResultsResponse, ProctorRunsResponse, GetProctorRunsParams, ProctorMetadataResponse, DiscoveredUrlsResponse, MarkDiscoveredUrlProcessedParams, MarkDiscoveredUrlProcessedResponse, DiscoveredUrlStats, MozMetricsResponse, MozBacklinksResponse, MozStoredMetricsResponse } from './types.js';
|
|
3
3
|
export interface IPulseMCPAdminClient {
|
|
4
4
|
getPosts(params?: {
|
|
5
5
|
search?: string;
|
|
@@ -169,6 +169,21 @@ export interface IPulseMCPAdminClient {
|
|
|
169
169
|
}): Promise<DiscoveredUrlsResponse>;
|
|
170
170
|
markDiscoveredUrlProcessed(params: MarkDiscoveredUrlProcessedParams): Promise<MarkDiscoveredUrlProcessedResponse>;
|
|
171
171
|
getDiscoveredUrlStats(): Promise<DiscoveredUrlStats>;
|
|
172
|
+
getMozMetrics(params: {
|
|
173
|
+
url: string;
|
|
174
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
175
|
+
}): Promise<MozMetricsResponse>;
|
|
176
|
+
getMozBacklinks(params: {
|
|
177
|
+
url: string;
|
|
178
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
179
|
+
limit?: number;
|
|
180
|
+
}): Promise<MozBacklinksResponse>;
|
|
181
|
+
getMozStoredMetrics(params: {
|
|
182
|
+
server_id: string;
|
|
183
|
+
canonical_id?: number;
|
|
184
|
+
limit?: number;
|
|
185
|
+
offset?: number;
|
|
186
|
+
}): Promise<MozStoredMetricsResponse>;
|
|
172
187
|
}
|
|
173
188
|
export declare class PulseMCPAdminClient implements IPulseMCPAdminClient {
|
|
174
189
|
private apiKey;
|
|
@@ -342,6 +357,21 @@ export declare class PulseMCPAdminClient implements IPulseMCPAdminClient {
|
|
|
342
357
|
}): Promise<DiscoveredUrlsResponse>;
|
|
343
358
|
markDiscoveredUrlProcessed(params: MarkDiscoveredUrlProcessedParams): Promise<MarkDiscoveredUrlProcessedResponse>;
|
|
344
359
|
getDiscoveredUrlStats(): Promise<DiscoveredUrlStats>;
|
|
360
|
+
getMozMetrics(params: {
|
|
361
|
+
url: string;
|
|
362
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
363
|
+
}): Promise<MozMetricsResponse>;
|
|
364
|
+
getMozBacklinks(params: {
|
|
365
|
+
url: string;
|
|
366
|
+
scope?: 'url' | 'domain' | 'subdomain';
|
|
367
|
+
limit?: number;
|
|
368
|
+
}): Promise<MozBacklinksResponse>;
|
|
369
|
+
getMozStoredMetrics(params: {
|
|
370
|
+
server_id: string;
|
|
371
|
+
canonical_id?: number;
|
|
372
|
+
limit?: number;
|
|
373
|
+
offset?: number;
|
|
374
|
+
}): Promise<MozStoredMetricsResponse>;
|
|
345
375
|
}
|
|
346
376
|
export type ClientFactory = () => IPulseMCPAdminClient;
|
|
347
377
|
export interface CreateMCPServerOptions {
|
package/shared/server.js
CHANGED
|
@@ -68,6 +68,9 @@ import { getProctorMetadata } from './pulsemcp-admin-client/lib/get-proctor-meta
|
|
|
68
68
|
import { getDiscoveredUrls } from './pulsemcp-admin-client/lib/get-discovered-urls.js';
|
|
69
69
|
import { markDiscoveredUrlProcessed } from './pulsemcp-admin-client/lib/mark-discovered-url-processed.js';
|
|
70
70
|
import { getDiscoveredUrlStats } from './pulsemcp-admin-client/lib/get-discovered-url-stats.js';
|
|
71
|
+
import { getMozMetrics } from './pulsemcp-admin-client/lib/get-moz-metrics.js';
|
|
72
|
+
import { getMozBacklinks } from './pulsemcp-admin-client/lib/get-moz-backlinks.js';
|
|
73
|
+
import { getMozStoredMetrics } from './pulsemcp-admin-client/lib/get-moz-stored-metrics.js';
|
|
71
74
|
// PulseMCP Admin API client implementation
|
|
72
75
|
export class PulseMCPAdminClient {
|
|
73
76
|
apiKey;
|
|
@@ -284,6 +287,16 @@ export class PulseMCPAdminClient {
|
|
|
284
287
|
async getDiscoveredUrlStats() {
|
|
285
288
|
return getDiscoveredUrlStats(this.apiKey, this.baseUrl);
|
|
286
289
|
}
|
|
290
|
+
// MOZ REST API methods
|
|
291
|
+
async getMozMetrics(params) {
|
|
292
|
+
return getMozMetrics(this.apiKey, this.baseUrl, params);
|
|
293
|
+
}
|
|
294
|
+
async getMozBacklinks(params) {
|
|
295
|
+
return getMozBacklinks(this.apiKey, this.baseUrl, params);
|
|
296
|
+
}
|
|
297
|
+
async getMozStoredMetrics(params) {
|
|
298
|
+
return getMozStoredMetrics(this.apiKey, this.baseUrl, params);
|
|
299
|
+
}
|
|
287
300
|
}
|
|
288
301
|
export function createMCPServer(options) {
|
|
289
302
|
const server = new Server({
|
|
@@ -67,9 +67,16 @@ Typical usage:
|
|
|
67
67
|
const targetType = typeMap[validatedArgs.section];
|
|
68
68
|
lines = lines.filter((line) => line.type === targetType);
|
|
69
69
|
}
|
|
70
|
-
// Filter by mirror_id
|
|
70
|
+
// Filter by mirror_id — check both top-level and data.mirror_id
|
|
71
|
+
// since the backend nests mirror_id inside the data payload.
|
|
71
72
|
if (validatedArgs.mirror_id !== undefined) {
|
|
72
|
-
lines = lines.filter((line) =>
|
|
73
|
+
lines = lines.filter((line) => {
|
|
74
|
+
if (line.type !== 'exam_result')
|
|
75
|
+
return true;
|
|
76
|
+
const data = line.data;
|
|
77
|
+
const mirrorId = line.mirror_id ?? data?.mirror_id;
|
|
78
|
+
return mirrorId === validatedArgs.mirror_id;
|
|
79
|
+
});
|
|
73
80
|
}
|
|
74
81
|
let content = `**Exam Result Details**\n\n`;
|
|
75
82
|
content += `Result ID: ${stored.result_id}\n`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import type { ClientFactory } from '../server.js';
|
|
3
|
+
export declare function getMozBacklinks(_server: Server, clientFactory: ClientFactory): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
url: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: "The URL to fetch backlinks for. Must be a valid HTTP/HTTPS URL.";
|
|
12
|
+
};
|
|
13
|
+
scope: {
|
|
14
|
+
type: string;
|
|
15
|
+
enum: string[];
|
|
16
|
+
description: "Scope of the backlinks lookup: \"url\" (exact URL, default), \"domain\" (entire root domain), or \"subdomain\" (specific subdomain)";
|
|
17
|
+
};
|
|
18
|
+
limit: {
|
|
19
|
+
type: string;
|
|
20
|
+
minimum: number;
|
|
21
|
+
maximum: number;
|
|
22
|
+
description: "Number of backlinks to return, 1-50. Default: 1";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
required: string[];
|
|
26
|
+
};
|
|
27
|
+
handler: (args: unknown) => Promise<{
|
|
28
|
+
content: {
|
|
29
|
+
type: string;
|
|
30
|
+
text: string;
|
|
31
|
+
}[];
|
|
32
|
+
isError?: undefined;
|
|
33
|
+
} | {
|
|
34
|
+
content: {
|
|
35
|
+
type: string;
|
|
36
|
+
text: string;
|
|
37
|
+
}[];
|
|
38
|
+
isError: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=get-moz-backlinks.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
url: 'The URL to fetch backlinks for. Must be a valid HTTP/HTTPS URL.',
|
|
4
|
+
scope: 'Scope of the backlinks lookup: "url" (exact URL, default), "domain" (entire root domain), or "subdomain" (specific subdomain)',
|
|
5
|
+
limit: 'Number of backlinks to return, 1-50. Default: 1',
|
|
6
|
+
};
|
|
7
|
+
const GetMozBacklinksSchema = z.object({
|
|
8
|
+
url: z.string().describe(PARAM_DESCRIPTIONS.url),
|
|
9
|
+
scope: z.enum(['url', 'domain', 'subdomain']).optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
10
|
+
limit: z.number().min(1).max(50).optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
11
|
+
});
|
|
12
|
+
export function getMozBacklinks(_server, clientFactory) {
|
|
13
|
+
return {
|
|
14
|
+
name: 'get_moz_backlinks',
|
|
15
|
+
description: `Fetch live backlink data from the MOZ API. Returns source pages, anchor text, and domain authority for backlinks pointing to a given URL.
|
|
16
|
+
|
|
17
|
+
Example response:
|
|
18
|
+
{
|
|
19
|
+
"backlinks": [
|
|
20
|
+
{
|
|
21
|
+
"source_page": "https://other.com/blog",
|
|
22
|
+
"anchor_text": "example link",
|
|
23
|
+
"domain_authority": 72
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"raw_response": { ... },
|
|
27
|
+
"processed_at": "2026-03-15T12:00:00Z"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Use cases:
|
|
31
|
+
- Discover which sites link to a given MCP server URL
|
|
32
|
+
- Analyze the quality of backlinks (by domain authority)
|
|
33
|
+
- Research link profiles for SEO analysis`,
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
url: { type: 'string', description: PARAM_DESCRIPTIONS.url },
|
|
38
|
+
scope: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
enum: ['url', 'domain', 'subdomain'],
|
|
41
|
+
description: PARAM_DESCRIPTIONS.scope,
|
|
42
|
+
},
|
|
43
|
+
limit: {
|
|
44
|
+
type: 'number',
|
|
45
|
+
minimum: 1,
|
|
46
|
+
maximum: 50,
|
|
47
|
+
description: PARAM_DESCRIPTIONS.limit,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ['url'],
|
|
51
|
+
},
|
|
52
|
+
handler: async (args) => {
|
|
53
|
+
const validatedArgs = GetMozBacklinksSchema.parse(args);
|
|
54
|
+
const client = clientFactory();
|
|
55
|
+
try {
|
|
56
|
+
const response = await client.getMozBacklinks({
|
|
57
|
+
url: validatedArgs.url,
|
|
58
|
+
scope: validatedArgs.scope,
|
|
59
|
+
limit: validatedArgs.limit,
|
|
60
|
+
});
|
|
61
|
+
let content = `**MOZ Backlinks for ${validatedArgs.url}**`;
|
|
62
|
+
if (validatedArgs.scope) {
|
|
63
|
+
content += ` (scope: ${validatedArgs.scope})`;
|
|
64
|
+
}
|
|
65
|
+
content += '\n\n';
|
|
66
|
+
if (response.backlinks.length === 0) {
|
|
67
|
+
content += 'No backlinks found.\n';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
content += `Found ${response.backlinks.length} backlink(s):\n\n`;
|
|
71
|
+
for (const [index, backlink] of response.backlinks.entries()) {
|
|
72
|
+
content += `${index + 1}. **${backlink.source_page || 'Unknown source'}**\n`;
|
|
73
|
+
if (backlink.anchor_text)
|
|
74
|
+
content += ` Anchor text: "${backlink.anchor_text}"\n`;
|
|
75
|
+
if (backlink.domain_authority !== undefined)
|
|
76
|
+
content += ` Domain Authority: ${backlink.domain_authority}\n`;
|
|
77
|
+
// Include any additional fields
|
|
78
|
+
const knownKeys = ['source_page', 'anchor_text', 'domain_authority'];
|
|
79
|
+
const extraKeys = Object.keys(backlink).filter((k) => !knownKeys.includes(k));
|
|
80
|
+
for (const key of extraKeys) {
|
|
81
|
+
content += ` ${key}: ${JSON.stringify(backlink[key])}\n`;
|
|
82
|
+
}
|
|
83
|
+
content += '\n';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
content += `**Processed at:** ${response.processed_at}`;
|
|
87
|
+
return { content: [{ type: 'text', text: content.trim() }] };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: 'text',
|
|
94
|
+
text: `Error fetching MOZ backlinks: ${error instanceof Error ? error.message : String(error)}`,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import type { ClientFactory } from '../server.js';
|
|
3
|
+
export declare function getMozMetrics(_server: Server, clientFactory: ClientFactory): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
url: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: "The URL to fetch MOZ metrics for. Must be a valid HTTP/HTTPS URL.";
|
|
12
|
+
};
|
|
13
|
+
scope: {
|
|
14
|
+
type: string;
|
|
15
|
+
enum: string[];
|
|
16
|
+
description: "Scope of the metrics lookup: \"url\" (exact URL, default), \"domain\" (entire root domain), or \"subdomain\" (specific subdomain)";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
required: string[];
|
|
20
|
+
};
|
|
21
|
+
handler: (args: unknown) => Promise<{
|
|
22
|
+
content: {
|
|
23
|
+
type: string;
|
|
24
|
+
text: string;
|
|
25
|
+
}[];
|
|
26
|
+
isError?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
content: {
|
|
29
|
+
type: string;
|
|
30
|
+
text: string;
|
|
31
|
+
}[];
|
|
32
|
+
isError: boolean;
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=get-moz-metrics.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
url: 'The URL to fetch MOZ metrics for. Must be a valid HTTP/HTTPS URL.',
|
|
4
|
+
scope: 'Scope of the metrics lookup: "url" (exact URL, default), "domain" (entire root domain), or "subdomain" (specific subdomain)',
|
|
5
|
+
};
|
|
6
|
+
const GetMozMetricsSchema = z.object({
|
|
7
|
+
url: z.string().describe(PARAM_DESCRIPTIONS.url),
|
|
8
|
+
scope: z.enum(['url', 'domain', 'subdomain']).optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
9
|
+
});
|
|
10
|
+
export function getMozMetrics(_server, clientFactory) {
|
|
11
|
+
return {
|
|
12
|
+
name: 'get_moz_metrics',
|
|
13
|
+
description: `Fetch live URL metrics from the MOZ API. Returns page authority, domain authority, spam score, and link counts for a given URL.
|
|
14
|
+
|
|
15
|
+
Example response:
|
|
16
|
+
{
|
|
17
|
+
"metrics": {
|
|
18
|
+
"page_authority": 88,
|
|
19
|
+
"domain_authority": 95,
|
|
20
|
+
"spam_score": 1,
|
|
21
|
+
"root_domains_to_page": 441855
|
|
22
|
+
},
|
|
23
|
+
"raw_response": { ... },
|
|
24
|
+
"processed_at": "2026-03-15T12:00:00Z"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Use cases:
|
|
28
|
+
- Check the authority and spam score of a URL
|
|
29
|
+
- Compare domain authority across different MCP server websites
|
|
30
|
+
- Evaluate the SEO strength of a page or domain`,
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
url: { type: 'string', description: PARAM_DESCRIPTIONS.url },
|
|
35
|
+
scope: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
enum: ['url', 'domain', 'subdomain'],
|
|
38
|
+
description: PARAM_DESCRIPTIONS.scope,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['url'],
|
|
42
|
+
},
|
|
43
|
+
handler: async (args) => {
|
|
44
|
+
const validatedArgs = GetMozMetricsSchema.parse(args);
|
|
45
|
+
const client = clientFactory();
|
|
46
|
+
try {
|
|
47
|
+
const response = await client.getMozMetrics({
|
|
48
|
+
url: validatedArgs.url,
|
|
49
|
+
scope: validatedArgs.scope,
|
|
50
|
+
});
|
|
51
|
+
let content = `**MOZ Metrics for ${validatedArgs.url}**`;
|
|
52
|
+
if (validatedArgs.scope) {
|
|
53
|
+
content += ` (scope: ${validatedArgs.scope})`;
|
|
54
|
+
}
|
|
55
|
+
content += '\n\n';
|
|
56
|
+
const m = response.metrics;
|
|
57
|
+
if (m.page_authority !== undefined)
|
|
58
|
+
content += `**Page Authority:** ${m.page_authority}\n`;
|
|
59
|
+
if (m.domain_authority !== undefined)
|
|
60
|
+
content += `**Domain Authority:** ${m.domain_authority}\n`;
|
|
61
|
+
if (m.spam_score !== undefined)
|
|
62
|
+
content += `**Spam Score:** ${m.spam_score}\n`;
|
|
63
|
+
if (m.root_domains_to_page !== undefined)
|
|
64
|
+
content += `**Root Domains to Page:** ${m.root_domains_to_page}\n`;
|
|
65
|
+
content += `\n**Processed at:** ${response.processed_at}\n`;
|
|
66
|
+
// Include any additional metrics
|
|
67
|
+
const knownKeys = [
|
|
68
|
+
'page_authority',
|
|
69
|
+
'domain_authority',
|
|
70
|
+
'spam_score',
|
|
71
|
+
'root_domains_to_page',
|
|
72
|
+
];
|
|
73
|
+
const extraKeys = Object.keys(m).filter((k) => !knownKeys.includes(k));
|
|
74
|
+
if (extraKeys.length > 0) {
|
|
75
|
+
content += '\n**Additional metrics:**\n';
|
|
76
|
+
for (const key of extraKeys) {
|
|
77
|
+
content += `- ${key}: ${JSON.stringify(m[key])}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { content: [{ type: 'text', text: content.trim() }] };
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: `Error fetching MOZ metrics: ${error instanceof Error ? error.message : String(error)}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|