adoptai-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.
Files changed (174) hide show
  1. package/README.md +70 -0
  2. package/bin/adoptai-mcp.js +2 -0
  3. package/dist/apps/canva.js +1 -0
  4. package/dist/apps/figma.js +1 -0
  5. package/dist/apps/github.js +2 -0
  6. package/dist/apps/notion.js +1 -0
  7. package/dist/apps/registry.js +20 -0
  8. package/dist/apps/salesforce.js +1 -0
  9. package/dist/cli/add.js +532 -0
  10. package/dist/cli/index.js +39 -0
  11. package/dist/cli/list.js +19 -0
  12. package/dist/cli/remove.js +37 -0
  13. package/dist/cli/serve.js +27 -0
  14. package/dist/cli/status.js +24 -0
  15. package/dist/config/clients.js +118 -0
  16. package/dist/config/credentials.js +34 -0
  17. package/dist/core/auth-manager.js +237 -0
  18. package/dist/core/config-writer.js +161 -0
  19. package/dist/core/doctor.js +199 -0
  20. package/dist/core/package.json +3 -0
  21. package/dist/core/server-base.js +81 -0
  22. package/dist/integrations/canva/.env +3 -0
  23. package/dist/integrations/canva/auth.js +287 -0
  24. package/dist/integrations/canva/env.js +9 -0
  25. package/dist/integrations/canva/index.js +12 -0
  26. package/dist/integrations/canva/package.json +31 -0
  27. package/dist/integrations/canva/publish-to-adoptai.js +365 -0
  28. package/dist/integrations/canva/setup.js +90 -0
  29. package/dist/integrations/canva/tools.js +1315 -0
  30. package/dist/integrations/canva/tools.original.js +1315 -0
  31. package/dist/integrations/figma/auth.js +48 -0
  32. package/dist/integrations/figma/index.js +11 -0
  33. package/dist/integrations/figma/package.json +27 -0
  34. package/dist/integrations/figma/publish-to-adoptai.js +384 -0
  35. package/dist/integrations/figma/setup.js +90 -0
  36. package/dist/integrations/figma/tools.js +1137 -0
  37. package/dist/integrations/github/auth.js +53 -0
  38. package/dist/integrations/github/index.js +11 -0
  39. package/dist/integrations/github/package.json +28 -0
  40. package/dist/integrations/github/publish-to-adoptai.js +240 -0
  41. package/dist/integrations/github/setup.js +103 -0
  42. package/dist/integrations/github/tools.js +78 -0
  43. package/dist/integrations/github-actions/auth.js +53 -0
  44. package/dist/integrations/github-actions/index.js +11 -0
  45. package/dist/integrations/github-actions/package.json +27 -0
  46. package/dist/integrations/github-actions/setup.js +103 -0
  47. package/dist/integrations/github-actions/tools.js +5642 -0
  48. package/dist/integrations/github-activity/auth.js +53 -0
  49. package/dist/integrations/github-activity/index.js +11 -0
  50. package/dist/integrations/github-activity/package.json +27 -0
  51. package/dist/integrations/github-activity/setup.js +103 -0
  52. package/dist/integrations/github-activity/tools.js +925 -0
  53. package/dist/integrations/github-apps/auth.js +53 -0
  54. package/dist/integrations/github-apps/index.js +11 -0
  55. package/dist/integrations/github-apps/package.json +27 -0
  56. package/dist/integrations/github-apps/setup.js +103 -0
  57. package/dist/integrations/github-apps/tools.js +791 -0
  58. package/dist/integrations/github-billing/auth.js +53 -0
  59. package/dist/integrations/github-billing/index.js +11 -0
  60. package/dist/integrations/github-billing/package.json +27 -0
  61. package/dist/integrations/github-billing/setup.js +103 -0
  62. package/dist/integrations/github-billing/tools.js +438 -0
  63. package/dist/integrations/github-checks/auth.js +53 -0
  64. package/dist/integrations/github-checks/index.js +11 -0
  65. package/dist/integrations/github-checks/package.json +27 -0
  66. package/dist/integrations/github-checks/setup.js +103 -0
  67. package/dist/integrations/github-checks/tools.js +607 -0
  68. package/dist/integrations/github-code-scanning/auth.js +53 -0
  69. package/dist/integrations/github-code-scanning/index.js +11 -0
  70. package/dist/integrations/github-code-scanning/package.json +27 -0
  71. package/dist/integrations/github-code-scanning/setup.js +103 -0
  72. package/dist/integrations/github-code-scanning/tools.js +987 -0
  73. package/dist/integrations/github-dependabot/auth.js +53 -0
  74. package/dist/integrations/github-dependabot/index.js +11 -0
  75. package/dist/integrations/github-dependabot/package.json +27 -0
  76. package/dist/integrations/github-dependabot/setup.js +103 -0
  77. package/dist/integrations/github-dependabot/tools.js +915 -0
  78. package/dist/integrations/github-gists/auth.js +53 -0
  79. package/dist/integrations/github-gists/index.js +11 -0
  80. package/dist/integrations/github-gists/package.json +27 -0
  81. package/dist/integrations/github-gists/setup.js +103 -0
  82. package/dist/integrations/github-gists/tools.js +545 -0
  83. package/dist/integrations/github-git/auth.js +53 -0
  84. package/dist/integrations/github-git/index.js +11 -0
  85. package/dist/integrations/github-git/package.json +27 -0
  86. package/dist/integrations/github-git/setup.js +103 -0
  87. package/dist/integrations/github-git/tools.js +513 -0
  88. package/dist/integrations/github-issues/auth.js +53 -0
  89. package/dist/integrations/github-issues/index.js +11 -0
  90. package/dist/integrations/github-issues/package.json +27 -0
  91. package/dist/integrations/github-issues/setup.js +103 -0
  92. package/dist/integrations/github-issues/tools.js +2232 -0
  93. package/dist/integrations/github-orgs/auth.js +53 -0
  94. package/dist/integrations/github-orgs/index.js +11 -0
  95. package/dist/integrations/github-orgs/package.json +27 -0
  96. package/dist/integrations/github-orgs/setup.js +103 -0
  97. package/dist/integrations/github-orgs/tools.js +3512 -0
  98. package/dist/integrations/github-packages/auth.js +53 -0
  99. package/dist/integrations/github-packages/index.js +11 -0
  100. package/dist/integrations/github-packages/package.json +27 -0
  101. package/dist/integrations/github-packages/setup.js +103 -0
  102. package/dist/integrations/github-packages/tools.js +1088 -0
  103. package/dist/integrations/github-pulls/auth.js +53 -0
  104. package/dist/integrations/github-pulls/index.js +11 -0
  105. package/dist/integrations/github-pulls/package.json +27 -0
  106. package/dist/integrations/github-pulls/setup.js +103 -0
  107. package/dist/integrations/github-pulls/tools.js +1252 -0
  108. package/dist/integrations/github-reactions/auth.js +53 -0
  109. package/dist/integrations/github-reactions/index.js +11 -0
  110. package/dist/integrations/github-reactions/package.json +27 -0
  111. package/dist/integrations/github-reactions/setup.js +103 -0
  112. package/dist/integrations/github-reactions/tools.js +706 -0
  113. package/dist/integrations/github-repos/auth.js +53 -0
  114. package/dist/integrations/github-repos/index.js +11 -0
  115. package/dist/integrations/github-repos/package.json +27 -0
  116. package/dist/integrations/github-repos/setup.js +103 -0
  117. package/dist/integrations/github-repos/tools.js +7286 -0
  118. package/dist/integrations/github-search/auth.js +53 -0
  119. package/dist/integrations/github-search/index.js +11 -0
  120. package/dist/integrations/github-search/package.json +27 -0
  121. package/dist/integrations/github-search/setup.js +103 -0
  122. package/dist/integrations/github-search/tools.js +370 -0
  123. package/dist/integrations/github-teams/auth.js +53 -0
  124. package/dist/integrations/github-teams/index.js +11 -0
  125. package/dist/integrations/github-teams/package.json +27 -0
  126. package/dist/integrations/github-teams/setup.js +103 -0
  127. package/dist/integrations/github-teams/tools.js +633 -0
  128. package/dist/integrations/github-users/auth.js +53 -0
  129. package/dist/integrations/github-users/index.js +11 -0
  130. package/dist/integrations/github-users/package.json +27 -0
  131. package/dist/integrations/github-users/setup.js +103 -0
  132. package/dist/integrations/github-users/tools.js +1118 -0
  133. package/dist/integrations/notion/api.js +108 -0
  134. package/dist/integrations/notion/auth.js +59 -0
  135. package/dist/integrations/notion/endpoints.json +630 -0
  136. package/dist/integrations/notion/index.js +11 -0
  137. package/dist/integrations/notion/package.json +33 -0
  138. package/dist/integrations/notion/publish-to-adoptai.js +271 -0
  139. package/dist/integrations/notion/scripts/generate-endpoints.mjs +306 -0
  140. package/dist/integrations/notion/setup.js +89 -0
  141. package/dist/integrations/notion/tools.js +586 -0
  142. package/dist/integrations/notion/tools.original.js +568 -0
  143. package/dist/integrations/salesforce/.env +8 -0
  144. package/dist/integrations/salesforce/.env.example +15 -0
  145. package/dist/integrations/salesforce/auth.js +311 -0
  146. package/dist/integrations/salesforce/endpoints.json +1359 -0
  147. package/dist/integrations/salesforce/env.js +9 -0
  148. package/dist/integrations/salesforce/index.js +12 -0
  149. package/dist/integrations/salesforce/package.json +42 -0
  150. package/dist/integrations/salesforce/publish-smart-specs.js +890 -0
  151. package/dist/integrations/salesforce/publish-to-adoptai.js +386 -0
  152. package/dist/integrations/salesforce/scripts/extract-postman.mjs +222 -0
  153. package/dist/integrations/salesforce/setup.js +112 -0
  154. package/dist/integrations/salesforce/tools.js +4544 -0
  155. package/dist/integrations/salesforce/tools.original.js +4487 -0
  156. package/dist/server/mcp-server.js +50 -0
  157. package/dist/server/tool-loader.js +47 -0
  158. package/dist/specs/figma-api.json +13621 -0
  159. package/dist/specs/split/salesforce-auth.json +3931 -0
  160. package/dist/specs/split/salesforce-bulk-v1.json +1489 -0
  161. package/dist/specs/split/salesforce-bulk-v2.json +1951 -0
  162. package/dist/specs/split/salesforce-composite.json +1246 -0
  163. package/dist/specs/split/salesforce-connect.json +11639 -0
  164. package/dist/specs/split/salesforce-einstein-prediction-service.json +576 -0
  165. package/dist/specs/split/salesforce-event-platform.json +2682 -0
  166. package/dist/specs/split/salesforce-graphql.json +1754 -0
  167. package/dist/specs/split/salesforce-industries.json +4115 -0
  168. package/dist/specs/split/salesforce-metadata.json +555 -0
  169. package/dist/specs/split/salesforce-rest.json +4798 -0
  170. package/dist/specs/split/salesforce-soap.json +210 -0
  171. package/dist/specs/split/salesforce-subscription-management.json +1299 -0
  172. package/dist/specs/split/salesforce-tooling.json +2026 -0
  173. package/dist/specs/split/salesforce-ui.json +7426 -0
  174. package/package.json +47 -0
@@ -0,0 +1,311 @@
1
+ import './env.js';
2
+ import { randomBytes, createHash } from 'node:crypto';
3
+ import { createServer } from 'node:http';
4
+ import { spawn } from 'node:child_process';
5
+ import axios from 'axios';
6
+ import { saveCredentials, getCredentials, isTokenExpired } from '../../core/auth-manager.js';
7
+
8
+ const APP_ID = 'salesforce';
9
+
10
+ function requireEnv(name) {
11
+ const v = process.env[name]?.trim();
12
+ if (!v) {
13
+ throw new Error(
14
+ `${name} is not set. Add it to your environment or integrations/salesforce/.env before running setup.`
15
+ );
16
+ }
17
+ return v;
18
+ }
19
+
20
+ function loginBaseUrl() {
21
+ return (process.env.SALESFORCE_LOGIN_URL || 'https://login.salesforce.com').replace(/\/$/, '');
22
+ }
23
+
24
+ function pkcePair() {
25
+ const codeVerifier = randomBytes(32).toString('base64url');
26
+ const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
27
+ return { codeVerifier, codeChallenge };
28
+ }
29
+
30
+ function openBrowser(url) {
31
+ if (process.platform === 'darwin') {
32
+ spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
33
+ } else if (process.platform === 'win32') {
34
+ spawn('cmd', ['/c', 'start', '""', url], { detached: true, stdio: 'ignore' }).unref();
35
+ } else {
36
+ spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();
37
+ }
38
+ }
39
+
40
+ function userIdFromIdentityUrl(idUrl) {
41
+ if (!idUrl || typeof idUrl !== 'string') return null;
42
+ const parts = idUrl.replace(/\/$/, '').split('/');
43
+ return parts[parts.length - 1] || null;
44
+ }
45
+
46
+ async function exchangeCodeForTokens({ code, codeVerifier, redirectUri, clientId, clientSecret }) {
47
+ const body = new URLSearchParams({
48
+ grant_type: 'authorization_code',
49
+ client_id: clientId,
50
+ client_secret: clientSecret,
51
+ redirect_uri: redirectUri,
52
+ code,
53
+ });
54
+ if (codeVerifier) {
55
+ body.set('code_verifier', codeVerifier);
56
+ }
57
+
58
+ const tokenUrl = `${loginBaseUrl()}/services/oauth2/token`;
59
+ const res = await axios.post(tokenUrl, body.toString(), {
60
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
61
+ timeout: 60000,
62
+ validateStatus: () => true,
63
+ });
64
+
65
+ if (res.status < 200 || res.status >= 300) {
66
+ const hint = typeof res.data === 'object' ? JSON.stringify(res.data) : String(res.data);
67
+ throw new Error(`Salesforce token exchange failed (${res.status}): ${hint}`);
68
+ }
69
+
70
+ const d = res.data;
71
+ const accessToken = d.access_token;
72
+ const refreshToken = d.refresh_token;
73
+ const instanceUrl = d.instance_url?.replace(/\/$/, '');
74
+ if (!accessToken || !instanceUrl) {
75
+ throw new Error('Token response missing access_token or instance_url');
76
+ }
77
+
78
+ const expiresIn = Number(d.expires_in);
79
+ const expiresAt =
80
+ Number.isFinite(expiresIn) && expiresIn > 0
81
+ ? new Date(Date.now() + expiresIn * 1000).toISOString()
82
+ : null;
83
+
84
+ return {
85
+ accessToken,
86
+ refreshToken: refreshToken || null,
87
+ instanceUrl,
88
+ userId: userIdFromIdentityUrl(d.id),
89
+ expiresAt,
90
+ };
91
+ }
92
+
93
+ async function refreshAccessToken(refreshToken, clientId, clientSecret) {
94
+ const body = new URLSearchParams({
95
+ grant_type: 'refresh_token',
96
+ client_id: clientId,
97
+ client_secret: clientSecret,
98
+ refresh_token: refreshToken,
99
+ });
100
+
101
+ const tokenUrl = `${loginBaseUrl()}/services/oauth2/token`;
102
+ const res = await axios.post(tokenUrl, body.toString(), {
103
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
104
+ timeout: 60000,
105
+ validateStatus: () => true,
106
+ });
107
+
108
+ if (res.status < 200 || res.status >= 300) {
109
+ const hint = typeof res.data === 'object' ? JSON.stringify(res.data) : String(res.data);
110
+ throw new Error(`Salesforce token refresh failed (${res.status}): ${hint}`);
111
+ }
112
+
113
+ const d = res.data;
114
+ const accessToken = d.access_token;
115
+ const instanceUrl = d.instance_url?.replace(/\/$/, '');
116
+ if (!accessToken) throw new Error('Refresh response missing access_token');
117
+
118
+ const expiresIn = Number(d.expires_in);
119
+ const expiresAt =
120
+ Number.isFinite(expiresIn) && expiresIn > 0
121
+ ? new Date(Date.now() + expiresIn * 1000).toISOString()
122
+ : null;
123
+
124
+ return { accessToken, instanceUrl, expiresAt };
125
+ }
126
+
127
+ export async function runAuth() {
128
+ const clientId = requireEnv('SALESFORCE_CLIENT_ID');
129
+ const clientSecret = requireEnv('SALESFORCE_CLIENT_SECRET');
130
+
131
+ const { codeVerifier, codeChallenge } = pkcePair();
132
+ const state = randomBytes(16).toString('hex');
133
+
134
+ const fixedRedirect = process.env.SALESFORCE_REDIRECT_URI?.trim();
135
+ let redirectUri;
136
+ let callbackPathname = '/callback';
137
+ let listenPort;
138
+
139
+ if (fixedRedirect) {
140
+ const ru = new URL(fixedRedirect);
141
+ if (!['127.0.0.1', 'localhost'].includes(ru.hostname)) {
142
+ throw new Error('SALESFORCE_REDIRECT_URI must use host 127.0.0.1 or localhost');
143
+ }
144
+ listenPort = Number(ru.port);
145
+ if (!Number.isFinite(listenPort) || listenPort <= 0) {
146
+ throw new Error(
147
+ 'SALESFORCE_REDIRECT_URI must include an explicit port (e.g. http://127.0.0.1:8765/callback)'
148
+ );
149
+ }
150
+ redirectUri = fixedRedirect;
151
+ callbackPathname = ru.pathname || '/callback';
152
+ }
153
+
154
+ const server = await new Promise((resolve, reject) => {
155
+ const s = createServer();
156
+ s.once('error', reject);
157
+ s.listen(fixedRedirect ? listenPort : 0, '127.0.0.1', () => resolve(s));
158
+ });
159
+
160
+ if (!fixedRedirect) {
161
+ listenPort = server.address().port;
162
+ redirectUri = `http://127.0.0.1:${listenPort}/callback`;
163
+ }
164
+
165
+ const scope =
166
+ process.env.SALESFORCE_SCOPES?.trim() || 'api refresh_token offline_access openid';
167
+
168
+ const authParams = new URLSearchParams({
169
+ response_type: 'code',
170
+ client_id: clientId,
171
+ redirect_uri: redirectUri,
172
+ scope,
173
+ state,
174
+ code_challenge: codeChallenge,
175
+ code_challenge_method: 'S256',
176
+ });
177
+
178
+ const authorizeFull = `${loginBaseUrl()}/services/oauth2/authorize?${authParams.toString()}`;
179
+
180
+ console.log(`
181
+ ┌─────────────────────────────────────────────┐
182
+ │ Salesforce Authentication (OAuth2 + PKCE) │
183
+ │ │
184
+ │ Add this callback URL to your Connected App│
185
+ │ OAuth web flow settings: │
186
+ │ ${redirectUri}
187
+ │ │
188
+ │ Opening browser to authorize… │
189
+ └─────────────────────────────────────────────┘
190
+ `);
191
+
192
+ const result = new Promise((resolve, reject) => {
193
+ server.on('request', (req, res) => {
194
+ try {
195
+ const u = new URL(req.url || '/', `http://127.0.0.1:${listenPort}`);
196
+ if (u.pathname !== callbackPathname) {
197
+ res.writeHead(404);
198
+ res.end();
199
+ return;
200
+ }
201
+ const code = u.searchParams.get('code');
202
+ const returned = u.searchParams.get('state');
203
+ const err = u.searchParams.get('error');
204
+ if (err) {
205
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
206
+ res.end(`Error: ${err}`);
207
+ server.close();
208
+ reject(new Error(u.searchParams.get('error_description') || err));
209
+ return;
210
+ }
211
+ if (!code || returned !== state) {
212
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
213
+ res.end('Invalid callback');
214
+ server.close();
215
+ reject(new Error('Invalid OAuth callback'));
216
+ return;
217
+ }
218
+ res.writeHead(200, { 'Content-Type': 'text/html' });
219
+ res.end(
220
+ '<html><body>You can close this window and return to the terminal.</body></html>'
221
+ );
222
+ server.close();
223
+ resolve(code);
224
+ } catch (e) {
225
+ reject(e);
226
+ }
227
+ });
228
+ });
229
+
230
+ openBrowser(authorizeFull);
231
+
232
+ let code;
233
+ try {
234
+ code = await result;
235
+ } catch (e) {
236
+ server.close();
237
+ throw e;
238
+ }
239
+
240
+ const tokens = await exchangeCodeForTokens({
241
+ code,
242
+ codeVerifier,
243
+ redirectUri,
244
+ clientId,
245
+ clientSecret,
246
+ });
247
+
248
+ saveCredentials(APP_ID, {
249
+ token: tokens.accessToken,
250
+ refreshToken: tokens.refreshToken,
251
+ tokenType: 'bearer',
252
+ expiresAt: tokens.expiresAt,
253
+ instanceUrl: tokens.instanceUrl,
254
+ userId: tokens.userId,
255
+ savedAt: new Date().toISOString(),
256
+ });
257
+
258
+ console.log('✅ Salesforce tokens saved (instance URL + access token).');
259
+ }
260
+
261
+ export function getToken() {
262
+ return getCredentials(APP_ID)?.token || null;
263
+ }
264
+
265
+ export function getInstanceUrl() {
266
+ const fromEnv = process.env.SALESFORCE_INSTANCE_URL?.replace(/\/$/, '');
267
+ if (fromEnv) return fromEnv;
268
+ return getCredentials(APP_ID)?.instanceUrl || null;
269
+ }
270
+
271
+ export function getDefaultUserId() {
272
+ return getCredentials(APP_ID)?.userId || null;
273
+ }
274
+
275
+ export function buildAuthHeaders() {
276
+ const token = getToken();
277
+ if (!token) {
278
+ throw new Error('Not authenticated. Run: npx adoptai-salesforce-mcp --client cursor');
279
+ }
280
+ return { Authorization: `Bearer ${token}` };
281
+ }
282
+
283
+ export async function ensureAccessToken() {
284
+ const creds = getCredentials(APP_ID);
285
+ if (!creds?.token) {
286
+ throw new Error('Not authenticated. Run: npx adoptai-salesforce-mcp --client cursor');
287
+ }
288
+ if (!isTokenExpired(APP_ID)) return;
289
+
290
+ const clientId = requireEnv('SALESFORCE_CLIENT_ID');
291
+ const clientSecret = requireEnv('SALESFORCE_CLIENT_SECRET');
292
+ if (!creds.refreshToken) {
293
+ throw new Error('Access token expired and no refresh token. Run setup again.');
294
+ }
295
+
296
+ const next = await refreshAccessToken(creds.refreshToken, clientId, clientSecret);
297
+ const instanceUrl = next.instanceUrl || creds.instanceUrl;
298
+
299
+ saveCredentials(APP_ID, {
300
+ ...creds,
301
+ token: next.accessToken,
302
+ tokenType: 'bearer',
303
+ expiresAt: next.expiresAt,
304
+ instanceUrl: instanceUrl || creds.instanceUrl,
305
+ savedAt: new Date().toISOString(),
306
+ });
307
+
308
+ if (instanceUrl && !process.env.SALESFORCE_INSTANCE_URL) {
309
+ process.env.SALESFORCE_INSTANCE_URL = instanceUrl;
310
+ }
311
+ }