# Model Context Playground

A React component for interacting with Model Context Servers within a chat interface.

> Demo: [https://model-context-playground.com](https://model-context-playground.com)

![GIF of Model Context Playground Demo](https://gitlab.com/ben_goodman/libraries/model-context-playground/-/raw/main/docs/demo.gif?ref_type=heads)

## Features

- Chat interface for interacting with language models
- Call MCP tools directly
- OpenAI support
- SSE streaming support with live tool-call events

## Installation

```bash
npm install model-context-playground
```

## Usage

```tsx
import React from 'react'
import ReactDOM from 'react-dom/client'

import 'model-context-playground/styles.css'
import { ModelContextPlayground } from 'model-context-playground'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <ModelContextPlayground />
  </React.StrictMode>,
)
```

## Options

`ModelContextPlayground` accepts the following props:

### `className?: string`

Optional utility class string appended to the root container. Useful for sizing or theming the playground within your app layout.

### `options?: ModelContextPlaygroundOptions`

Container for optional configuration objects. All values are merged with the data persisted in local storage, with the values you pass via props always taking priority.

- **`initialMcpServers`** – Array of MCP server definitions. Each entry is a `McpServerEntry` (`{ id, options }`). Entries are normalized and merged with any previously stored servers:
  - Servers are matched by trimmed `url` when available; matching entries are updated, new ones are appended.
  - Provide `serverName`, `url`, and any optional `toolPreferences`, `serverEnabled`, or other connection metadata supported by the playground.
- **`initialLanguageModels`** – Array of `StoredModel` objects that seed the language-model catalog. Models are merged by `id` (or a generated id based on provider/model if `id` is omitted). When a stored model already exists, the prop value overwrites persisted data.

Both collections are persisted after the initial merge, so subsequent renders do not duplicate entries.

```ts
type McpServerEntry = {
  id: string
  options: StoredMcpServer
}

type StoredMcpServer = {
  url: string
  autoFetch?: boolean
  headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)
  fetchTimeoutMs?: number
  serverName?: string
  serverEnabled?: boolean
  toolPreferences?: Record<string, boolean>
}

type StoredModel = {
  id: string
  provider: 'openai' | 'anthropic'
  model: string
  label: string
  description?: string
  baseUrl?: string
  settings?: Record<string, unknown>
  connection?: Record<string, string | number | boolean>
  disabled?: boolean
}
```

```tsx
<ModelContextPlayground
  options={{
    initialMcpServers: [
      {
        id: 'lorem-mcp',
        options: {
          serverName: 'Lorem MCP',
          url: 'https://lorem-api.com/api/mcp',
        }
      },
      {
        id: 'gastro-genie-mcp',
        options: {
          serverName: 'Gastro Genie MCP',
          url: 'https://mcp.gastro-genie.com',
        }
      }
    ],
    initialLanguageModels: [
      {
        id: 'openai-gpt-5',
        provider: 'openai',
        model: 'gpt-5',
        label: 'GPT-5',
        settings: {
          maxTokens: 2048,
          reasoningEffort: 'low',
          textVerbosity: 'low'
        }
      }
    ]
  }}
/>
```

### `theme?: ThemeTokens`

Provide a complete theme object to replace the default styling tokens. Each token is a Tailwind-friendly class string that the playground applies to specific UI elements (chat header, cards, buttons, etc.). Passing `theme` lets you fully control the visual language without touching component code.

### `themeOverrides?: ThemeOverrides`

Pass a deep-partial theme object to merge with the default theme. This is the quickest way to tweak just a few sections (for example, swapping gradients or badge colors) while inheriting the remaining defaults.

## Theming

The library exports a small theming toolkit:

- `defaultTheme` – the out-of-the-box look
- `darkTheme` – (WIP) a dark, high-contrast palette
- `systemTheme` – switches between light/dark based on OS preference
- `createTheme(overrides)` – deep merges your overrides with the default theme and returns a brand-new theme object
- `ThemeProvider` – low-level provider if you embed individual playground components
- `ThemeTokens` / `ThemeOverrides` – TypeScript types describing every available token

```tsx
import { ModelContextPlayground, createTheme, darkTheme } from 'model-context-playground'

export function DarkApp() {
  return <ModelContextPlayground theme={darkTheme} />
}

const midnightTheme = createTheme({
  layout: {
    appRoot: 'flex h-full flex-col bg-slate-950 font-sans text-slate-100'
  },
  chatHeader: {
    container: 'fixed top-0 left-0 right-0 z-50 flex items-center justify-between gap-4 bg-slate-900/80 px-4 py-2 backdrop-blur'
  },
  message: {
    cardContainer: 'space-y-3 rounded-2xl border border-slate-700 bg-slate-900/60 p-3 text-xs text-slate-100 shadow-lg ring-1 ring-slate-800/60'
  },
  toolCallCard: {
    error: {
      container: 'space-y-3 rounded-2xl border border-rose-700 bg-rose-900/40 text-sm text-rose-100 ring-1 ring-rose-800/60'
    }
  }
})

export function App() {
  return <ModelContextPlayground theme={midnightTheme} />
}
```

For lighter edits you can rely on `themeOverrides` without building a full theme:

```tsx
<ModelContextPlayground
  themeOverrides={{
    chatDialog: {
      inputCard: 'pointer-events-auto flex w-full max-w-3xl flex-col gap-3 rounded-2xl border border-emerald-500/30 bg-emerald-950/40 p-3 shadow-lg'
    },
    chatInput: {
      submitButton: 'inline-flex items-center rounded-full bg-emerald-500 px-4 py-2 text-sm font-semibold text-emerald-50 shadow transition enabled:hover:bg-emerald-400'
    }
  }}
/>
```

Refer to `ThemeTokens` for the complete list of customizable areas. Every token maps back to the exact UI element now used throughout the playground, making it straightforward to swap palettes or build brand-aligned skins.

## Development

To run the project locally:

````bash
# clone the project
git clone https://gitlab.com/ben_goodman/libraries/model-context-playground.git

# then, in the project root:
npm install
npm start
```


## License

MIT © [Benjamin Goodman](https://ben.website)
