Index Network User Profile
FreeNot checkedStores user preferences, display names, pronouns, and notes in volatile memory with explicit consent validation, enabling personalized interactions without pers
About
Stores user preferences, display names, pronouns, and notes in volatile memory with explicit consent validation, enabling personalized interactions without persistent data retention.
README
A complete ChatGPT App implementation using the OpenAI Apps SDK (MCP), with OAuth2 authentication via Privy.io.
🏗️ Architecture
- Backend: Express + MCP Server (TypeScript/Bun)
- OAuth UI: React + Privy + React Router
- Widgets: React components (rendered in ChatGPT)
- Auth: OAuth2 with PKCE + Privy.io
- Package Manager: Bun
📁 Project Structure
mcp2/
├── src/
│ ├── server/ # Express + MCP server
│ │ ├── oauth/ # OAuth2 endpoints
│ │ ├── mcp/ # MCP tools & resources
│ │ ├── api/ # Backend API integration
│ │ └── middleware/ # Auth middleware
│ ├── client/ # OAuth authorization UI
│ └── widgets/ # ChatGPT widget components
├── dist/
│ ├── client/ # Built OAuth UI
│ ├── widgets/ # Built widget bundles
│ └── server/ # Compiled server
└── package.json
🚀 Quick Start
Prerequisites
1. Install Bun
curl -fsSL https://bun.sh/install | bash
2. Install Dependencies
bun install
3. Generate JWT Keys
# Generate RSA key pair for JWT signing
openssl genrsa -out private-key.pem 2048
openssl rsa -in private-key.pem -pubout -out public-key.pem
# Base64 encode for .env
echo "JWT_PRIVATE_KEY=$(cat private-key.pem | base64)"
echo "JWT_PUBLIC_KEY=$(cat public-key.pem | base64)"
# Clean up PEM files
rm private-key.pem public-key.pem
4. Configure Environment
cp .env.example .env
# Edit .env with your values:
# - PRIVY_APP_ID (from Privy dashboard)
# - PRIVY_APP_SECRET (from Privy dashboard)
# - JWT_PRIVATE_KEY (from step 3)
# - JWT_PUBLIC_KEY (from step 3)
# - PROTOCOL_API_URL (your existing backend)
# - DATABASE_URL (optional - for production auth persistence)
# - AUTH_STORAGE_DRIVER (memory or postgres)
5. Set Up Auth Database (Production)
For production deployments, you need a PostgreSQL database to persist OAuth clients, tokens, and sessions across server restarts.
Option A: In-Memory (Development Only)
# In .env - no database needed, but data is lost on restart
AUTH_STORAGE_DRIVER=memory
Option B: PostgreSQL (Production)
# In .env
AUTH_STORAGE_DRIVER=postgres
DATABASE_URL=postgresql://USER:PASSWORD@HOST/DBNAME?sslmode=require
Then create the required tables in your database:
-- OAuth clients (DCR-registered clients like ChatGPT)
CREATE TABLE oauth_clients (
id TEXT PRIMARY KEY,
client_name TEXT,
redirect_uris TEXT[] NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Refresh tokens (30-day lifetime)
CREATE TABLE oauth_refresh_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token TEXT NOT NULL,
client_id TEXT NOT NULL,
privy_user_id TEXT NOT NULL,
scopes TEXT[] NOT NULL,
privy_access_token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX oauth_refresh_tokens_token_idx ON oauth_refresh_tokens (token);
CREATE INDEX oauth_refresh_tokens_user_client_idx ON oauth_refresh_tokens (privy_user_id, client_id);
-- Access token sessions (for /token/privy/access-token endpoint)
CREATE TABLE oauth_access_token_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
jti TEXT NOT NULL UNIQUE,
client_id TEXT NOT NULL,
privy_user_id TEXT NOT NULL,
scopes TEXT[] NOT NULL,
privy_access_token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX oauth_access_token_sessions_jti_idx ON oauth_access_token_sessions (jti);
Note: The static chatgpt-connector client is automatically registered on server startup. DCR-generated clients (like client_xxx) are persisted and survive restarts.
6. Build & Run
IMPORTANT: Widgets must be built before starting the server!
# First time: Build widgets (required!)
bun run build:widgets
# Then start development server
bun run dev
The server will start at http://localhost:3002
🔧 Development
Understanding the Widget Build Process
⚠️ Key Point: bun run dev does NOT automatically build widgets. You must build them separately!
There are three development workflows:
Option 1: Manual Build (Recommended for first-time setup)
# 1. Build widgets once
bun run build:widgets
# 2. Start server with auto-reload
bun run dev
# 3. Rebuild widgets manually when you change widget code
bun run build:widgets
Option 2: Watch Mode (Recommended for active widget development)
# Terminal 1: Build widgets in watch mode (auto-rebuilds on changes)
bun run dev:widgets
# Terminal 2: Run server with auto-reload
bun run dev
Option 3: Run Everything (Most convenient)
# Runs both server AND widget watch mode simultaneously
bun run dev:all
Other Development Commands
# Type check
bun run type-check
# Run tests
bun run test
# Build everything for production
bun run build
Project Configuration
Server: src/server/index.ts
- OAuth endpoints:
/authorize,/token,/.well-known/* - MCP endpoint:
/mcp - Health check:
/health
OAuth UI: src/client/src/App.tsx
- Authorization page with Privy login
- Consent screen
- Built with Vite + React + React Router
Widgets: src/widgets/src/
- ListView: Interactive list with actions
- Built as standalone bundles
- Communicate via
window.openaiAPI
🧪 Testing
Test with MCP Inspector
# Terminal 1: Run server
bun run dev
# Terminal 2: Run MCP Inspector
bunx @modelcontextprotocol/inspector http://localhost:3002/mcp
Test with ngrok
# Expose local server
ngrok http 3002
# Copy the HTTPS URL (e.g., https://abc123.ngrok.app)
# Use this URL in ChatGPT Settings → Connectors
Connect to ChatGPT
Enable Developer Mode:
- ChatGPT Settings → Apps & Connectors → Advanced settings
- Enable "Developer mode"
Create Connector:
- Settings → Connectors → Create
- Name: "Your App Name"
- Description: "What your app does"
- Connector URL:
https://your-server.com/mcp(or ngrok URL)
Test OAuth Flow:
- Start a new ChatGPT conversation
- Click + → More → Select your connector
- You'll be redirected to
/authorize - Log in with Privy
- Grant consent
- ChatGPT receives OAuth token
Test Tools:
- Ask ChatGPT: "Show me my items"
- The
get-itemstool will be called - Widget will render in ChatGPT
📦 Production Build
# Build everything
bun run build
# Run production server
bun run start
# Or preview locally
bun run preview
Docker Deployment
# Build image
docker build -t chatgpt-app .
# Run container
docker run -p 3000:3000 --env-file .env chatgpt-app
Deploy to Fly.io
# Install flyctl
curl -L https://fly.io/install.sh | sh
# Create app
fly launch
# Set secrets
fly secrets set PRIVY_APP_ID=xxx
fly secrets set PRIVY_APP_SECRET=xxx
fly secrets set JWT_PRIVATE_KEY=xxx
fly secrets set JWT_PUBLIC_KEY=xxx
fly secrets set BACKEND_API_URL=xxx
# Deploy
fly deploy
🔐 OAuth2 Flow
- ChatGPT redirects user to
/authorize?client_id=...&code_challenge=... - Server serves React UI (Privy login)
- User authenticates with Privy
- Frontend shows consent screen
- User approves, server generates authorization code
- Frontend redirects back to ChatGPT with code
- ChatGPT exchanges code for access token at
/token - Server validates PKCE, issues JWT
- ChatGPT uses JWT for
/mcprequests
🎨 Adding New Tools
1. Define Tool in src/server/mcp/tools.ts
{
name: 'my-new-tool',
description: 'What the tool does',
inputSchema: {
type: 'object',
properties: {
param: { type: 'string' }
},
required: ['param']
}
}
2. Implement Handler
async function handleMyNewTool(args: any, auth: any) {
// Validate auth
// Call backend API
// Return structured response
}
3. Link to Widget (Optional)
_meta: {
'openai/outputTemplate': 'ui://widget/my-widget.html',
}
🎨 Adding New Widgets
1. Create Widget Component
mkdir -p src/widgets/src/MyWidget
2. Build Widget
// src/widgets/src/MyWidget/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { MyWidget } from './MyWidget';
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(<MyWidget />);
3. Configure Vite
// Update src/widgets/vite.config.ts
build: {
lib: {
entry: {
'my-widget': 'src/MyWidget/index.tsx'
}
}
}
4. Register Resource
// src/server/mcp/resources.ts
await registerMyWidget(server, widgetPath);
📚 Environment Variables
| Variable | Description | Required |
|---|---|---|
PRIVY_APP_ID |
Your Privy app ID | ✅ |
PRIVY_APP_SECRET |
Your Privy app secret | ✅ |
VITE_PRIVY_APP_ID |
Privy app ID (for frontend) | ✅ |
JWT_PRIVATE_KEY |
Base64-encoded RSA private key | ✅ |
JWT_PUBLIC_KEY |
Base64-encoded RSA public key | ✅ |
SERVER_BASE_URL |
Your server URL | ✅ |
BACKEND_API_URL |
Your existing backend URL | ✅ |
PORT |
Server port (default: 3000) | ❌ |
NODE_ENV |
Environment (development/production) | ❌ |
🐛 Troubleshooting
Widgets not loading
# Build widgets first
bun run build:widgets
# Restart server
bun run dev
OAuth flow fails
- Check
SERVER_BASE_URLmatches your actual URL - Verify Privy app ID is correct
- Check JWT keys are properly base64-encoded
- Ensure redirect URI is registered in ChatGPT
Token validation fails
- Verify JWT keys are correct (public/private pair)
- Check token hasn't expired (1 hour default)
- Ensure
audclaim matches your server URL
MCP Inspector can't connect
# Ensure server is running
bun run dev
# Try:
bunx @modelcontextprotocol/inspector http://localhost:3002/mcp
📖 Resources
📝 License
MIT
🤝 Contributing
Contributions welcome! Please open an issue or PR.
Installing Index Network User Profile
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/indexnetwork/mcpFAQ
Is Index Network User Profile MCP free?
Yes, Index Network User Profile MCP is free — one-click install via Unyly at no cost.
Does Index Network User Profile need an API key?
No, Index Network User Profile runs without API keys or environment variables.
Is Index Network User Profile hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Index Network User Profile in Claude Desktop, Claude Code or Cursor?
Open Index Network User Profile on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Index Network User Profile with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
