delegate-sf-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.eslintrc.json +20 -0
  2. package/LICENSE +24 -0
  3. package/README.md +76 -0
  4. package/auth.js +148 -0
  5. package/bin/config-helper.js +51 -0
  6. package/bin/mcp-salesforce.js +12 -0
  7. package/bin/setup.js +266 -0
  8. package/bin/status.js +134 -0
  9. package/docs/README.md +52 -0
  10. package/docs/step1.png +0 -0
  11. package/docs/step2.png +0 -0
  12. package/docs/step3.png +0 -0
  13. package/docs/step4.png +0 -0
  14. package/examples/README.md +35 -0
  15. package/package.json +16 -0
  16. package/scripts/README.md +30 -0
  17. package/src/auth/file-storage.js +447 -0
  18. package/src/auth/oauth.js +417 -0
  19. package/src/auth/token-manager.js +207 -0
  20. package/src/backup/manager.js +949 -0
  21. package/src/index.js +168 -0
  22. package/src/salesforce/client.js +388 -0
  23. package/src/sf-client.js +79 -0
  24. package/src/tools/auth.js +190 -0
  25. package/src/tools/backup.js +486 -0
  26. package/src/tools/create.js +109 -0
  27. package/src/tools/delegate-hygiene.js +268 -0
  28. package/src/tools/delegate-validate.js +212 -0
  29. package/src/tools/delegate-verify.js +143 -0
  30. package/src/tools/delete.js +72 -0
  31. package/src/tools/describe.js +132 -0
  32. package/src/tools/installation-info.js +656 -0
  33. package/src/tools/learn-context.js +1077 -0
  34. package/src/tools/learn.js +351 -0
  35. package/src/tools/query.js +82 -0
  36. package/src/tools/repair-credentials.js +77 -0
  37. package/src/tools/setup.js +120 -0
  38. package/src/tools/time_machine.js +347 -0
  39. package/src/tools/update.js +138 -0
  40. package/src/tools.js +214 -0
  41. package/src/utils/cache.js +120 -0
  42. package/src/utils/debug.js +52 -0
  43. package/src/utils/logger.js +19 -0
  44. package/tokens.json +8 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "env": {
3
+ "es2022": true,
4
+ "node": true
5
+ },
6
+ "extends": [
7
+ "eslint:recommended"
8
+ ],
9
+ "parserOptions": {
10
+ "ecmaVersion": 2022,
11
+ "sourceType": "module"
12
+ },
13
+ "rules": {
14
+ "indent": ["error", 2],
15
+ "quotes": ["error", "single"],
16
+ "semi": ["error", "always"],
17
+ "no-console": "off",
18
+ "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
19
+ }
20
+ }
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2025, Aionda GmbH
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # Delegate SF MCP — v0.2
2
+
3
+ Nightly Salesforce hygiene sync. Human approval required before anything commits.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Install
8
+
9
+ ```bash
10
+ npm install
11
+ ```
12
+
13
+ ### 2. Salesforce Connected App
14
+
15
+ In Salesforce Setup → App Manager → New Connected App:
16
+ - Enable OAuth Settings: ✅
17
+ - Callback URL: `http://localhost:8482/callback`
18
+ - OAuth Scopes: `api`, `refresh_token`
19
+ - Save → wait 2-10 min to propagate
20
+
21
+ Copy the **Consumer Key** and **Consumer Secret**.
22
+
23
+ ### 3. Authenticate
24
+
25
+ ```bash
26
+ node auth.js
27
+ ```
28
+
29
+ Browser opens → log in → approve → tokens saved to `tokens.json`. Done.
30
+
31
+ ### 4. Configure Claude Desktop
32
+
33
+ In `~/Library/Application Support/Claude/claude_desktop_config.json`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "delegate-sf": {
39
+ "command": "node",
40
+ "args": ["/ABSOLUTE/PATH/TO/delegate-mcp/src/index.js"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Replace `/ABSOLUTE/PATH/TO/delegate-mcp` with the actual path.
47
+
48
+ ### 5. Restart Claude Desktop
49
+
50
+ Quit fully (⌘Q) and reopen. The tools should appear.
51
+
52
+ ---
53
+
54
+ ## Tools
55
+
56
+ | Tool | What it does |
57
+ |------|-------------|
58
+ | `sf_query` | Run any SOQL query |
59
+ | `sf_get_record` | Get a record by ID |
60
+ | `sf_search` | SOSL search by name/keyword |
61
+ | `sf_describe` | Get field metadata for any object |
62
+ | `sf_create` | Create a record (runs verify first) |
63
+ | `sf_update` | Update a record |
64
+ | `sf_verify_exists` | Duplicate check before create |
65
+ | `sf_hygiene_score` | Account hygiene score 0–100 |
66
+
67
+ ---
68
+
69
+ ## Re-authenticate
70
+
71
+ ```bash
72
+ node auth.js
73
+ # Answer 'y' to re-authenticate
74
+ ```
75
+
76
+ Tokens refresh automatically during normal use.
package/auth.js ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * auth.js — Delegate SF MCP Auth
3
+ *
4
+ * Run once: node auth.js
5
+ * Browser opens → approve in Salesforce → tokens saved to tokens.json
6
+ * Then start the MCP: node src/index.js
7
+ */
8
+
9
+ import jsforce from 'jsforce';
10
+ import http from 'http';
11
+ import { exec } from 'child_process';
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+ import readline from 'readline';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+ const TOKEN_FILE = path.join(__dirname, 'tokens.json');
19
+ const PORT = 8482;
20
+ const REDIRECT_URI = `http://localhost:${PORT}/callback`;
21
+
22
+ // ── Prompt helpers ────────────────────────────────────────────────────────────
23
+
24
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
25
+ const ask = (q) => new Promise(resolve => rl.question(q, resolve));
26
+
27
+ function openBrowser(url) {
28
+ const cmd =
29
+ process.platform === 'darwin' ? `open "${url}"` :
30
+ process.platform === 'win32' ? `start "" "${url}"` :
31
+ `xdg-open "${url}"`;
32
+ exec(cmd, err => { if (err) console.log(`\nCould not auto-open browser. Visit manually:\n${url}\n`); });
33
+ }
34
+
35
+ // ── Main ──────────────────────────────────────────────────────────────────────
36
+
37
+ console.log('\n━━━ Delegate SF MCP — Auth Setup ━━━\n');
38
+
39
+ // Check for existing tokens
40
+ if (fs.existsSync(TOKEN_FILE)) {
41
+ const existing = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8'));
42
+ console.log(`Found existing tokens for: ${existing.instanceUrl}`);
43
+ const reauth = await ask('Re-authenticate? (y/N): ');
44
+ if (reauth.toLowerCase() !== 'y') {
45
+ console.log('Using existing tokens. Run: node src/index.js');
46
+ process.exit(0);
47
+ }
48
+ }
49
+
50
+ // Get credentials
51
+ console.log('\nYou need a Salesforce Connected App with:');
52
+ console.log(` Callback URL: ${REDIRECT_URI}`);
53
+ console.log(' OAuth Scopes: api, refresh_token\n');
54
+
55
+ const loginUrl = await ask('Login URL (press Enter for https://login.salesforce.com): ');
56
+ const clientId = await ask('Consumer Key: ');
57
+ const clientSecret = await ask('Consumer Secret: ');
58
+ rl.close();
59
+
60
+ const oauth2 = new jsforce.OAuth2({
61
+ loginUrl: loginUrl.trim() || 'https://login.salesforce.com',
62
+ clientId: clientId.trim(),
63
+ clientSecret: clientSecret.trim(),
64
+ redirectUri: REDIRECT_URI,
65
+ });
66
+
67
+ const authUrl = oauth2.getAuthorizationUrl({ scope: 'api refresh_token sfap_api mcp_api' });
68
+
69
+ // ── Local callback server ─────────────────────────────────────────────────────
70
+
71
+ const server = http.createServer(async (req, res) => {
72
+ const url = new URL(req.url, `http://localhost:${PORT}`);
73
+
74
+ if (url.pathname !== '/callback') {
75
+ res.writeHead(404);
76
+ res.end('Not found');
77
+ return;
78
+ }
79
+
80
+ const code = url.searchParams.get('code');
81
+ const error = url.searchParams.get('error');
82
+
83
+ if (error || !code) {
84
+ const msg = error || 'No code returned';
85
+ res.writeHead(400);
86
+ res.end(`<h2 style="font-family:sans-serif;color:red">❌ Auth failed: ${msg}</h2>`);
87
+ server.close();
88
+ console.error(`\n❌ Auth failed: ${msg}`);
89
+ process.exit(1);
90
+ }
91
+
92
+ try {
93
+ const conn = new jsforce.Connection({ oauth2 });
94
+ await conn.authorize(code);
95
+
96
+ const tokens = {
97
+ instanceUrl: conn.instanceUrl,
98
+ accessToken: conn.accessToken,
99
+ refreshToken: conn.refreshToken,
100
+ clientId: clientId.trim(),
101
+ clientSecret: clientSecret.trim(),
102
+ loginUrl: loginUrl.trim() || 'https://login.salesforce.com',
103
+ };
104
+
105
+ fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2));
106
+
107
+ // Quick test — get user info
108
+ const identity = await conn.identity();
109
+
110
+ res.writeHead(200, { 'Content-Type': 'text/html' });
111
+ res.end(`
112
+ <html><body style="font-family:sans-serif;padding:40px;max-width:500px">
113
+ <h2>✅ Authorized!</h2>
114
+ <p>Logged in as <strong>${identity.display_name}</strong> (${identity.email})</p>
115
+ <p>Instance: <code>${conn.instanceUrl}</code></p>
116
+ <p>Tokens saved to <code>tokens.json</code>.</p>
117
+ <p>You can close this tab.<br>Start the MCP server: <code>node src/index.js</code></p>
118
+ </body></html>
119
+ `);
120
+
121
+ server.close();
122
+ console.log(`\n✅ Authorized as: ${identity.display_name} (${identity.email})`);
123
+ console.log(` Instance: ${conn.instanceUrl}`);
124
+ console.log(' Tokens saved to tokens.json\n');
125
+ console.log('Next step: node src/index.js\n');
126
+ process.exit(0);
127
+
128
+ } catch (err) {
129
+ res.writeHead(500, { 'Content-Type': 'text/html' });
130
+ res.end(`<html><body style="font-family:sans-serif;padding:40px"><h2 style="color:red">❌ Error</h2><pre>${err.message}</pre></body></html>`);
131
+ server.close();
132
+ console.error('\n❌ Token exchange failed:', err.message);
133
+ process.exit(1);
134
+ }
135
+ });
136
+
137
+ server.listen(PORT, () => {
138
+ console.log(`\nOpening Salesforce login in your browser...`);
139
+ console.log(`(If browser doesn't open, visit: ${authUrl})\n`);
140
+ openBrowser(authUrl);
141
+ });
142
+
143
+ // Timeout after 2 minutes
144
+ setTimeout(() => {
145
+ console.error('\n⏱ Timed out waiting for auth. Run auth.js again.');
146
+ server.close();
147
+ process.exit(1);
148
+ }, 120_000);
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { FileStorageManager } from '../src/auth/file-storage.js';
5
+
6
+ // Load environment variables
7
+ config();
8
+
9
+ async function showConfigHelper() {
10
+ console.log('🔧 MCP Salesforce Connected App Configuration Helper');
11
+ console.log('===================================================\n');
12
+
13
+ console.log('Your Salesforce Connected App needs to be configured with the following callback URLs:\n');
14
+
15
+ // Show multiple callback URLs to cover different scenarios
16
+ const ports = [8080, 8000, 8001, 8002, 8003, 8004, 8005];
17
+ console.log('Callback URLs to add to your Salesforce Connected App:');
18
+ ports.forEach(port => {
19
+ console.log(`• http://localhost:${port}/callback`);
20
+ });
21
+
22
+ console.log('\nInstructions:');
23
+ console.log('1. Go to Salesforce Setup → App Manager');
24
+ console.log('2. Find your Connected App and click "View"');
25
+ console.log('3. Click "Manage" → "Edit Policies"');
26
+ console.log('4. In "Callback URL" section, add ALL the URLs listed above');
27
+ console.log('5. Save the changes');
28
+ console.log('6. Wait 2-10 minutes for changes to propagate\n');
29
+
30
+ console.log('Current Configuration:');
31
+ try {
32
+ const fileStorage = new FileStorageManager();
33
+ const credentials = await fileStorage.getCredentials();
34
+ const apiConfig = await fileStorage.getApiConfig();
35
+
36
+ if (credentials) {
37
+ console.log(`• Instance URL: ${credentials.instanceUrl}`);
38
+ console.log(`• Client ID: ${credentials.clientId?.substring(0, 20)}...`);
39
+ console.log(`• Preferred Port: ${apiConfig.callbackPort}`);
40
+ } else {
41
+ console.log('• No configuration found in ~/.mcp-salesforce.json');
42
+ console.log('• Please run the salesforce_setup tool first');
43
+ }
44
+ } catch (error) {
45
+ console.log(`• Error reading configuration: ${error.message}`);
46
+ }
47
+
48
+ console.log('\nAfter updating your Connected App, run: npm run setup');
49
+ }
50
+
51
+ showConfigHelper().catch(console.error);
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Simple wrapper to run the main index.js file
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ // Import and run the main module
11
+ const mainModule = path.join(__dirname, '..', 'src', 'index.js');
12
+ import(mainModule);
package/bin/setup.js ADDED
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { TokenManager } from '../src/auth/token-manager.js';
5
+ import { FileStorageManager } from '../src/auth/file-storage.js';
6
+ import { readFile } from 'fs/promises';
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+
10
+ // Load environment variables
11
+ config();
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ class SetupTool {
17
+ constructor() {
18
+ this.fileStorage = new FileStorageManager();
19
+ }
20
+
21
+ /**
22
+ * Display welcome message and instructions
23
+ */
24
+ displayWelcome() {
25
+ console.log('🚀 MCP Salesforce Server Setup Tool');
26
+ console.log('=====================================\n');
27
+
28
+ console.log('This tool will help you authenticate with Salesforce using OAuth.');
29
+ console.log('You will need:\n');
30
+
31
+ console.log('1. A Salesforce Connected App with OAuth enabled');
32
+ console.log('2. The Consumer Key (Client ID) and Consumer Secret');
33
+ console.log('3. Your Salesforce instance URL (e.g., https://yourorg.salesforce.com)\n');
34
+
35
+ console.log('📖 For setup instructions, see: docs/oauth-guide.md\n');
36
+ }
37
+
38
+ /**
39
+ * Validate required configuration
40
+ */
41
+ async validateConfiguration() {
42
+ console.log('🔍 Checking configuration...');
43
+
44
+ try {
45
+ const credentials = await this.fileStorage.getCredentials();
46
+
47
+ if (!credentials) {
48
+ console.error('\n❌ No Salesforce credentials found in ~/.mcp-salesforce.json');
49
+ console.error('📝 Please run the salesforce_setup tool first to configure your credentials.');
50
+ process.exit(1);
51
+ }
52
+
53
+ const { clientId, clientSecret, instanceUrl } = credentials;
54
+
55
+ if (!clientId || !clientSecret || !instanceUrl) {
56
+ console.error('\n❌ Incomplete credentials in ~/.mcp-salesforce.json');
57
+ console.error('📝 Please run the salesforce_setup tool to reconfigure your credentials.');
58
+ process.exit(1);
59
+ }
60
+
61
+ console.log(`✅ Client ID: ${this.maskSensitive('CLIENT_ID', clientId)}`);
62
+ console.log(`✅ Client Secret: ${this.maskSensitive('CLIENT_SECRET', clientSecret)}`);
63
+ console.log(`✅ Instance URL: ${instanceUrl}`);
64
+
65
+ console.log('✅ Configuration valid\n');
66
+ return credentials;
67
+ } catch (error) {
68
+ console.error(`\n❌ Error reading configuration: ${error.message}`);
69
+ process.exit(1);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Mask sensitive values for display
75
+ */
76
+ maskSensitive(key, value) {
77
+ if (key.includes('SECRET')) {
78
+ return value.substring(0, 8) + '***';
79
+ }
80
+ if (key.includes('CLIENT_ID')) {
81
+ return value.substring(0, 12) + '***';
82
+ }
83
+ return value;
84
+ }
85
+
86
+ /**
87
+ * Test Salesforce instance connectivity
88
+ */
89
+ async testConnectivity(instanceUrl) {
90
+ console.log('🔗 Testing Salesforce instance connectivity...');
91
+
92
+ try {
93
+ const response = await fetch(`${instanceUrl}/services/data/`, {
94
+ method: 'GET',
95
+ timeout: 10000
96
+ });
97
+
98
+ if (response.ok) {
99
+ const data = await response.json();
100
+ console.log(`✅ Successfully connected to ${instanceUrl}`);
101
+ console.log(`📊 Available API versions: ${data.length}`);
102
+ return true;
103
+ } else {
104
+ console.error(`❌ HTTP ${response.status}: ${response.statusText}`);
105
+ return false;
106
+ }
107
+ } catch (error) {
108
+ console.error(`❌ Connection failed: ${error.message}`);
109
+ return false;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Perform OAuth authentication flow
115
+ */
116
+ async performOAuth(clientId, clientSecret, instanceUrl) {
117
+ console.log('🔐 Starting OAuth authentication flow...\n');
118
+
119
+ try {
120
+ const tokenManager = new TokenManager(clientId, clientSecret, instanceUrl);
121
+
122
+ console.log('📝 Instructions:');
123
+ console.log('1. Your browser will open to Salesforce login');
124
+ console.log('2. Log in with your Salesforce credentials');
125
+ console.log('3. If prompted, approve the OAuth request');
126
+ console.log('4. The browser will redirect back automatically\n');
127
+
128
+ // Wait for user to be ready
129
+ console.log('Press Enter when ready to continue...');
130
+ await this.waitForInput();
131
+
132
+ // Perform authentication
133
+ const tokens = await tokenManager.authenticateWithOAuth();
134
+
135
+ console.log('\n✅ OAuth authentication successful!');
136
+ console.log(`🏢 Instance: ${tokens.instance_url}`);
137
+ console.log(`⏰ Token expires: ${new Date(tokens.expires_at).toLocaleString()}`);
138
+
139
+ return tokens;
140
+ } catch (error) {
141
+ console.error(`\n❌ OAuth authentication failed: ${error.message}`);
142
+ throw error;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Test the stored tokens by making an API call
148
+ */
149
+ async testAuthentication(credentials) {
150
+ console.log('\n🧪 Testing authentication...');
151
+
152
+ try {
153
+ const tokenManager = new TokenManager(credentials.clientId, credentials.clientSecret, credentials.instanceUrl);
154
+ await tokenManager.initialize();
155
+
156
+ const result = await tokenManager.testTokens();
157
+
158
+ if (result.valid) {
159
+ console.log('✅ Authentication test successful!');
160
+ console.log(`📡 API versions available: ${result.apiVersions}`);
161
+ return true;
162
+ } else {
163
+ console.error(`❌ Authentication test failed: ${result.error}`);
164
+ return false;
165
+ }
166
+ } catch (error) {
167
+ console.error(`❌ Authentication test error: ${error.message}`);
168
+ return false;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Display final setup summary
174
+ */
175
+ async displaySummary() {
176
+ console.log('\n🎉 Setup Complete!');
177
+ console.log('==================\n');
178
+
179
+ console.log('Your MCP Salesforce server is now configured and ready to use.\n');
180
+
181
+ console.log('Next steps:');
182
+ console.log('1. Add the server to your Claude Desktop configuration');
183
+ console.log('2. Restart Claude Desktop');
184
+ console.log('3. Start using Salesforce tools in your conversations\n');
185
+
186
+ console.log('Configuration for Claude Desktop:');
187
+ console.log('```json');
188
+ console.log('{');
189
+ console.log(' "mcpServers": {');
190
+ console.log(' "salesforce": {');
191
+ console.log(' "command": "node",');
192
+ console.log(` "args": ["${join(process.cwd(), 'src/index.js')}"]`);
193
+ console.log(' }');
194
+ console.log(' }');
195
+ console.log('}```\n');
196
+
197
+ console.log('📚 For more information, see the documentation in the docs/ folder.');
198
+ console.log('🔧 Your credentials are stored securely in ~/.mcp-salesforce.json');
199
+ }
200
+
201
+ /**
202
+ * Wait for user input
203
+ */
204
+ async waitForInput() {
205
+ return new Promise((resolve) => {
206
+ process.stdin.once('data', () => {
207
+ resolve();
208
+ });
209
+ });
210
+ }
211
+
212
+ /**
213
+ * Main setup process
214
+ */
215
+ async run() {
216
+ try {
217
+ this.displayWelcome();
218
+
219
+ // Validate configuration
220
+ const credentials = await this.validateConfiguration();
221
+
222
+ // Test connectivity
223
+ const connected = await this.testConnectivity(credentials.instanceUrl);
224
+ if (!connected) {
225
+ console.error('\n❌ Cannot connect to Salesforce instance. Please check your instance URL.');
226
+ process.exit(1);
227
+ }
228
+
229
+ // Perform OAuth flow
230
+ await this.performOAuth(
231
+ credentials.clientId,
232
+ credentials.clientSecret,
233
+ credentials.instanceUrl
234
+ );
235
+
236
+ // Test authentication
237
+ const authValid = await this.testAuthentication(credentials);
238
+
239
+ if (!authValid) {
240
+ console.error('\n❌ Authentication test failed. Please try running setup again.');
241
+ process.exit(1);
242
+ }
243
+
244
+ // Show summary
245
+ await this.displaySummary();
246
+
247
+ console.log('✅ Setup completed successfully!');
248
+ process.exit(0);
249
+
250
+ } catch (error) {
251
+ console.error(`\n💥 Setup failed: ${error.message}`);
252
+ console.error('\n🔧 Troubleshooting:');
253
+ console.error('1. Check your ~/.mcp-salesforce.json configuration');
254
+ console.error('2. Verify your Salesforce Connected App settings');
255
+ console.error('3. Ensure you have the correct permissions');
256
+ console.error('4. Check your network connection\n');
257
+ process.exit(1);
258
+ }
259
+ }
260
+ }
261
+
262
+ // Run setup if called directly
263
+ if (import.meta.url === `file://${process.argv[1]}`) {
264
+ const setup = new SetupTool();
265
+ setup.run();
266
+ }