> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-assistant.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Integrate mcp-ts with Next.js: create an MCP API route with createNextMcpHandler, wire up authentication, and connect from React with the useMcp hook.

Complete guide for integrating mcp-ts with Next.js applications (App Router and Pages Router).

## App Router (Recommended)

### Step 1: Create API Route

Create an API route handler at `app/api/mcp/route.ts`:

```typescript theme={null}
import { createNextMcpHandler } from '@mcp-ts/client/server';

export const dynamic = 'force-dynamic';
export const runtime = 'nodejs';

export const { GET, POST } = createNextMcpHandler({
  // Extract userId from request
  getUserId: (request) => {
    return new URL(request.url).searchParams.get('userId');
  },

  // Optional: Custom authentication
  authenticate: async (userId, token) => {
    // Verify token with your auth system
    return true; // or throw error if invalid
  },

  // Optional: Heartbeat interval
  heartbeatInterval: 30000, // 30 seconds
});
```

### Step 2: Create Client Component

Create a component at `components/McpConnections.tsx`:

```typescript theme={null}
'use client';

import { useMcp } from '@mcp-ts/client/client/react';

export function McpConnections({ userId }: { userId: string }) {
  const {
    connections,
    status,
    connect,
    disconnect,
    callTool,
  } = useMcp({
    url: `/api/mcp?userId=${userId}`,
    userId,
    autoConnect: true,
  });

  const handleConnect = async () => {
    await connect({
      serverId: 'my-server',
      serverName: 'My MCP Server',
      serverUrl: 'https://mcp.example.com',
      callbackUrl: window.location.origin + '/api/mcp/callback',
    });
  };

  return (
    <div>
      <div>
        <h2>MCP Connections</h2>
        <p>Status: <strong>{status}</strong></p>
        <button onClick={handleConnect}>
          Connect to Server
        </button>
      </div>

      {connections.map((conn) => (
        <div key={conn.sessionId}>
          <h3>{conn.serverName}</h3>
          <p>State: {conn.state}</p>
          <p>Available Tools: {conn.tools.length}</p>

          {conn.state === 'CONNECTED' && (
            <div>
              {conn.tools.map((tool) => (
                <button
                  key={tool.name}
                  onClick={() => callTool(conn.sessionId, tool.name, {})}
                >
                  {tool.name}
                </button>
              ))}
              <button onClick={() => disconnect(conn.sessionId)}>
                Disconnect
              </button>
            </div>
          )}
        </div>
      ))}
    </div>
  );
}
```

### Step 3: Use in Page

Use the component in your page at `app/page.tsx`:

```typescript theme={null}
import { McpConnections } from '@/components/McpConnections';

export default function Home() {
  // Get userId from your auth system
  const userId = 'user-123'; // Replace with actual userId

  return (
    <main>
      <h1>My App</h1>
      <McpConnections userId={userId} />
    </main>
  );
}
```

## AI SDK

To build agentic workflows that use tools from multiple MCP servers, use `MultiSessionClient`.

```typescript theme={null}
// app/api/chat/route.ts
import { MultiSessionClient } from '@mcp-ts/client/server';
import { AIAdapter } from '@mcp-ts/client/adapters/ai';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

export async function POST(req: Request) {
  const { messages, userId } = await req.json();

  const client = new MultiSessionClient(userId);

  try {
    await client.connect();

    const adapter = new AIAdapter(client);
    const tools = await adapter.getTools();

    const result = streamText({
      model: openai('gpt-4'),
      messages,
      tools,
      onFinish: async () => {
        await client.disconnect();
      }
    });

    return result.toDataStreamResponse();
  } catch (error) {
    await client.disconnect();
    throw error;
  }
}
```

For more details, see the [AI SDK Adapter documentation](/ai-adapters/ai-sdk).

## Pages Router

### Step 1: Create API Route

Create `pages/api/mcp.ts`:

```typescript theme={null}
import { createSSEHandler } from '@mcp-ts/client/server';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const userId = req.query.userId as string;

  if (!userId) {
    return res.status(400).json({ error: 'userId required' });
  }

  const sseHandler = createSSEHandler({
    userId,
    heartbeatInterval: 30000,
  });

  return sseHandler(req, res);
}
```

### Step 2: Create Component

Same as App Router component above.

### Step 3: Use in Page

```typescript theme={null}
import { McpConnections } from '@/components/McpConnections';

export default function Home() {
  const userId = 'user-123';

  return (
    <div>
      <h1>My App</h1>
      <McpConnections userId={userId} />
    </div>
  );
}
```

## OAuth Callback Handler

Handle OAuth callbacks at `app/oauth/callback-popup/page.tsx` (for popups) or `app/oauth/callback/page.tsx` (for redirects):

```typescript theme={null}
'use client';

import { useEffect } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import { useMcp } from '@mcp-ts/client/client/react';

export default function OAuthCallback() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const { finishAuth } = useMcp({
    url: '/api/mcp',
    userId: 'user-123',
  });

  useEffect(() => {
    const code = searchParams.get('code');
    const state = searchParams.get('state');

    if (code && state) {
      finishAuth(state, code)
        .then(() => {
          router.push('/'); // Redirect back to main page
        })
        .catch((error) => {
          console.error('OAuth failed:', error);
        });
    }
  }, [searchParams, finishAuth, router]);

  return <div>Completing authentication...</div>;
}
```

### Popup helpers

If you want a turnkey popup flow in Next.js, the React client also exports:

* `createOAuthPopupRedirectHandler()` for `useMcp({ onRedirect })`
* `useMcpOAuthPopup(...)` for opener-side popup coordination
* `McpOAuthCallbackContent` for the callback popup page UI/logic

```tsx theme={null}
'use client';

import { Suspense, useMemo } from 'react';
import { useSearchParams } from 'next/navigation';
import {
  createOAuthPopupRedirectHandler,
  McpOAuthCallbackContent,
  McpOAuthCallbackFallback,
  useMcp,
  useMcpOAuthPopup,
} from '@mcp-ts/client/client/react';

function McpPopupBridge() {
  const mcpClient = useMcp({
    url: '/api/mcp',
    userId: 'user-123',
    onRedirect: useMemo(() => createOAuthPopupRedirectHandler(), []),
  });

  useMcpOAuthPopup(mcpClient.connections, mcpClient.finishAuth);
  return null;
}

function OAuthPopupPageInner() {
  const searchParams = useSearchParams();

  return (
    <McpOAuthCallbackContent
      code={searchParams.get('code')}
      sessionId={searchParams.get('state')}
    />
  );
}

export function OAuthPopupPage() {
  return (
    <Suspense fallback={<McpOAuthCallbackFallback />}>
      <OAuthPopupPageInner />
    </Suspense>
  );
}
```

These helpers are optional. If you prefer a branded popup page, pass custom
styles/props to `McpOAuthCallbackContent`, or skip popups entirely and use a
normal redirect callback page with `finishAuth(state, code)`.

## Environment Variables

Add to `.env.local`:

```bash theme={null}
# Redis connection
REDIS_URL=redis://localhost:6379

# Or for Upstash Redis
REDIS_URL=rediss://default:password@host.upstash.io:6379
```

## Production Deployment

### Vercel

1. **Add environment variable** in Vercel dashboard:
   * `REDIS_URL` - Your Redis connection string

2. **Deploy**:

```bash theme={null}
vercel deploy
```

### Other Platforms

Ensure your platform supports:

* Node.js runtime (for API routes)
* Environment variables
* WebSocket/SSE connections

## Complete Example

Here's a full working example:

```typescript title="app/api/mcp/route.ts" theme={null}
import { createNextMcpHandler } from '@mcp-ts/client/server';

export const dynamic = 'force-dynamic';
export const runtime = 'nodejs';

export const { GET, POST } = createNextMcpHandler({
  getUserId: (request) => {
    const userId = new URL(request.url).searchParams.get('userId');
    if (!userId) throw new Error('userId required');
    return userId;
  },
});
```

```typescript title="components/McpClient.tsx" theme={null}
'use client';

import { useMcp } from '@mcp-ts/client/client/react';
import { useState } from 'react';

export function McpClient({ userId }: { userId: string }) {
  const { connections, connect, callTool, status } = useMcp({
    url: `/api/mcp?userId=${userId}`,
    userId,
    autoConnect: true,
  });

  const [result, setResult] = useState<any>(null);

  const handleToolCall = async (sessionId: string, toolName: string) => {
    try {
      const res = await callTool(sessionId, toolName, {});
      setResult(res);
    } catch (error) {
      console.error('Tool call failed:', error);
    }
  };

  return (
    <div>
      <h2>MCP Client ({status})</h2>

      {connections.map(conn => (
        <div key={conn.sessionId}>
          <h3>{conn.serverName}</h3>
          <p>{conn.state}</p>

          {conn.tools.map(tool => (
            <button
              key={tool.name}
              onClick={() => handleToolCall(conn.sessionId, tool.name)}
            >
              {tool.name}
            </button>
          ))}
        </div>
      ))}

      {result && (
        <pre>{JSON.stringify(result, null, 2)}</pre>
      )}
    </div>
  );
}
```

## Next Steps

* [React Hook API](/react) - Detailed hook documentation
* [API Reference](/reference/server) - Complete API reference
* [Examples](https://github.com/zonlabs/mcp-ts/tree/main/examples) - More code examples
