- ✅ Bun installed
- ✅ Privy.io account with app created
- ✅ OpenSSL (for JWT keys)
# Generate RSA key pair
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout -out public-key.pem
# Get base64-encoded values
echo "JWT_PRIVATE_KEY=$(cat private-key.pem | base64 | tr -d '\n')"
echo "JWT_PUBLIC_KEY=$(cat public-key.pem | base64 | tr -d '\n')"
# Clean up
rm private-key.pem public-key.pem# Copy template
cp .env.example .env
# Edit .env and add:
# - Your Privy App ID & Secret (from dashboard.privy.io)
# - JWT keys from step 1
# - Your backend API URL# Install dependencies
bun install
# Build widgets (REQUIRED before starting server!)
bun run build:widgetsThree ways to run the development server:
# Start server with auto-reload
bun run dev
# Server runs at http://localhost:3002
# Note: Rebuild widgets manually when you change widget code# Terminal 1: Auto-rebuild widgets on changes
bun run dev:widgets
# Terminal 2: Run server
bun run dev# Runs both server AND widget watch mode
bun run dev:all
# Server runs at http://localhost:3002
# Widgets auto-rebuild when you make changesbunx @modelcontextprotocol/inspector http://localhost:3002/mcp# Terminal 1: Run server
bun run dev
# Terminal 2: Expose with ngrok
ngrok http 3002
# Copy the HTTPS URL (e.g., https://abc123.ngrok.app)
# Use this in ChatGPT Settings → Connectors-
Enable Developer Mode:
- ChatGPT Settings → Apps & Connectors → Advanced
- Enable "Developer mode"
-
Create Connector:
- Settings → Connectors → Create
- Name: Your App Name
- URL:
https://your-server.com/mcpor ngrok URL
-
Test:
- New conversation → Click + → More → Select your connector
- Authorize via Privy
- Try: "Show me my items"
# Build
docker build -t chatgpt-app .
# Run
docker run -p 3000:3000 --env-file .env chatgpt-app# Install flyctl
curl -L https://fly.io/install.sh | sh
# Login and launch
fly auth login
fly launch
# Set secrets
fly secrets set \
PRIVY_APP_ID=xxx \
PRIVY_APP_SECRET=xxx \
JWT_PRIVATE_KEY=xxx \
JWT_PUBLIC_KEY=xxx \
BACKEND_API_URL=xxx
# Deploy
fly deployThe most common issue is forgetting to build widgets!
# Solution 1: Build widgets once, then restart server
bun run build:widgets
bun run dev
# Solution 2: Use dev:all to auto-rebuild widgets
bun run dev:allRemember: bun run dev does NOT build widgets automatically!
- Verify
SERVER_BASE_URLin .env matches actual URL - Check Privy App ID is correct
- Ensure JWT keys are valid base64
- Must use HTTPS (use ngrok for local testing)
- Verify
/mcpendpoint is accessible - Check server logs for errors
# Server commands
bun run dev # Start dev server (does NOT build widgets!)
bun run start # Run production server
# Widget commands
bun run build:widgets # Build widgets once
bun run dev:widgets # Build widgets in watch mode (auto-rebuild)
# Combined commands
bun run dev:all # Run server + widget watch (recommended for development)
bun run build # Build everything for production
# Other commands
bun test # Run tests
bun run type-check # TypeScript type checkingmcp2/
├── src/
│ ├── server/ # Express + MCP + OAuth
│ ├── client/ # React OAuth UI
│ └── widgets/ # React widget components
├── dist/ # Build output (gitignored)
└── package.json
- 📖 Full docs: README.md
- 🔧 OpenAI Apps SDK: https://developers.openai.com/apps-sdk/
- 🔐 Privy Docs: https://docs.privy.io/
- 🚀 Bun Docs: https://bun.sh/docs
- Customize the ListView widget in
src/widgets/src/ListView/ - Add more tools in
src/server/mcp/tools.ts - Connect to your actual backend API in
src/server/api/backend.ts - Build additional widgets as needed
- Deploy to production!
Happy building! 🚀