@sonoma-security/mcp-gateway 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/__tests__/plugin-discovery.test.d.ts +12 -0
  2. package/dist/__tests__/plugin-discovery.test.d.ts.map +1 -0
  3. package/dist/__tests__/plugin-discovery.test.js +367 -0
  4. package/dist/__tests__/plugin-discovery.test.js.map +1 -0
  5. package/dist/__tests__/tool-blocking.test.d.ts +2 -0
  6. package/dist/__tests__/tool-blocking.test.d.ts.map +1 -0
  7. package/dist/__tests__/tool-blocking.test.js +256 -0
  8. package/dist/__tests__/tool-blocking.test.js.map +1 -0
  9. package/dist/auth/client.d.ts.map +1 -1
  10. package/dist/auth/client.js +49 -6
  11. package/dist/auth/client.js.map +1 -1
  12. package/dist/auth/keychain.d.ts +34 -0
  13. package/dist/auth/keychain.d.ts.map +1 -0
  14. package/dist/auth/keychain.js +305 -0
  15. package/dist/auth/keychain.js.map +1 -0
  16. package/dist/auth/server.d.ts +15 -3
  17. package/dist/auth/server.d.ts.map +1 -1
  18. package/dist/auth/server.js +110 -23
  19. package/dist/auth/server.js.map +1 -1
  20. package/dist/auth/storage.d.ts +5 -6
  21. package/dist/auth/storage.d.ts.map +1 -1
  22. package/dist/auth/storage.js +72 -21
  23. package/dist/auth/storage.js.map +1 -1
  24. package/dist/auth/upstream-oauth.d.ts +2 -0
  25. package/dist/auth/upstream-oauth.d.ts.map +1 -1
  26. package/dist/auth/upstream-oauth.js +58 -8
  27. package/dist/auth/upstream-oauth.js.map +1 -1
  28. package/dist/auth/upstream-token-store.d.ts +18 -6
  29. package/dist/auth/upstream-token-store.d.ts.map +1 -1
  30. package/dist/auth/upstream-token-store.js +127 -35
  31. package/dist/auth/upstream-token-store.js.map +1 -1
  32. package/dist/cli.js +16 -0
  33. package/dist/cli.js.map +1 -1
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/config.js +17 -7
  36. package/dist/config.js.map +1 -1
  37. package/dist/gateway.d.ts +17 -0
  38. package/dist/gateway.d.ts.map +1 -1
  39. package/dist/gateway.js +302 -66
  40. package/dist/gateway.js.map +1 -1
  41. package/dist/http-proxy.d.ts +76 -0
  42. package/dist/http-proxy.d.ts.map +1 -0
  43. package/dist/http-proxy.js +316 -0
  44. package/dist/http-proxy.js.map +1 -0
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/pattern-matcher.d.ts +25 -0
  50. package/dist/pattern-matcher.d.ts.map +1 -1
  51. package/dist/pattern-matcher.js +65 -0
  52. package/dist/pattern-matcher.js.map +1 -1
  53. package/dist/prompt-guard.d.ts +24 -0
  54. package/dist/prompt-guard.d.ts.map +1 -0
  55. package/dist/prompt-guard.js +161 -0
  56. package/dist/prompt-guard.js.map +1 -0
  57. package/dist/sonoma-client.d.ts +28 -1
  58. package/dist/sonoma-client.d.ts.map +1 -1
  59. package/dist/sonoma-client.js +67 -43
  60. package/dist/sonoma-client.js.map +1 -1
  61. package/dist/types.d.ts +6 -0
  62. package/dist/types.d.ts.map +1 -1
  63. package/package.json +1 -1
@@ -1,47 +1,98 @@
1
1
  /**
2
2
  * Secure token storage for MCP Gateway OAuth credentials
3
3
  *
4
- * Stores tokens in ~/.sonoma/gateway-credentials.json with AES-256-GCM encryption.
5
- * The encryption key is derived from a machine-specific identifier to prevent
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
- const CREDENTIALS_PATH = join(SONOMA_DIR, "gateway-credentials.json");
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 disk
15
+ * Load stored credentials from OS keychain (or legacy file).
14
16
  */
15
17
  export function loadCredentials() {
16
- if (!existsSync(CREDENTIALS_PATH)) {
17
- return null;
18
- }
19
- try {
20
- const encryptedData = readFileSync(CREDENTIALS_PATH, "utf-8");
21
- const decrypted = decrypt(encryptedData);
22
- return JSON.parse(decrypted);
23
- }
24
- catch (error) {
25
- // If decryption fails (e.g., machine changed), return null
26
- console.error("[auth] Failed to load credentials:", error);
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 disk (encrypted)
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(CREDENTIALS_PATH, encrypted, { mode: 0o600 });
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 (existsSync(CREDENTIALS_PATH)) {
44
- unlinkSync(CREDENTIALS_PATH);
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
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,8 @@ export interface AuthenticateUpstreamOptions {
20
20
  clientId: string;
21
21
  callbackPort?: number;
22
22
  };
23
+ /** Override the default callback port (19843). Used to give each server a unique port for parallel auth. */
24
+ callbackPort?: number;
23
25
  }
24
26
  /**
25
27
  * Authenticate with an upstream MCP server that requires OAuth.
@@ -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;CACH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAsEhC"}
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"}
@@ -33,7 +33,9 @@ export async function authenticateUpstream(options) {
33
33
  }
34
34
  };
35
35
  store.setServerName(serverUrl, serverName);
36
- const callbackPort = oauth?.callbackPort ?? UPSTREAM_CALLBACK_PORT;
36
+ // Pre-registered OAuth callbackPort takes priority because the redirect URI
37
+ // is fixed in the OAuth provider's app config (e.g., Slack only allows port 3118)
38
+ const callbackPort = oauth?.callbackPort ?? options.callbackPort ?? UPSTREAM_CALLBACK_PORT;
37
39
  const provider = new UpstreamOAuthProvider({
38
40
  serverUrl,
39
41
  store,
@@ -56,17 +58,49 @@ export async function authenticateUpstream(options) {
56
58
  throw new Error(`Auth flow returned REDIRECT but no authorization URL was captured for ${serverName}`);
57
59
  }
58
60
  const authUrl = provider.pendingAuthorizationUrl.toString();
61
+ // Try to start callback server (acquires the port atomically)
62
+ let handle;
63
+ try {
64
+ handle = await startCallbackServer({
65
+ port: callbackPort,
66
+ debug,
67
+ serverName,
68
+ });
69
+ }
70
+ catch (err) {
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).
75
+ if (err instanceof Error && err.message.includes("already in use")) {
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);
79
+ if (completed) {
80
+ log(`Upstream auth for ${serverName} completed by another process`);
81
+ const retryResult = await auth(provider, { serverUrl });
82
+ if (retryResult === "AUTHORIZED")
83
+ return provider;
84
+ }
85
+ log(`External auth did not complete for ${serverName}, retrying port...`);
86
+ handle = await startCallbackServer({
87
+ port: callbackPort,
88
+ debug,
89
+ serverName,
90
+ });
91
+ }
92
+ else {
93
+ throw err;
94
+ }
95
+ }
96
+ // Set auth URL on handle so the timeout page can offer a retry button
97
+ handle.setAuthUrl(authUrl);
98
+ // Port acquired: now safe to open browser (no duplicate tabs possible)
59
99
  console.error(`\nOpening browser to authenticate with ${serverName}...`);
60
100
  console.error(`If browser doesn't open, visit:\n${authUrl}\n`);
61
- // Start callback server and open browser
62
- const callbackPromise = startCallbackServer({
63
- port: callbackPort,
64
- debug,
65
- serverName,
66
- });
67
101
  await openBrowser(authUrl);
68
102
  // Wait for the authorization code
69
- const callbackResult = await callbackPromise;
103
+ const callbackResult = await handle.waitForCallback();
70
104
  // State validation is handled internally by the MCP SDK's auth() function.
71
105
  // The provider's codeVerifier storage implicitly binds the session.
72
106
  log(`Received authorization code from ${serverName}`);
@@ -81,4 +115,20 @@ export async function authenticateUpstream(options) {
81
115
  log(`Successfully authenticated with ${serverName}`);
82
116
  return provider;
83
117
  }
118
+ /**
119
+ * Wait for another gateway process to complete upstream OAuth.
120
+ * Polls the token store until tokens appear or timeout.
121
+ */
122
+ async function waitForUpstreamAuth(store, serverUrl, _port, _debug, timeoutMs = 5 * 60 * 1000) {
123
+ const start = Date.now();
124
+ const pollInterval = 2000;
125
+ while (Date.now() - start < timeoutMs) {
126
+ await new Promise((r) => setTimeout(r, pollInterval));
127
+ const tokens = store.getTokens(serverUrl);
128
+ if (tokens?.access_token) {
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ }
84
134
  //# sourceMappingURL=upstream-oauth.js.map
@@ -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;AAgBrC;;;;;;;;;;;;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,MAAM,YAAY,GAAG,KAAK,EAAE,YAAY,IAAI,sBAAsB,CAAC;IAEnE,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;IAC5D,OAAO,CAAC,KAAK,CAAC,0CAA0C,UAAU,KAAK,CAAC,CAAC;IACzE,OAAO,CAAC,KAAK,CAAC,oCAAoC,OAAO,IAAI,CAAC,CAAC;IAE/D,yCAAyC;IACzC,MAAM,eAAe,GAAG,mBAAmB,CAAC;QAC1C,IAAI,EAAE,YAAY;QAClB,KAAK;QACL,UAAU;KACX,CAAC,CAAC;IACH,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAE3B,kCAAkC;IAClC,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC;IAC7C,2EAA2E;IAC3E,oEAAoE;IACpE,GAAG,CAAC,oCAAoC,UAAU,EAAE,CAAC,CAAC;IAEtD,iEAAiE;IACjE,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE;QAC1C,SAAS;QACT,iBAAiB,EAAE,cAAc,CAAC,IAAI;KACvC,CAAC,CAAC;IAEH,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,wBAAwB,cAAc,GAAG,CAAC,CAAC;IACpG,CAAC;IAED,GAAG,CAAC,mCAAmC,UAAU,EAAE,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
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,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,sBAAsB;QACtB,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACnE,GAAG,CAAC,QAAQ,YAAY,eAAe,UAAU,wCAAwC,CAAC,CAAC;YAC3F,OAAO,CAAC,KAAK,CAAC,UAAU,YAAY,gCAAgC,UAAU,sBAAsB,CAAC,CAAC;YACtG,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YAC3F,IAAI,SAAS,EAAE,CAAC;gBACd,GAAG,CAAC,qBAAqB,UAAU,+BAA+B,CAAC,CAAC;gBACpE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;gBACxD,IAAI,WAAW,KAAK,YAAY;oBAAE,OAAO,QAAQ,CAAC;YACpD,CAAC;YACD,GAAG,CAAC,sCAAsC,UAAU,oBAAoB,CAAC,CAAC;YAC1E,MAAM,GAAG,MAAM,mBAAmB,CAAC;gBACjC,IAAI,EAAE,YAAY;gBAClB,KAAK;gBACL,UAAU;aACX,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAE3B,uEAAuE;IACvE,OAAO,CAAC,KAAK,CAAC,0CAA0C,UAAU,KAAK,CAAC,CAAC;IACzE,OAAO,CAAC,KAAK,CAAC,oCAAoC,OAAO,IAAI,CAAC,CAAC;IAE/D,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAE3B,kCAAkC;IAClC,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;IACtD,2EAA2E;IAC3E,oEAAoE;IACpE,GAAG,CAAC,oCAAoC,UAAU,EAAE,CAAC,CAAC;IAEtD,iEAAiE;IACjE,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE;QAC1C,SAAS;QACT,iBAAiB,EAAE,cAAc,CAAC,IAAI;KACvC,CAAC,CAAC;IAEH,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,wBAAwB,cAAc,GAAG,CAAC,CAAC;IACpG,CAAC;IAED,GAAG,CAAC,mCAAmC,UAAU,EAAE,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,mBAAmB,CAChC,KAAyB,EACzB,SAAiB,EACjB,KAAa,EACb,MAAe,EACf,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;IAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,YAAY,GAAG,IAAI,CAAC;IAE1B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;QACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE,YAAY,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -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 ~/.sonoma/upstream-credentials.json, encrypted with
5
- * the same AES-256-GCM scheme used for Sonoma gateway credentials.
6
- * Each server is keyed by its URL origin (e.g., "https://mcp.sentry.dev").
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 store;
11
- private load;
12
- private save;
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;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AAsBzG,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAwC;IAErD,OAAO,CAAC,IAAI;IAoBZ,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,SAAS;IASjB,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;IAOjC,mDAAmD;IACnD,eAAe,IAAI,IAAI;CAIxB"}
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 ~/.sonoma/upstream-credentials.json, encrypted with
5
- * the same AES-256-GCM scheme used for Sonoma gateway credentials.
6
- * Each server is keyed by its URL origin (e.g., "https://mcp.sentry.dev").
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
- const UPSTREAM_CREDENTIALS_PATH = join(SONOMA_DIR, "upstream-credentials.json");
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
- store = null;
18
- load() {
19
- if (this.store)
20
- return this.store;
21
- if (!existsSync(UPSTREAM_CREDENTIALS_PATH)) {
22
- this.store = { servers: {} };
23
- return this.store;
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(UPSTREAM_CREDENTIALS_PATH, "utf-8");
85
+ const encryptedData = readFileSync(LEGACY_CREDENTIALS_PATH, "utf-8");
27
86
  const decrypted = decrypt(encryptedData);
28
- this.store = JSON.parse(decrypted);
29
- return this.store;
87
+ this.fileStore = JSON.parse(decrypted);
88
+ return this.fileStore;
30
89
  }
31
90
  catch {
32
- // If decryption fails (e.g., machine changed), start fresh
33
- this.store = { servers: {} };
34
- return this.store;
91
+ this.fileStore = { servers: {} };
92
+ return this.fileStore;
35
93
  }
36
94
  }
37
- save() {
95
+ saveFile() {
38
96
  ensureSonomaDir();
39
- const data = JSON.stringify(this.load(), null, 2);
97
+ const data = JSON.stringify(this.loadFile(), null, 2);
40
98
  const encrypted = encrypt(data);
41
- writeFileSync(UPSTREAM_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
99
+ writeFileSync(LEGACY_CREDENTIALS_PATH, encrypted, { mode: 0o600 });
42
100
  }
43
- getServer(serverUrl) {
44
- const store = this.load();
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.save();
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.save();
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.save();
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.save();
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.save();
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.save();
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
- const store = this.load();
96
- const key = getServerKey(serverUrl);
97
- delete store.servers[key];
98
- this.save();
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.store = { servers: {} };
103
- this.save();
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":"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"}
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
@@ -123,6 +123,7 @@ async function main() {
123
123
  status: { type: "boolean" },
124
124
  reauth: { type: "boolean" },
125
125
  endpoint: { type: "string", short: "e" },
126
+ "http-proxy-port": { type: "string" },
126
127
  },
127
128
  strict: true,
128
129
  });
@@ -362,6 +363,16 @@ async function main() {
362
363
  }
363
364
  }
364
365
  }
366
+ // HTTP proxy mode: expose per-server HTTP endpoints for plugin URL rewriting
367
+ if (values["http-proxy-port"]) {
368
+ const port = parseInt(values["http-proxy-port"], 10);
369
+ if (Number.isNaN(port) || port < 1 || port > 65535) {
370
+ console.error(`Invalid HTTP proxy port: ${values["http-proxy-port"]}`);
371
+ process.exit(2);
372
+ }
373
+ config.httpProxyPort = port;
374
+ console.error(`[cli] HTTP proxy mode enabled on port ${port}`);
375
+ }
365
376
  const gateway = new McpGateway(config);
366
377
  // Handle shutdown gracefully
367
378
  const shutdown = async () => {
@@ -408,6 +419,11 @@ AUTH OPTIONS:
408
419
  --reauth Clear upstream OAuth tokens and re-authenticate
409
420
  -e, --endpoint <url> Sonoma API endpoint (default: https://app.sonoma.dev)
410
421
 
422
+ PROXY OPTIONS:
423
+ --http-proxy-port <port> Expose per-server HTTP endpoints for plugin proxying.
424
+ Each server is available at http://localhost:{port}/proxy/{name}/mcp.
425
+ Plugins can rewrite URLs to these endpoints to keep skills active.
426
+
411
427
  QUICKSTART (zero-config):
412
428
  Add to your ~/.cursor/mcp.json or Claude Desktop config:
413
429
  {