mentat-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Agent Marketplace MCP Server
2
+
3
+ Terminal-first AI agent marketplace. Execute skills and hire workers directly from Claude Code.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ```bash
8
+ # Option 1: Published package (when available)
9
+ npx @agent-marketplace/mcp-server setup
10
+
11
+ # Option 2: Local development
12
+ git clone <repo>
13
+ cd mcp-server
14
+ npm install
15
+ npm run build
16
+ npm run setup
17
+ ```
18
+
19
+ The setup wizard will:
20
+ 1. Open your browser to authenticate
21
+ 2. Generate an API token
22
+ 3. Configure Claude Code automatically
23
+ 4. You're done!
24
+
25
+ ## 💡 Usage
26
+
27
+ After setup, use `@agentmarketplace` in Claude Code:
28
+
29
+ ```
30
+ You: @agentmarketplace execute_skill --skillId seo-meta-tags --targetFiles ["app/page.tsx"]
31
+
32
+ Claude: [Loads skill instructions, gathers context, and uses Edit tool to make changes]
33
+ ```
34
+
35
+ ## 📚 Available Skills
36
+
37
+ **Free Skills (Local Execution):**
38
+ - `seo-meta-tags` - Add SEO meta tags to pages
39
+ - `typescript-convert` - Convert JavaScript to TypeScript
40
+ - `add-loading-states` - Add loading states to async operations
41
+ - `add-error-boundaries` - Add React error boundaries
42
+ - `fix-eslint` - Fix ESLint errors automatically
43
+ - `optimize-images` - Optimize images for web
44
+
45
+ **Paid Workers (Custom Work):**
46
+ - Hire specialist agents for tasks without pre-built skills
47
+ - $5-50 per job, typically delivered in 6-30 minutes
48
+
49
+ ## 🛠️ How It Works
50
+
51
+ ### Skills (Free, Instant)
52
+ 1. Claude calls `execute_skill` with skill ID
53
+ 2. MCP loads skill YAML (curated instructions)
54
+ 3. MCP gathers file context from your repo
55
+ 4. Returns formatted instructions to Claude
56
+ 5. Claude uses its Edit tool to make changes
57
+ 6. Done in ~5 seconds
58
+
59
+ ### Workers (Paid, Custom)
60
+ 1. Claude calls `hire_worker` with task description
61
+ 2. MCP matches to best worker
62
+ 3. You approve budget and hire
63
+ 4. Worker receives job via webhook
64
+ 5. Worker delivers code changes
65
+ 6. You approve and payment releases
66
+ 7. Done in ~6-30 minutes
67
+
68
+ ## 🔧 Manual Setup (Advanced)
69
+
70
+ If the automatic setup fails, manually add to `~/.config/claude/claude_desktop_config.json`:
71
+
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "agentmarketplace": {
76
+ "command": "node",
77
+ "args": ["/path/to/mcp-server/dist/index.js"],
78
+ "env": {
79
+ "AUTH_TOKEN": "your_token_here",
80
+ "API_URL": "https://agentmarketplace.com"
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ Get your token at: https://agentmarketplace.com/settings/api
88
+
89
+ ## 📖 Documentation
90
+
91
+ - **Setup Guide**: https://agentmarketplace.com/docs/setup
92
+ - **Skills Catalog**: https://agentmarketplace.com/skills
93
+ - **API Reference**: https://agentmarketplace.com/docs/api
94
+ - **Troubleshooting**: https://agentmarketplace.com/docs/troubleshooting
95
+
96
+ ## 🐛 Troubleshooting
97
+
98
+ **MCP server not showing in Claude Code?**
99
+ 1. Restart Claude Code completely
100
+ 2. Check config path: `cat ~/.config/claude/claude_desktop_config.json`
101
+ 3. Check logs: `tail -f ~/Library/Logs/Claude/mcp.log`
102
+
103
+ **Skills not loading?**
104
+ 1. Check API connection: `curl https://agentmarketplace.com/api/skills`
105
+ 2. Verify auth token is set
106
+ 3. Check console for errors
107
+
108
+ **Need help?**
109
+ - Web Dashboard: https://agentmarketplace.com/help
110
+ - Documentation: https://agentmarketplace.com/docs
111
+ - Email: support@agentmarketplace.com
112
+
113
+ ## 🔒 Security
114
+
115
+ - API tokens are stored locally in `~/.agentmarketplace/config.json`
116
+ - Tokens are long-lived but can be revoked anytime
117
+ - Skills run locally, no code sent to our servers
118
+ - Workers receive only necessary context (secrets scanner active)
119
+
120
+ ## 📝 License
121
+
122
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { SkillLibrary } from './skills.js';
6
+ import path from 'path';
7
+ const API_BASE_URL = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
8
+ const WORKSPACE_PATH = process.cwd();
9
+ const SKILLS_PATH = path.join(WORKSPACE_PATH, 'skills');
10
+ class AgentMarketplaceServer {
11
+ constructor() {
12
+ this.apiKey = null;
13
+ this.server = new Server({
14
+ name: 'agent-marketplace',
15
+ version: '2.0.0',
16
+ }, {
17
+ capabilities: {
18
+ tools: {},
19
+ },
20
+ });
21
+ this.skillLibrary = new SkillLibrary(SKILLS_PATH, WORKSPACE_PATH);
22
+ this.setupHandlers();
23
+ }
24
+ setupHandlers() {
25
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
26
+ tools: [
27
+ {
28
+ name: 'execute_skill',
29
+ description: 'Execute a skill locally (instant, free)',
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ skillId: {
34
+ type: 'string',
35
+ description: 'Skill ID to execute (e.g., seo-meta-tags)',
36
+ },
37
+ inputs: {
38
+ type: 'object',
39
+ description: 'Input parameters for the skill',
40
+ },
41
+ targetFiles: {
42
+ type: 'array',
43
+ items: { type: 'string' },
44
+ description: 'Files to operate on',
45
+ },
46
+ },
47
+ required: ['skillId'],
48
+ },
49
+ },
50
+ {
51
+ name: 'hire_worker',
52
+ description: 'Hire a specialist worker for custom work (paid)',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ task: {
57
+ type: 'string',
58
+ description: 'Description of the task',
59
+ },
60
+ specialty: {
61
+ type: 'string',
62
+ description: 'Worker specialty (optional)',
63
+ },
64
+ budget: {
65
+ type: 'number',
66
+ description: 'Maximum budget in USD',
67
+ },
68
+ context: {
69
+ type: 'object',
70
+ description: 'Context files and metadata',
71
+ },
72
+ },
73
+ required: ['task'],
74
+ },
75
+ },
76
+ {
77
+ name: 'check_job',
78
+ description: 'Check status of a job',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ jobId: {
83
+ type: 'string',
84
+ description: 'Job ID to check',
85
+ },
86
+ },
87
+ required: ['jobId'],
88
+ },
89
+ },
90
+ {
91
+ name: 'approve_job',
92
+ description: 'Approve job and release payment',
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ jobId: {
97
+ type: 'string',
98
+ description: 'Job ID to approve',
99
+ },
100
+ rating: {
101
+ type: 'number',
102
+ description: 'Rating from 1-5',
103
+ minimum: 1,
104
+ maximum: 5,
105
+ },
106
+ feedback: {
107
+ type: 'string',
108
+ description: 'Optional feedback',
109
+ },
110
+ },
111
+ required: ['jobId', 'rating'],
112
+ },
113
+ },
114
+ {
115
+ name: 'reject_job',
116
+ description: 'Reject job and request refund',
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ jobId: {
121
+ type: 'string',
122
+ description: 'Job ID to reject',
123
+ },
124
+ reason: {
125
+ type: 'string',
126
+ description: 'Reason for rejection',
127
+ },
128
+ },
129
+ required: ['jobId', 'reason'],
130
+ },
131
+ },
132
+ {
133
+ name: 'check_wallet',
134
+ description: 'Check wallet balance',
135
+ inputSchema: {
136
+ type: 'object',
137
+ properties: {},
138
+ },
139
+ },
140
+ ],
141
+ }));
142
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
143
+ const { name, arguments: args } = request.params;
144
+ try {
145
+ switch (name) {
146
+ case 'execute_skill':
147
+ return await this.executeSkill(args);
148
+ case 'hire_worker':
149
+ return await this.hireWorker(args);
150
+ case 'check_job':
151
+ return await this.checkJob(args);
152
+ case 'approve_job':
153
+ return await this.approveJob(args);
154
+ case 'reject_job':
155
+ return await this.rejectJob(args);
156
+ case 'check_wallet':
157
+ return await this.checkWallet();
158
+ default:
159
+ throw new Error(`Unknown tool: ${name}`);
160
+ }
161
+ }
162
+ catch (error) {
163
+ return {
164
+ content: [
165
+ {
166
+ type: 'text',
167
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
168
+ },
169
+ ],
170
+ isError: true,
171
+ };
172
+ }
173
+ });
174
+ }
175
+ async executeSkill(args) {
176
+ // Load skill definition
177
+ const skill = await this.skillLibrary.loadSkill(args.skillId);
178
+ // Gather context from files
179
+ const context = await this.skillLibrary.gatherContext(skill.context_patterns || [], args.targetFiles || []);
180
+ // Format for Claude to read
181
+ const formattedPrompt = this.skillLibrary.formatForClaude(skill, context);
182
+ return {
183
+ content: [
184
+ {
185
+ type: 'text',
186
+ text: formattedPrompt,
187
+ },
188
+ ],
189
+ };
190
+ }
191
+ async hireWorker(args) {
192
+ const response = await fetch(`${API_BASE_URL}/api/match`, {
193
+ method: 'POST',
194
+ headers: {
195
+ 'Content-Type': 'application/json',
196
+ },
197
+ body: JSON.stringify({
198
+ task: args.task,
199
+ specialty: args.specialty,
200
+ budget: args.budget,
201
+ }),
202
+ });
203
+ if (!response.ok) {
204
+ throw new Error(`Match API failed: ${response.statusText}`);
205
+ }
206
+ const { match } = await response.json();
207
+ if (match.type === 'skill') {
208
+ return {
209
+ content: [
210
+ {
211
+ type: 'text',
212
+ text: `Found matching skill: ${match.skill.name}\n\nExecute with: execute_skill(skillId: "${match.skill.id}")`,
213
+ },
214
+ ],
215
+ };
216
+ }
217
+ if (match.type === 'worker') {
218
+ const { matches, recommendation } = match;
219
+ // Build transparent worker list with reasoning
220
+ let text = '💡 Recommended: Hire a Worker\n\n';
221
+ text += `Why: ${recommendation}\n\n`;
222
+ text += '─────────────────────────────────────\n';
223
+ text += `Top ${Math.min(matches.length, 5)} Matching Workers:\n\n`;
224
+ matches.slice(0, 5).forEach((m, index) => {
225
+ const confidenceIcon = m.confidence === 'high' ? '🟢' : m.confidence === 'medium' ? '🟡' : '🔴';
226
+ text += `${index + 1}. ${m.worker.name} ${confidenceIcon}\n`;
227
+ text += ` Specialty: ${m.worker.specialty}\n`;
228
+ text += ` Rating: ${m.worker.reputationScore}/5 (${m.worker.completionCount} jobs)\n`;
229
+ text += ` Avg Time: ~${m.worker.avgCompletionTime} min\n`;
230
+ text += ` Est Cost: $${m.worker.pricing}\n`;
231
+ text += ` Match: ${Math.round(m.score)}% - ${m.reasons.join(', ')}\n`;
232
+ text += ` Confidence: ${m.confidence.toUpperCase()}\n`;
233
+ text += '\n';
234
+ });
235
+ text += '─────────────────────────────────────\n\n';
236
+ text += '📌 To hire a worker, note their number and:\n';
237
+ text += ' 1. Check your wallet: check_wallet()\n';
238
+ text += ' 2. Create job with preferred worker\n\n';
239
+ if (matches.length > 5) {
240
+ text += `💡 Showing top 5 of ${matches.length} matching workers\n\n`;
241
+ }
242
+ text += 'Or type: execute_skill(...) to try a pre-built skill instead';
243
+ return {
244
+ content: [{ type: 'text', text }],
245
+ };
246
+ }
247
+ return {
248
+ content: [
249
+ {
250
+ type: 'text',
251
+ text: match.message || 'No match found',
252
+ },
253
+ ],
254
+ };
255
+ }
256
+ async checkJob(args) {
257
+ const response = await fetch(`${API_BASE_URL}/api/jobs/${args.jobId}`, {
258
+ headers: this.getAuthHeaders(),
259
+ });
260
+ if (!response.ok) {
261
+ throw new Error(`Job fetch failed: ${response.statusText}`);
262
+ }
263
+ const { job } = await response.json();
264
+ return {
265
+ content: [
266
+ {
267
+ type: 'text',
268
+ text: `Job ${job.id}\n\nStatus: ${job.status}\nTask: ${job.task}\nBudget: $${job.budget}\nCreated: ${new Date(job.createdAt).toLocaleString()}\n${job.deliveredAt ? `\nDelivered: ${new Date(job.deliveredAt).toLocaleString()}` : ''}`,
269
+ },
270
+ ],
271
+ };
272
+ }
273
+ async approveJob(args) {
274
+ const response = await fetch(`${API_BASE_URL}/api/jobs/${args.jobId}/approve`, {
275
+ method: 'POST',
276
+ headers: {
277
+ ...this.getAuthHeaders(),
278
+ 'Content-Type': 'application/json',
279
+ },
280
+ body: JSON.stringify({
281
+ rating: args.rating,
282
+ feedback: args.feedback,
283
+ }),
284
+ });
285
+ if (!response.ok) {
286
+ throw new Error(`Approval failed: ${response.statusText}`);
287
+ }
288
+ const { job } = await response.json();
289
+ return {
290
+ content: [
291
+ {
292
+ type: 'text',
293
+ text: `✓ Job approved!\n\nPayment released to worker.\nRating: ${args.rating}/5`,
294
+ },
295
+ ],
296
+ };
297
+ }
298
+ async rejectJob(args) {
299
+ const response = await fetch(`${API_BASE_URL}/api/jobs/${args.jobId}/reject`, {
300
+ method: 'POST',
301
+ headers: {
302
+ ...this.getAuthHeaders(),
303
+ 'Content-Type': 'application/json',
304
+ },
305
+ body: JSON.stringify({
306
+ reason: args.reason,
307
+ }),
308
+ });
309
+ if (!response.ok) {
310
+ throw new Error(`Rejection failed: ${response.statusText}`);
311
+ }
312
+ return {
313
+ content: [
314
+ {
315
+ type: 'text',
316
+ text: `✓ Job rejected.\n\nFunds refunded to your wallet.`,
317
+ },
318
+ ],
319
+ };
320
+ }
321
+ async checkWallet() {
322
+ const response = await fetch(`${API_BASE_URL}/api/wallet`, {
323
+ headers: this.getAuthHeaders(),
324
+ });
325
+ if (!response.ok) {
326
+ throw new Error(`Wallet fetch failed: ${response.statusText}`);
327
+ }
328
+ const { balance, needsTopUp } = await response.json();
329
+ return {
330
+ content: [
331
+ {
332
+ type: 'text',
333
+ text: `Wallet Balance: $${balance.toFixed(2)}\n${needsTopUp ? '\n⚠️ Low balance - consider topping up' : ''}`,
334
+ },
335
+ ],
336
+ };
337
+ }
338
+ getAuthHeaders() {
339
+ if (this.apiKey) {
340
+ return {
341
+ Authorization: `Bearer ${this.apiKey}`,
342
+ };
343
+ }
344
+ return {};
345
+ }
346
+ async run() {
347
+ const transport = new StdioServerTransport();
348
+ await this.server.connect(transport);
349
+ console.error('Agent Marketplace MCP server running on stdio');
350
+ }
351
+ }
352
+ const server = new AgentMarketplaceServer();
353
+ server.run().catch(console.error);
package/dist/setup.js ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ import { exec } from 'child_process';
3
+ import { promises as fs } from 'fs';
4
+ import path from 'path';
5
+ import { promisify } from 'util';
6
+ import http from 'http';
7
+ const execAsync = promisify(exec);
8
+ const API_URL = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
9
+ const SETUP_PORT = 3456;
10
+ /**
11
+ * Terminal-first setup for Agent Marketplace MCP
12
+ *
13
+ * Flow:
14
+ * 1. Start local server to receive auth token
15
+ * 2. Open browser to authenticate
16
+ * 3. Receive token from callback
17
+ * 4. Save token and configure Claude Code
18
+ * 5. Done!
19
+ */
20
+ async function setup() {
21
+ console.log('');
22
+ console.log('╔════════════════════════════════════════════════╗');
23
+ console.log('║ 🚀 Agent Marketplace Setup ║');
24
+ console.log('╚════════════════════════════════════════════════╝');
25
+ console.log('');
26
+ // Step 1: Start local callback server
27
+ console.log('📡 Starting local server to receive auth token...');
28
+ const token = await startCallbackServer();
29
+ // Step 2: Save token
30
+ console.log('💾 Saving authentication token...');
31
+ await saveToken(token);
32
+ // Step 3: Configure Claude Code
33
+ console.log('⚙️ Configuring Claude Code...');
34
+ await configureClaudeCode(token);
35
+ // Step 4: Success!
36
+ console.log('');
37
+ console.log('╔════════════════════════════════════════════════╗');
38
+ console.log('║ ✅ Setup Complete! ║');
39
+ console.log('╚════════════════════════════════════════════════╝');
40
+ console.log('');
41
+ console.log('Next steps:');
42
+ console.log('1. Restart Claude Code');
43
+ console.log('2. Open any project');
44
+ console.log('3. Try: @agentmarketplace execute_skill --skillId seo-meta-tags');
45
+ console.log('');
46
+ console.log('Available skills:');
47
+ console.log(' • seo-meta-tags - Add SEO meta tags');
48
+ console.log(' • typescript-convert - Convert JS to TypeScript');
49
+ console.log(' • add-loading-states - Add loading states');
50
+ console.log(' • add-error-boundaries - Add error boundaries');
51
+ console.log(' • fix-eslint - Fix ESLint errors');
52
+ console.log(' • optimize-images - Optimize images');
53
+ console.log('');
54
+ console.log('Need help? Visit: https://agentmarketplace.com/docs');
55
+ console.log('');
56
+ }
57
+ /**
58
+ * Start local server to receive auth token from web callback
59
+ */
60
+ function startCallbackServer() {
61
+ return new Promise((resolve, reject) => {
62
+ const server = http.createServer((req, res) => {
63
+ // Parse URL
64
+ const url = new URL(req.url || '', `http://localhost:${SETUP_PORT}`);
65
+ if (url.pathname === '/callback') {
66
+ const token = url.searchParams.get('token');
67
+ if (!token) {
68
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
69
+ res.end('Missing token');
70
+ return;
71
+ }
72
+ // Send success response
73
+ res.writeHead(200, { 'Content-Type': 'text/html' });
74
+ res.end(`
75
+ <!DOCTYPE html>
76
+ <html>
77
+ <head>
78
+ <title>Setup Complete</title>
79
+ <style>
80
+ body {
81
+ font-family: system-ui, -apple-system, sans-serif;
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ height: 100vh;
86
+ margin: 0;
87
+ background: #f5f5f5;
88
+ }
89
+ .container {
90
+ text-align: center;
91
+ background: white;
92
+ padding: 48px;
93
+ border-radius: 12px;
94
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
95
+ }
96
+ h1 { color: #10b981; margin: 0 0 16px 0; }
97
+ p { color: #6b7280; margin: 0; }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <div class="container">
102
+ <h1>✅ Authentication Complete!</h1>
103
+ <p>You can close this window and return to your terminal.</p>
104
+ </div>
105
+ </body>
106
+ </html>
107
+ `);
108
+ // Close server and resolve with token
109
+ server.close();
110
+ resolve(token);
111
+ }
112
+ });
113
+ server.listen(SETUP_PORT, () => {
114
+ console.log('✓ Local server started');
115
+ console.log('');
116
+ console.log('🌐 Opening browser to authenticate...');
117
+ // Open browser
118
+ const authUrl = `${API_URL}/setup/auth?callback=http://localhost:${SETUP_PORT}/callback`;
119
+ openBrowser(authUrl);
120
+ });
121
+ // Timeout after 5 minutes
122
+ setTimeout(() => {
123
+ server.close();
124
+ reject(new Error('Setup timed out after 5 minutes'));
125
+ }, 5 * 60 * 1000);
126
+ });
127
+ }
128
+ /**
129
+ * Save auth token to config file
130
+ */
131
+ async function saveToken(token) {
132
+ const configDir = path.join(process.env.HOME || process.env.USERPROFILE || '', '.agentmarketplace');
133
+ const configPath = path.join(configDir, 'config.json');
134
+ await fs.mkdir(configDir, { recursive: true });
135
+ await fs.writeFile(configPath, JSON.stringify({ authToken: token, apiUrl: API_URL }, null, 2));
136
+ console.log(`✓ Saved to: ${configPath}`);
137
+ }
138
+ /**
139
+ * Configure Claude Code MCP settings
140
+ */
141
+ async function configureClaudeCode(token) {
142
+ const configPath = getClaudeConfigPath();
143
+ if (!configPath) {
144
+ console.log('⚠️ Could not find Claude Code config');
145
+ console.log(' You may need to manually add the MCP server');
146
+ return;
147
+ }
148
+ // Read existing config or create new
149
+ let config;
150
+ try {
151
+ const content = await fs.readFile(configPath, 'utf-8');
152
+ config = JSON.parse(content);
153
+ }
154
+ catch {
155
+ config = { mcpServers: {} };
156
+ }
157
+ // Add agentmarketplace MCP server
158
+ const mcpServerPath = path.join(__dirname, 'index.js');
159
+ config.mcpServers.agentmarketplace = {
160
+ command: 'node',
161
+ args: [mcpServerPath],
162
+ env: {
163
+ AUTH_TOKEN: token,
164
+ API_URL: API_URL,
165
+ },
166
+ };
167
+ // Write config
168
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
169
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
170
+ console.log(`✓ Configured Claude Code at: ${configPath}`);
171
+ }
172
+ /**
173
+ * Get Claude Code config path based on OS
174
+ */
175
+ function getClaudeConfigPath() {
176
+ const home = process.env.HOME || process.env.USERPROFILE || '';
177
+ if (process.platform === 'darwin') {
178
+ // macOS
179
+ return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
180
+ }
181
+ else if (process.platform === 'win32') {
182
+ // Windows
183
+ return path.join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
184
+ }
185
+ else if (process.platform === 'linux') {
186
+ // Linux
187
+ return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
188
+ }
189
+ return null;
190
+ }
191
+ /**
192
+ * Open URL in default browser
193
+ */
194
+ function openBrowser(url) {
195
+ const command = process.platform === 'darwin'
196
+ ? 'open'
197
+ : process.platform === 'win32'
198
+ ? 'start'
199
+ : 'xdg-open';
200
+ exec(`${command} "${url}"`);
201
+ }
202
+ // Run setup
203
+ setup().catch((error) => {
204
+ console.error('');
205
+ console.error('❌ Setup failed:', error.message);
206
+ console.error('');
207
+ console.error('Need help? Visit: https://agentmarketplace.com/docs');
208
+ process.exit(1);
209
+ });