@sonoma-security/mcp-gateway 0.1.12 → 0.1.14
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/__tests__/config.test.js +140 -2
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/plugin-discovery.test.d.ts +12 -0
- package/dist/__tests__/plugin-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/plugin-discovery.test.js +367 -0
- package/dist/__tests__/plugin-discovery.test.js.map +1 -0
- package/dist/__tests__/tool-blocking.test.d.ts +2 -0
- package/dist/__tests__/tool-blocking.test.d.ts.map +1 -0
- package/dist/__tests__/tool-blocking.test.js +256 -0
- package/dist/__tests__/tool-blocking.test.js.map +1 -0
- package/dist/auth/keychain.d.ts +34 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +305 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/storage.d.ts +5 -6
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +72 -21
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/upstream-oauth.d.ts.map +1 -1
- package/dist/auth/upstream-oauth.js +8 -5
- package/dist/auth/upstream-oauth.js.map +1 -1
- package/dist/auth/upstream-token-store.d.ts +18 -6
- package/dist/auth/upstream-token-store.d.ts.map +1 -1
- package/dist/auth/upstream-token-store.js +127 -35
- package/dist/auth/upstream-token-store.js.map +1 -1
- package/dist/cli.js +48 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +122 -27
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +15 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +302 -68
- package/dist/gateway.js.map +1 -1
- package/dist/http-proxy.d.ts +126 -0
- package/dist/http-proxy.d.ts.map +1 -0
- package/dist/http-proxy.js +875 -0
- package/dist/http-proxy.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pattern-matcher.d.ts +25 -0
- package/dist/pattern-matcher.d.ts.map +1 -1
- package/dist/pattern-matcher.js +65 -0
- package/dist/pattern-matcher.js.map +1 -1
- package/dist/sonoma-client.d.ts +17 -1
- package/dist/sonoma-client.d.ts.map +1 -1
- package/dist/sonoma-client.js +67 -43
- package/dist/sonoma-client.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/auth/storage.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secure token storage for MCP Gateway OAuth credentials
|
|
3
3
|
*
|
|
4
|
-
* Stores tokens in
|
|
5
|
-
*
|
|
6
|
-
* tokens from being copied between machines.
|
|
4
|
+
* Stores tokens in the OS keychain (macOS Keychain, Windows Credential Manager).
|
|
5
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
6
|
*/
|
|
8
7
|
import type { OAuthTokens, OAuthClientInformationMixed } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
8
|
export interface StoredCredentials {
|
|
@@ -20,15 +19,15 @@ export interface StoredCredentials {
|
|
|
20
19
|
lastRefreshed?: number;
|
|
21
20
|
}
|
|
22
21
|
/**
|
|
23
|
-
* Load stored credentials from
|
|
22
|
+
* Load stored credentials from OS keychain (or legacy file).
|
|
24
23
|
*/
|
|
25
24
|
export declare function loadCredentials(): StoredCredentials | null;
|
|
26
25
|
/**
|
|
27
|
-
* Save credentials to
|
|
26
|
+
* Save credentials to OS keychain (or fallback file).
|
|
28
27
|
*/
|
|
29
28
|
export declare function saveCredentials(credentials: StoredCredentials): void;
|
|
30
29
|
/**
|
|
31
|
-
* Clear all stored credentials
|
|
30
|
+
* Clear all stored credentials.
|
|
32
31
|
*/
|
|
33
32
|
export declare function clearCredentials(): void;
|
|
34
33
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AAQzG,MAAM,WAAW,iBAAiB;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CA4B1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAgBpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAQvC;AAkBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAY/E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAYpG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,2BAA2B,GAAG,SAAS,CAUrG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,SAAQ,GAAG,OAAO,CAMlF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
package/dist/auth/storage.js
CHANGED
|
@@ -1,47 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secure token storage for MCP Gateway OAuth credentials
|
|
3
3
|
*
|
|
4
|
-
* Stores tokens in
|
|
5
|
-
*
|
|
6
|
-
* tokens from being copied between machines.
|
|
4
|
+
* Stores tokens in the OS keychain (macOS Keychain, Windows Credential Manager).
|
|
5
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
6
|
*/
|
|
8
7
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
8
|
import { join } from "node:path";
|
|
10
9
|
import { SONOMA_DIR, ensureSonomaDir, getDeviceId, encrypt, decrypt } from "./crypto.js";
|
|
11
|
-
|
|
10
|
+
import { keychainAvailable, keychainGet, keychainSet, keychainDelete } from "./keychain.js";
|
|
11
|
+
const KEYCHAIN_ACCOUNT = "sonoma-auth";
|
|
12
|
+
// Legacy file path (for migration)
|
|
13
|
+
const LEGACY_CREDENTIALS_PATH = join(SONOMA_DIR, "gateway-credentials.json");
|
|
12
14
|
/**
|
|
13
|
-
* Load stored credentials from
|
|
15
|
+
* Load stored credentials from OS keychain (or legacy file).
|
|
14
16
|
*/
|
|
15
17
|
export function loadCredentials() {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// Try OS keychain first
|
|
19
|
+
if (keychainAvailable()) {
|
|
20
|
+
try {
|
|
21
|
+
const creds = keychainGet(KEYCHAIN_ACCOUNT);
|
|
22
|
+
if (creds)
|
|
23
|
+
return creds;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Keychain access failed, try legacy
|
|
27
|
+
}
|
|
28
|
+
// Migrate from legacy encrypted file if it exists
|
|
29
|
+
const legacy = loadLegacyCredentials();
|
|
30
|
+
if (legacy) {
|
|
31
|
+
try {
|
|
32
|
+
keychainSet(KEYCHAIN_ACCOUNT, legacy);
|
|
33
|
+
// Clean up legacy file after successful migration
|
|
34
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Migration failed, return legacy credentials anyway
|
|
38
|
+
}
|
|
39
|
+
return legacy;
|
|
40
|
+
}
|
|
27
41
|
return null;
|
|
28
42
|
}
|
|
43
|
+
// Fallback: encrypted file (Linux, etc.)
|
|
44
|
+
return loadLegacyCredentials();
|
|
29
45
|
}
|
|
30
46
|
/**
|
|
31
|
-
* Save credentials to
|
|
47
|
+
* Save credentials to OS keychain (or fallback file).
|
|
32
48
|
*/
|
|
33
49
|
export function saveCredentials(credentials) {
|
|
50
|
+
if (keychainAvailable()) {
|
|
51
|
+
keychainSet(KEYCHAIN_ACCOUNT, credentials);
|
|
52
|
+
// Clean up legacy file if it exists
|
|
53
|
+
if (existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
54
|
+
try {
|
|
55
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
56
|
+
}
|
|
57
|
+
catch { /* ignore */ }
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Fallback: encrypted file
|
|
34
62
|
ensureSonomaDir();
|
|
35
63
|
const data = JSON.stringify(credentials, null, 2);
|
|
36
64
|
const encrypted = encrypt(data);
|
|
37
|
-
writeFileSync(
|
|
65
|
+
writeFileSync(LEGACY_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
|
|
38
66
|
}
|
|
39
67
|
/**
|
|
40
|
-
* Clear all stored credentials
|
|
68
|
+
* Clear all stored credentials.
|
|
41
69
|
*/
|
|
42
70
|
export function clearCredentials() {
|
|
43
|
-
if (
|
|
44
|
-
|
|
71
|
+
if (keychainAvailable()) {
|
|
72
|
+
try {
|
|
73
|
+
keychainDelete(KEYCHAIN_ACCOUNT);
|
|
74
|
+
}
|
|
75
|
+
catch { /* ignore */ }
|
|
76
|
+
}
|
|
77
|
+
// Always clean up legacy file too
|
|
78
|
+
if (existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
79
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load from legacy encrypted file (pre-keychain migration).
|
|
84
|
+
*/
|
|
85
|
+
function loadLegacyCredentials() {
|
|
86
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
91
|
+
const decrypted = decrypt(encryptedData);
|
|
92
|
+
return JSON.parse(decrypted);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
45
96
|
}
|
|
46
97
|
}
|
|
47
98
|
/**
|
package/dist/auth/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAMpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8GAA8G;IAC9G,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"upstream-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAMpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8GAA8G;IAC9G,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAwGhC"}
|
|
@@ -68,18 +68,21 @@ export async function authenticateUpstream(options) {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
catch (err) {
|
|
71
|
-
// Port taken: another gateway
|
|
71
|
+
// Port taken: another gateway may be authenticating this server, or a
|
|
72
|
+
// stale process is holding the port. Wait briefly (15s) for tokens to
|
|
73
|
+
// appear; if not, try to take the port anyway (the stale process may
|
|
74
|
+
// have died by then).
|
|
72
75
|
if (err instanceof Error && err.message.includes("already in use")) {
|
|
73
|
-
log(`
|
|
74
|
-
console.error(`\
|
|
75
|
-
const completed = await waitForUpstreamAuth(store, serverUrl, callbackPort, debug);
|
|
76
|
+
log(`Port ${callbackPort} in use for ${serverName}, waiting briefly for external auth...`);
|
|
77
|
+
console.error(`\nPort ${callbackPort} in use. Waiting briefly for ${serverName} auth to complete...`);
|
|
78
|
+
const completed = await waitForUpstreamAuth(store, serverUrl, callbackPort, debug, 15_000);
|
|
76
79
|
if (completed) {
|
|
77
80
|
log(`Upstream auth for ${serverName} completed by another process`);
|
|
78
81
|
const retryResult = await auth(provider, { serverUrl });
|
|
79
82
|
if (retryResult === "AUTHORIZED")
|
|
80
83
|
return provider;
|
|
81
84
|
}
|
|
82
|
-
log(`External
|
|
85
|
+
log(`External auth did not complete for ${serverName}, retrying port...`);
|
|
83
86
|
handle = await startCallbackServer({
|
|
84
87
|
port: callbackPort,
|
|
85
88
|
debug,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-oauth.js","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,0CAA0C,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAkBrC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAoC;IAEpC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,eAAe,GAAG,KAAK,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAEhG,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE;QAC1B,IAAI,KAAK,EAAE,CAAC;YACV,uDAAuD;YACvD,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAC,CAAC,iCAAiC;QAC7E,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAE3C,4EAA4E;IAC5E,kFAAkF;IAClF,MAAM,YAAY,GAAG,KAAK,EAAE,YAAY,IAAI,OAAO,CAAC,YAAY,IAAI,sBAAsB,CAAC;IAE3F,MAAM,QAAQ,GAAG,IAAI,qBAAqB,CAAC;QACzC,SAAS;QACT,KAAK;QACL,YAAY;QACZ,KAAK;QACL,qBAAqB,EAAE,KAAK,EAAE,QAAQ;KACvC,CAAC,CAAC;IAEH,kFAAkF;IAClF,GAAG,CAAC,uBAAuB,UAAU,KAAK,SAAS,MAAM,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAEnD,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC5B,GAAG,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;QAC7C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,iEAAiE;IACjE,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,kCAAkC,UAAU,+CAA+C,CAAC,CAAC;IAC/G,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,uBAAuB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,yEAAyE,UAAU,EAAE,CAAC,CAAC;IACzG,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,uBAAuB,CAAC,QAAQ,EAAE,CAAC;IAE5D,8DAA8D;IAC9D,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,mBAAmB,CAAC;YACjC,IAAI,EAAE,YAAY;YAClB,KAAK;YACL,UAAU;SACX,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,
|
|
1
|
+
{"version":3,"file":"upstream-oauth.js","sourceRoot":"","sources":["../../src/auth/upstream-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-server token storage for upstream MCP servers that require OAuth.
|
|
3
3
|
*
|
|
4
|
-
* Stores credentials in
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Stores credentials in the OS keychain (macOS Keychain, Windows Credential Manager),
|
|
5
|
+
* keyed by server URL origin (e.g., "https://mcp.slack.com").
|
|
6
|
+
*
|
|
7
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
8
|
*/
|
|
8
9
|
import type { OAuthTokens, OAuthClientInformationMixed } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
10
|
export declare class UpstreamTokenStore {
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private
|
|
11
|
+
private useKeychain;
|
|
12
|
+
private fileStore;
|
|
13
|
+
private migrated;
|
|
14
|
+
constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Migrate credentials from legacy encrypted file to OS keychain.
|
|
17
|
+
*/
|
|
18
|
+
private migrateLegacy;
|
|
19
|
+
private getServerFromKeychain;
|
|
20
|
+
private saveServerToKeychain;
|
|
21
|
+
private loadFile;
|
|
22
|
+
private saveFile;
|
|
23
|
+
private getServerFromFile;
|
|
13
24
|
private getServer;
|
|
25
|
+
private saveServer;
|
|
14
26
|
getTokens(serverUrl: string): OAuthTokens | undefined;
|
|
15
27
|
saveTokens(serverUrl: string, tokens: OAuthTokens): void;
|
|
16
28
|
clearTokens(serverUrl: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-token-store.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"upstream-token-store.d.ts","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AA4BzG,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAU;IAE7B,OAAO,CAAC,SAAS,CAAwC;IAEzD,OAAO,CAAC,QAAQ,CAAS;;IAYzB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAWlB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIrD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAOxD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMpC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS;IAIzE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,GAAG,IAAI;IAM1E,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMxC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAItD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM3D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMpD,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAYjC,mDAAmD;IACnD,eAAe,IAAI,IAAI;CAQxB"}
|
|
@@ -1,53 +1,131 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-server token storage for upstream MCP servers that require OAuth.
|
|
3
3
|
*
|
|
4
|
-
* Stores credentials in
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Stores credentials in the OS keychain (macOS Keychain, Windows Credential Manager),
|
|
5
|
+
* keyed by server URL origin (e.g., "https://mcp.slack.com").
|
|
6
|
+
*
|
|
7
|
+
* Falls back to encrypted file storage on unsupported platforms.
|
|
7
8
|
*/
|
|
8
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
import { SONOMA_DIR, ensureSonomaDir, encrypt, decrypt } from "./crypto.js";
|
|
11
|
-
|
|
12
|
+
import { keychainAvailable, keychainGet, keychainSet, keychainDelete, keychainDeleteAll } from "./keychain.js";
|
|
13
|
+
// Legacy file path (for migration from encrypted file)
|
|
14
|
+
const LEGACY_CREDENTIALS_PATH = join(SONOMA_DIR, "upstream-credentials.json");
|
|
12
15
|
function getServerKey(serverUrl) {
|
|
13
16
|
const url = new URL(serverUrl);
|
|
14
17
|
return url.origin;
|
|
15
18
|
}
|
|
19
|
+
function keychainAccount(serverKey) {
|
|
20
|
+
return `upstream:${serverKey}`;
|
|
21
|
+
}
|
|
16
22
|
export class UpstreamTokenStore {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
useKeychain;
|
|
24
|
+
// In-memory cache for file-based fallback only
|
|
25
|
+
fileStore = null;
|
|
26
|
+
// Track whether legacy migration has been attempted
|
|
27
|
+
migrated = false;
|
|
28
|
+
constructor() {
|
|
29
|
+
this.useKeychain = keychainAvailable();
|
|
30
|
+
// Migrate legacy file to keychain on first use
|
|
31
|
+
if (this.useKeychain && !this.migrated) {
|
|
32
|
+
this.migrateLegacy();
|
|
33
|
+
this.migrated = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Migrate credentials from legacy encrypted file to OS keychain.
|
|
38
|
+
*/
|
|
39
|
+
migrateLegacy() {
|
|
40
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH))
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
44
|
+
const decrypted = decrypt(encryptedData);
|
|
45
|
+
const store = JSON.parse(decrypted);
|
|
46
|
+
if (store.servers) {
|
|
47
|
+
for (const [key, creds] of Object.entries(store.servers)) {
|
|
48
|
+
try {
|
|
49
|
+
keychainSet(keychainAccount(key), creds);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Skip entries that fail to migrate
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Clean up legacy file after successful migration
|
|
57
|
+
unlinkSync(LEGACY_CREDENTIALS_PATH);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Migration failed, legacy file may be corrupt
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Keychain-backed operations
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
getServerFromKeychain(serverUrl) {
|
|
67
|
+
const key = getServerKey(serverUrl);
|
|
68
|
+
return keychainGet(keychainAccount(key)) ?? {};
|
|
69
|
+
}
|
|
70
|
+
saveServerToKeychain(serverUrl, creds) {
|
|
71
|
+
const key = getServerKey(serverUrl);
|
|
72
|
+
keychainSet(keychainAccount(key), creds);
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// File-backed fallback (Linux, etc.)
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
loadFile() {
|
|
78
|
+
if (this.fileStore)
|
|
79
|
+
return this.fileStore;
|
|
80
|
+
if (!existsSync(LEGACY_CREDENTIALS_PATH)) {
|
|
81
|
+
this.fileStore = { servers: {} };
|
|
82
|
+
return this.fileStore;
|
|
24
83
|
}
|
|
25
84
|
try {
|
|
26
|
-
const encryptedData = readFileSync(
|
|
85
|
+
const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
|
|
27
86
|
const decrypted = decrypt(encryptedData);
|
|
28
|
-
this.
|
|
29
|
-
return this.
|
|
87
|
+
this.fileStore = JSON.parse(decrypted);
|
|
88
|
+
return this.fileStore;
|
|
30
89
|
}
|
|
31
90
|
catch {
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
return this.store;
|
|
91
|
+
this.fileStore = { servers: {} };
|
|
92
|
+
return this.fileStore;
|
|
35
93
|
}
|
|
36
94
|
}
|
|
37
|
-
|
|
95
|
+
saveFile() {
|
|
38
96
|
ensureSonomaDir();
|
|
39
|
-
const data = JSON.stringify(this.
|
|
97
|
+
const data = JSON.stringify(this.loadFile(), null, 2);
|
|
40
98
|
const encrypted = encrypt(data);
|
|
41
|
-
writeFileSync(
|
|
99
|
+
writeFileSync(LEGACY_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
|
|
42
100
|
}
|
|
43
|
-
|
|
44
|
-
const store = this.
|
|
101
|
+
getServerFromFile(serverUrl) {
|
|
102
|
+
const store = this.loadFile();
|
|
45
103
|
const key = getServerKey(serverUrl);
|
|
46
104
|
if (!store.servers[key]) {
|
|
47
105
|
store.servers[key] = {};
|
|
48
106
|
}
|
|
49
107
|
return store.servers[key];
|
|
50
108
|
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Public API (delegates to keychain or file based on platform)
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
getServer(serverUrl) {
|
|
113
|
+
if (this.useKeychain) {
|
|
114
|
+
return this.getServerFromKeychain(serverUrl);
|
|
115
|
+
}
|
|
116
|
+
return this.getServerFromFile(serverUrl);
|
|
117
|
+
}
|
|
118
|
+
saveServer(serverUrl, creds) {
|
|
119
|
+
if (this.useKeychain) {
|
|
120
|
+
this.saveServerToKeychain(serverUrl, creds);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const store = this.loadFile();
|
|
124
|
+
const key = getServerKey(serverUrl);
|
|
125
|
+
store.servers[key] = creds;
|
|
126
|
+
this.saveFile();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
51
129
|
getTokens(serverUrl) {
|
|
52
130
|
return this.getServer(serverUrl).tokens;
|
|
53
131
|
}
|
|
@@ -55,12 +133,12 @@ export class UpstreamTokenStore {
|
|
|
55
133
|
const server = this.getServer(serverUrl);
|
|
56
134
|
server.tokens = tokens;
|
|
57
135
|
server.lastAuthenticated = Date.now();
|
|
58
|
-
this.
|
|
136
|
+
this.saveServer(serverUrl, server);
|
|
59
137
|
}
|
|
60
138
|
clearTokens(serverUrl) {
|
|
61
139
|
const server = this.getServer(serverUrl);
|
|
62
140
|
delete server.tokens;
|
|
63
|
-
this.
|
|
141
|
+
this.saveServer(serverUrl, server);
|
|
64
142
|
}
|
|
65
143
|
getClientInfo(serverUrl) {
|
|
66
144
|
return this.getServer(serverUrl).clientInfo;
|
|
@@ -68,12 +146,12 @@ export class UpstreamTokenStore {
|
|
|
68
146
|
saveClientInfo(serverUrl, info) {
|
|
69
147
|
const server = this.getServer(serverUrl);
|
|
70
148
|
server.clientInfo = info;
|
|
71
|
-
this.
|
|
149
|
+
this.saveServer(serverUrl, server);
|
|
72
150
|
}
|
|
73
151
|
clearClientInfo(serverUrl) {
|
|
74
152
|
const server = this.getServer(serverUrl);
|
|
75
153
|
delete server.clientInfo;
|
|
76
|
-
this.
|
|
154
|
+
this.saveServer(serverUrl, server);
|
|
77
155
|
}
|
|
78
156
|
getCodeVerifier(serverUrl) {
|
|
79
157
|
return this.getServer(serverUrl).codeVerifier;
|
|
@@ -81,26 +159,40 @@ export class UpstreamTokenStore {
|
|
|
81
159
|
saveCodeVerifier(serverUrl, verifier) {
|
|
82
160
|
const server = this.getServer(serverUrl);
|
|
83
161
|
server.codeVerifier = verifier;
|
|
84
|
-
this.
|
|
162
|
+
this.saveServer(serverUrl, server);
|
|
85
163
|
}
|
|
86
164
|
setServerName(serverUrl, name) {
|
|
87
165
|
const server = this.getServer(serverUrl);
|
|
88
166
|
server.serverName = name;
|
|
89
|
-
this.
|
|
167
|
+
this.saveServer(serverUrl, server);
|
|
90
168
|
}
|
|
91
169
|
getLastAuthenticated(serverUrl) {
|
|
92
170
|
return this.getServer(serverUrl).lastAuthenticated;
|
|
93
171
|
}
|
|
94
172
|
clearAll(serverUrl) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
173
|
+
if (this.useKeychain) {
|
|
174
|
+
const key = getServerKey(serverUrl);
|
|
175
|
+
try {
|
|
176
|
+
keychainDelete(keychainAccount(key));
|
|
177
|
+
}
|
|
178
|
+
catch { /* ignore */ }
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const store = this.loadFile();
|
|
182
|
+
const key = getServerKey(serverUrl);
|
|
183
|
+
delete store.servers[key];
|
|
184
|
+
this.saveFile();
|
|
185
|
+
}
|
|
99
186
|
}
|
|
100
187
|
/** Clear credentials for every upstream server. */
|
|
101
188
|
clearAllServers() {
|
|
102
|
-
this.
|
|
103
|
-
|
|
189
|
+
if (this.useKeychain) {
|
|
190
|
+
keychainDeleteAll();
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.fileStore = { servers: {} };
|
|
194
|
+
this.saveFile();
|
|
195
|
+
}
|
|
104
196
|
}
|
|
105
197
|
}
|
|
106
198
|
//# sourceMappingURL=upstream-token-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstream-token-store.js","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"upstream-token-store.js","sourceRoot":"","sources":["../../src/auth/upstream-token-store.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { parseArgs } from "node:util";
|
|
11
11
|
import { readFileSync, existsSync } from "node:fs";
|
|
12
12
|
import { McpGateway } from "./gateway.js";
|
|
13
|
+
import { preListenHttpServer } from "./http-proxy.js";
|
|
13
14
|
import { loadConfig, findClaudeDesktopConfig, loadFromParentConfig, autoDetectConfig, discoverClaudeCodePlugins } from "./config.js";
|
|
14
15
|
import { login, logout, getAuthStatus, ensureValidToken, getAccessToken, UpstreamTokenStore } from "./auth/index.js";
|
|
15
16
|
import { SonomaClient } from "./sonoma-client.js";
|
|
@@ -123,6 +124,7 @@ async function main() {
|
|
|
123
124
|
status: { type: "boolean" },
|
|
124
125
|
reauth: { type: "boolean" },
|
|
125
126
|
endpoint: { type: "string", short: "e" },
|
|
127
|
+
"http-proxy-port": { type: "string" },
|
|
126
128
|
},
|
|
127
129
|
strict: true,
|
|
128
130
|
});
|
|
@@ -134,6 +136,29 @@ async function main() {
|
|
|
134
136
|
console.error("@sonoma/mcp-gateway v0.1.0");
|
|
135
137
|
process.exit(0);
|
|
136
138
|
}
|
|
139
|
+
// Claim the HTTP proxy port IMMEDIATELY, before any async startup work (auth,
|
|
140
|
+
// policy fetch, upstream connects). Claude Code probes proxy URLs in
|
|
141
|
+
// parallel with spawning the gateway stdio process, and a probe that hits
|
|
142
|
+
// ECONNREFUSED is treated as a permanent failure with no retry. A prebound
|
|
143
|
+
// socket returns 503 until the gateway takes over, which keeps the probe
|
|
144
|
+
// happy long enough for the real handler to attach.
|
|
145
|
+
let preboundProxy;
|
|
146
|
+
const proxyPortStr = values["http-proxy-port"];
|
|
147
|
+
if (proxyPortStr) {
|
|
148
|
+
const port = parseInt(proxyPortStr, 10);
|
|
149
|
+
if (!Number.isNaN(port) && port >= 1 && port <= 65535) {
|
|
150
|
+
try {
|
|
151
|
+
preboundProxy = preListenHttpServer(port);
|
|
152
|
+
await preboundProxy.listening;
|
|
153
|
+
console.error(`[cli] HTTP proxy port ${port} claimed (awaiting gateway handler)`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
157
|
+
console.error(`[cli] Failed to claim HTTP proxy port ${port}: ${message}`);
|
|
158
|
+
preboundProxy = undefined;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
137
162
|
// Auth commands
|
|
138
163
|
const sonomaEndpoint = values.endpoint || DEFAULT_SONOMA_ENDPOINT;
|
|
139
164
|
if (values.login) {
|
|
@@ -323,8 +348,11 @@ async function main() {
|
|
|
323
348
|
}
|
|
324
349
|
}
|
|
325
350
|
}
|
|
326
|
-
// Inject implicit Sonoma MCP server so users don't need a separate config entry
|
|
327
|
-
|
|
351
|
+
// Inject implicit Sonoma MCP server so users don't need a separate config entry.
|
|
352
|
+
// Skip in --http-proxy-port mode: that mode runs as a headless daemon (e.g.
|
|
353
|
+
// LaunchAgent) where OAuth browser flows are impossible and org API keys are
|
|
354
|
+
// rejected by /api/mcp — the implicit server would crash-loop on auth.
|
|
355
|
+
if (config.sonomaEndpoint && !values["http-proxy-port"]) {
|
|
328
356
|
const hasAuth = authStatus.loggedIn || !!config.sonomaApiKey;
|
|
329
357
|
const hasSonomaServer = config.servers.some((s) => s.name === "sonoma");
|
|
330
358
|
if (hasAuth && !hasSonomaServer) {
|
|
@@ -362,6 +390,19 @@ async function main() {
|
|
|
362
390
|
}
|
|
363
391
|
}
|
|
364
392
|
}
|
|
393
|
+
// HTTP proxy mode: expose per-server HTTP endpoints for plugin URL rewriting
|
|
394
|
+
if (values["http-proxy-port"]) {
|
|
395
|
+
const port = parseInt(values["http-proxy-port"], 10);
|
|
396
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
397
|
+
console.error(`Invalid HTTP proxy port: ${values["http-proxy-port"]}`);
|
|
398
|
+
process.exit(2);
|
|
399
|
+
}
|
|
400
|
+
config.httpProxyPort = port;
|
|
401
|
+
if (preboundProxy) {
|
|
402
|
+
config.httpProxyServer = preboundProxy.server;
|
|
403
|
+
}
|
|
404
|
+
console.error(`[cli] HTTP proxy mode enabled on port ${port}`);
|
|
405
|
+
}
|
|
365
406
|
const gateway = new McpGateway(config);
|
|
366
407
|
// Handle shutdown gracefully
|
|
367
408
|
const shutdown = async () => {
|
|
@@ -408,6 +449,11 @@ AUTH OPTIONS:
|
|
|
408
449
|
--reauth Clear upstream OAuth tokens and re-authenticate
|
|
409
450
|
-e, --endpoint <url> Sonoma API endpoint (default: https://app.sonoma.dev)
|
|
410
451
|
|
|
452
|
+
PROXY OPTIONS:
|
|
453
|
+
--http-proxy-port <port> Expose per-server HTTP endpoints for plugin proxying.
|
|
454
|
+
Each server is available at http://localhost:{port}/proxy/{name}/mcp.
|
|
455
|
+
Plugins can rewrite URLs to these endpoints to keep skills active.
|
|
456
|
+
|
|
411
457
|
QUICKSTART (zero-config):
|
|
412
458
|
Add to your ~/.cursor/mcp.json or Claude Desktop config:
|
|
413
459
|
{
|