@deimoscloud/coreai 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/.prettierrc +0 -9
- package/AGENT_SPEC.md +0 -347
- package/ARCHITECTURE.md +0 -547
- package/DRAFT_PRD.md +0 -1440
- package/IMPLEMENTATION_PLAN.md +0 -256
- package/PRODUCT.md +0 -473
- package/WORKFLOWS.md +0 -295
- package/commands/core/check-inbox.md +0 -34
- package/commands/core/delegate.md +0 -30
- package/commands/core/git-commit.md +0 -144
- package/commands/core/pr-create.md +0 -193
- package/commands/core/review.md +0 -56
- package/commands/core/sprint-status.md +0 -65
- package/commands/optional/docs-update.md +0 -200
- package/commands/optional/jira-create.md +0 -200
- package/commands/optional/jira-transition.md +0 -184
- package/commands/optional/worktree-cleanup.md +0 -167
- package/commands/optional/worktree-setup.md +0 -110
- package/eslint.config.js +0 -29
- package/jest.config.js +0 -22
- package/knowledge-library/README.md +0 -118
- package/knowledge-library/android-engineer/context/current.txt +0 -42
- package/knowledge-library/android-engineer/control/decisions.txt +0 -9
- package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/android-engineer/control/objectives.txt +0 -26
- package/knowledge-library/android-engineer/history/.gitkeep +0 -0
- package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/architecture.txt +0 -61
- package/knowledge-library/backend-engineer/context/current.txt +0 -42
- package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
- package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
- package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/context.txt +0 -52
- package/knowledge-library/devops-engineer/context/current.txt +0 -42
- package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
- package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
- package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/context/current.txt +0 -40
- package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
- package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
- package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
- package/knowledge-library/prd.txt +0 -81
- package/knowledge-library/product-manager/context/current.txt +0 -42
- package/knowledge-library/product-manager/control/decisions.txt +0 -9
- package/knowledge-library/product-manager/control/dependencies.txt +0 -19
- package/knowledge-library/product-manager/control/objectives.txt +0 -26
- package/knowledge-library/product-manager/history/.gitkeep +0 -0
- package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/product-manager/tech/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/context/current.txt +0 -42
- package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
- package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
- package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/security-engineer/context/current.txt +0 -42
- package/knowledge-library/security-engineer/control/decisions.txt +0 -9
- package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/security-engineer/control/objectives.txt +0 -26
- package/knowledge-library/security-engineer/history/.gitkeep +0 -0
- package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/context/current.txt +0 -42
- package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
- package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
- package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
- package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/context/current.txt +0 -42
- package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
- package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
- package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
- package/scripts/add-agent.sh +0 -323
- package/scripts/install.sh +0 -354
- package/src/adapters/factory.test.ts +0 -386
- package/src/adapters/factory.ts +0 -305
- package/src/adapters/index.ts +0 -113
- package/src/adapters/interfaces.ts +0 -268
- package/src/adapters/mcp/client.test.ts +0 -130
- package/src/adapters/mcp/client.ts +0 -451
- package/src/adapters/mcp/discovery.test.ts +0 -315
- package/src/adapters/mcp/discovery.ts +0 -340
- package/src/adapters/mcp/index.ts +0 -66
- package/src/adapters/mcp/mapper.test.ts +0 -218
- package/src/adapters/mcp/mapper.ts +0 -536
- package/src/adapters/mcp/registry.test.ts +0 -433
- package/src/adapters/mcp/registry.ts +0 -550
- package/src/adapters/mcp/types.ts +0 -258
- package/src/adapters/native/filesystem.test.ts +0 -350
- package/src/adapters/native/filesystem.ts +0 -393
- package/src/adapters/native/github.test.ts +0 -173
- package/src/adapters/native/github.ts +0 -627
- package/src/adapters/native/index.ts +0 -22
- package/src/adapters/native/selector.test.ts +0 -224
- package/src/adapters/native/selector.ts +0 -150
- package/src/adapters/types.ts +0 -270
- package/src/agents/compiler.test.ts +0 -410
- package/src/agents/compiler.ts +0 -424
- package/src/agents/index.ts +0 -37
- package/src/agents/loader.test.ts +0 -319
- package/src/agents/loader.ts +0 -143
- package/src/agents/resolver.test.ts +0 -282
- package/src/agents/resolver.ts +0 -262
- package/src/agents/types.ts +0 -97
- package/src/cache/index.ts +0 -38
- package/src/cache/interfaces.ts +0 -283
- package/src/cache/manager.test.ts +0 -266
- package/src/cache/manager.ts +0 -388
- package/src/cache/provider.test.ts +0 -485
- package/src/cache/provider.ts +0 -745
- package/src/cache/types.test.ts +0 -192
- package/src/cache/types.ts +0 -313
- package/src/cli/commands/build.test.ts +0 -248
- package/src/cli/commands/build.ts +0 -284
- package/src/cli/commands/cache.test.ts +0 -221
- package/src/cli/commands/cache.ts +0 -229
- package/src/cli/commands/index.ts +0 -63
- package/src/cli/commands/init.test.ts +0 -173
- package/src/cli/commands/init.ts +0 -296
- package/src/cli/commands/skills.test.ts +0 -272
- package/src/cli/commands/skills.ts +0 -348
- package/src/cli/commands/status.test.ts +0 -392
- package/src/cli/commands/status.ts +0 -332
- package/src/cli/commands/sync.test.ts +0 -213
- package/src/cli/commands/sync.ts +0 -251
- package/src/cli/commands/validate.test.ts +0 -216
- package/src/cli/commands/validate.ts +0 -340
- package/src/cli/index.test.ts +0 -190
- package/src/cli/index.ts +0 -493
- package/src/commands/context.test.ts +0 -163
- package/src/commands/context.ts +0 -111
- package/src/commands/index.ts +0 -56
- package/src/commands/loader.test.ts +0 -273
- package/src/commands/loader.ts +0 -355
- package/src/commands/registry.test.ts +0 -384
- package/src/commands/registry.ts +0 -248
- package/src/commands/runner.test.ts +0 -297
- package/src/commands/runner.ts +0 -222
- package/src/commands/types.ts +0 -361
- package/src/config/index.ts +0 -19
- package/src/config/loader.test.ts +0 -262
- package/src/config/loader.ts +0 -188
- package/src/config/types.ts +0 -154
- package/src/context/index.ts +0 -14
- package/src/context/loader.test.ts +0 -334
- package/src/context/loader.ts +0 -357
- package/src/index.test.ts +0 -13
- package/src/index.ts +0 -268
- package/src/knowledge-library/index.ts +0 -44
- package/src/knowledge-library/manager.test.ts +0 -536
- package/src/knowledge-library/manager.ts +0 -804
- package/src/knowledge-library/types.ts +0 -432
- package/src/skills/generator.test.ts +0 -602
- package/src/skills/generator.ts +0 -491
- package/src/skills/index.ts +0 -27
- package/src/skills/templates.ts +0 -520
- package/src/skills/types.ts +0 -251
- package/templates/completion-report.md +0 -72
- package/templates/feedback.md +0 -56
- package/templates/project-files/CLAUDE.md.template +0 -109
- package/templates/project-files/coreai.json.example +0 -47
- package/templates/project-files/mcp.json.template +0 -20
- package/templates/review-complete.md +0 -64
- package/templates/review-request.md +0 -67
- package/templates/task-assignment.md +0 -51
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -23
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { tmpdir } from 'os';
|
|
4
|
-
import {
|
|
5
|
-
discoverMcpServers,
|
|
6
|
-
loadServersFromFile,
|
|
7
|
-
parseServersFromConfig,
|
|
8
|
-
validateServerConfig,
|
|
9
|
-
findMcpConfigFile,
|
|
10
|
-
getDefaultMcpConfigPath,
|
|
11
|
-
} from './discovery.js';
|
|
12
|
-
import { McpError } from './types.js';
|
|
13
|
-
|
|
14
|
-
describe('MCP Discovery', () => {
|
|
15
|
-
let tempDir: string;
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
tempDir = mkdtempSync(join(tmpdir(), 'mcp-discovery-test-'));
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('validateServerConfig', () => {
|
|
26
|
-
it('should validate stdio config with command', () => {
|
|
27
|
-
const config = validateServerConfig(
|
|
28
|
-
{
|
|
29
|
-
command: 'node',
|
|
30
|
-
args: ['server.js'],
|
|
31
|
-
},
|
|
32
|
-
'test-server'
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
expect(config.transport).toBe('stdio');
|
|
36
|
-
expect(config).toEqual({
|
|
37
|
-
transport: 'stdio',
|
|
38
|
-
command: 'node',
|
|
39
|
-
args: ['server.js'],
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should validate stdio config with explicit transport', () => {
|
|
44
|
-
const config = validateServerConfig(
|
|
45
|
-
{
|
|
46
|
-
transport: 'stdio',
|
|
47
|
-
command: 'npx',
|
|
48
|
-
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
49
|
-
env: { GITHUB_TOKEN: 'xxx' },
|
|
50
|
-
},
|
|
51
|
-
'github'
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
expect(config.transport).toBe('stdio');
|
|
55
|
-
expect((config as { command: string }).command).toBe('npx');
|
|
56
|
-
expect((config as { args: string[] }).args).toEqual([
|
|
57
|
-
'-y',
|
|
58
|
-
'@modelcontextprotocol/server-github',
|
|
59
|
-
]);
|
|
60
|
-
expect((config as { env: Record<string, string> }).env).toEqual({ GITHUB_TOKEN: 'xxx' });
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should validate http config with url', () => {
|
|
64
|
-
const config = validateServerConfig(
|
|
65
|
-
{
|
|
66
|
-
url: 'https://api.example.com/mcp',
|
|
67
|
-
},
|
|
68
|
-
'remote-server'
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
expect(config.transport).toBe('http');
|
|
72
|
-
expect((config as { url: string }).url).toBe('https://api.example.com/mcp');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should validate sse config with explicit transport', () => {
|
|
76
|
-
const config = validateServerConfig(
|
|
77
|
-
{
|
|
78
|
-
transport: 'sse',
|
|
79
|
-
url: 'https://api.example.com/sse',
|
|
80
|
-
headers: { Authorization: 'Bearer xxx' },
|
|
81
|
-
},
|
|
82
|
-
'sse-server'
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
expect(config.transport).toBe('sse');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should throw for missing command or url', () => {
|
|
89
|
-
expect(() => validateServerConfig({}, 'test')).toThrow(McpError);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should throw for invalid url', () => {
|
|
93
|
-
expect(() => validateServerConfig({ url: 'not-a-url' }, 'test')).toThrow(McpError);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should throw for invalid args type', () => {
|
|
97
|
-
expect(() => validateServerConfig({ command: 'node', args: 'invalid' }, 'test')).toThrow(
|
|
98
|
-
McpError
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('parseServersFromConfig', () => {
|
|
104
|
-
it('should parse servers from config object', () => {
|
|
105
|
-
const config = {
|
|
106
|
-
mcpServers: {
|
|
107
|
-
github: {
|
|
108
|
-
command: 'npx',
|
|
109
|
-
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
110
|
-
},
|
|
111
|
-
filesystem: {
|
|
112
|
-
command: 'npx',
|
|
113
|
-
args: ['-y', '@modelcontextprotocol/server-filesystem'],
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const servers = parseServersFromConfig(config);
|
|
119
|
-
|
|
120
|
-
expect(servers).toHaveLength(2);
|
|
121
|
-
expect(servers.map((s) => s.name)).toContain('github');
|
|
122
|
-
expect(servers.map((s) => s.name)).toContain('filesystem');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should return empty array for config without mcpServers', () => {
|
|
126
|
-
const servers = parseServersFromConfig({});
|
|
127
|
-
expect(servers).toHaveLength(0);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should skip invalid server configs', () => {
|
|
131
|
-
const config = {
|
|
132
|
-
mcpServers: {
|
|
133
|
-
valid: { command: 'node' },
|
|
134
|
-
invalid: { invalid: true },
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const servers = parseServersFromConfig(config);
|
|
139
|
-
expect(servers).toHaveLength(1);
|
|
140
|
-
expect(servers[0].name).toBe('valid');
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('loadServersFromFile', () => {
|
|
145
|
-
it('should load servers from JSON file', () => {
|
|
146
|
-
const configPath = join(tempDir, 'mcp.json');
|
|
147
|
-
writeFileSync(
|
|
148
|
-
configPath,
|
|
149
|
-
JSON.stringify({
|
|
150
|
-
mcpServers: {
|
|
151
|
-
'test-server': {
|
|
152
|
-
command: 'node',
|
|
153
|
-
args: ['server.js'],
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
})
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const servers = loadServersFromFile(configPath);
|
|
160
|
-
|
|
161
|
-
expect(servers).toHaveLength(1);
|
|
162
|
-
expect(servers[0].name).toBe('test-server');
|
|
163
|
-
expect(servers[0].config.transport).toBe('stdio');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should throw for non-existent file', () => {
|
|
167
|
-
expect(() => loadServersFromFile('/nonexistent/file.json')).toThrow(McpError);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should throw for invalid JSON', () => {
|
|
171
|
-
const configPath = join(tempDir, 'invalid.json');
|
|
172
|
-
writeFileSync(configPath, 'not valid json');
|
|
173
|
-
|
|
174
|
-
expect(() => loadServersFromFile(configPath)).toThrow(McpError);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('discoverMcpServers', () => {
|
|
179
|
-
it('should discover servers from project directory', () => {
|
|
180
|
-
// Create project with .claude/mcp.json
|
|
181
|
-
const claudeDir = join(tempDir, '.claude');
|
|
182
|
-
mkdirSync(claudeDir);
|
|
183
|
-
writeFileSync(
|
|
184
|
-
join(claudeDir, 'mcp.json'),
|
|
185
|
-
JSON.stringify({
|
|
186
|
-
mcpServers: {
|
|
187
|
-
project: { command: 'node', args: ['project-server.js'] },
|
|
188
|
-
},
|
|
189
|
-
})
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
const servers = discoverMcpServers({
|
|
193
|
-
projectRoot: tempDir,
|
|
194
|
-
includeGlobal: false,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
expect(servers).toHaveLength(1);
|
|
198
|
-
expect(servers[0].name).toBe('project');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should discover from root mcp.json', () => {
|
|
202
|
-
writeFileSync(
|
|
203
|
-
join(tempDir, 'mcp.json'),
|
|
204
|
-
JSON.stringify({
|
|
205
|
-
mcpServers: {
|
|
206
|
-
root: { command: 'node' },
|
|
207
|
-
},
|
|
208
|
-
})
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
const servers = discoverMcpServers({
|
|
212
|
-
projectRoot: tempDir,
|
|
213
|
-
includeGlobal: false,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(servers).toHaveLength(1);
|
|
217
|
-
expect(servers[0].name).toBe('root');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('should prioritize project config over root config', () => {
|
|
221
|
-
// Create both configs with same server name
|
|
222
|
-
const claudeDir = join(tempDir, '.claude');
|
|
223
|
-
mkdirSync(claudeDir);
|
|
224
|
-
writeFileSync(
|
|
225
|
-
join(claudeDir, 'mcp.json'),
|
|
226
|
-
JSON.stringify({
|
|
227
|
-
mcpServers: {
|
|
228
|
-
shared: { command: 'project-cmd' },
|
|
229
|
-
},
|
|
230
|
-
})
|
|
231
|
-
);
|
|
232
|
-
writeFileSync(
|
|
233
|
-
join(tempDir, 'mcp.json'),
|
|
234
|
-
JSON.stringify({
|
|
235
|
-
mcpServers: {
|
|
236
|
-
shared: { command: 'root-cmd' },
|
|
237
|
-
},
|
|
238
|
-
})
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const servers = discoverMcpServers({
|
|
242
|
-
projectRoot: tempDir,
|
|
243
|
-
includeGlobal: false,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
expect(servers).toHaveLength(1);
|
|
247
|
-
expect((servers[0].config as { command: string }).command).toBe('project-cmd');
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('should discover from additional paths', () => {
|
|
251
|
-
const customPath = join(tempDir, 'custom', 'config.json');
|
|
252
|
-
mkdirSync(join(tempDir, 'custom'));
|
|
253
|
-
writeFileSync(
|
|
254
|
-
customPath,
|
|
255
|
-
JSON.stringify({
|
|
256
|
-
mcpServers: {
|
|
257
|
-
custom: { command: 'custom-cmd' },
|
|
258
|
-
},
|
|
259
|
-
})
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const servers = discoverMcpServers({
|
|
263
|
-
projectRoot: tempDir,
|
|
264
|
-
includeGlobal: false,
|
|
265
|
-
additionalPaths: [customPath],
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
expect(servers.some((s) => s.name === 'custom')).toBe(true);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should return empty array when no configs found', () => {
|
|
272
|
-
const servers = discoverMcpServers({
|
|
273
|
-
projectRoot: tempDir,
|
|
274
|
-
includeGlobal: false,
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(servers).toHaveLength(0);
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
describe('findMcpConfigFile', () => {
|
|
282
|
-
it('should find config in .claude directory', () => {
|
|
283
|
-
const claudeDir = join(tempDir, '.claude');
|
|
284
|
-
mkdirSync(claudeDir);
|
|
285
|
-
writeFileSync(join(claudeDir, 'mcp.json'), '{}');
|
|
286
|
-
|
|
287
|
-
const found = findMcpConfigFile(tempDir);
|
|
288
|
-
expect(found).toBe(join(claudeDir, 'mcp.json'));
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it('should find config in root directory', () => {
|
|
292
|
-
writeFileSync(join(tempDir, 'mcp.json'), '{}');
|
|
293
|
-
|
|
294
|
-
const found = findMcpConfigFile(tempDir);
|
|
295
|
-
expect(found).toBe(join(tempDir, 'mcp.json'));
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should return null when no config found', () => {
|
|
299
|
-
const found = findMcpConfigFile(tempDir);
|
|
300
|
-
expect(found).toBeNull();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
describe('getDefaultMcpConfigPath', () => {
|
|
305
|
-
it('should return .claude/mcp.json path', () => {
|
|
306
|
-
const path = getDefaultMcpConfigPath(tempDir);
|
|
307
|
-
expect(path).toBe(join(tempDir, '.claude', 'mcp.json'));
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should use cwd when no root provided', () => {
|
|
311
|
-
const path = getDefaultMcpConfigPath();
|
|
312
|
-
expect(path).toBe(join(process.cwd(), '.claude', 'mcp.json'));
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
});
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Server Discovery
|
|
3
|
-
*
|
|
4
|
-
* Discovers and loads MCP server configurations from various sources.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { existsSync, readFileSync } from 'fs';
|
|
8
|
-
import { join, dirname } from 'path';
|
|
9
|
-
import { homedir } from 'os';
|
|
10
|
-
import type {
|
|
11
|
-
McpConfigFile,
|
|
12
|
-
McpServerConfig,
|
|
13
|
-
McpServerDefinition,
|
|
14
|
-
StdioServerConfig,
|
|
15
|
-
HttpServerConfig,
|
|
16
|
-
} from './types.js';
|
|
17
|
-
import { McpError } from './types.js';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Options for discovering MCP servers
|
|
21
|
-
*/
|
|
22
|
-
export interface DiscoveryOptions {
|
|
23
|
-
/**
|
|
24
|
-
* Project root directory to search from
|
|
25
|
-
*/
|
|
26
|
-
projectRoot?: string;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Whether to include global config from home directory
|
|
30
|
-
*/
|
|
31
|
-
includeGlobal?: boolean;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Additional config file paths to check
|
|
35
|
-
*/
|
|
36
|
-
additionalPaths?: string[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Known config file locations
|
|
41
|
-
*/
|
|
42
|
-
const CONFIG_FILENAMES = ['mcp.json', '.mcp.json', 'claude_desktop_config.json'];
|
|
43
|
-
|
|
44
|
-
const PROJECT_CONFIG_DIRS = ['.claude', '.config', ''];
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Discover MCP servers from configuration files
|
|
48
|
-
*/
|
|
49
|
-
export function discoverMcpServers(options: DiscoveryOptions = {}): McpServerDefinition[] {
|
|
50
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
51
|
-
const includeGlobal = options.includeGlobal ?? true;
|
|
52
|
-
const additionalPaths = options.additionalPaths ?? [];
|
|
53
|
-
|
|
54
|
-
const servers: McpServerDefinition[] = [];
|
|
55
|
-
const seenServers = new Set<string>();
|
|
56
|
-
|
|
57
|
-
// Build list of config file paths to check
|
|
58
|
-
const configPaths: string[] = [];
|
|
59
|
-
|
|
60
|
-
// Project-level configs (highest priority)
|
|
61
|
-
for (const dir of PROJECT_CONFIG_DIRS) {
|
|
62
|
-
for (const filename of CONFIG_FILENAMES) {
|
|
63
|
-
const path = dir ? join(projectRoot, dir, filename) : join(projectRoot, filename);
|
|
64
|
-
configPaths.push(path);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Additional paths
|
|
69
|
-
configPaths.push(...additionalPaths);
|
|
70
|
-
|
|
71
|
-
// Global config (lowest priority)
|
|
72
|
-
if (includeGlobal) {
|
|
73
|
-
const homeDir = homedir();
|
|
74
|
-
const globalPaths = [
|
|
75
|
-
join(homeDir, '.config', 'claude', 'mcp.json'),
|
|
76
|
-
join(homeDir, '.claude', 'mcp.json'),
|
|
77
|
-
join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
78
|
-
];
|
|
79
|
-
configPaths.push(...globalPaths);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Load servers from each config file (earlier files take precedence)
|
|
83
|
-
for (const configPath of configPaths) {
|
|
84
|
-
if (!existsSync(configPath)) continue;
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const fileServers = loadServersFromFile(configPath);
|
|
88
|
-
|
|
89
|
-
for (const server of fileServers) {
|
|
90
|
-
// Skip if we already have a server with this name
|
|
91
|
-
if (seenServers.has(server.name)) continue;
|
|
92
|
-
|
|
93
|
-
seenServers.add(server.name);
|
|
94
|
-
servers.push(server);
|
|
95
|
-
}
|
|
96
|
-
} catch (error) {
|
|
97
|
-
// Log warning but continue with other files
|
|
98
|
-
console.warn(
|
|
99
|
-
`Warning: Failed to load MCP config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return servers;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Load MCP servers from a config file
|
|
109
|
-
*/
|
|
110
|
-
export function loadServersFromFile(filePath: string): McpServerDefinition[] {
|
|
111
|
-
if (!existsSync(filePath)) {
|
|
112
|
-
throw new McpError(`Config file not found: ${filePath}`, 'invalid_config');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
116
|
-
let config: McpConfigFile;
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
config = JSON.parse(content);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
throw new McpError(
|
|
122
|
-
`Invalid JSON in config file ${filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
123
|
-
'invalid_config',
|
|
124
|
-
undefined,
|
|
125
|
-
error instanceof Error ? error : undefined
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return parseServersFromConfig(config, filePath);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Parse server definitions from config object
|
|
134
|
-
*/
|
|
135
|
-
export function parseServersFromConfig(
|
|
136
|
-
config: McpConfigFile,
|
|
137
|
-
sourcePath?: string
|
|
138
|
-
): McpServerDefinition[] {
|
|
139
|
-
const servers: McpServerDefinition[] = [];
|
|
140
|
-
|
|
141
|
-
if (!config.mcpServers) {
|
|
142
|
-
return servers;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
146
|
-
try {
|
|
147
|
-
const validConfig = validateServerConfig(serverConfig, name);
|
|
148
|
-
|
|
149
|
-
// Resolve relative paths for stdio commands
|
|
150
|
-
if (validConfig.transport === 'stdio' && sourcePath) {
|
|
151
|
-
validConfig.cwd = validConfig.cwd ?? dirname(sourcePath);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
servers.push({
|
|
155
|
-
name,
|
|
156
|
-
config: validConfig,
|
|
157
|
-
enabled: true,
|
|
158
|
-
});
|
|
159
|
-
} catch (error) {
|
|
160
|
-
console.warn(
|
|
161
|
-
`Warning: Invalid server config for "${name}": ${error instanceof Error ? error.message : String(error)}`
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return servers;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Validate and normalize a server configuration
|
|
171
|
-
*/
|
|
172
|
-
export function validateServerConfig(config: unknown, serverName: string): McpServerConfig {
|
|
173
|
-
if (!config || typeof config !== 'object') {
|
|
174
|
-
throw new McpError(
|
|
175
|
-
`Server config for "${serverName}" must be an object`,
|
|
176
|
-
'invalid_config',
|
|
177
|
-
serverName
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const cfg = config as Record<string, unknown>;
|
|
182
|
-
|
|
183
|
-
// Determine transport type
|
|
184
|
-
// If 'command' exists, it's stdio
|
|
185
|
-
// If 'url' exists, it's http/sse
|
|
186
|
-
// Otherwise check explicit 'transport' field
|
|
187
|
-
|
|
188
|
-
if ('command' in cfg && typeof cfg.command === 'string') {
|
|
189
|
-
return validateStdioConfig(cfg, serverName);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if ('url' in cfg && typeof cfg.url === 'string') {
|
|
193
|
-
return validateHttpConfig(cfg, serverName);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Check explicit transport
|
|
197
|
-
if ('transport' in cfg) {
|
|
198
|
-
const transport = cfg.transport;
|
|
199
|
-
if (transport === 'stdio') {
|
|
200
|
-
return validateStdioConfig(cfg, serverName);
|
|
201
|
-
}
|
|
202
|
-
if (transport === 'http' || transport === 'sse') {
|
|
203
|
-
return validateHttpConfig(cfg, serverName);
|
|
204
|
-
}
|
|
205
|
-
throw new McpError(
|
|
206
|
-
`Unknown transport type "${transport}" for server "${serverName}"`,
|
|
207
|
-
'invalid_config',
|
|
208
|
-
serverName
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
throw new McpError(
|
|
213
|
-
`Server config for "${serverName}" must have either 'command' (stdio) or 'url' (http)`,
|
|
214
|
-
'invalid_config',
|
|
215
|
-
serverName
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Validate stdio server configuration
|
|
221
|
-
*/
|
|
222
|
-
function validateStdioConfig(cfg: Record<string, unknown>, serverName: string): StdioServerConfig {
|
|
223
|
-
if (!cfg.command || typeof cfg.command !== 'string') {
|
|
224
|
-
throw new McpError(
|
|
225
|
-
`Server "${serverName}" requires a 'command' string`,
|
|
226
|
-
'invalid_config',
|
|
227
|
-
serverName
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const config: StdioServerConfig = {
|
|
232
|
-
transport: 'stdio',
|
|
233
|
-
command: cfg.command,
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
if (cfg.args !== undefined) {
|
|
237
|
-
if (!Array.isArray(cfg.args) || !cfg.args.every((a) => typeof a === 'string')) {
|
|
238
|
-
throw new McpError(
|
|
239
|
-
`Server "${serverName}" 'args' must be an array of strings`,
|
|
240
|
-
'invalid_config',
|
|
241
|
-
serverName
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
config.args = cfg.args;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (cfg.env !== undefined) {
|
|
248
|
-
if (typeof cfg.env !== 'object' || cfg.env === null) {
|
|
249
|
-
throw new McpError(
|
|
250
|
-
`Server "${serverName}" 'env' must be an object`,
|
|
251
|
-
'invalid_config',
|
|
252
|
-
serverName
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
config.env = cfg.env as Record<string, string>;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (cfg.cwd !== undefined) {
|
|
259
|
-
if (typeof cfg.cwd !== 'string') {
|
|
260
|
-
throw new McpError(
|
|
261
|
-
`Server "${serverName}" 'cwd' must be a string`,
|
|
262
|
-
'invalid_config',
|
|
263
|
-
serverName
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
config.cwd = cfg.cwd;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return config;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Validate HTTP/SSE server configuration
|
|
274
|
-
*/
|
|
275
|
-
function validateHttpConfig(cfg: Record<string, unknown>, serverName: string): HttpServerConfig {
|
|
276
|
-
if (!cfg.url || typeof cfg.url !== 'string') {
|
|
277
|
-
throw new McpError(
|
|
278
|
-
`Server "${serverName}" requires a 'url' string`,
|
|
279
|
-
'invalid_config',
|
|
280
|
-
serverName
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Validate URL
|
|
285
|
-
try {
|
|
286
|
-
new URL(cfg.url);
|
|
287
|
-
} catch {
|
|
288
|
-
throw new McpError(
|
|
289
|
-
`Server "${serverName}" has invalid URL: ${cfg.url}`,
|
|
290
|
-
'invalid_config',
|
|
291
|
-
serverName
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const transport = cfg.transport === 'sse' ? 'sse' : 'http';
|
|
296
|
-
|
|
297
|
-
const config: HttpServerConfig = {
|
|
298
|
-
transport,
|
|
299
|
-
url: cfg.url,
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
if (cfg.headers !== undefined) {
|
|
303
|
-
if (typeof cfg.headers !== 'object' || cfg.headers === null) {
|
|
304
|
-
throw new McpError(
|
|
305
|
-
`Server "${serverName}" 'headers' must be an object`,
|
|
306
|
-
'invalid_config',
|
|
307
|
-
serverName
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
config.headers = cfg.headers as Record<string, string>;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return config;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Find an MCP config file in the project
|
|
318
|
-
*/
|
|
319
|
-
export function findMcpConfigFile(projectRoot?: string): string | null {
|
|
320
|
-
const root = projectRoot ?? process.cwd();
|
|
321
|
-
|
|
322
|
-
for (const dir of PROJECT_CONFIG_DIRS) {
|
|
323
|
-
for (const filename of CONFIG_FILENAMES) {
|
|
324
|
-
const path = dir ? join(root, dir, filename) : join(root, filename);
|
|
325
|
-
if (existsSync(path)) {
|
|
326
|
-
return path;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Get the default MCP config path for a project
|
|
336
|
-
*/
|
|
337
|
-
export function getDefaultMcpConfigPath(projectRoot?: string): string {
|
|
338
|
-
const root = projectRoot ?? process.cwd();
|
|
339
|
-
return join(root, '.claude', 'mcp.json');
|
|
340
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Integration Module
|
|
3
|
-
*
|
|
4
|
-
* Provides MCP (Model Context Protocol) integration for adapters.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Types
|
|
8
|
-
export type {
|
|
9
|
-
McpTransportType,
|
|
10
|
-
StdioServerConfig,
|
|
11
|
-
HttpServerConfig,
|
|
12
|
-
McpServerConfig,
|
|
13
|
-
McpServerDefinition,
|
|
14
|
-
McpConfigFile,
|
|
15
|
-
McpServerInfo,
|
|
16
|
-
McpCapabilities,
|
|
17
|
-
McpTool,
|
|
18
|
-
McpResource,
|
|
19
|
-
McpToolResult,
|
|
20
|
-
McpContent,
|
|
21
|
-
McpTextContent,
|
|
22
|
-
McpImageContent,
|
|
23
|
-
McpResourceContent,
|
|
24
|
-
McpErrorCode,
|
|
25
|
-
} from './types.js';
|
|
26
|
-
|
|
27
|
-
export { McpError } from './types.js';
|
|
28
|
-
|
|
29
|
-
// Client
|
|
30
|
-
export type { McpClientOptions } from './client.js';
|
|
31
|
-
|
|
32
|
-
export { McpClient, createMockMcpClient } from './client.js';
|
|
33
|
-
|
|
34
|
-
// Discovery
|
|
35
|
-
export type { DiscoveryOptions } from './discovery.js';
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
discoverMcpServers,
|
|
39
|
-
loadServersFromFile,
|
|
40
|
-
parseServersFromConfig,
|
|
41
|
-
validateServerConfig,
|
|
42
|
-
findMcpConfigFile,
|
|
43
|
-
getDefaultMcpConfigPath,
|
|
44
|
-
} from './discovery.js';
|
|
45
|
-
|
|
46
|
-
// Mapper
|
|
47
|
-
export type { ToolMapping, ServerMapping } from './mapper.js';
|
|
48
|
-
|
|
49
|
-
export {
|
|
50
|
-
findKnownServerMappings,
|
|
51
|
-
autoDiscoverMappings,
|
|
52
|
-
getMappingsForServer,
|
|
53
|
-
groupMappingsByAdapter,
|
|
54
|
-
} from './mapper.js';
|
|
55
|
-
|
|
56
|
-
// Registry
|
|
57
|
-
export type {
|
|
58
|
-
ServerEntry,
|
|
59
|
-
ServerStatus,
|
|
60
|
-
RegistryEvents,
|
|
61
|
-
RegistryOptions,
|
|
62
|
-
ServerStatusSummary,
|
|
63
|
-
RegistryStatus,
|
|
64
|
-
} from './registry.js';
|
|
65
|
-
|
|
66
|
-
export { McpRegistry, createRegistry } from './registry.js';
|