pulsemcp-cms-admin-mcp-server 0.9.1 → 0.9.5

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.
@@ -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);
@@ -56,6 +56,8 @@ export async function getProctorRuns(apiKey, baseUrl, params) {
56
56
  num_tools: run.num_tools,
57
57
  packages: run.packages,
58
58
  remotes: run.remotes,
59
+ known_missing_init_tools_list: run.known_missing_init_tools_list || false,
60
+ known_missing_auth_check: run.known_missing_auth_check || false,
59
61
  })),
60
62
  pagination: {
61
63
  current_page: data.meta.current_page,
@@ -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;
@@ -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
  }
@@ -151,6 +151,12 @@ Use cases:
151
151
  if (run.remotes.length > 0) {
152
152
  content += ` Remotes: ${run.remotes.join(', ')}\n`;
153
153
  }
154
+ if (run.known_missing_init_tools_list) {
155
+ content += ` Known Missing Init Tools List: yes\n`;
156
+ }
157
+ if (run.known_missing_auth_check) {
158
+ content += ` Known Missing Auth Check: yes\n`;
159
+ }
154
160
  content += '\n';
155
161
  }
156
162
  return { content: [{ type: 'text', text: content.trim() }] };
@@ -24,9 +24,11 @@ const PARAM_DESCRIPTIONS = {
24
24
  github_repo: 'GitHub repository name (without owner prefix).',
25
25
  github_subfolder: 'Subfolder path within the repository, for monorepos. Omit for root-level projects.',
26
26
  // Remote endpoints
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.',
27
+ remote: 'Array of remote endpoint configurations for MCP servers. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all. 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", or "url"), and optional note for additional context.',
29
+ canonical: 'Array of canonical URL configurations. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all. 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
  };
@@ -88,6 +90,11 @@ const SaveMCPImplementationSchema = z.object({
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
  });
@@ -99,14 +106,17 @@ export function saveMCPImplementation(_server, clientFactory) {
99
106
  **Creating a new implementation:**
100
107
  - Omit the \`id\` field to create a new implementation
101
108
  - Required fields for creation: \`name\`, \`type\` (either "server" or "client")
109
+ - Remote endpoints and canonical URLs CAN be included on creation
102
110
 
103
111
  **Updating an existing implementation:**
104
112
  - Provide the \`id\` field to update an existing implementation
105
- - Only provided fields will be updated; omitted fields remain unchanged
113
+ - Only provided fields will be updated; **omitted fields remain unchanged**
114
+ - Omitting \`remote\` or \`canonical\` leaves existing values unchanged (does NOT clear them)
115
+ - Providing \`remote: []\` or \`canonical: []\` (empty array) will DELETE all existing entries
106
116
 
107
117
  All business logic from the Rails controller is applied (validation, associations, callbacks).
108
118
 
109
- Example request (CREATE new implementation):
119
+ Example request (CREATE with remote endpoints and canonical URLs):
110
120
  {
111
121
  "name": "My New MCP Server",
112
122
  "type": "server",
@@ -114,7 +124,19 @@ Example request (CREATE new implementation):
114
124
  "classification": "community",
115
125
  "implementation_language": "typescript",
116
126
  "github_owner": "myorg",
117
- "github_repo": "my-mcp-server"
127
+ "github_repo": "my-mcp-server",
128
+ "remote": [
129
+ {
130
+ "url_direct": "https://api.example.com/mcp",
131
+ "transport": "sse",
132
+ "host_platform": "smithery",
133
+ "authentication_method": "open",
134
+ "cost": "free"
135
+ }
136
+ ],
137
+ "canonical": [
138
+ { "url": "https://github.com/myorg/my-mcp-server", "scope": "url" }
139
+ ]
118
140
  }
119
141
 
120
142
  Example request (UPDATE existing implementation):
@@ -127,7 +149,7 @@ Example request (UPDATE existing implementation):
127
149
  "implementation_language": "typescript"
128
150
  }
129
151
 
130
- Example request (with remote endpoints - new remote):
152
+ Example request (UPDATE with remote endpoints - replaces ALL existing remotes):
131
153
  {
132
154
  "id": 11371,
133
155
  "remote": [
@@ -144,21 +166,12 @@ Example request (with remote endpoints - new remote):
144
166
  ]
145
167
  }
146
168
 
147
- Example response:
148
- {
149
- "id": 11371,
150
- "name": "GitHub MCP Server",
151
- "slug": "github-mcp-server",
152
- "type": "server",
153
- "status": "live",
154
- "classification": "official",
155
- "updated_at": "2024-01-20T16:30:00Z"
156
- }
157
-
158
169
  Important notes:
159
170
  - Omit \`id\` to CREATE, provide \`id\` to UPDATE
160
171
  - When creating: \`name\` and \`type\` are required
161
172
  - When updating: only provided fields will be changed
173
+ - **Omission semantics:** omitting \`remote\` or \`canonical\` leaves them unchanged. To clear them, pass an empty array.
174
+ - Providing \`remote\` or \`canonical\` replaces ALL existing entries (not a merge)
162
175
  - CREATE-ONLY restrictions:
163
176
  - \`github_stars\` is read-only (derived from GitHub repository)
164
177
  - \`mcp_server_id\`/\`mcp_client_id\` are created automatically based on \`type\`
@@ -167,17 +180,7 @@ Important notes:
167
180
  - Setting mcp_server_id or mcp_client_id to null will unlink the association (UPDATE only)
168
181
  - Remote endpoints are for MCP servers only and configure how they can be accessed
169
182
  - Canonical URLs help identify the authoritative source for the implementation
170
-
171
- Use cases:
172
- - Create new MCP implementation entries
173
- - Update draft implementations before publishing
174
- - Change implementation status (draft → live, live → archived)
175
- - Update metadata (stars, language, classification)
176
- - Link or unlink MCP server/client associations
177
- - Update descriptions and documentation
178
- - Modify URLs and provider information
179
- - Add or update remote endpoint configurations for servers
180
- - Set canonical URLs for implementations`,
183
+ - After creating/updating, use \`get_mcp_server\` to verify the full state including remotes and canonical URLs`,
181
184
  inputSchema: {
182
185
  type: 'object',
183
186
  properties: {
@@ -301,6 +304,11 @@ Use cases:
301
304
  },
302
305
  description: PARAM_DESCRIPTIONS.canonical,
303
306
  },
307
+ // Flags
308
+ verified_no_remote_canonicals: {
309
+ type: 'boolean',
310
+ description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
311
+ },
304
312
  // Other fields
305
313
  internal_notes: {
306
314
  type: 'string',
@@ -365,9 +373,25 @@ Use cases:
365
373
  if (implementation.url) {
366
374
  content += `**URL:** ${implementation.url}\n`;
367
375
  }
376
+ if (implementation.canonical && implementation.canonical.length > 0) {
377
+ content += `**Canonical URLs:** ${implementation.canonical.length}\n`;
378
+ implementation.canonical.forEach((c) => {
379
+ content += ` - ${c.url} (${c.scope})\n`;
380
+ });
381
+ }
382
+ const remotes = implementation.mcp_server?.remotes;
383
+ if (remotes && remotes.length > 0) {
384
+ content += `**Remote Endpoints:** ${remotes.length}\n`;
385
+ remotes.forEach((r) => {
386
+ content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
387
+ });
388
+ }
368
389
  if (implementation.created_at) {
369
390
  content += `**Created:** ${new Date(implementation.created_at).toLocaleDateString()}\n`;
370
391
  }
392
+ if (implementation.type === 'server') {
393
+ content += `\n**Tip:** Use \`get_mcp_server\` with slug \`${implementation.slug}\` to verify the full state including remotes and canonical URLs.\n`;
394
+ }
371
395
  return {
372
396
  content: [
373
397
  {
@@ -422,6 +446,19 @@ Use cases:
422
446
  if (implementation.mcp_client_id) {
423
447
  content += `**Linked MCP Client ID:** ${implementation.mcp_client_id}\n`;
424
448
  }
449
+ if (implementation.canonical && implementation.canonical.length > 0) {
450
+ content += `**Canonical URLs:** ${implementation.canonical.length}\n`;
451
+ implementation.canonical.forEach((c) => {
452
+ content += ` - ${c.url} (${c.scope})\n`;
453
+ });
454
+ }
455
+ const updateRemotes = implementation.mcp_server?.remotes;
456
+ if (updateRemotes && updateRemotes.length > 0) {
457
+ content += `**Remote Endpoints:** ${updateRemotes.length}\n`;
458
+ updateRemotes.forEach((r) => {
459
+ content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
460
+ });
461
+ }
425
462
  if (implementation.updated_at) {
426
463
  content += `**Updated:** ${new Date(implementation.updated_at).toLocaleDateString()}\n`;
427
464
  }
@@ -16,10 +16,11 @@ 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
- tags: 'Tags for the server. Replaces all existing tags when provided. Use tag slugs.',
21
- canonical_urls: 'Authoritative URLs for the server. Replaces all existing canonical URLs when provided.',
22
- remotes: 'Remote endpoints for the server. Replaces all existing remotes when provided.',
21
+ tags: 'Tags for the server. Providing this replaces ALL existing tags. Omitting leaves them unchanged. Pass an empty array to delete all. Use tag slugs.',
22
+ canonical_urls: 'Authoritative URLs for the server. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all.',
23
+ remotes: 'Remote endpoints for the server. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all.',
23
24
  internal_notes: 'Admin-only internal notes',
24
25
  };
25
26
  const CanonicalUrlSchema = 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
@@ -94,10 +99,15 @@ const UpdateMCPServerSchema = z.object({
94
99
  export function updateMCPServer(_server, clientFactory) {
95
100
  return {
96
101
  name: 'update_mcp_server',
97
- description: `Update an MCP server's information. Only provided fields will be updated.
102
+ description: `Update an MCP server's information. Only provided fields will be updated; **omitted fields remain unchanged**.
98
103
 
99
104
  **Important:** Use the \`implementation_id\` from \`get_mcp_server\` or \`list_mcp_servers\`, NOT the server ID or slug.
100
105
 
106
+ ## Omission semantics
107
+ - **Omitting** \`remotes\`, \`canonical_urls\`, or \`tags\` leaves existing values **unchanged** (does NOT clear them)
108
+ - **Providing** \`remotes\`, \`canonical_urls\`, or \`tags\` **replaces ALL** existing entries (not a merge)
109
+ - To **delete all** entries, pass an empty array (e.g., \`"remotes": []\`)
110
+
101
111
  ## Updating Basic Info
102
112
  \`\`\`json
103
113
  {
@@ -121,7 +131,7 @@ export function updateMCPServer(_server, clientFactory) {
121
131
  \`\`\`
122
132
 
123
133
  ## Adding/Updating Canonical URLs
124
- Providing canonical_urls replaces ALL existing canonical URLs:
134
+ Providing canonical_urls replaces ALL existing canonical URLs (omitting leaves them unchanged):
125
135
  \`\`\`json
126
136
  {
127
137
  "implementation_id": 456,
@@ -133,7 +143,7 @@ Providing canonical_urls replaces ALL existing canonical URLs:
133
143
  \`\`\`
134
144
 
135
145
  ## Adding/Updating Remote Endpoints
136
- Providing remotes replaces ALL existing remote endpoints:
146
+ Providing remotes replaces ALL existing remote endpoints (omitting leaves them unchanged):
137
147
  \`\`\`json
138
148
  {
139
149
  "implementation_id": 456,
@@ -220,6 +230,10 @@ Create new provider:
220
230
  package_registry: { type: 'string', description: PARAM_DESCRIPTIONS.package_registry },
221
231
  package_name: { type: 'string', description: PARAM_DESCRIPTIONS.package_name },
222
232
  recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
233
+ verified_no_remote_canonicals: {
234
+ type: 'boolean',
235
+ description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
236
+ },
223
237
  created_on_override: {
224
238
  type: 'string',
225
239
  description: PARAM_DESCRIPTIONS.created_on_override,
@@ -318,6 +332,9 @@ Create new provider:
318
332
  if (server.recommended !== undefined) {
319
333
  content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
320
334
  }
335
+ if (server.verified_no_remote_canonicals !== undefined) {
336
+ content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
337
+ }
321
338
  if (server.updated_at) {
322
339
  content += `**Updated:** ${server.updated_at}\n`;
323
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulsemcp-cms-admin-mcp-server",
3
- "version": "0.9.1",
3
+ "version": "0.9.5",
4
4
  "description": "Local implementation of PulseMCP CMS Admin MCP server",
5
5
  "mcpName": "com.pulsemcp.servers/pulsemcp-cms-admin",
6
6
  "main": "build/index.js",
@@ -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);
@@ -56,6 +56,8 @@ export async function getProctorRuns(apiKey, baseUrl, params) {
56
56
  num_tools: run.num_tools,
57
57
  packages: run.packages,
58
58
  remotes: run.remotes,
59
+ known_missing_init_tools_list: run.known_missing_init_tools_list || false,
60
+ known_missing_auth_check: run.known_missing_auth_check || false,
59
61
  })),
60
62
  pagination: {
61
63
  current_page: data.meta.current_page,
@@ -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;
@@ -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
  }
@@ -151,6 +151,12 @@ Use cases:
151
151
  if (run.remotes.length > 0) {
152
152
  content += ` Remotes: ${run.remotes.join(', ')}\n`;
153
153
  }
154
+ if (run.known_missing_init_tools_list) {
155
+ content += ` Known Missing Init Tools List: yes\n`;
156
+ }
157
+ if (run.known_missing_auth_check) {
158
+ content += ` Known Missing Auth Check: yes\n`;
159
+ }
154
160
  content += '\n';
155
161
  }
156
162
  return { content: [{ type: 'text', text: content.trim() }] };
@@ -129,7 +129,7 @@ export declare function saveMCPImplementation(_server: Server, clientFactory: Cl
129
129
  };
130
130
  };
131
131
  };
132
- description: "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.";
132
+ description: "Array of remote endpoint configurations for MCP servers. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all. 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.";
133
133
  };
134
134
  canonical: {
135
135
  type: string;
@@ -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\", or \"url\"), and optional note for additional context.";
152
+ description: "Array of canonical URL configurations. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all. 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;
@@ -24,9 +24,11 @@ const PARAM_DESCRIPTIONS = {
24
24
  github_repo: 'GitHub repository name (without owner prefix).',
25
25
  github_subfolder: 'Subfolder path within the repository, for monorepos. Omit for root-level projects.',
26
26
  // Remote endpoints
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.',
27
+ remote: 'Array of remote endpoint configurations for MCP servers. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all. 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", or "url"), and optional note for additional context.',
29
+ canonical: 'Array of canonical URL configurations. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all. 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
  };
@@ -88,6 +90,11 @@ const SaveMCPImplementationSchema = z.object({
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
  });
@@ -99,14 +106,17 @@ export function saveMCPImplementation(_server, clientFactory) {
99
106
  **Creating a new implementation:**
100
107
  - Omit the \`id\` field to create a new implementation
101
108
  - Required fields for creation: \`name\`, \`type\` (either "server" or "client")
109
+ - Remote endpoints and canonical URLs CAN be included on creation
102
110
 
103
111
  **Updating an existing implementation:**
104
112
  - Provide the \`id\` field to update an existing implementation
105
- - Only provided fields will be updated; omitted fields remain unchanged
113
+ - Only provided fields will be updated; **omitted fields remain unchanged**
114
+ - Omitting \`remote\` or \`canonical\` leaves existing values unchanged (does NOT clear them)
115
+ - Providing \`remote: []\` or \`canonical: []\` (empty array) will DELETE all existing entries
106
116
 
107
117
  All business logic from the Rails controller is applied (validation, associations, callbacks).
108
118
 
109
- Example request (CREATE new implementation):
119
+ Example request (CREATE with remote endpoints and canonical URLs):
110
120
  {
111
121
  "name": "My New MCP Server",
112
122
  "type": "server",
@@ -114,7 +124,19 @@ Example request (CREATE new implementation):
114
124
  "classification": "community",
115
125
  "implementation_language": "typescript",
116
126
  "github_owner": "myorg",
117
- "github_repo": "my-mcp-server"
127
+ "github_repo": "my-mcp-server",
128
+ "remote": [
129
+ {
130
+ "url_direct": "https://api.example.com/mcp",
131
+ "transport": "sse",
132
+ "host_platform": "smithery",
133
+ "authentication_method": "open",
134
+ "cost": "free"
135
+ }
136
+ ],
137
+ "canonical": [
138
+ { "url": "https://github.com/myorg/my-mcp-server", "scope": "url" }
139
+ ]
118
140
  }
119
141
 
120
142
  Example request (UPDATE existing implementation):
@@ -127,7 +149,7 @@ Example request (UPDATE existing implementation):
127
149
  "implementation_language": "typescript"
128
150
  }
129
151
 
130
- Example request (with remote endpoints - new remote):
152
+ Example request (UPDATE with remote endpoints - replaces ALL existing remotes):
131
153
  {
132
154
  "id": 11371,
133
155
  "remote": [
@@ -144,21 +166,12 @@ Example request (with remote endpoints - new remote):
144
166
  ]
145
167
  }
146
168
 
147
- Example response:
148
- {
149
- "id": 11371,
150
- "name": "GitHub MCP Server",
151
- "slug": "github-mcp-server",
152
- "type": "server",
153
- "status": "live",
154
- "classification": "official",
155
- "updated_at": "2024-01-20T16:30:00Z"
156
- }
157
-
158
169
  Important notes:
159
170
  - Omit \`id\` to CREATE, provide \`id\` to UPDATE
160
171
  - When creating: \`name\` and \`type\` are required
161
172
  - When updating: only provided fields will be changed
173
+ - **Omission semantics:** omitting \`remote\` or \`canonical\` leaves them unchanged. To clear them, pass an empty array.
174
+ - Providing \`remote\` or \`canonical\` replaces ALL existing entries (not a merge)
162
175
  - CREATE-ONLY restrictions:
163
176
  - \`github_stars\` is read-only (derived from GitHub repository)
164
177
  - \`mcp_server_id\`/\`mcp_client_id\` are created automatically based on \`type\`
@@ -167,17 +180,7 @@ Important notes:
167
180
  - Setting mcp_server_id or mcp_client_id to null will unlink the association (UPDATE only)
168
181
  - Remote endpoints are for MCP servers only and configure how they can be accessed
169
182
  - Canonical URLs help identify the authoritative source for the implementation
170
-
171
- Use cases:
172
- - Create new MCP implementation entries
173
- - Update draft implementations before publishing
174
- - Change implementation status (draft → live, live → archived)
175
- - Update metadata (stars, language, classification)
176
- - Link or unlink MCP server/client associations
177
- - Update descriptions and documentation
178
- - Modify URLs and provider information
179
- - Add or update remote endpoint configurations for servers
180
- - Set canonical URLs for implementations`,
183
+ - After creating/updating, use \`get_mcp_server\` to verify the full state including remotes and canonical URLs`,
181
184
  inputSchema: {
182
185
  type: 'object',
183
186
  properties: {
@@ -301,6 +304,11 @@ Use cases:
301
304
  },
302
305
  description: PARAM_DESCRIPTIONS.canonical,
303
306
  },
307
+ // Flags
308
+ verified_no_remote_canonicals: {
309
+ type: 'boolean',
310
+ description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
311
+ },
304
312
  // Other fields
305
313
  internal_notes: {
306
314
  type: 'string',
@@ -365,9 +373,25 @@ Use cases:
365
373
  if (implementation.url) {
366
374
  content += `**URL:** ${implementation.url}\n`;
367
375
  }
376
+ if (implementation.canonical && implementation.canonical.length > 0) {
377
+ content += `**Canonical URLs:** ${implementation.canonical.length}\n`;
378
+ implementation.canonical.forEach((c) => {
379
+ content += ` - ${c.url} (${c.scope})\n`;
380
+ });
381
+ }
382
+ const remotes = implementation.mcp_server?.remotes;
383
+ if (remotes && remotes.length > 0) {
384
+ content += `**Remote Endpoints:** ${remotes.length}\n`;
385
+ remotes.forEach((r) => {
386
+ content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
387
+ });
388
+ }
368
389
  if (implementation.created_at) {
369
390
  content += `**Created:** ${new Date(implementation.created_at).toLocaleDateString()}\n`;
370
391
  }
392
+ if (implementation.type === 'server') {
393
+ content += `\n**Tip:** Use \`get_mcp_server\` with slug \`${implementation.slug}\` to verify the full state including remotes and canonical URLs.\n`;
394
+ }
371
395
  return {
372
396
  content: [
373
397
  {
@@ -422,6 +446,19 @@ Use cases:
422
446
  if (implementation.mcp_client_id) {
423
447
  content += `**Linked MCP Client ID:** ${implementation.mcp_client_id}\n`;
424
448
  }
449
+ if (implementation.canonical && implementation.canonical.length > 0) {
450
+ content += `**Canonical URLs:** ${implementation.canonical.length}\n`;
451
+ implementation.canonical.forEach((c) => {
452
+ content += ` - ${c.url} (${c.scope})\n`;
453
+ });
454
+ }
455
+ const updateRemotes = implementation.mcp_server?.remotes;
456
+ if (updateRemotes && updateRemotes.length > 0) {
457
+ content += `**Remote Endpoints:** ${updateRemotes.length}\n`;
458
+ updateRemotes.forEach((r) => {
459
+ content += ` - ${r.display_name || r.url_direct || `ID ${r.id}`}\n`;
460
+ });
461
+ }
425
462
  if (implementation.updated_at) {
426
463
  content += `**Updated:** ${new Date(implementation.updated_at).toLocaleDateString()}\n`;
427
464
  }
@@ -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\")";
@@ -97,7 +101,7 @@ export declare function updateMCPServer(_server: Server, clientFactory: ClientFa
97
101
  items: {
98
102
  type: string;
99
103
  };
100
- description: "Tags for the server. Replaces all existing tags when provided. Use tag slugs.";
104
+ description: "Tags for the server. Providing this replaces ALL existing tags. Omitting leaves them unchanged. Pass an empty array to delete all. Use tag slugs.";
101
105
  };
102
106
  canonical_urls: {
103
107
  type: string;
@@ -120,7 +124,7 @@ export declare function updateMCPServer(_server: Server, clientFactory: ClientFa
120
124
  };
121
125
  required: string[];
122
126
  };
123
- description: "Authoritative URLs for the server. Replaces all existing canonical URLs when provided.";
127
+ description: "Authoritative URLs for the server. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all.";
124
128
  };
125
129
  remotes: {
126
130
  type: string;
@@ -173,7 +177,7 @@ export declare function updateMCPServer(_server: Server, clientFactory: ClientFa
173
177
  };
174
178
  };
175
179
  };
176
- description: "Remote endpoints for the server. Replaces all existing remotes when provided.";
180
+ description: "Remote endpoints for the server. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all.";
177
181
  };
178
182
  internal_notes: {
179
183
  type: string;
@@ -16,10 +16,11 @@ 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
- tags: 'Tags for the server. Replaces all existing tags when provided. Use tag slugs.',
21
- canonical_urls: 'Authoritative URLs for the server. Replaces all existing canonical URLs when provided.',
22
- remotes: 'Remote endpoints for the server. Replaces all existing remotes when provided.',
21
+ tags: 'Tags for the server. Providing this replaces ALL existing tags. Omitting leaves them unchanged. Pass an empty array to delete all. Use tag slugs.',
22
+ canonical_urls: 'Authoritative URLs for the server. Providing this replaces ALL existing canonical URLs. Omitting leaves them unchanged. Pass an empty array to delete all.',
23
+ remotes: 'Remote endpoints for the server. Providing this replaces ALL existing remotes. Omitting leaves them unchanged. Pass an empty array to delete all.',
23
24
  internal_notes: 'Admin-only internal notes',
24
25
  };
25
26
  const CanonicalUrlSchema = 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
@@ -94,10 +99,15 @@ const UpdateMCPServerSchema = z.object({
94
99
  export function updateMCPServer(_server, clientFactory) {
95
100
  return {
96
101
  name: 'update_mcp_server',
97
- description: `Update an MCP server's information. Only provided fields will be updated.
102
+ description: `Update an MCP server's information. Only provided fields will be updated; **omitted fields remain unchanged**.
98
103
 
99
104
  **Important:** Use the \`implementation_id\` from \`get_mcp_server\` or \`list_mcp_servers\`, NOT the server ID or slug.
100
105
 
106
+ ## Omission semantics
107
+ - **Omitting** \`remotes\`, \`canonical_urls\`, or \`tags\` leaves existing values **unchanged** (does NOT clear them)
108
+ - **Providing** \`remotes\`, \`canonical_urls\`, or \`tags\` **replaces ALL** existing entries (not a merge)
109
+ - To **delete all** entries, pass an empty array (e.g., \`"remotes": []\`)
110
+
101
111
  ## Updating Basic Info
102
112
  \`\`\`json
103
113
  {
@@ -121,7 +131,7 @@ export function updateMCPServer(_server, clientFactory) {
121
131
  \`\`\`
122
132
 
123
133
  ## Adding/Updating Canonical URLs
124
- Providing canonical_urls replaces ALL existing canonical URLs:
134
+ Providing canonical_urls replaces ALL existing canonical URLs (omitting leaves them unchanged):
125
135
  \`\`\`json
126
136
  {
127
137
  "implementation_id": 456,
@@ -133,7 +143,7 @@ Providing canonical_urls replaces ALL existing canonical URLs:
133
143
  \`\`\`
134
144
 
135
145
  ## Adding/Updating Remote Endpoints
136
- Providing remotes replaces ALL existing remote endpoints:
146
+ Providing remotes replaces ALL existing remote endpoints (omitting leaves them unchanged):
137
147
  \`\`\`json
138
148
  {
139
149
  "implementation_id": 456,
@@ -220,6 +230,10 @@ Create new provider:
220
230
  package_registry: { type: 'string', description: PARAM_DESCRIPTIONS.package_registry },
221
231
  package_name: { type: 'string', description: PARAM_DESCRIPTIONS.package_name },
222
232
  recommended: { type: 'boolean', description: PARAM_DESCRIPTIONS.recommended },
233
+ verified_no_remote_canonicals: {
234
+ type: 'boolean',
235
+ description: PARAM_DESCRIPTIONS.verified_no_remote_canonicals,
236
+ },
223
237
  created_on_override: {
224
238
  type: 'string',
225
239
  description: PARAM_DESCRIPTIONS.created_on_override,
@@ -318,6 +332,9 @@ Create new provider:
318
332
  if (server.recommended !== undefined) {
319
333
  content += `**Recommended:** ${server.recommended ? 'Yes' : 'No'}\n`;
320
334
  }
335
+ if (server.verified_no_remote_canonicals !== undefined) {
336
+ content += `**Verified No Remote Canonicals:** ${server.verified_no_remote_canonicals ? 'Yes' : 'No'}\n`;
337
+ }
321
338
  if (server.updated_at) {
322
339
  content += `**Updated:** ${server.updated_at}\n`;
323
340
  }
package/shared/types.d.ts CHANGED
@@ -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[];
@@ -708,6 +714,8 @@ export interface ProctorRun {
708
714
  num_tools: number | null;
709
715
  packages: string[];
710
716
  remotes: string[];
717
+ known_missing_init_tools_list: boolean;
718
+ known_missing_auth_check: boolean;
711
719
  }
712
720
  export interface ProctorRunsResponse {
713
721
  runs: ProctorRun[];