@deimoscloud/coreai 0.1.0

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 (216) hide show
  1. package/.prettierrc +9 -0
  2. package/AGENT_SPEC.md +347 -0
  3. package/ARCHITECTURE.md +547 -0
  4. package/DRAFT_PRD.md +1440 -0
  5. package/IMPLEMENTATION_PLAN.md +256 -0
  6. package/PRODUCT.md +473 -0
  7. package/README.md +303 -0
  8. package/WORKFLOWS.md +295 -0
  9. package/agents/_templates/ic-engineer.md +185 -0
  10. package/agents/_templates/reviewer.md +182 -0
  11. package/agents/backend-engineer.yaml +72 -0
  12. package/agents/devops-engineer.yaml +72 -0
  13. package/agents/engineering-manager.yaml +70 -0
  14. package/agents/examples/android-engineer.md +302 -0
  15. package/agents/examples/backend-engineer.md +320 -0
  16. package/agents/examples/devops-engineer.md +742 -0
  17. package/agents/examples/engineering-manager.md +469 -0
  18. package/agents/examples/frontend-engineer.md +58 -0
  19. package/agents/examples/product-manager.md +315 -0
  20. package/agents/examples/qa-engineer.md +371 -0
  21. package/agents/examples/security-engineer.md +525 -0
  22. package/agents/examples/solutions-architect.md +351 -0
  23. package/agents/examples/wearos-engineer.md +359 -0
  24. package/agents/frontend-engineer.yaml +72 -0
  25. package/commands/core/check-inbox.md +34 -0
  26. package/commands/core/delegate.md +30 -0
  27. package/commands/core/git-commit.md +144 -0
  28. package/commands/core/pr-create.md +193 -0
  29. package/commands/core/review.md +56 -0
  30. package/commands/core/sprint-status.md +65 -0
  31. package/commands/optional/docs-update.md +200 -0
  32. package/commands/optional/jira-create.md +200 -0
  33. package/commands/optional/jira-transition.md +184 -0
  34. package/commands/optional/worktree-cleanup.md +167 -0
  35. package/commands/optional/worktree-setup.md +110 -0
  36. package/dist/cli/index.js +4037 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/index.d.ts +2978 -0
  39. package/dist/index.js +3867 -0
  40. package/dist/index.js.map +1 -0
  41. package/eslint.config.js +29 -0
  42. package/jest.config.js +22 -0
  43. package/knowledge-library/README.md +118 -0
  44. package/knowledge-library/android-engineer/context/current.txt +42 -0
  45. package/knowledge-library/android-engineer/control/decisions.txt +9 -0
  46. package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
  47. package/knowledge-library/android-engineer/control/objectives.txt +26 -0
  48. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  49. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  50. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  51. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  52. package/knowledge-library/architecture.txt +61 -0
  53. package/knowledge-library/backend-engineer/context/current.txt +42 -0
  54. package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
  55. package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
  56. package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
  57. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  58. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  59. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  60. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  61. package/knowledge-library/context.txt +52 -0
  62. package/knowledge-library/devops-engineer/context/current.txt +42 -0
  63. package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
  64. package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
  65. package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
  66. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  67. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  68. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  69. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  70. package/knowledge-library/engineering-manager/context/current.txt +40 -0
  71. package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
  72. package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
  73. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  74. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  76. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  77. package/knowledge-library/prd.txt +81 -0
  78. package/knowledge-library/product-manager/context/current.txt +42 -0
  79. package/knowledge-library/product-manager/control/decisions.txt +9 -0
  80. package/knowledge-library/product-manager/control/dependencies.txt +19 -0
  81. package/knowledge-library/product-manager/control/objectives.txt +26 -0
  82. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  83. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  84. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  85. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  86. package/knowledge-library/qa-engineer/context/current.txt +42 -0
  87. package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
  88. package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
  89. package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
  90. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  91. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  92. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  93. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  94. package/knowledge-library/security-engineer/context/current.txt +42 -0
  95. package/knowledge-library/security-engineer/control/decisions.txt +9 -0
  96. package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
  97. package/knowledge-library/security-engineer/control/objectives.txt +26 -0
  98. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  99. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  100. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  101. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  102. package/knowledge-library/solutions-architect/context/current.txt +42 -0
  103. package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
  104. package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
  105. package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
  106. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  107. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  108. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  109. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  110. package/knowledge-library/wearos-engineer/context/current.txt +42 -0
  111. package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
  112. package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
  113. package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
  114. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  115. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  116. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  117. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  118. package/package.json +66 -0
  119. package/schemas/agent.schema.json +171 -0
  120. package/schemas/coreai.config.schema.json +257 -0
  121. package/scripts/add-agent.sh +323 -0
  122. package/scripts/install.sh +354 -0
  123. package/src/adapters/factory.test.ts +386 -0
  124. package/src/adapters/factory.ts +305 -0
  125. package/src/adapters/index.ts +113 -0
  126. package/src/adapters/interfaces.ts +268 -0
  127. package/src/adapters/mcp/client.test.ts +130 -0
  128. package/src/adapters/mcp/client.ts +451 -0
  129. package/src/adapters/mcp/discovery.test.ts +315 -0
  130. package/src/adapters/mcp/discovery.ts +340 -0
  131. package/src/adapters/mcp/index.ts +66 -0
  132. package/src/adapters/mcp/mapper.test.ts +218 -0
  133. package/src/adapters/mcp/mapper.ts +536 -0
  134. package/src/adapters/mcp/registry.test.ts +433 -0
  135. package/src/adapters/mcp/registry.ts +550 -0
  136. package/src/adapters/mcp/types.ts +258 -0
  137. package/src/adapters/native/filesystem.test.ts +350 -0
  138. package/src/adapters/native/filesystem.ts +393 -0
  139. package/src/adapters/native/github.test.ts +173 -0
  140. package/src/adapters/native/github.ts +627 -0
  141. package/src/adapters/native/index.ts +22 -0
  142. package/src/adapters/native/selector.test.ts +224 -0
  143. package/src/adapters/native/selector.ts +150 -0
  144. package/src/adapters/types.ts +270 -0
  145. package/src/agents/compiler.test.ts +399 -0
  146. package/src/agents/compiler.ts +359 -0
  147. package/src/agents/index.ts +36 -0
  148. package/src/agents/loader.test.ts +319 -0
  149. package/src/agents/loader.ts +143 -0
  150. package/src/agents/resolver.test.ts +282 -0
  151. package/src/agents/resolver.ts +262 -0
  152. package/src/agents/types.ts +87 -0
  153. package/src/cache/index.ts +38 -0
  154. package/src/cache/interfaces.ts +283 -0
  155. package/src/cache/manager.test.ts +266 -0
  156. package/src/cache/manager.ts +388 -0
  157. package/src/cache/provider.test.ts +485 -0
  158. package/src/cache/provider.ts +745 -0
  159. package/src/cache/types.test.ts +192 -0
  160. package/src/cache/types.ts +313 -0
  161. package/src/cli/commands/build.test.ts +248 -0
  162. package/src/cli/commands/build.ts +244 -0
  163. package/src/cli/commands/cache.test.ts +221 -0
  164. package/src/cli/commands/cache.ts +229 -0
  165. package/src/cli/commands/index.ts +63 -0
  166. package/src/cli/commands/init.test.ts +173 -0
  167. package/src/cli/commands/init.ts +296 -0
  168. package/src/cli/commands/skills.test.ts +272 -0
  169. package/src/cli/commands/skills.ts +348 -0
  170. package/src/cli/commands/status.test.ts +392 -0
  171. package/src/cli/commands/status.ts +332 -0
  172. package/src/cli/commands/sync.test.ts +213 -0
  173. package/src/cli/commands/sync.ts +251 -0
  174. package/src/cli/commands/validate.test.ts +216 -0
  175. package/src/cli/commands/validate.ts +340 -0
  176. package/src/cli/index.test.ts +190 -0
  177. package/src/cli/index.ts +493 -0
  178. package/src/commands/context.test.ts +163 -0
  179. package/src/commands/context.ts +111 -0
  180. package/src/commands/index.ts +56 -0
  181. package/src/commands/loader.test.ts +273 -0
  182. package/src/commands/loader.ts +355 -0
  183. package/src/commands/registry.test.ts +384 -0
  184. package/src/commands/registry.ts +248 -0
  185. package/src/commands/runner.test.ts +297 -0
  186. package/src/commands/runner.ts +222 -0
  187. package/src/commands/types.ts +361 -0
  188. package/src/config/index.ts +19 -0
  189. package/src/config/loader.test.ts +262 -0
  190. package/src/config/loader.ts +188 -0
  191. package/src/config/types.ts +154 -0
  192. package/src/context/index.ts +14 -0
  193. package/src/context/loader.test.ts +334 -0
  194. package/src/context/loader.ts +357 -0
  195. package/src/index.test.ts +13 -0
  196. package/src/index.ts +244 -0
  197. package/src/knowledge-library/index.ts +44 -0
  198. package/src/knowledge-library/manager.test.ts +536 -0
  199. package/src/knowledge-library/manager.ts +804 -0
  200. package/src/knowledge-library/types.ts +432 -0
  201. package/src/skills/generator.test.ts +602 -0
  202. package/src/skills/generator.ts +491 -0
  203. package/src/skills/index.ts +27 -0
  204. package/src/skills/templates.ts +520 -0
  205. package/src/skills/types.ts +251 -0
  206. package/templates/completion-report.md +72 -0
  207. package/templates/feedback.md +56 -0
  208. package/templates/project-files/CLAUDE.md.template +109 -0
  209. package/templates/project-files/coreai.json.example +47 -0
  210. package/templates/project-files/mcp.json.template +20 -0
  211. package/templates/review-complete.md +64 -0
  212. package/templates/review-request.md +67 -0
  213. package/templates/task-assignment.md +51 -0
  214. package/tsconfig.build.json +4 -0
  215. package/tsconfig.json +26 -0
  216. package/tsup.config.ts +23 -0
@@ -0,0 +1,550 @@
1
+ /**
2
+ * MCP Server Registry
3
+ *
4
+ * Central registry for managing MCP server connections.
5
+ * Handles discovery, connection lifecycle, and provides access to connected servers.
6
+ */
7
+
8
+ import { EventEmitter } from 'events';
9
+ import type { McpServerDefinition, McpServerInfo, McpTool, McpResource } from './types.js';
10
+ import { McpError } from './types.js';
11
+ import type { McpClientOptions } from './client.js';
12
+ import { McpClient } from './client.js';
13
+ import type { DiscoveryOptions } from './discovery.js';
14
+ import { discoverMcpServers } from './discovery.js';
15
+ import type { ToolMapping } from './mapper.js';
16
+ import { getMappingsForServer, groupMappingsByAdapter } from './mapper.js';
17
+
18
+ /**
19
+ * Registry entry for a server
20
+ */
21
+ export interface ServerEntry {
22
+ /**
23
+ * Server definition from config
24
+ */
25
+ definition: McpServerDefinition;
26
+
27
+ /**
28
+ * MCP client instance (null if not connected)
29
+ */
30
+ client: McpClient | null;
31
+
32
+ /**
33
+ * Current connection status
34
+ */
35
+ status: ServerStatus;
36
+
37
+ /**
38
+ * Server info (available after connection)
39
+ */
40
+ info: McpServerInfo | null;
41
+
42
+ /**
43
+ * Available tools (cached after connection)
44
+ */
45
+ tools: McpTool[];
46
+
47
+ /**
48
+ * Available resources (cached after connection)
49
+ */
50
+ resources: McpResource[];
51
+
52
+ /**
53
+ * Tool mappings for adapter interfaces
54
+ */
55
+ mappings: ToolMapping[];
56
+
57
+ /**
58
+ * Last error if connection failed
59
+ */
60
+ lastError: Error | null;
61
+
62
+ /**
63
+ * Timestamp of last connection attempt
64
+ */
65
+ lastAttempt: Date | null;
66
+ }
67
+
68
+ /**
69
+ * Server connection status
70
+ */
71
+ export type ServerStatus = 'disconnected' | 'connecting' | 'connected' | 'error' | 'disabled';
72
+
73
+ /**
74
+ * Registry events
75
+ */
76
+ export interface RegistryEvents {
77
+ /**
78
+ * Emitted when a server connects
79
+ */
80
+ serverConnected: (name: string, info: McpServerInfo) => void;
81
+
82
+ /**
83
+ * Emitted when a server disconnects
84
+ */
85
+ serverDisconnected: (name: string) => void;
86
+
87
+ /**
88
+ * Emitted when a server connection fails
89
+ */
90
+ serverError: (name: string, error: Error) => void;
91
+
92
+ /**
93
+ * Emitted when server list changes
94
+ */
95
+ serversChanged: (servers: string[]) => void;
96
+ }
97
+
98
+ /**
99
+ * Options for the registry
100
+ */
101
+ export interface RegistryOptions {
102
+ /**
103
+ * Client options to use for all connections
104
+ */
105
+ clientOptions?: McpClientOptions;
106
+
107
+ /**
108
+ * Discovery options
109
+ */
110
+ discoveryOptions?: DiscoveryOptions;
111
+
112
+ /**
113
+ * Whether to auto-connect on discovery
114
+ */
115
+ autoConnect?: boolean;
116
+
117
+ /**
118
+ * Maximum retry attempts for failed connections
119
+ */
120
+ maxRetries?: number;
121
+
122
+ /**
123
+ * Delay between retries in milliseconds
124
+ */
125
+ retryDelay?: number;
126
+ }
127
+
128
+ const DEFAULT_OPTIONS: Required<RegistryOptions> = {
129
+ clientOptions: {},
130
+ discoveryOptions: {},
131
+ autoConnect: false,
132
+ maxRetries: 3,
133
+ retryDelay: 1000,
134
+ };
135
+
136
+ /**
137
+ * MCP Server Registry
138
+ *
139
+ * Manages MCP server connections and provides centralized access to
140
+ * connected servers, their tools, and adapter mappings.
141
+ */
142
+ export class McpRegistry extends EventEmitter {
143
+ private servers = new Map<string, ServerEntry>();
144
+ private options: Required<RegistryOptions>;
145
+
146
+ constructor(options?: RegistryOptions) {
147
+ super();
148
+ this.options = { ...DEFAULT_OPTIONS, ...options };
149
+ }
150
+
151
+ /**
152
+ * Discover and register MCP servers from config files
153
+ */
154
+ async discover(): Promise<string[]> {
155
+ const definitions = discoverMcpServers(this.options.discoveryOptions);
156
+ const registered: string[] = [];
157
+
158
+ for (const definition of definitions) {
159
+ if (!this.servers.has(definition.name)) {
160
+ this.registerServer(definition);
161
+ registered.push(definition.name);
162
+ }
163
+ }
164
+
165
+ if (registered.length > 0) {
166
+ this.emit('serversChanged', this.getServerNames());
167
+ }
168
+
169
+ if (this.options.autoConnect) {
170
+ await this.connectAll();
171
+ }
172
+
173
+ return registered;
174
+ }
175
+
176
+ /**
177
+ * Register a server definition manually
178
+ */
179
+ registerServer(definition: McpServerDefinition): void {
180
+ if (this.servers.has(definition.name)) {
181
+ throw new McpError(
182
+ `Server "${definition.name}" is already registered`,
183
+ 'invalid_config',
184
+ definition.name
185
+ );
186
+ }
187
+
188
+ const entry: ServerEntry = {
189
+ definition,
190
+ client: null,
191
+ status: definition.enabled === false ? 'disabled' : 'disconnected',
192
+ info: null,
193
+ tools: [],
194
+ resources: [],
195
+ mappings: [],
196
+ lastError: null,
197
+ lastAttempt: null,
198
+ };
199
+
200
+ this.servers.set(definition.name, entry);
201
+ this.emit('serversChanged', this.getServerNames());
202
+ }
203
+
204
+ /**
205
+ * Unregister a server
206
+ */
207
+ async unregisterServer(name: string): Promise<void> {
208
+ const entry = this.servers.get(name);
209
+ if (!entry) {
210
+ return;
211
+ }
212
+
213
+ if (entry.status === 'connected' && entry.client) {
214
+ await entry.client.disconnect();
215
+ }
216
+
217
+ this.servers.delete(name);
218
+ this.emit('serversChanged', this.getServerNames());
219
+ }
220
+
221
+ /**
222
+ * Connect to a specific server
223
+ */
224
+ async connect(name: string): Promise<McpServerInfo> {
225
+ const entry = this.servers.get(name);
226
+ if (!entry) {
227
+ throw new McpError(`Server "${name}" is not registered`, 'server_not_found', name);
228
+ }
229
+
230
+ if (entry.status === 'disabled') {
231
+ throw new McpError(`Server "${name}" is disabled`, 'invalid_config', name);
232
+ }
233
+
234
+ if (entry.status === 'connected' && entry.info) {
235
+ return entry.info;
236
+ }
237
+
238
+ return this.doConnect(entry);
239
+ }
240
+
241
+ /**
242
+ * Disconnect from a specific server
243
+ */
244
+ async disconnect(name: string): Promise<void> {
245
+ const entry = this.servers.get(name);
246
+ if (!entry) {
247
+ return;
248
+ }
249
+
250
+ if (entry.client) {
251
+ await entry.client.disconnect();
252
+ entry.client = null;
253
+ }
254
+
255
+ entry.status = entry.definition.enabled === false ? 'disabled' : 'disconnected';
256
+ entry.info = null;
257
+ entry.tools = [];
258
+ entry.resources = [];
259
+ entry.mappings = [];
260
+
261
+ this.emit('serverDisconnected', name);
262
+ }
263
+
264
+ /**
265
+ * Connect to all registered servers
266
+ */
267
+ async connectAll(): Promise<Map<string, McpServerInfo | Error>> {
268
+ const results = new Map<string, McpServerInfo | Error>();
269
+
270
+ const promises = Array.from(this.servers.entries())
271
+ .filter(([, entry]) => entry.status !== 'disabled')
272
+ .map(async ([name]) => {
273
+ try {
274
+ const info = await this.connect(name);
275
+ results.set(name, info);
276
+ } catch (error) {
277
+ results.set(name, error instanceof Error ? error : new Error(String(error)));
278
+ }
279
+ });
280
+
281
+ await Promise.all(promises);
282
+ return results;
283
+ }
284
+
285
+ /**
286
+ * Disconnect from all servers
287
+ */
288
+ async disconnectAll(): Promise<void> {
289
+ const promises = Array.from(this.servers.keys()).map((name) => this.disconnect(name));
290
+ await Promise.all(promises);
291
+ }
292
+
293
+ /**
294
+ * Get a connected client by server name
295
+ */
296
+ getClient(name: string): McpClient | null {
297
+ const entry = this.servers.get(name);
298
+ return entry?.client ?? null;
299
+ }
300
+
301
+ /**
302
+ * Get server entry by name
303
+ */
304
+ getServer(name: string): ServerEntry | undefined {
305
+ return this.servers.get(name);
306
+ }
307
+
308
+ /**
309
+ * Get all registered server names
310
+ */
311
+ getServerNames(): string[] {
312
+ return Array.from(this.servers.keys());
313
+ }
314
+
315
+ /**
316
+ * Get all connected server names
317
+ */
318
+ getConnectedServers(): string[] {
319
+ return Array.from(this.servers.entries())
320
+ .filter(([, entry]) => entry.status === 'connected')
321
+ .map(([name]) => name);
322
+ }
323
+
324
+ /**
325
+ * Get all server entries
326
+ */
327
+ getAllServers(): Map<string, ServerEntry> {
328
+ return new Map(this.servers);
329
+ }
330
+
331
+ /**
332
+ * Get tools from a connected server
333
+ */
334
+ getTools(name: string): McpTool[] {
335
+ const entry = this.servers.get(name);
336
+ return entry?.tools ?? [];
337
+ }
338
+
339
+ /**
340
+ * Get all tools from all connected servers
341
+ */
342
+ getAllTools(): Map<string, McpTool[]> {
343
+ const result = new Map<string, McpTool[]>();
344
+ for (const [name, entry] of this.servers) {
345
+ if (entry.status === 'connected' && entry.tools.length > 0) {
346
+ result.set(name, entry.tools);
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+
352
+ /**
353
+ * Get tool mappings for a server
354
+ */
355
+ getMappings(name: string): ToolMapping[] {
356
+ const entry = this.servers.get(name);
357
+ return entry?.mappings ?? [];
358
+ }
359
+
360
+ /**
361
+ * Get all mappings grouped by adapter type
362
+ */
363
+ getAllMappingsByAdapter(): Map<string, Map<string, ToolMapping[]>> {
364
+ const result = new Map<string, Map<string, ToolMapping[]>>();
365
+
366
+ for (const [serverName, entry] of this.servers) {
367
+ if (entry.mappings.length > 0) {
368
+ const grouped = groupMappingsByAdapter(entry.mappings);
369
+ for (const [adapterType, mappings] of grouped) {
370
+ let adapterMap = result.get(adapterType);
371
+ if (!adapterMap) {
372
+ adapterMap = new Map();
373
+ result.set(adapterType, adapterMap);
374
+ }
375
+ adapterMap.set(serverName, mappings);
376
+ }
377
+ }
378
+ }
379
+
380
+ return result;
381
+ }
382
+
383
+ /**
384
+ * Find servers that provide a specific adapter type
385
+ */
386
+ findServersForAdapter(
387
+ adapterType: 'issue_tracker' | 'git' | 'documentation' | 'state'
388
+ ): string[] {
389
+ const servers: string[] = [];
390
+
391
+ for (const [name, entry] of this.servers) {
392
+ if (entry.status === 'connected') {
393
+ const hasAdapter = entry.mappings.some((m) => m.adapterType === adapterType);
394
+ if (hasAdapter) {
395
+ servers.push(name);
396
+ }
397
+ }
398
+ }
399
+
400
+ return servers;
401
+ }
402
+
403
+ /**
404
+ * Call a tool on a connected server
405
+ */
406
+ async callTool(
407
+ serverName: string,
408
+ toolName: string,
409
+ args?: Record<string, unknown>
410
+ ): Promise<unknown> {
411
+ const entry = this.servers.get(serverName);
412
+ if (!entry) {
413
+ throw new McpError(
414
+ `Server "${serverName}" is not registered`,
415
+ 'server_not_found',
416
+ serverName
417
+ );
418
+ }
419
+
420
+ if (entry.status !== 'connected' || !entry.client) {
421
+ throw new McpError(
422
+ `Server "${serverName}" is not connected`,
423
+ 'connection_closed',
424
+ serverName
425
+ );
426
+ }
427
+
428
+ return entry.client.callTool(toolName, args);
429
+ }
430
+
431
+ /**
432
+ * Get registry status summary
433
+ */
434
+ getStatus(): RegistryStatus {
435
+ const servers: Record<string, ServerStatusSummary> = {};
436
+
437
+ for (const [name, entry] of this.servers) {
438
+ const summary: ServerStatusSummary = {
439
+ status: entry.status,
440
+ toolCount: entry.tools.length,
441
+ resourceCount: entry.resources.length,
442
+ mappingCount: entry.mappings.length,
443
+ };
444
+ if (entry.lastError?.message) {
445
+ summary.lastError = entry.lastError.message;
446
+ }
447
+ servers[name] = summary;
448
+ }
449
+
450
+ return {
451
+ totalServers: this.servers.size,
452
+ connectedServers: this.getConnectedServers().length,
453
+ servers,
454
+ };
455
+ }
456
+
457
+ /**
458
+ * Internal: perform connection with retries
459
+ */
460
+ private async doConnect(entry: ServerEntry, attempt = 1): Promise<McpServerInfo> {
461
+ const name = entry.definition.name;
462
+ entry.status = 'connecting';
463
+ entry.lastAttempt = new Date();
464
+
465
+ try {
466
+ // Create client if needed
467
+ if (!entry.client) {
468
+ entry.client = new McpClient(name, entry.definition.config, this.options.clientOptions);
469
+ }
470
+
471
+ // Connect
472
+ await entry.client.connect();
473
+
474
+ // Get server info
475
+ const info = entry.client.getServerInfo();
476
+ if (!info) {
477
+ throw new McpError(`Failed to get server info for "${name}"`, 'protocol_error', name);
478
+ }
479
+
480
+ // Cache tools and resources
481
+ if (info.capabilities?.tools) {
482
+ entry.tools = await entry.client.listTools();
483
+ }
484
+ if (info.capabilities?.resources) {
485
+ entry.resources = await entry.client.listResources();
486
+ }
487
+
488
+ // Generate mappings
489
+ entry.mappings = getMappingsForServer(name, entry.tools);
490
+
491
+ // Update status
492
+ entry.status = 'connected';
493
+ entry.info = info;
494
+ entry.lastError = null;
495
+
496
+ this.emit('serverConnected', name, info);
497
+ return info;
498
+ } catch (error) {
499
+ const err = error instanceof Error ? error : new Error(String(error));
500
+ entry.lastError = err;
501
+
502
+ // Retry if we haven't exceeded max retries
503
+ if (attempt < this.options.maxRetries) {
504
+ await this.delay(this.options.retryDelay);
505
+ return this.doConnect(entry, attempt + 1);
506
+ }
507
+
508
+ // Mark as error status
509
+ entry.status = 'error';
510
+ entry.client = null;
511
+
512
+ this.emit('serverError', name, err);
513
+ throw err;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Internal: delay helper
519
+ */
520
+ private delay(ms: number): Promise<void> {
521
+ return new Promise((resolve) => setTimeout(resolve, ms));
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Server status summary
527
+ */
528
+ export interface ServerStatusSummary {
529
+ status: ServerStatus;
530
+ toolCount: number;
531
+ resourceCount: number;
532
+ mappingCount: number;
533
+ lastError?: string;
534
+ }
535
+
536
+ /**
537
+ * Registry status
538
+ */
539
+ export interface RegistryStatus {
540
+ totalServers: number;
541
+ connectedServers: number;
542
+ servers: Record<string, ServerStatusSummary>;
543
+ }
544
+
545
+ /**
546
+ * Create a new registry with default options
547
+ */
548
+ export function createRegistry(options?: RegistryOptions): McpRegistry {
549
+ return new McpRegistry(options);
550
+ }