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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/apiClient.d.ts +55 -0
- package/dist/apiClient.js +59 -0
- package/dist/apiClient.js.map +1 -0
- package/dist/auth/deviceLogin.d.ts +5 -0
- package/dist/auth/deviceLogin.js +36 -0
- package/dist/auth/deviceLogin.js.map +1 -0
- package/dist/auth/keyring.d.ts +5 -0
- package/dist/auth/keyring.js +28 -0
- package/dist/auth/keyring.js.map +1 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.js +173 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/mcp.d.ts +2 -0
- package/dist/bin/mcp.js +67 -0
- package/dist/bin/mcp.js.map +1 -0
- package/dist/cache.d.ts +7 -0
- package/dist/cache.js +32 -0
- package/dist/cache.js.map +1 -0
- package/dist/install.d.ts +12 -0
- package/dist/install.js +82 -0
- package/dist/install.js.map +1 -0
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.js +46 -0
- package/dist/instructions.js.map +1 -0
- package/dist/memoryPath.d.ts +1 -0
- package/dist/memoryPath.js +13 -0
- package/dist/memoryPath.js.map +1 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +28 -0
- package/dist/paths.js.map +1 -0
- package/dist/stubs.d.ts +5 -0
- package/dist/stubs.js +52 -0
- package/dist/stubs.js.map +1 -0
- package/dist/tools/getAgentDefinition.d.ts +26 -0
- package/dist/tools/getAgentDefinition.js +26 -0
- package/dist/tools/getAgentDefinition.js.map +1 -0
- package/dist/tools/getSkill.d.ts +26 -0
- package/dist/tools/getSkill.js +26 -0
- package/dist/tools/getSkill.js.map +1 -0
- package/dist/tools/lists.d.ts +38 -0
- package/dist/tools/lists.js +39 -0
- package/dist/tools/lists.js.map +1 -0
- package/dist/tools/memoryRead.d.ts +25 -0
- package/dist/tools/memoryRead.js +33 -0
- package/dist/tools/memoryRead.js.map +1 -0
- package/dist/tools/memoryWrite.d.ts +27 -0
- package/dist/tools/memoryWrite.js +32 -0
- package/dist/tools/memoryWrite.js.map +1 -0
- 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,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,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"}
|
package/dist/bin/cli.js
ADDED
|
@@ -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":";AACA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,sBAAsB,CAAC;AAEtE,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAC9E,4CAA4C;AAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAwB,CAAC;AAChG,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/D,4EAA4E;AAC5E,yEAAyE;AACzE,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,uEAAuE;AACvE,SAAS,WAAW,CAAC,GAAW,EAAE,cAAsB;IACtD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,cAAc,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,wEAAwE;AACxE,0EAA0E;AAC1E,0DAA0D;AAC1D,SAAS,kBAAkB,CAAC,IAAY,EAAE,GAAW,EAAE,KAAc;IACnE,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC;gBACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;YACxD,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,iDAAiD,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAI,QAAQ,CAAC,UAAkD,IAAI,EAAE,CAAC;IACtF,QAAQ,CAAC,UAAU,GAAG,EAAE,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;IACtD,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxE,CAAC;AAED,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,qCAAqC,CAAC;KAClD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,KAAK,CAAC,SAAS,CAAC,CAAC;IACjB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,uBAAuB,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;KACnD,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE3B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,yBAAyB,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnG,IAAI,WAAW,CAAC,CAAC,CAAC,gBAAgB,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAEpB,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,IAAI,MAAM,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE;QAAE,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClE,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,gBAAgB,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,MAAM,IAAI,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC;IAC7B,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,GAAG,EAAE;IACX,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,IAAI,MAAM,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE;QAAE,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClE,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC;KACpD,MAAM,CAAC,SAAS,EAAE,gCAAgC,CAAC;KACnD,MAAM,CAAC,CAAC,IAA0C,EAAE,EAAE;IACrD,uEAAuE;IACvE,mEAAmE;IACnE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,GAAG,CAAC,IAAI,CAAC,qBAAqB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,GAAG,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QACvF,OAAO;IACT,CAAC;IACD,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,wEAAwE;QACxE,uBAAuB;QACvB,GAAG,CAAC,KAAK,CAAC,gCAAgC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACvB,uEAAuE;QACvE,sEAAsE;QACtE,kCAAkC;QAClC,OAAO;IACT,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,CAAC;YACrB,qEAAqE;YACrE,kEAAkE;YAClE,2DAA2D;YAC3D,MAAM,OAAO,GACX,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;YACnG,IAAI,OAAO,EAAE,CAAC;gBACZ,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,8BAA8B,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YACD,cAAc,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAA4C,CAAC;YAChF,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,gCAAgC,CAAC,CAAC;YACxE,kBAAkB,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,CAAC,CAAC,OAAO;QAAE,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEL,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC"}
|
package/dist/bin/mcp.js
ADDED
|
@@ -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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"}
|
package/dist/cache.d.ts
ADDED
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[];
|