@cybermem/dashboard 0.13.17 → 0.14.5
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/CHANGELOG.md +18 -0
- package/Dockerfile +3 -3
- package/app/api/mcp-config/route.ts +33 -21
- package/app/api/settings/route.ts +57 -3
- package/components/dashboard/settings/access-token-section.tsx +6 -1
- package/components/dashboard/settings-modal.tsx +10 -0
- package/e2e/api.spec.ts +23 -0
- package/e2e/ui.spec.ts +12 -9
- package/eslint.config.mjs +6 -3
- package/package.json +1 -1
- package/public/clients.json +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @cybermem/dashboard
|
|
2
|
+
|
|
3
|
+
## 0.14.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Automated release for patch version bump.
|
|
8
|
+
|
|
9
|
+
## 0.14.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#86](https://github.com/mikhailkogan17/cybermem/pull/86) [`ffca5dd`](https://github.com/mikhailkogan17/cybermem/commit/ffca5dd374a50f594c1a935f1fe81ee1e1e3c6fe) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Migrate version management to Changesets
|
|
14
|
+
|
|
15
|
+
- Remove custom bash scripts (version-bump.sh, release.sh)
|
|
16
|
+
- Add Changesets for automated versioning and changelog generation
|
|
17
|
+
- Update publish workflow to use Changesets
|
|
18
|
+
- Add documentation for new release process
|
package/Dockerfile
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Base stage for both dev and prod
|
|
2
|
-
FROM node:
|
|
2
|
+
FROM node:24-alpine AS base
|
|
3
3
|
WORKDIR /app
|
|
4
4
|
|
|
5
5
|
# Use corepack to get the pnpm version from the lockfile and speed up installs via cache
|
|
@@ -27,13 +27,13 @@ RUN --mount=type=cache,target=/root/.pnpm-store \
|
|
|
27
27
|
pnpm build
|
|
28
28
|
|
|
29
29
|
# Native stage for sqlite3 bindings and wrapper
|
|
30
|
-
FROM node:
|
|
30
|
+
FROM node:24-alpine AS native-builder
|
|
31
31
|
RUN apk update && apk add --no-cache python3 python3-dev make g++
|
|
32
32
|
WORKDIR /native
|
|
33
33
|
RUN npm init -y && npm install sqlite3@5.1.7 sqlite@5.1.1
|
|
34
34
|
|
|
35
35
|
# Production stage
|
|
36
|
-
FROM node:
|
|
36
|
+
FROM node:24-alpine AS production
|
|
37
37
|
RUN apk add --no-cache libc6-compat
|
|
38
38
|
WORKDIR /app
|
|
39
39
|
|
|
@@ -67,37 +67,49 @@ export async function GET(request: Request) {
|
|
|
67
67
|
let config: any;
|
|
68
68
|
let configType = client?.configType || "json";
|
|
69
69
|
|
|
70
|
+
// Local envs (isManaged): use @cybermem/mcp directly
|
|
71
|
+
// Remote envs: use mcp-remote (standard stdio-to-HTTP bridge)
|
|
70
72
|
if (configType === "toml") {
|
|
71
73
|
if (isManaged) {
|
|
72
|
-
|
|
74
|
+
const localArgs = isStaging
|
|
75
|
+
? `["-y", "@cybermem/mcp", "--env", "staging"]`
|
|
76
|
+
: `["-y", "@cybermem/mcp"]`;
|
|
77
|
+
config = `[mcpServers.cybermem]\ncommand = "npx"\nargs = ${localArgs}`;
|
|
73
78
|
} else {
|
|
74
79
|
const keyVal = maskKey ? displayKey : actualKey;
|
|
75
|
-
config = `[mcpServers.cybermem]\ncommand = "npx"\nargs = ["
|
|
80
|
+
config = `[mcpServers.cybermem]\ncommand = "npx"\nargs = ["-y", "mcp-remote", "${baseUrl}", "--header", "X-API-Key:${keyVal}"]`;
|
|
76
81
|
}
|
|
77
82
|
} else if (configType === "command" || configType === "cmd") {
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
83
|
+
// Generate command directly (don't rely on clients.json templates)
|
|
84
|
+
if (isManaged) {
|
|
85
|
+
const clientName = client?.id || "cybermem";
|
|
86
|
+
const cliPrefix = client?.id === "gemini-cli" ? "gemini" : "claude";
|
|
87
|
+
config = `${cliPrefix} mcp add ${clientName} npx -y @cybermem/mcp`;
|
|
88
|
+
} else {
|
|
89
|
+
const keyVal = maskKey ? displayKey : actualKey;
|
|
90
|
+
const clientName = client?.id || "cybermem";
|
|
91
|
+
const cliPrefix = client?.id === "gemini-cli" ? "gemini" : "claude";
|
|
92
|
+
config = `${cliPrefix} mcp add ${clientName} -- npx -y mcp-remote ${baseUrl} --header X-API-Key:${keyVal}`;
|
|
81
93
|
}
|
|
82
|
-
cmd = cmd.replace("{{ENDPOINT}}", baseUrl);
|
|
83
|
-
cmd = cmd.replace("{{API_KEY}}", maskKey ? displayKey : actualKey);
|
|
84
|
-
cmd = cmd.replace("{{TOKEN}}", maskKey ? displayKey : actualKey);
|
|
85
|
-
config = cmd;
|
|
86
94
|
} else {
|
|
87
95
|
// JSON (default)
|
|
88
|
-
|
|
89
|
-
? ["-y", "@cybermem/mcp"]
|
|
90
|
-
: [
|
|
91
|
-
"-y",
|
|
92
|
-
"@cybermem/mcp",
|
|
93
|
-
"--url",
|
|
94
|
-
baseUrl,
|
|
95
|
-
"--token",
|
|
96
|
-
maskKey ? displayKey : actualKey,
|
|
97
|
-
];
|
|
96
|
+
let args: string[];
|
|
98
97
|
|
|
99
|
-
if (
|
|
100
|
-
|
|
98
|
+
if (isManaged) {
|
|
99
|
+
// Local: use @cybermem/mcp directly (SDK mode)
|
|
100
|
+
args = ["-y", "@cybermem/mcp"];
|
|
101
|
+
if (isStaging) {
|
|
102
|
+
args.push("--env", "staging");
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// Remote: use mcp-remote (standard stdio-to-HTTP bridge)
|
|
106
|
+
args = [
|
|
107
|
+
"-y",
|
|
108
|
+
"mcp-remote",
|
|
109
|
+
baseUrl,
|
|
110
|
+
"--header",
|
|
111
|
+
`X-API-Key:${maskKey ? displayKey : actualKey}`,
|
|
112
|
+
];
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
config = {
|
|
@@ -120,11 +120,13 @@ export async function GET(request: NextRequest) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
let apiKey = process.env.OM_API_KEY || "not-set";
|
|
123
|
+
let tokenSource = "env";
|
|
123
124
|
|
|
124
125
|
// Try to read from HttpOnly cookie first
|
|
125
126
|
const cookieKey = request.cookies.get("cybermem_api_key")?.value;
|
|
126
127
|
if (cookieKey) {
|
|
127
128
|
apiKey = cookieKey;
|
|
129
|
+
tokenSource = "cookie";
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
// Fallback to config file
|
|
@@ -132,7 +134,10 @@ export async function GET(request: NextRequest) {
|
|
|
132
134
|
if (fs.existsSync(CONFIG_PATH)) {
|
|
133
135
|
const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
|
|
134
136
|
const conf = JSON.parse(raw);
|
|
135
|
-
if (conf.api_key && apiKey === "not-set")
|
|
137
|
+
if (conf.api_key && apiKey === "not-set") {
|
|
138
|
+
apiKey = conf.api_key;
|
|
139
|
+
tokenSource = "config";
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
142
|
} catch (e) {
|
|
138
143
|
// ignore
|
|
@@ -143,7 +148,45 @@ export async function GET(request: NextRequest) {
|
|
|
143
148
|
const secretPath = "/run/secrets/om_api_key";
|
|
144
149
|
if (fs.existsSync(secretPath)) {
|
|
145
150
|
const secret = fs.readFileSync(secretPath, "utf-8").trim();
|
|
146
|
-
if (secret)
|
|
151
|
+
if (secret) {
|
|
152
|
+
if (secret.startsWith("sk-")) {
|
|
153
|
+
apiKey = secret;
|
|
154
|
+
tokenSource = "docker-secret";
|
|
155
|
+
} else {
|
|
156
|
+
// Fallback: support env-file format like "OM_API_KEY=sk-..."
|
|
157
|
+
const envMatch = secret.match(
|
|
158
|
+
/OM_API_KEY=["']?(sk-[a-zA-Z0-9]+)["']?/,
|
|
159
|
+
);
|
|
160
|
+
if (envMatch) {
|
|
161
|
+
apiKey = envMatch[1];
|
|
162
|
+
tokenSource = "docker-secret-env";
|
|
163
|
+
} else {
|
|
164
|
+
console.warn(
|
|
165
|
+
`[Settings API] Token at ${secretPath} doesn't match expected format (sk-* or OM_API_KEY=sk-*)`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
// ignore
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Fallback: Auto-generated token location
|
|
176
|
+
try {
|
|
177
|
+
const fallbackPath = "/data/.cybermem_token";
|
|
178
|
+
if (apiKey === "not-set" && fs.existsSync(fallbackPath)) {
|
|
179
|
+
const token = fs.readFileSync(fallbackPath, "utf-8").trim();
|
|
180
|
+
if (token) {
|
|
181
|
+
if (token.startsWith("sk-")) {
|
|
182
|
+
apiKey = token;
|
|
183
|
+
tokenSource = "fallback";
|
|
184
|
+
} else {
|
|
185
|
+
console.warn(
|
|
186
|
+
`[Settings API] Token at ${fallbackPath} doesn't match expected format (sk-*)`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
147
190
|
}
|
|
148
191
|
} catch (e) {
|
|
149
192
|
// ignore
|
|
@@ -158,6 +201,16 @@ export async function GET(request: NextRequest) {
|
|
|
158
201
|
// isManaged = Local Mode (localhost auto-login)
|
|
159
202
|
const isManaged = isLocal;
|
|
160
203
|
|
|
204
|
+
// Mask the token for public display
|
|
205
|
+
const maskToken = (token: string) => {
|
|
206
|
+
if (!token || token === "not-set") return token;
|
|
207
|
+
if (token.length <= 10) return "****";
|
|
208
|
+
// sk-abcd...efgh
|
|
209
|
+
return `${token.slice(0, 7)}...${token.slice(-4)}`;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const maskedApiKey = maskToken(apiKey);
|
|
213
|
+
|
|
161
214
|
// Read version from package.json
|
|
162
215
|
let version = "v0.11.4"; // Default fallback
|
|
163
216
|
try {
|
|
@@ -176,8 +229,9 @@ export async function GET(request: NextRequest) {
|
|
|
176
229
|
|
|
177
230
|
return NextResponse.json(
|
|
178
231
|
{
|
|
179
|
-
token: apiKey,
|
|
180
232
|
apiKey: apiKey,
|
|
233
|
+
apiKeyMasked: maskedApiKey,
|
|
234
|
+
tokenSource: apiKey === "not-set" ? "not-set" : tokenSource,
|
|
181
235
|
endpoint,
|
|
182
236
|
isManaged,
|
|
183
237
|
isLocal,
|
|
@@ -7,6 +7,7 @@ import { Check, Copy, Eye, EyeOff, RotateCcw, Shield } from "lucide-react";
|
|
|
7
7
|
|
|
8
8
|
interface AccessTokenSectionProps {
|
|
9
9
|
apiKey: string;
|
|
10
|
+
apiKeyMasked: string;
|
|
10
11
|
showApiKey: boolean;
|
|
11
12
|
setShowApiKey: (show: boolean) => void;
|
|
12
13
|
copiedId: string | null;
|
|
@@ -18,6 +19,7 @@ interface AccessTokenSectionProps {
|
|
|
18
19
|
|
|
19
20
|
export default function AccessTokenSection({
|
|
20
21
|
apiKey,
|
|
22
|
+
apiKeyMasked,
|
|
21
23
|
showApiKey,
|
|
22
24
|
setShowApiKey,
|
|
23
25
|
copiedId,
|
|
@@ -40,7 +42,10 @@ export default function AccessTokenSection({
|
|
|
40
42
|
<div className="relative flex-1">
|
|
41
43
|
<Input
|
|
42
44
|
id="access-token"
|
|
43
|
-
value={
|
|
45
|
+
value={
|
|
46
|
+
(showApiKey ? apiKey : apiKeyMasked) ||
|
|
47
|
+
"Token not generated yet"
|
|
48
|
+
}
|
|
44
49
|
readOnly
|
|
45
50
|
className="bg-black/40 border-[0.5px] border-white/10 text-white font-mono text-sm pr-10"
|
|
46
51
|
type={showApiKey ? "text" : "password"}
|
|
@@ -12,6 +12,7 @@ import SystemInfoSection from "./settings/system-info-section";
|
|
|
12
12
|
|
|
13
13
|
export default function SettingsModal({ onClose }: { onClose: () => void }) {
|
|
14
14
|
const [apiKey, setApiKey] = useState("");
|
|
15
|
+
const [apiKeyMasked, setApiKeyMasked] = useState("");
|
|
15
16
|
const [endpoint, setEndpoint] = useState("");
|
|
16
17
|
const [isManaged, setIsManaged] = useState(false);
|
|
17
18
|
const [instanceType, setInstanceType] = useState<"local" | "rpi" | "vps">(
|
|
@@ -58,8 +59,16 @@ export default function SettingsModal({ onClose }: { onClose: () => void }) {
|
|
|
58
59
|
|
|
59
60
|
if (localKey && !data.isManaged) {
|
|
60
61
|
setApiKey(localKey);
|
|
62
|
+
// Mask the local key for display
|
|
63
|
+
const maskedLocal = localKey.length > 10
|
|
64
|
+
? `${localKey.slice(0, 7)}...${localKey.slice(-4)}`
|
|
65
|
+
: "****";
|
|
66
|
+
setApiKeyMasked(maskedLocal);
|
|
61
67
|
} else {
|
|
62
68
|
setApiKey(data.apiKey !== "not-set" ? data.apiKey : "");
|
|
69
|
+
setApiKeyMasked(
|
|
70
|
+
data.apiKeyMasked !== "not-set" ? data.apiKeyMasked : "",
|
|
71
|
+
);
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
let srvEndpoint = data.endpoint;
|
|
@@ -227,6 +236,7 @@ export default function SettingsModal({ onClose }: { onClose: () => void }) {
|
|
|
227
236
|
<div className="flex-1 overflow-y-auto p-8 space-y-8 bg-[#05100F]">
|
|
228
237
|
<AccessTokenSection
|
|
229
238
|
apiKey={apiKey}
|
|
239
|
+
apiKeyMasked={apiKeyMasked}
|
|
230
240
|
showApiKey={showApiKey}
|
|
231
241
|
setShowApiKey={setShowApiKey}
|
|
232
242
|
copiedId={copiedId}
|
package/e2e/api.spec.ts
CHANGED
|
@@ -215,5 +215,28 @@ test.describe("Dashboard:E2E:API (Deep Verification)", () => {
|
|
|
215
215
|
contentType: "text/plain",
|
|
216
216
|
});
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
await test.step("⚙️ Settings Fallback — Verify tokenSource and Masking", async () => {
|
|
220
|
+
console.log("⚙️ GET /api/settings (Fallback/Auto-gen Verification)");
|
|
221
|
+
|
|
222
|
+
const settingsResp = await request.get(`${DASHBOARD_URL}/api/settings`, {
|
|
223
|
+
headers: getHeaders("antigravity-client"),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const settings = await settingsResp.json();
|
|
227
|
+
console.log(` Token Source: ${settings.tokenSource}`);
|
|
228
|
+
|
|
229
|
+
expect(settingsResp.status()).toBe(200);
|
|
230
|
+
expect(settings).toHaveProperty("tokenSource");
|
|
231
|
+
// Verify apiKeyMasked is masked (not the raw apiKey field)
|
|
232
|
+
if (settings.apiKeyMasked && settings.apiKeyMasked !== "not-set") {
|
|
233
|
+
expect(settings.apiKeyMasked.length).toBeLessThanOrEqual(14); // 7 + 3 + 4 = 14
|
|
234
|
+
expect(settings.apiKeyMasked).toMatch(/^sk-.*\.\.\..*$/);
|
|
235
|
+
}
|
|
236
|
+
// Verify apiKey contains the raw token (for UI copy functionality)
|
|
237
|
+
if (settings.apiKey && settings.apiKey !== "not-set") {
|
|
238
|
+
expect(settings.apiKey).toMatch(/^sk-[a-f0-9]{32}$/);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
218
241
|
});
|
|
219
242
|
});
|
package/e2e/ui.spec.ts
CHANGED
|
@@ -199,8 +199,10 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
199
199
|
contentType: "application/json",
|
|
200
200
|
body: JSON.stringify({
|
|
201
201
|
apiKey: MOCK_API_KEY,
|
|
202
|
+
apiKeyMasked: "sk-e2e-...2345",
|
|
202
203
|
instanceId: "local-dev-mock",
|
|
203
204
|
instanceType: "local",
|
|
205
|
+
isManaged: true,
|
|
204
206
|
endpoint: "http://localhost:8626/mcp",
|
|
205
207
|
}),
|
|
206
208
|
});
|
|
@@ -210,7 +212,7 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
210
212
|
description: `API Key: ${MOCK_API_KEY.substring(0, 10)}...`,
|
|
211
213
|
});
|
|
212
214
|
|
|
213
|
-
// 5. Mock MCP Config
|
|
215
|
+
// 5. Mock MCP Config (local env = @cybermem/mcp direct, no --url)
|
|
214
216
|
await page.route("**/api/mcp-config*", async (route) => {
|
|
215
217
|
await route.fulfill({
|
|
216
218
|
status: 200,
|
|
@@ -219,19 +221,19 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
219
221
|
configType: "json",
|
|
220
222
|
config: {
|
|
221
223
|
mcpServers: {
|
|
222
|
-
|
|
224
|
+
cybermem: {
|
|
223
225
|
command: "npx",
|
|
224
|
-
args: ["@cybermem/mcp"
|
|
225
|
-
env: { X_CLIENT_NAME: MOCK_IDENTITY_WRITER },
|
|
226
|
+
args: ["-y", "@cybermem/mcp"],
|
|
226
227
|
},
|
|
227
228
|
},
|
|
228
229
|
},
|
|
230
|
+
isManaged: true,
|
|
229
231
|
}),
|
|
230
232
|
});
|
|
231
233
|
});
|
|
232
234
|
appliedMocks.push({
|
|
233
235
|
endpoint: "GET /api/mcp-config",
|
|
234
|
-
description: "MCP config with npx @cybermem/mcp",
|
|
236
|
+
description: "MCP config with npx @cybermem/mcp (local)",
|
|
235
237
|
});
|
|
236
238
|
});
|
|
237
239
|
|
|
@@ -326,9 +328,9 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
326
328
|
await expect(page.getByText(/ACCESS TOKEN/i).first()).toBeVisible();
|
|
327
329
|
});
|
|
328
330
|
|
|
329
|
-
await test.step(`Verify Token Matches Mock
|
|
331
|
+
await test.step(`Verify Token Matches Masked Mock by Default`, async () => {
|
|
330
332
|
const input = page.locator("input#access-token");
|
|
331
|
-
await expect(input).toHaveValue(
|
|
333
|
+
await expect(input).toHaveValue("sk-e2e-...2345");
|
|
332
334
|
});
|
|
333
335
|
|
|
334
336
|
await test.step("Toggle Token Visibility — password → text", async () => {
|
|
@@ -336,6 +338,7 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
336
338
|
await expect(input).toHaveAttribute("type", "password");
|
|
337
339
|
await page.getByTestId("toggle-visibility").click();
|
|
338
340
|
await expect(input).toHaveAttribute("type", "text");
|
|
341
|
+
await expect(input).toHaveValue(MOCK_API_KEY);
|
|
339
342
|
});
|
|
340
343
|
|
|
341
344
|
await flushNetwork();
|
|
@@ -357,14 +360,14 @@ test.describe("Dashboard:E2E:UI (High-Fidelity Mocks)", () => {
|
|
|
357
360
|
await page.getByRole("button", { name: MOCK_IDENTITY_WRITER }).click();
|
|
358
361
|
});
|
|
359
362
|
|
|
360
|
-
await test.step("Verify CLI Install Command — npx @cybermem/mcp", async () => {
|
|
363
|
+
await test.step("Verify CLI Install Command — npx @cybermem/mcp (local, no --url)", async () => {
|
|
361
364
|
const codeBlock = page.locator("pre code");
|
|
362
365
|
await expect(codeBlock).toContainText("npx");
|
|
363
366
|
await expect(codeBlock).toContainText("@cybermem/mcp");
|
|
364
367
|
});
|
|
365
368
|
|
|
366
369
|
await testInfo.attach("🚀 CLI Installation Command", {
|
|
367
|
-
body: `npx @cybermem/cli init\nnpx @cybermem/cli up\n\nMCP Server Config:\n"cybermem
|
|
370
|
+
body: `npx @cybermem/cli init\nnpx @cybermem/cli up\n\nMCP Server Config (local):\n"cybermem": {\n "command": "npx",\n "args": ["-y", "@cybermem/mcp"]\n}`,
|
|
368
371
|
contentType: "text/plain",
|
|
369
372
|
});
|
|
370
373
|
|
package/eslint.config.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import nextConfig from
|
|
1
|
+
import nextConfig from "eslint-config-next";
|
|
2
2
|
|
|
3
3
|
const eslintConfig = [
|
|
4
|
+
{
|
|
5
|
+
ignores: ["playwright-report/**", ".next/**", "test-results/**"],
|
|
6
|
+
},
|
|
4
7
|
...nextConfig,
|
|
5
8
|
{
|
|
6
9
|
rules: {
|
|
7
10
|
// Disable false-positive for sessionStorage reads in useEffect
|
|
8
|
-
|
|
11
|
+
"react-hooks/set-state-in-effect": "off",
|
|
9
12
|
// Disable for shadcn/ui components that use Math.random() in useMemo
|
|
10
|
-
|
|
13
|
+
"react-hooks/purity": "off",
|
|
11
14
|
},
|
|
12
15
|
},
|
|
13
16
|
];
|
package/package.json
CHANGED
package/public/clients.json
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
],
|
|
95
95
|
"configType": "command",
|
|
96
96
|
"localCommand": "claude mcp add cybermem npx @cybermem/mcp",
|
|
97
|
-
"remoteCommand": "claude mcp add cybermem -- npx
|
|
97
|
+
"remoteCommand": "claude mcp add cybermem -- npx -y mcp-remote {{ENDPOINT}} --header X-API-Key:{{TOKEN}}"
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"id": "chatgpt",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
],
|
|
159
159
|
"configType": "cmd",
|
|
160
160
|
"localCommand": "gemini mcp add cybermem npx @cybermem/mcp",
|
|
161
|
-
"remoteCommand": "gemini mcp add cybermem -- npx
|
|
161
|
+
"remoteCommand": "gemini mcp add cybermem -- npx -y mcp-remote {{ENDPOINT}} --header X-API-Key:{{TOKEN}}"
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
"id": "other",
|