onlymcp 0.17.0 → 0.20.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 (126) hide show
  1. package/LICENSE +33 -17
  2. package/README.md +2 -1
  3. package/dist/bin/cli.js +68 -583
  4. package/dist/bin/mcp.js +45 -73
  5. package/package.json +7 -10
  6. package/dist/apiBaseUrl.d.ts +0 -12
  7. package/dist/apiBaseUrl.js +0 -28
  8. package/dist/apiBaseUrl.js.map +0 -1
  9. package/dist/apiClient.d.ts +0 -296
  10. package/dist/apiClient.js +0 -224
  11. package/dist/apiClient.js.map +0 -1
  12. package/dist/auth/deviceLogin.d.ts +0 -5
  13. package/dist/auth/deviceLogin.js +0 -40
  14. package/dist/auth/deviceLogin.js.map +0 -1
  15. package/dist/auth/keyring.d.ts +0 -16
  16. package/dist/auth/keyring.js +0 -33
  17. package/dist/auth/keyring.js.map +0 -1
  18. package/dist/bin/cli.d.ts +0 -2
  19. package/dist/bin/cli.js.map +0 -1
  20. package/dist/bin/mcp.d.ts +0 -2
  21. package/dist/bin/mcp.js.map +0 -1
  22. package/dist/cache.d.ts +0 -7
  23. package/dist/cache.js +0 -32
  24. package/dist/cache.js.map +0 -1
  25. package/dist/deviceId.d.ts +0 -2
  26. package/dist/deviceId.js +0 -97
  27. package/dist/deviceId.js.map +0 -1
  28. package/dist/export/unpack.d.ts +0 -33
  29. package/dist/export/unpack.js +0 -54
  30. package/dist/export/unpack.js.map +0 -1
  31. package/dist/identity.d.ts +0 -29
  32. package/dist/identity.js +0 -116
  33. package/dist/identity.js.map +0 -1
  34. package/dist/import/collect.d.ts +0 -25
  35. package/dist/import/collect.js +0 -101
  36. package/dist/import/collect.js.map +0 -1
  37. package/dist/import/frontmatter.d.ts +0 -44
  38. package/dist/import/frontmatter.js +0 -93
  39. package/dist/import/frontmatter.js.map +0 -1
  40. package/dist/import/plan.d.ts +0 -74
  41. package/dist/import/plan.js +0 -190
  42. package/dist/import/plan.js.map +0 -1
  43. package/dist/import/prune.d.ts +0 -46
  44. package/dist/import/prune.js +0 -61
  45. package/dist/import/prune.js.map +0 -1
  46. package/dist/import/secrets.d.ts +0 -7
  47. package/dist/import/secrets.js +0 -77
  48. package/dist/import/secrets.js.map +0 -1
  49. package/dist/import/upload.d.ts +0 -21
  50. package/dist/import/upload.js +0 -99
  51. package/dist/import/upload.js.map +0 -1
  52. package/dist/install.d.ts +0 -81
  53. package/dist/install.js +0 -168
  54. package/dist/install.js.map +0 -1
  55. package/dist/instructions.d.ts +0 -4
  56. package/dist/instructions.js +0 -67
  57. package/dist/instructions.js.map +0 -1
  58. package/dist/memoryPath.d.ts +0 -1
  59. package/dist/memoryPath.js +0 -13
  60. package/dist/memoryPath.js.map +0 -1
  61. package/dist/memorySync.d.ts +0 -28
  62. package/dist/memorySync.js +0 -56
  63. package/dist/memorySync.js.map +0 -1
  64. package/dist/paths.d.ts +0 -18
  65. package/dist/paths.js +0 -62
  66. package/dist/paths.js.map +0 -1
  67. package/dist/registry/caveats.d.ts +0 -17
  68. package/dist/registry/caveats.js +0 -74
  69. package/dist/registry/caveats.js.map +0 -1
  70. package/dist/registry/clients.d.ts +0 -17
  71. package/dist/registry/clients.js +0 -402
  72. package/dist/registry/clients.js.map +0 -1
  73. package/dist/registry/index.d.ts +0 -6
  74. package/dist/registry/index.js +0 -7
  75. package/dist/registry/index.js.map +0 -1
  76. package/dist/registry/serverInfo.d.ts +0 -25
  77. package/dist/registry/serverInfo.js +0 -33
  78. package/dist/registry/serverInfo.js.map +0 -1
  79. package/dist/registry/servers.d.ts +0 -58
  80. package/dist/registry/servers.js +0 -259
  81. package/dist/registry/servers.js.map +0 -1
  82. package/dist/registry/toolHints.d.ts +0 -29
  83. package/dist/registry/toolHints.js +0 -41
  84. package/dist/registry/toolHints.js.map +0 -1
  85. package/dist/registry/types.d.ts +0 -71
  86. package/dist/registry/types.js +0 -6
  87. package/dist/registry/types.js.map +0 -1
  88. package/dist/skillFilePath.d.ts +0 -1
  89. package/dist/skillFilePath.js +0 -29
  90. package/dist/skillFilePath.js.map +0 -1
  91. package/dist/stubIgnore.d.ts +0 -22
  92. package/dist/stubIgnore.js +0 -29
  93. package/dist/stubIgnore.js.map +0 -1
  94. package/dist/stubs.d.ts +0 -12
  95. package/dist/stubs.js +0 -188
  96. package/dist/stubs.js.map +0 -1
  97. package/dist/tools/connections.d.ts +0 -154
  98. package/dist/tools/connections.js +0 -164
  99. package/dist/tools/connections.js.map +0 -1
  100. package/dist/tools/getAgentDefinition.d.ts +0 -26
  101. package/dist/tools/getAgentDefinition.js +0 -29
  102. package/dist/tools/getAgentDefinition.js.map +0 -1
  103. package/dist/tools/getSkill.d.ts +0 -26
  104. package/dist/tools/getSkill.js +0 -26
  105. package/dist/tools/getSkill.js.map +0 -1
  106. package/dist/tools/getSkillFile.d.ts +0 -27
  107. package/dist/tools/getSkillFile.js +0 -39
  108. package/dist/tools/getSkillFile.js.map +0 -1
  109. package/dist/tools/index.d.ts +0 -263
  110. package/dist/tools/index.js +0 -69
  111. package/dist/tools/index.js.map +0 -1
  112. package/dist/tools/lists.d.ts +0 -38
  113. package/dist/tools/lists.js +0 -39
  114. package/dist/tools/lists.js.map +0 -1
  115. package/dist/tools/memoryGraph.d.ts +0 -69
  116. package/dist/tools/memoryGraph.js +0 -128
  117. package/dist/tools/memoryGraph.js.map +0 -1
  118. package/dist/tools/memoryRead.d.ts +0 -25
  119. package/dist/tools/memoryRead.js +0 -33
  120. package/dist/tools/memoryRead.js.map +0 -1
  121. package/dist/tools/memoryWrite.d.ts +0 -27
  122. package/dist/tools/memoryWrite.js +0 -34
  123. package/dist/tools/memoryWrite.js.map +0 -1
  124. package/dist/tools/write.d.ts +0 -166
  125. package/dist/tools/write.js +0 -193
  126. package/dist/tools/write.js.map +0 -1
package/dist/bin/cli.js CHANGED
@@ -1,584 +1,69 @@
1
1
  #!/usr/bin/env node
2
- import { cancel, confirm, intro, isCancel, log, multiselect, outro, spinner } from '@clack/prompts';
3
- import { Command } from 'commander';
4
- import open from 'open';
5
- import QRCode from 'qrcode';
6
- import { createRequire } from 'node:module';
7
- import { homedir } from 'node:os';
8
- import { createWriteStream, existsSync, mkdirSync } from 'node:fs';
9
- import { pipeline } from 'node:stream/promises';
10
- import { Readable } from 'node:stream';
11
- import { dirname, join } from 'node:path';
12
- import { ApiClient, ApiError } from '../apiClient.js';
13
- import { Keyring } from '../auth/keyring.js';
14
- import { pollForToken } from '../auth/deviceLogin.js';
15
- import { TtlCache } from '../cache.js';
16
- import { CLAUDE_CODE_NOTE, planInstall, writeInstallTarget } from '../install.js';
17
- import { stubDirs, stubIgnoreFile } from '../paths.js';
18
- import { writeStubs } from '../stubs.js';
19
- import { readStubIgnore } from '../stubIgnore.js';
20
- import { collect } from '../import/collect.js';
21
- import { buildImportPlan } from '../import/plan.js';
22
- import { countPlan, runUpload } from '../import/upload.js';
23
- import { countTargets, isEmpty, planPrune, runPrune } from '../import/prune.js';
24
- import { syncMemoryToDisk } from '../memorySync.js';
25
- import { unpackArchive } from '../export/unpack.js';
26
- import { deviceLabel } from '../identity.js';
27
- const baseUrl = process.env.ONLYMCP_API_URL ?? 'https://only-mcp.com';
28
- // Die Version kommt aus der package.json und steht nicht ein zweites Mal hier
29
- // als Literal: sonst meldet `onlymcp --version` nach der ersten
30
- // Veroeffentlichung einen Stand, den niemand mehr nachfuehrt. Der Pfad stimmt
31
- // aus beiden Lagen, src/bin/ wie dist/bin/.
32
- const { version } = createRequire(import.meta.url)('../../package.json');
33
- const program = new Command().name('onlymcp').version(version);
34
- // Der Server liefert verification_uri im Antwortkoerper mit. Ein Aufruf von
35
- // open() nimmt jede Zeichenkette entgegen, auch file:// oder ein voellig
36
- // anderes Ziel. Geoeffnet wird deshalb nur, wenn Schema und Ursprung exakt zu
37
- // der konfigurierten API passen, die der Nutzer selbst als ONLYMCP_API_URL
38
- // gesetzt hat. Alles andere wird stillschweigend uebersprungen, Code und QR
39
- // stehen bereits vorher im Terminal und bleiben der zuverlaessige Weg.
40
- function safeOpenUrl(url, expectedOrigin) {
41
- try {
42
- const u = new URL(url);
43
- return (u.protocol === 'https:' || u.protocol === 'http:') && u.origin === expectedOrigin;
44
- }
45
- catch {
46
- return false;
47
- }
48
- }
49
- program
50
- .command('login')
51
- .description('Connect this device to OnlyMCP')
52
- .action(async () => {
53
- intro('OnlyMCP');
54
- // Einmal ehrlich sagen, was mitgeht, BEVOR es mitgeht. Die beiden Werte
55
- // stehen nachher in der Geraeteliste dieses Kontos, sonst nirgends - aber
56
- // wer sie nicht schicken will, soll das erfahren, solange er noch abbrechen
57
- // kann.
58
- log.info(`This device will register itself as: ${await deviceLabel()}`);
59
- const res = await fetch(`${baseUrl}/api/auth/device/code`, {
60
- method: 'POST',
61
- headers: { 'content-type': 'application/json' },
62
- body: JSON.stringify({ client_id: 'onlymcp-cli' }),
63
- });
64
- const d = await res.json();
65
- log.info(`Code: ${d.user_code}`);
66
- log.info(d.verification_uri);
67
- console.log(await QRCode.toString(d.verification_uri_complete, { type: 'terminal', small: true }));
68
- // Geoeffnet wird die vollstaendige Adresse, nicht die kurze: sie traegt den
69
- // Code mit, die Seite fuellt ihn dann selbst ein. Abgetippt und im QR-Code
70
- // steht weiterhin beides, fuer den Fall, dass kein Browser aufgeht.
71
- if (safeOpenUrl(d.verification_uri_complete, new URL(baseUrl).origin)) {
72
- await open(d.verification_uri_complete).catch(() => { });
73
- }
74
- const s = spinner();
75
- s.start('Waiting for confirmation');
76
- const token = await pollForToken(baseUrl, d.device_code, d.interval ?? 5);
77
- Keyring.save(token);
78
- s.stop('Connected');
79
- const api = new ApiClient(baseUrl, () => Keyring.read());
80
- const counts = await writeStubsEverywhere(api);
81
- const note = ignoredNote(counts);
82
- if (note)
83
- log.info(note);
84
- outro(`${counts.skills} skills, ${counts.agents} agents, ${counts.files} files ready`);
85
- });
86
- /**
87
- * Stubs in jeden Ordner, den ein Client auf diesem Geraet liest.
88
- *
89
- * Gezaehlt wird ueber alle Ordner zusammen, nicht der letzte: bis Aufgabe 12
90
- * ueberschrieb die Schleife ihr Ergebnis bei jedem Durchlauf und meldete
91
- * deshalb die Zahlen des zuletzt beschriebenen Ordners als Gesamtzahl.
92
- */
93
- async function writeStubsEverywhere(api) {
94
- const ignore = await readStubIgnore();
95
- // A name left out counts once, not once for each folder.
96
- const total = { skills: 0, agents: 0, files: 0, kept: [], ignored: new Set(), removed: 0 };
97
- for (const dir of stubDirs()) {
98
- const c = await writeStubs(api, dir, { ignore });
99
- total.skills += c.skills;
100
- total.agents += c.agents;
101
- total.files += c.files;
102
- for (const k of c.kept)
103
- total.kept.push(`${dir}/${k}`);
104
- for (const n of c.ignored)
105
- total.ignored.add(n);
106
- total.removed += c.removed;
107
- }
108
- return total;
109
- }
110
- /** What stub-ignore left out, said once so nobody looks for a stub the file kept away. */
111
- function ignoredNote(counts) {
112
- if (counts.ignored.size === 0)
113
- return null;
114
- const removed = counts.removed > 0 ? `, ${counts.removed} earlier stubs removed` : '';
115
- return `${counts.ignored.size} left out by ${stubIgnoreFile()}${removed}`;
116
- }
117
- /**
118
- * Reports a finished import or sync, for the "last import / last sync" line on the devices page.
119
- * A report that fails (an older server answering 404, no network, a timeout) prints nothing and
120
- * changes nothing: the command already succeeded.
121
- */
122
- async function reportRun(api, kind, files) {
123
- try {
124
- await api.reportRun(kind, files);
125
- }
126
- catch {
127
- /* deliberately silent, see above */
128
- }
129
- }
130
- /**
131
- * Ends this device's session on the server (next round A6). Clearing only the keyring left the
132
- * session alive there, where it kept counting as a device until it expired. Best effort: null when
133
- * the session ended, else what went wrong, and the logout goes on on this machine either way.
134
- */
135
- async function endServerSession(token) {
136
- try {
137
- await new ApiClient(baseUrl, () => token).signOut();
138
- return null;
139
- }
140
- catch (e) {
141
- if (e instanceof ApiError && e.status > 0)
142
- return `HTTP ${e.status}`;
143
- return e instanceof Error ? e.message : String(e);
144
- }
145
- }
146
- program
147
- .command('logout')
148
- .description("End this device's session, then remove its login and cache")
149
- .action(async () => {
150
- // The server first, while the token is still here to name the session by.
151
- const token = Keyring.read();
152
- const failure = token ? await endServerSession(token) : null;
153
- Keyring.clear();
154
- await new TtlCache().clear();
155
- if (failure) {
156
- log.warn(`The session on the server could not be ended (${failure}). It counts as a device until it ` +
157
- 'expires, or until you remove it in the dashboard under Sessions and devices.');
158
- log.success('Logged out on this device');
159
- return;
160
- }
161
- log.success('Logged out');
162
- });
163
- program
164
- .command('status')
165
- .description('Show whether this device is logged in')
166
- .action(() => {
167
- log.info(Keyring.read() ? 'logged in' : 'not logged in');
168
- });
169
- program
170
- .command('sync')
171
- .description('Rewrite the stub files')
172
- .option('--memory <dir>', 'Also write memory as files into this folder')
173
- .action(async (opts) => {
174
- const api = new ApiClient(baseUrl, () => Keyring.read());
175
- const counts = await writeStubsEverywhere(api);
176
- log.success(`${counts.skills} skills, ${counts.agents} agents, ${counts.files} files`);
177
- const note = ignoredNote(counts);
178
- if (note)
179
- log.info(note);
180
- if (counts.kept.length > 0) {
181
- // Stubs liegen in denselben Ordnern, aus denen importiert wird. Was
182
- // dort schon stand und kein Stub war, bleibt stehen - aber nicht
183
- // stillschweigend, sonst sucht jemand einen Stub, der nie geschrieben
184
- // wurde. Gekuerzt, weil ein Geraet mit 270 eigenen Agent-Dateien sonst
185
- // 270 Zeilen bekaeme und die Erfolgsmeldung aus dem Bild schoebe.
186
- log.warn([
187
- `Left untouched, because something of your own is already there (${counts.kept.length})`,
188
- ...counts.kept.slice(0, 10).map((k) => ` ${k}`),
189
- ...(counts.kept.length > 10 ? [` ... and ${counts.kept.length - 10} more`] : []),
190
- ].join('\n'));
191
- }
192
- // Every file this run wrote: the stubs, the skills' companion files, and (below) memory.
193
- const stubFiles = counts.skills + counts.agents + counts.files;
194
- if (!opts.memory) {
195
- await reportRun(api, 'sync', stubFiles);
196
- return;
197
- }
198
- const m = await syncMemoryToDisk(api, opts.memory);
199
- log.success(`${m.written} memory files to ${opts.memory}`);
200
- if (m.collisions.length > 0) {
201
- log.warn([
202
- 'Team files with a path an existing file of yours already has',
203
- ...m.collisions.map((p) => ` ${p}`),
204
- ].join('\n'));
205
- }
206
- if (m.refused.length > 0) {
207
- log.warn(['Paths refused', ...m.refused.map((p) => ` ${p}`)].join('\n'));
208
- }
209
- if (m.failures.length > 0) {
210
- log.error([`${m.failures.length} failed`, ...m.failures.map((f) => ` ${f.what} ${f.code}`)].join('\n'));
211
- process.exitCode = 1;
212
- }
213
- // A sync with failures is not a successful one: nothing is reported for it.
214
- if (m.failures.length === 0)
215
- await reportRun(api, 'sync', stubFiles + m.written);
216
- });
217
- program
218
- .command('install')
219
- .description('Register OnlyMCP in an MCP client')
220
- .option('--client <id>', 'claude-code, codex or antigravity (aliases: claude, gemini, gemini-cli)')
221
- .option('--all', 'Write every config file that already exists on this machine')
222
- .option('--print', 'Only print, write nothing')
223
- .action((opts) => {
224
- // ONLYMCP_HOME ist dieselbe Testschleuse wie in paths.ts: damit trifft
225
- // kein Testlauf jemals den echten ~/.codex- oder ~/.claude-Ordner.
226
- const home = process.env.ONLYMCP_HOME ?? homedir();
227
- const outcome = planInstall(home, opts);
228
- /** Writes one target, or only prints it with --print. Shared by --client and --all. */
229
- function apply(t) {
230
- if (opts.print) {
231
- log.info(`${t.name} -> ${t.configPath}`);
232
- console.log(t.snippet);
233
- // Bewusst keine zweite Zeile mit ONLYMCP_STUB_DIRS: die Variable steht
234
- // seit install.ts im Schnipsel selbst. Zwei Wege, dasselbe zu setzen,
235
- // laden dazu ein, beide zu gehen.
236
- return;
237
- }
238
- try {
239
- const result = writeInstallTarget(t);
240
- if (result === 'already-present') {
241
- log.info(`${t.name} is already set up: ${t.configPath}`);
242
- }
243
- else {
244
- log.success(`${t.name} set up: ${t.configPath}`);
245
- }
246
- if (t.id === 'claude-code')
247
- log.info(CLAUDE_CODE_NOTE);
248
- }
249
- catch (e) {
250
- log.error(e instanceof Error ? e.message : String(e));
251
- process.exitCode = 1;
252
- }
253
- }
254
- switch (outcome.kind) {
255
- case 'conflicting-options':
256
- log.error('Pass either --client <id> or --all, not both.');
257
- process.exitCode = 2;
258
- return;
259
- case 'missing-client':
260
- log.info(`Known clients: ${outcome.targets.map((t) => t.id).join(', ')}`);
261
- log.info('Pass --client <id>, or --all.');
262
- process.exitCode = 2;
263
- return;
264
- case 'unknown-client':
265
- // Kein Raten. Ein falsch geratener Pfad ueberschreibt sonst eine
266
- // fremde Konfigurationsdatei.
267
- log.error(`Unknown client. Known: ${outcome.targets.map((t) => t.id).join(', ')}`);
268
- process.exitCode = 1;
269
- return;
270
- case 'none-present-for-all':
271
- log.error('No known client is set up on this machine yet.');
272
- log.info(`Pass --client <id> to write its config anyway. Known: ${outcome.targets.map((t) => t.id).join(', ')}.`);
273
- process.exitCode = 2;
274
- return;
275
- case 'single':
276
- apply(outcome.target);
277
- return;
278
- case 'all':
279
- for (const t of outcome.present)
280
- apply(t);
281
- if (outcome.skipped.length > 0) {
282
- log.info(`Skipped, not set up on this machine: ${outcome.skipped.map((t) => t.id).join(', ')}`);
283
- }
284
- return;
285
- }
286
- });
287
- function formatBytes(bytes) {
288
- if (bytes < 1024)
289
- return `${bytes} B`;
290
- if (bytes < 1024 * 1024)
291
- return `${Math.round(bytes / 1024)} KB`;
292
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
293
- }
294
- function sumBytes(items) {
295
- return items.reduce((total, x) => total + x.bytes, 0);
296
- }
297
- /** Der Plan, ausgeschrieben. Steht immer, auch bei --yes: wer zuschaut, soll sehen. */
298
- function printPlan(plan, roots) {
299
- const files = plan.skills.reduce((n, s) => n + s.files.length, 0);
300
- const withFiles = plan.skills.filter((s) => s.files.length > 0).length;
301
- const fileBytes = plan.skills.reduce((n, s) => n + sumBytes(s.files), 0);
302
- if (roots.skills) {
303
- log.message(`Skills ${String(plan.skills.length).padStart(4)} from ${roots.skills}\n` +
304
- ` ${String(files).padStart(4)} more files across ${withFiles} skills ` +
305
- `${formatBytes(sumBytes(plan.skills) + fileBytes)}`);
306
- }
307
- if (roots.agents) {
308
- log.message(`Agents ${String(plan.agents.length).padStart(4)} from ${roots.agents} ${formatBytes(sumBytes(plan.agents))}`);
309
- }
310
- if (roots.memory) {
311
- log.message(`Memory ${String(plan.memory.length).padStart(4)} from ${roots.memory} ${formatBytes(sumBytes(plan.memory))}`);
312
- }
313
- if (plan.skipped.length > 0) {
314
- const byReason = new Map();
315
- for (const s of plan.skipped)
316
- byReason.set(s.reason, (byReason.get(s.reason) ?? 0) + 1);
317
- log.message(['Skipped', ...[...byReason].map(([reason, n]) => ` ${String(n).padStart(4)} ${reason}`)].join('\n'));
318
- }
319
- if (plan.noDescription.length > 0) {
320
- // Geht trotzdem hoch. Gemeldet, weil die Liste im Dashboard und die
321
- // Stubs auf jedem Geraet von der Beschreibung leben und ein Skill ohne
322
- // sie dort als blosser Name steht.
323
- log.warn(['Missing a description in the SKILL.md header', ...plan.noDescription.map((n) => ` ${n}`)].join('\n'));
324
- }
325
- if (plan.flagged.length > 0) {
326
- log.warn([
327
- 'Held back over possible credentials (none of it left this machine)',
328
- ...plan.flagged.flatMap((f) => [
329
- ` ${f.relative}`,
330
- ...f.hits.map((h) => ` line ${h.line} rule ${h.rule} ${h.excerpt}`),
331
- ]),
332
- ].join('\n'));
333
- }
334
- }
335
- program
336
- .command('import')
337
- .description('Bring existing local skills, agents and memory into OnlyMCP')
338
- .option('--skills <dir>', 'Folder with skill subfolders, or a plugin cache')
339
- .option('--agents <dir>', 'Folder with agent files')
340
- // --memory hat mit Absicht keine Vorgabe. Skills und Agents liegen an einem
341
- // bekannten Pfad, an dem nur Skills und Agents liegen. Ein Memory-Ordner
342
- // nicht: eine Vorgabe auf irgendjemandes Notizen zu richten und die
343
- // hochzuladen waere das Schlimmste, was dieser Befehl tun koennte.
344
- .option('--memory <dir>', 'Folder with memory files (no default)')
345
- .option('--dry-run', 'Only show what would be uploaded, then stop')
346
- .option('--yes', 'Upload without asking first')
347
- // --prune loescht im Konto. Es hat deshalb bewusst KEINE Abkuerzung ueber
348
- // --yes: die Liste wird immer gezeigt und immer einzeln bestaetigt, auch
349
- // wenn der Upload selbst unbeaufsichtigt laeuft.
350
- .option('--prune', 'Delete, after confirmation, what the account holds and the source no longer has')
351
- .action(async (opts) => {
352
- // Dieselbe Testschleuse wie in install.ts und paths.ts.
353
- const home = process.env.ONLYMCP_HOME ?? homedir();
354
- const roots = {
355
- skills: opts.skills ?? join(home, '.claude', 'skills'),
356
- agents: opts.agents ?? join(home, '.claude', 'agents'),
357
- memory: opts.memory,
358
- };
359
- intro('OnlyMCP Import');
360
- const s = spinner();
361
- s.start('Reading the folders');
362
- let plan;
363
- try {
364
- const [skills, agents, memory] = await Promise.all([
365
- existsSync(roots.skills) ? collect(roots.skills) : Promise.resolve([]),
366
- existsSync(roots.agents) ? collect(roots.agents) : Promise.resolve([]),
367
- roots.memory && existsSync(roots.memory) ? collect(roots.memory) : Promise.resolve([]),
368
- ]);
369
- plan = buildImportPlan({ skills, agents, memory }, { skillsRoot: roots.skills });
370
- }
371
- catch (e) {
372
- s.stop('Aborted');
373
- log.error(e instanceof Error ? e.message : String(e));
374
- process.exitCode = 1;
375
- return;
376
- }
377
- s.stop('Read');
378
- printPlan(plan, {
379
- skills: existsSync(roots.skills) ? roots.skills : undefined,
380
- agents: existsSync(roots.agents) ? roots.agents : undefined,
381
- memory: roots.memory && existsSync(roots.memory) ? roots.memory : undefined,
382
- });
383
- const total = countPlan(plan);
384
- // Mit --prune gibt es auch dann etwas zu tun, wenn nichts hochgeht: genau
385
- // ein leerer Quellordner ist der Fall, in dem am meisten geloescht wuerde.
386
- if (total === 0 && !opts.prune) {
387
- outro('Nothing to do.');
388
- return;
389
- }
390
- if (opts.dryRun && !opts.prune) {
391
- outro('Nothing uploaded (--dry-run).');
392
- return;
393
- }
394
- const api = new ApiClient(baseUrl, () => Keyring.read());
395
- // Ins falsche Konto zu laden ist ein leicht gemachter und unangenehm zu
396
- // behebender Fehler. Die Gegenmassnahme ist eine Zeile Ausgabe.
397
- try {
398
- const me = await api.getMe();
399
- log.info(`Target ${baseUrl} as ${me.email} plan ${me.plan.name}`);
400
- }
401
- catch (e) {
402
- log.error(`Not logged in, or the server is unreachable (${e instanceof Error ? e.message : String(e)}).`);
403
- log.info('First: npx onlymcp login');
404
- process.exitCode = 1;
405
- return;
406
- }
407
- if (total > 0 && !opts.yes && !opts.dryRun) {
408
- if (!process.stdin.isTTY) {
409
- // Ohne Terminal gibt es keine Rueckfrage, also auch keinen Upload.
410
- // Wer das automatisieren will, sagt --yes und hat den Plan vorher mit
411
- // --dry-run gesehen.
412
- log.error('No terminal for the prompt. Run --dry-run first, then --yes.');
413
- process.exitCode = 1;
414
- return;
415
- }
416
- const go = await confirm({ message: `Upload ${total} entries?`, initialValue: false });
417
- if (isCancel(go) || !go) {
418
- cancel('Cancelled. Nothing uploaded.');
419
- return;
420
- }
421
- if (plan.flagged.length > 0) {
422
- const release = await multiselect({
423
- message: 'Send the held-back files anyway?',
424
- options: plan.flagged.map((f) => ({ value: f.relative, label: f.relative })),
425
- required: false,
426
- initialValues: [],
427
- });
428
- if (isCancel(release)) {
429
- cancel('Cancelled. Nothing uploaded.');
430
- return;
431
- }
432
- if (release.length > 0) {
433
- // Freigeben heisst: die Datei noch einmal einsammeln, diesmal ohne
434
- // ihre Funde, und den Plan daraus neu bauen. Kein zweiter Weg in den
435
- // Upload, der die Pruefung umgehen koennte.
436
- const freed = new Set(release);
437
- const clear = (items) => items.map((c) => (freed.has(c.relative) ? { ...c, hits: [] } : c));
438
- const [skills, agents, memory] = await Promise.all([
439
- existsSync(roots.skills) ? collect(roots.skills) : Promise.resolve([]),
440
- existsSync(roots.agents) ? collect(roots.agents) : Promise.resolve([]),
441
- roots.memory && existsSync(roots.memory) ? collect(roots.memory) : Promise.resolve([]),
442
- ]);
443
- plan = buildImportPlan({
444
- skills: clear(skills),
445
- agents: clear(agents),
446
- memory: clear(memory),
447
- }, { skillsRoot: roots.skills });
448
- }
449
- }
450
- }
451
- if (total > 0 && !opts.dryRun) {
452
- const up = spinner();
453
- up.start('Uploading');
454
- const result = await runUpload(api, plan, (done, all, label) => {
455
- up.message(`${done}/${all} ${label}`);
456
- });
457
- up.stop('Done');
458
- log.success(`${result.skills} skills, ${result.skillFiles} skill files, ` +
459
- `${result.agents} agents, ${result.memory} memory files`);
460
- if (result.failures.length > 0) {
461
- log.error([`${result.failures.length} failed`, ...result.failures.map((f) => ` ${f.what} ${f.code}`)].join('\n'));
462
- process.exitCode = 1;
463
- }
464
- else {
465
- await reportRun(api, 'import', result.skills + result.skillFiles + result.agents + result.memory);
466
- }
467
- }
468
- if (opts.prune) {
469
- // Verglichen wird nur, was auch gelesen wurde. Ohne --memory sagt ein
470
- // Import gar nichts ueber Memory aus, und "steht im Konto, fehlt in der
471
- // Quelle" waere dort keine Aussage, sondern ein Unfall.
472
- const covered = {
473
- skills: existsSync(roots.skills),
474
- agents: existsSync(roots.agents),
475
- memory: Boolean(roots.memory && existsSync(roots.memory)),
476
- };
477
- const targets = await planPrune(api, plan, covered);
478
- if (isEmpty(targets)) {
479
- log.info('Nothing to delete: the account holds nothing the source does not know.');
480
- }
481
- else {
482
- // Vollstaendig aufgezaehlt, nicht gezaehlt. Eine Zahl zu bestaetigen
483
- // heisst, etwas zu bestaetigen, das man nicht gesehen hat.
484
- log.warn([
485
- 'Would delete (only your own, nothing from a team, nothing archived)',
486
- ...targets.skills.map((n) => ` Skill ${n}`),
487
- ...targets.agents.map((n) => ` Agent ${n}`),
488
- ...targets.memory.map((p) => ` Memory ${p}`),
489
- ].join('\n'));
490
- if (opts.dryRun) {
491
- log.info('Nothing deleted (--dry-run).');
492
- }
493
- else if (!process.stdin.isTTY) {
494
- // Kein --yes-Durchgriff: geloescht wird nur von Hand.
495
- log.error('No terminal for the prompt. Nothing deleted.');
496
- process.exitCode = 1;
497
- }
498
- else {
499
- const go = await confirm({
500
- message: `Delete ${countTargets(targets)} entries in the account?`,
501
- initialValue: false,
502
- });
503
- if (isCancel(go) || !go) {
504
- log.info('Nothing deleted.');
505
- }
506
- else {
507
- const pruned = await runPrune(api, targets);
508
- log.success(`${pruned.deleted} deleted`);
509
- if (pruned.failures.length > 0) {
510
- log.error([
511
- `${pruned.failures.length} failed`,
512
- ...pruned.failures.map((f) => ` ${f.what} ${f.code}`),
513
- ].join('\n'));
514
- process.exitCode = 1;
515
- }
516
- }
517
- }
518
- }
519
- }
520
- outro(opts.dryRun ? 'Nothing changed (--dry-run).' : 'Then: npx onlymcp sync on every device.');
521
- });
522
- program
523
- .command('export')
524
- .description('Get everything out of the account: memory, skills, agents, connections')
525
- .option('--out <dir>', 'Unpack the archive into this folder')
526
- .option('--zip <file>', 'Keep the archive as a file')
527
- .option('--scope <scope>', 'personal, all or team:<id>', 'all')
528
- .option('--types <list>', 'memory,skills,agents,connections')
529
- .action(async (opts) => {
530
- // Genau eines von beiden. Beides hiesse zweimal herunterladen, keines
531
- // hiesse, die teuerste Leseoperation des Produkts ins Nichts zu schicken.
532
- if ((opts.out ? 1 : 0) + (opts.zip ? 1 : 0) !== 1) {
533
- log.error('Either --out <folder> or --zip <file>, not both and not neither.');
534
- process.exitCode = 1;
535
- return;
536
- }
537
- intro('OnlyMCP Export');
538
- const api = new ApiClient(baseUrl, () => Keyring.read());
539
- const s = spinner();
540
- s.start('Fetching the archive');
541
- let res;
542
- try {
543
- res = await api.exportArchive({
544
- scope: opts.scope,
545
- types: opts.types?.split(',').map((t) => t.trim()).filter((t) => t !== ''),
546
- });
547
- }
548
- catch (e) {
549
- s.stop('Aborted');
550
- log.error(e instanceof Error ? e.message : String(e));
551
- log.info('Not logged in? First: npx onlymcp login');
552
- process.exitCode = 1;
553
- return;
554
- }
555
- if (opts.zip) {
556
- // In die Datei geschrieben, waehrend es ankommt. Ein Konto mit zwei
557
- // Gigabyte soll nicht erst zwei Gigabyte Arbeitsspeicher kosten.
558
- mkdirSync(dirname(opts.zip), { recursive: true });
559
- await pipeline(Readable.fromWeb(res.body), createWriteStream(opts.zip));
560
- s.stop('Done');
561
- outro(`Archive: ${opts.zip}`);
562
- return;
563
- }
564
- const bytes = new Uint8Array(await res.arrayBuffer());
565
- const result = await unpackArchive(bytes, opts.out);
566
- s.stop('Done');
567
- log.success(`${result.written} files in ${opts.out}`);
568
- if (result.refused.length > 0) {
569
- log.warn(['Not written, path in the archive is not allowed', ...result.refused.map((p) => ` ${p}`)].join('\n'));
570
- }
571
- if (result.failed.length > 0) {
572
- // Auf Windows der Normalfall fuer einen Skill mit Namensraum: der
573
- // Doppelpunkt ist dort kein zulaessiges Zeichen in einem Ordnernamen.
574
- log.warn([
575
- "Not written, the filename doesn't work on this system",
576
- ...result.failed.map((f) => ` ${f.path} ${f.reason}`),
577
- 'With --zip <file> the archive stays unchanged.',
578
- ].join('\n'));
579
- process.exitCode = 1;
580
- }
581
- outro(`Restore: npx onlymcp import --skills ${opts.out}/skills --agents ${opts.out}/agents --memory ${opts.out}/memory`);
582
- });
583
- await program.parseAsync();
584
- //# sourceMappingURL=cli.js.map
2
+ import{cancel as un,confirm as fn,intro as Ue,isCancel as Me,log as d,multiselect as Rr,outro as G,spinner as me}from"@clack/prompts";import{Command as Ur}from"commander";import Dr from"open";import jr from"qrcode";import{homedir as hn}from"node:os";import{createWriteStream as zr,existsSync as $,mkdirSync as Fr}from"node:fs";import{pipeline as Gr}from"node:stream/promises";import{Readable as Hr}from"node:stream";import{dirname as Wr,join as gn}from"node:path";import{hostname as Dn}from"node:os";import{execFile as En}from"node:child_process";import{hostname as On}from"node:os";import{readFile as Nn}from"node:fs/promises";import{promisify as Mn}from"node:util";import{randomUUID as Ge}from"node:crypto";import{link as kn,mkdir as wn,readFile as Sn,rename as Cn,rm as xn,writeFile as He}from"node:fs/promises";import{basename as $n,dirname as Be,join as An}from"node:path";import{existsSync as vn}from"node:fs";import{homedir as je}from"node:os";import{join as N}from"node:path";function J(){return process.env.ONLYMCP_HOME??N(je(),".claude","onlymcp")}function De(){return process.env.ONLYMCP_HOME??je()}function ze(){return N(J(),"device-id")}function Fe(){return N(J(),"cache")}function Y(){return N(J(),"stub-ignore")}function q(){let e=[];process.env.CLAUDE_PLUGIN_ROOT&&e.push(process.env.CLAUDE_PLUGIN_ROOT);for(let t of(process.env.ONLYMCP_STUB_DIRS??"").split(":"))t&&e.push(t);for(let t of[N(De(),".claude"),N(De(),".codex")])vn(t)&&!e.includes(t)&&e.push(t);return e.length>0?e:[N(J(),"plugin")]}var Tn=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,We;function Ke(){return We??=Pn(),We}async function ge(e){try{let t=(await Sn(e,"utf8")).trim().toLowerCase();return Tn.test(t)?t:null}catch{return null}}async function Pn(){let e=ze(),t=await ge(e);if(t)return t;try{return await wn(Be(e),{recursive:!0}),await _n(e)}catch{return null}}var In=new Set(["EPERM","ENOTSUP","EOPNOTSUPP","ENOSYS","EXDEV"]);async function _n(e){let t=Ge(),n=An(Be(e),`.${$n(e)}.${process.pid}.${Ge()}.tmp`);try{await He(n,`${t}
3
+ `,{flag:"wx",mode:384});try{return await kn(n,e),t}catch(r){let i=r.code;if(i!=="EEXIST"&&!In.has(i??""))throw r;if(i!=="EEXIST")try{return await He(e,`${t}
4
+ `,{flag:"wx",mode:384}),t}catch(s){if(s.code!=="EEXIST")throw s}}let o=await ge(e);return o||(await Cn(n,e),await ge(e)??t)}finally{await xn(n,{force:!0}).catch(()=>{})}}var D="0.20.0";var Ln=Mn(En),Rn=`onlymcp-cli/${D} (${process.platform} ${process.arch}; node ${process.versions.node})`,Ve=200;async function Je(e,t){let n;try{return await Promise.race([e,new Promise(o=>{n=setTimeout(()=>o(null),t),n.unref?.()})])}catch{return null}finally{n&&clearTimeout(n)}}async function Un(){try{if(process.platform==="linux"){let e=Nn("/sys/class/dmi/id/product_version","utf8"),n=(await Je(e,Ve))?.trim();return!n||/^(to be filled|default string|system version|none|n\/a)/i.test(n)?null:n}if(process.platform==="darwin"){let e=Ln("sysctl",["-n","hw.model"]);return(await Je(e,Ve))?.stdout.trim()||null}}catch{}return null}async function he(){let e="unknown";try{e=On()||"unknown"}catch{}let t=await Un();return(t&&t!==e?`${e} (${t})`:e).slice(0,60)}var Ye=null;async function H(){Ye??=await he();let e=await Ke();return{"user-agent":Rn,"x-onlymcp-device":Ye,...e?{"x-onlymcp-device-id":e}:{}}}var u=class extends Error{constructor(n,o={},r=0){super(n);this.code=n;this.params=o;this.status=r;this.name="ApiError"}code;params;status},T=class{constructor(t,n){this.baseUrl=t;this.token=n}baseUrl;token;async call(t,n={}){let o=this.token();if(!o)throw new u("errors.NOT_LOGGED_IN");let r=await fetch(`${this.baseUrl}${t}`,{...n,headers:{...await H(),...n.headers??{},authorization:`Bearer ${o}`,"content-type":"application/json"}});if(r.status===204)return;let i=await r.json().catch(()=>({}));if(!r.ok)throw new u(i.code??"errors.UNKNOWN",i.params??{},r.status);return i}getMe(){return this.call("/api/me")}reportRun(t,n){return this.call("/api/devices/current/run",{method:"POST",body:JSON.stringify({kind:t,files:n}),signal:AbortSignal.timeout(5e3)})}signOut(){return this.call("/api/auth/sign-out",{method:"POST",body:"{}",signal:AbortSignal.timeout(5e3)})}listSkills(){return this.call("/api/skills")}getSkill(t){return this.call(`/api/skills/${encodeURIComponent(t)}`)}putSkill(t,n,o={}){return this.call(`/api/skills/${encodeURIComponent(t)}`,{method:"PUT",body:JSON.stringify({body:n,...o})})}skillFileUrl(t,n){let o=n.split("/").map(encodeURIComponent).join("/");return`/api/skills/${encodeURIComponent(t)}/files/${o}`}getSkillFile(t,n){return this.call(this.skillFileUrl(t,n))}putSkillFile(t,n,o,r="utf8"){return this.call(this.skillFileUrl(t,n),{method:"PUT",body:JSON.stringify({content:o,encoding:r})})}listAgents(){return this.call("/api/agents")}putAgent(t,n){return this.call(`/api/agents/${encodeURIComponent(t)}`,{method:"PUT",body:JSON.stringify(n)})}getAgent(t,{use:n=!1}={}){return this.call(`/api/agents/${encodeURIComponent(t)}${n?"?use=1":""}`)}readMemory(t){return this.call(this.memoryUrl(t))}writeMemory(t,n,o){return this.call(this.memoryUrl(t),{method:"PUT",body:JSON.stringify({content:n,deviceLabel:o})})}memoryUrl(t){return`/api/memory/${t.split("/").map(encodeURIComponent).join("/")}`}listMemory(){return this.call("/api/memory")}searchContent(t,n={}){let o=new URLSearchParams({q:t});return n.types?.length&&o.set("types",n.types.join(",")),n.limit&&o.set("limit",String(n.limit)),this.call(`/api/search?${o.toString()}`)}memoryTree(t="all"){return this.call(`/api/memory/tree?scope=${encodeURIComponent(t)}`)}moveMemory(t,n,o=!0,r=Dn()){return this.call("/api/memory/move",{method:"POST",body:JSON.stringify({from:t,to:n,updateLinks:o,deviceLabel:r})})}async exportArchive(t={}){let n=this.token();if(!n)throw new u("errors.NOT_LOGGED_IN");let o=new URLSearchParams;t.scope&&o.set("scope",t.scope),t.types?.length&&o.set("types",t.types.join(","));let r=o.size>0?`?${o.toString()}`:"",i=await fetch(`${this.baseUrl}/api/export${r}`,{headers:{...await H(),authorization:`Bearer ${n}`}});if(!i.ok){let s=await i.json().catch(()=>({}));throw new u(s.code??"errors.UNKNOWN",s.params??{},i.status)}return i}deleteMemory(t){return this.call(this.memoryUrl(t),{method:"DELETE"})}deleteSkill(t){return this.call(`/api/skills/${encodeURIComponent(t)}`,{method:"DELETE"})}deleteSkillFile(t,n){return this.call(this.skillFileUrl(t,n),{method:"DELETE"})}deleteAgent(t){return this.call(`/api/agents/${encodeURIComponent(t)}`,{method:"DELETE"})}listConnections(){return this.call("/api/mcp-connections")}createConnection(t){return this.call("/api/mcp-connections",{method:"POST",body:JSON.stringify(t)})}updateConnection(t,n){return this.call(`/api/mcp-connections/${t}`,{method:"PATCH",body:JSON.stringify(n)})}deleteConnection(t){return this.call(`/api/mcp-connections/${t}`,{method:"DELETE"})}listCapabilities(t){return this.call(`/api/mcp-connections/${t}/capabilities`)}toggleCapability(t,n,o){return this.call(`/api/mcp-connections/${t}/capabilities/${n}`,{method:"PATCH",body:JSON.stringify({enabled:o})})}};import{Entry as jn}from"@napi-rs/keyring";function zn(e,t){let n=()=>new jn(e,t);return{save(o){n().setPassword(o)},read(){try{return n().getPassword()}catch{return null}},clear(){try{n().deletePassword()}catch{}}}}var A=zn("onlymcp","session-token");var Fn={sleep:e=>new Promise(t=>setTimeout(t,e))};async function qe(e,t,n,o=Fn){let r=n;for(;;){await o.sleep(r*1e3);let s=await(await fetch(`${e}/api/auth/device/token`,{method:"POST",headers:{...await H(),"content-type":"application/json"},body:JSON.stringify({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:t,client_id:"onlymcp-cli"})})).json().catch(()=>({}));if(s.access_token)return s.access_token;switch(s.error){case"authorization_pending":break;case"slow_down":r+=5;break;case"access_denied":throw new u("errors.DEVICE_ACCESS_DENIED");case"expired_token":throw new u("errors.DEVICE_CODE_EXPIRED");default:throw new u("errors.DEVICE_LOGIN_FAILED",{error:s.error})}}}import Z from"cacache";var Gn=Number(process.env.ONLYMCP_TTL_SECONDS??360*60),j=class{constructor(t=Fe()){this.dir=t}dir;async get(t){try{let n=await Z.get(this.dir,t),o=n.metadata;return!o||o.expiresAt<=Date.now()?(await Z.rm.entry(this.dir,t),null):n.data.toString("utf8")}catch{return null}}async set(t,n,o=Gn){await Z.put(this.dir,t,n,{metadata:{expiresAt:Date.now()+o*1e3}})}async clear(){await Z.rm.all(this.dir)}};import{appendFileSync as ro,existsSync as Q,mkdirSync as Xe,readFileSync as et,writeFileSync as io}from"node:fs";import{dirname as tt,join as so}from"node:path";function Ze(e){return{language:"bash",text:e}}function E(e,t){return{language:"json",text:JSON.stringify(e,null,2),path:t}}var Hn={id:"claude-code",name:"Claude Code",category:"coding",brand:"claudecode",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://code.claude.com/docs/en/mcp-quickstart",verifiedOn:"2026-09-25",caveats:["clients.caveat.claudeCodeConnectorSynced"],install:{configPath:"~/.claude.json",format:"json",stubDir:null},remote:e=>[Ze(`claude mcp add --transport http --scope user onlymcp ${e.endpoint} --header "Authorization: Bearer ${e.token}"`)]},Wn={id:"claude-app",name:"Claude",category:"chat",brand:"claude",methods:["remote-oauth"],docsUrl:"https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp",verifiedOn:"2026-09-25",caveats:["clients.caveat.claudeHeaderBeta"],install:null,oauthRedirectHosts:["claude.ai","claude.com"],remote:()=>null},Bn={id:"chatgpt",name:"ChatGPT",category:"chat",brand:"openai",methods:["remote-oauth"],docsUrl:"https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt",verifiedOn:"2026-09-25",caveats:["clients.caveat.chatgptDeveloperMode"],install:null,oauthRedirectHosts:["chatgpt.com"],remote:()=>null},Kn={id:"codex",name:"Codex",category:"coding",brand:"openai",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://learn.chatgpt.com/docs/extend/mcp?surface=cli",verifiedOn:"2026-09-25",caveats:["clients.caveat.codexTokenEnv"],install:{configPath:"~/.codex/config.toml",format:"toml",stubDir:"~/.codex/skills"},remote:e=>[Ze([`export ONLYMCP_TOKEN=${e.token}`,`codex mcp add onlymcp --url ${e.endpoint} --bearer-token-env-var ONLYMCP_TOKEN`].join(`
5
+ `))]},Vn={id:"antigravity",aliases:["gemini-cli"],name:"Antigravity",category:"coding",brand:"antigravity",methods:["remote-oauth","remote-header","stdio"],docsUrl:"https://antigravity.google/docs/mcp",verifiedOn:"2026-10-17",caveats:[],install:{configPath:"~/.gemini/config/mcp_config.json",format:"json",stubDir:null},oauthRedirectHosts:["antigravity.google"],remote:e=>[E({mcpServers:{onlymcp:{serverUrl:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"~/.gemini/config/mcp_config.json")]},Jn={id:"gemini-app",name:"Gemini",category:"chat",brand:"googlegemini",methods:["remote-oauth"],docsUrl:"https://support.google.com/gemini/answer/17209137",verifiedOn:"2026-10-10",caveats:["clients.caveat.geminiAppSpark"],install:null,remote:()=>null},Yn={id:"grok",name:"Grok",category:"chat",brand:"grok",methods:["remote-oauth"],docsUrl:"https://docs.x.ai/grok/connectors",verifiedOn:"2026-10-17",caveats:["clients.caveat.grokConnectors","clients.caveat.grokBusinessAdmin"],install:null,oauthRedirectHosts:["grok.com"],remote:()=>null},qn={id:"copilot-vscode",name:"GitHub Copilot (VS Code)",category:"ide",brand:"githubcopilot",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://code.visualstudio.com/docs/agents/reference/mcp-configuration",verifiedOn:"2026-09-25",caveats:["clients.caveat.copilotPolicy"],install:null,remote:e=>[E({servers:{onlymcp:{type:"http",url:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},".vscode/mcp.json")]},Zn={id:"cursor",name:"Cursor",category:"ide",brand:"cursor",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://cursor.com/docs/mcp",verifiedOn:"2026-09-25",caveats:[],install:null,remote:e=>[E({mcpServers:{onlymcp:{url:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"~/.cursor/mcp.json")]},Xn={id:"windsurf",name:"Windsurf",category:"ide",brand:"windsurf",methods:["remote-header","stdio"],docsUrl:"https://docs.windsurf.com/windsurf/cascade/mcp",verifiedOn:"2026-09-25",caveats:["clients.caveat.windsurfAllowlist"],install:null,remote:e=>[E({mcpServers:{onlymcp:{serverUrl:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"~/.codeium/windsurf/mcp_config.json")]},Qn={id:"cline",name:"Cline",category:"ide",brand:"cline",methods:["remote-header","stdio"],docsUrl:"https://docs.cline.bot/mcp/mcp-overview",verifiedOn:"2026-09-25",caveats:[],install:null,remote:e=>[E({mcpServers:{onlymcp:{type:"streamableHttp",url:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"~/.cline/mcp.json")]},eo={id:"zed",name:"Zed",category:"ide",brand:"zedindustries",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://zed.dev/docs/assistant/model-context-protocol",verifiedOn:"2026-09-25",caveats:[],install:null,remote:e=>[E({context_servers:{onlymcp:{url:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"settings.json")]},to={id:"continue",name:"Continue",category:"ide",brand:null,methods:["remote-header","stdio"],docsUrl:"https://docs.continue.dev/customize/deep-dives/mcp",verifiedOn:"2026-09-25",caveats:[],install:null,remote:e=>[{language:"yaml",path:"config.yaml",text:["mcpServers:"," - name: onlymcp"," type: streamable-http",` url: ${e.endpoint}`," requestOptions:"," headers:",` Authorization: Bearer ${e.token}`].join(`
6
+ `)}]},no={id:"opencode",name:"opencode",category:"coding",brand:"opencode",methods:["remote-header","remote-oauth","stdio"],docsUrl:"https://opencode.ai/docs/mcp-servers/",verifiedOn:"2026-09-25",caveats:[],install:null,remote:e=>[E({mcp:{onlymcp:{type:"remote",url:e.endpoint,headers:{Authorization:`Bearer ${e.token}`}}}},"opencode.json")]},oo={id:"gitlab-duo",name:"GitLab Duo",category:"ide",brand:"gitlab",methods:["remote-oauth"],docsUrl:"https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_clients",verifiedOn:"2026-10-18",caveats:["clients.caveat.gitlabDuoUserFile"],install:null,remote:e=>[E({mcpServers:{onlymcp:{type:"http",url:e.endpoint}}},"~/.gitlab/duo/mcp.json")]},X=[Wn,Bn,Jn,Yn,Hn,Kn,Vn,no,qn,Zn,Xn,Qn,eo,to,oo];function ye(e){return X.find(t=>t.id===e||t.aliases?.includes(e))}function be(e,t){let n=new URL(e);if(n.protocol!=="http:"&&n.protocol!=="https:")throw new TypeError(`serverInfo: "${e}" is not an http(s) origin`);let o=n.origin;return{name:"onlymcp",title:"OnlyMCP",version:t,websiteUrl:o,icons:[{src:`${o}/brand/connector-512.png`,mimeType:"image/png"},{src:`${o}/brand/favicon.svg`,mimeType:"image/svg+xml"}]}}var w=e=>({title:e,annotations:{readOnlyHint:!0,openWorldHint:!1}}),S=(e,t)=>({title:e,annotations:{readOnlyHint:!1,destructiveHint:t.destructive,idempotentHint:t.idempotent,openWorldHint:t.openWorld??!1}}),ve={list_memory:w("List memory files"),memory_read:w("Read a memory file"),memory_write:S("Write a memory file",{destructive:!0,idempotent:!0}),memory_delete:S("Delete a memory file",{destructive:!0,idempotent:!1}),memory_search:w("Search memory"),memory_tree:w("Show the memory tree"),memory_move:S("Move a memory file or folder",{destructive:!1,idempotent:!1}),memory_links:w("Show memory links"),list_skills:w("List skills"),get_skill:w("Load a skill"),skill_write:S("Write a skill",{destructive:!0,idempotent:!0}),skill_delete:S("Delete a skill",{destructive:!0,idempotent:!1}),list_skill_files:w("List skill files"),get_skill_file:w("Read a skill file"),skill_file_write:S("Write a skill file",{destructive:!0,idempotent:!0}),skill_file_delete:S("Delete a skill file",{destructive:!0,idempotent:!1}),list_agents:w("List agents"),get_agent_definition:w("Load an agent definition"),agent_write:S("Write an agent",{destructive:!0,idempotent:!0}),agent_delete:S("Delete an agent",{destructive:!0,idempotent:!1}),list_connections:w("List connections"),connection_create:S("Add a connection",{destructive:!1,idempotent:!1,openWorld:!0}),connection_update:S("Update a connection",{destructive:!1,idempotent:!0}),connection_delete:S("Delete a connection",{destructive:!0,idempotent:!1}),connection_tools:w("List a connection's tools"),connection_tool_toggle:S("Switch a connection tool on or off",{destructive:!1,idempotent:!0}),connection_tool_schema:w("Show a connection tool's schema"),connection_call:S("Call a connection tool",{destructive:!0,idempotent:!1,openWorld:!0})};var ao="onlymcp",we="npx",Se=["-y","-p",ao,"onlymcp-mcp"],nt='With the claude.ai connector synced into Claude Code, or the OnlyMCP plugin installed, you need nothing else. This local process is only for skill files on disk: it replaces the remote "onlymcp" entry in ~/.claude.json, and next to the connector or the plugin it would list every tool twice.';function ke(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function lo(e){return JSON.stringify({mcpServers:{onlymcp:e?{command:we,args:Se,env:e}:{command:we,args:Se}}},null,2)}function co(e){let t=["[mcp_servers.onlymcp]",`command = ${ke(we)}`,`args = [${Se.map(ke).join(", ")}]`];for(let[n,o]of Object.entries(e??{}))t.push(`env = { ${n} = ${ke(o)} }`);return`${t.join(`
7
+ `)}
8
+ `}var po={claude:"claude-code",gemini:"antigravity"};function ot(){return X.filter(e=>e.install!==null)}var mo=new Set(ot().map(e=>e.id));function uo(e){let t=po[e]??ye(e)?.id;return t&&mo.has(t)?t:void 0}function Qe(e,t){return e.startsWith("~/")?so(t,e.slice(2)):e}function fo(e){return ot().map(t=>{let n=Qe(t.install.configPath,e),o=t.install.stubDir?Qe(t.install.stubDir,e):null,r=o?tt(o):null,i=t.install.format==="toml"?co(r?{ONLYMCP_STUB_DIRS:r}:void 0):lo();return{id:t.id,name:t.name,configPath:n,format:t.install.format,stubDir:o,stubRoot:r,snippet:i}})}function go(e,t,n){let o={};if(Q(e)){let i=et(e,"utf8").trim();if(i)try{o=JSON.parse(i)}catch{throw new Error(`${e} contains invalid JSON. Nothing written.`)}}let r=o.mcpServers??{};o.mcpServers={...r,[t]:n},io(e,`${JSON.stringify(o,null,2)}
9
+ `,"utf8")}function ho(e,t){return Q(e)&&et(e,"utf8").includes("[mcp_servers.onlymcp]")?"already-present":(ro(e,`
10
+ ${t}`),"written")}function rt(e,t){let n=fo(e);if(t.all){if(t.client)return{kind:"conflicting-options"};let i=n.filter(p=>Q(p.configPath)),s=n.filter(p=>!Q(p.configPath));return i.length===0?{kind:"none-present-for-all",targets:n}:{kind:"all",present:i,skipped:s}}if(!t.client)return{kind:"missing-client",targets:n};let o=uo(t.client),r=o?n.find(i=>i.id===o):void 0;return r?{kind:"single",target:r}:{kind:"unknown-client",targets:n}}function it(e){Xe(tt(e.configPath),{recursive:!0});let t=e.format==="toml"?ho(e.configPath,e.snippet):(()=>{let n=JSON.parse(e.snippet),o=Object.entries(n.mcpServers)[0];if(!o)throw new Error(`${e.name}: empty mcpServers snippet.`);return go(e.configPath,o[0],o[1]),"written"})();return e.stubDir&&Xe(e.stubDir,{recursive:!0}),t}import{access as wo,mkdir as xe,readFile as So,rm as pt,writeFile as te}from"node:fs/promises";import{dirname as Co,join as B}from"node:path";function W(e){if(!e.startsWith(`---
11
+ `))return null;let t=e.indexOf(`
12
+ ---`,3);return t<0?null:e.slice(4,t)}function O(e,t){if(!/^[a-zA-Z0-9_-]+$/.test(t))return;let n=new RegExp(`^${t}:[ \\t]*(.*)$`,"m").exec(e);if(!n)return;let r=n[1].trim().replace(/^(['"])(.*)\1$/,"$2").trim();return r.length>0?r:void 0}function st(e){let t=W(e);if(t===null)return null;let n=O(t,"description");if(!n)return null;let o=O(t,"tools"),r=o?o.replace(/^\[(.*)\]$/,"$1").split(",").map(s=>s.trim()).filter(s=>s.length>0):[],i=O(t,"model");return i?{description:n,model:i,tools:r}:{description:n,tools:r}}function at(e){let t=W(e);return(t===null?void 0:O(t,"description"))??""}function lt(e){let t=W(e);return t===null?!1:O(t,"x-onlymcp-stub")==="true"}function ct(e){if(!e.startsWith(`---
13
+ `))return e;let t=e.indexOf(`
14
+ ---`,3);if(t<0)return e;let n=e.indexOf(`
15
+ `,t+4);return n<0?"":e.slice(n+1)}import{readFile as yo}from"node:fs/promises";var Ce=()=>!1;function bo(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function vo(e){let t=e.split(/\r?\n/).map(n=>n.trim()).filter(n=>n!==""&&!n.startsWith("#")).map(n=>new RegExp(`^${n.split("*").map(bo).join(".*")}$`));return n=>t.some(o=>o.test(n))}async function ee(e=Y()){try{return vo(await yo(e,"utf8"))}catch(t){if(t.code==="ENOENT")return Ce;throw t}}var ko=/^[A-Za-z0-9][A-Za-z0-9._-]*$/;function M(e){if(e.length===0||e.length>200||e==="SKILL.md")return!1;let t=e.split("/");return t.length>8?!1:t.every(n=>ko.test(n)&&n!==".."&&n!==".")}var xo=/^[a-z0-9]+(-[a-z0-9]+)*$/,$o=/^[a-z0-9]+(-[a-z0-9]+)*(:[a-z0-9]+(-[a-z0-9]+)*)?$/,$e="x-onlymcp-stub";function ne(e){return e.replace(/:/g,"-")}function oe(e){return(e??"").replace(/[\r\n]+/g," ").trim()}async function Ae(e,t){let n;try{n=await So(e,"utf8")}catch{return!0}let o=W(n);if(o===null||O(o,$e)!=="true")return!1;let r=O(o,"x-onlymcp-name");return r===void 0||r===t}async function dt(e,t){return await wo(e).then(()=>!0,()=>!1)&&await Ae(e,t)}async function re(e,t,n={}){let o=n.ignore??Ce,[{skills:r},{agents:i}]=await Promise.all([e.listSkills(),e.listAgents()]),s=0,p=0,a=[],l=[],c=0;for(let m of r){if(!$o.test(m.name))continue;let b=B(t,"skills",ne(m.name)),v=B(b,"SKILL.md");if(o(m.name)||o(ne(m.name))){l.push(m.name),await dt(v,m.name)&&(await pt(b,{recursive:!0,force:!0}),c+=1);continue}if(!await Ae(v,m.name)){a.push(`skills/${ne(m.name)}`);continue}await xe(b,{recursive:!0}),await te(v,`---
16
+ name: ${ne(m.name)}
17
+ description: ${oe(m.description)||"Synchronisiert ueber OnlyMCP. Inhalt wird bei Bedarf geladen."}
18
+ ${$e}: true
19
+ x-onlymcp-name: ${m.name}
20
+ ---
21
+
22
+ Call \`get_skill({ name: "${m.name}" })\` on the OnlyMCP server and follow whatever comes back.
23
+ `,"utf8"),p+=1;try{let f=await e.getSkill(m.name);for(let k of f.files??[]){if(!M(k.path))continue;let ue=await e.getSkillFile(m.name,k.path),fe=B(b,...k.path.split("/"));await xe(Co(fe),{recursive:!0}),ue.encoding==="base64"?await te(fe,Buffer.from(ue.content,"base64")):await te(fe,ue.content,"utf8"),s+=1}}catch{}}await xe(B(t,"agents"),{recursive:!0});let h=0;for(let m of i){if(!xo.test(m.name))continue;let b=B(t,"agents",`${m.name}.md`);if(o(m.name)){l.push(m.name),await dt(b,m.name)&&(await pt(b),c+=1);continue}if(!await Ae(b,m.name)){a.push(`agents/${m.name}.md`);continue}let v=oe(m.description),f=m.model?oe(m.model):"",k=m.tools.map(oe).join(", ");await te(b,`---
24
+ name: ${m.name}
25
+ description: ${v}
26
+ ${f?`model: ${f}
27
+ `:""}tools: ${k}
28
+ ${$e}: true
29
+ x-onlymcp-name: ${m.name}
30
+ ---
31
+
32
+ Call \`get_agent_definition({ name: "${m.name}" })\` on the OnlyMCP server and follow whatever comes back.
33
+ `,"utf8"),h+=1}return{skills:p,agents:h,files:s,kept:a,ignored:l,removed:c}}import{Server as rr}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as ir}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as sr,ListToolsRequestSchema as ar}from"@modelcontextprotocol/sdk/types.js";var Ao="https://only-mcp.com";function mt(e=process.env.ONLYMCP_API_URL){if(e===void 0)return Ao;let t;try{t=new URL(e).protocol}catch{}if(t!=="http:"&&t!=="https:"){let n=e.replace(/\/\/[^/@]*@/,"//***@");throw new Error(`ONLYMCP_API_URL is not a valid http(s) URL: ${JSON.stringify(n)}`)}return e}var ut=["OnlyMCP holds this user's skills, agents and memory in the cloud, identical","on every machine. The names below are names only, not content: call","get_skill(name) to load a skill before following it, and","get_agent_definition(name) for an agent. Call list_skills or list_agents for","anything not named here. Memory is live: memory_read(path) and","memory_write(path, content). All of it is writable, not just readable:","skill_write, agent_write and the matching *_delete tools; connection_create","adds another MCP server to this account.","Link with [[name]] or typed: [[skill:name]], [[agent:name]], [[mcp:slug]], [[memory:path]]."].join(" "),To=["Memory is a vault, not a scratch file: read MEMORY.md first, it is the index.","After finishing a task, write one solutions/sol-*.md for what was not obvious","and one entry in history/YYYY-MM-DD.md. Link related files with [[name]];","memory_links(path) says which of a file's links point nowhere yet."].join(" ");function ft(e,t,n,o){if(n.length===0)return"";let r=[];for(let p of n){let a=n.length-r.length-1,l=a>0?` (+${a} more, call ${t})`:"",c=`
34
+
35
+ ${e}: ${[...r,p].join(", ")}${l}`;if(o+c.length>1800)break;r.push(p)}if(r.length===0)return`
36
+
37
+ ${e}: call ${t}`;let i=n.length-r.length,s=i>0?` (+${i} more, call ${t})`:"";return`
38
+
39
+ ${e}: ${r.join(", ")}${s}`}function Te(e,t,n={}){let o=n.brainvault===!1?ut:`${ut} ${To}`,r=t.length>0?26:0;return o+=ft("Skills","list_skills",e,o.length+r),o+=ft("Agents","list_agents",t,o.length),o}import{z as or}from"zod";import{z as gt}from"zod";var Po=gt.object({name:gt.string().min(1).max(64)});function ht(e,t){return{name:"get_agent_definition",description:"Delivers an agent's system prompt from the OnlyMCP cloud.",inputSchema:Po,async handler(n){let o=`agent:${n.name}`,r=await t.get(o);if(r!==null)return{content:[{type:"text",text:r}]};try{let i=await e.getAgent(n.name,{use:!0});return await t.set(o,i.body),{content:[{type:"text",text:i.body}]}}catch(i){return{content:[{type:"text",text:i instanceof u?i.code:"errors.UNKNOWN"}],isError:!0}}}}}import{z as Pe}from"zod";var Io=Pe.object({name:Pe.string().min(1).max(64),path:Pe.string().min(1).max(200)});function yt(e){return{name:"get_skill_file",description:"Delivers a single companion file of a skill from the OnlyMCP cloud, e.g. references/palette.md. Usually not needed: `npx onlymcp sync` places these files next to the SKILL.md.",inputSchema:Io,async handler(t){try{let n=await e.getSkillFile(t.name,t.path);return n.encoding==="base64"?{content:[{type:"text",text:`errors.BINARY_FILE ${n.path} (${n.size} bytes) - fetch it to disk with \`npx onlymcp sync\`.`}],isError:!0}:{content:[{type:"text",text:n.content}]}}catch(n){return{content:[{type:"text",text:n instanceof u?n.code:"errors.UNKNOWN"}],isError:!0}}}}}import{z as bt}from"zod";var _o=bt.object({name:bt.string().min(1).max(64)});function vt(e,t){return{name:"get_skill",description:"Delivers a skill's full content from the OnlyMCP cloud. Called from the stub files.",inputSchema:_o,async handler(n){let o=`skill:${n.name}`,r=await t.get(o);if(r!==null)return{content:[{type:"text",text:r}]};try{let i=await e.getSkill(n.name);return await t.set(o,i.body),{content:[{type:"text",text:i.body}]}}catch(i){return{content:[{type:"text",text:i instanceof u?i.code:"errors.UNKNOWN"}],isError:!0}}}}}import{z as Eo}from"zod";var kt=Eo.object({});function wt(e){return{name:"list_skills",description:"List every skill name available to this user. Use when a skill you need is not named in the server instructions.",inputSchema:kt,async handler(){try{let{skills:t}=await e.listSkills();return{content:[{type:"text",text:t.map(n=>n.name).join(`
40
+ `)}]}}catch(t){return{content:[{type:"text",text:t instanceof u?t.code:"errors.UNKNOWN"}],isError:!0}}}}}function St(e){return{name:"list_agents",description:"List every agent available to this user, with its description. Use when this client has no agent files of its own.",inputSchema:kt,async handler(){try{let{agents:t}=await e.listAgents();return{content:[{type:"text",text:t.map(o=>`${o.name}: ${o.description}`).join(`
41
+ `)}]}}catch(t){return{content:[{type:"text",text:t instanceof u?t.code:"errors.UNKNOWN"}],isError:!0}}}}}import{z as Ct}from"zod";var Oo=/^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*\.md$/i;function C(e){return Oo.test(e)&&!e.split("/").includes("..")}var No=Ct.object({path:Ct.string().min(3).max(256)});function xt(e){let t=new Map;return{name:"memory_read",description:"Reads a memory file live from the OnlyMCP cloud. No disk cache.",inputSchema:No,async handler(n){if(!C(n.path))return{content:[{type:"text",text:"errors.INVALID_PATH"}],isError:!0};try{let o=t.get(n.path);return o||(o=e.readMemory(n.path).then(r=>r.content),t.set(n.path,o),o.finally(()=>t.delete(n.path))),{content:[{type:"text",text:await o}]}}catch(o){return{content:[{type:"text",text:o instanceof u?o.code:"errors.UNKNOWN"}],isError:!0}}}}}import{hostname as Mo}from"node:os";import{z as Ie}from"zod";var Lo=Ie.object({path:Ie.string().min(3).max(256),content:Ie.string().max(1e6)});function $t(e){return{name:"memory_write",description:"Writes a memory file live into the OnlyMCP cloud (write-through).",inputSchema:Lo,async handler(t){if(!C(t.path))return{content:[{type:"text",text:"errors.INVALID_PATH"}],isError:!0};try{let n=await e.writeMemory(t.path,t.content,Mo()),o=n.warning?`${n.path} ${n.updatedAt} ${n.warning.code} ${JSON.stringify(n.warning.params)}`:`${n.path} ${n.updatedAt}`;return n.routedTo&&(o+=` (created in team ${n.routedTo.teamName})`),{content:[{type:"text",text:o}]}}catch(n){return{content:[{type:"text",text:n instanceof u?n.code:"errors.UNKNOWN"}],isError:!0}}}}}import{z as x}from"zod";var L=e=>({content:[{type:"text",text:e}]}),z=e=>({content:[{type:"text",text:e}],isError:!0});function ie(e){return e instanceof u?e.code:"errors.UNKNOWN"}var Ro=x.object({query:x.string().min(1).max(200),types:x.string().max(64).optional(),limit:x.number().int().min(1).max(50).optional()});function Tt(e){return{name:"memory_search",description:"Full-text search across memory, skills and agents. Delivers the best hits with a short snippet each.",inputSchema:Ro,async handler(t){try{let{hits:n}=await e.searchContent(t.query,{types:t.types?.split(",").map(o=>o.trim()).filter(o=>o!==""),limit:t.limit});return n.length===0?L(`Nothing found for ${t.query}`):L(n.map(o=>`${o.kind} ${o.path??o.name}${o.scope==="team"?" (team)":""}
42
+ ${o.snippet}`).join(`
43
+ `))}catch(n){return z(ie(n))}}}}var Uo=x.object({scope:x.string().max(96).optional()});function Pt(e){return{name:"memory_tree",description:"This account's folder structure: every folder, empty ones included, and the files in it.",inputSchema:Uo,async handler(t){try{let{nodes:n}=await e.memoryTree(t.scope??"all");return n.length===0?L("(empty)"):L(n.map(o=>{let r=o.path.split("/").length-1,i=o.kind==="folder"?`${o.name}/`:o.name;return`${" ".repeat(r)}${i}${o.scope==="team"?" (team)":""}`}).join(`
44
+ `))}catch(n){return z(ie(n))}}}}var Do=x.object({from:x.string().min(3).max(256),to:x.string().min(3).max(256),updateLinks:x.boolean().optional()});function At(e){return`${e} ${e===1?"file":"files"}`}function It(e){return{name:"memory_move",description:"Moves or renames a memory file. Every [[link]] that would no longer find it follows along, by name or by path, unless turned off.",inputSchema:Do,async handler(t){if(!C(t.from)||!C(t.to))return z("errors.INVALID_PATH");let n=t.updateLinks??!0;try{let o=await e.moveMemory(t.from,t.to,n);return L(`${o.from} moved to ${o.to}`+(n?`, links updated in ${At(o.rewritten)}, ${At(o.skipped)} left unchanged`:""))}catch(o){return z(ie(o))}}}}var jo=x.object({path:x.string().min(3).max(256)});function _t(e){return{name:"memory_links",description:"A memory file's [[links]], split between the ones that point somewhere and the ones that point nowhere.",inputSchema:jo,async handler(t){if(!C(t.path))return z("errors.INVALID_PATH");try{let n=await e.readMemory(t.path),{resolved:o,unresolved:r}=n.links;return o.length===0&&r.length===0?L(`${t.path} does not link to anything.`):L([o.length>0?`Resolved: ${o.join(", ")}`:"",r.length>0?`Open (no file with this name yet): ${r.join(", ")}`:""].filter(i=>i!=="").join(`
45
+ `))}catch(n){return z(ie(n))}}}}import{z as g}from"zod";var zo=e=>({content:[{type:"text",text:e}]}),Et=e=>({content:[{type:"text",text:e}],isError:!0});async function F(e){try{return zo(await e())}catch(t){if(t instanceof u){let n=Object.keys(t.params).length?` ${JSON.stringify(t.params)}`:"";return Et(`${t.code}${n}`)}return Et("errors.UNKNOWN")}}var K=g.string().min(1).max(32).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/,"errors.INVALID_SLUG");async function se(e,t){let{connections:n}=await e.listConnections(),o=n.filter(r=>r.slug===t);return o.find(r=>r.scope==="personal")??o[0]??null}function _e(e){let t=e.authType==="none"?"no auth":`${e.authType}, secret ...${e.secretHint??"?"}`;return`${e.slug} \u2014 ${e.name} (${e.scope}, ${e.enabled?e.status:"disabled"}, ${t})
46
+ ${e.url}${e.statusError?`
47
+ last error: ${e.statusError}`:""}`}var Fo=g.object({});function Ot(e){return{name:"list_connections",description:"List the MCP servers connected through this OnlyMCP account, with the tool-name prefix each one uses.",inputSchema:Fo,handler:()=>F(async()=>{let{connections:t}=await e.listConnections();return t.length===0?"No MCP servers are connected yet. Add one with connection_create.":t.map(_e).join(`
48
+ `)})}}var Go=g.object({name:g.string().min(1).max(80),slug:K,url:g.string().url().max(2e3),transport:g.enum(["http","sse"]).default("http"),authType:g.enum(["none","bearer","header"]).default("none"),authHeaderName:g.string().max(80).optional(),secret:g.string().max(4e3).optional(),teamId:g.string().max(64).optional()});function Nt(e){return{name:"connection_create",description:"Connect a remote MCP server (http or sse only - a stdio server cannot be started on somebody else's behalf). It is probed straight away. Passing a secret here puts it into this conversation; leave it out and set it in the dashboard if that matters.",inputSchema:Go,handler:t=>F(async()=>{let{connection:n}=await e.createConnection(t);return`Connected.
49
+ ${_e(n)}
50
+
51
+ Its tools are now available as "${n.slug}__<tool>".`})}}var Ho=g.object({slug:K,name:g.string().min(1).max(80).optional(),url:g.string().url().max(2e3).optional(),transport:g.enum(["http","sse"]).optional(),authType:g.enum(["none","bearer","header"]).optional(),authHeaderName:g.string().max(80).optional(),secret:g.string().max(4e3).optional(),enabled:g.boolean().optional()});function Mt(e){return{name:"connection_update",description:"Change a connected MCP server. Only the fields you pass are touched; url, transport, authType and secret trigger a fresh probe.",inputSchema:Ho,handler:t=>F(async()=>{let n=await se(e,t.slug);if(!n)return`Connection not found: ${t.slug}`;let{slug:o,...r}=t,{connection:i}=await e.updateConnection(n.id,r);return`Updated.
52
+ ${_e(i)}`})}}var Wo=g.object({slug:K});function Lt(e){return{name:"connection_delete",description:"Disconnect a remote MCP server and forget its stored credential. Its tools disappear from the gateway.",inputSchema:Wo,handler:t=>F(async()=>{let n=await se(e,t.slug);return n?(await e.deleteConnection(n.id),`Disconnected ${t.slug}.`):`Connection not found: ${t.slug}`})}}var Bo=g.object({slug:K});function Rt(e){return{name:"connection_tools",description:"List what one connected server offers, and whether each tool is switched on for the gateway.",inputSchema:Bo,handler:t=>F(async()=>{let n=await se(e,t.slug);if(!n)return`Connection not found: ${t.slug}`;let{capabilities:o}=await e.listCapabilities(n.id);return o.length===0?`${t.slug} has nothing on record yet. Its status is "${n.status}".`:o.map(r=>`${r.enabled?"[on] ":"[off]"} ${r.kind} ${r.name}${r.exposedAs?` -> ${r.exposedAs}`:""}${r.description?` \u2014 ${r.description}`:""}`).join(`
53
+ `)})}}var Ko=g.object({slug:K,tool:g.string().min(1).max(200),enabled:g.boolean()});function Ut(e){return{name:"connection_tool_toggle",description:"Switch one tool of a connected server on or off. A tool that is off costs no room in any client's tool list.",inputSchema:Ko,handler:t=>F(async()=>{let n=await se(e,t.slug);if(!n)return`Connection not found: ${t.slug}`;let{capabilities:o}=await e.listCapabilities(n.id),r=o.find(i=>i.kind==="tool"&&i.name===t.tool);return r?(await e.toggleCapability(n.id,r.id,t.enabled),`${t.slug}__${r.name} is now ${t.enabled?"on":"off"}.`):`${t.slug} offers no tool called ${t.tool}.`})}}import{z as y}from"zod";var Vo=e=>({content:[{type:"text",text:e}]}),V=e=>({content:[{type:"text",text:e}],isError:!0});async function P(e){try{return Vo(await e())}catch(t){if(t instanceof u){let n=Object.keys(t.params).length?` ${JSON.stringify(t.params)}`:"";return V(`${t.code}${n}`)}return V("errors.UNKNOWN")}}var R=y.string().min(1).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/,"errors.INVALID_NAME"),Jo=y.object({name:R,body:y.string().max(2e5)});function Dt(e){return{name:"skill_write",description:"Create a skill or replace the body of an existing one. The body is the whole SKILL.md, frontmatter included.",inputSchema:Jo,handler:t=>P(async()=>{let n=await e.putSkill(t.name,t.body);return`${n.name} ${n.updatedAt}`})}}var Yo=y.object({name:R});function jt(e){return{name:"skill_delete",description:"Delete one skill and every file that belongs to it. Moves it to the trash; it can be restored in the dashboard for the trash period (default 7 days).",inputSchema:Yo,handler:t=>P(async()=>(await e.deleteSkill(t.name),`Deleted skill ${t.name}`))}}var qo=y.object({skill:R});function zt(e){return{name:"list_skill_files",description:"List the companion files of one skill, with their size in bytes.",inputSchema:qo,handler:t=>P(async()=>{let{files:n}=await e.getSkill(t.skill);return n.length===0?`${t.skill} has no companion files.`:n.map(o=>`${o.path} (${o.size} bytes, ${o.encoding})`).join(`
54
+ `)})}}var Zo=y.object({skill:R,path:y.string().min(1).max(256),content:y.string().max(3e6),encoding:y.enum(["utf8","base64"]).default("utf8")});function Ft(e){return{name:"skill_file_write",description:"Create or replace one companion file of a skill. Use encoding base64 for anything that is not text.",inputSchema:Zo,handler:t=>M(t.path)?P(async()=>{let n=await e.putSkillFile(t.skill,t.path,t.content,t.encoding);return`${t.skill}/${n.path} ${n.size} bytes`}):Promise.resolve(V("errors.INVALID_PATH"))}}var Xo=y.object({skill:R,path:y.string().min(1).max(256)});function Gt(e){return{name:"skill_file_delete",description:"Delete one companion file of a skill. The skill itself stays.",inputSchema:Xo,handler:t=>M(t.path)?P(async()=>(await e.deleteSkillFile(t.skill,t.path),`Deleted ${t.skill}/${t.path}`)):Promise.resolve(V("errors.INVALID_PATH"))}}var Qo=y.object({name:R,description:y.string().min(1).max(1e3),body:y.string().max(2e5),model:y.string().max(64).optional(),tools:y.array(y.string().max(128)).max(200).optional()});function Ht(e){return{name:"agent_write",description:"Create an agent or replace an existing one. Leave model out to keep the one it already has.",inputSchema:Qo,handler:t=>P(async()=>{let n=await e.getAgent(t.name).catch(()=>null),o=await e.putAgent(t.name,{description:t.description,body:t.body,model:t.model??n?.model??void 0,tools:t.tools??n?.tools??[]});return`${o.name} ${o.updatedAt}`})}}var er=y.object({name:R});function Wt(e){return{name:"agent_delete",description:"Delete one agent. Moves it to the trash; it can be restored in the dashboard for the trash period (default 7 days).",inputSchema:er,handler:t=>P(async()=>(await e.deleteAgent(t.name),`Deleted agent ${t.name}`))}}var tr=y.object({});function Bt(e){return{name:"list_memory",description:"List the paths of every memory file in this account, private and team, sorted by path.",inputSchema:tr,handler:()=>P(async()=>{let{files:t}=await e.listMemory();return t.length===0?"(none)":t.map(n=>n.path+(n.scope==="team"?` (team${n.teamName?`: ${n.teamName}`:""})`:"")).sort().join(`
55
+ `)})}}var nr=y.object({path:y.string().min(3).max(256)});function Kt(e){return{name:"memory_delete",description:"Delete one memory file. Moves it to the trash; it can be restored in the dashboard for the trash period (default 7 days).",inputSchema:nr,handler:t=>C(t.path)?P(async()=>(await e.deleteMemory(t.path),`Deleted ${t.path}`)):Promise.resolve(V("errors.INVALID_PATH"))}}function Vt(e,t){return[xt(e),$t(e),Bt(e),Kt(e),Tt(e),Pt(e),It(e),_t(e),wt(e),vt(e,t),Dt(e),jt(e),zt(e),yt(e),Ft(e),Gt(e),St(e),ht(e,t),Ht(e),Wt(e),Ot(e),Nt(e),Mt(e),Lt(e),Rt(e),Ut(e)]}function Jt(e){return e.map(t=>{let n=ve[t.name];if(!n)throw new Error(`No title and annotations in the registry table for the tool ${t.name}`);return{name:t.name,title:n.title,description:t.description,inputSchema:or.toJSONSchema(t.inputSchema),annotations:n.annotations}})}async function Yt(){let e=mt(),t=new T(e,()=>A.read()),n=new j,o=Vt(t,n),r=await(async()=>{try{let[{skills:s},{agents:p},a]=await Promise.all([t.listSkills(),t.listAgents(),t.getMe().catch(()=>({brainvaultMode:!0}))]);return Te(s.map(l=>l.name),p.map(l=>l.name),{brainvault:a.brainvaultMode!==!1})}catch{return Te([],[])}})(),i=new rr(be(e,D),{capabilities:{tools:{}},instructions:r});i.setRequestHandler(ar,async()=>({tools:Jt(o)})),i.setRequestHandler(sr,async s=>{let p=o.find(l=>l.name===s.params.name);if(!p)return{content:[{type:"text",text:"errors.UNKNOWN_TOOL"}],isError:!0};let a=p.inputSchema.safeParse(s.params.arguments??{});return a.success?p.handler(a.data):{content:[{type:"text",text:"errors.INVALID_ARGUMENTS"}],isError:!0}}),ee().then(s=>{for(let p of q())re(t,p,{ignore:s}).catch(()=>{})}).catch(()=>{}),await i.connect(new ir)}import{readdir as dr,readFile as mr,realpath as Xt,stat as ur}from"node:fs/promises";import{join as fr,relative as gr,resolve as hr,sep as Qt}from"node:path";var lr=[/^\.env(\..+)?$/i,/^\.credentials\.json$/i,/^id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/i,/\.(pem|key|p12|pfx|jks|keystore)$/i,/^\.(npmrc|netrc|pgpass|htpasswd)$/i,/^\.git-credentials$/i];function qt(e){return lr.some(t=>t.test(e))}var cr=[{rule:"anthropic",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{rule:"openai",re:/\bsk-(?!ant-)[A-Za-z0-9_-]{20,}/},{rule:"github",re:/\bgh[pousr]_[A-Za-z0-9]{36,}/},{rule:"aws",re:/\bAKIA[0-9A-Z]{16}\b/},{rule:"slack",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{rule:"google",re:/\bAIza[0-9A-Za-z_-]{35}\b/},{rule:"jwt",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\./},{rule:"private-key",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{rule:"url-credentials",re:/\b[a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:[^\s:/@]{6,}@/i},{rule:"assignment",re:/\b(api[_-]?key|secret|passwd|password|token|credential)\b\s*[:=]\s*['"][^'"\s]{16,}['"]/i}];function pr(e,t){let n=e.indexOf(t);return`${e.slice(Math.max(0,n-24),n)}\u2026${t.slice(0,4)}\u2026 (${t.length} characters)`}function Zt(e){let t=[],n=e.split(`
56
+ `);for(let o=0;o<n.length;o+=1){let r=n[o];for(let{rule:i,re:s}of cr){let p=s.exec(r);if(p){t.push({rule:i,line:o+1,excerpt:pr(r,p[0])});break}}}return t}var yr=new Set([".git","node_modules","__pycache__",".venv","venv",".pytest_cache",".mypy_cache","dist","build",".next",".cache"]),br=2*1024*1024;function vr(e){return e.subarray(0,8192).includes(0)}async function U(e,t={}){let n=t.maxBytes??br,o=await Xt(hr(e)),r=[];async function i(s){let p=await dr(s,{withFileTypes:!0});for(let a of p){let l=fr(s,a.name);if(a.isDirectory()){if(yr.has(a.name))continue;await i(l);continue}let c;try{c=await Xt(l)}catch{continue}if(c!==o&&!c.startsWith(o+Qt))continue;let h=gr(o,c).split(Qt).join("/");if(qt(a.name)){r.push({absolute:c,relative:h,bytes:0,binary:!1,hits:[],skip:"denied-name",description:null});continue}let m=await ur(c);if(!m.isFile())continue;if(m.size>n){r.push({absolute:c,relative:h,bytes:m.size,binary:!1,hits:[],skip:"too-large",description:null});continue}let b=await mr(c),v=vr(b),f=!v&&h.toLowerCase().endsWith(".md")?b.toString("utf8"):null;r.push({absolute:c,relative:h,bytes:m.size,binary:v,hits:v?[]:Zt(b.toString("utf8")),skip:f!==null&&lt(f)?"stub":null,description:f===null?null:at(f)||null})}}return await i(o),r.sort((s,p)=>s.relative.localeCompare(p.relative)),r}import{basename as kr}from"node:path";var wr=/^[a-z0-9]+(-[a-z0-9]+)*$/,Sr=/^[a-z0-9]+(-[a-z0-9]+)*(:[a-z0-9]+(-[a-z0-9]+)*)?$/,en=/^[a-z0-9][a-z0-9._-]*$/i;function Cr(e){let t=e.split("/"),n=t.indexOf("skills");return n>=4&&t[n-4]==="cache"&&n+1<t.length-1?{prefix:t.slice(0,n+2).join("/"),name:`${t[n-2]}:${t[n+1]}`,plugin:t[n-2],path:t.slice(n+2).join("/")}:t.length<2?null:{prefix:t[0],name:t[0],plugin:null,path:t.slice(1).join("/")}}function ae(e){return e.skip===null&&e.hits.length===0}function le(e){return e.skip??(e.hits.length>0?"flagged":"unusable")}function xr(e,t={}){let n=[],o=[],r=[],i=new Map,s=t.root?kr(t.root.replace(/[/\\]+$/,"")):"",p=en.test(s)?{key:s,kind:"directory",label:s}:null;for(let a of e){let l=Cr(a.relative);if(!l){o.push({relative:a.relative,reason:"not-in-a-skill-folder"});continue}let c=i.get(l.prefix);c?c.entries.push(a):i.set(l.prefix,{at:l,entries:[a]})}for(let[a,{at:l,entries:c}]of i){let h=l.name;if(!Sr.test(h)){o.push({relative:a,reason:"invalid-name"});continue}let m=c.find(f=>f.relative===`${a}/SKILL.md`);if(!m){o.push({relative:a,reason:"no-skill-md"});continue}if(!ae(m)){o.push({relative:`${a}/SKILL.md`,reason:le(m)});continue}let b=[];for(let f of c){if(f===m)continue;if(!ae(f)){o.push({relative:f.relative,reason:le(f)});continue}let k=f.relative.slice(a.length+1);if(!M(k)){o.push({relative:f.relative,reason:"invalid-path"});continue}b.push({path:k,absolute:f.absolute,bytes:f.bytes,binary:f.binary})}b.sort((f,k)=>f.path.localeCompare(k.path));let v=m.description??"";v||r.push(h),n.push({name:h,source:m.absolute,sourcePath:m.relative,description:v,collection:l.plugin?en.test(l.plugin)?{key:l.plugin,kind:"plugin",label:l.plugin}:null:p,bytes:m.bytes,files:b})}return n.sort((a,l)=>a.name.localeCompare(l.name)),r.sort(),{plans:n,skipped:o,noDescription:r}}function $r(e){let t=[],n=[];for(let o of e){if(o.relative.includes("/")){n.push({relative:o.relative,reason:"not-at-top-level"});continue}if(!o.relative.endsWith(".md")){n.push({relative:o.relative,reason:"not-markdown"});continue}let r=o.relative.slice(0,-3);if(!wr.test(r)){n.push({relative:o.relative,reason:"invalid-name"});continue}if(!ae(o)){n.push({relative:o.relative,reason:le(o)});continue}t.push({name:r,source:o.absolute,bytes:o.bytes})}return t.sort((o,r)=>o.name.localeCompare(r.name)),{plans:t,skipped:n}}function Ar(e){let t=[],n=[];for(let o of e){if(!C(o.relative)){n.push({relative:o.relative,reason:"invalid-path"});continue}if(!ae(o)){n.push({relative:o.relative,reason:le(o)});continue}t.push({path:o.relative,source:o.absolute,bytes:o.bytes})}return t.sort((o,r)=>o.path.localeCompare(r.path)),{plans:t,skipped:n}}function Ee(e,t={}){let n=xr(e.skills??[],{root:t.skillsRoot}),o=$r(e.agents??[]),r=Ar(e.memory??[]),i=[...e.skills??[],...e.agents??[],...e.memory??[]];return{skills:n.plans,agents:o.plans,memory:r.plans,flagged:i.filter(s=>s.skip===null&&s.hits.length>0),skipped:[...n.skipped,...o.skipped,...r.skipped],noDescription:n.noDescription}}import{readFile as ce}from"node:fs/promises";import{hostname as Tr}from"node:os";function pe(e){return e instanceof u?e.code:e instanceof Error?e.message:String(e)}function Oe(e){return e.skills.length+e.skills.reduce((t,n)=>t+n.files.length,0)+e.agents.length+e.memory.length}async function tn(e,t,n){let o={skills:0,skillFiles:0,agents:0,memory:0,failures:[]},r=Oe(t),i=0,s=a=>n(i+=1,r,a);for(let a of t.skills){try{await e.putSkill(a.name,await ce(a.source,"utf8"),{...a.description?{description:a.description}:{},sourcePath:a.sourcePath,...a.collection?{collection:a.collection}:{}}),o.skills+=1}catch(l){o.failures.push({what:`skill ${a.name}`,code:pe(l)}),i+=a.files.length,s(a.name);continue}s(a.name);for(let l of a.files){try{let c=await ce(l.absolute);await e.putSkillFile(a.name,l.path,l.binary?c.toString("base64"):c.toString("utf8"),l.binary?"base64":"utf8"),o.skillFiles+=1}catch(c){o.failures.push({what:`${a.name}/${l.path}`,code:pe(c)})}s(`${a.name}/${l.path}`)}}for(let a of t.agents){try{let l=await ce(a.source,"utf8"),c=st(l);if(!c){o.failures.push({what:`agent ${a.name}`,code:"no-frontmatter"}),s(a.name);continue}await e.putAgent(a.name,{...c,body:ct(l)}),o.agents+=1}catch(l){o.failures.push({what:`agent ${a.name}`,code:pe(l)})}s(a.name)}let p=Tr();for(let a of t.memory){try{await e.writeMemory(a.path,await ce(a.source,"utf8"),p),o.memory+=1}catch(l){o.failures.push({what:a.path,code:pe(l)})}s(a.path)}return o}function nn(e){return e.skills.length===0&&e.agents.length===0&&e.memory.length===0}function on(e){return e.skills.length+e.agents.length+e.memory.length}function Ne(e){return e.scope==="personal"&&e.isOwn&&e.archivedAt===null}async function rn(e,t,n){let o={skills:[],agents:[],memory:[]};if(n.skills){let r=new Set(t.skills.map(s=>s.name)),{skills:i}=await e.listSkills();o.skills=i.filter(s=>Ne(s)&&!r.has(s.name)).map(s=>s.name)}if(n.agents){let r=new Set(t.agents.map(s=>s.name)),{agents:i}=await e.listAgents();o.agents=i.filter(s=>Ne(s)&&!r.has(s.name)).map(s=>s.name)}if(n.memory){let r=new Set(t.memory.map(s=>s.path)),{files:i}=await e.listMemory();o.memory=i.filter(s=>Ne(s)&&!r.has(s.path)).map(s=>s.path)}return o.skills.sort(),o.agents.sort(),o.memory.sort(),o}function Pr(e){return e instanceof u?e.code:e instanceof Error?e.message:String(e)}async function sn(e,t){let n={deleted:0,failures:[]},o=async(r,i,s)=>{for(let p of r)try{await s(p),n.deleted+=1}catch(a){n.failures.push({what:i(p),code:Pr(a)})}};return await o(t.skills,r=>`skill ${r}`,r=>e.deleteSkill(r)),await o(t.agents,r=>`agent ${r}`,r=>e.deleteAgent(r)),await o(t.memory,r=>r,r=>e.deleteMemory(r)),n}import{mkdir as an,writeFile as Ir}from"node:fs/promises";import{dirname as _r,resolve as ln,sep as Er}from"node:path";async function cn(e,t){let n=ln(t);await an(n,{recursive:!0});let o={written:0,refused:[],collisions:[],failures:[]},{files:r}=await e.listMemory(),i=[...r].sort((p,a)=>p.scope===a.scope?p.path.localeCompare(a.path):p.scope==="personal"?-1:1),s=new Set;for(let p of i){if(!C(p.path)){o.refused.push(p.path);continue}let a=ln(n,...p.path.split("/"));if(!a.startsWith(n+Er)){o.refused.push(p.path);continue}if(s.has(a)){o.collisions.push(p.path);continue}try{let l=await e.readMemory(p.path);await an(_r(a),{recursive:!0}),await Ir(a,l.content,"utf8"),s.add(a),o.written+=1}catch(l){o.failures.push({what:p.path,code:l instanceof Error?l.message:String(l)})}}return o}import{mkdir as pn,writeFile as Or}from"node:fs/promises";import{dirname as Nr,resolve as dn,sep as Mr}from"node:path";import{unzipSync as Lr}from"fflate";async function mn(e,t){let n=dn(t);await pn(n,{recursive:!0});let o={written:0,refused:[],failed:[]},r=Lr(e);for(let[i,s]of Object.entries(r)){if(i.endsWith("/"))continue;let p=i.split("/");if(i.startsWith("/")||/^[a-zA-Z]:/.test(i)||i.includes("\\")||p.some(c=>c===""||c==="."||c==="..")){o.refused.push(i);continue}let l=dn(n,...p);if(!l.startsWith(n+Mr)){o.refused.push(i);continue}try{await pn(Nr(l),{recursive:!0}),await Or(l,s),o.written+=1}catch(c){o.failed.push({path:i,reason:c instanceof Error?c.message:String(c)})}}return o}var I=process.env.ONLYMCP_API_URL??"https://only-mcp.com",_=new Ur().name("onlymcp").version(D);function Br(e,t){try{let n=new URL(e);return(n.protocol==="https:"||n.protocol==="http:")&&n.origin===t}catch{return!1}}_.command("login").description("Connect this device to OnlyMCP").action(async()=>{Ue("OnlyMCP"),d.info(`This device will register itself as: ${await he()}`);let t=await(await fetch(`${I}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:"onlymcp-cli"})})).json();d.info(`Code: ${t.user_code}`),d.info(t.verification_uri),console.log(await jr.toString(t.verification_uri_complete,{type:"terminal",small:!0})),Br(t.verification_uri_complete,new URL(I).origin)&&await Dr(t.verification_uri_complete).catch(()=>{});let n=me();n.start("Waiting for confirmation");let o=await qe(I,t.device_code,t.interval??5);A.save(o),n.stop("Connected");let r=new T(I,()=>A.read()),i=await yn(r),s=bn(i);s&&d.info(s),G(`${i.skills} skills, ${i.agents} agents, ${i.files} files ready`)});async function yn(e){let t=await ee(),n={skills:0,agents:0,files:0,kept:[],ignored:new Set,removed:0};for(let o of q()){let r=await re(e,o,{ignore:t});n.skills+=r.skills,n.agents+=r.agents,n.files+=r.files;for(let i of r.kept)n.kept.push(`${o}/${i}`);for(let i of r.ignored)n.ignored.add(i);n.removed+=r.removed}return n}function bn(e){if(e.ignored.size===0)return null;let t=e.removed>0?`, ${e.removed} earlier stubs removed`:"";return`${e.ignored.size} left out by ${Y()}${t}`}async function Re(e,t,n){try{await e.reportRun(t,n)}catch{}}async function Kr(e){try{return await new T(I,()=>e).signOut(),null}catch(t){return t instanceof u&&t.status>0?`HTTP ${t.status}`:t instanceof Error?t.message:String(t)}}_.command("logout").description("End this device's session, then remove its login and cache").action(async()=>{let e=A.read(),t=e?await Kr(e):null;if(A.clear(),await new j().clear(),t){d.warn(`The session on the server could not be ended (${t}). It counts as a device until it expires, or until you remove it in the dashboard under Sessions and devices.`),d.success("Logged out on this device");return}d.success("Logged out")});_.command("status").description("Show whether this device is logged in").action(()=>{d.info(A.read()?"logged in":"not logged in")});_.command("sync").description("Rewrite the stub files").option("--memory <dir>","Also write memory as files into this folder").action(async e=>{let t=new T(I,()=>A.read()),n=await yn(t);d.success(`${n.skills} skills, ${n.agents} agents, ${n.files} files`);let o=bn(n);o&&d.info(o),n.kept.length>0&&d.warn([`Left untouched, because something of your own is already there (${n.kept.length})`,...n.kept.slice(0,10).map(s=>` ${s}`),...n.kept.length>10?[` ... and ${n.kept.length-10} more`]:[]].join(`
57
+ `));let r=n.skills+n.agents+n.files;if(!e.memory){await Re(t,"sync",r);return}let i=await cn(t,e.memory);d.success(`${i.written} memory files to ${e.memory}`),i.collisions.length>0&&d.warn(["Team files with a path an existing file of yours already has",...i.collisions.map(s=>` ${s}`)].join(`
58
+ `)),i.refused.length>0&&d.warn(["Paths refused",...i.refused.map(s=>` ${s}`)].join(`
59
+ `)),i.failures.length>0&&(d.error([`${i.failures.length} failed`,...i.failures.map(s=>` ${s.what} ${s.code}`)].join(`
60
+ `)),process.exitCode=1),i.failures.length===0&&await Re(t,"sync",r+i.written)});_.command("install").description("Register OnlyMCP in an MCP client").option("--client <id>","claude-code, codex or antigravity (aliases: claude, gemini, gemini-cli)").option("--all","Write every config file that already exists on this machine").option("--print","Only print, write nothing").action(e=>{let t=process.env.ONLYMCP_HOME??hn(),n=rt(t,e);function o(r){if(e.print){d.info(`${r.name} -> ${r.configPath}`),console.log(r.snippet);return}try{it(r)==="already-present"?d.info(`${r.name} is already set up: ${r.configPath}`):d.success(`${r.name} set up: ${r.configPath}`),r.id==="claude-code"&&d.info(nt)}catch(i){d.error(i instanceof Error?i.message:String(i)),process.exitCode=1}}switch(n.kind){case"conflicting-options":d.error("Pass either --client <id> or --all, not both."),process.exitCode=2;return;case"missing-client":d.info(`Known clients: ${n.targets.map(r=>r.id).join(", ")}`),d.info("Pass --client <id>, or --all."),process.exitCode=2;return;case"unknown-client":d.error(`Unknown client. Known: ${n.targets.map(r=>r.id).join(", ")}`),process.exitCode=1;return;case"none-present-for-all":d.error("No known client is set up on this machine yet."),d.info(`Pass --client <id> to write its config anyway. Known: ${n.targets.map(r=>r.id).join(", ")}.`),process.exitCode=2;return;case"single":o(n.target);return;case"all":for(let r of n.present)o(r);n.skipped.length>0&&d.info(`Skipped, not set up on this machine: ${n.skipped.map(r=>r.id).join(", ")}`);return}});function Le(e){return e<1024?`${e} B`:e<1024*1024?`${Math.round(e/1024)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function de(e){return e.reduce((t,n)=>t+n.bytes,0)}function Vr(e,t){let n=e.skills.reduce((i,s)=>i+s.files.length,0),o=e.skills.filter(i=>i.files.length>0).length,r=e.skills.reduce((i,s)=>i+de(s.files),0);if(t.skills&&d.message(`Skills ${String(e.skills.length).padStart(4)} from ${t.skills}
61
+ ${String(n).padStart(4)} more files across ${o} skills ${Le(de(e.skills)+r)}`),t.agents&&d.message(`Agents ${String(e.agents.length).padStart(4)} from ${t.agents} ${Le(de(e.agents))}`),t.memory&&d.message(`Memory ${String(e.memory.length).padStart(4)} from ${t.memory} ${Le(de(e.memory))}`),e.skipped.length>0){let i=new Map;for(let s of e.skipped)i.set(s.reason,(i.get(s.reason)??0)+1);d.message(["Skipped",...[...i].map(([s,p])=>` ${String(p).padStart(4)} ${s}`)].join(`
62
+ `))}e.noDescription.length>0&&d.warn(["Missing a description in the SKILL.md header",...e.noDescription.map(i=>` ${i}`)].join(`
63
+ `)),e.flagged.length>0&&d.warn(["Held back over possible credentials (none of it left this machine)",...e.flagged.flatMap(i=>[` ${i.relative}`,...i.hits.map(s=>` line ${s.line} rule ${s.rule} ${s.excerpt}`)])].join(`
64
+ `))}_.command("import").description("Bring existing local skills, agents and memory into OnlyMCP").option("--skills <dir>","Folder with skill subfolders, or a plugin cache (~/.claude/skills when no folder is named)").option("--agents <dir>","Folder with agent files (~/.claude/agents when no folder is named)").option("--memory <dir>","Folder with memory files (no default)").option("--dry-run","Only show what would be uploaded, then stop").option("--yes","Upload without asking first").option("--prune","Delete, after confirmation, what the account holds and the source no longer has").action(async e=>{let t=process.env.ONLYMCP_HOME??hn(),n=!!(e.skills||e.agents||e.memory),o={skills:n?e.skills:gn(t,".claude","skills"),agents:n?e.agents:gn(t,".claude","agents"),memory:e.memory};Ue("OnlyMCP Import");let r=me();r.start("Reading the folders");let i;try{let[a,l,c]=await Promise.all([o.skills&&$(o.skills)?U(o.skills):Promise.resolve([]),o.agents&&$(o.agents)?U(o.agents):Promise.resolve([]),o.memory&&$(o.memory)?U(o.memory):Promise.resolve([])]);i=Ee({skills:a,agents:l,memory:c},{skillsRoot:o.skills})}catch(a){r.stop("Aborted"),d.error(a instanceof Error?a.message:String(a)),process.exitCode=1;return}r.stop("Read"),Vr(i,{skills:o.skills&&$(o.skills)?o.skills:void 0,agents:o.agents&&$(o.agents)?o.agents:void 0,memory:o.memory&&$(o.memory)?o.memory:void 0});let s=Oe(i);if(s===0&&!e.prune){G("Nothing to do.");return}if(e.dryRun&&!e.prune){G("Nothing uploaded (--dry-run).");return}let p=new T(I,()=>A.read());try{let a=await p.getMe();d.info(`Target ${I} as ${a.email} plan ${a.plan.name}`)}catch(a){d.error(`Not logged in, or the server is unreachable (${a instanceof Error?a.message:String(a)}).`),d.info("First: npx onlymcp login"),process.exitCode=1;return}if(s>0&&!e.yes&&!e.dryRun){if(!process.stdin.isTTY){d.error("No terminal for the prompt. Run --dry-run first, then --yes."),process.exitCode=1;return}let a=await fn({message:`Upload ${s} entries?`,initialValue:!1});if(Me(a)||!a){un("Cancelled. Nothing uploaded.");return}if(i.flagged.length>0){let l=await Rr({message:"Send the held-back files anyway?",options:i.flagged.map(c=>({value:c.relative,label:c.relative})),required:!1,initialValues:[]});if(Me(l)){un("Cancelled. Nothing uploaded.");return}if(l.length>0){let c=new Set(l),h=f=>f.map(k=>c.has(k.relative)?{...k,hits:[]}:k),[m,b,v]=await Promise.all([o.skills&&$(o.skills)?U(o.skills):Promise.resolve([]),o.agents&&$(o.agents)?U(o.agents):Promise.resolve([]),o.memory&&$(o.memory)?U(o.memory):Promise.resolve([])]);i=Ee({skills:h(m),agents:h(b),memory:h(v)},{skillsRoot:o.skills})}}}if(s>0&&!e.dryRun){let a=me();a.start("Uploading");let l=await tn(p,i,(c,h,m)=>{a.message(`${c}/${h} ${m}`)});a.stop("Done"),d.success(`${l.skills} skills, ${l.skillFiles} skill files, ${l.agents} agents, ${l.memory} memory files`),l.failures.length>0?(d.error([`${l.failures.length} failed`,...l.failures.map(c=>` ${c.what} ${c.code}`)].join(`
65
+ `)),process.exitCode=1):await Re(p,"import",l.skills+l.skillFiles+l.agents+l.memory)}if(e.prune){let a={skills:!!(o.skills&&$(o.skills)),agents:!!(o.agents&&$(o.agents)),memory:!!(o.memory&&$(o.memory))},l=await rn(p,i,a);if(nn(l))d.info("Nothing to delete: the account holds nothing the source does not know.");else if(d.warn(["Would delete (only your own, nothing from a team, nothing archived)",...l.skills.map(c=>` Skill ${c}`),...l.agents.map(c=>` Agent ${c}`),...l.memory.map(c=>` Memory ${c}`)].join(`
66
+ `)),e.dryRun)d.info("Nothing deleted (--dry-run).");else if(!process.stdin.isTTY)d.error("No terminal for the prompt. Nothing deleted."),process.exitCode=1;else{let c=await fn({message:`Delete ${on(l)} entries in the account?`,initialValue:!1});if(Me(c)||!c)d.info("Nothing deleted.");else{let h=await sn(p,l);d.success(`${h.deleted} deleted`),h.failures.length>0&&(d.error([`${h.failures.length} failed`,...h.failures.map(m=>` ${m.what} ${m.code}`)].join(`
67
+ `)),process.exitCode=1)}}}G(e.dryRun?"Nothing changed (--dry-run).":"Then: npx onlymcp sync on every device.")});_.command("export").description("Get everything out of the account: memory, skills, agents, connections").option("--out <dir>","Unpack the archive into this folder").option("--zip <file>","Keep the archive as a file").option("--scope <scope>","personal, all or team:<id>","all").option("--types <list>","memory,skills,agents,connections").action(async e=>{if((e.out?1:0)+(e.zip?1:0)!=1){d.error("Either --out <folder> or --zip <file>, not both and not neither."),process.exitCode=1;return}Ue("OnlyMCP Export");let t=new T(I,()=>A.read()),n=me();n.start("Fetching the archive");let o;try{o=await t.exportArchive({scope:e.scope,types:e.types?.split(",").map(s=>s.trim()).filter(s=>s!=="")})}catch(s){n.stop("Aborted"),d.error(s instanceof Error?s.message:String(s)),d.info("Not logged in? First: npx onlymcp login"),process.exitCode=1;return}if(e.zip){Fr(Wr(e.zip),{recursive:!0}),await Gr(Hr.fromWeb(o.body),zr(e.zip)),n.stop("Done"),G(`Archive: ${e.zip}`);return}let r=new Uint8Array(await o.arrayBuffer()),i=await mn(r,e.out);n.stop("Done"),d.success(`${i.written} files in ${e.out}`),i.refused.length>0&&d.warn(["Not written, path in the archive is not allowed",...i.refused.map(s=>` ${s}`)].join(`
68
+ `)),i.failed.length>0&&(d.warn(["Not written, the filename doesn't work on this system",...i.failed.map(s=>` ${s.path} ${s.reason}`),"With --zip <file> the archive stays unchanged."].join(`
69
+ `)),process.exitCode=1),G(`Restore: npx onlymcp import --skills ${e.out}/skills --agents ${e.out}/agents --memory ${e.out}/memory`)});_.command("mcp").description("Run the stdio MCP server, the process an MCP client starts").action(Yt);await _.parseAsync();