Panel
Panel
Panel is a standalone remote MCP server that lets Claude call ChatGPT, DeepSeek, Kimi, and Gemini inside one persistent, shared discussion. It exposes exactly one tool:
ask_model(provider, message, conversation_id?)Architecture
Streamable HTTP MCP endpoint:
POST /mcpHealth endpoint:
GET /healthStatic bearer authentication on every MCP request
PostgreSQL source-of-truth transcript with one ordered log per
conversation_idOne OpenAI-compatible adapter configured three ways, plus a native Gemini adapter
Request-time context compaction only; stored turns are never edited or deleted
The selected provider sees its own earlier turns as assistant/model. Claude and every other provider are rendered as user with [Speaker]: prefixes.
Local setup
Requirements: Node.js 22+, a reachable PostgreSQL database, and API keys for the four providers.
Copy
.env.exampleto.envand fill values locally. Never commit.env.Run
npm ci.Run
npm run db:migrate(the server also initializes the idempotent schema at startup).Run
npm run dev.In another terminal, run
npm run manual.
Useful checks:
npm run build
npm run lint
npm test
npm run test:integration
npm run verify:persistenceThe integration and persistence commands require DATABASE_URL. The manual test requires the running server, all provider keys, and PANEL_BEARER_TOKEN. Set PANEL_URL when testing a deployed instance.
Environment
Required server variables:
DATABASE_URLPANEL_BEARER_TOKEN(use at least 32 random bytes)OPENAI_API_KEYDEEPSEEK_API_KEYMOONSHOT_API_KEYGOOGLE_API_KEY
Optional variables and defaults:
MAX_CONTEXT_CHARS=60000PORT=3000OPENAI_BASE_URL=https://api.openai.com/v1OPENAI_MODEL=gpt-4o-miniDEEPSEEK_BASE_URL=https://api.deepseek.comDEEPSEEK_MODEL=deepseek-v4-flashMOONSHOT_BASE_URL=https://api.moonshot.ai/v1MOONSHOT_MODEL=kimi-k2.5GEMINI_MODEL=gemini-2.5-flash
Deploy
Render
Push this repository to GitHub, create a Blueprint from render.yaml, and enter every sync: false secret in Render's dashboard. Point DATABASE_URL at external Neon or Supabase Postgres. The service URL will be https://<service>.onrender.com/mcp.
Railway
Push to GitHub, create a Railway project from the repository, and add the same variables. railway.json uses the included Dockerfile. Generate a public HTTPS domain and append /mcp.
Connect to claude.ai
Current Claude connector documentation lists fixed request-header authentication (static_headers) as a beta option for custom connectors. Add the deployed /mcp URL under Customize → Connectors → Add custom connector (Team/Enterprise owners add it under organization connector settings). In advanced/request-header settings add:
Authorization: Bearer <the exact PANEL_BEARER_TOKEN value>Then connect/enable Panel in the target Claude conversation. Do not put the token in the URL.
If the request-header field is not exposed for your account, do not make this server public without auth. That account/UI has not enabled the beta static-header path; OAuth would then be a separate, explicitly required follow-up.
Verification harness behavior
npm run manual invokes all four providers through MCP using one conversation_id. Its DeepSeek, Kimi, and Gemini prompts explicitly require reactions to earlier named providers, making shared transcript behavior visible. npm run verify:persistence writes through one PostgreSQL pool, closes it, creates a fresh pool, and reads the sentinel back. The compaction unit test confirms the request rendering is compacted while the original full turn array remains unchanged; the PostgreSQL transcript is only read during rendering.