### Install Dependencies Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md Navigate to the test directory and install project dependencies using pnpm. ```bash cd test pnpm install ``` -------------------------------- ### Run Development Server Source: https://github.com/geelen/mcp-remote/blob/main/AGENT.md Start the client or proxy in development mode using `tsx`. ```bash npx tsx src/client.ts ``` ```bash npx tsx src/proxy.ts ``` -------------------------------- ### Install mcp-remote with Auto-Accept Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use the `-y` flag with `npx` to automatically accept the installation of the `mcp-remote` package if prompted. ```json "command": "npx", "args": [ "-y", "mcp-remote", "https://remote.mcp.server/sse" ] ``` -------------------------------- ### Example Test Case Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md An example test case demonstrating how to connect to a new server, verify its capabilities, and assert expected results. Includes setting a custom timeout. ```typescript it('connects to new server', async () => { client = await createMCPClient('https://example.com/mcp') const result = await verifyConnection(client.client) expect(result.hasTools).toBe(true) }, 30000) ``` -------------------------------- ### Install Latest mcp-remote Version Source: https://github.com/geelen/mcp-remote/blob/main/README.md Append `@latest` to `mcp-remote` to ensure `npx` always fetches the most recent version. ```json "args": [ "mcp-remote@latest", "https://remote.mcp.server/sse" ] ``` -------------------------------- ### setupOAuthCallbackServerWithLongPoll Source: https://context7.com/geelen/mcp-remote/llms.txt Starts an Express server that handles the OAuth `/oauth/callback` redirect and exposes a `/wait-for-auth` long-polling endpoint for secondary instances. ```APIDOC ## setupOAuthCallbackServerWithLongPoll(options) ### Description Sets up an Express server for handling OAuth redirects and long-polling for authentication status. This allows concurrent instances to coordinate authentication. ### Parameters #### Path Parameters - **options** (object) - Required - Configuration options for the server. - **port** (number) - Required - The port for the server. - **path** (string) - Required - The path for the OAuth callback (e.g., `/oauth/callback`). - **events** (EventEmitter) - Required - An event emitter instance. - **authTimeoutMs** (number) - Required - The timeout for authentication in milliseconds. ### Returns An object containing: - **server** (Express server instance) - The started Express server. - **waitForAuthCode** (function) - A function that blocks until the browser redirect completes and returns the authorization code. - **authCompletedPromise** (Promise) - A promise that resolves with the authorization code upon completion. ``` -------------------------------- ### mcp-remote-client Diagnostic Client Usage Source: https://context7.com/geelen/mcp-remote/llms.txt Examples of using the mcp-remote-client for diagnostics. Shows basic usage, clearing credentials, and forcing specific transport and host configurations. Includes expected stderr output. ```bash # Basic usage — will open browser for OAuth if needed npx -p mcp-remote@latest mcp-remote-client https://api.example.com/mcp/sse # Clear stale credentials first, then test rm -rf ~/.mcp-auth npx -p mcp-remote@latest mcp-remote-client https://api.example.com/mcp/sse --debug # Force SSE transport, custom callback host/port npx mcp-remote-client https://api.example.com/mcp/sse 9090 \ --transport sse-only \ --host 127.0.0.1 \ --header "Authorization:Bearer ${TOKEN}" # Expected stderr output: # [12345] Discovering OAuth server configuration... # [12345] Discovered authorization server: https://auth.example.com # [12345] Connected successfully! # [12345] Tools: { "tools": [ { "name": "search", ... } ] } # [12345] Resources: { "resources": [] } # [12345] Exiting OK... ``` -------------------------------- ### Claude Desktop Configuration for mcp-remote Proxy Source: https://context7.com/geelen/mcp-remote/llms.txt Example configuration for Claude Desktop to use a remote MCP server via mcp-remote. Specifies the remote server URL, transport, headers, resource, and authentication timeout. ```jsonc // Claude Desktop (~/.config/claude/claude_desktop_config.json) { "mcpServers": { "my-remote-server": { "command": "npx", "args": [ "mcp-remote@latest", "https://api.example.com/mcp/sse", "--transport", "sse-only", "--header", "X-Tenant-Id:acme-corp", "--resource", "https://acme.example.com/", "--auth-timeout", "60", "--debug" ], "env": { "MCP_REMOTE_CONFIG_DIR": "/home/user/.config/mcp-auth" } }, // Multiple isolated instances sharing the same server "tenant-a": { "command": "npx", "args": [ "mcp-remote", "https://mcp.atlassian.com/v1/sse", "--resource", "https://tenant-a.atlassian.net/" ] }, "tenant-b": { "command": "npx", "args": [ "mcp-remote", "https://mcp.atlassian.com/v1/sse", "--resource", "https://tenant-b.atlassian.net/" ] } } } ``` -------------------------------- ### Basic Remote MCP Server Configuration Source: https://github.com/geelen/mcp-remote/blob/main/README.md Configure an MCP client to connect to a remote MCP server using npx. This is the most basic setup for remote server access. ```json { "mcpServers": { "remote-example": { "command": "npx", "args": [ "mcp-remote", "https://remote.mcp.server/sse" ] } } } ``` -------------------------------- ### Parsing CLI Arguments with parseCommandLineArgs Source: https://context7.com/geelen/mcp-remote/llms.txt Demonstrates how to use the `parseCommandLineArgs` utility function to parse CLI arguments and options for mcp-remote. Shows example arguments and the resulting parsed options. ```typescript import { parseCommandLineArgs } from './lib/utils' const opts = await parseCommandLineArgs( [ 'https://api.example.com/mcp/sse', '--transport', 'http-first', '--header', 'Authorization:Bearer ${API_TOKEN}', '--header', 'X-Workspace:my-workspace', '--resource', 'https://workspace.example.com/', '--ignore-tool', 'delete*', '--ignore-tool', 'admin*', '--static-oauth-client-metadata', '{"scope":"openid profile email read:data"}', '--auth-timeout', '90', '--host', '127.0.0.1', '--debug', ], 'Usage: mcp-remote [port] [flags]', ) console.log(opts.serverUrl) // 'https://api.example.com/mcp/sse' console.log(opts.transportStrategy) // 'http-first' console.log(opts.headers) // { Authorization: 'Bearer ', 'X-Workspace': 'my-workspace' } console.log(opts.ignoredTools) // ['delete*', 'admin*'] console.log(opts.callbackPort) // e.g. 17823 (deterministic from URL hash or existing registration) console.log(opts.serverUrlHash) // md5 hex string isolating this session console.log(opts.authTimeoutMs) // 90000 ``` -------------------------------- ### createLazyAuthCoordinator Source: https://context7.com/geelen/mcp-remote/llms.txt Returns an AuthCoordinator whose `initializeAuth()` method is only called when authentication is actually required. On first call, it checks for a lockfile from a concurrent instance or starts its own OAuth callback server. ```APIDOC ## createLazyAuthCoordinator(serverUrlHash, callbackPort, events, authTimeoutMs) ### Description Creates an `AuthCoordinator` that lazily initializes authentication. It checks for concurrent instances or starts its own OAuth callback server. Subsequent calls return cached state. ### Parameters #### Path Parameters - **serverUrlHash** (string) - Required - A hash representing the server URL. - **callbackPort** (number) - Required - The port for the OAuth callback server. - **events** (EventEmitter) - Required - An event emitter instance. - **authTimeoutMs** (number) - Required - The timeout for authentication in milliseconds. ### Returns An `AuthCoordinator` object with an `initializeAuth` method. ### Methods on returned object #### `initializeAuth()` Initializes authentication lazily. Returns an object containing `skipBrowserAuth` (boolean) and `waitForAuthCode` (function), and `server` (HTTP server instance). #### `waitForAuthCode()` (function) Blocks until the browser redirect completes and returns the authorization code. ``` -------------------------------- ### Build Project Source: https://github.com/geelen/mcp-remote/blob/main/AGENT.md Use `pnpm build` to compile the project. Use `pnpm build:watch` for continuous compilation during development. ```bash pnpm build ``` ```bash pnpm build:watch ``` -------------------------------- ### NodeOAuthClientProvider Initialization and Usage Source: https://context7.com/geelen/mcp-remote/llms.txt Demonstrates how to initialize and use the NodeOAuthClientProvider for managing OAuth client registration, token storage, and PKCE flow. Supports static client info or dynamic registration. ```typescript import { NodeOAuthClientProvider } from './lib/node-oauth-client-provider' import { getServerUrlHash } from './lib/utils' const serverUrl = 'https://api.example.com' const serverUrlHash = getServerUrlHash(serverUrl) const provider = new NodeOAuthClientProvider({ serverUrl, callbackPort: 17823, host: 'localhost', clientName: 'My MCP App', serverUrlHash, // Optional: skip dynamic registration staticOAuthClientInfo: { client_id: 'pre-registered-client-id', client_secret: 'my-secret', redirect_uris: ['http://localhost:17823/oauth/callback'], }, // Optional: override OAuth metadata staticOAuthClientMetadata: { scope: 'openid read:tools write:tools', }, authorizeResource: 'https://workspace.example.com/', }) // Read stored tokens (returns undefined if none) const tokens = await provider.tokens() console.log(tokens?.access_token) // 'eyJhbGciOiJSUzI1NiIsInR5cCI6...' console.log(tokens?.expires_in) // 3600 // Read or generate client registration const clientInfo = await provider.clientInformation() console.log(clientInfo?.client_id) // 'dyn-client-abc123' // Save tokens after token exchange (called by SDK internally) await provider.saveTokens({ access_token: 'new-access-token', refresh_token: 'new-refresh-token', token_type: 'Bearer', expires_in: 3600, }) // Invalidate specific credentials (e.g. after auth error) await provider.invalidateCredentials('tokens') // remove tokens only await provider.invalidateCredentials('all') // remove tokens + client reg + verifier // The redirect URL used in OAuth registration console.log(provider.redirectUrl) // 'http://localhost:17823/oauth/callback' ``` -------------------------------- ### Provide Static OAuth Client Metadata Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use the `--static-oauth-client-metadata` flag with a JSON string or a file path prefixed with `@` to supply custom OAuth client metadata, such as client IDs or scopes. ```bash npx mcp-remote https://example.remote/server --static-oauth-client-metadata '{ "scope": "space separated scopes" }' ``` -------------------------------- ### Connect to MCP Remote Server with Static OAuth Client Metadata Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use this command to connect to an MCP remote server, providing static OAuth client metadata via a file path. Ensure the path to the metadata file is absolute if the current working directory is uncertain. ```bash npx mcp-remote https://example.remote/server --static-oauth-client-metadata '@/Users/username/Library/Application Support/Claude/oauth_client_metadata.json' ``` -------------------------------- ### Provide Static OAuth Client Information via File Path Source: https://github.com/geelen/mcp-remote/blob/main/README.md Connect to an MCP remote server using static OAuth client information stored in a JSON file. The path to the file should be prefixed with '@'. Absolute paths are recommended. ```bash npx mcp-remote https://example.remote/server --static-oauth-client-info '@/Users/username/Library/Application Support/Claude/oauth_client_info.json' ``` -------------------------------- ### Run All Tests Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md Execute all end-to-end tests. This command first builds the mcp-remote project. ```bash pnpm test ``` -------------------------------- ### Provide Static OAuth Client Information via Environment Variables and JSON String Source: https://github.com/geelen/mcp-remote/blob/main/README.md Set MCP_REMOTE_CLIENT_ID and MCP_REMOTE_CLIENT_SECRET environment variables to use them within a JSON string for the --static-oauth-client-info flag. This method is useful for servers requiring pre-registered clients. ```bash export MCP_REMOTE_CLIENT_ID=xxx export MCP_REMOTE_CLIENT_SECRET=yyy npx mcp-remote https://example.remote/server --static-oauth-client-info "{ \"client_id\": \"$MCP_REMOTE_CLIENT_ID\", \"client_secret\": \"$MCP_REMOTE_CLIENT_SECRET\" }" ``` -------------------------------- ### connectToRemoteServer Source: https://context7.com/geelen/mcp-remote/llms.txt Creates and connects an SSE or Streamable HTTP transport to a remote server, with automatic fallback between transports. It handles UnauthorizedError by lazily invoking an authInitializer to complete the OAuth flow before reconnecting. ```APIDOC ## `connectToRemoteServer(client, serverUrl, authProvider, headers, authInitializer, transportStrategy?)` ### Description Creates an SSE or Streamable HTTP transport, connects it (with automatic fallback between transports on 404/405), and handles `UnauthorizedError` by lazily invoking `authInitializer` to complete the OAuth flow before reconnecting. Recursive with cycle protection via a `recursionReasons` set. ### Parameters #### Path Parameters - **client** (Client) - The MCP client instance. - **serverUrl** (string) - The URL of the remote server. - **authProvider** (AuthProvider) - An authentication provider instance. - **headers** (object) - Additional headers to include in the request. - **authInitializer** (function) - An asynchronous function that initializes the OAuth flow. - **transportStrategy** (string) - Optional. The transport strategy to use ('http-first' or 'sse-first'). Defaults to 'http-first'. ### Returns - **transport** (Transport) - The connected transport instance. ### Example ```typescript import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { connectToRemoteServer } from './lib/utils' import { NodeOAuthClientProvider } from './lib/node-oauth-client-provider' import { EventEmitter } from 'events' import { createLazyAuthCoordinator } from './lib/coordination' const events = new EventEmitter() const serverUrlHash = 'abc123def456' // from getServerUrlHash(...) const callbackPort = 17823 const authCoordinator = createLazyAuthCoordinator(serverUrlHash, callbackPort, events, 30000) const authProvider = new NodeOAuthClientProvider({ serverUrl: 'https://auth.example.com', callbackPort, host: 'localhost', serverUrlHash, }) const client = new Client({ name: 'my-app', version: '1.0.0' }, { capabilities: {} }) const transport = await connectToRemoteServer( client, 'https://api.example.com/mcp/sse', authProvider, { 'X-Custom': 'value' }, async () => { const state = await authCoordinator.initializeAuth() return { waitForAuthCode: state.waitForAuthCode, skipBrowserAuth: state.skipBrowserAuth, } }, 'http-first', // tries HTTP, falls back to SSE on 404/405 ) // Transport is connected; client can now make requests const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema) console.log(tools) // { tools: [{ name: 'search', description: '...', inputSchema: {...} }] } ``` ``` -------------------------------- ### Create MCP Client with Debug Flag Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md Instantiate an MCP client with the '--debug' flag enabled for verbose logging during testing. ```typescript createMCPClient(url, ['--debug']) ``` -------------------------------- ### Integration Test File Structure Source: https://github.com/geelen/mcp-remote/blob/main/test/PLAN.md Details the structure for integration tests, focusing on transport connections, OAuth flows, proxy behavior, and multi-instance coordination. ```text test/integration/ ├── transport.test.ts # SSE/HTTP selection, fallback logic ├── oauth-flow.test.ts # Auth flows with minimal OAuth simulator ├── proxy.test.ts # Bidirectional message passing, filtering └── multi-instance.test.ts # Coordination between concurrent processes ``` -------------------------------- ### Enable Detailed Debugging Logs Source: https://github.com/geelen/mcp-remote/blob/main/README.md Include the `--debug` flag to activate verbose logging. This writes detailed information about the authentication process, connections, and token refreshing to `~/.mcp-auth/{server_hash}_debug.log`. ```json "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--debug" ] ``` -------------------------------- ### Enable Outbound HTTP(S) Proxy Source: https://github.com/geelen/mcp-remote/blob/main/README.md Add the `--enable-proxy` flag to utilize proxy settings from environment variables like `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` for outbound connections. ```json "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--enable-proxy" ], "env": { "HTTPS_PROXY": "http://127.0.0.1:3128", "NO_PROXY": "localhost,127.0.0.1" } ``` -------------------------------- ### Test Architecture Diagram Source: https://github.com/geelen/mcp-remote/blob/main/test/PLAN.md Illustrates the interaction between the Test Client, mcp-remote proxy, and the Test MCP Server using stdio and SSE/HTTP transports. ```text ┌─────────────────┐ │ Test Client │ ← Simple Node.js script using MCP SDK │ (stdio) │ └────────┬────────┘ │ stdio ↓ ┌─────────────────┐ │ mcp-remote │ ← Spawned as: node dist/proxy.js │ (proxy.ts) │ └────────┬────────┘ │ SSE/HTTP ↓ ┌─────────────────┐ │ Test MCP │ ← Local server (Bun + @hono/mcp) │ Server │ └─────────────────┘ ``` -------------------------------- ### Unit Test File Structure Source: https://github.com/geelen/mcp-remote/blob/main/test/PLAN.md Outlines the organization of unit tests for mcp-remote, covering utilities, coordination, OAuth client logic, and message transformations. ```text test/unit/ ├── utils.test.ts # CLI parsing, header substitution, pattern matching ├── coordination.test.ts # Lockfile logic (mocked filesystem) ├── oauth-client.test.ts # Token storage, validation (mocked filesystem) └── message-transforms.test.ts # Ignored tools, clientInfo rewrite ``` -------------------------------- ### Configure MCP Server in Claude Desktop with Custom CA Certificate Source: https://github.com/geelen/mcp-remote/blob/main/README.md This JSON configuration is for Claude Desktop's mcpServers. It specifies a command to run mcp-remote and includes an environment variable NODE_EXTRA_CA_CERTS to point to a custom CA certificate file, which can resolve VPN-related issues. ```json { "mcpServers": { "remote-example": { "command": "npx", "args": [ "mcp-remote", "https://remote.mcp.server/sse" ], "env": { "NODE_EXTRA_CA_CERTS": "{your CA certificate file path}.pem" } } } } ``` -------------------------------- ### Enable Debug Output Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md Run tests with detailed debug output by setting the DEBUG environment variable. ```bash DEBUG=* pnpm test ``` -------------------------------- ### Run Unit Tests Source: https://github.com/geelen/mcp-remote/blob/main/AGENT.md Use `pnpm test:unit` to run unit tests. `pnpm test:unit:watch` enables watch mode for continuous testing. ```bash pnpm test:unit ``` ```bash pnpm test:unit:watch ``` -------------------------------- ### Connect to Remote Server with Auth Fallback Source: https://context7.com/geelen/mcp-remote/llms.txt Establishes an SSE or Streamable HTTP transport, connecting with automatic fallback on 404/405 errors. Handles UnauthorizedError by lazily initializing OAuth flow before reconnecting. ```typescript import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { connectToRemoteServer } from './lib/utils' import { NodeOAuthClientProvider } from './lib/node-oauth-client-provider' import { EventEmitter } from 'events' import { createLazyAuthCoordinator } from './lib/coordination' const events = new EventEmitter() const serverUrlHash = 'abc123def456' // from getServerUrlHash(...) const callbackPort = 17823 const authCoordinator = createLazyAuthCoordinator(serverUrlHash, callbackPort, events, 30000) const authProvider = new NodeOAuthClientProvider({ serverUrl: 'https://auth.example.com', callbackPort, host: 'localhost', serverUrlHash, }) const client = new Client({ name: 'my-app', version: '1.0.0' }, { capabilities: {} }) const transport = await connectToRemoteServer( client, // pass null to get a raw transport without connecting a Client 'https://api.example.com/mcp/sse', authProvider, { 'X-Custom': 'value' }, async () => { const state = await authCoordinator.initializeAuth() return { waitForAuthCode: state.waitForAuthCode, skipBrowserAuth: state.skipBrowserAuth, } }, 'http-first', // tries HTTP, falls back to SSE on 404/405 ) // Transport is connected; client can now make requests const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema) console.log(tools) // { tools: [{ name: 'search', description: '...', inputSchema: {...} }] } ``` -------------------------------- ### Lint and Format Code Source: https://github.com/geelen/mcp-remote/blob/main/AGENT.md Execute `pnpm lint-fix` to automatically format code according to Prettier rules. ```bash pnpm lint-fix ``` -------------------------------- ### Specify Transport Strategy Source: https://github.com/geelen/mcp-remote/blob/main/README.md Control the transport mechanism by using the `--transport` flag with available strategies like `sse-only`, `http-only`, `http-first`, or `sse-first`. ```bash npx mcp-remote https://example.remote/server --transport sse-only ``` -------------------------------- ### Set up MCP Bidirectional JSON-RPC Proxy Source: https://context7.com/geelen/mcp-remote/llms.txt Establishes a fully bidirectional JSON-RPC proxy between two transports. It forwards messages, intercepts `tools/list` and `tools/call` for ignored tools, and appends proxy information to `initialize` messages. ```typescript import { mcpProxy } from './lib/utils' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' // remoteTransport — already connected SSE or HTTP transport const localTransport = new StdioServerTransport() mcpProxy({ transportToClient: localTransport, transportToServer: remoteTransport, ignoredTools: ['delete*', 'admin*', 'resetDatabase'], }) await localTransport.start() // From this point on: // - All stdio JSON-RPC from the MCP client flows to remoteTransport // - All responses/notifications from remote flow back to stdio // - tools/list responses will omit tools matching 'delete*', 'admin*', 'resetDatabase' // - tools/call for ignored tools returns: // { jsonrpc: '2.0', id: , error: { code: -32603, message: 'Tool "deleteUser" is not available' } } // - initialize messages get: clientInfo.name = "MyClient (via mcp-remote 0.1.38)" ``` -------------------------------- ### Discover MCP OAuth Server Info Source: https://context7.com/geelen/mcp-remote/llms.txt Implements the full MCP Authorization Server Discovery flow. Use this to probe an MCP server for authentication details, including Protected Resource Metadata and Authorization Server Metadata, to configure an OAuth client. ```typescript import { discoverOAuthServerInfo } from './lib/utils' const result = await discoverOAuthServerInfo( 'https://api.example.com/mcp/sse', { 'X-Tenant': 'acme' }, ) // result.authorizationServerUrl — the OAuth server to use console.log(result.authorizationServerUrl) // e.g. 'https://auth.example.com' // result.authorizationServerMetadata — RFC 8414 metadata console.log(result.authorizationServerMetadata?.token_endpoint) // e.g. 'https://auth.example.com/oauth/token' // result.protectedResourceMetadata — RFC 9728 metadata console.log(result.protectedResourceMetadata?.scopes_supported) // e.g. ['openid', 'read:tools', 'write:tools'] // result.wwwAuthenticateScope — scope hint from WWW-Authenticate header console.log(result.wwwAuthenticateScope) // e.g. 'openid read:tools' // If no auth needed (server returns 200), authorizationServerUrl === serverUrl // and authorizationServerMetadata may be populated from /.well-known/oauth-authorization-server ``` -------------------------------- ### mcpProxy Source: https://context7.com/geelen/mcp-remote/llms.txt Sets up a fully bidirectional JSON-RPC proxy between two transports, forwarding messages and filtering/blocking specific tools. ```APIDOC ## `mcpProxy({ transportToClient, transportToServer, ignoredTools? })` ### Description Sets up a fully bidirectional JSON-RPC proxy between two transports: forwards messages from the local stdio client to the remote server and vice-versa. Intercepts `tools/list` responses to filter out ignored tools and blocks `tools/call` requests for ignored tools with an immediate error response. Appends `(via mcp-remote )` to the `clientInfo.name` in `initialize` messages. ### Parameters #### Path Parameters - **transportToClient** (Transport) - The transport to send messages to the client. - **transportToServer** (Transport) - The transport to send messages to the server. - **ignoredTools** (string[]) - Optional. A list of tool names or patterns to ignore. ### Example ```typescript import { mcpProxy } from './lib/utils' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' // remoteTransport — already connected SSE or HTTP transport const localTransport = new StdioServerTransport() mcpProxy({ transportToClient: localTransport, transportToServer: remoteTransport, ignoredTools: ['delete*', 'admin*', 'resetDatabase'], }) await localTransport.start() // From this point on: // - All stdio JSON-RPC from the MCP client flows to remoteTransport // - All responses/notifications from remote flow back to stdio // - tools/list responses will omit tools matching 'delete*', 'admin*', 'resetDatabase' // - tools/call for ignored tools returns: // { jsonrpc: '2.0', id: , error: { code: -32603, message: 'Tool "deleteUser" is not available' } } // - initialize messages get: clientInfo.name = "MyClient (via mcp-remote 0.1.38)" ``` ``` -------------------------------- ### discoverOAuthServerInfo Source: https://context7.com/geelen/mcp-remote/llms.txt Implements the full MCP Authorization Server Discovery flow to obtain URLs and metadata for configuring NodeOAuthClientProvider. It probes the MCP server for a WWW-Authenticate header on a 401, discovers Protected Resource Metadata (RFC 9728), and then fetches Authorization Server Metadata (RFC 8414). ```APIDOC ## `discoverOAuthServerInfo(serverUrl, headers?)` ### Description Implements the full MCP Authorization Server Discovery flow: probes the MCP server for a `WWW-Authenticate` header on a 401, discovers Protected Resource Metadata (RFC 9728), then fetches Authorization Server Metadata (RFC 8414). Returns URLs and metadata objects used to configure `NodeOAuthClientProvider`. ### Parameters #### Path Parameters - **serverUrl** (string) - Required - The URL of the MCP server. - **headers** (object) - Optional - Additional headers to include in the request. ### Returns - **authorizationServerUrl** (string) - The URL of the OAuth server. - **authorizationServerMetadata** (object) - RFC 8414 metadata of the authorization server. - **protectedResourceMetadata** (object) - RFC 9728 metadata of the protected resource. - **wwwAuthenticateScope** (string) - Scope hint from the WWW-Authenticate header. ### Example ```typescript import { discoverOAuthServerInfo } from './lib/utils' const result = await discoverOAuthServerInfo( 'https://api.example.com/mcp/sse', { 'X-Tenant': 'acme' }, ) // result.authorizationServerUrl — the OAuth server to use console.log(result.authorizationServerUrl) // e.g. 'https://auth.example.com' // result.authorizationServerMetadata — RFC 8414 metadata console.log(result.authorizationServerMetadata?.token_endpoint) // e.g. 'https://auth.example.com/oauth/token' // result.protectedResourceMetadata — RFC 9728 metadata console.log(result.protectedResourceMetadata?.scopes_supported) // e.g. ['openid', 'read:tools', 'write:tools'] // result.wwwAuthenticateScope — scope hint from WWW-Authenticate header console.log(result.wwwAuthenticateScope) // e.g. 'openid read:tools' // If no auth needed (server returns 200), authorizationServerUrl === serverUrl // and authorizationServerMetadata may be populated from /.well-known/oauth-authorization-server ``` ``` -------------------------------- ### Setting up OAuth Callback Server with Long Polling Source: https://context7.com/geelen/mcp-remote/llms.txt Sets up an Express server for handling OAuth redirects and provides a long-polling endpoint for secondary instances to check authentication status. Useful for coordinating auth across concurrent processes. ```typescript import { setupOAuthCallbackServerWithLongPoll } from './lib/utils' import { EventEmitter } from 'events' const events = new EventEmitter() const { server, waitForAuthCode, authCompletedPromise } = setupOAuthCallbackServerWithLongPoll({ port: 17823, path: '/oauth/callback', events, authTimeoutMs: 30000, }) // In another process or concurrent instance, poll for completion: const pollResponse = await fetch('http://127.0.0.1:17823/wait-for-auth') // 202 → auth still in progress // 200 → auth completed (read tokens from disk) // The primary flow waits for the browser redirect: const authCode = await waitForAuthCode() // authCode = 'SplxlOBeZQQYbYS6WxSbIA...' // Auth completion promise (resolves with the code): authCompletedPromise.then((code) => { console.log('Auth complete, code starts with:', code.substring(0, 8)) }) // Shutdown server.close() ``` -------------------------------- ### `discoverProtectedResourceMetadata(resourceUrl, wwwAuthenticateHeader?)` Source: https://context7.com/geelen/mcp-remote/llms.txt Implements RFC 9728 Protected Resource Metadata discovery. It first attempts to retrieve metadata from the `resource_metadata` URL specified in a `WWW-Authenticate` header. If not provided or invalid, it falls back to checking standard well-known URLs (`/.well-known/oauth-protected-resource`) at both path-specific and root levels. ```APIDOC ## `discoverProtectedResourceMetadata(resourceUrl, wwwAuthenticateHeader?)` ### Description Implements RFC 9728 Protected Resource Metadata discovery. Tries the `resource_metadata` URL from a `WWW-Authenticate` header first, then falls back to path-specific and root-level `/.well-known/oauth-protected-resource` URLs in order. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters * **resourceUrl** (string) - The URL of the protected resource. * **wwwAuthenticateHeader** (string) - Optional. The `WWW-Authenticate` header value received from a 401 response. ### Request Example ```typescript import { discoverProtectedResourceMetadata, parseWWWAuthenticateHeader, getAuthorizationServerUrl, buildProtectedResourceMetadataUrls, } from './lib/protected-resource-metadata' // Parse WWW-Authenticate header from a 401 response const header = 'Bearer error="invalid_request", resource_metadata="https://api.example.com/.well-known/oauth-protected-resource", scope="openid read:tools"' const params = parseWWWAuthenticateHeader(header) console.log(params.resourceMetadataUrl) // 'https://api.example.com/.well-known/oauth-protected-resource' console.log(params.scope) // 'openid read:tools' // Well-known URL candidates for a given resource URL const urls = buildProtectedResourceMetadataUrls('https://api.example.com/v1/mcp') // ['https://api.example.com/.well-known/oauth-protected-resource/v1/mcp', // 'https://api.example.com/.well-known/oauth-protected-resource'] // Full discovery flow const metadata = await discoverProtectedResourceMetadata( 'https://api.example.com/v1/mcp', 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"', ) // metadata = { // resource: 'https://api.example.com', // authorization_servers: ['https://auth.example.com'], // scopes_supported: ['openid', 'read:tools', 'write:tools'], // bearer_methods_supported: ['header'] // } const authServerUrl = getAuthorizationServerUrl(metadata!) console.log(authServerUrl) // 'https://auth.example.com' ``` ### Response * **metadata** (object | null) - An object containing the protected resource metadata, or `null` if discovery fails. The object may contain fields like `resource`, `authorization_servers`, `scopes_supported`, and `bearer_methods_supported`. ``` -------------------------------- ### Multiple Remote MCP Server Instances Source: https://github.com/geelen/mcp-remote/blob/main/README.md Configure multiple instances of the same remote MCP server with different configurations, such as different Atlassian tenants. The '--resource' flag is used to isolate OAuth sessions for each instance. ```json { "mcpServers": { "atlassian_tenant1": { "command": "npx", "args": [ "mcp-remote", "https://mcp.atlassian.com/v1/sse", "--resource", "https://tenant1.atlassian.net/" ] }, "atlassian_tenant2": { "command": "npx", "args": [ "mcp-remote", "https://mcp.atlassian.com/v1/sse", "--resource", "https://tenant2.atlassian.net/" ] } } } ``` -------------------------------- ### Type Check Project Source: https://github.com/geelen/mcp-remote/blob/main/AGENT.md Run `pnpm check` to perform type checking with TypeScript and formatting checks with Prettier. ```bash pnpm check ``` -------------------------------- ### Remote MCP Server with Custom Headers Source: https://github.com/geelen/mcp-remote/blob/main/README.md Configure an MCP client to connect to a remote MCP server, including custom headers for authentication or other purposes. The AUTH_TOKEN environment variable is used to provide the token. ```json { "mcpServers": { "remote-example": { "command": "npx", "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--header", "Authorization: Bearer ${AUTH_TOKEN}" ], "env": { "AUTH_TOKEN": "..." } } } } ``` -------------------------------- ### Discover Protected Resource Metadata via RFC 9728 Source: https://context7.com/geelen/mcp-remote/llms.txt Implements RFC 9728 Protected Resource Metadata discovery. It first tries the `resource_metadata` URL from a `WWW-Authenticate` header. If not found, it falls back to path-specific and root-level `/.well-known/oauth-protected-resource` URLs. ```typescript import { discoverProtectedResourceMetadata, parseWWWAuthenticateHeader, getAuthorizationServerUrl, buildProtectedResourceMetadataUrls, } from './lib/protected-resource-metadata' // Parse WWW-Authenticate header from a 401 response const header = 'Bearer error="invalid_request", resource_metadata="https://api.example.com/.well-known/oauth-protected-resource", scope="openid read:tools"' const params = parseWWWAuthenticateHeader(header) console.log(params.resourceMetadataUrl) // 'https://api.example.com/.well-known/oauth-protected-resource' console.log(params.scope) // 'openid read:tools' // Well-known URL candidates for a given resource URL const urls = buildProtectedResourceMetadataUrls('https://api.example.com/v1/mcp') // ['https://api.example.com/.well-known/oauth-protected-resource/v1/mcp', // 'https://api.example.com/.well-known/oauth-protected-resource'] // Full discovery flow const metadata = await discoverProtectedResourceMetadata( 'https://api.example.com/v1/mcp', 'Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"', ) // metadata = { // resource: 'https://api.example.com', // authorization_servers: ['https://auth.example.com'], // scopes_supported: ['openid', 'read:tools', 'write:tools'], // bearer_methods_supported: ['header'] // } const authServerUrl = getAuthorizationServerUrl(metadata!) console.log(authServerUrl) // 'https://auth.example.com' ``` -------------------------------- ### Run MCP Remote in Client Mode Source: https://github.com/geelen/mcp-remote/blob/main/README.md Execute the MCP Remote client from your command line to test the authorization flow and list remote tools and resources. This is useful for diagnosing issues after clearing local credentials. ```shell npx -p mcp-remote@latest mcp-remote-client https://remote.mcp.server/sse ``` -------------------------------- ### fetchAuthorizationServerMetadata Source: https://context7.com/geelen/mcp-remote/llms.txt Fetches OAuth 2.0 Authorization Server Metadata from the /.well-known/oauth-authorization-server endpoint per RFC 8414. Returns the metadata object or undefined if the endpoint is absent or unreachable. ```APIDOC ## `fetchAuthorizationServerMetadata(serverUrl)` Fetches OAuth 2.0 Authorization Server Metadata from the `/.well-known/oauth-authorization-server` endpoint per RFC 8414. Returns the metadata object or `undefined` if the endpoint is absent or unreachable (non-fatal — discovery degrades gracefully). ### Parameters #### Path Parameters - **serverUrl** (string) - Required - The URL of the authorization server. ### Response - **metadata** (object | undefined) - The authorization server metadata object, or undefined if not found. ### Request Example ```typescript import { fetchAuthorizationServerMetadata } from './lib/authorization-server-metadata' const metadata = await fetchAuthorizationServerMetadata('https://auth.example.com') if (metadata) { console.log(metadata.issuer) console.log(metadata.authorization_endpoint) } ``` ``` -------------------------------- ### Enable Debug Logs for MCP Remote Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use the --debug flag to generate detailed logs for troubleshooting token refreshing, authentication, and connection issues. Logs are saved to ~/.mcp-auth/{server_hash}_debug.log. ```json "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--debug" ] ``` -------------------------------- ### Specify OAuth Callback Host Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use the `--host` flag followed by the desired host to change the host `mcp-remote` registers as the OAuth callback URL. ```json "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--host", "127.0.0.1" ] ``` -------------------------------- ### NodeOAuthClientProvider Source: https://context7.com/geelen/mcp-remote/llms.txt Implements the MCP SDK's OAuthClientProvider interface for Node.js. Manages OAuth dynamic client registration, PKCE code verifier lifecycle, token storage, and browser-based redirect. ```APIDOC ## NodeOAuthClientProvider ### Description Manages OAuth dynamic client registration, PKCE code verifier lifecycle, token storage, and browser-based redirect. All state is persisted per-server under `~/.mcp-auth/mcp-remote-/_*.json`. ### Methods #### `tokens()` Reads stored tokens. Returns `undefined` if none are found. #### `clientInformation()` Reads or generates client registration information. #### `saveTokens(tokens)` Saves tokens after a token exchange. This is called by the SDK internally. - **tokens** (object) - Required - An object containing `access_token`, `refresh_token`, `token_type`, and `expires_in`. #### `invalidateCredentials(type)` Invalidates specific credentials. - **type** (string) - Required - Can be `'tokens'` to remove only tokens, or `'all'` to remove tokens, client registration, and verifier. ### Properties #### `redirectUrl` (string) The redirect URL used in OAuth registration. ``` -------------------------------- ### Tail MCP Log Files on macOS/Linux Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use this command to view the last 20 lines of MCP log files in real-time on macOS or Linux systems. This is useful for debugging. ```bash tail -n 20 -F ~/Library/Logs/Claude/mcp*.log ``` -------------------------------- ### `getServerUrlHash(serverUrl, authorizeResource?, headers?)` Source: https://context7.com/geelen/mcp-remote/llms.txt Generates an MD5 hex hash to uniquely identify an OAuth session. This hash is derived from the server URL, an optional resource parameter, and sorted custom headers. It serves as a filesystem key for isolating token and client registration storage across different server configurations. ```APIDOC ## `getServerUrlHash(serverUrl, authorizeResource?, headers?)` ### Description Generates an MD5 hex hash that uniquely identifies an OAuth session by combining server URL, optional resource parameter, and sorted custom headers. Used as a filesystem key to isolate token and client registration storage between different server configurations. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters * **serverUrl** (string) - Required - The base URL of the server. * **authorizeResource** (string) - Optional - A specific resource to authorize. * **headers** (object) - Optional - Custom headers to include in the hash calculation. Keys are header names, values are header values. ### Request Example ```typescript import { getServerUrlHash } from './lib/utils' // Simple — just URL const hash1 = getServerUrlHash('https://api.example.com/mcp/sse') // e.g. 'a1b2c3d4e5f6...' (32 hex chars) // With resource — different session from hash1 const hash2 = getServerUrlHash( 'https://api.example.com/mcp/sse', 'https://workspace-a.example.com/', ) // With headers — different session again const hash3 = getServerUrlHash( 'https://api.example.com/mcp/sse', 'https://workspace-a.example.com/', { 'X-Tenant': 'acme', Authorization: 'Bearer token123' }, ) console.log(hash1 !== hash2) // true — isolated OAuth sessions console.log(hash2 !== hash3) // true // Files on disk: ~/.mcp-auth/mcp-remote-0.1.38/_tokens.json // ~/.mcp-auth/mcp-remote-0.1.38/_client_info.json // ~/.mcp-auth/mcp-remote-0.1.38/_code_verifier.txt // ~/.mcp-auth/mcp-remote-0.1.38/_lock.json ``` ### Response * **hash** (string) - A 32-character hexadecimal MD5 hash. ``` -------------------------------- ### Ignore Specific Tools with Wildcards Source: https://github.com/geelen/mcp-remote/blob/main/README.md Employ the `--ignore-tool` flag followed by wildcard patterns to filter out specific tools from `tools/list` responses and block `tools/call` requests. Multiple flags can be used for different patterns. ```json "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--ignore-tool", "delete*", "--ignore-tool", "remove*" ] ``` -------------------------------- ### Run Tests in Watch Mode Source: https://github.com/geelen/mcp-remote/blob/main/test/README.md Execute tests continuously, automatically re-running them when changes are detected. ```bash pnpm test:watch ``` -------------------------------- ### Tail MCP Log Files with PowerShell Source: https://github.com/geelen/mcp-remote/blob/main/README.md Use this command in PowerShell to continuously monitor the last 20 lines of the MCP log file. Replace 'YourUsername' with your actual Windows username. ```powershell Get-Content "C:\Users\YourUsername\AppData\Local\Claude\Logs\mcp.log" -Wait -Tail 20 ``` -------------------------------- ### createMessageTransformer Source: https://context7.com/geelen/mcp-remote/llms.txt Creates a request/response interceptor pair that tracks in-flight requests by ID, applies an optional request transform, and applies an optional response transform with access to the original request. ```APIDOC ## `createMessageTransformer({ transformRequestFunction?, transformResponseFunction? })` Creates a request/response interceptor pair that tracks in-flight requests by ID, applies an optional request transform (returning `MESSAGE_BLOCKED` to drop the message), and applies an optional response transform with access to the original request. Used internally by `mcpProxy` for tool filtering. ### Parameters #### Options - **transformRequestFunction** (function) - Optional - A function to transform outgoing requests. - **transformResponseFunction** (function) - Optional - A function to transform incoming responses. ### Response - **transformer** (object) - An object with `interceptRequest` and `interceptResponse` methods. ### Request Example ```typescript import { createMessageTransformer } from './lib/utils' const transformer = createMessageTransformer({ transformRequestFunction: (request) => { if (request.method === 'tools/call' && request.params?.name === 'sendEmail') { return { ...request, params: { ...request.params, arguments: { ...request.params.arguments, body: '[REDACTED]' } }, } } return request }, transformResponseFunction: (originalRequest, response) => { return { ...response, _method: originalRequest.method } }, }) const modifiedRequest = transformer.interceptRequest({ jsonrpc: '2.0', id: '1', method: 'tools/call', params: { name: 'sendEmail', arguments: { to: 'user@example.com', body: 'Secret content' } } }) const annotatedResponse = transformer.interceptResponse({ jsonrpc: '2.0', id: '1', result: { content: [{ type: 'text', text: 'Email sent' }] } }) ``` ``` -------------------------------- ### Fetch OAuth Authorization Server Metadata Source: https://context7.com/geelen/mcp-remote/llms.txt Fetches OAuth 2.0 Authorization Server Metadata from the /.well-known/oauth-authorization-server endpoint. Returns the metadata object or undefined if the endpoint is absent or unreachable. ```typescript import { fetchAuthorizationServerMetadata, getMetadataUrl } from './lib/authorization-server-metadata' // Construct the well-known URL const metadataUrl = getMetadataUrl('https://auth.example.com') console.log(metadataUrl) // 'https://auth.example.com/.well-known/oauth-authorization-server' // Fetch metadata with 5-second timeout const metadata = await fetchAuthorizationServerMetadata('https://auth.example.com') if (metadata) { console.log(metadata.issuer) // 'https://auth.example.com' console.log(metadata.authorization_endpoint) // 'https://auth.example.com/oauth/authorize' console.log(metadata.token_endpoint) // 'https://auth.example.com/oauth/token' console.log(metadata.scopes_supported) // ['openid', 'email', 'profile', 'read:tools'] console.log(metadata.grant_types_supported) // ['authorization_code', 'refresh_token'] } else { // Server doesn't expose RFC 8414 metadata — OAuth still proceeds using defaults console.log('No Authorization Server Metadata found, using discovery fallbacks') } ```