@cybermem/mcp 0.8.5 → 0.8.7
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/dist/index.js +17 -22
- package/package.json +1 -1
- package/src/index.ts +21 -20
- package/src/auth.ts +0 -244
package/dist/index.js
CHANGED
|
@@ -21,7 +21,6 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
21
21
|
const cors_1 = __importDefault(require("cors"));
|
|
22
22
|
const express_1 = __importDefault(require("express"));
|
|
23
23
|
const zod_1 = require("zod");
|
|
24
|
-
const auth_1 = require("./auth");
|
|
25
24
|
// Redirect all stdout to stderr IMMEDIATELY to protect Stdio protocol
|
|
26
25
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
27
26
|
process.stdout.write = (chunk, encoding, callback) => {
|
|
@@ -37,34 +36,17 @@ console.log = console.error;
|
|
|
37
36
|
console.info = console.error;
|
|
38
37
|
// Async Storage for Request Context (User ID and Client Name)
|
|
39
38
|
const requestContext = new async_hooks_1.AsyncLocalStorage();
|
|
40
|
-
//
|
|
39
|
+
// CLI args processing
|
|
41
40
|
const args = process.argv.slice(2);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.then(() => process.exit(0))
|
|
45
|
-
.catch((err) => {
|
|
46
|
-
console.error("Login failed:", err.message);
|
|
47
|
-
process.exit(1);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
else if (args.includes("--logout")) {
|
|
51
|
-
(0, auth_1.logout)();
|
|
52
|
-
process.exit(0);
|
|
53
|
-
}
|
|
54
|
-
else if (args.includes("--status")) {
|
|
55
|
-
(0, auth_1.showStatus)();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
startServer();
|
|
60
|
-
}
|
|
41
|
+
// Start the server
|
|
42
|
+
startServer();
|
|
61
43
|
async function startServer() {
|
|
62
44
|
const getArg = (name) => {
|
|
63
45
|
const idx = args.indexOf(name);
|
|
64
46
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
65
47
|
};
|
|
66
48
|
const cliUrl = getArg("--url");
|
|
67
|
-
const cliToken = getArg("--token") || getArg("--api-key")
|
|
49
|
+
const cliToken = getArg("--token") || getArg("--api-key");
|
|
68
50
|
let stdioClientName = undefined;
|
|
69
51
|
// Protocol Instructions
|
|
70
52
|
const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
|
|
@@ -181,6 +163,19 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
|
|
|
181
163
|
if (err)
|
|
182
164
|
console.error("[MCP] Init access_log table error:", err.message);
|
|
183
165
|
});
|
|
166
|
+
// Access keys table for token-based auth
|
|
167
|
+
db.run(`CREATE TABLE IF NOT EXISTS access_keys (
|
|
168
|
+
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
169
|
+
key_hash TEXT NOT NULL,
|
|
170
|
+
name TEXT DEFAULT 'default',
|
|
171
|
+
user_id TEXT DEFAULT 'default',
|
|
172
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
173
|
+
last_used_at TEXT,
|
|
174
|
+
is_active INTEGER DEFAULT 1
|
|
175
|
+
);`, (err) => {
|
|
176
|
+
if (err)
|
|
177
|
+
console.error("[MCP] Init access_keys table error:", err.message);
|
|
178
|
+
});
|
|
184
179
|
});
|
|
185
180
|
db.close();
|
|
186
181
|
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -17,7 +17,6 @@ import axios from "axios";
|
|
|
17
17
|
import cors from "cors";
|
|
18
18
|
import express from "express";
|
|
19
19
|
import { z } from "zod";
|
|
20
|
-
import { getToken, login, logout, showStatus } from "./auth";
|
|
21
20
|
|
|
22
21
|
// Redirect all stdout to stderr IMMEDIATELY to protect Stdio protocol
|
|
23
22
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
@@ -40,25 +39,11 @@ const requestContext = new AsyncLocalStorage<{
|
|
|
40
39
|
clientName?: string;
|
|
41
40
|
}>();
|
|
42
41
|
|
|
43
|
-
//
|
|
42
|
+
// CLI args processing
|
|
44
43
|
const args = process.argv.slice(2);
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.then(() => process.exit(0))
|
|
49
|
-
.catch((err) => {
|
|
50
|
-
console.error("Login failed:", err.message);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
});
|
|
53
|
-
} else if (args.includes("--logout")) {
|
|
54
|
-
logout();
|
|
55
|
-
process.exit(0);
|
|
56
|
-
} else if (args.includes("--status")) {
|
|
57
|
-
showStatus();
|
|
58
|
-
process.exit(0);
|
|
59
|
-
} else {
|
|
60
|
-
startServer();
|
|
61
|
-
}
|
|
45
|
+
// Start the server
|
|
46
|
+
startServer();
|
|
62
47
|
|
|
63
48
|
async function startServer() {
|
|
64
49
|
const getArg = (name: string) => {
|
|
@@ -67,7 +52,7 @@ async function startServer() {
|
|
|
67
52
|
};
|
|
68
53
|
|
|
69
54
|
const cliUrl = getArg("--url");
|
|
70
|
-
const cliToken = getArg("--token") || getArg("--api-key")
|
|
55
|
+
const cliToken = getArg("--token") || getArg("--api-key");
|
|
71
56
|
let stdioClientName: string | undefined = undefined;
|
|
72
57
|
|
|
73
58
|
// Protocol Instructions
|
|
@@ -154,7 +139,7 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
|
|
|
154
139
|
try {
|
|
155
140
|
const dir = path.dirname(dbPath);
|
|
156
141
|
if (dir) fs.mkdirSync(dir, { recursive: true });
|
|
157
|
-
} catch {
|
|
142
|
+
} catch {}
|
|
158
143
|
|
|
159
144
|
try {
|
|
160
145
|
// Dynamic import to ensure env vars are set before loading SDK
|
|
@@ -204,6 +189,22 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
|
|
|
204
189
|
console.error("[MCP] Init access_log table error:", err.message);
|
|
205
190
|
},
|
|
206
191
|
);
|
|
192
|
+
// Access keys table for token-based auth
|
|
193
|
+
db.run(
|
|
194
|
+
`CREATE TABLE IF NOT EXISTS access_keys (
|
|
195
|
+
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
196
|
+
key_hash TEXT NOT NULL,
|
|
197
|
+
name TEXT DEFAULT 'default',
|
|
198
|
+
user_id TEXT DEFAULT 'default',
|
|
199
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
200
|
+
last_used_at TEXT,
|
|
201
|
+
is_active INTEGER DEFAULT 1
|
|
202
|
+
);`,
|
|
203
|
+
(err: any) => {
|
|
204
|
+
if (err)
|
|
205
|
+
console.error("[MCP] Init access_keys table error:", err.message);
|
|
206
|
+
},
|
|
207
|
+
);
|
|
207
208
|
});
|
|
208
209
|
db.close();
|
|
209
210
|
} catch (e) {
|
package/src/auth.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CyberMem MCP Auth Module
|
|
3
|
-
*
|
|
4
|
-
* Token storage and browser-based OAuth login flow.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as fs from "fs";
|
|
8
|
-
import * as http from "http";
|
|
9
|
-
import * as os from "os";
|
|
10
|
-
import * as path from "path";
|
|
11
|
-
|
|
12
|
-
const AUTH_DIR = path.join(os.homedir(), ".cybermem");
|
|
13
|
-
const TOKEN_FILE = path.join(AUTH_DIR, "token.json");
|
|
14
|
-
const AUTH_URL = process.env.CYBERMEM_AUTH_URL || "https://cybermem.dev";
|
|
15
|
-
|
|
16
|
-
interface StoredToken {
|
|
17
|
-
access_token: string;
|
|
18
|
-
expires_at: string;
|
|
19
|
-
email?: string;
|
|
20
|
-
name?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Ensure the .cybermem directory exists
|
|
25
|
-
*/
|
|
26
|
-
function ensureAuthDir(): void {
|
|
27
|
-
if (!fs.existsSync(AUTH_DIR)) {
|
|
28
|
-
fs.mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get stored token if valid
|
|
34
|
-
*/
|
|
35
|
-
export function getToken(): string | null {
|
|
36
|
-
try {
|
|
37
|
-
if (!fs.existsSync(TOKEN_FILE)) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const data: StoredToken = JSON.parse(fs.readFileSync(TOKEN_FILE, "utf-8"));
|
|
42
|
-
|
|
43
|
-
// Check expiration
|
|
44
|
-
if (new Date(data.expires_at) < new Date()) {
|
|
45
|
-
console.error("Token expired, please run --login");
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return data.access_token;
|
|
50
|
-
} catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if user is logged in with valid token
|
|
57
|
-
*/
|
|
58
|
-
export function isLoggedIn(): boolean {
|
|
59
|
-
return getToken() !== null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get user info from stored token
|
|
64
|
-
*/
|
|
65
|
-
export function getUserInfo(): { email?: string; name?: string } | null {
|
|
66
|
-
try {
|
|
67
|
-
if (!fs.existsSync(TOKEN_FILE)) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
const data: StoredToken = JSON.parse(fs.readFileSync(TOKEN_FILE, "utf-8"));
|
|
71
|
-
return { email: data.email, name: data.name };
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Save token to disk
|
|
79
|
-
*/
|
|
80
|
-
function saveToken(
|
|
81
|
-
token: string,
|
|
82
|
-
expiresIn: number,
|
|
83
|
-
email?: string,
|
|
84
|
-
name?: string,
|
|
85
|
-
): void {
|
|
86
|
-
ensureAuthDir();
|
|
87
|
-
|
|
88
|
-
const expiresAt = new Date(Date.now() + expiresIn * 1000);
|
|
89
|
-
const data: StoredToken = {
|
|
90
|
-
access_token: token,
|
|
91
|
-
expires_at: expiresAt.toISOString(),
|
|
92
|
-
email,
|
|
93
|
-
name,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
fs.writeFileSync(TOKEN_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Remove stored token
|
|
101
|
-
*/
|
|
102
|
-
export function logout(): void {
|
|
103
|
-
if (fs.existsSync(TOKEN_FILE)) {
|
|
104
|
-
fs.unlinkSync(TOKEN_FILE);
|
|
105
|
-
console.log("✅ Logged out successfully");
|
|
106
|
-
} else {
|
|
107
|
-
console.log("Already logged out");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Show current auth status
|
|
113
|
-
*/
|
|
114
|
-
export function showStatus(): void {
|
|
115
|
-
const token = getToken();
|
|
116
|
-
const userInfo = getUserInfo();
|
|
117
|
-
|
|
118
|
-
if (token && userInfo) {
|
|
119
|
-
console.log(
|
|
120
|
-
"✅ Logged in as:",
|
|
121
|
-
userInfo.email || userInfo.name || "Unknown",
|
|
122
|
-
);
|
|
123
|
-
if (userInfo.name) console.log(" Name:", userInfo.name);
|
|
124
|
-
} else {
|
|
125
|
-
console.log("❌ Not logged in");
|
|
126
|
-
console.log(" Run: npx @cybermem/mcp --login");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Start OAuth login flow
|
|
132
|
-
* Opens browser and waits for callback with token
|
|
133
|
-
*/
|
|
134
|
-
export async function login(): Promise<void> {
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
// Find available port
|
|
137
|
-
const server = http.createServer();
|
|
138
|
-
|
|
139
|
-
server.listen(0, "127.0.0.1", () => {
|
|
140
|
-
const address = server.address();
|
|
141
|
-
if (!address || typeof address === "string") {
|
|
142
|
-
reject(new Error("Failed to start callback server"));
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const port = address.port;
|
|
147
|
-
const callbackUrl = `http://localhost:${port}/callback`;
|
|
148
|
-
const authUrl = `${AUTH_URL}/api/auth/signin?callbackUrl=${encodeURIComponent(`${AUTH_URL}/api/auth/cli/callback?redirect=${encodeURIComponent(callbackUrl)}`)}`;
|
|
149
|
-
|
|
150
|
-
console.log("🔐 Opening browser for GitHub login...");
|
|
151
|
-
console.log(` If browser doesn't open, visit: ${authUrl}`);
|
|
152
|
-
|
|
153
|
-
// Open browser
|
|
154
|
-
const open = async (url: string) => {
|
|
155
|
-
const { exec } = await import("child_process");
|
|
156
|
-
const cmd =
|
|
157
|
-
process.platform === "darwin"
|
|
158
|
-
? `open "${url}"`
|
|
159
|
-
: process.platform === "win32"
|
|
160
|
-
? `start "${url}"`
|
|
161
|
-
: `xdg-open "${url}"`;
|
|
162
|
-
exec(cmd);
|
|
163
|
-
};
|
|
164
|
-
open(authUrl);
|
|
165
|
-
|
|
166
|
-
// Handle callback
|
|
167
|
-
server.on("request", async (req, res) => {
|
|
168
|
-
if (!req.url?.startsWith("/callback")) {
|
|
169
|
-
res.writeHead(404);
|
|
170
|
-
res.end("Not found");
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const url = new URL(req.url, `http://localhost:${port}`);
|
|
175
|
-
const token = url.searchParams.get("token");
|
|
176
|
-
|
|
177
|
-
if (!token) {
|
|
178
|
-
res.writeHead(400);
|
|
179
|
-
res.end("Missing token");
|
|
180
|
-
server.close();
|
|
181
|
-
reject(new Error("No token received"));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Decode token to get user info (JWT payload)
|
|
186
|
-
let email: string | undefined;
|
|
187
|
-
let name: string | undefined;
|
|
188
|
-
try {
|
|
189
|
-
const payload = JSON.parse(
|
|
190
|
-
Buffer.from(token.split(".")[1], "base64").toString(),
|
|
191
|
-
);
|
|
192
|
-
email = payload.email;
|
|
193
|
-
name = payload.name;
|
|
194
|
-
} catch {
|
|
195
|
-
// Ignore decode errors
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Save token (30 days expiry)
|
|
199
|
-
saveToken(token, 30 * 24 * 60 * 60, email, name);
|
|
200
|
-
|
|
201
|
-
// Send success page
|
|
202
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
203
|
-
res.end(`
|
|
204
|
-
<!DOCTYPE html>
|
|
205
|
-
<html>
|
|
206
|
-
<head>
|
|
207
|
-
<title>CyberMem - Logged In</title>
|
|
208
|
-
<style>
|
|
209
|
-
body { font-family: system-ui; text-align: center; padding: 50px; background: #0a0a0a; color: #fff; }
|
|
210
|
-
h1 { color: #22c55e; }
|
|
211
|
-
.logo { font-size: 48px; margin-bottom: 20px; }
|
|
212
|
-
</style>
|
|
213
|
-
</head>
|
|
214
|
-
<body>
|
|
215
|
-
<div class="logo">🧠</div>
|
|
216
|
-
<h1>Successfully Logged In!</h1>
|
|
217
|
-
<p>You can close this window and return to your terminal.</p>
|
|
218
|
-
<p style="color: #888;">Logged in as: ${email || name || "Unknown"}</p>
|
|
219
|
-
</body>
|
|
220
|
-
</html>
|
|
221
|
-
`);
|
|
222
|
-
|
|
223
|
-
console.log("");
|
|
224
|
-
console.log(
|
|
225
|
-
"✅ Successfully logged in as:",
|
|
226
|
-
email || name || "Unknown",
|
|
227
|
-
);
|
|
228
|
-
console.log(" Token saved to:", TOKEN_FILE);
|
|
229
|
-
|
|
230
|
-
server.close();
|
|
231
|
-
resolve();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// Timeout after 5 minutes
|
|
235
|
-
setTimeout(
|
|
236
|
-
() => {
|
|
237
|
-
server.close();
|
|
238
|
-
reject(new Error("Login timeout - no callback received"));
|
|
239
|
-
},
|
|
240
|
-
5 * 60 * 1000,
|
|
241
|
-
);
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
}
|