@l4yercak3/cli 1.2.7 ā 1.2.9
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/.claude/settings.local.json +2 -1
- package/bin/cli.js +6 -0
- package/package.json +1 -1
- package/src/commands/mcp-server.js +54 -1
- package/src/commands/mcp-setup.js +510 -0
- package/src/commands/spread.js +54 -35
- package/src/mcp/registry/domains/applications.js +4 -4
- package/src/mcp/registry/domains/core.js +2 -4
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"Bash(git push:*)",
|
|
18
18
|
"Bash(session\" errors when trying to use features.\n\nNow the login command validates the session with the backend first.\nIf validation fails, it clears the local session and prompts for\nfresh login, avoiding the confusing loop.\n\nš¤ Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
|
19
19
|
"Bash(npm run build:*)",
|
|
20
|
-
"Bash(npm version:*)"
|
|
20
|
+
"Bash(npm version:*)",
|
|
21
|
+
"Bash(npm run lint:fix:*)"
|
|
21
22
|
]
|
|
22
23
|
}
|
|
23
24
|
}
|
package/bin/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ const spreadCommand = require('../src/commands/spread');
|
|
|
17
17
|
const apiKeysCommand = require('../src/commands/api-keys');
|
|
18
18
|
const upgradeCommand = require('../src/commands/upgrade');
|
|
19
19
|
const mcpServerCommand = require('../src/commands/mcp-server');
|
|
20
|
+
const mcpSetupCommand = require('../src/commands/mcp-setup');
|
|
20
21
|
|
|
21
22
|
// Create CLI program
|
|
22
23
|
const program = new Command();
|
|
@@ -72,6 +73,11 @@ program
|
|
|
72
73
|
.description(mcpServerCommand.description)
|
|
73
74
|
.action(mcpServerCommand.handler);
|
|
74
75
|
|
|
76
|
+
program
|
|
77
|
+
.command(mcpSetupCommand.command)
|
|
78
|
+
.description(mcpSetupCommand.description)
|
|
79
|
+
.action(mcpSetupCommand.handler);
|
|
80
|
+
|
|
75
81
|
// Show logo and welcome message if no command provided
|
|
76
82
|
if (process.argv.length === 2) {
|
|
77
83
|
console.log(''); // initial spacing
|
package/package.json
CHANGED
|
@@ -15,13 +15,66 @@
|
|
|
15
15
|
|
|
16
16
|
const { startServer } = require('../mcp/server');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Check if running in interactive terminal (manual invocation)
|
|
20
|
+
* vs being called by an MCP client (piped stdin/stdout)
|
|
21
|
+
*/
|
|
22
|
+
function isInteractiveTerminal() {
|
|
23
|
+
return process.stdin.isTTY && process.stdout.isTTY;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Show setup instructions when run manually
|
|
28
|
+
*/
|
|
29
|
+
function showManualInvocationHelp() {
|
|
30
|
+
// Use stderr since stdout is reserved for MCP protocol
|
|
31
|
+
console.error('');
|
|
32
|
+
console.error('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
33
|
+
console.error('ā L4YERCAK3 MCP Server - Setup Guide ā');
|
|
34
|
+
console.error('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
35
|
+
console.error('');
|
|
36
|
+
console.error('This command starts the MCP server for AI assistant integration.');
|
|
37
|
+
console.error('It\'s typically called by your MCP client, not run directly.');
|
|
38
|
+
console.error('');
|
|
39
|
+
console.error('š¦ Claude Code (CLI):');
|
|
40
|
+
console.error(' l4yercak3 mcp-setup');
|
|
41
|
+
console.error(' # or manually:');
|
|
42
|
+
console.error(' claude mcp add l4yercak3 -- npx @l4yercak3/cli mcp-server');
|
|
43
|
+
console.error('');
|
|
44
|
+
console.error('š„ļø Claude Desktop:');
|
|
45
|
+
console.error(' Add to ~/Library/Application Support/Claude/claude_desktop_config.json:');
|
|
46
|
+
console.error(' {');
|
|
47
|
+
console.error(' "mcpServers": {');
|
|
48
|
+
console.error(' "l4yercak3": {');
|
|
49
|
+
console.error(' "command": "npx",');
|
|
50
|
+
console.error(' "args": ["@l4yercak3/cli", "mcp-server"]');
|
|
51
|
+
console.error(' }');
|
|
52
|
+
console.error(' }');
|
|
53
|
+
console.error(' }');
|
|
54
|
+
console.error('');
|
|
55
|
+
console.error('š Other MCP Clients (Cursor, Cody, Continue, etc.):');
|
|
56
|
+
console.error(' Command: npx @l4yercak3/cli mcp-server');
|
|
57
|
+
console.error(' Transport: stdio');
|
|
58
|
+
console.error('');
|
|
59
|
+
console.error('š” Requirements:');
|
|
60
|
+
console.error(' ⢠Logged in to L4YERCAK3 (l4yercak3 login)');
|
|
61
|
+
console.error('');
|
|
62
|
+
}
|
|
63
|
+
|
|
18
64
|
module.exports = {
|
|
19
65
|
command: 'mcp-server',
|
|
20
|
-
description: 'Start the MCP server for
|
|
66
|
+
description: 'Start the MCP server for AI assistant integration',
|
|
21
67
|
handler: async () => {
|
|
22
68
|
// Note: All output goes to stderr because stdout is used for MCP protocol
|
|
23
69
|
// console.error is used intentionally here
|
|
24
70
|
|
|
71
|
+
// If running in an interactive terminal (not piped), show help
|
|
72
|
+
if (isInteractiveTerminal()) {
|
|
73
|
+
showManualInvocationHelp();
|
|
74
|
+
console.error('Starting server anyway (press Ctrl+C to exit)...');
|
|
75
|
+
console.error('');
|
|
76
|
+
}
|
|
77
|
+
|
|
25
78
|
try {
|
|
26
79
|
await startServer();
|
|
27
80
|
} catch (error) {
|
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Setup Command
|
|
3
|
+
*
|
|
4
|
+
* Configures MCP clients to use the L4YERCAK3 MCP server.
|
|
5
|
+
* Supports Claude Code CLI, Claude Desktop, and other MCP-compatible clients.
|
|
6
|
+
*
|
|
7
|
+
* @module commands/mcp-setup
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const chalk = require('chalk');
|
|
15
|
+
const inquirer = require('inquirer');
|
|
16
|
+
const configManager = require('../config/config-manager');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if Claude CLI is installed
|
|
20
|
+
*/
|
|
21
|
+
function isClaudeInstalled() {
|
|
22
|
+
try {
|
|
23
|
+
execSync('claude --version', { stdio: 'pipe' });
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if MCP server is already configured in Claude
|
|
32
|
+
*/
|
|
33
|
+
function isMcpConfigured() {
|
|
34
|
+
try {
|
|
35
|
+
const result = execSync('claude mcp list', {
|
|
36
|
+
stdio: 'pipe',
|
|
37
|
+
encoding: 'utf8'
|
|
38
|
+
});
|
|
39
|
+
return result.includes('l4yercak3');
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configure Claude MCP with l4yercak3 server
|
|
47
|
+
*/
|
|
48
|
+
async function configureMcp(scope = 'user') {
|
|
49
|
+
const scopeFlag = scope === 'project' ? '--scope project' : '';
|
|
50
|
+
const command = `claude mcp add l4yercak3 ${scopeFlag} -- npx @l4yercak3/cli mcp-server`.trim();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
execSync(command, { stdio: 'inherit' });
|
|
54
|
+
return true;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(chalk.red(`Failed to configure MCP: ${error.message}`));
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove existing MCP configuration from Claude Code
|
|
63
|
+
*/
|
|
64
|
+
function removeMcpConfig() {
|
|
65
|
+
try {
|
|
66
|
+
execSync('claude mcp remove l4yercak3', { stdio: 'pipe' });
|
|
67
|
+
return true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get Claude Desktop config path based on platform
|
|
75
|
+
*/
|
|
76
|
+
function getClaudeDesktopConfigPath() {
|
|
77
|
+
const platform = os.platform();
|
|
78
|
+
const home = os.homedir();
|
|
79
|
+
|
|
80
|
+
if (platform === 'darwin') {
|
|
81
|
+
return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
82
|
+
} else if (platform === 'win32') {
|
|
83
|
+
return path.join(process.env.APPDATA || home, 'Claude', 'claude_desktop_config.json');
|
|
84
|
+
} else {
|
|
85
|
+
// Linux
|
|
86
|
+
return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the full path to npx executable
|
|
92
|
+
* Claude Desktop needs absolute paths since it doesn't inherit shell PATH
|
|
93
|
+
*/
|
|
94
|
+
function getNpxPath() {
|
|
95
|
+
try {
|
|
96
|
+
const npxPath = execSync('which npx', { stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
97
|
+
return npxPath || 'npx';
|
|
98
|
+
} catch {
|
|
99
|
+
// Fallback to common locations
|
|
100
|
+
const commonPaths = [
|
|
101
|
+
'/usr/local/bin/npx',
|
|
102
|
+
'/opt/homebrew/bin/npx',
|
|
103
|
+
'/usr/bin/npx',
|
|
104
|
+
path.join(os.homedir(), '.nvm/versions/node', 'current', 'bin', 'npx'),
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
for (const p of commonPaths) {
|
|
108
|
+
if (fs.existsSync(p)) {
|
|
109
|
+
return p;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return 'npx'; // Last resort fallback
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the full path to l4yercak3 CLI if globally installed
|
|
119
|
+
* Returns null if not found
|
|
120
|
+
*/
|
|
121
|
+
function getL4yercak3Path() {
|
|
122
|
+
try {
|
|
123
|
+
const cliPath = execSync('which l4yercak3', { stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
124
|
+
return cliPath || null;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the best command configuration for Claude Desktop
|
|
132
|
+
* Prefers globally installed l4yercak3 over npx for reliability
|
|
133
|
+
*/
|
|
134
|
+
function getClaudeDesktopCommand() {
|
|
135
|
+
const l4yercak3Path = getL4yercak3Path();
|
|
136
|
+
|
|
137
|
+
if (l4yercak3Path) {
|
|
138
|
+
// Use globally installed CLI directly (more reliable)
|
|
139
|
+
return {
|
|
140
|
+
command: l4yercak3Path,
|
|
141
|
+
args: ['mcp-server'],
|
|
142
|
+
description: `${l4yercak3Path} mcp-server`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Fall back to npx
|
|
147
|
+
const npxPath = getNpxPath();
|
|
148
|
+
return {
|
|
149
|
+
command: npxPath,
|
|
150
|
+
args: ['@l4yercak3/cli', 'mcp-server'],
|
|
151
|
+
description: `${npxPath} @l4yercak3/cli mcp-server`
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if Claude Desktop config exists
|
|
157
|
+
*/
|
|
158
|
+
function hasClaudeDesktopConfig() {
|
|
159
|
+
return fs.existsSync(getClaudeDesktopConfigPath());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Configure Claude Desktop with l4yercak3 MCP server
|
|
164
|
+
*/
|
|
165
|
+
function configureClaudeDesktop() {
|
|
166
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
167
|
+
let config = { mcpServers: {} };
|
|
168
|
+
|
|
169
|
+
// Read existing config if it exists
|
|
170
|
+
if (fs.existsSync(configPath)) {
|
|
171
|
+
try {
|
|
172
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
173
|
+
config = JSON.parse(content);
|
|
174
|
+
if (!config.mcpServers) {
|
|
175
|
+
config.mcpServers = {};
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// If parsing fails, start fresh but preserve the file structure
|
|
179
|
+
config = { mcpServers: {} };
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// Ensure directory exists
|
|
183
|
+
const configDir = path.dirname(configPath);
|
|
184
|
+
if (!fs.existsSync(configDir)) {
|
|
185
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Add l4yercak3 server with full path
|
|
190
|
+
// Claude Desktop doesn't inherit shell PATH, so we need absolute paths
|
|
191
|
+
const cmdConfig = getClaudeDesktopCommand();
|
|
192
|
+
|
|
193
|
+
config.mcpServers.l4yercak3 = {
|
|
194
|
+
command: cmdConfig.command,
|
|
195
|
+
args: cmdConfig.args
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Write config
|
|
199
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
200
|
+
return { configPath, commandDescription: cmdConfig.description };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Remove l4yercak3 from Claude Desktop config
|
|
205
|
+
*/
|
|
206
|
+
function removeClaudeDesktopConfig() {
|
|
207
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
208
|
+
|
|
209
|
+
if (!fs.existsSync(configPath)) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
215
|
+
const config = JSON.parse(content);
|
|
216
|
+
|
|
217
|
+
if (config.mcpServers && config.mcpServers.l4yercak3) {
|
|
218
|
+
delete config.mcpServers.l4yercak3;
|
|
219
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if l4yercak3 is configured in Claude Desktop
|
|
231
|
+
*/
|
|
232
|
+
function isClaudeDesktopConfigured() {
|
|
233
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
234
|
+
|
|
235
|
+
if (!fs.existsSync(configPath)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
241
|
+
const config = JSON.parse(content);
|
|
242
|
+
return config.mcpServers && config.mcpServers.l4yercak3;
|
|
243
|
+
} catch {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = {
|
|
249
|
+
command: 'mcp-setup',
|
|
250
|
+
description: 'Configure MCP clients to use the L4YERCAK3 server',
|
|
251
|
+
handler: async () => {
|
|
252
|
+
console.log('');
|
|
253
|
+
console.log(chalk.bold.hex('#9F7AEA')('š§ L4YERCAK3 MCP Setup\n'));
|
|
254
|
+
|
|
255
|
+
// Check if logged in
|
|
256
|
+
const session = configManager.getSession();
|
|
257
|
+
if (!session) {
|
|
258
|
+
console.log(chalk.yellow('ā ļø Not logged in to L4YERCAK3'));
|
|
259
|
+
console.log(chalk.gray(' Run `l4yercak3 login` first to authenticate.\n'));
|
|
260
|
+
console.log(chalk.gray(' The MCP server needs your session to access L4YERCAK3 features.'));
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
console.log(chalk.green('ā Logged in as: ') + chalk.white(session.email || session.userId));
|
|
265
|
+
console.log('');
|
|
266
|
+
|
|
267
|
+
// Detect available clients
|
|
268
|
+
const hasClaudeCli = isClaudeInstalled();
|
|
269
|
+
const hasClaudeDesktop = hasClaudeDesktopConfig() || fs.existsSync(path.dirname(getClaudeDesktopConfigPath()));
|
|
270
|
+
|
|
271
|
+
// Build client choices
|
|
272
|
+
const clientChoices = [];
|
|
273
|
+
|
|
274
|
+
if (hasClaudeCli) {
|
|
275
|
+
const configured = isMcpConfigured();
|
|
276
|
+
clientChoices.push({
|
|
277
|
+
name: `Claude Code (CLI)${configured ? chalk.gray(' - already configured') : ''}`,
|
|
278
|
+
value: 'claude-code',
|
|
279
|
+
configured
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
clientChoices.push({
|
|
284
|
+
name: `Claude Desktop${isClaudeDesktopConfigured() ? chalk.gray(' - already configured') : ''}`,
|
|
285
|
+
value: 'claude-desktop',
|
|
286
|
+
configured: isClaudeDesktopConfigured()
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
clientChoices.push({
|
|
290
|
+
name: 'Other MCP client (show manual instructions)',
|
|
291
|
+
value: 'other'
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Ask which client to configure
|
|
295
|
+
const { client } = await inquirer.prompt([
|
|
296
|
+
{
|
|
297
|
+
type: 'list',
|
|
298
|
+
name: 'client',
|
|
299
|
+
message: 'Which MCP client would you like to configure?',
|
|
300
|
+
choices: clientChoices,
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
console.log('');
|
|
305
|
+
|
|
306
|
+
if (client === 'claude-code') {
|
|
307
|
+
await setupClaudeCode();
|
|
308
|
+
} else if (client === 'claude-desktop') {
|
|
309
|
+
await setupClaudeDesktop();
|
|
310
|
+
} else {
|
|
311
|
+
showOtherClientInstructions();
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Setup Claude Code CLI
|
|
318
|
+
*/
|
|
319
|
+
async function setupClaudeCode() {
|
|
320
|
+
const alreadyConfigured = isMcpConfigured();
|
|
321
|
+
|
|
322
|
+
if (alreadyConfigured) {
|
|
323
|
+
const { action } = await inquirer.prompt([
|
|
324
|
+
{
|
|
325
|
+
type: 'list',
|
|
326
|
+
name: 'action',
|
|
327
|
+
message: 'L4YERCAK3 is already configured. What would you like to do?',
|
|
328
|
+
choices: [
|
|
329
|
+
{ name: 'Keep existing configuration', value: 'keep' },
|
|
330
|
+
{ name: 'Reconfigure (remove and re-add)', value: 'reconfigure' },
|
|
331
|
+
{ name: 'Remove configuration', value: 'remove' },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
]);
|
|
335
|
+
|
|
336
|
+
if (action === 'keep') {
|
|
337
|
+
console.log(chalk.gray('\nKeeping existing configuration.'));
|
|
338
|
+
showUsageInstructions('Claude Code');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (action === 'remove') {
|
|
343
|
+
removeMcpConfig();
|
|
344
|
+
console.log(chalk.green('\nā L4YERCAK3 MCP server removed from Claude Code'));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
removeMcpConfig();
|
|
349
|
+
console.log(chalk.gray('Removed existing configuration...'));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Ask for scope
|
|
353
|
+
const { scope } = await inquirer.prompt([
|
|
354
|
+
{
|
|
355
|
+
type: 'list',
|
|
356
|
+
name: 'scope',
|
|
357
|
+
message: 'Where should the MCP server be configured?',
|
|
358
|
+
choices: [
|
|
359
|
+
{ name: 'User (global) - Available in all projects', value: 'user' },
|
|
360
|
+
{ name: 'Project - Only for current directory', value: 'project' },
|
|
361
|
+
],
|
|
362
|
+
default: 'user',
|
|
363
|
+
},
|
|
364
|
+
]);
|
|
365
|
+
|
|
366
|
+
console.log('');
|
|
367
|
+
console.log(chalk.gray('Configuring Claude Code...'));
|
|
368
|
+
|
|
369
|
+
const success = await configureMcp(scope);
|
|
370
|
+
|
|
371
|
+
if (success) {
|
|
372
|
+
console.log('');
|
|
373
|
+
console.log(chalk.green.bold('ā L4YERCAK3 MCP server configured for Claude Code!\n'));
|
|
374
|
+
showUsageInstructions('Claude Code');
|
|
375
|
+
} else {
|
|
376
|
+
console.log('');
|
|
377
|
+
console.log(chalk.red('Failed to configure MCP server.'));
|
|
378
|
+
console.log(chalk.gray('\nYou can try manual configuration:'));
|
|
379
|
+
console.log(chalk.cyan(' claude mcp add l4yercak3 -- npx @l4yercak3/cli mcp-server'));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Setup Claude Desktop
|
|
385
|
+
*/
|
|
386
|
+
async function setupClaudeDesktop() {
|
|
387
|
+
const alreadyConfigured = isClaudeDesktopConfigured();
|
|
388
|
+
|
|
389
|
+
if (alreadyConfigured) {
|
|
390
|
+
const { action } = await inquirer.prompt([
|
|
391
|
+
{
|
|
392
|
+
type: 'list',
|
|
393
|
+
name: 'action',
|
|
394
|
+
message: 'L4YERCAK3 is already configured. What would you like to do?',
|
|
395
|
+
choices: [
|
|
396
|
+
{ name: 'Keep existing configuration', value: 'keep' },
|
|
397
|
+
{ name: 'Reconfigure', value: 'reconfigure' },
|
|
398
|
+
{ name: 'Remove configuration', value: 'remove' },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
|
|
403
|
+
if (action === 'keep') {
|
|
404
|
+
console.log(chalk.gray('\nKeeping existing configuration.'));
|
|
405
|
+
showUsageInstructions('Claude Desktop');
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (action === 'remove') {
|
|
410
|
+
removeClaudeDesktopConfig();
|
|
411
|
+
console.log(chalk.green('\nā L4YERCAK3 MCP server removed from Claude Desktop'));
|
|
412
|
+
console.log(chalk.gray(' Restart Claude Desktop for changes to take effect.'));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
console.log(chalk.gray('Configuring Claude Desktop...'));
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
const { configPath, commandDescription } = configureClaudeDesktop();
|
|
421
|
+
console.log('');
|
|
422
|
+
console.log(chalk.green.bold('ā L4YERCAK3 MCP server configured for Claude Desktop!\n'));
|
|
423
|
+
console.log(chalk.gray(` Config: ${configPath}`));
|
|
424
|
+
console.log(chalk.gray(` Command: ${commandDescription}`));
|
|
425
|
+
console.log(chalk.yellow('\n ā ļø Restart Claude Desktop for changes to take effect.'));
|
|
426
|
+
console.log(chalk.gray(' (Using absolute path since Claude Desktop doesn\'t inherit your shell PATH)\n'));
|
|
427
|
+
showUsageInstructions('Claude Desktop');
|
|
428
|
+
} catch (error) {
|
|
429
|
+
console.log('');
|
|
430
|
+
console.log(chalk.red(`Failed to configure Claude Desktop: ${error.message}`));
|
|
431
|
+
showClaudeDesktopManualInstructions();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Show manual instructions for Claude Desktop
|
|
437
|
+
*/
|
|
438
|
+
function showClaudeDesktopManualInstructions() {
|
|
439
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
440
|
+
const cmdConfig = getClaudeDesktopCommand();
|
|
441
|
+
|
|
442
|
+
console.log(chalk.gray('\nManual configuration:'));
|
|
443
|
+
console.log(chalk.gray(` Edit: ${configPath}`));
|
|
444
|
+
console.log('');
|
|
445
|
+
console.log(chalk.white(' Add to mcpServers (use absolute paths):'));
|
|
446
|
+
console.log(chalk.cyan(' {'));
|
|
447
|
+
console.log(chalk.cyan(' "mcpServers": {'));
|
|
448
|
+
console.log(chalk.cyan(' "l4yercak3": {'));
|
|
449
|
+
console.log(chalk.cyan(` "command": "${cmdConfig.command}",`));
|
|
450
|
+
console.log(chalk.cyan(` "args": ${JSON.stringify(cmdConfig.args)}`));
|
|
451
|
+
console.log(chalk.cyan(' }'));
|
|
452
|
+
console.log(chalk.cyan(' }'));
|
|
453
|
+
console.log(chalk.cyan(' }'));
|
|
454
|
+
console.log('');
|
|
455
|
+
console.log(chalk.gray(' Note: Claude Desktop doesn\'t inherit your shell PATH.'));
|
|
456
|
+
console.log(chalk.gray(` Find paths with: ${chalk.cyan('which l4yercak3')} or ${chalk.cyan('which npx')}`));
|
|
457
|
+
console.log('');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Show instructions for other MCP clients
|
|
462
|
+
*/
|
|
463
|
+
function showOtherClientInstructions() {
|
|
464
|
+
console.log(chalk.bold('š Manual MCP Configuration\n'));
|
|
465
|
+
|
|
466
|
+
console.log(chalk.white(' Server command:'));
|
|
467
|
+
console.log(chalk.cyan(' npx @l4yercak3/cli mcp-server\n'));
|
|
468
|
+
|
|
469
|
+
console.log(chalk.white(' Transport: ') + chalk.cyan('stdio\n'));
|
|
470
|
+
|
|
471
|
+
console.log(chalk.white(' Compatible clients include:'));
|
|
472
|
+
console.log(chalk.gray(' ⢠Cursor'));
|
|
473
|
+
console.log(chalk.gray(' ⢠Cody (Sourcegraph)'));
|
|
474
|
+
console.log(chalk.gray(' ⢠Continue'));
|
|
475
|
+
console.log(chalk.gray(' ⢠Zed'));
|
|
476
|
+
console.log(chalk.gray(' ⢠Any MCP-compatible AI assistant'));
|
|
477
|
+
console.log('');
|
|
478
|
+
|
|
479
|
+
console.log(chalk.white(' JSON configuration format:'));
|
|
480
|
+
console.log(chalk.cyan(' {'));
|
|
481
|
+
console.log(chalk.cyan(' "command": "npx",'));
|
|
482
|
+
console.log(chalk.cyan(' "args": ["@l4yercak3/cli", "mcp-server"],'));
|
|
483
|
+
console.log(chalk.cyan(' "transport": "stdio"'));
|
|
484
|
+
console.log(chalk.cyan(' }'));
|
|
485
|
+
console.log('');
|
|
486
|
+
|
|
487
|
+
console.log(chalk.gray(' Refer to your client\'s documentation for where to add this config.\n'));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Show usage instructions after setup
|
|
492
|
+
*/
|
|
493
|
+
function showUsageInstructions(clientName = 'your AI assistant') {
|
|
494
|
+
console.log(chalk.bold('š How to use:\n'));
|
|
495
|
+
|
|
496
|
+
console.log(chalk.white(` In ${clientName}, you can now:`));
|
|
497
|
+
console.log(chalk.gray(' ⢠Ask Claude to list your L4YERCAK3 contacts, projects, or invoices'));
|
|
498
|
+
console.log(chalk.gray(' ⢠Create or update CRM records through natural language'));
|
|
499
|
+
console.log(chalk.gray(' ⢠Get project integration help with context from your L4YERCAK3 data'));
|
|
500
|
+
console.log('');
|
|
501
|
+
|
|
502
|
+
console.log(chalk.white(' Example prompts:'));
|
|
503
|
+
console.log(chalk.cyan(' "Show me my recent contacts in L4YERCAK3"'));
|
|
504
|
+
console.log(chalk.cyan(' "Create a new project called Website Redesign"'));
|
|
505
|
+
console.log(chalk.cyan(' "What invoices are pending?"'));
|
|
506
|
+
console.log('');
|
|
507
|
+
|
|
508
|
+
console.log(chalk.gray(' The MCP server runs automatically when needed.'));
|
|
509
|
+
console.log(chalk.gray(' Your session stays active until you run `l4yercak3 logout`.\n'));
|
|
510
|
+
}
|
package/src/commands/spread.js
CHANGED
|
@@ -490,6 +490,46 @@ async function handleSpread() {
|
|
|
490
490
|
// Check if application already exists for this project
|
|
491
491
|
const existingApp = await backendClient.checkExistingApplication(organizationId, projectPathHash);
|
|
492
492
|
|
|
493
|
+
// Helper to register a new application
|
|
494
|
+
const registerNewApplication = async () => {
|
|
495
|
+
// Build source object, only including routerType if it has a value
|
|
496
|
+
const sourceData = {
|
|
497
|
+
type: 'cli',
|
|
498
|
+
projectPathHash,
|
|
499
|
+
cliVersion: pkg.version,
|
|
500
|
+
framework: detection.framework.type || 'unknown',
|
|
501
|
+
frameworkVersion: detection.framework.metadata?.version,
|
|
502
|
+
hasTypeScript: detection.framework.metadata?.hasTypeScript || false,
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// Only add routerType if it exists
|
|
506
|
+
if (detection.framework.metadata?.routerType) {
|
|
507
|
+
sourceData.routerType = detection.framework.metadata.routerType;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const registrationData = {
|
|
511
|
+
organizationId,
|
|
512
|
+
name: projectName,
|
|
513
|
+
description: `Connected via CLI from ${detection.framework.type || 'unknown'} project`,
|
|
514
|
+
source: sourceData,
|
|
515
|
+
connection: {
|
|
516
|
+
features,
|
|
517
|
+
hasFrontendDatabase: !!detection.framework.metadata?.hasPrisma,
|
|
518
|
+
frontendDatabaseType: detection.framework.metadata?.hasPrisma ? 'prisma' : undefined,
|
|
519
|
+
},
|
|
520
|
+
deployment: {
|
|
521
|
+
productionUrl: productionDomain ? `https://${productionDomain}` : undefined,
|
|
522
|
+
githubRepo: detection.github.isGitHub ? `${detection.github.owner}/${detection.github.repo}` : undefined,
|
|
523
|
+
githubBranch: detection.github.branch || 'main',
|
|
524
|
+
},
|
|
525
|
+
// Don't generate a new API key if user already has one configured
|
|
526
|
+
skipApiKeyGeneration: apiKey === null,
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const registrationResult = await backendClient.registerApplication(registrationData);
|
|
530
|
+
return registrationResult;
|
|
531
|
+
};
|
|
532
|
+
|
|
493
533
|
if (existingApp.found && existingApp.application) {
|
|
494
534
|
// Application already registered
|
|
495
535
|
console.log(chalk.yellow(` ā ļø This project is already registered as "${existingApp.application.name}"`));
|
|
@@ -501,6 +541,7 @@ async function handleSpread() {
|
|
|
501
541
|
message: 'What would you like to do?',
|
|
502
542
|
choices: [
|
|
503
543
|
{ name: 'Update existing registration', value: 'update' },
|
|
544
|
+
{ name: 'Register as new application', value: 'new' },
|
|
504
545
|
{ name: 'Skip registration (keep existing)', value: 'skip' },
|
|
505
546
|
],
|
|
506
547
|
},
|
|
@@ -509,6 +550,7 @@ async function handleSpread() {
|
|
|
509
550
|
if (updateAction === 'update') {
|
|
510
551
|
// Update existing application
|
|
511
552
|
const updateData = {
|
|
553
|
+
name: projectName, // Update name too
|
|
512
554
|
connection: {
|
|
513
555
|
features,
|
|
514
556
|
hasFrontendDatabase: !!detection.framework.metadata?.hasPrisma,
|
|
@@ -524,51 +566,28 @@ async function handleSpread() {
|
|
|
524
566
|
applicationId = existingApp.application.id;
|
|
525
567
|
isUpdate = true;
|
|
526
568
|
console.log(chalk.green(` ā
Application registration updated\n`));
|
|
569
|
+
} else if (updateAction === 'new') {
|
|
570
|
+
// Register as new application
|
|
571
|
+
const registrationResult = await registerNewApplication();
|
|
572
|
+
applicationId = registrationResult.applicationId;
|
|
573
|
+
console.log(chalk.green(` ā
New application registered with L4YERCAK3\n`));
|
|
574
|
+
|
|
575
|
+
if (registrationResult.apiKey && registrationResult.apiKey.key) {
|
|
576
|
+
console.log(chalk.gray(` API key generated: ${registrationResult.apiKey.prefix}`));
|
|
577
|
+
}
|
|
527
578
|
} else {
|
|
528
579
|
applicationId = existingApp.application.id;
|
|
529
580
|
console.log(chalk.gray(` Skipped registration update\n`));
|
|
530
581
|
}
|
|
531
582
|
} else {
|
|
532
583
|
// Register new application
|
|
533
|
-
|
|
534
|
-
const sourceData = {
|
|
535
|
-
type: 'cli',
|
|
536
|
-
projectPathHash,
|
|
537
|
-
cliVersion: pkg.version,
|
|
538
|
-
framework: detection.framework.type || 'unknown',
|
|
539
|
-
frameworkVersion: detection.framework.metadata?.version,
|
|
540
|
-
hasTypeScript: detection.framework.metadata?.hasTypeScript || false,
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
// Only add routerType if it exists (Next.js has 'app'/'pages', Expo has 'expo-router'/'react-navigation')
|
|
544
|
-
if (detection.framework.metadata?.routerType) {
|
|
545
|
-
sourceData.routerType = detection.framework.metadata.routerType;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const registrationData = {
|
|
549
|
-
organizationId,
|
|
550
|
-
name: projectName,
|
|
551
|
-
description: `Connected via CLI from ${detection.framework.type || 'unknown'} project`,
|
|
552
|
-
source: sourceData,
|
|
553
|
-
connection: {
|
|
554
|
-
features,
|
|
555
|
-
hasFrontendDatabase: !!detection.framework.metadata?.hasPrisma,
|
|
556
|
-
frontendDatabaseType: detection.framework.metadata?.hasPrisma ? 'prisma' : undefined,
|
|
557
|
-
},
|
|
558
|
-
deployment: {
|
|
559
|
-
productionUrl: productionDomain ? `https://${productionDomain}` : undefined,
|
|
560
|
-
githubRepo: detection.github.isGitHub ? `${detection.github.owner}/${detection.github.repo}` : undefined,
|
|
561
|
-
githubBranch: detection.github.branch || 'main',
|
|
562
|
-
},
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
const registrationResult = await backendClient.registerApplication(registrationData);
|
|
584
|
+
const registrationResult = await registerNewApplication();
|
|
566
585
|
applicationId = registrationResult.applicationId;
|
|
567
586
|
console.log(chalk.green(` ā
Application registered with L4YERCAK3\n`));
|
|
568
587
|
|
|
569
|
-
//
|
|
588
|
+
// Show API key if backend generated one
|
|
570
589
|
if (registrationResult.apiKey && registrationResult.apiKey.key) {
|
|
571
|
-
console.log(chalk.gray(` API key
|
|
590
|
+
console.log(chalk.gray(` API key: ${registrationResult.apiKey.prefix}`));
|
|
572
591
|
}
|
|
573
592
|
}
|
|
574
593
|
} catch (regError) {
|
|
@@ -464,11 +464,11 @@ Looks at database schemas, types, and common patterns.`,
|
|
|
464
464
|
},
|
|
465
465
|
},
|
|
466
466
|
requiresAuth: true,
|
|
467
|
-
handler: async (params, authContext) =>
|
|
467
|
+
handler: async (params, authContext) =>
|
|
468
468
|
// This tool provides suggestions based on common patterns
|
|
469
469
|
// In a real implementation, it would analyze actual project files
|
|
470
470
|
|
|
471
|
-
|
|
471
|
+
({
|
|
472
472
|
suggestions: [
|
|
473
473
|
{
|
|
474
474
|
localModel: 'User',
|
|
@@ -509,8 +509,8 @@ Looks at database schemas, types, and common patterns.`,
|
|
|
509
509
|
'Use l4yercak3_add_model_mapping to add mappings you want',
|
|
510
510
|
'Customize field mappings as needed for your schema',
|
|
511
511
|
],
|
|
512
|
-
}
|
|
513
|
-
|
|
512
|
+
})
|
|
513
|
+
,
|
|
514
514
|
},
|
|
515
515
|
],
|
|
516
516
|
};
|
|
@@ -195,8 +195,7 @@ Use this when the user needs to login but hasn't yet.`,
|
|
|
195
195
|
properties: {},
|
|
196
196
|
},
|
|
197
197
|
requiresAuth: false,
|
|
198
|
-
handler: async () => {
|
|
199
|
-
return {
|
|
198
|
+
handler: async () => ({
|
|
200
199
|
instructions: [
|
|
201
200
|
'1. Open a terminal in this project directory',
|
|
202
201
|
'2. Run: l4yercak3 login',
|
|
@@ -209,8 +208,7 @@ Use this when the user needs to login but hasn't yet.`,
|
|
|
209
208
|
'2. Then run: l4yercak3 login',
|
|
210
209
|
],
|
|
211
210
|
documentation: 'https://docs.l4yercak3.com/cli/authentication',
|
|
212
|
-
}
|
|
213
|
-
},
|
|
211
|
+
}),
|
|
214
212
|
},
|
|
215
213
|
|
|
216
214
|
// ========================================
|