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

## Installation

Install the Integrate SDK and OpenAI SDK packages:

```bash
bun add integrate-sdk openai
```

## Setup

Setting up OpenAI 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 OpenAI format:

```typescript
// app/api/chat/route.ts
import { serverClient } from "@/lib/integrate";
import { getOpenAITools, handleOpenAIResponse } from "integrate-sdk/server";
import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

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

  const response = await openai.responses.create({
    model: "gpt-5-nano",
    input: messages,
    tools: await getOpenAITools(serverClient),
  });

  const result = await handleOpenAIResponse(serverClient, response);

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

## Usage

Use OpenAI's API in any component:

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

import { useState, useEffect } from "react";
import { client } from "integrate-sdk";
import { Streamdown } from "streamdown";

interface Message {
  role: "user" | "assistant";
  content: string;
}

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

  const isLoading = loading;

  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);
    }
  }

  function formatFunctionOutput(output: unknown): string {
    try {
      const data = typeof output === "string" ? JSON.parse(output) : output;

      if (data.structuredContent?.repositories) {
        let displayText = "\n\n📦 **GitHub Repositories:**\n\n";
        for (const repo of data.structuredContent.repositories) {
          displayText += `**${repo.name}**\n`;
          displayText += `${repo.html_url}\n`;
          if (repo.description) {
            displayText += `_${repo.description}_\n`;
          }
          displayText += `Language: ${repo.language || "N/A"} • Stars: ${
            repo.stargazers_count
          } • ${repo.private ? "Private" : "Public"}\n\n`;
        }
        return displayText;
      }

      if (data.content && Array.isArray(data.content)) {
        let text = "";
        for (const item of data.content) {
          if (item.type === "text" && item.text) {
            try {
              const parsed = JSON.parse(item.text);
              if (parsed.repositories) {
                let repoText = "\n\n📦 **GitHub Repositories:**\n\n";
                for (const repo of parsed.repositories) {
                  repoText += `**${repo.name}**\n`;
                  repoText += `${repo.html_url}\n`;
                  if (repo.description) {
                    repoText += `_${repo.description}_\n`;
                  }
                  repoText += `Language: ${repo.language || "N/A"} • Stars: ${
                    repo.stargazers_count
                  } • ${repo.private ? "Private" : "Public"}\n\n`;
                }
                text += repoText;
              } else {
                text += JSON.stringify(parsed, null, 2);
              }
            } catch {
              text += item.text;
            }
          }
        }
        return text;
      }

      return JSON.stringify(data, null, 2);
    } catch (e) {
      console.error("Error formatting function output:", e);
      return String(output);
    }
  }

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

    const userMessage: Message = { 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],
        }),
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const result = await response.json();

      let assistantContent = "";

      const outputArray = Array.isArray(result) ? result : result.output;

      if (outputArray && Array.isArray(outputArray)) {
        for (const item of outputArray) {
          if (item.type === "message" && item.content) {
            for (const content of item.content) {
              if (content.type === "text" || content.type === "output_text") {
                assistantContent += content.text + "\n";
              }
            }
          } else if (item.type === "function_call") {
            assistantContent += `🔧 Called function: ${item.name}\n`;
          } else if (item.type === "function_call_output") {
            assistantContent += formatFunctionOutput(item.output);
          }
        }
      }

      if (!assistantContent.trim()) {
        assistantContent =
          "Response received but no displayable content found.";
      }

      setMessages((prev) => [
        ...prev,
        { role: "assistant", content: assistantContent.trim() },
      ]);
    } catch (error) {
      console.error("Error:", error);
      setMessages((prev) => [
        ...prev,
        {
          role: "assistant",
          content: `Error: ${
            error instanceof Error ? error.message : "Unknown error"
          }`,
        },
      ]);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="flex flex-col h-screen">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((m, i) => (
          <div
            key={i}
            className={m.role === "user" ? "text-right" : "text-left"}
          >
            <Streamdown
              isAnimating={
                isLoading && m.role === "assistant" && i === messages.length - 1
              }
              className="inline-block max-w-2xl whitespace-pre-wrap"
            >
              {m.content}
            </Streamdown>
          </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 AI 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
