inspector/core/auth/node/storage-node.ts
Cliff Hall 82269e78cb
refactor(core): apply v1.5-port cleanups deferred from #1306 (#1316)
* refactor(core): apply v1.5-port cleanups deferred from #1306

Addresses issue #1309 — five small, mechanical cleanups deferred from the
v1.5 runtime port:

1. Collapse BrowserOAuthStorage / NodeOAuthStorage / RemoteOAuthStorage
   onto a shared OAuthStorageBase. Three ~130-line duplicates become
   thin adapter wrappers; behavior + public API unchanged.
2. Re-enable erasableSyntaxOnly in tsconfig.app.json by rewriting the
   seven (plus two the issue missed in sampling/elicitation create-
   message) parameter-property constructors as explicit field + assign.
3. Route the lone console.error in inspectorClient.ts through this.logger.
4. Replace `${Date.now()}-${Math.random()}` with crypto.randomUUID() at
   the five local-only ID sites (message tracking + sampling/elicit).
5. Pin the remote-server hono app generic at new Hono<Env>() and drop
   the streamSSE cast. Root cause of the cast was an unresolved hono
   subpath: the tsconfig "hono/*" wildcard didn't honor the exports
   map, so hono/streaming leaked to the root-hoisted hono@4.12.12 while
   hono itself resolved to clients/web/hono@4.12.18. Replaced the
   wildcard with an explicit hono/streaming entry in both tsconfig.app
   and tsconfig.test, removing the package-identity mismatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(core): address #1316 PR review feedback

- Drop the stranded no-op getServerState() read at the top of
  clearClientInformation in OAuthStorageBase (carried over verbatim
  from the three originals).
- Drop the crypto.randomUUID() guard at the task-ID site so all
  six in-scope sites are consistent; the fallback branch was dead
  code on every supported runtime.
- Remove the residual `c as Context` cast at the streamSSE call site
  in core/mcp/remote/node/server.ts — once both hono imports
  resolve to the same package the route-typed Context flows through
  to streamSSE with no cast at all.
- Move the new `options` / `config` field declarations in
  RemoteClientTransport up with the rest of the field block, matching
  the layout used by the other five rewritten classes in this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:15:48 -04:00

63 lines
2.1 KiB
TypeScript

import { OAuthStorageBase } from "../oauth-storage.js";
import { createOAuthStore } from "../store.js";
import { createFileStorageAdapter } from "../../storage/adapters/file-storage.js";
import {
getDefaultStorageDir,
getStoreFilePath,
} from "../../storage/store-io.js";
/** Default path: ~/.mcp-inspector/storage/oauth.json */
const DEFAULT_STATE_PATH = getStoreFilePath(getDefaultStorageDir(), "oauth");
/**
* Get path to OAuth state file.
* @param customPath - Optional custom path (full path to state file). Default: ~/.mcp-inspector/storage/oauth.json
*/
export function getStateFilePath(customPath?: string): string {
return customPath ?? DEFAULT_STATE_PATH;
}
const storeCache = new Map<string, ReturnType<typeof createOAuthStore>>();
/**
* Get or create the OAuth store instance for the given path.
* @param stateFilePath - Optional custom path to state file. Default: ~/.mcp-inspector/storage/oauth.json
*/
export function getOAuthStore(stateFilePath?: string) {
const key = getStateFilePath(stateFilePath);
let store = storeCache.get(key);
if (!store) {
const filePath = getStateFilePath(stateFilePath);
const storage = createFileStorageAdapter({ filePath });
store = createOAuthStore(storage);
storeCache.set(key, store);
}
return store;
}
/**
* Clear all OAuth client state (all servers) in the default store.
* Useful for test isolation in E2E OAuth tests.
* Use a custom-path store and clear per serverUrl if you need to clear non-default storage.
*/
export function clearAllOAuthClientState(): void {
const store = getOAuthStore();
const state = store.getState();
const urls = Object.keys(state.servers ?? {});
for (const url of urls) {
state.clearServerState(url);
}
}
/**
* Node.js storage implementation using Zustand with file-based persistence.
* For InspectorClient, CLI, and TUI.
*/
export class NodeOAuthStorage extends OAuthStorageBase {
/**
* @param storagePath - Optional path to state file. Default: ~/.mcp-inspector/oauth/state.json
*/
constructor(storagePath?: string) {
super(getOAuthStore(storagePath));
}
}