fraim 2.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +445 -0
- package/bin/fraim.js +23 -0
- package/dist/src/cli/api/get-provider-client.js +41 -0
- package/dist/src/cli/api/provider-client.js +107 -0
- package/dist/src/cli/commands/add-ide.js +430 -0
- package/dist/src/cli/commands/add-provider.js +233 -0
- package/dist/src/cli/commands/doctor.js +149 -0
- package/dist/src/cli/commands/init-project.js +301 -0
- package/dist/src/cli/commands/list-overridable.js +184 -0
- package/dist/src/cli/commands/list.js +57 -0
- package/dist/src/cli/commands/login.js +84 -0
- package/dist/src/cli/commands/mcp.js +15 -0
- package/dist/src/cli/commands/migrate-project-fraim.js +42 -0
- package/dist/src/cli/commands/override.js +177 -0
- package/dist/src/cli/commands/setup.js +651 -0
- package/dist/src/cli/commands/sync.js +162 -0
- package/dist/src/cli/commands/test-mcp.js +171 -0
- package/dist/src/cli/doctor/check-runner.js +199 -0
- package/dist/src/cli/doctor/checks/global-setup-checks.js +220 -0
- package/dist/src/cli/doctor/checks/ide-config-checks.js +250 -0
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +381 -0
- package/dist/src/cli/doctor/checks/project-setup-checks.js +282 -0
- package/dist/src/cli/doctor/checks/scripts-checks.js +157 -0
- package/dist/src/cli/doctor/checks/workflow-checks.js +251 -0
- package/dist/src/cli/doctor/reporters/console-reporter.js +96 -0
- package/dist/src/cli/doctor/reporters/json-reporter.js +11 -0
- package/dist/src/cli/doctor/types.js +6 -0
- package/dist/src/cli/fraim.js +100 -0
- package/dist/src/cli/internal/device-flow-service.js +83 -0
- package/dist/src/cli/mcp/ide-formats.js +243 -0
- package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +160 -0
- package/dist/src/cli/mcp/types.js +3 -0
- package/dist/src/cli/providers/local-provider-registry.js +166 -0
- package/dist/src/cli/providers/provider-registry.js +230 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +331 -0
- package/dist/src/cli/setup/codex-local-config.js +37 -0
- package/dist/src/cli/setup/first-run.js +242 -0
- package/dist/src/cli/setup/ide-detector.js +179 -0
- package/dist/src/cli/setup/mcp-config-generator.js +192 -0
- package/dist/src/cli/setup/provider-prompts.js +339 -0
- package/dist/src/cli/utils/agent-adapters.js +126 -0
- package/dist/src/cli/utils/digest-utils.js +47 -0
- package/dist/src/cli/utils/fraim-gitignore.js +40 -0
- package/dist/src/cli/utils/platform-detection.js +258 -0
- package/dist/src/cli/utils/project-bootstrap.js +93 -0
- package/dist/src/cli/utils/remote-sync.js +315 -0
- package/dist/src/cli/utils/script-sync-utils.js +221 -0
- package/dist/src/cli/utils/version-utils.js +32 -0
- package/dist/src/core/ai-mentor.js +230 -0
- package/dist/src/core/config-loader.js +114 -0
- package/dist/src/core/config-writer.js +75 -0
- package/dist/src/core/types.js +23 -0
- package/dist/src/core/utils/git-utils.js +95 -0
- package/dist/src/core/utils/include-resolver.js +92 -0
- package/dist/src/core/utils/inheritance-parser.js +288 -0
- package/dist/src/core/utils/job-parser.js +176 -0
- package/dist/src/core/utils/local-registry-resolver.js +616 -0
- package/dist/src/core/utils/object-utils.js +11 -0
- package/dist/src/core/utils/project-fraim-migration.js +103 -0
- package/dist/src/core/utils/project-fraim-paths.js +38 -0
- package/dist/src/core/utils/provider-utils.js +18 -0
- package/dist/src/core/utils/server-startup.js +34 -0
- package/dist/src/core/utils/stub-generator.js +147 -0
- package/dist/src/core/utils/workflow-parser.js +174 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +229 -0
- package/dist/src/local-mcp-server/stdio-server.js +1698 -0
- package/dist/src/local-mcp-server/usage-collector.js +264 -0
- package/index.js +85 -0
- package/package.json +139 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DeviceFlowService = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
class DeviceFlowService {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Start the Device Flow Login
|
|
15
|
+
*/
|
|
16
|
+
async login() {
|
|
17
|
+
console.log(chalk_1.default.blue('\n🔗 Starting Authentication...'));
|
|
18
|
+
try {
|
|
19
|
+
// 1. Request device and user codes
|
|
20
|
+
const deviceCode = await this.requestDeviceCode();
|
|
21
|
+
console.log(chalk_1.default.yellow('\nACTION REQUIRED:'));
|
|
22
|
+
console.log(`1. Go to: ${chalk_1.default.cyan.underline(deviceCode.verification_uri)}`);
|
|
23
|
+
console.log(`2. Enter the code: ${chalk_1.default.bold.green(deviceCode.user_code)}`);
|
|
24
|
+
console.log(chalk_1.default.gray(`\nWaiting for authorization (expires in ${Math.floor(deviceCode.expires_in / 60)} minutes)...`));
|
|
25
|
+
// 2. Poll for the access token
|
|
26
|
+
const token = await this.pollForToken(deviceCode.device_code, deviceCode.interval);
|
|
27
|
+
console.log(chalk_1.default.green('\n✅ Authentication Successful!'));
|
|
28
|
+
return token;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error(chalk_1.default.red(`\n❌ Authentication failed: ${error.message}`));
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async requestDeviceCode() {
|
|
36
|
+
const response = await axios_1.default.post(this.config.authUrl, {
|
|
37
|
+
client_id: this.config.clientId,
|
|
38
|
+
scope: this.config.scope
|
|
39
|
+
}, {
|
|
40
|
+
headers: { Accept: 'application/json' }
|
|
41
|
+
});
|
|
42
|
+
return response.data;
|
|
43
|
+
}
|
|
44
|
+
async pollForToken(deviceCode, interval) {
|
|
45
|
+
let currentInterval = interval * 1000;
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const poll = async () => {
|
|
48
|
+
try {
|
|
49
|
+
const response = await axios_1.default.post(this.config.tokenUrl, {
|
|
50
|
+
client_id: this.config.clientId,
|
|
51
|
+
device_code: deviceCode,
|
|
52
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
53
|
+
}, {
|
|
54
|
+
headers: { Accept: 'application/json' }
|
|
55
|
+
});
|
|
56
|
+
if (response.data.access_token) {
|
|
57
|
+
resolve(response.data.access_token);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (response.data.error) {
|
|
61
|
+
const error = response.data.error;
|
|
62
|
+
if (error === 'authorization_pending') {
|
|
63
|
+
// Keep polling
|
|
64
|
+
setTimeout(poll, currentInterval);
|
|
65
|
+
}
|
|
66
|
+
else if (error === 'slow_down') {
|
|
67
|
+
currentInterval += 5000;
|
|
68
|
+
setTimeout(poll, currentInterval);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
reject(new Error(response.data.error_description || error));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
reject(error);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
setTimeout(poll, currentInterval);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.DeviceFlowService = DeviceFlowService;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// IDE Format Adapters - transform logical server structure to IDE-specific formats
|
|
3
|
+
// Uses the centralized registry to determine server types
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.IDE_FORMATS = exports.CodexFormat = exports.WindsurfFormat = exports.ClaudeCodeFormat = exports.ClaudeFormat = exports.VSCodeFormat = exports.KiroFormat = exports.StandardFormat = void 0;
|
|
6
|
+
exports.getIDEFormat = getIDEFormat;
|
|
7
|
+
const mcp_server_registry_1 = require("./mcp-server-registry");
|
|
8
|
+
const provider_registry_1 = require("../providers/provider-registry");
|
|
9
|
+
// Standard format (Cursor, Kiro, Antigravity, etc.)
|
|
10
|
+
class StandardFormat {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.name = 'standard';
|
|
13
|
+
}
|
|
14
|
+
transform(servers) {
|
|
15
|
+
const mcpServers = {};
|
|
16
|
+
for (const [key, server] of servers) {
|
|
17
|
+
if (server.url) {
|
|
18
|
+
// HTTP server - use serverUrl for standard format
|
|
19
|
+
mcpServers[key] = {
|
|
20
|
+
serverUrl: server.url,
|
|
21
|
+
headers: server.headers
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// stdio server
|
|
26
|
+
mcpServers[key] = {
|
|
27
|
+
command: server.command,
|
|
28
|
+
...(server.args && { args: server.args }),
|
|
29
|
+
...(server.env && { env: server.env })
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { mcpServers };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.StandardFormat = StandardFormat;
|
|
37
|
+
// Kiro format (also used by Cursor)
|
|
38
|
+
class KiroFormat {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.name = 'kiro';
|
|
41
|
+
}
|
|
42
|
+
transform(servers) {
|
|
43
|
+
const mcpServers = {};
|
|
44
|
+
for (const [key, server] of servers) {
|
|
45
|
+
if (server.url) {
|
|
46
|
+
// HTTP server - use url (not serverUrl)
|
|
47
|
+
mcpServers[key] = {
|
|
48
|
+
url: server.url,
|
|
49
|
+
headers: server.headers
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// stdio server
|
|
54
|
+
mcpServers[key] = {
|
|
55
|
+
command: server.command,
|
|
56
|
+
...(server.args && { args: server.args }),
|
|
57
|
+
...(server.env && { env: server.env })
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { mcpServers };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.KiroFormat = KiroFormat;
|
|
65
|
+
// VSCode format
|
|
66
|
+
class VSCodeFormat {
|
|
67
|
+
constructor() {
|
|
68
|
+
this.name = 'vscode';
|
|
69
|
+
}
|
|
70
|
+
transform(servers) {
|
|
71
|
+
const vscodeServers = {};
|
|
72
|
+
for (const [key, server] of servers) {
|
|
73
|
+
if (server.url) {
|
|
74
|
+
// HTTP server
|
|
75
|
+
vscodeServers[key] = {
|
|
76
|
+
type: 'http',
|
|
77
|
+
url: server.url,
|
|
78
|
+
headers: server.headers
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// stdio server
|
|
83
|
+
vscodeServers[key] = {
|
|
84
|
+
type: 'stdio',
|
|
85
|
+
command: server.command,
|
|
86
|
+
...(server.args && { args: server.args }),
|
|
87
|
+
...(server.env && { env: server.env })
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { servers: vscodeServers };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.VSCodeFormat = VSCodeFormat;
|
|
95
|
+
// Claude Desktop format (excludes provider servers - Issue #132)
|
|
96
|
+
class ClaudeFormat {
|
|
97
|
+
constructor() {
|
|
98
|
+
this.name = 'claude';
|
|
99
|
+
}
|
|
100
|
+
async transform(servers) {
|
|
101
|
+
const mcpServers = {};
|
|
102
|
+
for (const [key, server] of servers) {
|
|
103
|
+
// Skip all provider servers - they break Claude Desktop
|
|
104
|
+
// See: https://github.com/mathursrus/FRAIM/issues/132
|
|
105
|
+
if (await (0, mcp_server_registry_1.isProviderServer)(key)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
// Only include base servers (git, playwright, fraim)
|
|
109
|
+
mcpServers[key] = {
|
|
110
|
+
command: server.command,
|
|
111
|
+
...(server.args && { args: server.args }),
|
|
112
|
+
...(server.env && { env: server.env })
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return { mcpServers };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ClaudeFormat = ClaudeFormat;
|
|
119
|
+
// Claude Code format (supports HTTP servers with VSCode-style format)
|
|
120
|
+
class ClaudeCodeFormat {
|
|
121
|
+
constructor() {
|
|
122
|
+
this.name = 'claude-code';
|
|
123
|
+
}
|
|
124
|
+
transform(servers) {
|
|
125
|
+
const mcpServers = {};
|
|
126
|
+
for (const [key, server] of servers) {
|
|
127
|
+
if (server.url) {
|
|
128
|
+
// HTTP server - use VSCode-style format
|
|
129
|
+
mcpServers[key] = {
|
|
130
|
+
type: 'http',
|
|
131
|
+
url: server.url,
|
|
132
|
+
headers: server.headers
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// stdio server
|
|
137
|
+
mcpServers[key] = {
|
|
138
|
+
command: server.command,
|
|
139
|
+
...(server.args && { args: server.args }),
|
|
140
|
+
...(server.env && { env: server.env })
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { mcpServers };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.ClaudeCodeFormat = ClaudeCodeFormat;
|
|
148
|
+
// Windsurf format (uses server-fetch wrapper for HTTP servers)
|
|
149
|
+
class WindsurfFormat {
|
|
150
|
+
constructor() {
|
|
151
|
+
this.name = 'windsurf';
|
|
152
|
+
}
|
|
153
|
+
async transform(servers) {
|
|
154
|
+
const mcpServers = {};
|
|
155
|
+
for (const [key, server] of servers) {
|
|
156
|
+
if (server.url) {
|
|
157
|
+
// HTTP server - wrap with server-fetch
|
|
158
|
+
const provider = await (0, provider_registry_1.getProvider)(key);
|
|
159
|
+
const tokenEnvVar = provider?.id ? `${provider.id.toUpperCase()}_TOKEN` : undefined;
|
|
160
|
+
if (tokenEnvVar && server.headers?.Authorization) {
|
|
161
|
+
const token = server.headers.Authorization.replace('Bearer ', '');
|
|
162
|
+
mcpServers[key] = {
|
|
163
|
+
command: 'npx',
|
|
164
|
+
args: ['-y', '@modelcontextprotocol/server-fetch', server.url],
|
|
165
|
+
env: {
|
|
166
|
+
[tokenEnvVar]: token
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// stdio server
|
|
173
|
+
mcpServers[key] = {
|
|
174
|
+
command: server.command,
|
|
175
|
+
...(server.args && { args: server.args }),
|
|
176
|
+
...(server.env && { env: server.env })
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { mcpServers };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.WindsurfFormat = WindsurfFormat;
|
|
184
|
+
// Codex format (TOML)
|
|
185
|
+
class CodexFormat {
|
|
186
|
+
constructor() {
|
|
187
|
+
this.name = 'codex';
|
|
188
|
+
}
|
|
189
|
+
transform(servers) {
|
|
190
|
+
const sections = [];
|
|
191
|
+
for (const [key, server] of servers) {
|
|
192
|
+
if (server.url) {
|
|
193
|
+
// HTTP server
|
|
194
|
+
const token = server.headers?.Authorization?.replace('Bearer ', '') || '';
|
|
195
|
+
const escapedToken = this.escapeToml(token);
|
|
196
|
+
const escapedUrl = this.escapeToml(server.url);
|
|
197
|
+
sections.push(`[mcp_servers.${key}]`);
|
|
198
|
+
sections.push(`url = "${escapedUrl}"`);
|
|
199
|
+
sections.push(`http_headers = { Authorization = "Bearer ${escapedToken}" }`);
|
|
200
|
+
sections.push('');
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// stdio server
|
|
204
|
+
sections.push(`[mcp_servers.${key}]`);
|
|
205
|
+
sections.push(`command = "${this.escapeToml(server.command || '')}"`);
|
|
206
|
+
if (server.args && server.args.length > 0) {
|
|
207
|
+
const argsStr = server.args.map(arg => `"${this.escapeToml(arg)}"`).join(', ');
|
|
208
|
+
sections.push(`args = [${argsStr}]`);
|
|
209
|
+
}
|
|
210
|
+
if (server.env) {
|
|
211
|
+
sections.push('');
|
|
212
|
+
sections.push(`[mcp_servers.${key}.env]`);
|
|
213
|
+
for (const [envKey, envValue] of Object.entries(server.env)) {
|
|
214
|
+
sections.push(`${envKey} = "${this.escapeToml(envValue)}"`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
sections.push('');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return sections.join('\n');
|
|
221
|
+
}
|
|
222
|
+
escapeToml(value) {
|
|
223
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.CodexFormat = CodexFormat;
|
|
227
|
+
// Format registry
|
|
228
|
+
exports.IDE_FORMATS = {
|
|
229
|
+
standard: new StandardFormat(),
|
|
230
|
+
kiro: new KiroFormat(),
|
|
231
|
+
vscode: new VSCodeFormat(),
|
|
232
|
+
claude: new ClaudeFormat(),
|
|
233
|
+
'claude-code': new ClaudeCodeFormat(),
|
|
234
|
+
windsurf: new WindsurfFormat(),
|
|
235
|
+
codex: new CodexFormat()
|
|
236
|
+
};
|
|
237
|
+
function getIDEFormat(configType) {
|
|
238
|
+
const format = exports.IDE_FORMATS[configType];
|
|
239
|
+
if (!format) {
|
|
240
|
+
throw new Error(`Unsupported IDE format: ${configType}`);
|
|
241
|
+
}
|
|
242
|
+
return format;
|
|
243
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// MCP Server Builder - builds logical server structure (format-agnostic)
|
|
3
|
+
// Uses the centralized registry for all server definitions
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.MCPServerBuilder = void 0;
|
|
6
|
+
const mcp_server_registry_1 = require("./mcp-server-registry");
|
|
7
|
+
class MCPServerBuilder {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.servers = new Map();
|
|
10
|
+
}
|
|
11
|
+
addBaseServers(fraimKey) {
|
|
12
|
+
const baseServers = (0, mcp_server_registry_1.buildAllBaseServers)(fraimKey);
|
|
13
|
+
for (const [id, server] of baseServers) {
|
|
14
|
+
this.servers.set(id, server);
|
|
15
|
+
}
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
async addProvider(providerId, token, config) {
|
|
19
|
+
try {
|
|
20
|
+
const server = await (0, mcp_server_registry_1.buildProviderMCPServer)(providerId, token, config);
|
|
21
|
+
this.servers.set(providerId, server);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
// Always warn user when MCP server can't be built
|
|
25
|
+
// Common case: Jira without baseUrl/email config
|
|
26
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
27
|
+
console.warn(`⚠️ Skipping ${providerId} MCP server: ${errorMessage}`);
|
|
28
|
+
}
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
// Legacy methods for backward compatibility - delegate to addProvider
|
|
32
|
+
async addGitHub(token) {
|
|
33
|
+
return await this.addProvider('github', token);
|
|
34
|
+
}
|
|
35
|
+
async addGitLab(token) {
|
|
36
|
+
return await this.addProvider('gitlab', token);
|
|
37
|
+
}
|
|
38
|
+
async addJira(token, config) {
|
|
39
|
+
return await this.addProvider('jira', token, config);
|
|
40
|
+
}
|
|
41
|
+
getServers() {
|
|
42
|
+
return new Map(this.servers);
|
|
43
|
+
}
|
|
44
|
+
toObject() {
|
|
45
|
+
return Object.fromEntries(this.servers);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.MCPServerBuilder = MCPServerBuilder;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BASE_MCP_SERVERS = void 0;
|
|
4
|
+
exports.buildProviderMCPServer = buildProviderMCPServer;
|
|
5
|
+
exports.getBaseMCPServer = getBaseMCPServer;
|
|
6
|
+
exports.getProviderMCPServerIds = getProviderMCPServerIds;
|
|
7
|
+
exports.getAllMCPServerIds = getAllMCPServerIds;
|
|
8
|
+
exports.isProviderServer = isProviderServer;
|
|
9
|
+
exports.isHTTPServer = isHTTPServer;
|
|
10
|
+
exports.buildAllBaseServers = buildAllBaseServers;
|
|
11
|
+
const provider_registry_1 = require("../providers/provider-registry");
|
|
12
|
+
exports.BASE_MCP_SERVERS = [
|
|
13
|
+
{
|
|
14
|
+
id: 'git',
|
|
15
|
+
name: 'Git',
|
|
16
|
+
description: 'Git repository operations (commit, branch, merge, etc.)',
|
|
17
|
+
buildServer: () => ({
|
|
18
|
+
command: 'npx',
|
|
19
|
+
args: ['-y', '@cyanheads/git-mcp-server']
|
|
20
|
+
})
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'playwright',
|
|
24
|
+
name: 'Playwright',
|
|
25
|
+
description: 'Browser automation and testing',
|
|
26
|
+
buildServer: () => ({
|
|
27
|
+
command: 'npx',
|
|
28
|
+
args: ['-y', '@playwright/mcp']
|
|
29
|
+
})
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'fraim',
|
|
33
|
+
name: 'FRAIM',
|
|
34
|
+
description: 'FRAIM job orchestration and mentoring',
|
|
35
|
+
buildServer: (fraimKey) => ({
|
|
36
|
+
command: 'npx',
|
|
37
|
+
args: ['-y', 'fraim-framework@latest', 'mcp'],
|
|
38
|
+
env: {
|
|
39
|
+
// Include API key for IDE configs (Codex, VSCode, etc.)
|
|
40
|
+
// The stdio-server will use this if set, otherwise falls back to ~/.fraim/config.json
|
|
41
|
+
FRAIM_API_KEY: fraimKey,
|
|
42
|
+
FRAIM_REMOTE_URL: 'https://fraim.wellnessatwork.me'
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// PROVIDER MCP SERVER BUILDER (uses provider registry)
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Build an MCP server for a provider using its definition from the registry
|
|
52
|
+
*/
|
|
53
|
+
async function buildProviderMCPServer(providerId, token, config) {
|
|
54
|
+
// Get provider definition from registry (server or local fallback)
|
|
55
|
+
const provider = await (0, provider_registry_1.getProvider)(providerId);
|
|
56
|
+
if (!provider) {
|
|
57
|
+
throw new Error(`Unknown provider: ${providerId}`);
|
|
58
|
+
}
|
|
59
|
+
if (!provider.mcpServer) {
|
|
60
|
+
throw new Error(`Provider ${providerId} does not have an MCP server configuration`);
|
|
61
|
+
}
|
|
62
|
+
const mcpConfig = provider.mcpServer;
|
|
63
|
+
// Validate config if provider requires additional config
|
|
64
|
+
if (provider.hasAdditionalConfig) {
|
|
65
|
+
const validation = await (0, provider_registry_1.validateProviderConfig)(providerId, config || {});
|
|
66
|
+
if (!validation.valid) {
|
|
67
|
+
throw new Error(`${provider.displayName} requires additional configuration: ${validation.missing.join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Build MCP server based on type
|
|
71
|
+
if (mcpConfig.type === 'http') {
|
|
72
|
+
return buildHTTPServer(mcpConfig, token);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return buildStdioServer(mcpConfig, token, config);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Build an HTTP MCP server
|
|
80
|
+
*/
|
|
81
|
+
function buildHTTPServer(mcpConfig, token) {
|
|
82
|
+
if (!mcpConfig.url) {
|
|
83
|
+
throw new Error('HTTP MCP server requires url');
|
|
84
|
+
}
|
|
85
|
+
const authHeader = mcpConfig.authHeaderTemplate?.replace('{token}', token) || `Bearer ${token}`;
|
|
86
|
+
return {
|
|
87
|
+
url: mcpConfig.url,
|
|
88
|
+
headers: {
|
|
89
|
+
Authorization: authHeader
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build a stdio MCP server
|
|
95
|
+
*/
|
|
96
|
+
function buildStdioServer(mcpConfig, token, config) {
|
|
97
|
+
if (!mcpConfig.command) {
|
|
98
|
+
throw new Error('Stdio MCP server requires command');
|
|
99
|
+
}
|
|
100
|
+
const resolveTemplate = (template) => {
|
|
101
|
+
let value = template.replace('{token}', token);
|
|
102
|
+
if (config) {
|
|
103
|
+
for (const [configKey, configValue] of Object.entries(config)) {
|
|
104
|
+
value = value.replace(`{config.${configKey}}`, String(configValue));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
};
|
|
109
|
+
// Build environment variables from template
|
|
110
|
+
const env = {};
|
|
111
|
+
if (mcpConfig.envTemplate) {
|
|
112
|
+
for (const [key, template] of Object.entries(mcpConfig.envTemplate)) {
|
|
113
|
+
let value = resolveTemplate(template);
|
|
114
|
+
// Handle URL normalization for Jira
|
|
115
|
+
if (key === 'JIRA_URL' && value && !value.startsWith('http')) {
|
|
116
|
+
value = `https://${value}`;
|
|
117
|
+
}
|
|
118
|
+
env[key] = value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
command: mcpConfig.command,
|
|
123
|
+
args: (mcpConfig.args || []).map(resolveTemplate),
|
|
124
|
+
env
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// REGISTRY LOOKUP FUNCTIONS
|
|
129
|
+
// ============================================================================
|
|
130
|
+
function getBaseMCPServer(id) {
|
|
131
|
+
return exports.BASE_MCP_SERVERS.find(server => server.id === id);
|
|
132
|
+
}
|
|
133
|
+
async function getProviderMCPServerIds() {
|
|
134
|
+
// This would need to fetch all providers and filter those with MCP servers
|
|
135
|
+
// For now, return empty array - this function is not critical
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
function getAllMCPServerIds() {
|
|
139
|
+
// Return only base server IDs
|
|
140
|
+
// Provider server IDs are dynamic and come from the registry
|
|
141
|
+
return exports.BASE_MCP_SERVERS.map(s => s.id);
|
|
142
|
+
}
|
|
143
|
+
async function isProviderServer(serverId) {
|
|
144
|
+
const provider = await (0, provider_registry_1.getProvider)(serverId);
|
|
145
|
+
return provider !== undefined && provider.mcpServer !== undefined;
|
|
146
|
+
}
|
|
147
|
+
async function isHTTPServer(serverId) {
|
|
148
|
+
const provider = await (0, provider_registry_1.getProvider)(serverId);
|
|
149
|
+
return provider?.mcpServer?.type === 'http';
|
|
150
|
+
}
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// BUILDER HELPERS
|
|
153
|
+
// ============================================================================
|
|
154
|
+
function buildAllBaseServers(fraimKey) {
|
|
155
|
+
const servers = new Map();
|
|
156
|
+
for (const def of exports.BASE_MCP_SERVERS) {
|
|
157
|
+
servers.set(def.id, def.buildServer(fraimKey));
|
|
158
|
+
}
|
|
159
|
+
return servers;
|
|
160
|
+
}
|