OpenAI MCP Server
by arthurcolle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenAI Code Assistant Web Client</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #2c3e50;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.chat-container {
display: flex;
height: 70vh;
}
.sidebar {
width: 250px;
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-right: 20px;
}
.main-chat {
flex: 1;
display: flex;
flex-direction: column;
border: 1px solid #ddd;
border-radius: 5px;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 15px;
background-color: #fff;
}
.chat-input {
display: flex;
padding: 10px;
background-color: #f8f9fa;
border-top: 1px solid #ddd;
}
.chat-input textarea {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
font-family: inherit;
}
.chat-input button {
margin-left: 10px;
padding: 10px 15px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.chat-input button:hover {
background-color: #45a049;
}
.message {
margin-bottom: 15px;
padding: 10px;
border-radius: 5px;
}
.user-message {
background-color: #e3f2fd;
align-self: flex-end;
margin-left: 20%;
}
.assistant-message {
background-color: #f1f1f1;
align-self: flex-start;
margin-right: 20%;
}
.tool-message {
background-color: #fff8e1;
border-left: 3px solid #ffc107;
padding-left: 10px;
font-family: monospace;
white-space: pre-wrap;
}
.status-message {
color: #666;
font-style: italic;
text-align: center;
margin: 10px 0;
}
.warning-message {
color: #ff9800;
border-left: 3px solid #ff9800;
padding-left: 10px;
}
.error-message {
color: #f44336;
border-left: 3px solid #f44336;
padding-left: 10px;
}
.conversation-list {
list-style: none;
padding: 0;
}
.conversation-list li {
padding: 8px 10px;
margin-bottom: 5px;
background-color: #e9ecef;
border-radius: 4px;
cursor: pointer;
}
.conversation-list li:hover {
background-color: #dee2e6;
}
.conversation-list li.active {
background-color: #4CAF50;
color: white;
}
.settings-panel {
margin-top: 20px;
}
.settings-panel h3 {
margin-bottom: 10px;
}
.settings-panel label {
display: block;
margin-bottom: 5px;
}
.settings-panel select, .settings-panel input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.new-conversation-btn {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 15px;
}
.new-conversation-btn:hover {
background-color: #45a049;
}
pre {
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
code {
font-family: 'Courier New', Courier, monospace;
}
</style>
</head>
<body>
<h1>OpenAI Code Assistant</h1>
<div class="chat-container">
<div class="sidebar">
<button id="newConversationBtn" class="new-conversation-btn">New Conversation</button>
<h3>Conversations</h3>
<ul id="conversationList" class="conversation-list">
<!-- Conversations will be added here -->
</ul>
<div class="settings-panel">
<h3>Settings</h3>
<label for="modelSelect">Model:</label>
<select id="modelSelect">
<option value="gpt-4o">GPT-4o</option>
<option value="gpt-4-turbo">GPT-4 Turbo</option>
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
</select>
<label for="temperatureInput">Temperature:</label>
<input type="number" id="temperatureInput" min="0" max="2" step="0.1" value="0">
</div>
</div>
<div class="main-chat">
<div id="chatMessages" class="chat-messages">
<div class="status-message">Start a new conversation or select an existing one.</div>
</div>
<div class="chat-input">
<textarea id="userInput" placeholder="Type your message here..." rows="3"></textarea>
<button id="sendButton">Send</button>
</div>
</div>
</div>
<script>
// API endpoint (change this to match your server)
const API_BASE_URL = 'http://localhost:8000';
// State
let currentConversationId = null;
let conversations = [];
// DOM Elements
const chatMessages = document.getElementById('chatMessages');
const userInput = document.getElementById('userInput');
const sendButton = document.getElementById('sendButton');
const newConversationBtn = document.getElementById('newConversationBtn');
const conversationList = document.getElementById('conversationList');
const modelSelect = document.getElementById('modelSelect');
const temperatureInput = document.getElementById('temperatureInput');
// Event Listeners
sendButton.addEventListener('click', sendMessage);
newConversationBtn.addEventListener('click', createNewConversation);
userInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Initialize
function init() {
// Load conversations from local storage
const savedConversations = localStorage.getItem('conversations');
if (savedConversations) {
conversations = JSON.parse(savedConversations);
updateConversationList();
}
}
// Create a new conversation
async function createNewConversation() {
try {
const model = modelSelect.value;
const temperature = parseFloat(temperatureInput.value);
const response = await fetch(`${API_BASE_URL}/conversation`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ model, temperature })
});
const data = await response.json();
if (data.conversation_id) {
const newConversation = {
id: data.conversation_id,
model: data.model,
created: new Date().toISOString(),
messages: []
};
conversations.push(newConversation);
saveConversations();
updateConversationList();
// Switch to the new conversation
switchConversation(data.conversation_id);
}
} catch (error) {
console.error('Error creating conversation:', error);
addErrorMessage('Failed to create a new conversation. Please try again.');
}
}
// Switch to a different conversation
function switchConversation(conversationId) {
currentConversationId = conversationId;
// Update UI
const conversationItems = conversationList.querySelectorAll('li');
conversationItems.forEach(item => {
if (item.dataset.id === conversationId) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
// Clear and load messages
chatMessages.innerHTML = '';
const conversation = conversations.find(c => c.id === conversationId);
if (conversation && conversation.messages) {
conversation.messages.forEach(msg => {
if (msg.role === 'user') {
addUserMessage(msg.content);
} else if (msg.role === 'assistant') {
addAssistantMessage(msg.content);
} else if (msg.role === 'tool') {
addToolMessage(msg.name, msg.content);
}
});
}
// Focus input
userInput.focus();
}
// Send a message
async function sendMessage() {
const message = userInput.value.trim();
if (!message) return;
if (!currentConversationId) {
await createNewConversation();
}
// Add user message to UI
addUserMessage(message);
// Save message to conversation
const conversation = conversations.find(c => c.id === currentConversationId);
if (conversation) {
conversation.messages.push({
role: 'user',
content: message
});
saveConversations();
}
// Clear input
userInput.value = '';
// Send to API and stream response
try {
const response = await fetch(`${API_BASE_URL}/conversation/${currentConversationId}/message/stream`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantResponse = '';
let responseElement = null;
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\n').filter(line => line.trim());
for (const line of lines) {
try {
const data = JSON.parse(line);
if (data.type === 'content') {
if (!responseElement) {
responseElement = addAssistantMessage('');
}
assistantResponse += data.content;
responseElement.textContent = assistantResponse;
}
else if (data.type === 'status') {
if (data.status === 'running_tools') {
addStatusMessage('Running tools...');
} else if (data.status.startsWith('running_tools_iteration_')) {
const iteration = data.status.split('_').pop();
addStatusMessage(`Running tools (iteration ${iteration})...`);
}
}
else if (data.type === 'tool_result') {
addToolMessage(data.tool, data.result);
}
else if (data.type === 'warning') {
addWarningMessage(data.warning);
}
else if (data.type === 'error') {
addErrorMessage(data.error);
}
} catch (e) {
console.error('Error parsing stream data:', e, line);
}
}
}
// Save assistant response to conversation
if (conversation && assistantResponse) {
conversation.messages.push({
role: 'assistant',
content: assistantResponse
});
saveConversations();
}
} catch (error) {
console.error('Error sending message:', error);
addErrorMessage('Failed to send message. Please try again.');
}
}
// Add a user message to the chat
function addUserMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'message user-message';
messageElement.textContent = message;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Add an assistant message to the chat
function addAssistantMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'message assistant-message';
messageElement.textContent = message;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Add a tool message to the chat
function addToolMessage(toolName, result) {
const messageElement = document.createElement('div');
messageElement.className = 'message tool-message';
messageElement.innerHTML = `<strong>${toolName}:</strong>\n${result}`;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Add a status message to the chat
function addStatusMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'status-message';
messageElement.textContent = message;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Add a warning message to the chat
function addWarningMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'warning-message';
messageElement.textContent = message;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Add an error message to the chat
function addErrorMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = 'error-message';
messageElement.textContent = message;
chatMessages.appendChild(messageElement);
scrollToBottom();
return messageElement;
}
// Update the conversation list in the sidebar
function updateConversationList() {
conversationList.innerHTML = '';
conversations.forEach(conversation => {
const listItem = document.createElement('li');
listItem.textContent = new Date(conversation.created).toLocaleString();
listItem.dataset.id = conversation.id;
if (conversation.id === currentConversationId) {
listItem.classList.add('active');
}
listItem.addEventListener('click', () => {
switchConversation(conversation.id);
});
conversationList.appendChild(listItem);
});
}
// Save conversations to local storage
function saveConversations() {
localStorage.setItem('conversations', JSON.stringify(conversations));
}
// Scroll chat to bottom
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Initialize the app
init();
</script>
</body>
</html>