brightsy
Proxy requests to Brightsy AI agents through the Model Context Protocol, enabling message exchange between users and AI agents for interactive communication.
Instructions
Proxy requests to an Brightsy AI agent
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| messages | Yes | The messages to send to the agent |
Implementation Reference
- src/index.ts:150-383 (handler)The main asynchronous handler function for the 'brightsy' tool. It proxies user messages to the Brightsy AI agent API via HTTP POST, maintains conversation history across calls, handles special test commands (echo, history, simulate, clear), processes and formats responses into MCP content format, and includes comprehensive logging and error handling.async ({ messages }) => { try { console.error(`Agent proxy tool called with messages:`); console.error(JSON.stringify(messages, null, 2)); // Check for special command to clear history if (messages.length === 1 && messages[0].role === 'user' && typeof messages[0].content === 'string' && messages[0].content.trim().toLowerCase() === 'clear history') { conversationHistory = []; console.error('Conversation history cleared'); return { content: [ { type: "text", text: "Conversation history has been cleared.", }, ], }; } // Check for test commands if (messages.length === 1 && messages[0].role === 'user' && typeof messages[0].content === 'string') { const content = messages[0].content.trim(); // Add the message to conversation history before processing conversationHistory = [...conversationHistory, messages[0]]; // Handle test:echo command if (content.startsWith('test:echo ')) { const message = content.substring('test:echo '.length); return { content: [ { type: "text", text: "```\n" + message + "\n```" } ] }; } // Handle test:history command if (content === 'test:history') { console.error(`Checking history with ${conversationHistory.length} messages:`); conversationHistory.forEach((msg, i) => { console.error(`[${i}] ${msg.role}: ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`); }); if (conversationHistory.length > 1) { // Find the first non-history-check message for (let i = 0; i < conversationHistory.length - 1; i++) { const msg = conversationHistory[i]; console.error(`Checking message ${i}: ${msg.role} - ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`); if (msg.role === 'user' && typeof msg.content === 'string' && !msg.content.includes('test:history')) { console.error(`Found message: ${msg.content}`); return { content: [ { type: "text", text: msg.content } ] }; } } } console.error('No suitable message found in history'); return { content: [ { type: "text", text: "I notice you want to test something related to history" } ] }; } // Handle test:simulate command if (content.startsWith('test:simulate ')) { const message = content.substring('test:simulate '.length); return { content: [ { type: "text", text: message } ] }; } } // Add new messages to conversation history (for non-test commands) conversationHistory = [...conversationHistory, ...messages]; console.error(`Using conversation history with ${conversationHistory.length} messages`); const agentUrl = `${agentApiBaseUrl}/api/v1beta/agent/${agent_id}/chat/completion`; console.error(`Forwarding request to agent: ${agent_id}`); console.error(`Agent URL: ${agentUrl}`); const requestBody = { messages: conversationHistory, stream: false }; console.error(`Request body: ${JSON.stringify(requestBody, null, 2)}`); const response = await fetch(agentUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${api_key}` }, body: JSON.stringify(requestBody) }); console.error(`Response status: ${response.status} ${response.statusText}`); console.error(`Response headers: ${JSON.stringify(Object.fromEntries([...response.headers]), null, 2)}`); // Clone the response to log the raw response body const responseClone = response.clone(); const rawResponseText = await responseClone.text(); console.error(`Raw response body: ${rawResponseText}`); if (!response.ok) { const errorText = await response.text(); console.error(`Error from agent: ${response.status} ${errorText}`); return { content: [ { type: "text", text: `Error from agent: ${errorText}`, }, ], }; } // Try to parse the response as JSON let data; try { data = JSON.parse(rawResponseText); console.error(`Response received from agent and parsed as JSON`); } catch (parseError) { console.error(`Failed to parse response as JSON: ${parseError}`); return { content: [ { type: "text", text: `Error parsing response: ${parseError}\nRaw response: ${rawResponseText}`, }, ], }; } console.error(`Response data: ${JSON.stringify(data, null, 2)}`); // Extract the assistant's response const assistantMessage = data.choices?.[0]?.message; if (!assistantMessage) { console.error(`No assistant message found in response: ${JSON.stringify(data, null, 2)}`); return { content: [ { type: "text", text: "No message in agent response", }, ], }; } console.error(`Assistant message: ${JSON.stringify(assistantMessage, null, 2)}`); // Add the assistant's response to the conversation history if (assistantMessage) { conversationHistory.push({ role: assistantMessage.role || 'assistant', content: assistantMessage.content }); console.error(`Added assistant response to history. History now has ${conversationHistory.length} messages`); } // Handle the case where content is already an array of content blocks if (Array.isArray(assistantMessage.content)) { console.error(`Content is an array, processing directly`); // Map the content array to the expected format const processedContent = assistantMessage.content.map((item: any) => { if (typeof item === 'string') { return { type: "text", text: item }; } // Handle content blocks (text, image, etc.) if (item.text) { return { type: "text", text: item.text }; } // For other types, convert to string representation return { type: "text", text: `[${item.type || 'unknown'} content: ${JSON.stringify(item)}]` }; }); console.error(`Directly processed content: ${JSON.stringify(processedContent, null, 2)}`); return { content: processedContent, }; } // Process the content from the assistant's message (for string or other formats) const processedContent = processContent(assistantMessage.content); console.error(`Processed content: ${JSON.stringify(processedContent, null, 2)}`); return { content: processedContent, }; } catch (error) { console.error('Error forwarding request:', error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } },
- src/index.ts:143-148 (schema)Zod input schema for the tool: an array of messages, each with 'role' (string) and 'content' (string or array).messages: z.array( z.object({ role: z.string().describe("The role of the message sender"), content: z.union([z.string(), z.array(z.any())]).describe("The content of the message") }) ).describe("The messages to send to the agent")
- src/index.ts:139-384 (registration)MCP tool registration using server.tool(), with dynamic tool name (defaults to 'brightsy'), description 'Proxy requests to an Brightsy AI agent', the input schema, and the handler function.server.tool( tool_name, `Proxy requests to an Brightsy AI agent`, { messages: z.array( z.object({ role: z.string().describe("The role of the message sender"), content: z.union([z.string(), z.array(z.any())]).describe("The content of the message") }) ).describe("The messages to send to the agent") }, async ({ messages }) => { try { console.error(`Agent proxy tool called with messages:`); console.error(JSON.stringify(messages, null, 2)); // Check for special command to clear history if (messages.length === 1 && messages[0].role === 'user' && typeof messages[0].content === 'string' && messages[0].content.trim().toLowerCase() === 'clear history') { conversationHistory = []; console.error('Conversation history cleared'); return { content: [ { type: "text", text: "Conversation history has been cleared.", }, ], }; } // Check for test commands if (messages.length === 1 && messages[0].role === 'user' && typeof messages[0].content === 'string') { const content = messages[0].content.trim(); // Add the message to conversation history before processing conversationHistory = [...conversationHistory, messages[0]]; // Handle test:echo command if (content.startsWith('test:echo ')) { const message = content.substring('test:echo '.length); return { content: [ { type: "text", text: "```\n" + message + "\n```" } ] }; } // Handle test:history command if (content === 'test:history') { console.error(`Checking history with ${conversationHistory.length} messages:`); conversationHistory.forEach((msg, i) => { console.error(`[${i}] ${msg.role}: ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`); }); if (conversationHistory.length > 1) { // Find the first non-history-check message for (let i = 0; i < conversationHistory.length - 1; i++) { const msg = conversationHistory[i]; console.error(`Checking message ${i}: ${msg.role} - ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`); if (msg.role === 'user' && typeof msg.content === 'string' && !msg.content.includes('test:history')) { console.error(`Found message: ${msg.content}`); return { content: [ { type: "text", text: msg.content } ] }; } } } console.error('No suitable message found in history'); return { content: [ { type: "text", text: "I notice you want to test something related to history" } ] }; } // Handle test:simulate command if (content.startsWith('test:simulate ')) { const message = content.substring('test:simulate '.length); return { content: [ { type: "text", text: message } ] }; } } // Add new messages to conversation history (for non-test commands) conversationHistory = [...conversationHistory, ...messages]; console.error(`Using conversation history with ${conversationHistory.length} messages`); const agentUrl = `${agentApiBaseUrl}/api/v1beta/agent/${agent_id}/chat/completion`; console.error(`Forwarding request to agent: ${agent_id}`); console.error(`Agent URL: ${agentUrl}`); const requestBody = { messages: conversationHistory, stream: false }; console.error(`Request body: ${JSON.stringify(requestBody, null, 2)}`); const response = await fetch(agentUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${api_key}` }, body: JSON.stringify(requestBody) }); console.error(`Response status: ${response.status} ${response.statusText}`); console.error(`Response headers: ${JSON.stringify(Object.fromEntries([...response.headers]), null, 2)}`); // Clone the response to log the raw response body const responseClone = response.clone(); const rawResponseText = await responseClone.text(); console.error(`Raw response body: ${rawResponseText}`); if (!response.ok) { const errorText = await response.text(); console.error(`Error from agent: ${response.status} ${errorText}`); return { content: [ { type: "text", text: `Error from agent: ${errorText}`, }, ], }; } // Try to parse the response as JSON let data; try { data = JSON.parse(rawResponseText); console.error(`Response received from agent and parsed as JSON`); } catch (parseError) { console.error(`Failed to parse response as JSON: ${parseError}`); return { content: [ { type: "text", text: `Error parsing response: ${parseError}\nRaw response: ${rawResponseText}`, }, ], }; } console.error(`Response data: ${JSON.stringify(data, null, 2)}`); // Extract the assistant's response const assistantMessage = data.choices?.[0]?.message; if (!assistantMessage) { console.error(`No assistant message found in response: ${JSON.stringify(data, null, 2)}`); return { content: [ { type: "text", text: "No message in agent response", }, ], }; } console.error(`Assistant message: ${JSON.stringify(assistantMessage, null, 2)}`); // Add the assistant's response to the conversation history if (assistantMessage) { conversationHistory.push({ role: assistantMessage.role || 'assistant', content: assistantMessage.content }); console.error(`Added assistant response to history. History now has ${conversationHistory.length} messages`); } // Handle the case where content is already an array of content blocks if (Array.isArray(assistantMessage.content)) { console.error(`Content is an array, processing directly`); // Map the content array to the expected format const processedContent = assistantMessage.content.map((item: any) => { if (typeof item === 'string') { return { type: "text", text: item }; } // Handle content blocks (text, image, etc.) if (item.text) { return { type: "text", text: item.text }; } // For other types, convert to string representation return { type: "text", text: `[${item.type || 'unknown'} content: ${JSON.stringify(item)}]` }; }); console.error(`Directly processed content: ${JSON.stringify(processedContent, null, 2)}`); return { content: processedContent, }; } // Process the content from the assistant's message (for string or other formats) const processedContent = processContent(assistantMessage.content); console.error(`Processed content: ${JSON.stringify(processedContent, null, 2)}`); return { content: processedContent, }; } catch (error) { console.error('Error forwarding request:', error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }, );
- src/index.ts:104-136 (helper)Helper function processContent() that normalizes agent response content into an array of {type: 'text', text: string} blocks, handling strings, arrays, and content objects.function processContent(content: any): { type: "text"; text: string }[] { if (!content) { return [{ type: "text", text: "No content in response" }]; } // If content is a string, return it as text if (typeof content === 'string') { return [{ type: "text", text: content }]; } // If content is an array, process each item if (Array.isArray(content)) { return content.map(item => { if (typeof item === 'string') { return { type: "text", text: item }; } // Handle content blocks (text, image, etc.) if (item.text) { return { type: "text", text: item.text }; } // For other types, convert to string representation return { type: "text", text: `[${item.type} content: ${JSON.stringify(item)}]` }; }); } // If we can't process it, return a string representation return [{ type: "text", text: JSON.stringify(content) }]; }