---
title: "Anthropic Claude"
description: "Use MCP tools with Anthropic's Claude API"
---
The Integrate SDK provides seamless integration with Anthropic's Claude API, allowing Claude to access your integrations through MCP tools.

## Installation

Install the Integrate SDK and Anthropic SDK packages:

```bash
bun add integrate-sdk @anthropic-ai/sdk
```

## Setup

Setting up Anthropic integration requires **3 files** (or **5 files** if not using a database):

### 1. Server Configuration

Create a server configuration file with your OAuth credentials:

```typescript
// lib/integrate.ts
import { createMCPServer, githubIntegration } from "integrate-sdk/server";

export const { client: serverClient } = createMCPServer({
  apiKey: process.env.INTEGRATE_API_KEY,
  integrations: [
    githubIntegration({
      scopes: ["repo", "user"],
    }),
  ],
});
```

### 2. Providers (only if no database)

If you're not using a database, create a providers component that injects tokens:

```typescript
// app/providers.tsx
"use client";

import { client } from "integrate-sdk";
import { useIntegrateAI } from "integrate-sdk/react";

export function Providers({ children }: { children: React.ReactNode }) {
  useIntegrateAI(client);
  return <>{children}</>;
}
```

### 3. Layout (only if no database)

Wrap your app with the Providers component:

```typescript
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

### 4. OAuth Handler

Create a catch-all route that handles OAuth operations at `app/api/integrate/[...all]/route.ts`:

```typescript
import { serverClient } from "@/lib/integrate";
import { toNextJsHandler } from "integrate-sdk/server";

export const { POST, GET } = toNextJsHandler(serverClient);
```

### 5. Chat API Route

Create a chat route that converts MCP tools to Anthropic format:

```typescript
// app/api/chat/route.ts
import { serverClient } from "@/lib/integrate";
import { getAnthropicTools } from "integrate-sdk/server";
import Anthropic from "@anthropic-ai/sdk";
import { handleAnthropicMessage } from "integrate-sdk/ai/anthropic";

const anthropic = new Anthropic();

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

  const msg = await anthropic.messages.create({
    model: "claude-sonnet-4-5",
    messages,
    tools: await getAnthropicTools(serverClient),
    max_tokens: 1000,
  });

  const result = await handleAnthropicMessage(serverClient, msg);

  return Response.json(result);
}
```

## Usage

Use Anthropic's Claude in any component:

```typescript
// app/page.tsx
"use client";

import { useState, useEffect } from "react";
import { client } from "integrate-sdk";
import Anthropic from "@anthropic-ai/sdk";
import { Streamdown } from "streamdown";

export default function ChatPage() {
  const [messages, setMessages] = useState<Anthropic.MessageParam[]>([]);
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);
  const [githubAuthorized, setGithubAuthorized] = useState(false);

  useEffect(() => {
    client.isAuthorized("github").then(setGithubAuthorized);
  }, []);

  async function handleGithubClick() {
    try {
      if (githubAuthorized) {
        await client.disconnectProvider("github");
      } else {
        await client.authorize("github");
      }
      setGithubAuthorized(client.isAuthorized("github"));
    } catch (error) {
      console.error("Error:", error);
    }
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!input.trim()) return;

    const userMessage: Anthropic.MessageParam = {
      role: "user",
      content: input,
    };
    setMessages((prev) => [...prev, userMessage]);
    setInput("");
    setLoading(true);

    try {
      const response = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          messages: [...messages, userMessage],
        }),
      });

      const data = await response.json();

      const newMessages = Array.isArray(data) ? data : [data];

      setMessages((prev) => [
        ...prev,
        ...newMessages.map((msg: Anthropic.MessageParam) => ({
          role: msg.role,
          content: msg.content,
        })),
      ]);
    } catch (error) {
      console.error("Error:", error);
    } finally {
      setLoading(false);
    }
  }

  const renderContent = (
    content: Anthropic.MessageParam["content"],
    isAssistant: boolean
  ) => {
    if (typeof content === "string") {
      if (isAssistant) {
        return <Streamdown isAnimating={loading}>{content}</Streamdown>;
      }
      return <div className="whitespace-pre-wrap">{content}</div>;
    }

    if (Array.isArray(content)) {
      return (
        <div className="space-y-2">
          {content.map((block, index) => {
            if (block.type === "text") {
              if (isAssistant) {
                return (
                  <Streamdown key={index} isAnimating={loading}>
                    {block.text}
                  </Streamdown>
                );
              }
              return (
                <div key={index} className="whitespace-pre-wrap">
                  {block.text}
                </div>
              );
            }
            if (block.type === "tool_use") {
              return null;
            }
            if (block.type === "tool_result") {
              let displayContent = block.content;

              if (typeof block.content === "string") {
                try {
                  const parsed = JSON.parse(block.content);
                  if (parsed.content && Array.isArray(parsed.content)) {
                    return (
                      <div key={index} className="space-y-2">
                        {parsed.content.map(
                          (
                            innerBlock: { type: string; text: string },
                            innerIndex: number
                          ) => {
                            if (innerBlock.type === "text") {
                              return (
                                <div
                                  key={innerIndex}
                                  className="whitespace-pre-wrap"
                                >
                                  {innerBlock.text}
                                </div>
                              );
                            }
                            return null;
                          }
                        )}
                      </div>
                    );
                  }
                  displayContent = JSON.stringify(parsed, null, 2);
                } catch {}
              }

              return (
                <div key={index} className="whitespace-pre-wrap">
                  {typeof displayContent === "string"
                    ? displayContent
                    : JSON.stringify(displayContent)}
                </div>
              );
            }
            return null;
          })}
        </div>
      );
    }

    return JSON.stringify(content);
  };

  return (
    <div className="flex flex-col h-screen">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((m, i) => {
          let hasRenderableContent = false;
          let isToolResult = false;

          if (typeof m.content === "string") hasRenderableContent = true;
          else if (Array.isArray(m.content)) {
            hasRenderableContent = m.content.some(
              (block) => block.type === "text" || block.type === "tool_result"
            );
            isToolResult = m.content.some(
              (block) => block.type === "tool_result"
            );
          }

          if (!hasRenderableContent) return null;

          const alignmentClass =
            m.role === "user" && !isToolResult ? "text-right" : "text-left";

          return (
            <div key={i} className={alignmentClass}>
              <div className="inline-block max-w-2xl">
                {renderContent(
                  m.content,
                  m.role === "assistant" || isToolResult
                )}
              </div>
            </div>
          );
        })}
      </div>
      <form
        onSubmit={handleSubmit}
        className="p-4 border-t border-gray-500 space-y-2"
      >
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask Claude anything..."
          className="w-full px-4 py-2 border border-gray-500 rounded-lg"
          disabled={loading}
        />
        <div className="flex justify-between">
          <button
            type="button"
            onClick={handleGithubClick}
            className="px-4 py-2 bg-black text-white rounded-lg disabled:opacity-50"
          >
            {githubAuthorized ? "Disconnect" : "Connect"} GitHub
          </button>
          <button
            type="submit"
            disabled={loading || !input.trim()}
            className="px-4 py-2 bg-black text-white rounded-lg disabled:opacity-50"
          >
            Send
          </button>
        </div>
      </form>
    </div>
  );
}
```

## Next Steps

- Explore [Advanced Configuration](/docs/getting-started/advanced-usage)
- Check the [API Reference](/docs/reference/options) for complete type definitions
