76 lines
2.7 KiB
TypeScript
76 lines
2.7 KiB
TypeScript
import type { InspectorClientEnvironment } from "@inspector/core/mcp/index.js";
|
|
import {
|
|
createRemoteTransport,
|
|
createRemoteFetch,
|
|
createRemoteLogger,
|
|
} from "@inspector/core/mcp/remote/index.js";
|
|
import { BrowserNavigation } from "@inspector/core/auth/browser/index.js";
|
|
import type { RedirectUrlProvider } from "@inspector/core/auth/index.js";
|
|
import { getWebRemoteOAuthStorage } from "./remoteOAuthStorage.js";
|
|
|
|
export interface WebEnvironmentResult {
|
|
environment: InspectorClientEnvironment;
|
|
logger: InspectorClientEnvironment["logger"];
|
|
}
|
|
|
|
/**
|
|
* Assemble an `InspectorClientEnvironment` for the browser:
|
|
* - transport / fetch / logger all routed through the in-process Hono
|
|
* backend at `window.location.origin` (the `clients/web/server`
|
|
* dev-backend wires this in `/api/*`).
|
|
* - OAuth storage uses `RemoteOAuthStorage` (shared `oauth.json` via
|
|
* `/api/storage/oauth`); navigation uses `BrowserNavigation`.
|
|
*
|
|
* Returns both the assembled environment and the logger so callers can share
|
|
* the same pino instance for any direct logging they need to do, instead of
|
|
* reaching back through the client.
|
|
*
|
|
* `authToken` is supplied by `App.tsx` (`getAuthToken()`) and forwarded to
|
|
* remote transport/fetch/logger and `getWebRemoteOAuthStorage` so every
|
|
* `/api/*` call (including `/api/storage/oauth`) carries `x-mcp-remote-auth`
|
|
* when the backend requires it.
|
|
*
|
|
* `onBeforeOAuthRedirect` runs synchronously immediately before the OAuth
|
|
* full-page redirect (see `BrowserNavigation`). The app uses it to flush the
|
|
* pre-redirect Network log to backend storage so the auth handshake's first
|
|
* half (discovery + Dynamic Client Registration) survives the navigation.
|
|
*/
|
|
export function createWebEnvironment(
|
|
authToken: string | undefined,
|
|
redirectUrlProvider: RedirectUrlProvider,
|
|
onBeforeOAuthRedirect?: (authorizationUrl: URL) => void,
|
|
): WebEnvironmentResult {
|
|
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
|
|
|
// Passing `window.fetch` directly raises "Illegal invocation" because the
|
|
// function loses its `this` binding when extracted off `window`. Wrap so
|
|
// the call site preserves the global receiver.
|
|
const fetchFn: typeof fetch = (...args) => globalThis.fetch(...args);
|
|
|
|
const logger = createRemoteLogger({
|
|
baseUrl,
|
|
authToken,
|
|
fetchFn,
|
|
});
|
|
|
|
const environment: InspectorClientEnvironment = {
|
|
transport: createRemoteTransport({
|
|
baseUrl,
|
|
authToken,
|
|
fetchFn,
|
|
}),
|
|
fetch: createRemoteFetch({
|
|
baseUrl,
|
|
authToken,
|
|
fetchFn,
|
|
}),
|
|
logger,
|
|
oauth: {
|
|
storage: getWebRemoteOAuthStorage(authToken),
|
|
navigation: new BrowserNavigation(undefined, onBeforeOAuthRedirect),
|
|
redirectUrlProvider,
|
|
},
|
|
};
|
|
|
|
return { environment, logger };
|
|
}
|