pulsemcp-cms-admin-mcp-server 0.8.0 → 0.9.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 +1 -1
- package/build/shared/src/pulsemcp-admin-client/lib/create-mcp-implementation.js +3 -0
- package/build/shared/src/pulsemcp-admin-client/lib/get-proctor-runs.js +68 -0
- package/build/shared/src/pulsemcp-admin-client/lib/save-mcp-implementation.js +3 -0
- package/build/shared/src/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +1 -0
- package/build/shared/src/pulsemcp-admin-client/lib/update-unified-mcp-server.js +2 -0
- package/build/shared/src/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +6 -0
- package/build/shared/src/server.js +4 -0
- package/build/shared/src/tools/get-mcp-server.js +5 -2
- package/build/shared/src/tools/list-proctor-runs.js +171 -0
- package/build/shared/src/tools/save-mcp-implementation.js +15 -3
- package/build/shared/src/tools/update-mcp-server.js +16 -4
- package/build/shared/src/tools.js +3 -1
- package/package.json +1 -1
- package/shared/pulsemcp-admin-client/lib/create-mcp-implementation.js +3 -0
- package/shared/pulsemcp-admin-client/lib/get-proctor-runs.d.ts +11 -0
- package/shared/pulsemcp-admin-client/lib/get-proctor-runs.js +68 -0
- package/shared/pulsemcp-admin-client/lib/save-mcp-implementation.js +3 -0
- package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.d.ts +2 -0
- package/shared/pulsemcp-admin-client/lib/unified-mcp-server-mapper.js +1 -0
- package/shared/pulsemcp-admin-client/lib/update-unified-mcp-server.js +2 -0
- package/shared/pulsemcp-admin-client/pulsemcp-admin-client.integration-mock.js +6 -0
- package/shared/server.d.ts +3 -1
- package/shared/server.js +4 -0
- package/shared/tools/get-mcp-server.js +5 -2
- package/shared/tools/list-proctor-runs.d.ts +58 -0
- package/shared/tools/list-proctor-runs.js +171 -0
- package/shared/tools/save-mcp-implementation.d.ts +5 -1
- package/shared/tools/save-mcp-implementation.js +15 -3
- package/shared/tools/update-mcp-server.d.ts +4 -0
- package/shared/tools/update-mcp-server.js +16 -4
- package/shared/tools.d.ts +2 -2
- package/shared/tools.js +3 -1
- package/shared/types.d.ts +44 -1
package/README.md
CHANGED
|
@@ -218,7 +218,7 @@ TOOL_GROUPS=newsletter,server_directory_readonly
|
|
|
218
218
|
- Enable or disable specific tool groups by setting `TOOL_GROUPS` environment variable
|
|
219
219
|
- Use `_readonly` suffixes to restrict groups to read-only operations (e.g., `server_directory_readonly`)
|
|
220
220
|
- Use the `remote` array parameter in `save_mcp_implementation` to configure remote endpoints for MCP servers (transport, host_platform, authentication_method, etc.)
|
|
221
|
-
- Use the `canonical` array parameter in `save_mcp_implementation` to set canonical URLs with scope (domain, subdomain,
|
|
221
|
+
- Use the `canonical` array parameter in `save_mcp_implementation` to set canonical URLs with scope (domain, subdomain, or url)
|
|
222
222
|
- Remote endpoints allow specifying how MCP servers can be accessed (direct URL, setup URL, authentication method, cost, etc.)
|
|
223
223
|
- When updating existing remotes, include the remote `id` (number from `get_draft_mcp_implementations`) in the remote object
|
|
224
224
|
- Use `list_mcp_servers` to browse MCP servers with filtering by status (draft/live/archived) and classification (official/community)
|
|
@@ -64,6 +64,9 @@ export async function createMCPImplementation(apiKey, baseUrl, params) {
|
|
|
64
64
|
if (params.recommended !== undefined) {
|
|
65
65
|
formData.append('mcp_implementation[recommended]', params.recommended.toString());
|
|
66
66
|
}
|
|
67
|
+
if (params.verified_no_remote_canonicals !== undefined) {
|
|
68
|
+
formData.append('mcp_implementation[verified_no_remote_canonicals]', params.verified_no_remote_canonicals.toString());
|
|
69
|
+
}
|
|
67
70
|
// Date overrides
|
|
68
71
|
if (params.created_on_override !== undefined) {
|
|
69
72
|
formData.append('mcp_implementation[created_on_override]', params.created_on_override);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export async function getProctorRuns(apiKey, baseUrl, params) {
|
|
2
|
+
const url = new URL('/api/proctor_runs', baseUrl);
|
|
3
|
+
if (params?.q) {
|
|
4
|
+
url.searchParams.append('q', params.q);
|
|
5
|
+
}
|
|
6
|
+
if (params?.recommended) {
|
|
7
|
+
url.searchParams.append('recommended', '1');
|
|
8
|
+
}
|
|
9
|
+
if (params?.tenant_ids) {
|
|
10
|
+
url.searchParams.append('tenant_ids', params.tenant_ids);
|
|
11
|
+
}
|
|
12
|
+
if (params?.sort) {
|
|
13
|
+
url.searchParams.append('sort', params.sort);
|
|
14
|
+
}
|
|
15
|
+
if (params?.direction) {
|
|
16
|
+
url.searchParams.append('direction', params.direction);
|
|
17
|
+
}
|
|
18
|
+
if (params?.limit) {
|
|
19
|
+
url.searchParams.append('limit', params.limit.toString());
|
|
20
|
+
}
|
|
21
|
+
if (params?.offset !== undefined && params.offset > 0) {
|
|
22
|
+
url.searchParams.append('offset', params.offset.toString());
|
|
23
|
+
}
|
|
24
|
+
const response = await fetch(url.toString(), {
|
|
25
|
+
method: 'GET',
|
|
26
|
+
headers: {
|
|
27
|
+
'X-API-Key': apiKey,
|
|
28
|
+
Accept: 'application/json',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
if (response.status === 401) {
|
|
33
|
+
throw new Error('Invalid API key');
|
|
34
|
+
}
|
|
35
|
+
if (response.status === 403) {
|
|
36
|
+
throw new Error('User lacks admin privileges');
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Failed to fetch proctor runs: ${response.status} ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
const data = (await response.json());
|
|
41
|
+
return {
|
|
42
|
+
runs: data.data.map((run) => ({
|
|
43
|
+
id: run.id,
|
|
44
|
+
slug: run.slug,
|
|
45
|
+
name: run.name,
|
|
46
|
+
recommended: run.recommended,
|
|
47
|
+
mirrors_count: run.mirrors_count,
|
|
48
|
+
tenant_count: run.tenant_count,
|
|
49
|
+
latest_version: run.latest_version,
|
|
50
|
+
latest_mirror_id: run.latest_mirror_id,
|
|
51
|
+
latest_mirror_name: run.latest_mirror_name,
|
|
52
|
+
latest_tested: run.latest_tested,
|
|
53
|
+
last_auth_check_days: run.last_auth_check_days,
|
|
54
|
+
last_tools_list_days: run.last_tools_list_days,
|
|
55
|
+
auth_types: run.auth_types,
|
|
56
|
+
num_tools: run.num_tools,
|
|
57
|
+
packages: run.packages,
|
|
58
|
+
remotes: run.remotes,
|
|
59
|
+
})),
|
|
60
|
+
pagination: {
|
|
61
|
+
current_page: data.meta.current_page,
|
|
62
|
+
total_pages: data.meta.total_pages,
|
|
63
|
+
total_count: data.meta.total_count,
|
|
64
|
+
has_next: data.meta.has_next,
|
|
65
|
+
limit: data.meta.limit,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -74,6 +74,9 @@ export async function saveMCPImplementation(apiKey, baseUrl, id, params) {
|
|
|
74
74
|
if (params.recommended !== undefined) {
|
|
75
75
|
formData.append('mcp_implementation[recommended]', params.recommended.toString());
|
|
76
76
|
}
|
|
77
|
+
if (params.verified_no_remote_canonicals !== undefined) {
|
|
78
|
+
formData.append('mcp_implementation[verified_no_remote_canonicals]', params.verified_no_remote_canonicals.toString());
|
|
79
|
+
}
|
|
77
80
|
// Date overrides
|
|
78
81
|
if (params.created_on_override !== undefined) {
|
|
79
82
|
formData.append('mcp_implementation[created_on_override]', params.created_on_override);
|
|
@@ -47,6 +47,7 @@ export function mapToUnifiedServer(impl) {
|
|
|
47
47
|
package_name: impl.package_name,
|
|
48
48
|
// Flags
|
|
49
49
|
recommended: impl.recommended,
|
|
50
|
+
verified_no_remote_canonicals: mcpServer.verified_no_remote_canonicals,
|
|
50
51
|
// Canonical URLs
|
|
51
52
|
canonical_urls: impl.canonical,
|
|
52
53
|
// Remote endpoints
|
|
@@ -49,6 +49,8 @@ export async function updateUnifiedMCPServer(apiKey, baseUrl, implementationId,
|
|
|
49
49
|
// Flags
|
|
50
50
|
if (params.recommended !== undefined)
|
|
51
51
|
implParams.recommended = params.recommended;
|
|
52
|
+
if (params.verified_no_remote_canonicals !== undefined)
|
|
53
|
+
implParams.verified_no_remote_canonicals = params.verified_no_remote_canonicals;
|
|
52
54
|
// Date overrides
|
|
53
55
|
if (params.created_on_override !== undefined)
|
|
54
56
|
implParams.created_on_override = params.created_on_override;
|
|
@@ -958,6 +958,12 @@ export function createMockPulseMCPAdminClient(mockData) {
|
|
|
958
958
|
errors: [],
|
|
959
959
|
};
|
|
960
960
|
},
|
|
961
|
+
async getProctorRuns() {
|
|
962
|
+
return {
|
|
963
|
+
runs: [],
|
|
964
|
+
pagination: { current_page: 1, total_pages: 1, total_count: 0, has_next: false, limit: 30 },
|
|
965
|
+
};
|
|
966
|
+
},
|
|
961
967
|
// Discovered URL methods
|
|
962
968
|
async getDiscoveredUrls() {
|
|
963
969
|
return {
|
|
@@ -261,6 +261,10 @@ export class PulseMCPAdminClient {
|
|
|
261
261
|
const { saveResultsForMirror } = await import('./pulsemcp-admin-client/lib/save-results-for-mirror.js');
|
|
262
262
|
return saveResultsForMirror(this.apiKey, this.baseUrl, params);
|
|
263
263
|
}
|
|
264
|
+
async getProctorRuns(params) {
|
|
265
|
+
const { getProctorRuns } = await import('./pulsemcp-admin-client/lib/get-proctor-runs.js');
|
|
266
|
+
return getProctorRuns(this.apiKey, this.baseUrl, params);
|
|
267
|
+
}
|
|
264
268
|
// Discovered URL REST API methods
|
|
265
269
|
async getDiscoveredUrls(params) {
|
|
266
270
|
const { getDiscoveredUrls } = await import('./pulsemcp-admin-client/lib/get-discovered-urls.js');
|
|
@@ -14,7 +14,7 @@ The response includes:
|
|
|
14
14
|
- **Basic info**: name, descriptions, status, classification, implementation language
|
|
15
15
|
- **Provider**: organization/person details
|
|
16
16
|
- **Source code location**: GitHub repository info with stars and last update
|
|
17
|
-
- **Canonical URLs**: authoritative URLs with scope (domain, subdomain,
|
|
17
|
+
- **Canonical URLs**: authoritative URLs with scope (domain, subdomain, url)
|
|
18
18
|
- **Remote endpoints**: all deployment endpoints with transport, platform, auth, and cost info
|
|
19
19
|
- **Tags**: categorization tags
|
|
20
20
|
- **Download statistics**: npm download estimates
|
|
@@ -48,7 +48,7 @@ Example response:
|
|
|
48
48
|
"github_stars": 5000
|
|
49
49
|
},
|
|
50
50
|
"canonical_urls": [
|
|
51
|
-
{ "url": "https://github.com/modelcontextprotocol/servers", "scope": "
|
|
51
|
+
{ "url": "https://github.com/modelcontextprotocol/servers", "scope": "domain" }
|
|
52
52
|
],
|
|
53
53
|
"remotes": [
|
|
54
54
|
{
|
|
@@ -102,6 +102,9 @@ Example response:
|
|
|
102
102
|
if (server.recommended !== undefined) {
|
|
103
103
|
content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
|
|
104
104
|
}
|
|
105
|
+
if (server.verified_no_remote_canonicals !== undefined) {
|
|
106
|
+
content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
|
|
107
|
+
}
|
|
105
108
|
if (server.short_description) {
|
|
106
109
|
content += `\n**Short Description:**\n${server.short_description}\n`;
|
|
107
110
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
q: 'Search by server slug, implementation name, or mirror name',
|
|
4
|
+
recommended: 'When true, filter to recommended servers only. When false or omitted, shows all servers',
|
|
5
|
+
tenant_ids: 'Comma-separated tenant IDs to filter by (OR logic)',
|
|
6
|
+
sort: 'Column to sort by: slug, name, mirrors, recommended, tenants, latest_tested, last_auth_check, last_tools_list',
|
|
7
|
+
direction: 'Sort direction: asc or desc. Default: asc',
|
|
8
|
+
limit: 'Results per page, range 1-100. Default: 30',
|
|
9
|
+
offset: 'Pagination offset. Default: 0',
|
|
10
|
+
};
|
|
11
|
+
const ListProctorRunsSchema = z.object({
|
|
12
|
+
q: z.string().optional().describe(PARAM_DESCRIPTIONS.q),
|
|
13
|
+
recommended: z.boolean().optional().describe(PARAM_DESCRIPTIONS.recommended),
|
|
14
|
+
tenant_ids: z.string().optional().describe(PARAM_DESCRIPTIONS.tenant_ids),
|
|
15
|
+
sort: z
|
|
16
|
+
.enum([
|
|
17
|
+
'slug',
|
|
18
|
+
'name',
|
|
19
|
+
'mirrors',
|
|
20
|
+
'recommended',
|
|
21
|
+
'tenants',
|
|
22
|
+
'latest_tested',
|
|
23
|
+
'last_auth_check',
|
|
24
|
+
'last_tools_list',
|
|
25
|
+
])
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(PARAM_DESCRIPTIONS.sort),
|
|
28
|
+
direction: z.enum(['asc', 'desc']).optional().describe(PARAM_DESCRIPTIONS.direction),
|
|
29
|
+
limit: z.number().min(1).max(100).optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
30
|
+
offset: z.number().min(0).optional().describe(PARAM_DESCRIPTIONS.offset),
|
|
31
|
+
});
|
|
32
|
+
export function listProctorRuns(_server, clientFactory) {
|
|
33
|
+
return {
|
|
34
|
+
name: 'list_proctor_runs',
|
|
35
|
+
description: `List proctor run summaries for MCP servers. Shows testing status across all servers including auth-check and tools-list exam results. Useful for identifying untested servers or servers that need re-testing.
|
|
36
|
+
|
|
37
|
+
Default sort order: untested servers first, then stalest auth check, then stalest tools list, then alphabetical by slug.
|
|
38
|
+
|
|
39
|
+
Example response:
|
|
40
|
+
{
|
|
41
|
+
"runs": [
|
|
42
|
+
{
|
|
43
|
+
"id": 123,
|
|
44
|
+
"slug": "some-server",
|
|
45
|
+
"name": "Some Server",
|
|
46
|
+
"recommended": true,
|
|
47
|
+
"mirrors_count": 2,
|
|
48
|
+
"latest_tested": true,
|
|
49
|
+
"last_auth_check_days": 2,
|
|
50
|
+
"last_tools_list_days": 3,
|
|
51
|
+
"auth_types": ["oauth2"],
|
|
52
|
+
"num_tools": 5,
|
|
53
|
+
"packages": ["npm"],
|
|
54
|
+
"remotes": ["streamable-http"]
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"pagination": { "current_page": 1, "total_pages": 2, "total_count": 45, "has_next": true }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Use cases:
|
|
61
|
+
- Find servers that haven't been tested yet (untested appear first by default)
|
|
62
|
+
- Check which servers have stale proctor results
|
|
63
|
+
- Filter to recommended servers to prioritize testing
|
|
64
|
+
- Search for a specific server's testing status`,
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
q: { type: 'string', description: PARAM_DESCRIPTIONS.q },
|
|
69
|
+
recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
|
|
70
|
+
tenant_ids: { type: 'string', description: PARAM_DESCRIPTIONS.tenant_ids },
|
|
71
|
+
sort: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
enum: [
|
|
74
|
+
'slug',
|
|
75
|
+
'name',
|
|
76
|
+
'mirrors',
|
|
77
|
+
'recommended',
|
|
78
|
+
'tenants',
|
|
79
|
+
'latest_tested',
|
|
80
|
+
'last_auth_check',
|
|
81
|
+
'last_tools_list',
|
|
82
|
+
],
|
|
83
|
+
description: PARAM_DESCRIPTIONS.sort,
|
|
84
|
+
},
|
|
85
|
+
direction: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
enum: ['asc', 'desc'],
|
|
88
|
+
description: PARAM_DESCRIPTIONS.direction,
|
|
89
|
+
},
|
|
90
|
+
limit: { type: 'number', minimum: 1, maximum: 100, description: PARAM_DESCRIPTIONS.limit },
|
|
91
|
+
offset: { type: 'number', minimum: 0, description: PARAM_DESCRIPTIONS.offset },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
handler: async (args) => {
|
|
95
|
+
const validatedArgs = ListProctorRunsSchema.parse(args);
|
|
96
|
+
const client = clientFactory();
|
|
97
|
+
try {
|
|
98
|
+
const response = await client.getProctorRuns({
|
|
99
|
+
q: validatedArgs.q,
|
|
100
|
+
recommended: validatedArgs.recommended,
|
|
101
|
+
tenant_ids: validatedArgs.tenant_ids,
|
|
102
|
+
sort: validatedArgs.sort,
|
|
103
|
+
direction: validatedArgs.direction,
|
|
104
|
+
limit: validatedArgs.limit,
|
|
105
|
+
offset: validatedArgs.offset,
|
|
106
|
+
});
|
|
107
|
+
let content = `Found ${response.runs.length} proctor run summaries`;
|
|
108
|
+
if (response.pagination) {
|
|
109
|
+
content += ` (page ${response.pagination.current_page} of ${response.pagination.total_pages}, total: ${response.pagination.total_count})`;
|
|
110
|
+
}
|
|
111
|
+
content += ':\n\n';
|
|
112
|
+
for (const [index, run] of response.runs.entries()) {
|
|
113
|
+
content += `${index + 1}. **${run.slug}**`;
|
|
114
|
+
if (run.name) {
|
|
115
|
+
content += ` — ${run.name}`;
|
|
116
|
+
}
|
|
117
|
+
content += ` (ID: ${run.id})\n`;
|
|
118
|
+
if (run.recommended) {
|
|
119
|
+
content += ` Recommended: yes\n`;
|
|
120
|
+
}
|
|
121
|
+
content += ` Mirrors: ${run.mirrors_count}, Tenants: ${run.tenant_count}\n`;
|
|
122
|
+
if (run.latest_version) {
|
|
123
|
+
content += ` Latest Version: ${run.latest_version}`;
|
|
124
|
+
if (run.latest_mirror_name) {
|
|
125
|
+
content += ` (mirror: ${run.latest_mirror_name}, ID: ${run.latest_mirror_id})`;
|
|
126
|
+
}
|
|
127
|
+
content += '\n';
|
|
128
|
+
}
|
|
129
|
+
content += ` Latest Tested: ${run.latest_tested ? 'yes' : 'no'}\n`;
|
|
130
|
+
if (run.last_auth_check_days !== null) {
|
|
131
|
+
content += ` Last Auth Check: ${run.last_auth_check_days} day(s) ago\n`;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
content += ` Last Auth Check: never\n`;
|
|
135
|
+
}
|
|
136
|
+
if (run.last_tools_list_days !== null) {
|
|
137
|
+
content += ` Last Tools List: ${run.last_tools_list_days} day(s) ago\n`;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
content += ` Last Tools List: never\n`;
|
|
141
|
+
}
|
|
142
|
+
if (run.auth_types.length > 0) {
|
|
143
|
+
content += ` Auth Types: ${run.auth_types.join(', ')}\n`;
|
|
144
|
+
}
|
|
145
|
+
if (run.num_tools !== null) {
|
|
146
|
+
content += ` Num Tools: ${run.num_tools}\n`;
|
|
147
|
+
}
|
|
148
|
+
if (run.packages.length > 0) {
|
|
149
|
+
content += ` Packages: ${run.packages.join(', ')}\n`;
|
|
150
|
+
}
|
|
151
|
+
if (run.remotes.length > 0) {
|
|
152
|
+
content += ` Remotes: ${run.remotes.join(', ')}\n`;
|
|
153
|
+
}
|
|
154
|
+
content += '\n';
|
|
155
|
+
}
|
|
156
|
+
return { content: [{ type: 'text', text: content.trim() }] };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: 'text',
|
|
163
|
+
text: `Error fetching proctor runs: ${error instanceof Error ? error.message : String(error)}`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
isError: true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -26,7 +26,9 @@ const PARAM_DESCRIPTIONS = {
|
|
|
26
26
|
// Remote endpoints
|
|
27
27
|
remote: 'Array of remote endpoint configurations for MCP servers. Each remote can have: id (existing remote ID or blank for new), url_direct, url_setup, transport (e.g., "sse"), host_platform (e.g., "smithery"), host_infrastructure (e.g., "cloudflare"), authentication_method (e.g., "open"), cost (e.g., "free"), status (defaults to "live"), display_name, and internal_notes.',
|
|
28
28
|
// Canonical URLs
|
|
29
|
-
canonical: 'Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain",
|
|
29
|
+
canonical: 'Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain", or "url"), and optional note for additional context.',
|
|
30
|
+
// Flags
|
|
31
|
+
verified_no_remote_canonicals: 'Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)',
|
|
30
32
|
// Other fields
|
|
31
33
|
internal_notes: 'Admin-only notes. Not displayed publicly. Used for tracking submission sources, reviewer comments, etc.',
|
|
32
34
|
};
|
|
@@ -83,11 +85,16 @@ const SaveMCPImplementationSchema = z.object({
|
|
|
83
85
|
canonical: z
|
|
84
86
|
.array(z.object({
|
|
85
87
|
url: z.string(),
|
|
86
|
-
scope: z.enum(['domain', 'subdomain', '
|
|
88
|
+
scope: z.enum(['domain', 'subdomain', 'url']),
|
|
87
89
|
note: z.string().optional(),
|
|
88
90
|
}))
|
|
89
91
|
.optional()
|
|
90
92
|
.describe(PARAM_DESCRIPTIONS.canonical),
|
|
93
|
+
// Flags
|
|
94
|
+
verified_no_remote_canonicals: z
|
|
95
|
+
.boolean()
|
|
96
|
+
.optional()
|
|
97
|
+
.describe(PARAM_DESCRIPTIONS.verified_no_remote_canonicals),
|
|
91
98
|
// Other fields
|
|
92
99
|
internal_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.internal_notes),
|
|
93
100
|
});
|
|
@@ -294,13 +301,18 @@ Use cases:
|
|
|
294
301
|
type: 'object',
|
|
295
302
|
properties: {
|
|
296
303
|
url: { type: 'string' },
|
|
297
|
-
scope: { type: 'string', enum: ['domain', 'subdomain', '
|
|
304
|
+
scope: { type: 'string', enum: ['domain', 'subdomain', 'url'] },
|
|
298
305
|
note: { type: 'string' },
|
|
299
306
|
},
|
|
300
307
|
required: ['url', 'scope'],
|
|
301
308
|
},
|
|
302
309
|
description: PARAM_DESCRIPTIONS.canonical,
|
|
303
310
|
},
|
|
311
|
+
// Flags
|
|
312
|
+
verified_no_remote_canonicals: {
|
|
313
|
+
type: 'boolean',
|
|
314
|
+
description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
|
|
315
|
+
},
|
|
304
316
|
// Other fields
|
|
305
317
|
internal_notes: {
|
|
306
318
|
type: 'string',
|
|
@@ -16,6 +16,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
16
16
|
package_registry: 'Package registry: npm, pypi, cargo, etc.',
|
|
17
17
|
package_name: 'Package name on the registry (e.g., "@modelcontextprotocol/server-filesystem")',
|
|
18
18
|
recommended: 'Mark this server as recommended by PulseMCP',
|
|
19
|
+
verified_no_remote_canonicals: 'Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)',
|
|
19
20
|
created_on_override: 'Override the automatically derived created date (ISO date string, e.g., "2025-01-15")',
|
|
20
21
|
tags: 'Tags for the server. Replaces all existing tags when provided. Use tag slugs.',
|
|
21
22
|
canonical_urls: 'Authoritative URLs for the server. Replaces all existing canonical URLs when provided.',
|
|
@@ -25,8 +26,8 @@ const PARAM_DESCRIPTIONS = {
|
|
|
25
26
|
const CanonicalUrlSchema = z.object({
|
|
26
27
|
url: z.string().describe('The canonical URL'),
|
|
27
28
|
scope: z
|
|
28
|
-
.enum(['domain', 'subdomain', '
|
|
29
|
-
.describe('Scope of the canonical: domain, subdomain,
|
|
29
|
+
.enum(['domain', 'subdomain', 'url'])
|
|
30
|
+
.describe('Scope of the canonical: domain, subdomain, or url (exact match)'),
|
|
30
31
|
note: z.string().optional().describe('Optional note about this canonical URL'),
|
|
31
32
|
});
|
|
32
33
|
const RemoteEndpointSchema = z.object({
|
|
@@ -82,6 +83,10 @@ const UpdateMCPServerSchema = z.object({
|
|
|
82
83
|
package_registry: z.string().optional().describe(PARAM_DESCRIPTIONS.package_registry),
|
|
83
84
|
package_name: z.string().optional().describe(PARAM_DESCRIPTIONS.package_name),
|
|
84
85
|
recommended: z.boolean().optional().describe(PARAM_DESCRIPTIONS.recommended),
|
|
86
|
+
verified_no_remote_canonicals: z
|
|
87
|
+
.boolean()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe(PARAM_DESCRIPTIONS.verified_no_remote_canonicals),
|
|
85
90
|
created_on_override: z.string().optional().describe(PARAM_DESCRIPTIONS.created_on_override),
|
|
86
91
|
tags: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.tags),
|
|
87
92
|
canonical_urls: z
|
|
@@ -126,7 +131,7 @@ Providing canonical_urls replaces ALL existing canonical URLs:
|
|
|
126
131
|
{
|
|
127
132
|
"implementation_id": 456,
|
|
128
133
|
"canonical_urls": [
|
|
129
|
-
{ "url": "https://github.com/org/repo", "scope": "
|
|
134
|
+
{ "url": "https://github.com/org/repo", "scope": "domain" },
|
|
130
135
|
{ "url": "https://npmjs.com/package/name", "scope": "url" }
|
|
131
136
|
]
|
|
132
137
|
}
|
|
@@ -220,6 +225,10 @@ Create new provider:
|
|
|
220
225
|
package_registry: { type: 'string', description: PARAM_DESCRIPTIONS.package_registry },
|
|
221
226
|
package_name: { type: 'string', description: PARAM_DESCRIPTIONS.package_name },
|
|
222
227
|
recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
|
|
228
|
+
verified_no_remote_canonicals: {
|
|
229
|
+
type: 'boolean',
|
|
230
|
+
description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
|
|
231
|
+
},
|
|
223
232
|
created_on_override: {
|
|
224
233
|
type: 'string',
|
|
225
234
|
description: PARAM_DESCRIPTIONS.created_on_override,
|
|
@@ -237,7 +246,7 @@ Create new provider:
|
|
|
237
246
|
url: { type: 'string', description: 'The canonical URL' },
|
|
238
247
|
scope: {
|
|
239
248
|
type: 'string',
|
|
240
|
-
enum: ['domain', 'subdomain', '
|
|
249
|
+
enum: ['domain', 'subdomain', 'url'],
|
|
241
250
|
description: 'Scope of the canonical',
|
|
242
251
|
},
|
|
243
252
|
note: { type: 'string', description: 'Optional note' },
|
|
@@ -318,6 +327,9 @@ Create new provider:
|
|
|
318
327
|
if (server.recommended !== undefined) {
|
|
319
328
|
content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
|
|
320
329
|
}
|
|
330
|
+
if (server.verified_no_remote_canonicals !== undefined) {
|
|
331
|
+
content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
|
|
332
|
+
}
|
|
321
333
|
if (server.updated_at) {
|
|
322
334
|
content += `**Updated:** ${server.updated_at}\n`;
|
|
323
335
|
}
|
|
@@ -60,6 +60,7 @@ import { cleanupGoodJobs } from './tools/cleanup-good-jobs.js';
|
|
|
60
60
|
import { runExamForMirror } from './tools/run-exam-for-mirror.js';
|
|
61
61
|
import { getExamResult } from './tools/get-exam-result.js';
|
|
62
62
|
import { saveResultsForMirror } from './tools/save-results-for-mirror.js';
|
|
63
|
+
import { listProctorRuns } from './tools/list-proctor-runs.js';
|
|
63
64
|
// Discovered URLs tools
|
|
64
65
|
import { listDiscoveredUrls } from './tools/list-discovered-urls.js';
|
|
65
66
|
import { markDiscoveredUrlProcessed } from './tools/mark-discovered-url-processed.js';
|
|
@@ -230,6 +231,7 @@ const ALL_TOOLS = [
|
|
|
230
231
|
{ factory: runExamForMirror, groups: ['proctor'], isWriteOperation: true },
|
|
231
232
|
{ factory: getExamResult, groups: ['proctor'], isWriteOperation: false },
|
|
232
233
|
{ factory: saveResultsForMirror, groups: ['proctor'], isWriteOperation: true },
|
|
234
|
+
{ factory: listProctorRuns, groups: ['proctor'], isWriteOperation: false },
|
|
233
235
|
// Discovered URLs tools
|
|
234
236
|
{ factory: listDiscoveredUrls, groups: ['discovered_urls'], isWriteOperation: false },
|
|
235
237
|
{
|
|
@@ -367,7 +369,7 @@ function shouldIncludeTool(toolDef, enabledGroups) {
|
|
|
367
369
|
* - good_jobs: GoodJob background job management tools (read + write)
|
|
368
370
|
* - good_jobs_readonly: GoodJob tools (read only)
|
|
369
371
|
* - proctor: Proctor exam execution and result storage tools (read + write)
|
|
370
|
-
* - proctor_readonly: Proctor tools (read only - get_exam_result
|
|
372
|
+
* - proctor_readonly: Proctor tools (read only - get_exam_result and list_proctor_runs)
|
|
371
373
|
* - discovered_urls: Discovered URL management tools for processing URLs into MCP implementations (read + write)
|
|
372
374
|
* - discovered_urls_readonly: Discovered URL tools (read only - list and stats)
|
|
373
375
|
* - notifications: Notification email tools - send_impl_posted_notif (write-only, no readonly variant)
|
package/package.json
CHANGED
|
@@ -64,6 +64,9 @@ export async function createMCPImplementation(apiKey, baseUrl, params) {
|
|
|
64
64
|
if (params.recommended !== undefined) {
|
|
65
65
|
formData.append('mcp_implementation[recommended]', params.recommended.toString());
|
|
66
66
|
}
|
|
67
|
+
if (params.verified_no_remote_canonicals !== undefined) {
|
|
68
|
+
formData.append('mcp_implementation[verified_no_remote_canonicals]', params.verified_no_remote_canonicals.toString());
|
|
69
|
+
}
|
|
67
70
|
// Date overrides
|
|
68
71
|
if (params.created_on_override !== undefined) {
|
|
69
72
|
formData.append('mcp_implementation[created_on_override]', params.created_on_override);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProctorRunsResponse } from '../../types.js';
|
|
2
|
+
export declare function getProctorRuns(apiKey: string, baseUrl: string, params?: {
|
|
3
|
+
q?: string;
|
|
4
|
+
recommended?: boolean;
|
|
5
|
+
tenant_ids?: string;
|
|
6
|
+
sort?: 'slug' | 'name' | 'mirrors' | 'recommended' | 'tenants' | 'latest_tested' | 'last_auth_check' | 'last_tools_list';
|
|
7
|
+
direction?: 'asc' | 'desc';
|
|
8
|
+
limit?: number;
|
|
9
|
+
offset?: number;
|
|
10
|
+
}): Promise<ProctorRunsResponse>;
|
|
11
|
+
//# sourceMappingURL=get-proctor-runs.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export async function getProctorRuns(apiKey, baseUrl, params) {
|
|
2
|
+
const url = new URL('/api/proctor_runs', baseUrl);
|
|
3
|
+
if (params?.q) {
|
|
4
|
+
url.searchParams.append('q', params.q);
|
|
5
|
+
}
|
|
6
|
+
if (params?.recommended) {
|
|
7
|
+
url.searchParams.append('recommended', '1');
|
|
8
|
+
}
|
|
9
|
+
if (params?.tenant_ids) {
|
|
10
|
+
url.searchParams.append('tenant_ids', params.tenant_ids);
|
|
11
|
+
}
|
|
12
|
+
if (params?.sort) {
|
|
13
|
+
url.searchParams.append('sort', params.sort);
|
|
14
|
+
}
|
|
15
|
+
if (params?.direction) {
|
|
16
|
+
url.searchParams.append('direction', params.direction);
|
|
17
|
+
}
|
|
18
|
+
if (params?.limit) {
|
|
19
|
+
url.searchParams.append('limit', params.limit.toString());
|
|
20
|
+
}
|
|
21
|
+
if (params?.offset !== undefined && params.offset > 0) {
|
|
22
|
+
url.searchParams.append('offset', params.offset.toString());
|
|
23
|
+
}
|
|
24
|
+
const response = await fetch(url.toString(), {
|
|
25
|
+
method: 'GET',
|
|
26
|
+
headers: {
|
|
27
|
+
'X-API-Key': apiKey,
|
|
28
|
+
Accept: 'application/json',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
if (response.status === 401) {
|
|
33
|
+
throw new Error('Invalid API key');
|
|
34
|
+
}
|
|
35
|
+
if (response.status === 403) {
|
|
36
|
+
throw new Error('User lacks admin privileges');
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Failed to fetch proctor runs: ${response.status} ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
const data = (await response.json());
|
|
41
|
+
return {
|
|
42
|
+
runs: data.data.map((run) => ({
|
|
43
|
+
id: run.id,
|
|
44
|
+
slug: run.slug,
|
|
45
|
+
name: run.name,
|
|
46
|
+
recommended: run.recommended,
|
|
47
|
+
mirrors_count: run.mirrors_count,
|
|
48
|
+
tenant_count: run.tenant_count,
|
|
49
|
+
latest_version: run.latest_version,
|
|
50
|
+
latest_mirror_id: run.latest_mirror_id,
|
|
51
|
+
latest_mirror_name: run.latest_mirror_name,
|
|
52
|
+
latest_tested: run.latest_tested,
|
|
53
|
+
last_auth_check_days: run.last_auth_check_days,
|
|
54
|
+
last_tools_list_days: run.last_tools_list_days,
|
|
55
|
+
auth_types: run.auth_types,
|
|
56
|
+
num_tools: run.num_tools,
|
|
57
|
+
packages: run.packages,
|
|
58
|
+
remotes: run.remotes,
|
|
59
|
+
})),
|
|
60
|
+
pagination: {
|
|
61
|
+
current_page: data.meta.current_page,
|
|
62
|
+
total_pages: data.meta.total_pages,
|
|
63
|
+
total_count: data.meta.total_count,
|
|
64
|
+
has_next: data.meta.has_next,
|
|
65
|
+
limit: data.meta.limit,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -74,6 +74,9 @@ export async function saveMCPImplementation(apiKey, baseUrl, id, params) {
|
|
|
74
74
|
if (params.recommended !== undefined) {
|
|
75
75
|
formData.append('mcp_implementation[recommended]', params.recommended.toString());
|
|
76
76
|
}
|
|
77
|
+
if (params.verified_no_remote_canonicals !== undefined) {
|
|
78
|
+
formData.append('mcp_implementation[verified_no_remote_canonicals]', params.verified_no_remote_canonicals.toString());
|
|
79
|
+
}
|
|
77
80
|
// Date overrides
|
|
78
81
|
if (params.created_on_override !== undefined) {
|
|
79
82
|
formData.append('mcp_implementation[created_on_override]', params.created_on_override);
|
|
@@ -45,6 +45,8 @@ export interface RailsImplementation {
|
|
|
45
45
|
downloads_estimate_last_30_days?: number;
|
|
46
46
|
downloads_estimate_total?: number;
|
|
47
47
|
mcp_server_remotes_count?: number;
|
|
48
|
+
recommended?: boolean;
|
|
49
|
+
verified_no_remote_canonicals?: boolean;
|
|
48
50
|
tags?: MCPServerTag[];
|
|
49
51
|
remotes?: MCPServerRemote[];
|
|
50
52
|
created_at?: string;
|
|
@@ -47,6 +47,7 @@ export function mapToUnifiedServer(impl) {
|
|
|
47
47
|
package_name: impl.package_name,
|
|
48
48
|
// Flags
|
|
49
49
|
recommended: impl.recommended,
|
|
50
|
+
verified_no_remote_canonicals: mcpServer.verified_no_remote_canonicals,
|
|
50
51
|
// Canonical URLs
|
|
51
52
|
canonical_urls: impl.canonical,
|
|
52
53
|
// Remote endpoints
|
|
@@ -49,6 +49,8 @@ export async function updateUnifiedMCPServer(apiKey, baseUrl, implementationId,
|
|
|
49
49
|
// Flags
|
|
50
50
|
if (params.recommended !== undefined)
|
|
51
51
|
implParams.recommended = params.recommended;
|
|
52
|
+
if (params.verified_no_remote_canonicals !== undefined)
|
|
53
|
+
implParams.verified_no_remote_canonicals = params.verified_no_remote_canonicals;
|
|
52
54
|
// Date overrides
|
|
53
55
|
if (params.created_on_override !== undefined)
|
|
54
56
|
implParams.created_on_override = params.created_on_override;
|
|
@@ -958,6 +958,12 @@ export function createMockPulseMCPAdminClient(mockData) {
|
|
|
958
958
|
errors: [],
|
|
959
959
|
};
|
|
960
960
|
},
|
|
961
|
+
async getProctorRuns() {
|
|
962
|
+
return {
|
|
963
|
+
runs: [],
|
|
964
|
+
pagination: { current_page: 1, total_pages: 1, total_count: 0, has_next: false, limit: 30 },
|
|
965
|
+
};
|
|
966
|
+
},
|
|
961
967
|
// Discovered URL methods
|
|
962
968
|
async getDiscoveredUrls() {
|
|
963
969
|
return {
|
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, 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, DiscoveredUrlsResponse, MarkDiscoveredUrlProcessedParams, MarkDiscoveredUrlProcessedResponse, DiscoveredUrlStats } from './types.js';
|
|
3
3
|
export interface IPulseMCPAdminClient {
|
|
4
4
|
getPosts(params?: {
|
|
5
5
|
search?: string;
|
|
@@ -160,6 +160,7 @@ export interface IPulseMCPAdminClient {
|
|
|
160
160
|
}): Promise<GoodJobCleanupResponse>;
|
|
161
161
|
runExamForMirror(params: ProctorRunExamParams): Promise<ProctorRunExamResponse>;
|
|
162
162
|
saveResultsForMirror(params: ProctorSaveResultsParams): Promise<ProctorSaveResultsResponse>;
|
|
163
|
+
getProctorRuns(params?: GetProctorRunsParams): Promise<ProctorRunsResponse>;
|
|
163
164
|
getDiscoveredUrls(params?: {
|
|
164
165
|
status?: 'pending' | 'processed' | 'all';
|
|
165
166
|
page?: number;
|
|
@@ -331,6 +332,7 @@ export declare class PulseMCPAdminClient implements IPulseMCPAdminClient {
|
|
|
331
332
|
}): Promise<GoodJobCleanupResponse>;
|
|
332
333
|
runExamForMirror(params: ProctorRunExamParams): Promise<ProctorRunExamResponse>;
|
|
333
334
|
saveResultsForMirror(params: ProctorSaveResultsParams): Promise<ProctorSaveResultsResponse>;
|
|
335
|
+
getProctorRuns(params?: GetProctorRunsParams): Promise<ProctorRunsResponse>;
|
|
334
336
|
getDiscoveredUrls(params?: {
|
|
335
337
|
status?: 'pending' | 'processed' | 'all';
|
|
336
338
|
page?: number;
|
package/shared/server.js
CHANGED
|
@@ -261,6 +261,10 @@ export class PulseMCPAdminClient {
|
|
|
261
261
|
const { saveResultsForMirror } = await import('./pulsemcp-admin-client/lib/save-results-for-mirror.js');
|
|
262
262
|
return saveResultsForMirror(this.apiKey, this.baseUrl, params);
|
|
263
263
|
}
|
|
264
|
+
async getProctorRuns(params) {
|
|
265
|
+
const { getProctorRuns } = await import('./pulsemcp-admin-client/lib/get-proctor-runs.js');
|
|
266
|
+
return getProctorRuns(this.apiKey, this.baseUrl, params);
|
|
267
|
+
}
|
|
264
268
|
// Discovered URL REST API methods
|
|
265
269
|
async getDiscoveredUrls(params) {
|
|
266
270
|
const { getDiscoveredUrls } = await import('./pulsemcp-admin-client/lib/get-discovered-urls.js');
|
|
@@ -14,7 +14,7 @@ The response includes:
|
|
|
14
14
|
- **Basic info**: name, descriptions, status, classification, implementation language
|
|
15
15
|
- **Provider**: organization/person details
|
|
16
16
|
- **Source code location**: GitHub repository info with stars and last update
|
|
17
|
-
- **Canonical URLs**: authoritative URLs with scope (domain, subdomain,
|
|
17
|
+
- **Canonical URLs**: authoritative URLs with scope (domain, subdomain, url)
|
|
18
18
|
- **Remote endpoints**: all deployment endpoints with transport, platform, auth, and cost info
|
|
19
19
|
- **Tags**: categorization tags
|
|
20
20
|
- **Download statistics**: npm download estimates
|
|
@@ -48,7 +48,7 @@ Example response:
|
|
|
48
48
|
"github_stars": 5000
|
|
49
49
|
},
|
|
50
50
|
"canonical_urls": [
|
|
51
|
-
{ "url": "https://github.com/modelcontextprotocol/servers", "scope": "
|
|
51
|
+
{ "url": "https://github.com/modelcontextprotocol/servers", "scope": "domain" }
|
|
52
52
|
],
|
|
53
53
|
"remotes": [
|
|
54
54
|
{
|
|
@@ -102,6 +102,9 @@ Example response:
|
|
|
102
102
|
if (server.recommended !== undefined) {
|
|
103
103
|
content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
|
|
104
104
|
}
|
|
105
|
+
if (server.verified_no_remote_canonicals !== undefined) {
|
|
106
|
+
content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
|
|
107
|
+
}
|
|
105
108
|
if (server.short_description) {
|
|
106
109
|
content += `\n**Short Description:**\n${server.short_description}\n`;
|
|
107
110
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import type { ClientFactory } from '../server.js';
|
|
3
|
+
export declare function listProctorRuns(_server: Server, clientFactory: ClientFactory): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
q: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: "Search by server slug, implementation name, or mirror name";
|
|
12
|
+
};
|
|
13
|
+
recommended: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: "When true, filter to recommended servers only. When false or omitted, shows all servers";
|
|
16
|
+
};
|
|
17
|
+
tenant_ids: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: "Comma-separated tenant IDs to filter by (OR logic)";
|
|
20
|
+
};
|
|
21
|
+
sort: {
|
|
22
|
+
type: string;
|
|
23
|
+
enum: string[];
|
|
24
|
+
description: "Column to sort by: slug, name, mirrors, recommended, tenants, latest_tested, last_auth_check, last_tools_list";
|
|
25
|
+
};
|
|
26
|
+
direction: {
|
|
27
|
+
type: string;
|
|
28
|
+
enum: string[];
|
|
29
|
+
description: "Sort direction: asc or desc. Default: asc";
|
|
30
|
+
};
|
|
31
|
+
limit: {
|
|
32
|
+
type: string;
|
|
33
|
+
minimum: number;
|
|
34
|
+
maximum: number;
|
|
35
|
+
description: "Results per page, range 1-100. Default: 30";
|
|
36
|
+
};
|
|
37
|
+
offset: {
|
|
38
|
+
type: string;
|
|
39
|
+
minimum: number;
|
|
40
|
+
description: "Pagination offset. Default: 0";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
handler: (args: unknown) => Promise<{
|
|
45
|
+
content: {
|
|
46
|
+
type: string;
|
|
47
|
+
text: string;
|
|
48
|
+
}[];
|
|
49
|
+
isError?: undefined;
|
|
50
|
+
} | {
|
|
51
|
+
content: {
|
|
52
|
+
type: string;
|
|
53
|
+
text: string;
|
|
54
|
+
}[];
|
|
55
|
+
isError: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=list-proctor-runs.d.ts.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
q: 'Search by server slug, implementation name, or mirror name',
|
|
4
|
+
recommended: 'When true, filter to recommended servers only. When false or omitted, shows all servers',
|
|
5
|
+
tenant_ids: 'Comma-separated tenant IDs to filter by (OR logic)',
|
|
6
|
+
sort: 'Column to sort by: slug, name, mirrors, recommended, tenants, latest_tested, last_auth_check, last_tools_list',
|
|
7
|
+
direction: 'Sort direction: asc or desc. Default: asc',
|
|
8
|
+
limit: 'Results per page, range 1-100. Default: 30',
|
|
9
|
+
offset: 'Pagination offset. Default: 0',
|
|
10
|
+
};
|
|
11
|
+
const ListProctorRunsSchema = z.object({
|
|
12
|
+
q: z.string().optional().describe(PARAM_DESCRIPTIONS.q),
|
|
13
|
+
recommended: z.boolean().optional().describe(PARAM_DESCRIPTIONS.recommended),
|
|
14
|
+
tenant_ids: z.string().optional().describe(PARAM_DESCRIPTIONS.tenant_ids),
|
|
15
|
+
sort: z
|
|
16
|
+
.enum([
|
|
17
|
+
'slug',
|
|
18
|
+
'name',
|
|
19
|
+
'mirrors',
|
|
20
|
+
'recommended',
|
|
21
|
+
'tenants',
|
|
22
|
+
'latest_tested',
|
|
23
|
+
'last_auth_check',
|
|
24
|
+
'last_tools_list',
|
|
25
|
+
])
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(PARAM_DESCRIPTIONS.sort),
|
|
28
|
+
direction: z.enum(['asc', 'desc']).optional().describe(PARAM_DESCRIPTIONS.direction),
|
|
29
|
+
limit: z.number().min(1).max(100).optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
30
|
+
offset: z.number().min(0).optional().describe(PARAM_DESCRIPTIONS.offset),
|
|
31
|
+
});
|
|
32
|
+
export function listProctorRuns(_server, clientFactory) {
|
|
33
|
+
return {
|
|
34
|
+
name: 'list_proctor_runs',
|
|
35
|
+
description: `List proctor run summaries for MCP servers. Shows testing status across all servers including auth-check and tools-list exam results. Useful for identifying untested servers or servers that need re-testing.
|
|
36
|
+
|
|
37
|
+
Default sort order: untested servers first, then stalest auth check, then stalest tools list, then alphabetical by slug.
|
|
38
|
+
|
|
39
|
+
Example response:
|
|
40
|
+
{
|
|
41
|
+
"runs": [
|
|
42
|
+
{
|
|
43
|
+
"id": 123,
|
|
44
|
+
"slug": "some-server",
|
|
45
|
+
"name": "Some Server",
|
|
46
|
+
"recommended": true,
|
|
47
|
+
"mirrors_count": 2,
|
|
48
|
+
"latest_tested": true,
|
|
49
|
+
"last_auth_check_days": 2,
|
|
50
|
+
"last_tools_list_days": 3,
|
|
51
|
+
"auth_types": ["oauth2"],
|
|
52
|
+
"num_tools": 5,
|
|
53
|
+
"packages": ["npm"],
|
|
54
|
+
"remotes": ["streamable-http"]
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"pagination": { "current_page": 1, "total_pages": 2, "total_count": 45, "has_next": true }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Use cases:
|
|
61
|
+
- Find servers that haven't been tested yet (untested appear first by default)
|
|
62
|
+
- Check which servers have stale proctor results
|
|
63
|
+
- Filter to recommended servers to prioritize testing
|
|
64
|
+
- Search for a specific server's testing status`,
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
q: { type: 'string', description: PARAM_DESCRIPTIONS.q },
|
|
69
|
+
recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
|
|
70
|
+
tenant_ids: { type: 'string', description: PARAM_DESCRIPTIONS.tenant_ids },
|
|
71
|
+
sort: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
enum: [
|
|
74
|
+
'slug',
|
|
75
|
+
'name',
|
|
76
|
+
'mirrors',
|
|
77
|
+
'recommended',
|
|
78
|
+
'tenants',
|
|
79
|
+
'latest_tested',
|
|
80
|
+
'last_auth_check',
|
|
81
|
+
'last_tools_list',
|
|
82
|
+
],
|
|
83
|
+
description: PARAM_DESCRIPTIONS.sort,
|
|
84
|
+
},
|
|
85
|
+
direction: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
enum: ['asc', 'desc'],
|
|
88
|
+
description: PARAM_DESCRIPTIONS.direction,
|
|
89
|
+
},
|
|
90
|
+
limit: { type: 'number', minimum: 1, maximum: 100, description: PARAM_DESCRIPTIONS.limit },
|
|
91
|
+
offset: { type: 'number', minimum: 0, description: PARAM_DESCRIPTIONS.offset },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
handler: async (args) => {
|
|
95
|
+
const validatedArgs = ListProctorRunsSchema.parse(args);
|
|
96
|
+
const client = clientFactory();
|
|
97
|
+
try {
|
|
98
|
+
const response = await client.getProctorRuns({
|
|
99
|
+
q: validatedArgs.q,
|
|
100
|
+
recommended: validatedArgs.recommended,
|
|
101
|
+
tenant_ids: validatedArgs.tenant_ids,
|
|
102
|
+
sort: validatedArgs.sort,
|
|
103
|
+
direction: validatedArgs.direction,
|
|
104
|
+
limit: validatedArgs.limit,
|
|
105
|
+
offset: validatedArgs.offset,
|
|
106
|
+
});
|
|
107
|
+
let content = `Found ${response.runs.length} proctor run summaries`;
|
|
108
|
+
if (response.pagination) {
|
|
109
|
+
content += ` (page ${response.pagination.current_page} of ${response.pagination.total_pages}, total: ${response.pagination.total_count})`;
|
|
110
|
+
}
|
|
111
|
+
content += ':\n\n';
|
|
112
|
+
for (const [index, run] of response.runs.entries()) {
|
|
113
|
+
content += `${index + 1}. **${run.slug}**`;
|
|
114
|
+
if (run.name) {
|
|
115
|
+
content += ` — ${run.name}`;
|
|
116
|
+
}
|
|
117
|
+
content += ` (ID: ${run.id})\n`;
|
|
118
|
+
if (run.recommended) {
|
|
119
|
+
content += ` Recommended: yes\n`;
|
|
120
|
+
}
|
|
121
|
+
content += ` Mirrors: ${run.mirrors_count}, Tenants: ${run.tenant_count}\n`;
|
|
122
|
+
if (run.latest_version) {
|
|
123
|
+
content += ` Latest Version: ${run.latest_version}`;
|
|
124
|
+
if (run.latest_mirror_name) {
|
|
125
|
+
content += ` (mirror: ${run.latest_mirror_name}, ID: ${run.latest_mirror_id})`;
|
|
126
|
+
}
|
|
127
|
+
content += '\n';
|
|
128
|
+
}
|
|
129
|
+
content += ` Latest Tested: ${run.latest_tested ? 'yes' : 'no'}\n`;
|
|
130
|
+
if (run.last_auth_check_days !== null) {
|
|
131
|
+
content += ` Last Auth Check: ${run.last_auth_check_days} day(s) ago\n`;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
content += ` Last Auth Check: never\n`;
|
|
135
|
+
}
|
|
136
|
+
if (run.last_tools_list_days !== null) {
|
|
137
|
+
content += ` Last Tools List: ${run.last_tools_list_days} day(s) ago\n`;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
content += ` Last Tools List: never\n`;
|
|
141
|
+
}
|
|
142
|
+
if (run.auth_types.length > 0) {
|
|
143
|
+
content += ` Auth Types: ${run.auth_types.join(', ')}\n`;
|
|
144
|
+
}
|
|
145
|
+
if (run.num_tools !== null) {
|
|
146
|
+
content += ` Num Tools: ${run.num_tools}\n`;
|
|
147
|
+
}
|
|
148
|
+
if (run.packages.length > 0) {
|
|
149
|
+
content += ` Packages: ${run.packages.join(', ')}\n`;
|
|
150
|
+
}
|
|
151
|
+
if (run.remotes.length > 0) {
|
|
152
|
+
content += ` Remotes: ${run.remotes.join(', ')}\n`;
|
|
153
|
+
}
|
|
154
|
+
content += '\n';
|
|
155
|
+
}
|
|
156
|
+
return { content: [{ type: 'text', text: content.trim() }] };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: 'text',
|
|
163
|
+
text: `Error fetching proctor runs: ${error instanceof Error ? error.message : String(error)}`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
isError: true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -149,7 +149,11 @@ export declare function saveMCPImplementation(_server: Server, clientFactory: Cl
|
|
|
149
149
|
};
|
|
150
150
|
required: string[];
|
|
151
151
|
};
|
|
152
|
-
description: "Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of \"domain\", \"subdomain\",
|
|
152
|
+
description: "Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of \"domain\", \"subdomain\", or \"url\"), and optional note for additional context.";
|
|
153
|
+
};
|
|
154
|
+
verified_no_remote_canonicals: {
|
|
155
|
+
type: string;
|
|
156
|
+
description: "Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)";
|
|
153
157
|
};
|
|
154
158
|
internal_notes: {
|
|
155
159
|
type: string;
|
|
@@ -26,7 +26,9 @@ const PARAM_DESCRIPTIONS = {
|
|
|
26
26
|
// Remote endpoints
|
|
27
27
|
remote: 'Array of remote endpoint configurations for MCP servers. Each remote can have: id (existing remote ID or blank for new), url_direct, url_setup, transport (e.g., "sse"), host_platform (e.g., "smithery"), host_infrastructure (e.g., "cloudflare"), authentication_method (e.g., "open"), cost (e.g., "free"), status (defaults to "live"), display_name, and internal_notes.',
|
|
28
28
|
// Canonical URLs
|
|
29
|
-
canonical: 'Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain",
|
|
29
|
+
canonical: 'Array of canonical URL configurations. Each entry must have: url (the canonical URL), scope (one of "domain", "subdomain", or "url"), and optional note for additional context.',
|
|
30
|
+
// Flags
|
|
31
|
+
verified_no_remote_canonicals: 'Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)',
|
|
30
32
|
// Other fields
|
|
31
33
|
internal_notes: 'Admin-only notes. Not displayed publicly. Used for tracking submission sources, reviewer comments, etc.',
|
|
32
34
|
};
|
|
@@ -83,11 +85,16 @@ const SaveMCPImplementationSchema = z.object({
|
|
|
83
85
|
canonical: z
|
|
84
86
|
.array(z.object({
|
|
85
87
|
url: z.string(),
|
|
86
|
-
scope: z.enum(['domain', 'subdomain', '
|
|
88
|
+
scope: z.enum(['domain', 'subdomain', 'url']),
|
|
87
89
|
note: z.string().optional(),
|
|
88
90
|
}))
|
|
89
91
|
.optional()
|
|
90
92
|
.describe(PARAM_DESCRIPTIONS.canonical),
|
|
93
|
+
// Flags
|
|
94
|
+
verified_no_remote_canonicals: z
|
|
95
|
+
.boolean()
|
|
96
|
+
.optional()
|
|
97
|
+
.describe(PARAM_DESCRIPTIONS.verified_no_remote_canonicals),
|
|
91
98
|
// Other fields
|
|
92
99
|
internal_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.internal_notes),
|
|
93
100
|
});
|
|
@@ -294,13 +301,18 @@ Use cases:
|
|
|
294
301
|
type: 'object',
|
|
295
302
|
properties: {
|
|
296
303
|
url: { type: 'string' },
|
|
297
|
-
scope: { type: 'string', enum: ['domain', 'subdomain', '
|
|
304
|
+
scope: { type: 'string', enum: ['domain', 'subdomain', 'url'] },
|
|
298
305
|
note: { type: 'string' },
|
|
299
306
|
},
|
|
300
307
|
required: ['url', 'scope'],
|
|
301
308
|
},
|
|
302
309
|
description: PARAM_DESCRIPTIONS.canonical,
|
|
303
310
|
},
|
|
311
|
+
// Flags
|
|
312
|
+
verified_no_remote_canonicals: {
|
|
313
|
+
type: 'boolean',
|
|
314
|
+
description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
|
|
315
|
+
},
|
|
304
316
|
// Other fields
|
|
305
317
|
internal_notes: {
|
|
306
318
|
type: 'string',
|
|
@@ -88,6 +88,10 @@ export declare function updateMCPServer(_server: Server, clientFactory: ClientFa
|
|
|
88
88
|
type: string;
|
|
89
89
|
description: "Mark this server as recommended by PulseMCP";
|
|
90
90
|
};
|
|
91
|
+
verified_no_remote_canonicals: {
|
|
92
|
+
type: string;
|
|
93
|
+
description: "Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)";
|
|
94
|
+
};
|
|
91
95
|
created_on_override: {
|
|
92
96
|
type: string;
|
|
93
97
|
description: "Override the automatically derived created date (ISO date string, e.g., \"2025-01-15\")";
|
|
@@ -16,6 +16,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
16
16
|
package_registry: 'Package registry: npm, pypi, cargo, etc.',
|
|
17
17
|
package_name: 'Package name on the registry (e.g., "@modelcontextprotocol/server-filesystem")',
|
|
18
18
|
recommended: 'Mark this server as recommended by PulseMCP',
|
|
19
|
+
verified_no_remote_canonicals: 'Mark that this server has been verified to have no remote canonical URLs (true = verified no remote canonicals exist, false = reset/canonicals found)',
|
|
19
20
|
created_on_override: 'Override the automatically derived created date (ISO date string, e.g., "2025-01-15")',
|
|
20
21
|
tags: 'Tags for the server. Replaces all existing tags when provided. Use tag slugs.',
|
|
21
22
|
canonical_urls: 'Authoritative URLs for the server. Replaces all existing canonical URLs when provided.',
|
|
@@ -25,8 +26,8 @@ const PARAM_DESCRIPTIONS = {
|
|
|
25
26
|
const CanonicalUrlSchema = z.object({
|
|
26
27
|
url: z.string().describe('The canonical URL'),
|
|
27
28
|
scope: z
|
|
28
|
-
.enum(['domain', 'subdomain', '
|
|
29
|
-
.describe('Scope of the canonical: domain, subdomain,
|
|
29
|
+
.enum(['domain', 'subdomain', 'url'])
|
|
30
|
+
.describe('Scope of the canonical: domain, subdomain, or url (exact match)'),
|
|
30
31
|
note: z.string().optional().describe('Optional note about this canonical URL'),
|
|
31
32
|
});
|
|
32
33
|
const RemoteEndpointSchema = z.object({
|
|
@@ -82,6 +83,10 @@ const UpdateMCPServerSchema = z.object({
|
|
|
82
83
|
package_registry: z.string().optional().describe(PARAM_DESCRIPTIONS.package_registry),
|
|
83
84
|
package_name: z.string().optional().describe(PARAM_DESCRIPTIONS.package_name),
|
|
84
85
|
recommended: z.boolean().optional().describe(PARAM_DESCRIPTIONS.recommended),
|
|
86
|
+
verified_no_remote_canonicals: z
|
|
87
|
+
.boolean()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe(PARAM_DESCRIPTIONS.verified_no_remote_canonicals),
|
|
85
90
|
created_on_override: z.string().optional().describe(PARAM_DESCRIPTIONS.created_on_override),
|
|
86
91
|
tags: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.tags),
|
|
87
92
|
canonical_urls: z
|
|
@@ -126,7 +131,7 @@ Providing canonical_urls replaces ALL existing canonical URLs:
|
|
|
126
131
|
{
|
|
127
132
|
"implementation_id": 456,
|
|
128
133
|
"canonical_urls": [
|
|
129
|
-
{ "url": "https://github.com/org/repo", "scope": "
|
|
134
|
+
{ "url": "https://github.com/org/repo", "scope": "domain" },
|
|
130
135
|
{ "url": "https://npmjs.com/package/name", "scope": "url" }
|
|
131
136
|
]
|
|
132
137
|
}
|
|
@@ -220,6 +225,10 @@ Create new provider:
|
|
|
220
225
|
package_registry: { type: 'string', description: PARAM_DESCRIPTIONS.package_registry },
|
|
221
226
|
package_name: { type: 'string', description: PARAM_DESCRIPTIONS.package_name },
|
|
222
227
|
recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
|
|
228
|
+
verified_no_remote_canonicals: {
|
|
229
|
+
type: 'boolean',
|
|
230
|
+
description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
|
|
231
|
+
},
|
|
223
232
|
created_on_override: {
|
|
224
233
|
type: 'string',
|
|
225
234
|
description: PARAM_DESCRIPTIONS.created_on_override,
|
|
@@ -237,7 +246,7 @@ Create new provider:
|
|
|
237
246
|
url: { type: 'string', description: 'The canonical URL' },
|
|
238
247
|
scope: {
|
|
239
248
|
type: 'string',
|
|
240
|
-
enum: ['domain', 'subdomain', '
|
|
249
|
+
enum: ['domain', 'subdomain', 'url'],
|
|
241
250
|
description: 'Scope of the canonical',
|
|
242
251
|
},
|
|
243
252
|
note: { type: 'string', description: 'Optional note' },
|
|
@@ -318,6 +327,9 @@ Create new provider:
|
|
|
318
327
|
if (server.recommended !== undefined) {
|
|
319
328
|
content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
|
|
320
329
|
}
|
|
330
|
+
if (server.verified_no_remote_canonicals !== undefined) {
|
|
331
|
+
content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
|
|
332
|
+
}
|
|
321
333
|
if (server.updated_at) {
|
|
322
334
|
content += `**Updated:** ${server.updated_at}\n`;
|
|
323
335
|
}
|
package/shared/tools.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { ClientFactory } from './server.js';
|
|
|
23
23
|
* - mcp_servers / mcp_servers_readonly: Unified MCP server tools (abstracted interface)
|
|
24
24
|
* - redirects / redirects_readonly: URL redirect management tools
|
|
25
25
|
* - good_jobs / good_jobs_readonly: GoodJob background job management tools
|
|
26
|
-
* - proctor / proctor_readonly: Proctor exam execution and result storage tools. The readonly variant includes get_exam_result for retrieving stored results without running exams or saving
|
|
26
|
+
* - proctor / proctor_readonly: Proctor exam execution and result storage tools. The readonly variant includes get_exam_result and list_proctor_runs for retrieving stored results without running exams or saving
|
|
27
27
|
* - discovered_urls / discovered_urls_readonly: Discovered URL management tools for processing URLs into MCP implementations
|
|
28
28
|
* - notifications: Notification email tools (send_impl_posted_notif). Separated from server_directory so notification capability can be granted independently.
|
|
29
29
|
*/
|
|
@@ -69,7 +69,7 @@ export declare function parseEnabledToolGroups(enabledGroupsParam?: string): Too
|
|
|
69
69
|
* - good_jobs: GoodJob background job management tools (read + write)
|
|
70
70
|
* - good_jobs_readonly: GoodJob tools (read only)
|
|
71
71
|
* - proctor: Proctor exam execution and result storage tools (read + write)
|
|
72
|
-
* - proctor_readonly: Proctor tools (read only - get_exam_result
|
|
72
|
+
* - proctor_readonly: Proctor tools (read only - get_exam_result and list_proctor_runs)
|
|
73
73
|
* - discovered_urls: Discovered URL management tools for processing URLs into MCP implementations (read + write)
|
|
74
74
|
* - discovered_urls_readonly: Discovered URL tools (read only - list and stats)
|
|
75
75
|
* - notifications: Notification email tools - send_impl_posted_notif (write-only, no readonly variant)
|
package/shared/tools.js
CHANGED
|
@@ -60,6 +60,7 @@ import { cleanupGoodJobs } from './tools/cleanup-good-jobs.js';
|
|
|
60
60
|
import { runExamForMirror } from './tools/run-exam-for-mirror.js';
|
|
61
61
|
import { getExamResult } from './tools/get-exam-result.js';
|
|
62
62
|
import { saveResultsForMirror } from './tools/save-results-for-mirror.js';
|
|
63
|
+
import { listProctorRuns } from './tools/list-proctor-runs.js';
|
|
63
64
|
// Discovered URLs tools
|
|
64
65
|
import { listDiscoveredUrls } from './tools/list-discovered-urls.js';
|
|
65
66
|
import { markDiscoveredUrlProcessed } from './tools/mark-discovered-url-processed.js';
|
|
@@ -230,6 +231,7 @@ const ALL_TOOLS = [
|
|
|
230
231
|
{ factory: runExamForMirror, groups: ['proctor'], isWriteOperation: true },
|
|
231
232
|
{ factory: getExamResult, groups: ['proctor'], isWriteOperation: false },
|
|
232
233
|
{ factory: saveResultsForMirror, groups: ['proctor'], isWriteOperation: true },
|
|
234
|
+
{ factory: listProctorRuns, groups: ['proctor'], isWriteOperation: false },
|
|
233
235
|
// Discovered URLs tools
|
|
234
236
|
{ factory: listDiscoveredUrls, groups: ['discovered_urls'], isWriteOperation: false },
|
|
235
237
|
{
|
|
@@ -367,7 +369,7 @@ function shouldIncludeTool(toolDef, enabledGroups) {
|
|
|
367
369
|
* - good_jobs: GoodJob background job management tools (read + write)
|
|
368
370
|
* - good_jobs_readonly: GoodJob tools (read only)
|
|
369
371
|
* - proctor: Proctor exam execution and result storage tools (read + write)
|
|
370
|
-
* - proctor_readonly: Proctor tools (read only - get_exam_result
|
|
372
|
+
* - proctor_readonly: Proctor tools (read only - get_exam_result and list_proctor_runs)
|
|
371
373
|
* - discovered_urls: Discovered URL management tools for processing URLs into MCP implementations (read + write)
|
|
372
374
|
* - discovered_urls_readonly: Discovered URL tools (read only - list and stats)
|
|
373
375
|
* - notifications: Notification email tools - send_impl_posted_notif (write-only, no readonly variant)
|
package/shared/types.d.ts
CHANGED
|
@@ -105,7 +105,7 @@ export interface RemoteEndpointParams {
|
|
|
105
105
|
}
|
|
106
106
|
export interface CanonicalUrlParams {
|
|
107
107
|
url: string;
|
|
108
|
-
scope: 'domain' | 'subdomain' | '
|
|
108
|
+
scope: 'domain' | 'subdomain' | 'url';
|
|
109
109
|
note?: string;
|
|
110
110
|
}
|
|
111
111
|
export interface MCPServer {
|
|
@@ -124,6 +124,8 @@ export interface MCPServer {
|
|
|
124
124
|
downloads_estimate_last_four_weeks?: number;
|
|
125
125
|
visitors_estimate_total?: number;
|
|
126
126
|
mcp_server_remotes_count?: number;
|
|
127
|
+
recommended?: boolean;
|
|
128
|
+
verified_no_remote_canonicals?: boolean;
|
|
127
129
|
tags?: MCPServerTag[];
|
|
128
130
|
remotes?: MCPServerRemote[];
|
|
129
131
|
}
|
|
@@ -203,6 +205,7 @@ export interface SaveMCPImplementationParams {
|
|
|
203
205
|
package_registry?: string;
|
|
204
206
|
package_name?: string;
|
|
205
207
|
recommended?: boolean;
|
|
208
|
+
verified_no_remote_canonicals?: boolean;
|
|
206
209
|
created_on_override?: string;
|
|
207
210
|
tags?: string[];
|
|
208
211
|
remote?: RemoteEndpointParams[];
|
|
@@ -238,6 +241,7 @@ export interface CreateMCPImplementationParams {
|
|
|
238
241
|
package_registry?: string;
|
|
239
242
|
package_name?: string;
|
|
240
243
|
recommended?: boolean;
|
|
244
|
+
verified_no_remote_canonicals?: boolean;
|
|
241
245
|
created_on_override?: string;
|
|
242
246
|
tags?: string[];
|
|
243
247
|
remote?: RemoteEndpointParams[];
|
|
@@ -515,6 +519,7 @@ export interface UnifiedMCPServer {
|
|
|
515
519
|
package_registry?: string;
|
|
516
520
|
package_name?: string;
|
|
517
521
|
recommended?: boolean;
|
|
522
|
+
verified_no_remote_canonicals?: boolean;
|
|
518
523
|
canonical_urls?: CanonicalUrl[];
|
|
519
524
|
remotes?: RemoteEndpoint[];
|
|
520
525
|
tags?: MCPServerTag[];
|
|
@@ -564,6 +569,7 @@ export interface UpdateUnifiedMCPServerParams {
|
|
|
564
569
|
package_registry?: string;
|
|
565
570
|
package_name?: string;
|
|
566
571
|
recommended?: boolean;
|
|
572
|
+
verified_no_remote_canonicals?: boolean;
|
|
567
573
|
created_on_override?: string;
|
|
568
574
|
tags?: string[];
|
|
569
575
|
canonical_urls?: CanonicalUrl[];
|
|
@@ -691,6 +697,43 @@ export interface ProctorSaveResultsResponse {
|
|
|
691
697
|
error: string;
|
|
692
698
|
}>;
|
|
693
699
|
}
|
|
700
|
+
export interface ProctorRun {
|
|
701
|
+
id: number;
|
|
702
|
+
slug: string;
|
|
703
|
+
name: string | null;
|
|
704
|
+
recommended: boolean;
|
|
705
|
+
mirrors_count: number;
|
|
706
|
+
tenant_count: number;
|
|
707
|
+
latest_version: string | null;
|
|
708
|
+
latest_mirror_id: number | null;
|
|
709
|
+
latest_mirror_name: string | null;
|
|
710
|
+
latest_tested: boolean;
|
|
711
|
+
last_auth_check_days: number | null;
|
|
712
|
+
last_tools_list_days: number | null;
|
|
713
|
+
auth_types: string[];
|
|
714
|
+
num_tools: number | null;
|
|
715
|
+
packages: string[];
|
|
716
|
+
remotes: string[];
|
|
717
|
+
}
|
|
718
|
+
export interface ProctorRunsResponse {
|
|
719
|
+
runs: ProctorRun[];
|
|
720
|
+
pagination?: {
|
|
721
|
+
current_page: number;
|
|
722
|
+
total_pages: number;
|
|
723
|
+
total_count: number;
|
|
724
|
+
has_next?: boolean;
|
|
725
|
+
limit?: number;
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
export interface GetProctorRunsParams {
|
|
729
|
+
q?: string;
|
|
730
|
+
recommended?: boolean;
|
|
731
|
+
tenant_ids?: string;
|
|
732
|
+
sort?: 'slug' | 'name' | 'mirrors' | 'recommended' | 'tenants' | 'latest_tested' | 'last_auth_check' | 'last_tools_list';
|
|
733
|
+
direction?: 'asc' | 'desc';
|
|
734
|
+
limit?: number;
|
|
735
|
+
offset?: number;
|
|
736
|
+
}
|
|
694
737
|
export type DiscoveredUrlResult = 'posted' | 'skipped' | 'rejected' | 'error';
|
|
695
738
|
export interface DiscoveredUrl {
|
|
696
739
|
id: number;
|