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.
- package/README.md +70 -0
- package/bin/adoptai-mcp.js +2 -0
- package/dist/apps/canva.js +1 -0
- package/dist/apps/figma.js +1 -0
- package/dist/apps/github.js +2 -0
- package/dist/apps/notion.js +1 -0
- package/dist/apps/registry.js +20 -0
- package/dist/apps/salesforce.js +1 -0
- package/dist/cli/add.js +532 -0
- package/dist/cli/index.js +39 -0
- package/dist/cli/list.js +19 -0
- package/dist/cli/remove.js +37 -0
- package/dist/cli/serve.js +27 -0
- package/dist/cli/status.js +24 -0
- package/dist/config/clients.js +118 -0
- package/dist/config/credentials.js +34 -0
- package/dist/core/auth-manager.js +237 -0
- package/dist/core/config-writer.js +161 -0
- package/dist/core/doctor.js +199 -0
- package/dist/core/package.json +3 -0
- package/dist/core/server-base.js +81 -0
- package/dist/integrations/canva/.env +3 -0
- package/dist/integrations/canva/auth.js +287 -0
- package/dist/integrations/canva/env.js +9 -0
- package/dist/integrations/canva/index.js +12 -0
- package/dist/integrations/canva/package.json +31 -0
- package/dist/integrations/canva/publish-to-adoptai.js +365 -0
- package/dist/integrations/canva/setup.js +90 -0
- package/dist/integrations/canva/tools.js +1315 -0
- package/dist/integrations/canva/tools.original.js +1315 -0
- package/dist/integrations/figma/auth.js +48 -0
- package/dist/integrations/figma/index.js +11 -0
- package/dist/integrations/figma/package.json +27 -0
- package/dist/integrations/figma/publish-to-adoptai.js +384 -0
- package/dist/integrations/figma/setup.js +90 -0
- package/dist/integrations/figma/tools.js +1137 -0
- package/dist/integrations/github/auth.js +53 -0
- package/dist/integrations/github/index.js +11 -0
- package/dist/integrations/github/package.json +28 -0
- package/dist/integrations/github/publish-to-adoptai.js +240 -0
- package/dist/integrations/github/setup.js +103 -0
- package/dist/integrations/github/tools.js +78 -0
- package/dist/integrations/github-actions/auth.js +53 -0
- package/dist/integrations/github-actions/index.js +11 -0
- package/dist/integrations/github-actions/package.json +27 -0
- package/dist/integrations/github-actions/setup.js +103 -0
- package/dist/integrations/github-actions/tools.js +5642 -0
- package/dist/integrations/github-activity/auth.js +53 -0
- package/dist/integrations/github-activity/index.js +11 -0
- package/dist/integrations/github-activity/package.json +27 -0
- package/dist/integrations/github-activity/setup.js +103 -0
- package/dist/integrations/github-activity/tools.js +925 -0
- package/dist/integrations/github-apps/auth.js +53 -0
- package/dist/integrations/github-apps/index.js +11 -0
- package/dist/integrations/github-apps/package.json +27 -0
- package/dist/integrations/github-apps/setup.js +103 -0
- package/dist/integrations/github-apps/tools.js +791 -0
- package/dist/integrations/github-billing/auth.js +53 -0
- package/dist/integrations/github-billing/index.js +11 -0
- package/dist/integrations/github-billing/package.json +27 -0
- package/dist/integrations/github-billing/setup.js +103 -0
- package/dist/integrations/github-billing/tools.js +438 -0
- package/dist/integrations/github-checks/auth.js +53 -0
- package/dist/integrations/github-checks/index.js +11 -0
- package/dist/integrations/github-checks/package.json +27 -0
- package/dist/integrations/github-checks/setup.js +103 -0
- package/dist/integrations/github-checks/tools.js +607 -0
- package/dist/integrations/github-code-scanning/auth.js +53 -0
- package/dist/integrations/github-code-scanning/index.js +11 -0
- package/dist/integrations/github-code-scanning/package.json +27 -0
- package/dist/integrations/github-code-scanning/setup.js +103 -0
- package/dist/integrations/github-code-scanning/tools.js +987 -0
- package/dist/integrations/github-dependabot/auth.js +53 -0
- package/dist/integrations/github-dependabot/index.js +11 -0
- package/dist/integrations/github-dependabot/package.json +27 -0
- package/dist/integrations/github-dependabot/setup.js +103 -0
- package/dist/integrations/github-dependabot/tools.js +915 -0
- package/dist/integrations/github-gists/auth.js +53 -0
- package/dist/integrations/github-gists/index.js +11 -0
- package/dist/integrations/github-gists/package.json +27 -0
- package/dist/integrations/github-gists/setup.js +103 -0
- package/dist/integrations/github-gists/tools.js +545 -0
- package/dist/integrations/github-git/auth.js +53 -0
- package/dist/integrations/github-git/index.js +11 -0
- package/dist/integrations/github-git/package.json +27 -0
- package/dist/integrations/github-git/setup.js +103 -0
- package/dist/integrations/github-git/tools.js +513 -0
- package/dist/integrations/github-issues/auth.js +53 -0
- package/dist/integrations/github-issues/index.js +11 -0
- package/dist/integrations/github-issues/package.json +27 -0
- package/dist/integrations/github-issues/setup.js +103 -0
- package/dist/integrations/github-issues/tools.js +2232 -0
- package/dist/integrations/github-orgs/auth.js +53 -0
- package/dist/integrations/github-orgs/index.js +11 -0
- package/dist/integrations/github-orgs/package.json +27 -0
- package/dist/integrations/github-orgs/setup.js +103 -0
- package/dist/integrations/github-orgs/tools.js +3512 -0
- package/dist/integrations/github-packages/auth.js +53 -0
- package/dist/integrations/github-packages/index.js +11 -0
- package/dist/integrations/github-packages/package.json +27 -0
- package/dist/integrations/github-packages/setup.js +103 -0
- package/dist/integrations/github-packages/tools.js +1088 -0
- package/dist/integrations/github-pulls/auth.js +53 -0
- package/dist/integrations/github-pulls/index.js +11 -0
- package/dist/integrations/github-pulls/package.json +27 -0
- package/dist/integrations/github-pulls/setup.js +103 -0
- package/dist/integrations/github-pulls/tools.js +1252 -0
- package/dist/integrations/github-reactions/auth.js +53 -0
- package/dist/integrations/github-reactions/index.js +11 -0
- package/dist/integrations/github-reactions/package.json +27 -0
- package/dist/integrations/github-reactions/setup.js +103 -0
- package/dist/integrations/github-reactions/tools.js +706 -0
- package/dist/integrations/github-repos/auth.js +53 -0
- package/dist/integrations/github-repos/index.js +11 -0
- package/dist/integrations/github-repos/package.json +27 -0
- package/dist/integrations/github-repos/setup.js +103 -0
- package/dist/integrations/github-repos/tools.js +7286 -0
- package/dist/integrations/github-search/auth.js +53 -0
- package/dist/integrations/github-search/index.js +11 -0
- package/dist/integrations/github-search/package.json +27 -0
- package/dist/integrations/github-search/setup.js +103 -0
- package/dist/integrations/github-search/tools.js +370 -0
- package/dist/integrations/github-teams/auth.js +53 -0
- package/dist/integrations/github-teams/index.js +11 -0
- package/dist/integrations/github-teams/package.json +27 -0
- package/dist/integrations/github-teams/setup.js +103 -0
- package/dist/integrations/github-teams/tools.js +633 -0
- package/dist/integrations/github-users/auth.js +53 -0
- package/dist/integrations/github-users/index.js +11 -0
- package/dist/integrations/github-users/package.json +27 -0
- package/dist/integrations/github-users/setup.js +103 -0
- package/dist/integrations/github-users/tools.js +1118 -0
- package/dist/integrations/notion/api.js +108 -0
- package/dist/integrations/notion/auth.js +59 -0
- package/dist/integrations/notion/endpoints.json +630 -0
- package/dist/integrations/notion/index.js +11 -0
- package/dist/integrations/notion/package.json +33 -0
- package/dist/integrations/notion/publish-to-adoptai.js +271 -0
- package/dist/integrations/notion/scripts/generate-endpoints.mjs +306 -0
- package/dist/integrations/notion/setup.js +89 -0
- package/dist/integrations/notion/tools.js +586 -0
- package/dist/integrations/notion/tools.original.js +568 -0
- package/dist/integrations/salesforce/.env +8 -0
- package/dist/integrations/salesforce/.env.example +15 -0
- package/dist/integrations/salesforce/auth.js +311 -0
- package/dist/integrations/salesforce/endpoints.json +1359 -0
- package/dist/integrations/salesforce/env.js +9 -0
- package/dist/integrations/salesforce/index.js +12 -0
- package/dist/integrations/salesforce/package.json +42 -0
- package/dist/integrations/salesforce/publish-smart-specs.js +890 -0
- package/dist/integrations/salesforce/publish-to-adoptai.js +386 -0
- package/dist/integrations/salesforce/scripts/extract-postman.mjs +222 -0
- package/dist/integrations/salesforce/setup.js +112 -0
- package/dist/integrations/salesforce/tools.js +4544 -0
- package/dist/integrations/salesforce/tools.original.js +4487 -0
- package/dist/server/mcp-server.js +50 -0
- package/dist/server/tool-loader.js +47 -0
- package/dist/specs/figma-api.json +13621 -0
- package/dist/specs/split/salesforce-auth.json +3931 -0
- package/dist/specs/split/salesforce-bulk-v1.json +1489 -0
- package/dist/specs/split/salesforce-bulk-v2.json +1951 -0
- package/dist/specs/split/salesforce-composite.json +1246 -0
- package/dist/specs/split/salesforce-connect.json +11639 -0
- package/dist/specs/split/salesforce-einstein-prediction-service.json +576 -0
- package/dist/specs/split/salesforce-event-platform.json +2682 -0
- package/dist/specs/split/salesforce-graphql.json +1754 -0
- package/dist/specs/split/salesforce-industries.json +4115 -0
- package/dist/specs/split/salesforce-metadata.json +555 -0
- package/dist/specs/split/salesforce-rest.json +4798 -0
- package/dist/specs/split/salesforce-soap.json +210 -0
- package/dist/specs/split/salesforce-subscription-management.json +1299 -0
- package/dist/specs/split/salesforce-tooling.json +2026 -0
- package/dist/specs/split/salesforce-ui.json +7426 -0
- package/package.json +47 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Publish integrations/canva/tools.js (55 tools) to adopt.ai.
|
|
4
|
+
* See docs/publisher.md — env in repo root .env:
|
|
5
|
+
* ADOPTAI_BEARER_TOKEN, ADOPTAI_INTEGRATION_ID, ADOPTAI_API_BASE_URL (optional)
|
|
6
|
+
*
|
|
7
|
+
* node publish-to-adoptai.js
|
|
8
|
+
*/
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import { existsSync, readFileSync } from 'fs';
|
|
11
|
+
import { dirname, resolve } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import { tools } from './tools.js';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
function loadEnvFile(filePath) {
|
|
18
|
+
if (!existsSync(filePath)) return;
|
|
19
|
+
const text = readFileSync(filePath, 'utf8');
|
|
20
|
+
for (const line of text.split('\n')) {
|
|
21
|
+
const t = line.trim();
|
|
22
|
+
if (!t || t.startsWith('#')) continue;
|
|
23
|
+
const eq = t.indexOf('=');
|
|
24
|
+
if (eq <= 0) continue;
|
|
25
|
+
const key = t.slice(0, eq).trim();
|
|
26
|
+
let val = t.slice(eq + 1).trim();
|
|
27
|
+
if (
|
|
28
|
+
(val.startsWith('"') && val.endsWith('"')) ||
|
|
29
|
+
(val.startsWith("'") && val.endsWith("'"))
|
|
30
|
+
) {
|
|
31
|
+
val = val.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
if (process.env[key] === undefined) process.env[key] = val;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
loadEnvFile(resolve(__dirname, '../../.env'));
|
|
38
|
+
loadEnvFile(resolve(__dirname, '.env'));
|
|
39
|
+
|
|
40
|
+
const ADOPTAI_API_BASE = (
|
|
41
|
+
process.env.ADOPTAI_API_BASE_URL || 'https://api.adopt.ai'
|
|
42
|
+
).replace(/\/$/, '');
|
|
43
|
+
const ADOPTAI_API = `${ADOPTAI_API_BASE}/v1/mcp-integrations/add-update-integration-tool`;
|
|
44
|
+
const PUBLISH_DELAY_MS = Number(process.env.ADOPTAI_PUBLISH_DELAY_MS) || 300;
|
|
45
|
+
|
|
46
|
+
const BEARER_TOKEN = process.env.ADOPTAI_BEARER_TOKEN;
|
|
47
|
+
const INTEGRATION_ID = process.env.ADOPTAI_INTEGRATION_ID;
|
|
48
|
+
|
|
49
|
+
const BASE_URL = 'https://api.canva.com/rest/v1';
|
|
50
|
+
const APP_NAME = 'CANVA';
|
|
51
|
+
|
|
52
|
+
if (!BEARER_TOKEN) throw new Error('ADOPTAI_BEARER_TOKEN not set in .env');
|
|
53
|
+
if (!INTEGRATION_ID) throw new Error('ADOPTAI_INTEGRATION_ID not set in .env');
|
|
54
|
+
|
|
55
|
+
/** Tools that do not call apiRequest(…) — explicit REST mapping */
|
|
56
|
+
const PUBLISH_OVERRIDE = {
|
|
57
|
+
create_asset_upload_job: {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
path: '/asset-uploads',
|
|
60
|
+
binary: true,
|
|
61
|
+
headerPlaceholders: {
|
|
62
|
+
'asset-upload-metadata': '{asset_upload_metadata}',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
create_design_import_job: {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
path: '/imports',
|
|
68
|
+
binary: true,
|
|
69
|
+
headerPlaceholders: {
|
|
70
|
+
'import-metadata': '{import_metadata}',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Custom body placeholder maps (schema arg name → API JSON field).
|
|
77
|
+
* Values are adopt placeholders "{schemaKey}".
|
|
78
|
+
*/
|
|
79
|
+
const SPECIAL_BODY = {
|
|
80
|
+
move_design_to_folder: {
|
|
81
|
+
to_folder_id: '{folder_id}',
|
|
82
|
+
item_id: '{item_id}',
|
|
83
|
+
},
|
|
84
|
+
duplicate_design: {
|
|
85
|
+
title: '{new_title}',
|
|
86
|
+
source_design_id: '{design_id}',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function pathParamsFromTemplate(pathTemplate) {
|
|
91
|
+
return [...String(pathTemplate).matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function camelToSnake(p) {
|
|
95
|
+
return p.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function schemaKeyForPathParam(param, propertyKeys) {
|
|
99
|
+
if (propertyKeys.includes(param)) return param;
|
|
100
|
+
const snake = camelToSnake(param);
|
|
101
|
+
if (propertyKeys.includes(snake)) return snake;
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function mapArgType(prop) {
|
|
106
|
+
if (!prop || typeof prop !== 'object') return 'string';
|
|
107
|
+
const t = prop.type;
|
|
108
|
+
if (t === 'integer') return 'number';
|
|
109
|
+
if (t === 'number' || t === 'boolean' || t === 'object' || t === 'array') return t;
|
|
110
|
+
return 'string';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function extractApiRequestFromHandler(handler) {
|
|
114
|
+
if (typeof handler !== 'function') return null;
|
|
115
|
+
const src = Function.prototype.toString.call(handler);
|
|
116
|
+
const re = /apiRequest\s*\(\s*['"]([A-Z]+)['"]\s*,\s*['"]([^'"]+)['"]/is;
|
|
117
|
+
const m = src.match(re);
|
|
118
|
+
if (!m) return null;
|
|
119
|
+
return { method: m[1].toUpperCase(), path: m[2] };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function placeholderMap(keys) {
|
|
123
|
+
if (!keys?.length) return null;
|
|
124
|
+
return Object.fromEntries(keys.map((k) => [k, `{${k}}`]));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function fetchExistingToolIdsByName() {
|
|
128
|
+
const map = new Map();
|
|
129
|
+
try {
|
|
130
|
+
const res = await axios.get(
|
|
131
|
+
`${ADOPTAI_API_BASE}/v1/mcp-integrations/get-integration-tools`,
|
|
132
|
+
{
|
|
133
|
+
params: { integration_id: INTEGRATION_ID },
|
|
134
|
+
headers: {
|
|
135
|
+
Authorization: `Bearer ${BEARER_TOKEN}`,
|
|
136
|
+
Accept: 'application/json',
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
if (Array.isArray(res.data)) {
|
|
141
|
+
for (const t of res.data) {
|
|
142
|
+
if (t?.name && t?.id) map.set(t.name, t.id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.warn(
|
|
147
|
+
'⚠️ Could not load existing tools; updates may 409:',
|
|
148
|
+
err.response?.data?.detail || err.message
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return map;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildAlignedArgsAndRequired(pathTemplate, inputSchema) {
|
|
155
|
+
const pathParams = pathParamsFromTemplate(pathTemplate);
|
|
156
|
+
const properties = inputSchema?.properties || {};
|
|
157
|
+
const requiredIn = inputSchema?.required || [];
|
|
158
|
+
const pkeys = Object.keys(properties);
|
|
159
|
+
|
|
160
|
+
const args = {};
|
|
161
|
+
const requiredOut = [];
|
|
162
|
+
|
|
163
|
+
for (const pp of pathParams) {
|
|
164
|
+
const sk = schemaKeyForPathParam(pp, pkeys);
|
|
165
|
+
if (sk) {
|
|
166
|
+
args[pp] = mapArgType(properties[sk]);
|
|
167
|
+
if (requiredIn.includes(sk)) requiredOut.push(pp);
|
|
168
|
+
} else {
|
|
169
|
+
args[pp] = 'string';
|
|
170
|
+
if (requiredIn.includes(pp)) requiredOut.push(pp);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
175
|
+
const usedAsPath = pathParams.some((pp) => schemaKeyForPathParam(pp, pkeys) === k);
|
|
176
|
+
if (usedAsPath) continue;
|
|
177
|
+
args[k] = mapArgType(v);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const r of requiredIn) {
|
|
181
|
+
const pp = pathParams.find((p) => schemaKeyForPathParam(p, pkeys) === r);
|
|
182
|
+
const outKey = pp || r;
|
|
183
|
+
if (!requiredOut.includes(outKey)) requiredOut.push(outKey);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { args, required: [...new Set(requiredOut)] };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildPayload(tool, publish) {
|
|
190
|
+
const { method, path: pathTemplate, binary, headerPlaceholders } = publish;
|
|
191
|
+
const properties = tool.inputSchema?.properties || {};
|
|
192
|
+
const pathParams = new Set(pathParamsFromTemplate(pathTemplate));
|
|
193
|
+
const pkeys = Object.keys(properties);
|
|
194
|
+
|
|
195
|
+
const { args, required } = buildAlignedArgsAndRequired(pathTemplate, tool.inputSchema);
|
|
196
|
+
|
|
197
|
+
const nonPathKeys = pkeys.filter((k) => {
|
|
198
|
+
for (const pp of pathParams) {
|
|
199
|
+
if (schemaKeyForPathParam(pp, pkeys) === k) return false;
|
|
200
|
+
}
|
|
201
|
+
return !pathParams.has(k);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const useQuery = method === 'GET' || method === 'DELETE';
|
|
205
|
+
|
|
206
|
+
let body = null;
|
|
207
|
+
let query_params = null;
|
|
208
|
+
|
|
209
|
+
if (binary) {
|
|
210
|
+
// Octet-stream body is the file bytes; metadata lives in headers (see PUBLISH_OVERRIDE).
|
|
211
|
+
body = { body_base64: '{body_base64}' };
|
|
212
|
+
} else if (SPECIAL_BODY[tool.name]) {
|
|
213
|
+
body = { ...SPECIAL_BODY[tool.name] };
|
|
214
|
+
} else if (useQuery) {
|
|
215
|
+
query_params = placeholderMap(nonPathKeys);
|
|
216
|
+
} else {
|
|
217
|
+
body = placeholderMap(nonPathKeys);
|
|
218
|
+
if (body && Object.keys(body).length === 0) body = null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const headers = {
|
|
222
|
+
authorization: '{secrets.bearer_token}',
|
|
223
|
+
'content-type': binary ? 'application/octet-stream' : 'application/json',
|
|
224
|
+
...(headerPlaceholders || {}),
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
integration_id: INTEGRATION_ID,
|
|
229
|
+
name: `${APP_NAME}_${String(tool.name).toUpperCase()}`,
|
|
230
|
+
description: (tool.description || '').slice(0, 8000),
|
|
231
|
+
tool_spec: {
|
|
232
|
+
url: BASE_URL + pathTemplate,
|
|
233
|
+
method,
|
|
234
|
+
headers,
|
|
235
|
+
body,
|
|
236
|
+
query_params,
|
|
237
|
+
},
|
|
238
|
+
input_schema: {
|
|
239
|
+
args,
|
|
240
|
+
required,
|
|
241
|
+
},
|
|
242
|
+
is_custom_integration: false,
|
|
243
|
+
is_published: false,
|
|
244
|
+
is_browser_call: false,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolvePublishSpec(tool) {
|
|
249
|
+
const over = PUBLISH_OVERRIDE[tool.name];
|
|
250
|
+
if (over) {
|
|
251
|
+
return {
|
|
252
|
+
method: over.method,
|
|
253
|
+
path: over.path,
|
|
254
|
+
binary: over.binary,
|
|
255
|
+
headerPlaceholders: over.headerPlaceholders,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const ex = extractApiRequestFromHandler(tool.handler);
|
|
259
|
+
if (!ex) {
|
|
260
|
+
throw new Error('Could not infer method/path from handler');
|
|
261
|
+
}
|
|
262
|
+
return { method: ex.method, path: ex.path, binary: false, headerPlaceholders: null };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// create_design_import_job schema missing import_metadata — add synthetic prop for publish args only
|
|
266
|
+
function toolWithPublishSchema(tool) {
|
|
267
|
+
if (tool.name !== 'create_design_import_job') return tool;
|
|
268
|
+
return {
|
|
269
|
+
...tool,
|
|
270
|
+
inputSchema: {
|
|
271
|
+
...tool.inputSchema,
|
|
272
|
+
properties: {
|
|
273
|
+
import_metadata: {
|
|
274
|
+
type: 'object',
|
|
275
|
+
description: 'Import job metadata (JSON) for Import-Metadata header.',
|
|
276
|
+
},
|
|
277
|
+
...tool.inputSchema.properties,
|
|
278
|
+
},
|
|
279
|
+
required: ['import_metadata', ...(tool.inputSchema.required || [])],
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function publishAll() {
|
|
285
|
+
console.log(
|
|
286
|
+
`Publishing ${tools.length} Canva tools (adopt API: ${ADOPTAI_API_BASE})...\n`
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const existingIds = await fetchExistingToolIdsByName();
|
|
290
|
+
if (existingIds.size) {
|
|
291
|
+
console.log(`Loaded ${existingIds.size} existing tool id(s) for upsert.\n`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let published = 0;
|
|
295
|
+
let updated = 0;
|
|
296
|
+
let skipped = 0;
|
|
297
|
+
let failed = 0;
|
|
298
|
+
|
|
299
|
+
for (const rawTool of tools) {
|
|
300
|
+
const tool = toolWithPublishSchema(rawTool);
|
|
301
|
+
let payload;
|
|
302
|
+
try {
|
|
303
|
+
const spec = resolvePublishSpec(rawTool);
|
|
304
|
+
payload = buildPayload(tool, spec);
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error(`❌ Skip: ${rawTool.name} — ${e.message}`);
|
|
307
|
+
failed++;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const existingId = existingIds.get(payload.name);
|
|
312
|
+
if (existingId) payload.id = existingId;
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
await axios.post(ADOPTAI_API, payload, {
|
|
316
|
+
headers: {
|
|
317
|
+
Authorization: `Bearer ${BEARER_TOKEN}`,
|
|
318
|
+
'Content-Type': 'application/json',
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (existingId) {
|
|
323
|
+
console.log(`✅ Updated: ${payload.name}`);
|
|
324
|
+
updated++;
|
|
325
|
+
} else {
|
|
326
|
+
console.log(`✅ Published: ${payload.name}`);
|
|
327
|
+
published++;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await new Promise((r) => setTimeout(r, PUBLISH_DELAY_MS));
|
|
331
|
+
} catch (err) {
|
|
332
|
+
const status = err.response?.status;
|
|
333
|
+
const data = err.response?.data;
|
|
334
|
+
const message =
|
|
335
|
+
(typeof data?.detail === 'string' && data.detail) ||
|
|
336
|
+
data?.message ||
|
|
337
|
+
err.message;
|
|
338
|
+
|
|
339
|
+
const isDuplicate =
|
|
340
|
+
status === 409 &&
|
|
341
|
+
typeof message === 'string' &&
|
|
342
|
+
message.toLowerCase().includes('already exists');
|
|
343
|
+
|
|
344
|
+
if (isDuplicate) {
|
|
345
|
+
console.log(`⏭️ Skipped (already exists, no id): ${payload.name}`);
|
|
346
|
+
skipped++;
|
|
347
|
+
} else {
|
|
348
|
+
console.error(`❌ Failed: ${payload.name} — ${status}: ${message}`);
|
|
349
|
+
failed++;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
await new Promise((r) => setTimeout(r, PUBLISH_DELAY_MS));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
console.log(`\n─────────────────────────────────`);
|
|
357
|
+
console.log(`✅ Published (new): ${published}`);
|
|
358
|
+
console.log(`✅ Updated: ${updated}`);
|
|
359
|
+
console.log(`⏭️ Skipped: ${skipped}`);
|
|
360
|
+
console.log(`❌ Failed: ${failed}`);
|
|
361
|
+
console.log(`─────────────────────────────────`);
|
|
362
|
+
console.log(`Total: ${tools.length} tools`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
publishAll();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import './env.js';
|
|
3
|
+
import { writeConfig, removeConfig } from '../../core/config-writer.js';
|
|
4
|
+
import { getCredentials, isTokenExpired } from '../../core/auth-manager.js';
|
|
5
|
+
import { runAuth } from './auth.js';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname, resolve } from 'path';
|
|
8
|
+
import readline from 'readline';
|
|
9
|
+
import { tools } from './tools.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const clientFlag = args.includes('--client') ? args[args.indexOf('--client') + 1] : 'cursor';
|
|
14
|
+
const isRemove = args.includes('--remove');
|
|
15
|
+
const isStatus = args.includes('--status');
|
|
16
|
+
const SUPPORTED = ['cursor', 'claude', 'windsurf', 'vscode'];
|
|
17
|
+
const toolCount = tools.length;
|
|
18
|
+
|
|
19
|
+
if (isRemove) {
|
|
20
|
+
removeConfig({ client: clientFlag, serverName: 'canva-adoptai' });
|
|
21
|
+
console.log(`✅ Canva MCP removed from ${clientFlag}`);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isStatus) {
|
|
26
|
+
const creds = getCredentials('canva');
|
|
27
|
+
if (!creds) {
|
|
28
|
+
console.log('❌ Not authenticated');
|
|
29
|
+
} else {
|
|
30
|
+
console.log(`✅ Authenticated\n Token: ${creds.token?.substring(0, 20)}...\n Saved: ${creds.savedAt}`);
|
|
31
|
+
}
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!SUPPORTED.includes(clientFlag)) {
|
|
36
|
+
console.error(`Invalid client: ${clientFlag}\nSupported: ${SUPPORTED.join(', ')}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
const existing = getCredentials('canva');
|
|
42
|
+
|
|
43
|
+
if (existing && !isTokenExpired('canva')) {
|
|
44
|
+
console.log('✅ Already authenticated');
|
|
45
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
46
|
+
const ans = await new Promise((r) =>
|
|
47
|
+
rl.question('Re-authenticate? (y/n): ', (a) => {
|
|
48
|
+
rl.close();
|
|
49
|
+
r(a.trim());
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
if (ans !== 'y') {
|
|
53
|
+
writeConfig({
|
|
54
|
+
client: clientFlag,
|
|
55
|
+
serverName: 'canva-adoptai',
|
|
56
|
+
command: 'node',
|
|
57
|
+
args: [resolve(__dirname, 'index.js')],
|
|
58
|
+
env: {},
|
|
59
|
+
});
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await runAuth();
|
|
65
|
+
|
|
66
|
+
writeConfig({
|
|
67
|
+
client: clientFlag,
|
|
68
|
+
serverName: 'canva-adoptai',
|
|
69
|
+
command: 'node',
|
|
70
|
+
args: [resolve(__dirname, 'index.js')],
|
|
71
|
+
env: {},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log(`
|
|
75
|
+
✅ Canva MCP is ready!
|
|
76
|
+
────────────────────────────────────────
|
|
77
|
+
Your AI (${clientFlag}) can now use ${toolCount} tools.
|
|
78
|
+
|
|
79
|
+
Try asking:
|
|
80
|
+
"Use get_current_user_profile to confirm I'm connected to Canva"
|
|
81
|
+
"List my designs with list_all_designs"
|
|
82
|
+
|
|
83
|
+
Run health check: node core/doctor.js --client ${clientFlag}
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
main().catch((err) => {
|
|
88
|
+
console.error('❌ Setup failed:', err.message);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
});
|