onlymcp 0.9.1 → 0.11.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 (76) hide show
  1. package/README.md +14 -11
  2. package/dist/apiClient.d.ts +129 -7
  3. package/dist/apiClient.js +98 -4
  4. package/dist/apiClient.js.map +1 -1
  5. package/dist/auth/deviceLogin.js +5 -1
  6. package/dist/auth/deviceLogin.js.map +1 -1
  7. package/dist/bin/cli.js +349 -131
  8. package/dist/bin/cli.js.map +1 -1
  9. package/dist/bin/mcp.js +16 -2
  10. package/dist/bin/mcp.js.map +1 -1
  11. package/dist/export/unpack.d.ts +33 -0
  12. package/dist/export/unpack.js +54 -0
  13. package/dist/export/unpack.js.map +1 -0
  14. package/dist/identity.d.ts +25 -0
  15. package/dist/identity.js +106 -0
  16. package/dist/identity.js.map +1 -0
  17. package/dist/import/collect.d.ts +14 -1
  18. package/dist/import/collect.js +25 -3
  19. package/dist/import/collect.js.map +1 -1
  20. package/dist/import/frontmatter.d.ts +31 -5
  21. package/dist/import/frontmatter.js +64 -22
  22. package/dist/import/frontmatter.js.map +1 -1
  23. package/dist/import/plan.d.ts +22 -1
  24. package/dist/import/plan.js +66 -19
  25. package/dist/import/plan.js.map +1 -1
  26. package/dist/import/prune.d.ts +46 -0
  27. package/dist/import/prune.js +61 -0
  28. package/dist/import/prune.js.map +1 -0
  29. package/dist/import/secrets.js +1 -1
  30. package/dist/import/secrets.js.map +1 -1
  31. package/dist/import/upload.js +17 -2
  32. package/dist/import/upload.js.map +1 -1
  33. package/dist/install.d.ts +67 -6
  34. package/dist/install.js +137 -59
  35. package/dist/install.js.map +1 -1
  36. package/dist/instructions.d.ts +3 -1
  37. package/dist/instructions.js +19 -2
  38. package/dist/instructions.js.map +1 -1
  39. package/dist/memorySync.d.ts +28 -0
  40. package/dist/memorySync.js +56 -0
  41. package/dist/memorySync.js.map +1 -0
  42. package/dist/paths.d.ts +12 -0
  43. package/dist/paths.js +34 -8
  44. package/dist/paths.js.map +1 -1
  45. package/dist/registry/caveats.d.ts +17 -0
  46. package/dist/registry/caveats.js +50 -0
  47. package/dist/registry/caveats.js.map +1 -0
  48. package/dist/registry/clients.d.ts +11 -0
  49. package/dist/registry/clients.js +327 -0
  50. package/dist/registry/clients.js.map +1 -0
  51. package/dist/registry/index.d.ts +4 -0
  52. package/dist/registry/index.js +5 -0
  53. package/dist/registry/index.js.map +1 -0
  54. package/dist/registry/servers.d.ts +50 -0
  55. package/dist/registry/servers.js +225 -0
  56. package/dist/registry/servers.js.map +1 -0
  57. package/dist/registry/types.d.ts +54 -0
  58. package/dist/registry/types.js +6 -0
  59. package/dist/registry/types.js.map +1 -0
  60. package/dist/stubs.d.ts +1 -0
  61. package/dist/stubs.js +85 -13
  62. package/dist/stubs.js.map +1 -1
  63. package/dist/tools/getAgentDefinition.js +5 -2
  64. package/dist/tools/getAgentDefinition.js.map +1 -1
  65. package/dist/tools/getSkill.js +1 -1
  66. package/dist/tools/getSkill.js.map +1 -1
  67. package/dist/tools/getSkillFile.js +3 -3
  68. package/dist/tools/getSkillFile.js.map +1 -1
  69. package/dist/tools/memoryGraph.d.ts +69 -0
  70. package/dist/tools/memoryGraph.js +128 -0
  71. package/dist/tools/memoryGraph.js.map +1 -0
  72. package/dist/tools/memoryRead.js +1 -1
  73. package/dist/tools/memoryRead.js.map +1 -1
  74. package/dist/tools/memoryWrite.js +1 -1
  75. package/dist/tools/memoryWrite.js.map +1 -1
  76. package/package.json +5 -4
package/dist/install.js CHANGED
@@ -1,30 +1,27 @@
1
- import { join } from 'node:path';
2
- /** Das npm-Paket, das sowohl `onlymcp` als auch `onlymcp-mcp` mitbringt. */
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { CLIENTS } from './registry/index.js';
4
+ /** The npm package that ships both `onlymcp` and `onlymcp-mcp`. */
3
5
  export const PACKAGE = 'onlymcp';
4
- // Gestartet wird ueber npx und nicht als blosser Binaername `onlymcp-mcp`.
5
- // Die Startseite bewirbt `npx onlymcp install`, und npx fuehrt ein Paket
6
- // einmal aus, ohne irgendetwas auf dem PATH zu hinterlassen. Ein Eintrag mit
7
- // nacktem `onlymcp-mcp` waere also erfolgreich geschrieben und beim naechsten
8
- // Start des Clients trotzdem tot - der Fehler faellt genau dann auf, wenn der
9
- // Nutzer laengst weitergezogen ist. `-p onlymcp` ist dabei Pflicht, weil der
10
- // Binaername vom Paketnamen abweicht; ohne das sucht npx ein Paket namens
11
- // `onlymcp-mcp`, das es nicht gibt.
6
+ // Started through npx, never as a bare `onlymcp-mcp` binary name: the home
7
+ // page advertises `npx onlymcp install`, and npx runs a package once without
8
+ // leaving anything on the PATH. A bare `onlymcp-mcp` entry would be written
9
+ // successfully and only fail the next time the client starts, which is the
10
+ // worst moment to find out. `-p onlymcp` is required because the binary
11
+ // name differs from the package name; without it npx looks for a package
12
+ // literally called `onlymcp-mcp`, which does not exist.
12
13
  //
13
- // Bewusst ohne feste Version: der Server spricht mit einer Cloud-API, die sich
14
- // weiterbewegt, und darf nicht auf einem Stand einfrieren, den danach niemand
15
- // mehr anfasst.
14
+ // Deliberately unpinned: the server talks to a cloud API that keeps moving,
15
+ // and must not freeze on a version nobody touches again afterwards.
16
16
  const COMMAND = 'npx';
17
17
  const ARGS = ['-y', '-p', PACKAGE, 'onlymcp-mcp'];
18
- // TOML-Basic-Strings deuten den Rueckwaertsschraegstrich als Escape. Ein
19
- // Windows-Heimatpfad wuerde ohne diese Behandlung eine kaputte Datei
20
- // erzeugen, ein Anfuehrungszeichen im Pfad ebenso.
18
+ // TOML basic strings treat a backslash as an escape. A Windows home path
19
+ // would produce a broken file without this, and so would a quote in the path.
21
20
  function tomlString(value) {
22
21
  return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
23
22
  }
24
23
  function jsonSnippet(env) {
25
- const server = env
26
- ? { command: COMMAND, args: ARGS, env }
27
- : { command: COMMAND, args: ARGS };
24
+ const server = env ? { command: COMMAND, args: ARGS, env } : { command: COMMAND, args: ARGS };
28
25
  return JSON.stringify({ mcpServers: { onlymcp: server } }, null, 2);
29
26
  }
30
27
  function tomlSnippet(env) {
@@ -33,50 +30,131 @@ function tomlSnippet(env) {
33
30
  `command = ${tomlString(COMMAND)}`,
34
31
  `args = [${ARGS.map(tomlString).join(', ')}]`,
35
32
  ];
36
- // Inline-Tabelle statt eigenem [mcp_servers.onlymcp.env]-Abschnitt: der
37
- // Schnipsel wird an eine bestehende Datei angehaengt, und ein zweiter
38
- // Abschnitt wuerde jede spaeter angehaengte Tabelle in ihn hineinziehen.
33
+ // An inline table instead of a separate [mcp_servers.onlymcp.env] section:
34
+ // the snippet gets appended to an existing file, and a second section
35
+ // would pull in whatever gets appended after it.
39
36
  for (const [k, v] of Object.entries(env ?? {})) {
40
37
  lines.push(`env = { ${k} = ${tomlString(v)} }`);
41
38
  }
42
39
  return `${lines.join('\n')}\n`;
43
40
  }
44
- // `home` ist ein Parameter und nicht homedir(), damit jeder Test gegen einen
45
- // Wegwerf-Ordner laeuft. Die Leitplanke aus Spec Abschnitt 13 ist damit im
46
- // Typ verankert und nicht bloss eine Absicht.
47
- export function targets(home) {
48
- // Geprueft an Codex 0.147.0: ~/.codex/skills wird direkt gescannt.
49
- const codexStubs = join(home, '.codex', 'skills');
50
- return [
51
- {
52
- id: 'claude',
53
- label: 'Claude Code',
54
- configPath: join(home, '.claude.json'),
55
- // Die Stubs setzt bei Claude Code die Plugin-Installation ueber
56
- // CLAUDE_PLUGIN_ROOT, nicht dieser Befehl.
57
- stubDir: null,
58
- snippet: jsonSnippet(),
59
- },
60
- {
61
- id: 'codex',
62
- label: 'Codex',
63
- configPath: join(home, '.codex', 'config.toml'),
64
- stubDir: codexStubs,
65
- // Ohne ONLYMCP_STUB_DIRS im Eintrag selbst faellt stubDirs() auf
66
- // ~/.claude/onlymcp/plugin zurueck: die Stubs landen dann an einer
67
- // Stelle, die Codex nie liest, und ~/.codex/skills bleibt leer. Die
68
- // Variable gehoert deshalb in die Konfiguration und nicht bloss als
69
- // Hinweis in die Ausgabe.
70
- snippet: tomlSnippet({ ONLYMCP_STUB_DIRS: codexStubs }),
71
- },
72
- {
73
- id: 'gemini',
74
- label: 'Gemini CLI',
75
- configPath: join(home, '.gemini', 'settings.json'),
76
- // Kein eigener Skill-Begriff: laeuft ueber instructions und list_skills.
77
- stubDir: null,
78
- snippet: jsonSnippet(),
79
- },
80
- ];
41
+ /**
42
+ * Old CLI spellings, kept so instructions written before the registry
43
+ * existed keep working. `codex` needs no alias: it was always spelled the
44
+ * same as the registry's client id.
45
+ */
46
+ const ALIASES = { claude: 'claude-code', gemini: 'gemini-cli' };
47
+ function installableClients() {
48
+ return CLIENTS.filter((c) => c.install !== null);
49
+ }
50
+ const INSTALLABLE_IDS = new Set(installableClients().map((c) => c.id));
51
+ /** Resolves an alias or a registry id to a registry id; undefined if neither. */
52
+ export function resolveClientId(id) {
53
+ const aliased = ALIASES[id];
54
+ if (aliased)
55
+ return aliased;
56
+ return INSTALLABLE_IDS.has(id) ? id : undefined;
57
+ }
58
+ function resolveHome(path, home) {
59
+ return path.startsWith('~/') ? join(home, path.slice(2)) : path;
60
+ }
61
+ /**
62
+ * Every client the registry can write a config for, resolved against `home`.
63
+ *
64
+ * `home` is a parameter and not `homedir()` so every test runs against a
65
+ * throwaway directory: the guardrail from spec §13 sits in the type itself,
66
+ * not merely in intent.
67
+ */
68
+ export function installTargets(home) {
69
+ return installableClients().map((c) => {
70
+ const configPath = resolveHome(c.install.configPath, home);
71
+ const stubDir = c.install.stubDir ? resolveHome(c.install.stubDir, home) : null;
72
+ // `stubDir` is always `<root>/skills` (see paths.ts, `stubDirs()`): the
73
+ // env var the server reads carries the root, because `writeStubs`
74
+ // appends `skills/` itself. Pointing it at the skills dir directly wrote
75
+ // into `<root>/skills/skills`.
76
+ const stubRoot = stubDir ? dirname(stubDir) : null;
77
+ const snippet = c.install.format === 'toml'
78
+ ? tomlSnippet(stubRoot ? { ONLYMCP_STUB_DIRS: stubRoot } : undefined)
79
+ : jsonSnippet();
80
+ return { id: c.id, name: c.name, configPath, format: c.install.format, stubDir, stubRoot, snippet };
81
+ });
82
+ }
83
+ /**
84
+ * Merges one MCP server entry into an existing JSON config, without
85
+ * touching anything else already in the file.
86
+ *
87
+ * `writeFileSync` alone would replace the whole file (e.g. `~/.claude.json`
88
+ * or `~/.gemini/settings.json`), throwing away every other setting stored
89
+ * there. An existing but broken file is never silently overwritten either:
90
+ * better an error than quiet data loss.
91
+ */
92
+ export function mergeJsonServer(path, key, value) {
93
+ let existing = {};
94
+ if (existsSync(path)) {
95
+ const raw = readFileSync(path, 'utf8').trim();
96
+ if (raw) {
97
+ try {
98
+ existing = JSON.parse(raw);
99
+ }
100
+ catch {
101
+ throw new Error(`${path} contains invalid JSON. Nothing written.`);
102
+ }
103
+ }
104
+ }
105
+ const mcpServers = existing.mcpServers ?? {};
106
+ existing.mcpServers = { ...mcpServers, [key]: value };
107
+ writeFileSync(path, `${JSON.stringify(existing, null, 2)}\n`, 'utf8');
108
+ }
109
+ /**
110
+ * Appends the TOML snippet, unless it is already there.
111
+ *
112
+ * Appended, not replaced: a second run would otherwise add a second
113
+ * `[mcp_servers.onlymcp]` table, and two identical tables make the file
114
+ * invalid for most TOML parsers.
115
+ */
116
+ export function appendTomlServer(path, snippet) {
117
+ const already = existsSync(path) && readFileSync(path, 'utf8').includes('[mcp_servers.onlymcp]');
118
+ if (already)
119
+ return 'already-present';
120
+ appendFileSync(path, `\n${snippet}`);
121
+ return 'written';
122
+ }
123
+ /** Pure given the filesystem: same inputs, same decision, every time. */
124
+ export function planInstall(home, opts) {
125
+ const targets = installTargets(home);
126
+ if (opts.all) {
127
+ if (opts.client)
128
+ return { kind: 'conflicting-options' };
129
+ const present = targets.filter((t) => existsSync(t.configPath));
130
+ const skipped = targets.filter((t) => !existsSync(t.configPath));
131
+ if (present.length === 0)
132
+ return { kind: 'none-present-for-all', targets };
133
+ return { kind: 'all', present, skipped };
134
+ }
135
+ if (!opts.client)
136
+ return { kind: 'missing-client', targets };
137
+ const id = resolveClientId(opts.client);
138
+ const target = id ? targets.find((x) => x.id === id) : undefined;
139
+ if (!target)
140
+ return { kind: 'unknown-client', targets };
141
+ return { kind: 'single', target };
142
+ }
143
+ /** Writes one target's snippet into its config file, creating directories as needed. */
144
+ export function writeInstallTarget(target) {
145
+ mkdirSync(dirname(target.configPath), { recursive: true });
146
+ const result = target.format === 'toml'
147
+ ? appendTomlServer(target.configPath, target.snippet)
148
+ : (() => {
149
+ const parsed = JSON.parse(target.snippet);
150
+ const entry = Object.entries(parsed.mcpServers)[0];
151
+ if (!entry)
152
+ throw new Error(`${target.name}: empty mcpServers snippet.`);
153
+ mergeJsonServer(target.configPath, entry[0], entry[1]);
154
+ return 'written';
155
+ })();
156
+ if (target.stubDir)
157
+ mkdirSync(target.stubDir, { recursive: true });
158
+ return result;
81
159
  }
82
160
  //# sourceMappingURL=install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,4 @@
1
1
  export declare const INSTRUCTIONS_BUDGET = 1800;
2
- export declare function buildInstructions(skills: string[], agents: string[]): string;
2
+ export declare function buildInstructions(skills: string[], agents: string[], opts?: {
3
+ brainvault?: boolean;
4
+ }): string;
@@ -16,6 +16,20 @@ const PREAMBLE = [
16
16
  'skill_write, agent_write and the matching *_delete tools; connection_create',
17
17
  'adds another MCP server to this account.',
18
18
  ].join(' ');
19
+ /**
20
+ * Der BrainVault-Ablauf in drei Saetzen (Spec 7), wortgleich mit dem der
21
+ * gehosteten Oberflaeche (packages/api/src/mcp/instructions.ts). Zwei
22
+ * Fassungen desselben Satzes waeren zwei Gewohnheiten.
23
+ *
24
+ * Je Konto abschaltbar: wer OnlyMCP als reinen Skill-Speicher benutzt, will
25
+ * keinen Ablauf und will die Zeichen fuer Namen zurueck.
26
+ */
27
+ const BRAINVAULT = [
28
+ 'Memory is a vault, not a scratch file: read MEMORY.md first, it is the index.',
29
+ 'After finishing a task, write one solutions/sol-*.md for what was not obvious',
30
+ 'and one entry in history/YYYY-MM-DD.md. Link related files with [[name]];',
31
+ "memory_links(path) says which of a file's links point nowhere yet.",
32
+ ].join(' ');
19
33
  function section(label, tool, names, used) {
20
34
  if (names.length === 0)
21
35
  return '';
@@ -34,8 +48,11 @@ function section(label, tool, names, used) {
34
48
  const tail = rest > 0 ? ` (+${rest} more, call ${tool})` : '';
35
49
  return `\n\n${label}: ${kept.join(', ')}${tail}`;
36
50
  }
37
- export function buildInstructions(skills, agents) {
38
- let out = PREAMBLE;
51
+ export function buildInstructions(skills, agents, opts = {}) {
52
+ // Vor den Namenslisten und nach der Anleitung: abgeschnitten wird der
53
+ // Schluss, also steht vorn, was ueberleben muss - und ein halb angekommener
54
+ // Ablauf ist schlimmer als gar keiner.
55
+ let out = opts.brainvault === false ? PREAMBLE : `${PREAMBLE} ${BRAINVAULT}`;
39
56
  // Platz fuer die Agents-Zeile zuruecklegen, BEVOR die Skills ihn aufbrauchen.
40
57
  // Ohne diese Reserve frisst eine lange Skill-Liste das ganze Budget, die
41
58
  // Agents-Zeile wird trotzdem angehaengt, und das Feld ueberschreitet 1800.
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,sEAAsE;AACtE,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,QAAQ,GAAG;IACf,6EAA6E;IAC7E,qEAAqE;IACrE,0DAA0D;IAC1D,8EAA8E;IAC9E,gEAAgE;IAChE,wEAAwE;IACxE,6EAA6E;IAC7E,0CAA0C;CAC3C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY,EAAE,KAAe,EAAE,IAAY;IACzE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;QAClE,IAAI,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,mBAAmB;YAAE,MAAM;QACpD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,KAAK,UAAU,IAAI,EAAE,CAAC;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,OAAO,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAgB,EAAE,MAAgB;IAClE,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,8EAA8E;IAC9E,yEAAyE;IACzE,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;IACtE,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,sEAAsE;AACtE,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,QAAQ,GAAG;IACf,6EAA6E;IAC7E,qEAAqE;IACrE,0DAA0D;IAC1D,8EAA8E;IAC9E,gEAAgE;IAChE,wEAAwE;IACxE,6EAA6E;IAC7E,0CAA0C;CAC3C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG;IACjB,+EAA+E;IAC/E,+EAA+E;IAC/E,2EAA2E;IAC3E,oEAAoE;CACrE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY,EAAE,KAAe,EAAE,IAAY;IACzE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;QAClE,IAAI,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,mBAAmB;YAAE,MAAM;QACpD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,KAAK,UAAU,IAAI,EAAE,CAAC;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,OAAO,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAgB,EAChB,MAAgB,EAChB,OAAiC,EAAE;IAEnC,sEAAsE;IACtE,4EAA4E;IAC5E,uCAAuC;IACvC,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,UAAU,EAAE,CAAC;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;IACtE,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { ApiClient } from './apiClient.js';
2
+ export type MemorySyncResult = {
3
+ written: number;
4
+ /** Paths the server sent that this command refused to turn into a file. */
5
+ refused: string[];
6
+ /** A team file whose path a personal file already took. */
7
+ collisions: string[];
8
+ failures: {
9
+ what: string;
10
+ code: string;
11
+ }[];
12
+ };
13
+ /**
14
+ * Write every memory file this account can read into one directory, folder
15
+ * structure included (package 03 task 12, spec 8.5).
16
+ *
17
+ * Two independent checks stand between a path from the server and a write on
18
+ * the disk, and they are not redundant: `isSafeMemoryPath` is the rule the
19
+ * server enforces on the way in, and the prefix check below is the one that
20
+ * holds even if that rule is ever loosened, if a path arrives from a wrongly
21
+ * configured ONLYMCP_API_URL, or if `resolve` and the regexp disagree about
22
+ * some encoding. Nothing outside `dir` is ever written.
23
+ *
24
+ * Archived rows are included on purpose. The archive is exactly the state in
25
+ * which somebody wants their notes back on their own disk, and spec 8a calls
26
+ * leaving them out a shortcoming, not a feature.
27
+ */
28
+ export declare function syncMemoryToDisk(api: ApiClient, dir: string): Promise<MemorySyncResult>;
@@ -0,0 +1,56 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve, sep } from 'node:path';
3
+ import { isSafeMemoryPath } from './memoryPath.js';
4
+ /**
5
+ * Write every memory file this account can read into one directory, folder
6
+ * structure included (package 03 task 12, spec 8.5).
7
+ *
8
+ * Two independent checks stand between a path from the server and a write on
9
+ * the disk, and they are not redundant: `isSafeMemoryPath` is the rule the
10
+ * server enforces on the way in, and the prefix check below is the one that
11
+ * holds even if that rule is ever loosened, if a path arrives from a wrongly
12
+ * configured ONLYMCP_API_URL, or if `resolve` and the regexp disagree about
13
+ * some encoding. Nothing outside `dir` is ever written.
14
+ *
15
+ * Archived rows are included on purpose. The archive is exactly the state in
16
+ * which somebody wants their notes back on their own disk, and spec 8a calls
17
+ * leaving them out a shortcoming, not a feature.
18
+ */
19
+ export async function syncMemoryToDisk(api, dir) {
20
+ const base = resolve(dir);
21
+ await mkdir(base, { recursive: true });
22
+ const result = { written: 0, refused: [], collisions: [], failures: [] };
23
+ const { files } = await api.listMemory();
24
+ // Personal first, so a path claimed by both scopes belongs to the file this
25
+ // account owns. The team file is then reported rather than quietly dropped;
26
+ // a directory per scope is the export's job (task 12a), not this one's.
27
+ const ordered = [...files].sort((a, b) => a.scope === b.scope ? a.path.localeCompare(b.path) : a.scope === 'personal' ? -1 : 1);
28
+ const taken = new Set();
29
+ for (const f of ordered) {
30
+ if (!isSafeMemoryPath(f.path)) {
31
+ result.refused.push(f.path);
32
+ continue;
33
+ }
34
+ const dest = resolve(base, ...f.path.split('/'));
35
+ if (!dest.startsWith(base + sep)) {
36
+ result.refused.push(f.path);
37
+ continue;
38
+ }
39
+ if (taken.has(dest)) {
40
+ result.collisions.push(f.path);
41
+ continue;
42
+ }
43
+ try {
44
+ const file = await api.readMemory(f.path);
45
+ await mkdir(dirname(dest), { recursive: true });
46
+ await writeFile(dest, file.content, 'utf8');
47
+ taken.add(dest);
48
+ result.written += 1;
49
+ }
50
+ catch (e) {
51
+ result.failures.push({ what: f.path, code: e instanceof Error ? e.message : String(e) });
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+ //# sourceMappingURL=memorySync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorySync.js","sourceRoot":"","sources":["../src/memorySync.ts"],"names":[],"mappings":"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"}
package/dist/paths.d.ts CHANGED
@@ -1,2 +1,14 @@
1
1
  export declare function cacheDir(): string;
2
+ /**
3
+ * Die Ordner, in die Stubs geschrieben werden.
4
+ *
5
+ * Zurueck kommt jeweils die WURZEL eines Clients, nicht sein `skills`-Ordner:
6
+ * writeStubs legt darunter `skills/` und `agents/` an, weil beide Scanner
7
+ * nebeneinander stehen. Bis Aufgabe 12 gab ONLYMCP_STUB_DIRS `~/.codex/skills`
8
+ * zurueck und die Stubs landeten in `~/.codex/skills/skills` - also an einer
9
+ * Stelle, die Codex nie liest.
10
+ *
11
+ * Mehrzahl, weil mehrere Clients gleichzeitig auf demselben Geraet laufen
12
+ * koennen und jeder einen anderen Ordner scannt.
13
+ */
2
14
  export declare function stubDirs(): string[];
package/dist/paths.js CHANGED
@@ -1,28 +1,54 @@
1
+ import { existsSync } from 'node:fs';
1
2
  import { homedir } from 'node:os';
2
3
  import { join } from 'node:path';
4
+ // ONLYMCP_HOME wird an zwei Stellen unterschiedlich gelesen, und das ist
5
+ // Absicht: hier unten als Ersatz fuer ~/.claude/onlymcp (der eigene Ordner
6
+ // dieses Werkzeugs), in clientHome() als Ersatz fuer das Heimatverzeichnis
7
+ // (so wie install.ts es liest). Ein Test setzt die Variable auf einen
8
+ // Wegwerf-Ordner und trifft damit beides, ohne je ein echtes ~/.claude
9
+ // anzufassen.
3
10
  function base() {
4
11
  return process.env.ONLYMCP_HOME ?? join(homedir(), '.claude', 'onlymcp');
5
12
  }
13
+ function clientHome() {
14
+ return process.env.ONLYMCP_HOME ?? homedir();
15
+ }
6
16
  export function cacheDir() {
7
17
  return join(base(), 'cache');
8
18
  }
19
+ /**
20
+ * Die Ordner, in die Stubs geschrieben werden.
21
+ *
22
+ * Zurueck kommt jeweils die WURZEL eines Clients, nicht sein `skills`-Ordner:
23
+ * writeStubs legt darunter `skills/` und `agents/` an, weil beide Scanner
24
+ * nebeneinander stehen. Bis Aufgabe 12 gab ONLYMCP_STUB_DIRS `~/.codex/skills`
25
+ * zurueck und die Stubs landeten in `~/.codex/skills/skills` - also an einer
26
+ * Stelle, die Codex nie liest.
27
+ *
28
+ * Mehrzahl, weil mehrere Clients gleichzeitig auf demselben Geraet laufen
29
+ * koennen und jeder einen anderen Ordner scannt.
30
+ */
9
31
  export function stubDirs() {
10
- // Mehrzahl, weil mehrere Clients gleichzeitig auf demselben Geraet laufen
11
- // koennen und jeder einen anderen Ordner scannt.
12
32
  const dirs = [];
13
33
  // Claude Code setzt CLAUDE_PLUGIN_ROOT, wenn es den MCP-Server aus dem
14
- // Plugin heraus startet. Nur dort liegen Stubs an einer Stelle, die der
15
- // Skill- und Agent-Scanner ueberhaupt liest.
34
+ // Plugin heraus startet. Dort liegen Stubs an einer Stelle, die der Skill-
35
+ // und Agent-Scanner ohnehin liest.
16
36
  if (process.env.CLAUDE_PLUGIN_ROOT)
17
37
  dirs.push(process.env.CLAUDE_PLUGIN_ROOT);
18
- // Alle uebrigen Clients mit eigenem Scanner, gesetzt von `onlymcp install`
19
- // (heute Codex mit ~/.codex/skills). Mehrere durch Doppelpunkt getrennt wie
20
- // PATH: ein Client mehr ist ein Eintrag mehr, keine Codeaenderung.
38
+ // Alle uebrigen Clients mit eigenem Scanner, gesetzt von `onlymcp install`.
39
+ // Mehrere durch Doppelpunkt getrennt wie PATH: ein Client mehr ist ein
40
+ // Eintrag mehr, keine Codeaenderung.
21
41
  for (const d of (process.env.ONLYMCP_STUB_DIRS ?? '').split(':')) {
22
42
  if (d)
23
43
  dirs.push(d);
24
44
  }
25
- // Rueckfall fuer CLI und Tests ohne jeden Client-Kontext.
45
+ // Was ohne jede Konfiguration da ist. Geprueft, nicht angelegt: wer kein
46
+ // Codex installiert hat, soll durch `onlymcp sync` kein ~/.codex bekommen.
47
+ for (const d of [join(clientHome(), '.claude'), join(clientHome(), '.codex')]) {
48
+ if (existsSync(d) && !dirs.includes(d))
49
+ dirs.push(d);
50
+ }
51
+ // Letzter Rueckfall fuer CLI und Tests ohne jeden Client-Kontext.
26
52
  return dirs.length > 0 ? dirs : [join(base(), 'plugin')];
27
53
  }
28
54
  //# sourceMappingURL=paths.js.map
package/dist/paths.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,IAAI;IACX,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,0EAA0E;IAC1E,iDAAiD;IACjD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,uEAAuE;IACvE,wEAAwE;IACxE,6CAA6C;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,mEAAmE;IACnE,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,0DAA0D;IAC1D,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,yEAAyE;AACzE,2EAA2E;AAC3E,2EAA2E;AAC3E,sEAAsE;AACtE,uEAAuE;AACvE,cAAc;AACd,SAAS,IAAI;IACX,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,uEAAuE;IACvE,2EAA2E;IAC3E,mCAAmC;IACnC,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,uEAAuE;IACvE,qCAAqC;IACrC,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,yEAAyE;IACzE,2EAA2E;IAC3E,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC9E,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,kEAAkE;IAClE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The caveat sentences, in the four languages the product carries.
3
+ *
4
+ * A caveat is a client fact like the command next to it, and two places state
5
+ * it: the Connect view of the dashboard and the generated client sections of
6
+ * the public documentation (packages/api/src/docs/generated.ts). Each kept its
7
+ * own copy until packages 07 and 09 met, and 17 of the 24 sentences already
8
+ * differed. `ClientRecord.caveats` names an entry by its key.
9
+ */
10
+ export type CaveatLanguage = 'en' | 'de' | 'fr' | 'it';
11
+ export declare const CAVEATS: Record<string, Record<CaveatLanguage, string>>;
12
+ /**
13
+ * The sentence for one key. An unknown language reads English, and an unknown
14
+ * key comes back as it is, so a typo shows on the first read instead of
15
+ * vanishing.
16
+ */
17
+ export declare function caveatText(key: string, language: string): string;
@@ -0,0 +1,50 @@
1
+ export const CAVEATS = {
2
+ 'clients.caveat.chatgptDeveloperMode': {
3
+ en: 'Custom MCP connectors live in ChatGPT’s developer mode on the web, and need a paid plan.',
4
+ de: 'Eigene MCP-Connectors gibt es in ChatGPT nur im Entwicklermodus im Web, und sie brauchen ein bezahltes Abo.',
5
+ fr: 'Les connecteurs MCP personnalisés se trouvent dans le mode développeur de ChatGPT sur le web et demandent un abonnement payant.',
6
+ it: 'I connettori MCP personalizzati si trovano nella modalità sviluppatore di ChatGPT sul web e richiedono un piano a pagamento.',
7
+ },
8
+ 'clients.caveat.geminiAppSpark': {
9
+ en: 'Custom connectors are limited to Gemini Spark and are not available in every country.',
10
+ de: 'Eigene Connectors gibt es nur für Gemini Spark, und nicht in jedem Land.',
11
+ fr: 'Les connecteurs personnalisés sont réservés à Gemini Spark et ne sont pas disponibles dans tous les pays.',
12
+ it: 'I connettori personalizzati sono riservati a Gemini Spark e non sono disponibili in tutti i paesi.',
13
+ },
14
+ 'clients.caveat.claudeHeaderBeta': {
15
+ en: 'Claude connects through OAuth. Static headers exist only in a limited beta.',
16
+ de: 'Claude verbindet sich über OAuth. Feste Header gibt es nur in einer beschränkten Beta.',
17
+ fr: 'Claude se connecte via OAuth. Les en-têtes fixes n’existent que dans une bêta limitée.',
18
+ it: 'Claude si connette tramite OAuth. Gli header statici esistono solo in una beta limitata.',
19
+ },
20
+ 'clients.caveat.copilotPolicy': {
21
+ en: 'Business and Enterprise tenants must enable the MCP policy first.',
22
+ de: 'In Business- und Enterprise-Organisationen muss zuerst die MCP-Richtlinie eingeschaltet werden.',
23
+ fr: 'Les organisations Business et Enterprise doivent d’abord activer la stratégie MCP.',
24
+ it: 'Nelle organizzazioni Business ed Enterprise va prima attivata la policy MCP.',
25
+ },
26
+ 'clients.caveat.windsurfAllowlist': {
27
+ en: 'An admin allowlist can block servers that are not listed.',
28
+ de: 'Eine Freigabeliste der Administration kann Server sperren, die nicht darauf stehen.',
29
+ fr: 'Une liste d’autorisation définie par un administrateur peut bloquer les serveurs qui n’y figurent pas.',
30
+ it: 'Una lista di autorizzazione dell’amministratore può bloccare i server che non vi compaiono.',
31
+ },
32
+ 'clients.caveat.codexTokenEnv': {
33
+ en: 'Codex reads ONLYMCP_TOKEN from the environment every time it starts. Put the export line in your shell profile, not just the current terminal session.',
34
+ de: 'Codex liest ONLYMCP_TOKEN bei jedem Start aus der Umgebung. Schreib die export-Zeile in dein Shell-Profil, nicht nur in die aktuelle Terminal-Sitzung.',
35
+ fr: 'Codex lit ONLYMCP_TOKEN dans l’environnement à chaque démarrage. Mets la ligne export dans le profil de ton shell, pas seulement dans la session de terminal en cours.',
36
+ it: 'Codex legge ONLYMCP_TOKEN dall’ambiente a ogni avvio. Metti la riga export nel profilo della tua shell, non solo nella sessione di terminale attuale.',
37
+ },
38
+ };
39
+ /**
40
+ * The sentence for one key. An unknown language reads English, and an unknown
41
+ * key comes back as it is, so a typo shows on the first read instead of
42
+ * vanishing.
43
+ */
44
+ export function caveatText(key, language) {
45
+ const entry = CAVEATS[key];
46
+ if (!entry)
47
+ return key;
48
+ return entry[language.slice(0, 2)] ?? entry.en ?? key;
49
+ }
50
+ //# sourceMappingURL=caveats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caveats.js","sourceRoot":"","sources":["../../src/registry/caveats.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,OAAO,GAAmD;IACrE,qCAAqC,EAAE;QACrC,EAAE,EAAE,0FAA0F;QAC9F,EAAE,EAAE,6GAA6G;QACjH,EAAE,EAAE,iIAAiI;QACrI,EAAE,EAAE,8HAA8H;KACnI;IACD,+BAA+B,EAAE;QAC/B,EAAE,EAAE,uFAAuF;QAC3F,EAAE,EAAE,0EAA0E;QAC9E,EAAE,EAAE,2GAA2G;QAC/G,EAAE,EAAE,oGAAoG;KACzG;IACD,iCAAiC,EAAE;QACjC,EAAE,EAAE,6EAA6E;QACjF,EAAE,EAAE,wFAAwF;QAC5F,EAAE,EAAE,wFAAwF;QAC5F,EAAE,EAAE,0FAA0F;KAC/F;IACD,8BAA8B,EAAE;QAC9B,EAAE,EAAE,mEAAmE;QACvE,EAAE,EAAE,iGAAiG;QACrG,EAAE,EAAE,oFAAoF;QACxF,EAAE,EAAE,8EAA8E;KACnF;IACD,kCAAkC,EAAE;QAClC,EAAE,EAAE,2DAA2D;QAC/D,EAAE,EAAE,qFAAqF;QACzF,EAAE,EAAE,wGAAwG;QAC5G,EAAE,EAAE,6FAA6F;KAClG;IACD,8BAA8B,EAAE;QAC9B,EAAE,EAAE,wJAAwJ;QAC5J,EAAE,EAAE,wJAAwJ;QAC5J,EAAE,EAAE,wKAAwK;QAC5K,EAAE,EAAE,uJAAuJ;KAC5J;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,QAAgB;IACtD,MAAM,KAAK,GAAuC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC;IACvB,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,IAAI,GAAG,CAAC;AACxD,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ClientId, ClientRecord, RemoteContext, Snippet } from './types.js';
2
+ export declare const CLIENTS: readonly ClientRecord[];
3
+ export declare function clientById(id: ClientId): ClientRecord | undefined;
4
+ export declare function remoteSnippets(id: ClientId, ctx: RemoteContext): Snippet[] | null;
5
+ /**
6
+ * The local-install command for a client `npx onlymcp install` can set up,
7
+ * or null for every other one. Spec 07 §3.4: always with `npx`, always with
8
+ * `--client`, because the CLI without `--client` only lists the targets and
9
+ * exits 2.
10
+ */
11
+ export declare function installCommand(id: ClientId): string | null;