diff --git a/.gitignore b/.gitignore index 2807b72..b613deb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .mcp-cli dist .env +.aider* diff --git a/README.md b/README.md index 08896e7..7619400 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,9 @@ This main package (use-mcp) exports utilities primarily for handling OAuth in th For React applications, the easiest way to use this library is via the useMcp hook. -See the React README for detailed usage instructions and examples. -Basic OAuth Callback Setup +See the [src/react/README.md](src/react/README.md) for detailed usage instructions and examples. + +## Basic OAuth Callback Setup You need an endpoint in your application (e.g., /oauth/callback) that the OAuth server redirects the user back to after they approve the authorization request. This endpoint should call onMcpAuthorization. diff --git a/src/react/README.md b/src/react/README.md index 2dbe273..cf380f5 100644 --- a/src/react/README.md +++ b/src/react/README.md @@ -21,12 +21,12 @@ The useMcp hook manages the connection to an MCP server, handles authentication ## Importing ```tsx -import { useMcp } from 'use-mcp/react'; +import { useMcp } from 'use-mcp'; // Optional: Import types if needed -import type { UseMcpOptions, UseMcpResult, Tool } from 'use-mcp/react'; +import type { UseMcpOptions, UseMcpResult, Tool } from 'use-mcp'; ``` -## Basic Usage +## Usage ```tsx import React from 'react'; @@ -142,7 +142,7 @@ function MyChatComponent() { export default MyChatComponent; ``` -Hook Options (UseMcpOptions) +### Hook Options (UseMcpOptions) * url (required): The /sse URL of your MCP server. * clientName: Name used for OAuth dynamic client registration (if applicable). Defaults to "MCP React Client". @@ -155,7 +155,7 @@ Hook Options (UseMcpOptions) * autoReconnect: If true or a number (milliseconds), automatically tries to reconnect if an established connection is lost. Defaults to 3000 (3 seconds). Set to false to disable. * popupFeatures: The features string passed to window.open for the OAuth popup. Defaults to "width=600,height=700,resizable=yes,scrollbars=yes,status=yes". -Hook Return Value (UseMcpResult) +### Hook Return Value (UseMcpResult) * tools: An array of Tool objects provided by the server. Empty until state is 'ready'. * state: The current connection state ('discovering', 'authenticating', 'connecting', 'loading', 'ready', 'failed'). Use this to conditionally render UI or enable/disable features. @@ -168,6 +168,6 @@ Hook Return Value (UseMcpResult) * authenticate(): Manually attempts to start the authentication flow. Useful for triggering the popup via a user click if it was initially blocked. * clearStorage(): Removes all authentication-related data (tokens, client info, code verifier, state) for the configured server URL from localStorage. Useful for development or allowing users to "log out". Automatically disconnects the client. -Setting up the OAuth Callback Route +### Setting up the OAuth Callback Route Remember to create the callback route (e.g., /oauth/callback) specified in callbackUrl and have it execute the onMcpAuthorization function exported from the main use-mcp package. See the root README for an example. \ No newline at end of file diff --git a/src/react/useMcp.ts b/src/react/useMcp.ts index b50689b..8ca250b 100644 --- a/src/react/useMcp.ts +++ b/src/react/useMcp.ts @@ -4,8 +4,8 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { discoverOAuthMetadata, exchangeAuthorization, startAuthorization, UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'; import { OAuthClientInformation, OAuthMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js'; -import { BrowserOAuthClientProvider } from '../../auth/browser-provider.js'; -import { assert } from '../../utils/assert.js'; +import { BrowserOAuthClientProvider } from '../auth/browser-provider.js'; +import { assert } from '../utils/assert.js'; import type { UseMcpOptions, UseMcpResult } from './types.js'; const DEFAULT_RECONNECT_DELAY = 3000; @@ -47,7 +47,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { const connectingRef = useRef(false); // Prevent concurrent connection attempts const isMountedRef = useRef(true); // Track component mount status const connectAttemptRef = useRef(0); // Track connection attempts for retry logic - const authTimeoutRef = useRef(null); // Timer for auth popup + const authTimeoutRef = useRef(null); // Timer for auth popup // ===== Logging Utility ===== @@ -262,6 +262,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { } transportRef.current = new SSEClientTransport(serverUrl, { + // @ts-ignore authProvider: authProviderRef.current, // Pass debug flag to transport if it supports it }); @@ -272,6 +273,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { // @ts-expect-error - SDK type might be union, check message content addLog('debug', `[Transport] Received message: Method=${message.method || 'N/A'} ID=${message.id || 'N/A'}`); // Forward notifications/responses to the client instance + // @ts-ignore clientRef.current?.handleMessage(message); }; @@ -534,7 +536,7 @@ export function useMcp(options: UseMcpOptions): UseMcpResult { // Effect for auto-retry logic useEffect(() => { - let retryTimeoutId: NodeJS.Timeout | null = null; + let retryTimeoutId: number | null = null; if (state === 'failed' && autoRetry && connectAttemptRef.current > 0) { // Only retry if the *initial* connection or subsequent retries failed const delay = typeof autoRetry === 'number' ? autoRetry : DEFAULT_RETRY_DELAY; diff --git a/tsconfig.json b/tsconfig.json index cd9cfa1..d97121e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "esModuleInterop": true, "noEmit": true, "lib": ["ES2022", "DOM"], - "types": ["node", "react"], + "types": ["react"], "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }