@xsreality/mcp-gateway 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Use it to connect stdio-only MCP clients (Claude Desktop, IDE MCP integrations,
9
9
  MCP server as a subprocess) to remote, OAuth-protected MCP servers that those clients can't reach directly.
10
10
 
11
11
  ```
12
- ┌──────────────┐ stdio ┌──────────────┐ Streamable HTTP + OAuth ┌──────────────┐
12
+ ┌──────────────┐ stdio ┌──────────────┐ Streamable HTTP + OAuth ┌──────────────┐
13
13
  │ MCP client │ ─────────▶ │ mcp-gateway │ ─────────────────────────▶ │ Remote MCP │
14
14
  │ (Claude etc.)│ ◀───────── │ │ ◀───────────────────────── │ server │
15
15
  └──────────────┘ └──────────────┘ └──────────────┘
@@ -83,7 +83,8 @@ Point your client at the `mcp-gateway` command:
83
83
  | `--no-dcr` | Disable Dynamic Client Registration (requires `--client-id`) | DCR enabled |
84
84
  | `--callback-port <port>` | Fixed loopback port for the OAuth redirect | auto (persisted) |
85
85
  | `--auth-timeout <seconds>` | How long to wait for you to finish authorizing in the browser | `300` |
86
- | `--token-store <dir>` | Where tokens + client registration are stored | `~/.mcp-gateway` |
86
+ | `--credential-store <mode>` | Where credentials persist: `keychain` (OS keychain), `file` (on-disk), or `auto` | `auto` (`$MCP_GATEWAY_CREDENTIAL_STORE`) |
87
+ | `--token-store <dir>` | Directory for `file`-stored tokens + client registration | `~/.mcp-gateway` |
87
88
  | `--no-browser` | Print the authorization URL instead of opening a browser (headless) | opens browser |
88
89
  | `--log-level <level>` | `trace` `debug` `info` `warn` `error` `silent` (stderr/file only) | `info` |
89
90
  | `--log-file <path>` | Write logs to a file instead of stderr | stderr |
@@ -105,9 +106,14 @@ config blocks.
105
106
 
106
107
  ### Where credentials live
107
108
 
108
- Tokens, the registered client, and the chosen callback port are stored as one JSON file per server (keyed by
109
- the server's canonical URL) under `--token-store` (default `~/.mcp-gateway`), written with `0600` permissions.
110
- Delete that directory to force re-authorization.
109
+ Tokens, the registered client, and the chosen callback port are stored per server, keyed by the server's
110
+ canonical URL. By default (`--credential-store auto`) they go into the **OS keychain** (macOS Keychain,
111
+ Windows Credential Manager, Linux Secret Service) under the service name `mcp-gateway`, falling back to
112
+ on-disk JSON when no keychain is reachable (headless Linux, CI). The first time the keychain is used, any
113
+ existing `~/.mcp-gateway` file for that server is migrated into it and the plaintext file deleted.
114
+
115
+ Force a backend with `--credential-store keychain` or `--credential-store file`. In `file` mode the blob is
116
+ one JSON file per server under `--token-store` (default `~/.mcp-gateway`), written `0600`.
111
117
 
112
118
  ## Logging
113
119
 
@@ -119,7 +125,8 @@ a file.
119
125
 
120
126
  - **Browser didn't open** — copy the URL printed on stderr, or run with `--no-browser`.
121
127
  - **`authorization timed out`** — you didn't finish within `--auth-timeout`; just reconnect to retry.
122
- - **Re-authorize from scratch** — delete the server's file under `~/.mcp-gateway` (or the whole directory).
128
+ - **Re-authorize from scratch** — in `file` mode delete the server's file under `~/.mcp-gateway`; in keychain
129
+ mode delete the `mcp-gateway` entry for that server URL from your OS keychain.
123
130
  - **Corporate proxy / extra auth** — forward static headers with repeated `--header "Key: value"`.
124
131
  - **Stuck after the server changed its auth** — clear the token store; cached discovery/registration may be stale.
125
132
 
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { ConfigError, defaultTokenStoreDir, parseHeaders, parseUrl, } from "./co
4
4
  import { createLogger } from "./log.js";
5
5
  import { Gateway } from "./gateway.js";
6
6
  const LOG_LEVELS = ["trace", "debug", "info", "warn", "error", "silent"];
7
+ const CREDENTIAL_STORES = ["auto", "keychain", "file"];
7
8
  function collect(value, previous) {
8
9
  return [...previous, value];
9
10
  }
@@ -29,7 +30,10 @@ function buildProgram() {
29
30
  .option("--no-dcr", "Disable Dynamic Client Registration")
30
31
  .option("--callback-port <port>", "Fixed loopback OAuth callback port", intArg("--callback-port"))
31
32
  .option("--auth-timeout <seconds>", "Max wait for browser authorization", intArg("--auth-timeout"), 300)
32
- .option("--token-store <dir>", "Directory for tokens + client registration", defaultTokenStoreDir())
33
+ .addOption(new Option("--credential-store <mode>", "Where to persist credentials: keychain (OS keychain), file (on-disk), or auto")
34
+ .choices(CREDENTIAL_STORES)
35
+ .default(process.env.MCP_GATEWAY_CREDENTIAL_STORE ?? "auto"))
36
+ .option("--token-store <dir>", "Directory for file-stored tokens + client registration", defaultTokenStoreDir())
33
37
  .option("--no-browser", "Print the authorization URL instead of opening a browser")
34
38
  .addOption(new Option("--log-level <level>", "Log verbosity (stderr/file only)")
35
39
  .choices(LOG_LEVELS)
@@ -43,6 +47,9 @@ function resolveConfig(opts) {
43
47
  if (!LOG_LEVELS.includes(opts.logLevel)) {
44
48
  throw new InvalidArgumentError(`invalid --log-level "${opts.logLevel}"`);
45
49
  }
50
+ if (!CREDENTIAL_STORES.includes(opts.credentialStore)) {
51
+ throw new InvalidArgumentError(`invalid --credential-store "${opts.credentialStore}"`);
52
+ }
46
53
  if (!opts.dcr && !opts.clientId) {
47
54
  throw new ConfigError("--no-dcr requires --client-id to be provided");
48
55
  }
@@ -56,6 +63,7 @@ function resolveConfig(opts) {
56
63
  dcr: opts.dcr,
57
64
  callbackPort: opts.callbackPort,
58
65
  authTimeoutSec: opts.authTimeout,
66
+ credentialStore: opts.credentialStore,
59
67
  tokenStoreDir: opts.tokenStore,
60
68
  openBrowser: opts.browser,
61
69
  logLevel: opts.logLevel,
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import type { LogLevel } from "./log.js";
2
+ /**
3
+ * Where credentials are persisted:
4
+ * - `auto` — OS keychain when available, else file storage (default).
5
+ * - `keychain` — OS keychain only; error out if it's unavailable.
6
+ * - `file` — on-disk JSON under `tokenStoreDir`.
7
+ */
8
+ export type CredentialStore = "auto" | "keychain" | "file";
2
9
  /**
3
10
  * Resolved gateway configuration.
4
11
  */
@@ -23,7 +30,9 @@ export interface Config {
23
30
  callbackPort?: number;
24
31
  /** Max seconds to wait for the user to complete browser authorization. */
25
32
  authTimeoutSec: number;
26
- /** Directory holding per-server tokens + registration. */
33
+ /** Where credentials are persisted (OS keychain vs. on-disk file). */
34
+ credentialStore: CredentialStore;
35
+ /** Directory holding per-server tokens + registration when using file storage. */
27
36
  tokenStoreDir: string;
28
37
  /** Open the system browser automatically (false => print the URL). */
29
38
  openBrowser: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAmC7B,MAAM,OAAO,WAAY,SAAQ,KAAK;CAAG;AAEzC,sEAAsE;AACtE,MAAM,UAAU,YAAY,CAAC,GAAa;IACxC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,WAAW,CAAC,qBAAqB,KAAK,0BAA0B,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,WAAW,CAAC,qBAAqB,KAAK,sBAAsB,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,WAAW,CAAC,kBAAkB,GAAG,GAAG,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,WAAW,CAAC,+BAA+B,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AA6C7B,MAAM,OAAO,WAAY,SAAQ,KAAK;CAAG;AAEzC,sEAAsE;AACtE,MAAM,UAAU,YAAY,CAAC,GAAa;IACxC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,WAAW,CAAC,qBAAqB,KAAK,0BAA0B,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,WAAW,CAAC,qBAAqB,KAAK,sBAAsB,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,WAAW,CAAC,kBAAkB,GAAG,GAAG,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,WAAW,CAAC,+BAA+B,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { Entry as KeyringEntry } from "@napi-rs/keyring";
2
+ import type { Logger } from "../log.js";
3
+ import type { SecretBackend } from "./store.js";
4
+ /** Thrown when the platform keychain can't be loaded or reached. */
5
+ export declare class KeychainUnavailable extends Error {
6
+ }
7
+ /**
8
+ * Stores one server's credential blob in the OS keychain (macOS Keychain,
9
+ * Windows Credential Manager, Linux Secret Service) via `@napi-rs/keyring`.
10
+ * The account is the server's canonical URI, so entries are recognizable in
11
+ * tools like Keychain Access. A missing entry reads as `undefined`.
12
+ */
13
+ export declare class KeychainBackend implements SecretBackend {
14
+ private readonly entry;
15
+ constructor(entry: KeyringEntry);
16
+ read(): Promise<string | undefined>;
17
+ write(data: string): Promise<void>;
18
+ remove(): Promise<void>;
19
+ }
20
+ /**
21
+ * Open the keychain backend for one server. The native module is imported
22
+ * lazily so a missing/broken binary (or an unreachable Secret Service) never
23
+ * breaks file-only users — it surfaces as {@link KeychainUnavailable}. A probe
24
+ * read both proves the backend works and returns the current blob, sparing the
25
+ * caller a second read for migration.
26
+ */
27
+ export declare function openKeychain(canonicalUri: string, log: Logger): Promise<{
28
+ backend: KeychainBackend;
29
+ current: string | undefined;
30
+ }>;
@@ -0,0 +1,46 @@
1
+ /** Service name under which every server's blob is filed in the OS keychain. */
2
+ const SERVICE = "mcp-gateway";
3
+ /** Thrown when the platform keychain can't be loaded or reached. */
4
+ export class KeychainUnavailable extends Error {
5
+ }
6
+ /**
7
+ * Stores one server's credential blob in the OS keychain (macOS Keychain,
8
+ * Windows Credential Manager, Linux Secret Service) via `@napi-rs/keyring`.
9
+ * The account is the server's canonical URI, so entries are recognizable in
10
+ * tools like Keychain Access. A missing entry reads as `undefined`.
11
+ */
12
+ export class KeychainBackend {
13
+ entry;
14
+ constructor(entry) {
15
+ this.entry = entry;
16
+ }
17
+ async read() {
18
+ return this.entry.getPassword() ?? undefined;
19
+ }
20
+ async write(data) {
21
+ this.entry.setPassword(data);
22
+ }
23
+ async remove() {
24
+ this.entry.deletePassword();
25
+ }
26
+ }
27
+ /**
28
+ * Open the keychain backend for one server. The native module is imported
29
+ * lazily so a missing/broken binary (or an unreachable Secret Service) never
30
+ * breaks file-only users — it surfaces as {@link KeychainUnavailable}. A probe
31
+ * read both proves the backend works and returns the current blob, sparing the
32
+ * caller a second read for migration.
33
+ */
34
+ export async function openKeychain(canonicalUri, log) {
35
+ try {
36
+ const { Entry } = await import("@napi-rs/keyring");
37
+ const backend = new KeychainBackend(new Entry(SERVICE, canonicalUri));
38
+ const current = await backend.read();
39
+ return { backend, current };
40
+ }
41
+ catch (err) {
42
+ log.debug({ err }, "OS keychain unavailable");
43
+ throw new KeychainUnavailable("OS keychain is unavailable", { cause: err });
44
+ }
45
+ }
46
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/oauth/keychain.ts"],"names":[],"mappings":"AAIA,gFAAgF;AAChF,MAAM,OAAO,GAAG,aAAa,CAAC;AAE9B,oEAAoE;AACpE,MAAM,OAAO,mBAAoB,SAAQ,KAAK;CAAG;AAEjD;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAEpD,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,SAAS,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;IAC9B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,YAAoB,EACpB,GAAW;IAEX,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { canonicalResourceUri } from "./canonical.js";
3
3
  import { CallbackServer, openBrowser } from "./callback.js";
4
- import { AuthStore } from "./store.js";
4
+ import { createAuthStore } from "./store-factory.js";
5
5
  /**
6
6
  * OAuth 2.1 client provider for one remote MCP server. The SDK's `auth()` helper
7
7
  * drives discovery (RFC 9728 → RFC 8414), Dynamic Client Registration (RFC 7591),
@@ -23,7 +23,7 @@ export class GatewayOAuthProvider {
23
23
  /** Async factory: loads persisted state and binds the loopback callback port. */
24
24
  static async create(config, log) {
25
25
  const canonical = canonicalResourceUri(config.url);
26
- const store = new AuthStore(config.tokenStoreDir, canonical, log);
26
+ const store = await createAuthStore(config, canonical, log);
27
27
  const stored = await store.load();
28
28
  const port = config.callbackPort ?? stored.redirectPort;
29
29
  let provider;
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/oauth/provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/oauth/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Config } from "../config.js";
2
+ import type { Logger } from "../log.js";
3
+ import { AuthStore } from "./store.js";
4
+ /**
5
+ * Build the credential store for one server, honoring `config.credentialStore`:
6
+ *
7
+ * - `file` — on-disk JSON under `--token-store` (legacy behavior).
8
+ * - `keychain` — OS keychain only; fail loud if it's unavailable.
9
+ * - `auto` — keychain when reachable, otherwise fall back to file storage.
10
+ *
11
+ * When the keychain is used and holds nothing yet, any legacy on-disk blob for
12
+ * this server is migrated into it (and the plaintext file removed) on first use.
13
+ */
14
+ export declare function createAuthStore(config: Config, canonicalUri: string, log: Logger): Promise<AuthStore>;
@@ -0,0 +1,48 @@
1
+ import { KeychainBackend, KeychainUnavailable, openKeychain } from "./keychain.js";
2
+ import { AuthStore, FileBackend } from "./store.js";
3
+ /**
4
+ * Build the credential store for one server, honoring `config.credentialStore`:
5
+ *
6
+ * - `file` — on-disk JSON under `--token-store` (legacy behavior).
7
+ * - `keychain` — OS keychain only; fail loud if it's unavailable.
8
+ * - `auto` — keychain when reachable, otherwise fall back to file storage.
9
+ *
10
+ * When the keychain is used and holds nothing yet, any legacy on-disk blob for
11
+ * this server is migrated into it (and the plaintext file removed) on first use.
12
+ */
13
+ export async function createAuthStore(config, canonicalUri, log) {
14
+ if (config.credentialStore === "file") {
15
+ return AuthStore.file(config.tokenStoreDir, canonicalUri, log);
16
+ }
17
+ try {
18
+ const { backend, current } = await openKeychain(canonicalUri, log);
19
+ if (current === undefined) {
20
+ await migrateLegacyFile(config, canonicalUri, backend, log);
21
+ }
22
+ return new AuthStore(backend, log);
23
+ }
24
+ catch (err) {
25
+ // Explicit `--credential-store keychain` must surface the failure.
26
+ if (config.credentialStore === "keychain" || !(err instanceof KeychainUnavailable)) {
27
+ throw err;
28
+ }
29
+ log.warn({ err }, "OS keychain unavailable; falling back to file storage");
30
+ return AuthStore.file(config.tokenStoreDir, canonicalUri, log);
31
+ }
32
+ }
33
+ /** Move a pre-existing on-disk blob into the keychain. Best-effort: never fatal. */
34
+ async function migrateLegacyFile(config, canonicalUri, backend, log) {
35
+ try {
36
+ const file = new FileBackend(config.tokenStoreDir, canonicalUri);
37
+ const legacy = await file.read();
38
+ if (legacy === undefined)
39
+ return;
40
+ await backend.write(legacy);
41
+ await file.remove();
42
+ log.info("migrated credentials from disk to OS keychain");
43
+ }
44
+ catch (err) {
45
+ log.warn({ err }, "failed to migrate on-disk credentials to OS keychain");
46
+ }
47
+ }
48
+ //# sourceMappingURL=store-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-factory.js","sourceRoot":"","sources":["../../src/oauth/store-factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,YAAoB,EACpB,GAAW;IAEX,IAAI,MAAM,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,mEAAmE;QACnE,IAAI,MAAM,CAAC,eAAe,KAAK,UAAU,IAAI,CAAC,CAAC,GAAG,YAAY,mBAAmB,CAAC,EAAE,CAAC;YACnF,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,uDAAuD,CAAC,CAAC;QAC3E,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,KAAK,UAAU,iBAAiB,CAC9B,MAAc,EACd,YAAoB,EACpB,OAAwB,EACxB,GAAW;IAEX,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,sDAAsD,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC"}
@@ -14,19 +14,48 @@ export interface StoredAuth {
14
14
  redirectPort?: number;
15
15
  }
16
16
  /**
17
- * File-backed credential store. One JSON file per server under a 0700 directory,
18
- * each written 0600. Tolerates a missing/corrupt file by treating it as empty.
17
+ * Persistence primitive for one server's credential blob. The store layers JSON
18
+ * parse/merge/cache/serialization on top; a backend only moves an opaque string
19
+ * in and out of some medium (a file, the OS keychain, ...). `read()` returns
20
+ * `undefined` when nothing is stored yet.
19
21
  */
20
- export declare class AuthStore {
22
+ export interface SecretBackend {
23
+ read(): Promise<string | undefined>;
24
+ write(data: string): Promise<void>;
25
+ remove(): Promise<void>;
26
+ }
27
+ /** Derive the 32-hex storage key shared by the file name and keychain account. */
28
+ export declare function storeKey(canonicalUri: string): string;
29
+ /**
30
+ * File backend: one JSON file per server under a 0700 directory, written 0600
31
+ * via a temp file + atomic rename. A missing file reads as `undefined`.
32
+ */
33
+ export declare class FileBackend implements SecretBackend {
21
34
  private readonly dir;
22
- private readonly log;
23
35
  private readonly file;
36
+ constructor(dir: string, canonicalUri: string);
37
+ read(): Promise<string | undefined>;
38
+ write(data: string): Promise<void>;
39
+ remove(): Promise<void>;
40
+ }
41
+ /**
42
+ * Credential store over a {@link SecretBackend}. Holds the in-memory cache,
43
+ * serializes read-modify-write cycles, and tolerates an unreadable/corrupt blob
44
+ * by treating it as empty.
45
+ */
46
+ export declare class AuthStore {
47
+ private readonly backend;
48
+ private readonly log;
24
49
  private cache;
25
50
  /** Serializes read-modify-write cycles so concurrent patches don't lose updates. */
26
51
  private writeChain;
27
- constructor(dir: string, canonicalUri: string, log: Logger);
52
+ constructor(backend: SecretBackend, log: Logger);
53
+ /** Convenience constructor for the on-disk backend. */
54
+ static file(dir: string, canonicalUri: string, log: Logger): AuthStore;
28
55
  load(): Promise<StoredAuth>;
29
56
  private save;
57
+ /** Append a unit of work to the serialized chain, surfacing its result to the caller. */
58
+ private enqueue;
30
59
  /** Run a read-modify-write cycle serialized against all other mutations. */
31
60
  private mutate;
32
61
  patch(update: Partial<StoredAuth>): Promise<void>;
@@ -1,62 +1,103 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
+ /** Derive the 32-hex storage key shared by the file name and keychain account. */
5
+ export function storeKey(canonicalUri) {
6
+ return createHash("sha256").update(canonicalUri).digest("hex").slice(0, 32);
7
+ }
4
8
  /**
5
- * File-backed credential store. One JSON file per server under a 0700 directory,
6
- * each written 0600. Tolerates a missing/corrupt file by treating it as empty.
9
+ * File backend: one JSON file per server under a 0700 directory, written 0600
10
+ * via a temp file + atomic rename. A missing file reads as `undefined`.
7
11
  */
8
- export class AuthStore {
12
+ export class FileBackend {
9
13
  dir;
10
- log;
11
14
  file;
15
+ constructor(dir, canonicalUri) {
16
+ this.dir = dir;
17
+ this.file = path.join(dir, `${storeKey(canonicalUri)}.json`);
18
+ }
19
+ async read() {
20
+ try {
21
+ return await fs.readFile(this.file, "utf8");
22
+ }
23
+ catch (err) {
24
+ if (err.code === "ENOENT")
25
+ return undefined;
26
+ throw err;
27
+ }
28
+ }
29
+ async write(data) {
30
+ await fs.mkdir(this.dir, { recursive: true, mode: 0o700 });
31
+ const tmp = `${this.file}.tmp`;
32
+ await fs.writeFile(tmp, data, { mode: 0o600 });
33
+ await fs.rename(tmp, this.file);
34
+ }
35
+ async remove() {
36
+ await fs.rm(this.file, { force: true });
37
+ }
38
+ }
39
+ /**
40
+ * Credential store over a {@link SecretBackend}. Holds the in-memory cache,
41
+ * serializes read-modify-write cycles, and tolerates an unreadable/corrupt blob
42
+ * by treating it as empty.
43
+ */
44
+ export class AuthStore {
45
+ backend;
46
+ log;
12
47
  cache;
13
48
  /** Serializes read-modify-write cycles so concurrent patches don't lose updates. */
14
49
  writeChain = Promise.resolve();
15
- constructor(dir, canonicalUri, log) {
16
- this.dir = dir;
50
+ constructor(backend, log) {
51
+ this.backend = backend;
17
52
  this.log = log;
18
- const key = createHash("sha256").update(canonicalUri).digest("hex").slice(0, 32);
19
- this.file = path.join(dir, `${key}.json`);
53
+ }
54
+ /** Convenience constructor for the on-disk backend. */
55
+ static file(dir, canonicalUri, log) {
56
+ return new AuthStore(new FileBackend(dir, canonicalUri), log);
20
57
  }
21
58
  async load() {
22
59
  if (this.cache)
23
60
  return this.cache;
24
61
  try {
25
- const raw = await fs.readFile(this.file, "utf8");
26
- this.cache = JSON.parse(raw);
62
+ const raw = await this.backend.read();
63
+ this.cache = raw ? JSON.parse(raw) : {};
27
64
  }
28
65
  catch (err) {
29
- if (err.code !== "ENOENT") {
30
- this.log.warn({ err, file: this.file }, "ignoring unreadable auth store file");
31
- }
66
+ this.log.warn({ err }, "ignoring unreadable auth store");
32
67
  this.cache = {};
33
68
  }
34
69
  return this.cache;
35
70
  }
36
71
  async save() {
37
- await fs.mkdir(this.dir, { recursive: true, mode: 0o700 });
38
- const tmp = `${this.file}.tmp`;
39
- await fs.writeFile(tmp, JSON.stringify(this.cache ?? {}, null, 2), { mode: 0o600 });
40
- await fs.rename(tmp, this.file);
72
+ await this.backend.write(JSON.stringify(this.cache ?? {}, null, 2));
73
+ }
74
+ /** Append a unit of work to the serialized chain, surfacing its result to the caller. */
75
+ enqueue(fn) {
76
+ const next = this.writeChain.then(fn);
77
+ // Keep the chain alive even if one unit rejects.
78
+ this.writeChain = next.catch(() => { });
79
+ return next;
41
80
  }
42
81
  /** Run a read-modify-write cycle serialized against all other mutations. */
43
82
  mutate(fn) {
44
- const next = this.writeChain.then(async () => {
83
+ return this.enqueue(async () => {
45
84
  this.cache = fn(await this.load());
46
85
  await this.save();
47
86
  });
48
- // Keep the chain alive even if one mutation rejects.
49
- this.writeChain = next.catch(() => { });
50
- return next;
51
87
  }
52
88
  async patch(update) {
53
89
  return this.mutate((current) => ({ ...current, ...update }));
54
90
  }
55
91
  /** Remove credentials by scope; used by invalidateCredentials. */
56
92
  async clear(scope) {
93
+ if (scope === "all") {
94
+ // Drop the backing entry entirely so no empty blob lingers.
95
+ return this.enqueue(async () => {
96
+ this.cache = {};
97
+ await this.backend.remove();
98
+ });
99
+ }
57
100
  return this.mutate((current) => {
58
- if (scope === "all")
59
- return {};
60
101
  if (scope === "client")
61
102
  return { ...current, clientInformation: undefined };
62
103
  if (scope === "tokens")
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/oauth/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/oauth/store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsreality/mcp-gateway",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI gateway that exposes a local STDIO MCP endpoint and proxies a remote Streamable-HTTP MCP server (OAuth 2.1 + DCR).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,15 +43,23 @@
43
43
  "dev": "tsc -p tsconfig.json --watch",
44
44
  "start": "node dist/cli.js",
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit",
46
- "test": "npm run build && node test/manual-relay.mjs && node test/manual-oauth.mjs"
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "test:unit": "vitest run test/unit",
49
+ "test:e2e": "vitest run test/e2e",
50
+ "coverage": "vitest run --coverage"
47
51
  },
48
52
  "dependencies": {
49
53
  "@modelcontextprotocol/sdk": "^1.29.0",
54
+ "@napi-rs/keyring": "^1.3.0",
50
55
  "commander": "^13.0.0",
51
56
  "pino": "^9.6.0"
52
57
  },
53
58
  "devDependencies": {
54
59
  "@types/node": "^22.10.0",
55
- "typescript": "^5.7.0"
60
+ "@vitest/coverage-v8": "^3.2.6",
61
+ "typescript": "^5.7.0",
62
+ "vitest": "^3.2.6",
63
+ "zod": "^4.4.3"
56
64
  }
57
65
  }