onlymcp 0.1.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/apiClient.d.ts +55 -0
  4. package/dist/apiClient.js +59 -0
  5. package/dist/apiClient.js.map +1 -0
  6. package/dist/auth/deviceLogin.d.ts +5 -0
  7. package/dist/auth/deviceLogin.js +36 -0
  8. package/dist/auth/deviceLogin.js.map +1 -0
  9. package/dist/auth/keyring.d.ts +5 -0
  10. package/dist/auth/keyring.js +28 -0
  11. package/dist/auth/keyring.js.map +1 -0
  12. package/dist/bin/cli.d.ts +2 -0
  13. package/dist/bin/cli.js +173 -0
  14. package/dist/bin/cli.js.map +1 -0
  15. package/dist/bin/mcp.d.ts +2 -0
  16. package/dist/bin/mcp.js +67 -0
  17. package/dist/bin/mcp.js.map +1 -0
  18. package/dist/cache.d.ts +7 -0
  19. package/dist/cache.js +32 -0
  20. package/dist/cache.js.map +1 -0
  21. package/dist/install.d.ts +12 -0
  22. package/dist/install.js +82 -0
  23. package/dist/install.js.map +1 -0
  24. package/dist/instructions.d.ts +2 -0
  25. package/dist/instructions.js +46 -0
  26. package/dist/instructions.js.map +1 -0
  27. package/dist/memoryPath.d.ts +1 -0
  28. package/dist/memoryPath.js +13 -0
  29. package/dist/memoryPath.js.map +1 -0
  30. package/dist/paths.d.ts +2 -0
  31. package/dist/paths.js +28 -0
  32. package/dist/paths.js.map +1 -0
  33. package/dist/stubs.d.ts +5 -0
  34. package/dist/stubs.js +52 -0
  35. package/dist/stubs.js.map +1 -0
  36. package/dist/tools/getAgentDefinition.d.ts +26 -0
  37. package/dist/tools/getAgentDefinition.js +26 -0
  38. package/dist/tools/getAgentDefinition.js.map +1 -0
  39. package/dist/tools/getSkill.d.ts +26 -0
  40. package/dist/tools/getSkill.js +26 -0
  41. package/dist/tools/getSkill.js.map +1 -0
  42. package/dist/tools/lists.d.ts +38 -0
  43. package/dist/tools/lists.js +39 -0
  44. package/dist/tools/lists.js.map +1 -0
  45. package/dist/tools/memoryRead.d.ts +25 -0
  46. package/dist/tools/memoryRead.js +33 -0
  47. package/dist/tools/memoryRead.js.map +1 -0
  48. package/dist/tools/memoryWrite.d.ts +27 -0
  49. package/dist/tools/memoryWrite.js +32 -0
  50. package/dist/tools/memoryWrite.js.map +1 -0
  51. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Francesco Palazzo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # onlymcp
2
+
3
+ Your Claude Code skills, agents and memory, stored once in the cloud and available
4
+ in every MCP client you use. This package ships two binaries: the stdio MCP server
5
+ (`onlymcp-mcp`) and the CLI that sets up a client (`onlymcp`).
6
+
7
+ Requires a free account at [only-mcp.com](https://only-mcp.com) and Node.js 22 or newer.
8
+
9
+ ## Set up a client
10
+
11
+ ```bash
12
+ npx onlymcp install --client claude # or: codex, gemini
13
+ npx onlymcp login
14
+ ```
15
+
16
+ `install` writes the MCP server entry into the client's own configuration file and
17
+ merges into what is already there rather than replacing it:
18
+
19
+ | Client | File |
20
+ | --- | --- |
21
+ | Claude Code | `~/.claude.json` |
22
+ | Codex | `~/.codex/config.toml` |
23
+ | Gemini CLI | `~/.gemini/settings.json` |
24
+
25
+ Any other MCP client works too. `npx onlymcp install --print --client claude` prints
26
+ the JSON snippet to paste in by hand.
27
+
28
+ `login` connects the device over an OAuth device code (a code plus a QR in the
29
+ terminal) and stores the token in the operating system keyring, never in a file.
30
+
31
+ ## Commands
32
+
33
+ | Command | What it does |
34
+ | --- | --- |
35
+ | `onlymcp install --client <id>` | Add OnlyMCP to a client's config |
36
+ | `onlymcp install --print --client <id>` | Print the snippet, write nothing |
37
+ | `onlymcp login` | Connect this device |
38
+ | `onlymcp status` | Show whether this device is connected |
39
+ | `onlymcp sync` | Rewrite the local stub files |
40
+ | `onlymcp logout` | Remove the token and the local cache |
41
+
42
+ ## What the agent gets
43
+
44
+ The MCP tools (`list_skills`, `get_skill`, `list_agents`, `get_agent_definition`,
45
+ `memory_read`, `memory_write`) work in every MCP client. Clients with their own skill
46
+ scanner additionally get stub files so the agent knows what exists without asking
47
+ first. Claude Code can also spawn the agents as subagents; elsewhere they stay
48
+ readable but not spawnable.
49
+
50
+ ## Environment
51
+
52
+ | Variable | Purpose |
53
+ | --- | --- |
54
+ | `ONLYMCP_API_URL` | Point at another instance (default `https://only-mcp.com`) |
55
+ | `ONLYMCP_STUB_DIRS` | Colon-separated list of directories to write stubs into |
56
+ | `ONLYMCP_HOME` | Move all local state, used by the tests |
57
+ | `ONLYMCP_TTL_SECONDS` | Cache lifetime |
58
+
59
+ MIT licensed. Source at [github.com/Francesco070/onlymcp](https://github.com/Francesco070/onlymcp).
@@ -0,0 +1,55 @@
1
+ export declare class ApiError extends Error {
2
+ readonly code: string;
3
+ readonly params: Record<string, unknown>;
4
+ readonly status: number;
5
+ constructor(code: string, params?: Record<string, unknown>, status?: number);
6
+ }
7
+ export type SkillListItem = {
8
+ name: string;
9
+ updatedAt: string;
10
+ };
11
+ export type AgentListItem = {
12
+ name: string;
13
+ description: string;
14
+ model: string | null;
15
+ tools: string[];
16
+ updatedAt: string;
17
+ };
18
+ export type MemoryFile = {
19
+ path: string;
20
+ content: string;
21
+ frontmatter: Record<string, unknown>;
22
+ links: string[];
23
+ updatedAt: string;
24
+ updatedByDevice: string | null;
25
+ };
26
+ export type WriteResult = {
27
+ path: string;
28
+ updatedAt: string;
29
+ warning?: {
30
+ code: string;
31
+ params: Record<string, unknown>;
32
+ };
33
+ };
34
+ export declare class ApiClient {
35
+ private readonly baseUrl;
36
+ private readonly token;
37
+ constructor(baseUrl: string, token: () => string | null);
38
+ private call;
39
+ listSkills(): Promise<{
40
+ skills: SkillListItem[];
41
+ }>;
42
+ getSkill(name: string): Promise<{
43
+ name: string;
44
+ body: string;
45
+ updatedAt: string;
46
+ }>;
47
+ listAgents(): Promise<{
48
+ agents: AgentListItem[];
49
+ }>;
50
+ getAgent(name: string): Promise<AgentListItem & {
51
+ body: string;
52
+ }>;
53
+ readMemory(path: string): Promise<MemoryFile>;
54
+ writeMemory(path: string, content: string, deviceLabel?: string): Promise<WriteResult>;
55
+ }
@@ -0,0 +1,59 @@
1
+ export class ApiError extends Error {
2
+ code;
3
+ params;
4
+ status;
5
+ constructor(code, params = {}, status = 0) {
6
+ super(code);
7
+ this.code = code;
8
+ this.params = params;
9
+ this.status = status;
10
+ this.name = 'ApiError';
11
+ }
12
+ }
13
+ export class ApiClient {
14
+ baseUrl;
15
+ token;
16
+ constructor(baseUrl, token) {
17
+ this.baseUrl = baseUrl;
18
+ this.token = token;
19
+ }
20
+ async call(path, init = {}) {
21
+ const tok = this.token();
22
+ if (!tok)
23
+ throw new ApiError('errors.NOT_LOGGED_IN');
24
+ const res = await fetch(`${this.baseUrl}${path}`, {
25
+ ...init,
26
+ headers: {
27
+ ...(init.headers ?? {}),
28
+ authorization: `Bearer ${tok}`,
29
+ 'content-type': 'application/json',
30
+ },
31
+ });
32
+ if (res.status === 204)
33
+ return undefined;
34
+ const body = await res.json().catch(() => ({}));
35
+ if (!res.ok) {
36
+ throw new ApiError(body.code ?? 'errors.UNKNOWN', body.params ?? {}, res.status);
37
+ }
38
+ return body;
39
+ }
40
+ listSkills() {
41
+ return this.call('/api/skills');
42
+ }
43
+ getSkill(name) {
44
+ return this.call(`/api/skills/${encodeURIComponent(name)}`);
45
+ }
46
+ listAgents() {
47
+ return this.call('/api/agents');
48
+ }
49
+ getAgent(name) {
50
+ return this.call(`/api/agents/${encodeURIComponent(name)}`);
51
+ }
52
+ readMemory(path) {
53
+ return this.call(`/api/memory/${path.split('/').map(encodeURIComponent).join('/')}`);
54
+ }
55
+ writeMemory(path, content, deviceLabel) {
56
+ return this.call(`/api/memory/${path.split('/').map(encodeURIComponent).join('/')}`, { method: 'PUT', body: JSON.stringify({ content, deviceLabel }) });
57
+ }
58
+ }
59
+ //# sourceMappingURL=apiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB;IACA;IACA;IAHX,YACW,IAAY,EACZ,SAAkC,EAAE,EACpC,SAAS,CAAC;QAEnB,KAAK,CAAC,IAAI,CAAC,CAAC;QAJH,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAA8B;QACpC,WAAM,GAAN,MAAM,CAAI;QAGnB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAwBD,MAAM,OAAO,SAAS;IAED;IACA;IAFnB,YACmB,OAAe,EACf,KAA0B;QAD1B,YAAO,GAAP,OAAO,CAAQ;QACf,UAAK,GAAL,KAAK,CAAqB;IAC1C,CAAC;IAEI,KAAK,CAAC,IAAI,CAAI,IAAY,EAAE,OAAoB,EAAE;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,QAAQ,CAAC,sBAAsB,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YAChD,GAAG,IAAI;YACP,OAAO,EAAE;gBACP,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;gBACvB,aAAa,EAAE,UAAU,GAAG,EAAE;gBAC9B,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAc,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,gBAAgB,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,IAAS,CAAC;IACnB,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,IAAI,CAA8B,aAAa,CAAC,CAAC;IAC/D,CAAC;IACD,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,IAAI,CACd,eAAe,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAC1C,CAAC;IACJ,CAAC;IACD,UAAU;QACR,OAAO,IAAI,CAAC,IAAI,CAA8B,aAAa,CAAC,CAAC;IAC/D,CAAC;IACD,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,IAAI,CACd,eAAe,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAC1C,CAAC;IACJ,CAAC;IACD,UAAU,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,IAAI,CAAa,eAAe,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,WAAW,CAAC,IAAY,EAAE,OAAe,EAAE,WAAoB;QAC7D,OAAO,IAAI,CAAC,IAAI,CACd,eAAe,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAClE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,CAClE,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ type Deps = {
2
+ sleep: (ms: number) => Promise<void>;
3
+ };
4
+ export declare function pollForToken(baseUrl: string, deviceCode: string, intervalSeconds: number, deps?: Deps): Promise<string>;
5
+ export {};
@@ -0,0 +1,36 @@
1
+ import { ApiError } from '../apiClient.js';
2
+ const defaultDeps = {
3
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
4
+ };
5
+ export async function pollForToken(baseUrl, deviceCode, intervalSeconds, deps = defaultDeps) {
6
+ let interval = intervalSeconds;
7
+ for (;;) {
8
+ await deps.sleep(interval * 1000);
9
+ const res = await fetch(`${baseUrl}/api/auth/device/token`, {
10
+ method: 'POST',
11
+ headers: { 'content-type': 'application/json' },
12
+ body: JSON.stringify({
13
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
14
+ device_code: deviceCode,
15
+ client_id: 'onlymcp-cli',
16
+ }),
17
+ });
18
+ const body = await res.json().catch(() => ({}));
19
+ if (body.access_token)
20
+ return body.access_token;
21
+ switch (body.error) {
22
+ case 'authorization_pending':
23
+ break;
24
+ case 'slow_down':
25
+ interval += 5;
26
+ break;
27
+ case 'access_denied':
28
+ throw new ApiError('errors.DEVICE_ACCESS_DENIED');
29
+ case 'expired_token':
30
+ throw new ApiError('errors.DEVICE_CODE_EXPIRED');
31
+ default:
32
+ throw new ApiError('errors.DEVICE_LOGIN_FAILED', { error: body.error });
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=deviceLogin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deviceLogin.js","sourceRoot":"","sources":["../../src/auth/deviceLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI3C,MAAM,WAAW,GAAS;IACxB,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,UAAkB,EAClB,eAAuB,EACvB,OAAa,WAAW;IAExB,IAAI,QAAQ,GAAG,eAAe,CAAC;IAC/B,SAAS,CAAC;QACR,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,wBAAwB,EAAE;YAC1D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,UAAU,EAAE,8CAA8C;gBAC1D,WAAW,EAAE,UAAU;gBACvB,SAAS,EAAE,aAAa;aACzB,CAAC;SACH,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAsB,CAAC;QAC1D,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,uBAAuB;gBAC1B,MAAM;YACR,KAAK,WAAW;gBACd,QAAQ,IAAI,CAAC,CAAC;gBACd,MAAM;YACR,KAAK,eAAe;gBAClB,MAAM,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAAC;YACpD,KAAK,eAAe;gBAClB,MAAM,IAAI,QAAQ,CAAC,4BAA4B,CAAC,CAAC;YACnD;gBACE,MAAM,IAAI,QAAQ,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const Keyring: {
2
+ save(token: string): void;
3
+ read(): string | null;
4
+ clear(): void;
5
+ };
@@ -0,0 +1,28 @@
1
+ import { Entry } from '@napi-rs/keyring';
2
+ const SERVICE = 'onlymcp';
3
+ const ACCOUNT = 'session-token';
4
+ function entry() {
5
+ return new Entry(SERVICE, ACCOUNT);
6
+ }
7
+ export const Keyring = {
8
+ save(token) {
9
+ entry().setPassword(token);
10
+ },
11
+ read() {
12
+ try {
13
+ return entry().getPassword();
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ },
19
+ clear() {
20
+ try {
21
+ entry().deletePassword();
22
+ }
23
+ catch {
24
+ /* nichts gespeichert */
25
+ }
26
+ },
27
+ };
28
+ //# sourceMappingURL=keyring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyring.js","sourceRoot":"","sources":["../../src/auth/keyring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,OAAO,GAAG,eAAe,CAAC;AAEhC,SAAS,KAAK;IACZ,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,IAAI,CAAC,KAAa;QAChB,KAAK,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI;QACF,IAAI,CAAC;YACH,OAAO,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,KAAK;QACH,IAAI,CAAC;YACH,KAAK,EAAE,CAAC,cAAc,EAAE,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ import { intro, log, 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 { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { dirname } from 'node:path';
10
+ import { ApiClient } from '../apiClient.js';
11
+ import { Keyring } from '../auth/keyring.js';
12
+ import { pollForToken } from '../auth/deviceLogin.js';
13
+ import { TtlCache } from '../cache.js';
14
+ import { targets } from '../install.js';
15
+ import { stubDirs } from '../paths.js';
16
+ import { writeStubs } from '../stubs.js';
17
+ const baseUrl = process.env.ONLYMCP_API_URL ?? 'https://only-mcp.com';
18
+ // Die Version kommt aus der package.json und steht nicht ein zweites Mal hier
19
+ // als Literal: sonst meldet `onlymcp --version` nach der ersten
20
+ // Veroeffentlichung einen Stand, den niemand mehr nachfuehrt. Der Pfad stimmt
21
+ // aus beiden Lagen, src/bin/ wie dist/bin/.
22
+ const { version } = createRequire(import.meta.url)('../../package.json');
23
+ const program = new Command().name('onlymcp').version(version);
24
+ // Der Server liefert verification_uri im Antwortkoerper mit. Ein Aufruf von
25
+ // open() nimmt jede Zeichenkette entgegen, auch file:// oder ein voellig
26
+ // anderes Ziel. Geoeffnet wird deshalb nur, wenn Schema und Ursprung exakt zu
27
+ // der konfigurierten API passen, die der Nutzer selbst als ONLYMCP_API_URL
28
+ // gesetzt hat. Alles andere wird stillschweigend uebersprungen, Code und QR
29
+ // stehen bereits vorher im Terminal und bleiben der zuverlaessige Weg.
30
+ function safeOpenUrl(url, expectedOrigin) {
31
+ try {
32
+ const u = new URL(url);
33
+ return (u.protocol === 'https:' || u.protocol === 'http:') && u.origin === expectedOrigin;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ // `writeFileSync` allein wuerde ~/.claude.json oder ~/.gemini/settings.json
40
+ // komplett ersetzen und damit jede andere dort gespeicherte Einstellung
41
+ // wegwerfen. Eine vorhandene, aber kaputte Datei wird nie stillschweigend
42
+ // ueberschrieben, lieber ein Fehler als ein Datenverlust.
43
+ function mergeJsonMcpServer(path, key, value) {
44
+ let existing = {};
45
+ if (existsSync(path)) {
46
+ const raw = readFileSync(path, 'utf8').trim();
47
+ if (raw) {
48
+ try {
49
+ existing = JSON.parse(raw);
50
+ }
51
+ catch {
52
+ throw new Error(`${path} enthaelt ungueltiges JSON. Nichts geschrieben.`);
53
+ }
54
+ }
55
+ }
56
+ const mcpServers = existing.mcpServers ?? {};
57
+ existing.mcpServers = { ...mcpServers, [key]: value };
58
+ writeFileSync(path, `${JSON.stringify(existing, null, 2)}\n`, 'utf8');
59
+ }
60
+ program
61
+ .command('login')
62
+ .description('Dieses Geraet mit OnlyMCP verbinden')
63
+ .action(async () => {
64
+ intro('OnlyMCP');
65
+ const res = await fetch(`${baseUrl}/api/auth/device/code`, {
66
+ method: 'POST',
67
+ headers: { 'content-type': 'application/json' },
68
+ body: JSON.stringify({ client_id: 'onlymcp-cli' }),
69
+ });
70
+ const d = await res.json();
71
+ log.info(`Code: ${d.user_code}`);
72
+ log.info(d.verification_uri);
73
+ console.log(await QRCode.toString(d.verification_uri_complete, { type: 'terminal', small: true }));
74
+ if (safeOpenUrl(d.verification_uri, new URL(baseUrl).origin)) {
75
+ await open(d.verification_uri).catch(() => { });
76
+ }
77
+ const s = spinner();
78
+ s.start('Warte auf Bestaetigung');
79
+ const token = await pollForToken(baseUrl, d.device_code, d.interval ?? 5);
80
+ Keyring.save(token);
81
+ s.stop('Verbunden');
82
+ const api = new ApiClient(baseUrl, () => Keyring.read());
83
+ let counts = { skills: 0, agents: 0 };
84
+ for (const dir of stubDirs())
85
+ counts = await writeStubs(api, dir);
86
+ outro(`${counts.skills} Skills, ${counts.agents} Agents bereit`);
87
+ });
88
+ program
89
+ .command('logout')
90
+ .description('Anmeldung und Cache auf diesem Geraet loeschen')
91
+ .action(async () => {
92
+ Keyring.clear();
93
+ await new TtlCache().clear();
94
+ log.success('Abgemeldet');
95
+ });
96
+ program
97
+ .command('status')
98
+ .description('Zeigt, ob dieses Geraet angemeldet ist')
99
+ .action(() => {
100
+ log.info(Keyring.read() ? 'angemeldet' : 'nicht angemeldet');
101
+ });
102
+ program
103
+ .command('sync')
104
+ .description('Stub-Dateien neu schreiben')
105
+ .action(async () => {
106
+ const api = new ApiClient(baseUrl, () => Keyring.read());
107
+ let counts = { skills: 0, agents: 0 };
108
+ for (const dir of stubDirs())
109
+ counts = await writeStubs(api, dir);
110
+ log.success(`${counts.skills} Skills, ${counts.agents} Agents`);
111
+ });
112
+ program
113
+ .command('install')
114
+ .description('OnlyMCP in einem MCP-Client eintragen')
115
+ .option('--client <id>', 'claude, codex oder gemini')
116
+ .option('--print', 'nur ausgeben, nichts schreiben')
117
+ .action((opts) => {
118
+ // ONLYMCP_HOME ist dieselbe Testschleuse wie in paths.ts: damit trifft
119
+ // kein Testlauf jemals den echten ~/.codex- oder ~/.claude-Ordner.
120
+ const all = targets(process.env.ONLYMCP_HOME ?? homedir());
121
+ if (!opts.client) {
122
+ log.info(`Bekannte Clients: ${all.map((t) => t.id).join(', ')}`);
123
+ log.info('Fuer jeden anderen MCP-Client: --print und den Schnipsel selbst eintragen.');
124
+ return;
125
+ }
126
+ const t = all.find((x) => x.id === opts.client);
127
+ if (!t) {
128
+ // Kein Raten. Ein falsch geratener Pfad ueberschreibt sonst eine fremde
129
+ // Konfigurationsdatei.
130
+ log.error(`Unbekannter Client. Bekannt: ${all.map((x) => x.id).join(', ')}`);
131
+ process.exitCode = 1;
132
+ return;
133
+ }
134
+ if (opts.print) {
135
+ log.info(`${t.label} -> ${t.configPath}`);
136
+ console.log(t.snippet);
137
+ // Bewusst keine zweite Zeile mit ONLYMCP_STUB_DIRS: die Variable steht
138
+ // seit install.ts im Schnipsel selbst. Zwei Wege, dasselbe zu setzen,
139
+ // laden dazu ein, beide zu gehen.
140
+ return;
141
+ }
142
+ mkdirSync(dirname(t.configPath), { recursive: true });
143
+ try {
144
+ if (t.id === 'codex') {
145
+ // TOML wird angehaengt, nicht ersetzt. Ein zweiter Lauf haengt sonst
146
+ // ein zweites [mcp_servers.onlymcp] an, und zwei gleiche Tabellen
147
+ // machen die Datei fuer die meisten TOML-Parser ungueltig.
148
+ const already = existsSync(t.configPath) && readFileSync(t.configPath, 'utf8').includes('[mcp_servers.onlymcp]');
149
+ if (already) {
150
+ log.info(`${t.label} ist bereits eingerichtet: ${t.configPath}`);
151
+ return;
152
+ }
153
+ appendFileSync(t.configPath, `\n${t.snippet}`);
154
+ }
155
+ else {
156
+ const parsed = JSON.parse(t.snippet);
157
+ const entry = Object.entries(parsed.mcpServers)[0];
158
+ if (!entry)
159
+ throw new Error(`${t.label}: leerer mcpServers-Schnipsel.`);
160
+ mergeJsonMcpServer(t.configPath, entry[0], entry[1]);
161
+ }
162
+ }
163
+ catch (e) {
164
+ log.error(e instanceof Error ? e.message : String(e));
165
+ process.exitCode = 1;
166
+ return;
167
+ }
168
+ if (t.stubDir)
169
+ mkdirSync(t.stubDir, { recursive: true });
170
+ log.success(`${t.label} eingerichtet: ${t.configPath}`);
171
+ });
172
+ await program.parseAsync();
173
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { z } from 'zod';
6
+ import { ApiClient } from '../apiClient.js';
7
+ import { Keyring } from '../auth/keyring.js';
8
+ import { TtlCache } from '../cache.js';
9
+ import { buildInstructions } from '../instructions.js';
10
+ import { stubDirs } from '../paths.js';
11
+ import { writeStubs } from '../stubs.js';
12
+ import { createGetAgentDefinitionTool } from '../tools/getAgentDefinition.js';
13
+ import { createGetSkillTool } from '../tools/getSkill.js';
14
+ import { createListAgentsTool, createListSkillsTool } from '../tools/lists.js';
15
+ import { createMemoryReadTool } from '../tools/memoryRead.js';
16
+ import { createMemoryWriteTool } from '../tools/memoryWrite.js';
17
+ const baseUrl = process.env.ONLYMCP_API_URL ?? 'https://only-mcp.com';
18
+ const api = new ApiClient(baseUrl, () => Keyring.read());
19
+ const cache = new TtlCache();
20
+ const tools = [
21
+ createGetSkillTool(api, cache),
22
+ createGetAgentDefinitionTool(api, cache),
23
+ createListSkillsTool(api),
24
+ createListAgentsTool(api),
25
+ createMemoryReadTool(api),
26
+ createMemoryWriteTool(api),
27
+ ];
28
+ // Der Katalog fuers instructions-Feld. Schlaegt der Abruf fehl (offline, nicht
29
+ // angemeldet), bleibt die reine Bedienungsanleitung uebrig: der Client weiss
30
+ // dann immer noch, dass es list_skills gibt, und kommt spaeter selbst dran.
31
+ const catalog = await (async () => {
32
+ try {
33
+ const [{ skills }, { agents }] = await Promise.all([api.listSkills(), api.listAgents()]);
34
+ return buildInstructions(skills.map((x) => x.name), agents.map((x) => x.name));
35
+ }
36
+ catch {
37
+ return buildInstructions([], []);
38
+ }
39
+ })();
40
+ const server = new Server({ name: 'onlymcp', version: '0.0.1' }, { capabilities: { tools: {} }, instructions: catalog });
41
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
42
+ tools: tools.map((t) => ({
43
+ name: t.name,
44
+ description: t.description,
45
+ inputSchema: z.toJSONSchema(t.inputSchema),
46
+ })),
47
+ }));
48
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
49
+ const tool = tools.find((t) => t.name === req.params.name);
50
+ if (!tool)
51
+ return { content: [{ type: 'text', text: 'errors.UNKNOWN_TOOL' }], isError: true };
52
+ const parsed = tool.inputSchema.safeParse(req.params.arguments ?? {});
53
+ if (!parsed.success) {
54
+ return { content: [{ type: 'text', text: 'errors.INVALID_ARGUMENTS' }], isError: true };
55
+ }
56
+ return tool.handler(parsed.data);
57
+ });
58
+ // Stubs beim Start auffrischen, fuer jeden Client mit eigenem Scanner. Faellt
59
+ // das aus (offline, nicht angemeldet), laeuft der Server trotzdem an: die
60
+ // vorhandenen Stubs bleiben gueltig, und der eigentliche Fehler zeigt sich
61
+ // erst beim Werkzeugaufruf mit klarem Code. Ein Client ohne Scanner bekommt
62
+ // hier gar nichts und braucht auch nichts: fuer ihn traegt `catalog`.
63
+ for (const dir of stubDirs()) {
64
+ writeStubs(api, dir).catch(() => { });
65
+ }
66
+ await server.connect(new StdioServerTransport());
67
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/bin/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare class TtlCache {
2
+ private readonly dir;
3
+ constructor(dir?: string);
4
+ get(key: string): Promise<string | null>;
5
+ set(key: string, value: string, ttlSeconds?: number): Promise<void>;
6
+ clear(): Promise<void>;
7
+ }
package/dist/cache.js ADDED
@@ -0,0 +1,32 @@
1
+ import cacache from 'cacache';
2
+ import { cacheDir } from './paths.js';
3
+ const DEFAULT_TTL_SECONDS = Number(process.env.ONLYMCP_TTL_SECONDS ?? 6 * 60 * 60);
4
+ export class TtlCache {
5
+ dir;
6
+ constructor(dir = cacheDir()) {
7
+ this.dir = dir;
8
+ }
9
+ async get(key) {
10
+ try {
11
+ const entry = await cacache.get(this.dir, key);
12
+ const meta = entry.metadata;
13
+ if (!meta || meta.expiresAt <= Date.now()) {
14
+ await cacache.rm.entry(this.dir, key);
15
+ return null;
16
+ }
17
+ return entry.data.toString('utf8');
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ async set(key, value, ttlSeconds = DEFAULT_TTL_SECONDS) {
24
+ await cacache.put(this.dir, key, value, {
25
+ metadata: { expiresAt: Date.now() + ttlSeconds * 1000 },
26
+ });
27
+ }
28
+ async clear() {
29
+ await cacache.rm.all(this.dir);
30
+ }
31
+ }
32
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,mBAAmB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AAInF,MAAM,OAAO,QAAQ;IACU;IAA7B,YAA6B,MAAc,QAAQ,EAAE;QAAxB,QAAG,GAAH,GAAG,CAAqB;IAAG,CAAC;IAEzD,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,QAA4B,CAAC;YAChD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC1C,MAAM,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACtC,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,UAAU,GAAG,mBAAmB;QACpE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE;YACtC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,EAAiB;SACvE,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ export type ClientId = 'claude' | 'codex' | 'gemini';
2
+ export interface InstallTarget {
3
+ id: ClientId;
4
+ label: string;
5
+ configPath: string;
6
+ /** null = dieser Client hat keinen Skill-Scanner und braucht keine Stubs. */
7
+ stubDir: string | null;
8
+ snippet: string;
9
+ }
10
+ /** Das npm-Paket, das sowohl `onlymcp` als auch `onlymcp-mcp` mitbringt. */
11
+ export declare const PACKAGE = "onlymcp";
12
+ export declare function targets(home: string): InstallTarget[];