claude-flow 2.7.33 → 2.7.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.claude/settings.local.json +9 -2
  2. package/.claude/skills/agentic-jujutsu/SKILL.md +1 -1
  3. package/CHANGELOG.md +75 -0
  4. package/bin/claude-flow +1 -1
  5. package/dist/src/cli/commands/mcp.js +61 -7
  6. package/dist/src/cli/commands/mcp.js.map +1 -1
  7. package/dist/src/cli/help-formatter.js +5 -0
  8. package/dist/src/cli/simple-commands/init/agent-copier.js +9 -3
  9. package/dist/src/cli/simple-commands/init/agent-copier.js.map +1 -1
  10. package/dist/src/core/version.js +1 -1
  11. package/dist/src/mcp/async/job-manager-mcp25.js +240 -0
  12. package/dist/src/mcp/async/job-manager-mcp25.js.map +1 -0
  13. package/dist/src/mcp/index.js +8 -0
  14. package/dist/src/mcp/index.js.map +1 -1
  15. package/dist/src/mcp/protocol/version-negotiation.js +182 -0
  16. package/dist/src/mcp/protocol/version-negotiation.js.map +1 -0
  17. package/dist/src/mcp/registry/mcp-registry-client-2025.js +210 -0
  18. package/dist/src/mcp/registry/mcp-registry-client-2025.js.map +1 -0
  19. package/dist/src/mcp/server-factory.js +189 -0
  20. package/dist/src/mcp/server-factory.js.map +1 -0
  21. package/dist/src/mcp/server-mcp-2025.js +283 -0
  22. package/dist/src/mcp/server-mcp-2025.js.map +1 -0
  23. package/dist/src/mcp/tool-registry-progressive.js +319 -0
  24. package/dist/src/mcp/tool-registry-progressive.js.map +1 -0
  25. package/dist/src/mcp/tools/_template.js +62 -0
  26. package/dist/src/mcp/tools/_template.js.map +1 -0
  27. package/dist/src/mcp/tools/loader.js +228 -0
  28. package/dist/src/mcp/tools/loader.js.map +1 -0
  29. package/dist/src/mcp/tools/system/search.js +224 -0
  30. package/dist/src/mcp/tools/system/search.js.map +1 -0
  31. package/dist/src/mcp/tools/system/status.js +168 -0
  32. package/dist/src/mcp/tools/system/status.js.map +1 -0
  33. package/dist/src/mcp/validation/schema-validator-2025.js +198 -0
  34. package/dist/src/mcp/validation/schema-validator-2025.js.map +1 -0
  35. package/docs/.claude-flow/metrics/performance.json +3 -3
  36. package/docs/.claude-flow/metrics/task-metrics.json +3 -3
  37. package/docs/.github-release-issue-v2.7.33.md +488 -0
  38. package/docs/AGENTDB_BRANCH_MERGE_VERIFICATION.md +436 -0
  39. package/docs/BRANCH_REVIEW_SUMMARY.md +439 -0
  40. package/docs/DEEP_CODE_REVIEW_v2.7.33.md +1159 -0
  41. package/docs/MCP_2025_FEATURE_CONFIRMATION.md +698 -0
  42. package/docs/NPM_PUBLISH_GUIDE_v2.7.33.md +628 -0
  43. package/docs/REGRESSION_TEST_REPORT_v2.7.33.md +397 -0
  44. package/docs/RELEASE_NOTES_v2.7.33.md +618 -0
  45. package/docs/RELEASE_READINESS_SUMMARY.md +377 -0
  46. package/docs/RELEASE_SUMMARY_v2.7.33.md +456 -0
  47. package/docs/agentic-flow-agentdb-mcp-integration.md +1198 -0
  48. package/docs/mcp-2025-implementation-summary.md +459 -0
  49. package/docs/mcp-spec-2025-implementation-plan.md +1330 -0
  50. package/docs/phase-1-2-implementation-summary.md +676 -0
  51. package/docs/regression-analysis-phase-1-2.md +555 -0
  52. package/package.json +5 -2
  53. package/src/cli/commands/mcp.ts +86 -9
  54. package/src/cli/simple-commands/init/agent-copier.js +10 -5
  55. package/src/mcp/async/job-manager-mcp25.ts +456 -0
  56. package/src/mcp/index.ts +60 -0
  57. package/src/mcp/protocol/version-negotiation.ts +329 -0
  58. package/src/mcp/registry/mcp-registry-client-2025.ts +334 -0
  59. package/src/mcp/server-factory.ts +426 -0
  60. package/src/mcp/server-mcp-2025.ts +507 -0
  61. package/src/mcp/tool-registry-progressive.ts +539 -0
  62. package/src/mcp/tools/_template.ts +174 -0
  63. package/src/mcp/tools/loader.ts +362 -0
  64. package/src/mcp/tools/system/search.ts +276 -0
  65. package/src/mcp/tools/system/status.ts +206 -0
  66. package/src/mcp/validation/schema-validator-2025.ts +294 -0
  67. package/docs/AGENTDB_V1.6.1_DEEP_REVIEW.md +0 -386
  68. package/docs/AGENT_FOLDER_STRUCTURE_FIX.md +0 -192
  69. package/docs/RECENT_RELEASES_SUMMARY.md +0 -375
  70. package/docs/V2.7.31_RELEASE_NOTES.md +0 -375
  71. /package/.claude/agents/analysis/{analyze-code-quality.md → code-review/analyze-code-quality.md} +0 -0
  72. /package/.claude/agents/architecture/{arch-system-design.md → system-design/arch-system-design.md} +0 -0
  73. /package/.claude/agents/data/{data-ml-model.md → ml/data-ml-model.md} +0 -0
  74. /package/.claude/agents/development/{dev-backend-api.md → backend/dev-backend-api.md} +0 -0
  75. /package/.claude/agents/devops/{ops-cicd-github.md → ci-cd/ops-cicd-github.md} +0 -0
  76. /package/.claude/agents/documentation/{docs-api-openapi.md → api-docs/docs-api-openapi.md} +0 -0
  77. /package/.claude/agents/specialized/{spec-mobile-react-native.md → mobile/spec-mobile-react-native.md} +0 -0
  78. /package/.claude/agents/testing/{tdd-london-swarm.md → unit/tdd-london-swarm.md} +0 -0
  79. /package/.claude/agents/testing/{production-validator.md → validation/production-validator.md} +0 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * MCP 2025-11 Version Negotiation Protocol
3
+ *
4
+ * Implements version negotiation and capability exchange
5
+ * per MCP 2025-11 specification
6
+ */
7
+
8
+ import type { ILogger } from '../../interfaces/logger.js';
9
+
10
+ /**
11
+ * MCP version in YYYY-MM format
12
+ */
13
+ export type MCPVersion = '2025-11' | '2024-11' | '2024-10';
14
+
15
+ /**
16
+ * MCP capability flags
17
+ */
18
+ export type MCPCapability =
19
+ | 'async' // Job handles, poll/resume
20
+ | 'registry' // Self-registration
21
+ | 'code_exec' // External code execution
22
+ | 'stream' // Streaming output
23
+ | 'sandbox' // Isolated execution
24
+ | 'schema_ref'; // Shared schema references
25
+
26
+ /**
27
+ * MCP handshake request/response
28
+ */
29
+ export interface MCPHandshake {
30
+ mcp_version: MCPVersion;
31
+ client_id?: string;
32
+ server_id?: string;
33
+ transport: 'stdio' | 'http' | 'ws';
34
+ capabilities: MCPCapability[];
35
+ metadata?: {
36
+ name?: string;
37
+ version?: string;
38
+ description?: string;
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Version negotiation result
44
+ */
45
+ export interface NegotiationResult {
46
+ success: boolean;
47
+ agreed_version: MCPVersion;
48
+ agreed_capabilities: MCPCapability[];
49
+ error?: string;
50
+ }
51
+
52
+ /**
53
+ * Version negotiation errors
54
+ */
55
+ export class VersionNegotiationError extends Error {
56
+ constructor(
57
+ message: string,
58
+ public code: 'VERSION_MISMATCH' | 'UNSUPPORTED_CAPABILITY' | 'INVALID_HANDSHAKE'
59
+ ) {
60
+ super(message);
61
+ this.name = 'VersionNegotiationError';
62
+ }
63
+ }
64
+
65
+ /**
66
+ * MCP Version Negotiator
67
+ *
68
+ * Handles version compatibility checking and capability negotiation
69
+ */
70
+ export class VersionNegotiator {
71
+ // Supported versions in order of preference
72
+ private supportedVersions: MCPVersion[] = ['2025-11', '2024-11'];
73
+
74
+ // Current server version
75
+ private serverVersion: MCPVersion = '2025-11';
76
+
77
+ // Server capabilities
78
+ private serverCapabilities: MCPCapability[] = [
79
+ 'async',
80
+ 'registry',
81
+ 'code_exec',
82
+ 'stream',
83
+ ];
84
+
85
+ constructor(private logger: ILogger) {}
86
+
87
+ /**
88
+ * Negotiate version with client
89
+ */
90
+ async negotiate(clientHandshake: MCPHandshake): Promise<NegotiationResult> {
91
+ this.logger.info('Starting version negotiation', {
92
+ clientVersion: clientHandshake.mcp_version,
93
+ serverVersion: this.serverVersion,
94
+ clientCapabilities: clientHandshake.capabilities,
95
+ });
96
+
97
+ // Validate handshake structure
98
+ if (!this.isValidHandshake(clientHandshake)) {
99
+ return {
100
+ success: false,
101
+ agreed_version: this.serverVersion,
102
+ agreed_capabilities: [],
103
+ error: 'Invalid handshake structure',
104
+ };
105
+ }
106
+
107
+ // Check version compatibility
108
+ const versionResult = this.checkVersionCompatibility(clientHandshake.mcp_version);
109
+ if (!versionResult.compatible) {
110
+ return {
111
+ success: false,
112
+ agreed_version: this.serverVersion,
113
+ agreed_capabilities: [],
114
+ error: versionResult.error,
115
+ };
116
+ }
117
+
118
+ // Negotiate capabilities
119
+ const agreedCapabilities = this.negotiateCapabilities(
120
+ clientHandshake.capabilities
121
+ );
122
+
123
+ this.logger.info('Version negotiation successful', {
124
+ agreedVersion: versionResult.version,
125
+ agreedCapabilities,
126
+ });
127
+
128
+ return {
129
+ success: true,
130
+ agreed_version: versionResult.version,
131
+ agreed_capabilities: agreedCapabilities,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Check if handshake is valid
137
+ */
138
+ private isValidHandshake(handshake: MCPHandshake): boolean {
139
+ return !!(
140
+ handshake.mcp_version &&
141
+ handshake.transport &&
142
+ Array.isArray(handshake.capabilities)
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Check version compatibility
148
+ */
149
+ private checkVersionCompatibility(clientVersion: MCPVersion): {
150
+ compatible: boolean;
151
+ version: MCPVersion;
152
+ error?: string;
153
+ } {
154
+ // Exact match - best case
155
+ if (clientVersion === this.serverVersion) {
156
+ return { compatible: true, version: clientVersion };
157
+ }
158
+
159
+ // Check if client version is supported
160
+ if (this.supportedVersions.includes(clientVersion)) {
161
+ this.logger.warn('Client using older version, but compatible', {
162
+ clientVersion,
163
+ serverVersion: this.serverVersion,
164
+ });
165
+ return { compatible: true, version: clientVersion };
166
+ }
167
+
168
+ // Check version gap
169
+ const clientDate = this.parseVersion(clientVersion);
170
+ const serverDate = this.parseVersion(this.serverVersion);
171
+ const monthsDiff = this.getMonthsDifference(clientDate, serverDate);
172
+
173
+ // Reject if more than 1 cycle (1 month) difference
174
+ if (Math.abs(monthsDiff) > 1) {
175
+ return {
176
+ compatible: false,
177
+ version: this.serverVersion,
178
+ error: `Version mismatch: client ${clientVersion}, server ${this.serverVersion}. Difference exceeds 1 cycle.`,
179
+ };
180
+ }
181
+
182
+ // Accept with warning for small differences
183
+ this.logger.warn('Version close enough, accepting', {
184
+ clientVersion,
185
+ serverVersion: this.serverVersion,
186
+ monthsDiff,
187
+ });
188
+ return { compatible: true, version: this.serverVersion };
189
+ }
190
+
191
+ /**
192
+ * Negotiate capabilities between client and server
193
+ */
194
+ private negotiateCapabilities(
195
+ clientCapabilities: MCPCapability[]
196
+ ): MCPCapability[] {
197
+ // Return intersection of client and server capabilities
198
+ return clientCapabilities.filter(cap =>
199
+ this.serverCapabilities.includes(cap)
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Parse version string to date
205
+ */
206
+ private parseVersion(version: MCPVersion): Date {
207
+ const [year, month] = version.split('-').map(Number);
208
+ return new Date(year, month - 1, 1);
209
+ }
210
+
211
+ /**
212
+ * Calculate months difference between dates
213
+ */
214
+ private getMonthsDifference(date1: Date, date2: Date): number {
215
+ const months = (date2.getFullYear() - date1.getFullYear()) * 12;
216
+ return months + date2.getMonth() - date1.getMonth();
217
+ }
218
+
219
+ /**
220
+ * Create server handshake response
221
+ */
222
+ createServerHandshake(
223
+ serverId: string,
224
+ transport: 'stdio' | 'http' | 'ws',
225
+ metadata?: MCPHandshake['metadata']
226
+ ): MCPHandshake {
227
+ return {
228
+ mcp_version: this.serverVersion,
229
+ server_id: serverId,
230
+ transport,
231
+ capabilities: this.serverCapabilities,
232
+ metadata,
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Get current server version
238
+ */
239
+ getServerVersion(): MCPVersion {
240
+ return this.serverVersion;
241
+ }
242
+
243
+ /**
244
+ * Get server capabilities
245
+ */
246
+ getServerCapabilities(): MCPCapability[] {
247
+ return [...this.serverCapabilities];
248
+ }
249
+
250
+ /**
251
+ * Check if capability is supported
252
+ */
253
+ hasCapability(capability: MCPCapability): boolean {
254
+ return this.serverCapabilities.includes(capability);
255
+ }
256
+
257
+ /**
258
+ * Add capability (dynamic capability registration)
259
+ */
260
+ addCapability(capability: MCPCapability): void {
261
+ if (!this.serverCapabilities.includes(capability)) {
262
+ this.serverCapabilities.push(capability);
263
+ this.logger.info('Capability added', { capability });
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Remove capability
269
+ */
270
+ removeCapability(capability: MCPCapability): void {
271
+ const index = this.serverCapabilities.indexOf(capability);
272
+ if (index > -1) {
273
+ this.serverCapabilities.splice(index, 1);
274
+ this.logger.info('Capability removed', { capability });
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Backward compatibility helper
281
+ * Converts legacy requests to MCP 2025-11 format
282
+ */
283
+ export class BackwardCompatibilityAdapter {
284
+ constructor(private logger: ILogger) {}
285
+
286
+ /**
287
+ * Detect if request is legacy format
288
+ */
289
+ isLegacyRequest(request: any): boolean {
290
+ // Legacy requests don't have mcp_version field
291
+ return !request.mcp_version || request.version;
292
+ }
293
+
294
+ /**
295
+ * Convert legacy request to MCP 2025-11 format
296
+ */
297
+ convertToModern(legacyRequest: any): MCPHandshake {
298
+ this.logger.info('Converting legacy request to MCP 2025-11 format');
299
+
300
+ return {
301
+ mcp_version: '2025-11',
302
+ client_id: legacyRequest.clientId || 'legacy-client',
303
+ transport: legacyRequest.transport || 'stdio',
304
+ capabilities: legacyRequest.capabilities || [],
305
+ metadata: {
306
+ name: legacyRequest.name || 'Legacy Client',
307
+ version: legacyRequest.version || '1.0.0',
308
+ },
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Convert modern response to legacy format if needed
314
+ */
315
+ convertToLegacy(modernResponse: any, requestedLegacy: boolean): any {
316
+ if (!requestedLegacy) {
317
+ return modernResponse;
318
+ }
319
+
320
+ this.logger.info('Converting response to legacy format');
321
+
322
+ return {
323
+ version: modernResponse.mcp_version,
324
+ serverId: modernResponse.server_id,
325
+ capabilities: modernResponse.capabilities,
326
+ ...modernResponse,
327
+ };
328
+ }
329
+ }
@@ -0,0 +1,334 @@
1
+ /**
2
+ * MCP 2025-11 Registry Client
3
+ *
4
+ * Implements server registration and health reporting
5
+ * per MCP 2025-11 specification
6
+ */
7
+
8
+ import type { ILogger } from '../../interfaces/logger.js';
9
+ import type { MCPVersion, MCPCapability } from '../protocol/version-negotiation.js';
10
+
11
+ /**
12
+ * MCP Registry entry (2025-11 format)
13
+ */
14
+ export interface MCPRegistryEntry {
15
+ server_id: string;
16
+ version: MCPVersion;
17
+ endpoint: string;
18
+ tools: string[];
19
+ auth: 'bearer' | 'mutual_tls' | 'none';
20
+ capabilities: MCPCapability[];
21
+ metadata: {
22
+ name: string;
23
+ description: string;
24
+ author: string;
25
+ homepage?: string;
26
+ documentation?: string;
27
+ repository?: string;
28
+ };
29
+ health: {
30
+ status: 'healthy' | 'degraded' | 'unhealthy';
31
+ last_check: string; // ISO 8601
32
+ latency_ms: number;
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Registry search query
38
+ */
39
+ export interface RegistrySearchQuery {
40
+ category?: string;
41
+ tags?: string[];
42
+ capabilities?: MCPCapability[];
43
+ limit?: number;
44
+ }
45
+
46
+ /**
47
+ * Registry configuration
48
+ */
49
+ export interface RegistryConfig {
50
+ enabled: boolean;
51
+ registryUrl?: string;
52
+ apiKey?: string;
53
+ serverId: string;
54
+ serverEndpoint: string;
55
+ authMethod: 'bearer' | 'mutual_tls' | 'none';
56
+ metadata: MCPRegistryEntry['metadata'];
57
+ healthCheckInterval?: number; // milliseconds
58
+ }
59
+
60
+ /**
61
+ * MCP 2025-11 Registry Client
62
+ */
63
+ export class MCPRegistryClient {
64
+ private registryUrl: string;
65
+ private healthCheckInterval?: NodeJS.Timeout;
66
+
67
+ constructor(
68
+ private config: RegistryConfig,
69
+ private logger: ILogger,
70
+ private getTools: () => Promise<string[]>,
71
+ private getCapabilities: () => MCPCapability[],
72
+ private getHealth: () => Promise<{ status: 'healthy' | 'degraded' | 'unhealthy'; latency_ms: number }>
73
+ ) {
74
+ this.registryUrl = config.registryUrl || 'https://registry.mcp.anthropic.com/api/v1';
75
+ }
76
+
77
+ /**
78
+ * Register server with MCP Registry
79
+ */
80
+ async register(): Promise<void> {
81
+ if (!this.config.enabled) {
82
+ this.logger.info('Registry registration disabled');
83
+ return;
84
+ }
85
+
86
+ try {
87
+ const entry = await this.buildRegistryEntry();
88
+
89
+ this.logger.info('Registering server with MCP Registry', {
90
+ server_id: entry.server_id,
91
+ endpoint: entry.endpoint,
92
+ capabilities: entry.capabilities,
93
+ });
94
+
95
+ const response = await fetch(`${this.registryUrl}/servers`, {
96
+ method: 'POST',
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ ...(this.config.apiKey && {
100
+ 'Authorization': `Bearer ${this.config.apiKey}`,
101
+ }),
102
+ },
103
+ body: JSON.stringify(entry),
104
+ });
105
+
106
+ if (!response.ok) {
107
+ const error = await response.text();
108
+ throw new Error(`Registration failed: ${response.status} - ${error}`);
109
+ }
110
+
111
+ const result = await response.json();
112
+ this.logger.info('Server registered successfully', {
113
+ server_id: result.server_id,
114
+ });
115
+
116
+ // Start periodic health reporting
117
+ this.startHealthReporting();
118
+ } catch (error) {
119
+ this.logger.error('Failed to register with MCP Registry', {
120
+ error: error instanceof Error ? error.message : String(error),
121
+ });
122
+ throw error;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Update server metadata in registry
128
+ */
129
+ async updateMetadata(updates: Partial<MCPRegistryEntry>): Promise<void> {
130
+ if (!this.config.enabled) {
131
+ return;
132
+ }
133
+
134
+ try {
135
+ this.logger.info('Updating server metadata in registry', {
136
+ server_id: this.config.serverId,
137
+ });
138
+
139
+ const response = await fetch(
140
+ `${this.registryUrl}/servers/${this.config.serverId}`,
141
+ {
142
+ method: 'PATCH',
143
+ headers: {
144
+ 'Content-Type': 'application/json',
145
+ ...(this.config.apiKey && {
146
+ 'Authorization': `Bearer ${this.config.apiKey}`,
147
+ }),
148
+ },
149
+ body: JSON.stringify(updates),
150
+ }
151
+ );
152
+
153
+ if (!response.ok) {
154
+ const error = await response.text();
155
+ throw new Error(`Update failed: ${response.status} - ${error}`);
156
+ }
157
+
158
+ this.logger.info('Server metadata updated successfully');
159
+ } catch (error) {
160
+ this.logger.error('Failed to update metadata', {
161
+ error: error instanceof Error ? error.message : String(error),
162
+ });
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Report health status to registry
168
+ */
169
+ async reportHealth(): Promise<void> {
170
+ if (!this.config.enabled) {
171
+ return;
172
+ }
173
+
174
+ try {
175
+ const health = await this.getHealth();
176
+
177
+ const response = await fetch(
178
+ `${this.registryUrl}/servers/${this.config.serverId}/health`,
179
+ {
180
+ method: 'POST',
181
+ headers: {
182
+ 'Content-Type': 'application/json',
183
+ ...(this.config.apiKey && {
184
+ 'Authorization': `Bearer ${this.config.apiKey}`,
185
+ }),
186
+ },
187
+ body: JSON.stringify({
188
+ status: health.status,
189
+ last_check: new Date().toISOString(),
190
+ latency_ms: health.latency_ms,
191
+ }),
192
+ }
193
+ );
194
+
195
+ if (!response.ok) {
196
+ this.logger.warn('Health report failed', {
197
+ status: response.status,
198
+ });
199
+ }
200
+ } catch (error) {
201
+ this.logger.error('Failed to report health', {
202
+ error: error instanceof Error ? error.message : String(error),
203
+ });
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Search for servers in registry
209
+ */
210
+ async searchServers(query: RegistrySearchQuery): Promise<MCPRegistryEntry[]> {
211
+ try {
212
+ const params = new URLSearchParams();
213
+
214
+ if (query.category) {
215
+ params.set('category', query.category);
216
+ }
217
+ if (query.tags) {
218
+ params.set('tags', query.tags.join(','));
219
+ }
220
+ if (query.capabilities) {
221
+ params.set('capabilities', query.capabilities.join(','));
222
+ }
223
+ if (query.limit) {
224
+ params.set('limit', query.limit.toString());
225
+ }
226
+
227
+ const response = await fetch(`${this.registryUrl}/servers?${params}`);
228
+
229
+ if (!response.ok) {
230
+ throw new Error(`Search failed: ${response.status}`);
231
+ }
232
+
233
+ const results = await response.json();
234
+ return results.servers || [];
235
+ } catch (error) {
236
+ this.logger.error('Failed to search servers', {
237
+ error: error instanceof Error ? error.message : String(error),
238
+ });
239
+ return [];
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Unregister from registry
245
+ */
246
+ async unregister(): Promise<void> {
247
+ if (!this.config.enabled) {
248
+ return;
249
+ }
250
+
251
+ // Stop health reporting
252
+ this.stopHealthReporting();
253
+
254
+ try {
255
+ this.logger.info('Unregistering from MCP Registry', {
256
+ server_id: this.config.serverId,
257
+ });
258
+
259
+ const response = await fetch(
260
+ `${this.registryUrl}/servers/${this.config.serverId}`,
261
+ {
262
+ method: 'DELETE',
263
+ headers: {
264
+ ...(this.config.apiKey && {
265
+ 'Authorization': `Bearer ${this.config.apiKey}`,
266
+ }),
267
+ },
268
+ }
269
+ );
270
+
271
+ if (!response.ok) {
272
+ this.logger.warn('Unregistration failed', {
273
+ status: response.status,
274
+ });
275
+ } else {
276
+ this.logger.info('Server unregistered successfully');
277
+ }
278
+ } catch (error) {
279
+ this.logger.error('Failed to unregister', {
280
+ error: error instanceof Error ? error.message : String(error),
281
+ });
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Build registry entry from current server state
287
+ */
288
+ private async buildRegistryEntry(): Promise<MCPRegistryEntry> {
289
+ const tools = await this.getTools();
290
+ const capabilities = this.getCapabilities();
291
+ const health = await this.getHealth();
292
+
293
+ return {
294
+ server_id: this.config.serverId,
295
+ version: '2025-11',
296
+ endpoint: this.config.serverEndpoint,
297
+ tools,
298
+ auth: this.config.authMethod,
299
+ capabilities,
300
+ metadata: this.config.metadata,
301
+ health: {
302
+ status: health.status,
303
+ last_check: new Date().toISOString(),
304
+ latency_ms: health.latency_ms,
305
+ },
306
+ };
307
+ }
308
+
309
+ /**
310
+ * Start periodic health reporting
311
+ */
312
+ private startHealthReporting(): void {
313
+ const interval = this.config.healthCheckInterval || 60000; // Default: 60 seconds
314
+
315
+ this.healthCheckInterval = setInterval(async () => {
316
+ await this.reportHealth();
317
+ }, interval);
318
+
319
+ this.logger.info('Health reporting started', {
320
+ interval_ms: interval,
321
+ });
322
+ }
323
+
324
+ /**
325
+ * Stop health reporting
326
+ */
327
+ private stopHealthReporting(): void {
328
+ if (this.healthCheckInterval) {
329
+ clearInterval(this.healthCheckInterval);
330
+ this.healthCheckInterval = undefined;
331
+ this.logger.info('Health reporting stopped');
332
+ }
333
+ }
334
+ }