Yassine Mhirsi
added mcp tools in chatbot settings icon
5193b92
---
alwaysApply: true
---
# NLP IBM Debater - Cursor Rules
This is a React + TypeScript project using Create React App, Tailwind CSS, and a modular feature-first architecture.
## File Extensions & TypeScript
- **`.tsx`** - Use for React components (components, pages, layouts)
- **`.ts`** - Use for non-JSX files (hooks, utils, types, services)
- **`.js`** - Only for config files (index.js, config files)
- **Always type everything**: props, functions, API responses, hook returns
## Project Structure
```
src/app/
β”œβ”€β”€ App.tsx # Entry composition
β”œβ”€β”€ layouts/ # Layout components (.tsx)
β”œβ”€β”€ pages/ # Page components (.tsx)
β”œβ”€β”€ components/ # Reusable UI (.tsx)
β”‚ └── common/ # Common components (Loading, ErrorBoundary)
β”œβ”€β”€ hooks/ # Custom hooks (.ts)
β”œβ”€β”€ services/ # API services (.ts)
β”œβ”€β”€ types/ # TypeScript types (.ts)
β”œβ”€β”€ utils/ # Utility functions (.ts)
└── constants/ # App constants (.ts)
```
## Code Patterns
### Components
- Use functional components with plain arrow functions (avoid `React.FC` due to local React type limitations)
- Always type props with interfaces or types
- Use Tailwind CSS for styling (avoid custom CSS files)
- Keep components presentational - pass data via props
```typescript
type MyComponentProps = {
title: string;
count?: number;
};
const MyComponent = ({ title, count = 0 }: MyComponentProps) => {
return <div>{title}: {count}</div>;
};
```
### Authentication & State Management
- Use `useAuth` hook for reactive authentication state instead of static `isUserRegistered()` calls
- Always use `setUserIdAndNotify()` when logging in/registering to ensure immediate UI updates
- Never call `isUserRegistered()` directly in components - use the `useAuth` hook instead
- Authentication state should be reactive across all components
- Use custom hooks for any state that needs to be shared across multiple components
```typescript
// src/app/hooks/useAuth.ts - Reactive authentication hook
import { useState, useEffect } from 'react';
import { isUserRegistered } from '../utils/index.ts';
export const useAuth = () => {
const [isAuthenticated, setIsAuthenticated] = useState(isUserRegistered());
useEffect(() => {
const checkAuth = () => setIsAuthenticated(isUserRegistered());
// Listen for auth changes
window.addEventListener('storage', (e) => {
if (e.key === 'user_id') checkAuth();
});
window.addEventListener('authChange', checkAuth);
return () => {
window.removeEventListener('storage', checkAuth);
window.removeEventListener('authChange', checkAuth);
};
}, []);
return { isAuthenticated };
};
// Usage in components
const MyComponent = () => {
const { isAuthenticated } = useAuth();
// Component will re-render when authentication state changes
};
// When setting user authentication, use the notifying version
import { setUserIdAndNotify } from '../utils/index.ts';
setUserIdAndNotify(user.id); // Triggers immediate UI updates
// When logging out, use the notifying version
import { logoutAndNotify } from '../utils/index.ts';
logoutAndNotify(); // Clears user data and triggers immediate UI updates
```
### Route Protection
- Use `ProtectedRoute` component in `components/common/` for authentication-based routing
- Always type `children` as optional (`children?: any`) for route wrapper components
- Wrap protected content in React Fragment (`<>{children}</>`) when returning children
```typescript
// src/app/components/common/ProtectedRoute.tsx
import React from 'react';
import { Navigate } from 'react-router-dom';
import { isUserRegistered } from '../../utils/index.ts';
type ProtectedRouteProps = {
children?: any; // Optional children prop for JSX content
};
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const isAuthenticated = isUserRegistered();
if (!isAuthenticated) {
return <Navigate to="/" replace />;
}
return <>{children}</>; // Wrap in fragment for proper React element return
};
export default ProtectedRoute;
```
```typescript
// Usage in App.tsx routing
<Route
path="/protected"
element={
<ProtectedRoute>
<ProtectedPage />
</ProtectedRoute>
}
/>
```
### React & TypeScript Typing Constraints (Local Setup)
- **React component types**
- ❌ Avoid `React.FC`, `ReactNode`, and React-specific event types (`FormEvent`, `ChangeEvent`, etc.) – they are not available in this project's React type setup and will cause errors like "Namespace 'react' has no exported member 'FC'".
- βœ… Prefer plain arrow components with explicitly typed props:
- `type Props = { ... }`
- `const Component = (props: Props) => { ... }`
- For `children` props:
- **Route wrapper components** (like `ProtectedRoute`): Use `children?: any` (optional)
- **Regular components**: Use `children: any` (required) or omit if not needed
- **Hooks and generics**
- ❌ Do **not** pass generic type parameters to React hooks such as `useState` (e.g., `useState<string>()` or `useState<MyType>()`). This triggers β€œExpected 0 type arguments, but got 1.”
- βœ… Let hooks infer types from initial values, and when generics are needed:
- Use a type assertion on the initial state:
- `const [state, setState] = useState({ ... } as MyStateType);`
- Or, for tuple-like state:
- `const [value, setValue] = useState(() => initialValue) as [MyType, (next: MyType | ((prev: MyType) => MyType)) => void];`
- **Error boundary**
- The only class component should be `ErrorBoundary`, and it should:
- Import only `Component` from React (no `ErrorInfo`, `ReactNode`).
- Extend `Component` without generic parameters: `class ErrorBoundary extends Component { ... }`.
- Declare its `state` field explicitly with a typed object (e.g., `state = { hasError: false, error: null }`).
### API Calls
- Use `api-wrapper.ts` for direct API calls
- Use `useApi` hook for components needing loading/error states
- Always type API responses
```typescript
// Direct API call (note: include .ts extension)
import api from '../services/api-wrapper.ts';
const data = await api.get<User[]>('/api/users');
// With hook (note: include .ts extension)
import { useApi } from '../hooks/useApi.ts';
const { data, loading, error } = useApi<User[]>('/api/users');
```
### Types
- Add types to `src/app/types/` and export from `index.ts`
- Use descriptive type names
- Export types for reuse
```typescript
// src/app/types/debater.types.ts
export type Debate = {
id: number;
topic: string;
arguments: Argument[];
};
// When importing types, use explicit extension:
import type { Debate } from '../types/debater.types.ts';
```
### Constants
- Add constants to `src/app/constants/index.ts`
- Use `API_ENDPOINTS` for API paths
- Use `APP_CONFIG` for app configuration
- Use `UI` for UI-related constants
### Utils
- Add utility functions to `src/app/utils/index.ts`
- Keep functions pure and typed
- Export for reuse
### Custom Hooks & Data Fetching
- Create custom hooks in `src/app/hooks/` for reusable data-fetching logic
- Use `useCallback` to memoize async functions and prevent unnecessary re-renders
- Implement intelligent caching with localStorage (use `cache.utils.ts`)
- Handle loading, error, and empty states explicitly
- Use defensive type checking for API responses:
```typescript
// Example: Custom hook with caching and error handling
import { useState, useEffect, useCallback } from 'react';
import api from '../services/api-wrapper.ts';
import type { MyType } from '../types/index.ts';
import { cacheMyData, getCachedMyData } from '../utils/cache.utils.ts';
type MyDataState = {
data: MyType[];
loading: boolean;
error: Error | null;
};
const isValidData = (value: unknown): value is MyType => {
if (!value || typeof value !== 'object') return false;
const candidate = value as { id?: unknown; name?: unknown };
return typeof candidate.id === 'number' && typeof candidate.name === 'string';
};
const normalizeResponse = (payload: unknown): MyType[] | null => {
if (Array.isArray(payload)) return payload.filter(isValidData);
if (payload && typeof payload === 'object') {
const candidate = payload as { data?: unknown; items?: unknown };
if (Array.isArray(candidate.data)) return candidate.data.filter(isValidData);
if (Array.isArray(candidate.items)) return candidate.items.filter(isValidData);
}
return null;
};
export const useMyData = () => {
const [state, setState] = useState<MyDataState>({ data: [], loading: false, error: null });
const fetchData = useCallback(async () => {
try {
const cached = getCachedMyData();
if (cached?.length > 0) {
setState({ data: cached, loading: false, error: null });
} else {
setState(prev => ({ ...prev, loading: true, error: null }));
}
const payload = await api.get<unknown>('/api/endpoint');
const normalized = normalizeResponse(payload);
if (!normalized) throw new Error('Invalid response shape');
setState({ data: normalized, loading: false, error: null });
cacheMyData(normalized);
} catch (err) {
setState({
data: getCachedMyData() ?? [],
loading: false,
error: err instanceof Error ? err : new Error('Failed to fetch data'),
});
}
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
return { ...state, refetch: fetchData };
};
```
### UI Components & Interactions
- Use refs (`useRef`) for managing DOM elements that need direct access (e.g., dropdown containers)
- Implement click-outside detection for dropdowns/modals:
- Add `mousedown` listener (not `click`) to capture events before bubbling
- Always clean up event listeners in `useEffect` return
- Implement state visibility toggles that also trigger data refetch on open:
- Useful for keeping data fresh when user opens a dropdown/modal
- Add proper ARIA attributes for accessibility:
- `aria-expanded` on toggle buttons
- `aria-haspopup` on buttons that open menus
- Limit list item display (e.g., `slice(0, 6)`) to prevent UI overflow
```typescript
// Dropdown component pattern
const dropdownRef = useRef<HTMLDivElement>(null);
const [showDropdown, setShowDropdown] = useState(false);
// Close on click outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setShowDropdown(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
// Toggle with refetch on open
const toggleDropdown = () => {
const nextState = !showDropdown;
setShowDropdown(nextState);
if (nextState) {
refetch(); // Refresh data when opening
}
};
```
## Conventions
1. **Authentication**:
- Use `setUserIdAndNotify()` for login/registration (immediate UI updates)
- Use `logoutAndNotify()` for logout (immediate UI updates)
- Use `setUserId()` only for background operations or initialization
- Always use `useAuth()` hook in components instead of `isUserRegistered()`
2. **Imports**:
- Use relative paths from `src/`
- **ALWAYS include explicit file extensions** in imports:
- Use `.tsx` for React components (e.g., `import Component from './Component.tsx'`)
- Use `.ts` for TypeScript files (e.g., `import { func } from './utils/index.ts'`)
- Use `/index.ts` for directory barrel exports (e.g., `import { something } from './utils/index.ts'`)
- Use `type` keyword for type imports (e.g., `import type { Type } from './types.ts'`)
- This is required for webpack module resolution in this project
- **Import organization order**:
1. React and React hooks (e.g., `import React, { useState } from 'react'`)
2. Third-party libraries (e.g., `import { Icon } from 'lucide-react'`)
3. Local imports (hooks, types, components, services, utils)
4. Type imports last (e.g., `import type { MyType } from './types.ts'`)
2. **Styling**:
- Tailwind-first, avoid custom CSS files
- For utility classes needed globally (e.g., `.scrollbar-hide`), add to `src/index.css` with appropriate vendor prefixes
- Use Tailwind modifiers for responsive, dark mode, and interactive states
- Example utilities to `src/index.css`:
```css
.scrollbar-hide {
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
```
3. **Error Handling**: Use ErrorBoundary for React errors, try/catch for API errors
4. **Loading States**: Use `Loading` component from `components/common/Loading`
5. **Environment Variables**: All must start with `REACT_APP_` prefix
6. **Testing**: Use React Testing Library, co-locate tests
## Response Normalization & API Best Practices
- **Assume API responses may vary**: Backend APIs can return data in different shapes (direct array, `.data` property, `.items` property, etc.)
- **Always implement type guards**: Use predicate functions to validate each item before using
- **Normalize responses early**: Convert varied response shapes into a consistent internal format in hooks/services
- **Handle edge cases**: Plan for empty responses, null values, and unexpected data structures
```typescript
// βœ… GOOD: Defensive response handling
const isValidTool = (value: unknown): value is MCPTool => {
if (!value || typeof value !== 'object') return false;
const candidate = value as { name?: unknown; description?: unknown };
return typeof candidate.name === 'string' &&
(candidate.description === undefined || typeof candidate.description === 'string');
};
const normalizeToolsResponse = (payload: unknown): MCPTool[] | null => {
if (Array.isArray(payload)) return payload.filter(isValidTool);
if (payload && typeof payload === 'object') {
const candidate = payload as { tools?: unknown; data?: unknown };
if (Array.isArray(candidate.tools)) return candidate.tools.filter(isValidTool);
if (Array.isArray(candidate.data)) return candidate.data.filter(isValidTool);
}
return null;
};
```
## What NOT to Do
- ❌ Don't use class components (except ErrorBoundary)
- ❌ Don't use `.jsx` - use `.tsx` for components
- ❌ Don't use raw `fetch` - use `api-wrapper.ts`
- ❌ Don't create custom CSS files - use Tailwind
- ❌ Don't commit `.env*` files (except `.env.example`)
- ❌ Don't skip TypeScript types
- ❌ Don't call `isUserRegistered()` directly in components - use `useAuth()` hook instead
- ❌ Don't assume API response shapes - always normalize and validate
- ❌ Don't forget to clean up event listeners in useEffect return statements
- ❌ Don't use inline anonymous functions in onClick/onChange - prefer named handlers
- ❌ Don't skip error states in loading components - implement proper error UI
## When Adding New Features
1. **New Page**: Create in `src/app/pages/` (`.tsx`), wire via `App.tsx`
- Import with `.tsx` extension: `import NewPage from './pages/NewPage.tsx'`
2. **New Component**: Create in `src/app/components/` (`.tsx`), type props
- Import with `.tsx` extension: `import Component from './components/Component.tsx'`
3. **New API Endpoint**: Add to `API_ENDPOINTS` in constants, use `api-wrapper.ts`
- Import services with `.ts` extension: `import { service } from './services/service.ts'`
4. **New Type**: Add to `src/app/types/`, export from `index.ts`
- Import types with `.ts` extension: `import type { Type } from '../types/type.types.ts'`
5. **New Hook**: Create in `src/app/hooks/` (`.ts`), type return values
- Import hooks with `.ts` extension: `import { useHook } from '../hooks/useHook.ts'`
6. **New Utility**: Add to `src/app/utils/index.ts`, keep pure and typed
- Export with `.ts` extension: `export * from './new-util.ts'`
- Import from index: `import { util } from '../utils/index.ts'`
## Quality Checks
- Run `npm run build` to verify TypeScript compilation
- Run `npm test` before committing
- Ensure all props are typed
- Ensure all API calls use typed responses
- Follow existing code patterns and structure