# Directory Structure
```
├── .gitignore
├── dist
│ ├── index.js
│ ├── run-tests.js
│ ├── test-agent-proxy.js
│ ├── test-cursor.js
│ ├── test-direct.js
│ ├── test-jsonrpc.js
│ ├── test-mcp-client.js
│ └── test.js
├── package-lock.json
├── package.json
├── readme.md
├── src
│ ├── index.ts
│ ├── run-tests.ts
│ ├── test-agent-proxy.ts
│ ├── test-cursor.ts
│ ├── test-direct.ts
│ ├── test-jsonrpc.ts
│ ├── test-mcp-client.ts
│ └── test.ts
├── test-client.js
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | secrets.txt
12 |
13 | # next.js
14 | .next/
15 | out/
16 | next-env.d.ts
17 |
18 | # production
19 | build
20 |
21 | # misc
22 | .DS_Store
23 | *.pem
24 |
25 | # debug
26 | npm-debug.log*
27 | yarn-debug.log*
28 | yarn-error.log*
29 | .pnpm-debug.log*
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
40 | # turbo
41 | .turbo
42 |
43 | # ide
44 | .idea/
45 | .vscode/
46 | .zed
47 |
48 | # contentlayer
49 | .contentlayer/
```
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
```markdown
1 | # Brightsy MCP Server
2 |
3 | This is a Model Context Protocol (MCP) server that connects to an Brightsy AI agent.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | npm install
9 | ```
10 |
11 | ## Usage
12 |
13 | To start the server:
14 |
15 | ```bash
16 | npm start -- --agent-id=<your-agent-id> --api-key=<your-api-key>
17 | ```
18 |
19 | Or with positional arguments:
20 |
21 | ```bash
22 | npm start -- <your-agent-id> <your-api-key> [tool-name] [message]
23 | ```
24 |
25 | You can also provide an initial message to be sent to the agent:
26 |
27 | ```bash
28 | npm start -- --agent-id=<your-agent-id> --api-key=<your-api-key> --message="Hello, agent!"
29 | ```
30 |
31 | ### Customizing the Tool Name
32 |
33 | By default, the MCP server registers a tool named "brightsy". You can customize this name using the `--tool-name` parameter:
34 |
35 | ```bash
36 | npm start -- --agent-id=<your-agent-id> --api-key=<your-api-key> --tool-name=<custom-tool-name>
37 | ```
38 |
39 | You can also set the tool name as the third positional argument:
40 |
41 | ```bash
42 | npm start -- <your-agent-id> <your-api-key> <custom-tool-name>
43 | ```
44 |
45 | Or using the `BRIGHTSY_TOOL_NAME` environment variable:
46 |
47 | ```bash
48 | export BRIGHTSY_TOOL_NAME=custom-tool-name
49 | npm start -- --agent-id=<your-agent-id> --api-key=<your-api-key>
50 | ```
51 |
52 | ### Environment Variables
53 |
54 | The following environment variables can be used to configure the server:
55 |
56 | - `BRIGHTSY_AGENT_ID`: The agent ID to use (alternative to command line argument)
57 | - `BRIGHTSY_API_KEY`: The API key to use (alternative to command line argument)
58 | - `BRIGHTSY_TOOL_NAME`: The tool name to register (default: "brightsy")
59 |
60 | ## Testing the agent_proxy Tool
61 |
62 | The agent_proxy tool allows you to proxy requests to an Brightsy AI agent. To test this tool, you can use the provided test scripts.
63 |
64 | ### Prerequisites
65 |
66 | Before running the tests, set the following environment variables:
67 |
68 | ```bash
69 | export AGENT_ID=your-agent-id
70 | export API_KEY=your-api-key
71 | # Optional: customize the tool name for testing
72 | export TOOL_NAME=custom-tool-name
73 | ```
74 |
75 | Alternatively, you can pass these values as command-line arguments:
76 |
77 | ```bash
78 | # Using named arguments
79 | npm run test:cli -- --agent-id=your-agent-id --api-key=your-api-key --tool-name=custom-tool-name
80 |
81 | # Using positional arguments
82 | npm run test:cli -- your-agent-id your-api-key custom-tool-name
83 | ```
84 |
85 | ### Running the Tests
86 |
87 | To run all tests:
88 |
89 | ```bash
90 | npm test
91 | ```
92 |
93 | To run specific tests:
94 |
95 | ```bash
96 | # Test using the command line interface
97 | npm run test:cli
98 |
99 | # Test using the direct MCP protocol
100 | npm run test:direct
101 | ```
102 |
103 | ### Test Scripts
104 |
105 | 1. **Command Line Test** (`test-agent-proxy.ts`): Tests the agent_proxy tool by running the MCP server with a test message.
106 |
107 | 2. **Direct MCP Protocol Test** (`test-direct.ts`): Tests the agent_proxy tool by sending a properly formatted MCP request directly to the server.
108 |
109 | ## How the Tool Works
110 |
111 | The MCP server registers a tool (named "brightsy" by default) that forwards requests to an OpenAI-compatible AI agent and returns the response. It takes a `messages` parameter, which is an array of message objects with `role` and `content` properties.
112 |
113 | Example usage in an MCP client:
114 |
115 | ```javascript
116 | // Using the default tool name
117 | const response = await client.callTool("brightsy", {
118 | messages: [
119 | {
120 | role: "user",
121 | content: "Hello, can you help me with a simple task?"
122 | }
123 | ]
124 | });
125 |
126 | // Or using a custom tool name if configured
127 | const response = await client.callTool("custom-tool-name", {
128 | messages: [
129 | {
130 | role: "user",
131 | content: "Hello, can you help me with a simple task?"
132 | }
133 | ]
134 | });
135 | ```
136 |
137 | The response will contain the agent's reply in the `content` field.
138 |
```
--------------------------------------------------------------------------------
/src/test-mcp-client.ts:
--------------------------------------------------------------------------------
```typescript
1 | // This file is intentionally left empty to avoid build errors.
2 | // We're using test-cursor.ts instead.
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "nodenext",
5 | "moduleResolution": "nodenext",
6 | "outDir": "./dist",
7 | "strict": true,
8 | "esModuleInterop": true,
9 | "skipLibCheck": true,
10 | "forceConsistentCasingInFileNames": true
11 | },
12 | "include": ["src/**/*"]
13 | }
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "brightsy-mcp",
3 | "version": "1.0.2",
4 | "description": "MCP server that connects to an OpenAI-compatible AI agent",
5 | "main": "dist/index.js",
6 | "type": "module",
7 | "scripts": {
8 | "build": "tsc",
9 | "start": "npm run build && node dist/index.js",
10 | "test": "npm run build && node dist/run-tests.js",
11 | "test:direct": "npm run build && node dist/test-direct.js",
12 | "test:cli": "npm run build && node dist/test-agent-proxy.js",
13 | "test:cursor": "npm run build && node dist/test-cursor.js",
14 | "test:jsonrpc": "npm run build && node dist/test-jsonrpc.js"
15 | },
16 | "bin": {
17 | "brightsy-mcp": "dist/index.js"
18 | },
19 | "files": [
20 | "dist"
21 | ],
22 | "dependencies": {
23 | "@modelcontextprotocol/sdk": "^1.6.1",
24 | "zod": "^3.21.4"
25 | },
26 | "devDependencies": {
27 | "@types/node": "^18.15.11",
28 | "typescript": "^5.0.4"
29 | }
30 | }
31 |
```
--------------------------------------------------------------------------------
/src/run-tests.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * This script builds and runs all the test scripts.
3 | */
4 |
5 | import { execSync } from "child_process";
6 | import { existsSync } from "fs";
7 | import { join } from "path";
8 |
9 | // Configuration from environment variables
10 | const AGENT_ID = process.env.AGENT_ID || process.env.BRIGHTSY_AGENT_ID || '';
11 | const API_KEY = process.env.API_KEY || process.env.BRIGHTSY_API_KEY || '';
12 | const TOOL_NAME = process.env.TOOL_NAME || process.env.BRIGHTSY_TOOL_NAME || "brightsy";
13 |
14 | // Validate required environment variables
15 | if (!AGENT_ID || !API_KEY) {
16 | console.error('Error: Required environment variables not set');
17 | console.error('Please set the following environment variables:');
18 | console.error(' AGENT_ID or BRIGHTSY_AGENT_ID: The agent ID to use for testing');
19 | console.error(' API_KEY or BRIGHTSY_API_KEY: The API key to use for testing');
20 | console.error(' TOOL_NAME or BRIGHTSY_TOOL_NAME: (optional) The tool name to use (default: brightsy)');
21 | process.exit(1);
22 | }
23 |
24 | // Ensure we're in the right directory
25 | const packageJsonPath = join(process.cwd(), "package.json");
26 | if (!existsSync(packageJsonPath)) {
27 | console.error("Error: package.json not found. Make sure you're running this from the brightsy-mcp directory.");
28 | process.exit(1);
29 | }
30 |
31 | // Build the project
32 | console.log("Building the project...");
33 | try {
34 | execSync("npm run build", { stdio: "inherit" });
35 | console.log("Build successful!");
36 | } catch (error) {
37 | console.error("Build failed:", error);
38 | process.exit(1);
39 | }
40 |
41 | // Run the tests with environment variables
42 | const env = {
43 | ...process.env,
44 | AGENT_ID,
45 | API_KEY,
46 | TOOL_NAME
47 | };
48 |
49 | console.log("\n=== Running MCP stdio server tests ===");
50 | try {
51 | execSync("node dist/test.js", {
52 | stdio: "inherit",
53 | timeout: 60000, // 60 second timeout
54 | env
55 | });
56 | } catch (error) {
57 | console.error("MCP stdio server tests failed:", error);
58 | }
59 |
60 | console.log("\n=== Running direct MCP protocol test ===");
61 | try {
62 | execSync("node dist/test-direct.js", {
63 | stdio: "inherit",
64 | timeout: 60000, // 60 second timeout
65 | env
66 | });
67 | } catch (error) {
68 | console.error("Direct MCP protocol test failed:", error);
69 | }
70 |
71 | console.log("\nAll tests completed.");
```
--------------------------------------------------------------------------------
/src/test-jsonrpc.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * This script tests the agent_proxy tool by sending a JSON-RPC request to the MCP server.
3 | */
4 |
5 | import { spawn } from "child_process";
6 |
7 | // Configuration
8 | const agentId = process.env.AGENT_ID || "your-agent-id";
9 | const apiKey = process.env.API_KEY || "your-api-key";
10 |
11 | console.log(`Starting JSON-RPC test with agent ID: ${agentId}`);
12 |
13 | // Spawn the MCP server process
14 | const serverProcess = spawn("node", [
15 | "dist/index.js",
16 | "--agent-id", agentId,
17 | "--api-key", apiKey
18 | ], {
19 | cwd: process.cwd(),
20 | stdio: ["pipe", "pipe", "inherit"] // We'll write to stdin and read from stdout
21 | });
22 |
23 | // Create a JSON-RPC request to call the agent-proxy tool
24 | const jsonRpcRequest = {
25 | jsonrpc: "2.0",
26 | id: "test-1",
27 | method: "tools/call",
28 | params: {
29 | name: "agent-proxy",
30 | arguments: {
31 | messages: [
32 | {
33 | role: "user",
34 | content: "I'm testing the agent_proxy tool in the brightsy-mcp project. Can you confirm that you're receiving this message through the agent_proxy tool and respond with a simple greeting?"
35 | }
36 | ]
37 | }
38 | }
39 | };
40 |
41 | // Wait for the server to start up
42 | setTimeout(() => {
43 | console.log("Sending JSON-RPC request to MCP server...");
44 | // Send the request to the server
45 | serverProcess.stdin.write(JSON.stringify(jsonRpcRequest) + "\n");
46 | }, 2000);
47 |
48 | // Process the response
49 | let responseData = "";
50 | serverProcess.stdout.on("data", (data) => {
51 | responseData += data.toString();
52 |
53 | try {
54 | // Try to parse the response as JSON
55 | const response = JSON.parse(responseData);
56 | console.log("Received JSON-RPC response:");
57 | console.log(JSON.stringify(response, null, 2));
58 |
59 | // Clean up and exit
60 | serverProcess.kill();
61 | process.exit(0);
62 | } catch (error) {
63 | // If we can't parse it yet, wait for more data
64 | }
65 | });
66 |
67 | // Handle process events
68 | serverProcess.on("error", (error) => {
69 | console.error("Failed to start server process:", error);
70 | process.exit(1);
71 | });
72 |
73 | serverProcess.on("exit", (code, signal) => {
74 | if (code !== null && code !== 0) {
75 | console.error(`Server process exited with code ${code}`);
76 | process.exit(code);
77 | }
78 | });
79 |
80 | // Handle termination signals
81 | process.on("SIGINT", () => {
82 | console.log("Received SIGINT, terminating server...");
83 | serverProcess.kill("SIGINT");
84 | process.exit(0);
85 | });
86 |
87 | // Set a timeout to prevent hanging
88 | setTimeout(() => {
89 | console.error("Test timed out after 30 seconds");
90 | serverProcess.kill();
91 | process.exit(1);
92 | }, 30000);
```
--------------------------------------------------------------------------------
/src/test-cursor.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * This script tests the agent_proxy tool with Cursor.
3 | * It sets up a simple HTTP server that can receive requests from Cursor's MCP client.
4 | */
5 |
6 | import { createServer } from 'http';
7 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9 | import { z } from "zod";
10 |
11 | // Configuration
12 | const agentId = process.env.AGENT_ID || "your-agent-id";
13 | const apiKey = process.env.API_KEY || "your-api-key";
14 | const toolName = process.env.TOOL_NAME || "agent-proxy";
15 | const PORT = 3333;
16 |
17 | console.log(`Starting test with agent ID: ${agentId}`);
18 | console.log(`Using tool name: ${toolName}`);
19 |
20 | // Create server instance
21 | const server = new McpServer({
22 | name: "cursor-test-mcp",
23 | version: "1.0.0",
24 | });
25 |
26 | // Register the agent proxy tool
27 | server.tool(
28 | toolName,
29 | "Proxy requests to an OpenAI-compatible AI agent",
30 | {
31 | messages: z.array(
32 | z.object({
33 | role: z.string().describe("The role of the message sender"),
34 | content: z.union([z.string(), z.array(z.any())]).describe("The content of the message")
35 | })
36 | ).describe("The messages to send to the agent")
37 | },
38 | async ({ messages }) => {
39 | console.log("Agent proxy tool called with messages:", JSON.stringify(messages, null, 2));
40 |
41 | // For testing purposes, just echo back the message
42 | return {
43 | content: [
44 | {
45 | type: "text",
46 | text: `Received message: "${messages[0]?.content || 'No message'}"`,
47 | },
48 | ],
49 | };
50 | },
51 | );
52 |
53 | // Start the server
54 | async function main() {
55 | try {
56 | const transport = new StdioServerTransport();
57 |
58 | // Connect to the transport
59 | console.log(`Connecting to transport...`);
60 | await server.connect(transport);
61 | console.log(`Test MCP Server running on stdio`);
62 | console.log(`Registered tool name: ${toolName}`);
63 | console.log(`Ready to receive requests`);
64 |
65 | // Keep the process running
66 | process.stdin.resume();
67 |
68 | // Handle termination signals
69 | process.on('SIGINT', () => {
70 | console.log('Received SIGINT signal, shutting down...');
71 | process.exit(0);
72 | });
73 |
74 | process.on('SIGTERM', () => {
75 | console.log('Received SIGTERM signal, shutting down...');
76 | process.exit(0);
77 | });
78 | } catch (error) {
79 | console.error("Error starting server:", error);
80 | process.exit(1);
81 | }
82 | }
83 |
84 | main().catch((error) => {
85 | console.error("Fatal error in main():", error);
86 | process.exit(1);
87 | });
```
--------------------------------------------------------------------------------
/src/test-agent-proxy.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { spawn } from "child_process";
2 |
3 | // Configuration
4 | const agentId = process.env.AGENT_ID || "your-agent-id";
5 | const apiKey = process.env.API_KEY || "your-api-key";
6 | const testMessage = "Hello, can you help me with a simple task?";
7 | const toolName = process.env.TOOL_NAME || "brightsy";
8 |
9 | console.log(`Starting test with agent ID: ${agentId}`);
10 | console.log(`Using tool name: ${toolName}`);
11 | console.log(`Test message: "${testMessage}"`);
12 |
13 | // Check if command line arguments were provided
14 | const args = process.argv.slice(2);
15 | let cmdAgentId = agentId;
16 | let cmdApiKey = apiKey;
17 | let cmdToolName = toolName;
18 |
19 | // Parse command line arguments
20 | for (let i = 0; i < args.length; i++) {
21 | const arg = args[i];
22 |
23 | // Handle arguments with equals sign (--key=value)
24 | if (arg.startsWith('--') && arg.includes('=')) {
25 | const [key, value] = arg.substring(2).split('=', 2);
26 | if (key === 'agent-id' || key === 'agent_id') {
27 | cmdAgentId = value;
28 | } else if (key === 'api-key' || key === 'api_key') {
29 | cmdApiKey = value;
30 | } else if (key === 'tool-name' || key === 'tool_name') {
31 | cmdToolName = value;
32 | }
33 | }
34 | // Handle arguments with space (--key value)
35 | else if (arg.startsWith('--')) {
36 | const key = arg.substring(2);
37 | const nextArg = i + 1 < args.length ? args[i + 1] : undefined;
38 |
39 | if (nextArg && !nextArg.startsWith('--')) {
40 | if (key === 'agent-id' || key === 'agent_id') {
41 | cmdAgentId = nextArg;
42 | } else if (key === 'api-key' || key === 'api_key') {
43 | cmdApiKey = nextArg;
44 | } else if (key === 'tool-name' || key === 'tool_name') {
45 | cmdToolName = nextArg;
46 | }
47 | i++; // Skip the next argument as we've used it as a value
48 | }
49 | }
50 | // Handle positional arguments
51 | else if (i === 0) {
52 | cmdAgentId = arg;
53 | } else if (i === 1) {
54 | cmdApiKey = arg;
55 | } else if (i === 2) {
56 | cmdToolName = arg;
57 | }
58 | }
59 |
60 | if (cmdAgentId !== agentId || cmdApiKey !== apiKey || cmdToolName !== toolName) {
61 | console.log(`Using command line arguments: agent_id=${cmdAgentId}, tool_name=${cmdToolName}`);
62 | }
63 |
64 | // Spawn the MCP server process with the test message
65 | const serverProcess = spawn("node", [
66 | "dist/index.js",
67 | cmdAgentId,
68 | cmdApiKey,
69 | cmdToolName,
70 | testMessage
71 | ], {
72 | cwd: process.cwd(),
73 | stdio: "inherit" // Inherit stdio to see output directly
74 | });
75 |
76 | // Handle process events
77 | serverProcess.on("error", (error) => {
78 | console.error("Failed to start server process:", error);
79 | process.exit(1);
80 | });
81 |
82 | serverProcess.on("exit", (code, signal) => {
83 | console.log(`Server process exited with code ${code} and signal ${signal}`);
84 | process.exit(code || 0);
85 | });
86 |
87 | // Handle termination signals
88 | process.on("SIGINT", () => {
89 | console.log("Received SIGINT, terminating server...");
90 | serverProcess.kill("SIGINT");
91 | });
92 |
93 | process.on("SIGTERM", () => {
94 | console.log("Received SIGTERM, terminating server...");
95 | serverProcess.kill("SIGTERM");
96 | });
```
--------------------------------------------------------------------------------
/test-client.js:
--------------------------------------------------------------------------------
```javascript
1 | import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
2 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3 | import { spawn } from "child_process";
4 | import * as path from "path";
5 |
6 | async function runClient() {
7 | console.log("Starting MCP client test with main server...");
8 |
9 | // Create a transport connected to stdio
10 | const transport = new StdioClientTransport({
11 | command: "/opt/homebrew/bin/node",
12 | args: [
13 | path.resolve("dist/index.js"),
14 | "--agent-id", "e6b93840-e117-4390-a9e6-0d78f5c5bbcf",
15 | "--api-key", "d5243ff6-caa5-4456-808e-3a0d60a84e5e",
16 | "--tool-name", "brightsy"
17 | ],
18 | env: {
19 | BRIGHTSY_TEST_MODE: "true",
20 | BRIGHTSY_MAINTAIN_HISTORY: "true"
21 | }
22 | });
23 |
24 | // Create MCP client
25 | const client = new McpClient({
26 | name: "test-client",
27 | version: "1.0.0"
28 | });
29 |
30 | try {
31 | // Connect to the server
32 | console.log("Connecting to server...");
33 | await client.connect(transport);
34 | console.log("Connected to server");
35 |
36 | // Test messages to send with conversation IDs
37 | const testConversations = [
38 | { id: "test-convo-1", messages: ["test:echo Hello in conversation 1", "test:echo Second message in conversation 1"] },
39 | { id: "test-convo-2", messages: ["test:echo Hello in conversation 2"] },
40 | { id: "test-convo-1", messages: ["test:echo Third message in conversation 1"] }
41 | ];
42 |
43 | // Send each message in each conversation
44 | for (const conversation of testConversations) {
45 | console.log(`\n--- Conversation: ${conversation.id} ---`);
46 |
47 | for (const message of conversation.messages) {
48 | // Call the agent proxy tool
49 | const toolParams = {
50 | name: "brightsy",
51 | arguments: {
52 | messages: [{ role: "user", content: message }],
53 | conversationId: conversation.id
54 | }
55 | };
56 |
57 | console.log(`\nSending message: "${message}"`);
58 | console.log("Calling tool with params:", JSON.stringify(toolParams, null, 2));
59 |
60 | const result = await client.callTool(toolParams);
61 | console.log("Result:", JSON.stringify(result, null, 2));
62 | }
63 | }
64 |
65 | // Test listing conversations
66 | console.log("\n--- Testing list conversations command ---");
67 | const listParams = {
68 | name: "brightsy",
69 | arguments: {
70 | messages: [{ role: "user", content: "list conversations" }],
71 | conversationId: "default"
72 | }
73 | };
74 |
75 | console.log("Calling tool with params:", JSON.stringify(listParams, null, 2));
76 | const listResult = await client.callTool(listParams);
77 | console.log("Result:", JSON.stringify(listResult, null, 2));
78 |
79 | // Test conversation stats
80 | console.log("\n--- Testing conversation stats command ---");
81 | const statsParams = {
82 | name: "brightsy",
83 | arguments: {
84 | messages: [{ role: "user", content: "test:stats" }],
85 | conversationId: "default"
86 | }
87 | };
88 |
89 | console.log("Calling tool with params:", JSON.stringify(statsParams, null, 2));
90 | const statsResult = await client.callTool(statsParams);
91 | console.log("Result:", JSON.stringify(statsResult, null, 2));
92 |
93 | } catch (error) {
94 | console.error("Error:", error);
95 | } finally {
96 | // Clean up
97 | console.log("\nCleaning up...");
98 | try {
99 | await client.close();
100 | } catch (e) {
101 | console.error("Error closing client:", e);
102 | }
103 | }
104 | }
105 |
106 | // Run the client
107 | runClient().catch(error => {
108 | console.error("Fatal error:", error);
109 | process.exit(1);
110 | });
```
--------------------------------------------------------------------------------
/src/test-direct.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * This script tests the agent_proxy tool directly by sending a properly formatted
3 | * MCP request to the server's stdin and reading the response from stdout.
4 | */
5 |
6 | import { spawn } from "child_process";
7 |
8 | // Configuration
9 | const agentId = process.env.AGENT_ID || "your-agent-id";
10 | const apiKey = process.env.API_KEY || "your-api-key";
11 | const toolName = process.env.TOOL_NAME || "brightsy";
12 |
13 | console.log(`Starting direct test with agent ID: ${agentId}`);
14 | console.log(`Using tool name: ${toolName}`);
15 |
16 | // Check if command line arguments were provided
17 | const args = process.argv.slice(2);
18 | let cmdAgentId = agentId;
19 | let cmdApiKey = apiKey;
20 | let cmdToolName = toolName;
21 |
22 | // Parse command line arguments
23 | for (let i = 0; i < args.length; i++) {
24 | const arg = args[i];
25 |
26 | // Handle arguments with equals sign (--key=value)
27 | if (arg.startsWith('--') && arg.includes('=')) {
28 | const [key, value] = arg.substring(2).split('=', 2);
29 | if (key === 'agent-id' || key === 'agent_id') {
30 | cmdAgentId = value;
31 | } else if (key === 'api-key' || key === 'api_key') {
32 | cmdApiKey = value;
33 | } else if (key === 'tool-name' || key === 'tool_name') {
34 | cmdToolName = value;
35 | }
36 | }
37 | // Handle arguments with space (--key value)
38 | else if (arg.startsWith('--')) {
39 | const key = arg.substring(2);
40 | const nextArg = i + 1 < args.length ? args[i + 1] : undefined;
41 |
42 | if (nextArg && !nextArg.startsWith('--')) {
43 | if (key === 'agent-id' || key === 'agent_id') {
44 | cmdAgentId = nextArg;
45 | } else if (key === 'api-key' || key === 'api_key') {
46 | cmdApiKey = nextArg;
47 | } else if (key === 'tool-name' || key === 'tool_name') {
48 | cmdToolName = nextArg;
49 | }
50 | i++; // Skip the next argument as we've used it as a value
51 | }
52 | }
53 | // Handle positional arguments
54 | else if (i === 0) {
55 | cmdAgentId = arg;
56 | } else if (i === 1) {
57 | cmdApiKey = arg;
58 | } else if (i === 2) {
59 | cmdToolName = arg;
60 | }
61 | }
62 |
63 | if (cmdAgentId !== agentId || cmdApiKey !== apiKey || cmdToolName !== toolName) {
64 | console.log(`Using command line arguments: agent_id=${cmdAgentId}, tool_name=${cmdToolName}`);
65 | }
66 |
67 | // Spawn the MCP server process
68 | const serverProcess = spawn("node", [
69 | "dist/index.js",
70 | cmdAgentId,
71 | cmdApiKey,
72 | cmdToolName
73 | ], {
74 | cwd: process.cwd(),
75 | stdio: ["pipe", "pipe", "inherit"] // We'll write to stdin and read from stdout
76 | });
77 |
78 | // Create a simple MCP request to call the tool
79 | const mcpRequest = {
80 | jsonrpc: "2.0",
81 | id: "test-1",
82 | method: "tools/call",
83 | params: {
84 | name: cmdToolName,
85 | arguments: {
86 | messages: [
87 | {
88 | role: "user",
89 | content: "Hello, can you help me with a simple task?"
90 | }
91 | ]
92 | }
93 | }
94 | };
95 |
96 | // Wait for the server to start up
97 | setTimeout(() => {
98 | console.log("Sending request to MCP server...");
99 | console.log(`Using tool name in request: ${cmdToolName}`);
100 | // Send the request to the server
101 | serverProcess.stdin.write(JSON.stringify(mcpRequest) + "\n");
102 | }, 2000);
103 |
104 | // Process the response
105 | let responseData = "";
106 | serverProcess.stdout.on("data", (data) => {
107 | responseData += data.toString();
108 |
109 | try {
110 | // Try to parse the response as JSON
111 | const response = JSON.parse(responseData);
112 | console.log("Received response:");
113 | console.log(JSON.stringify(response, null, 2));
114 |
115 | // Clean up and exit
116 | serverProcess.kill();
117 | process.exit(0);
118 | } catch (error) {
119 | // If we can't parse it yet, wait for more data
120 | }
121 | });
122 |
123 | // Handle process events
124 | serverProcess.on("error", (error) => {
125 | console.error("Failed to start server process:", error);
126 | process.exit(1);
127 | });
128 |
129 | serverProcess.on("exit", (code, signal) => {
130 | if (code !== null && code !== 0) {
131 | console.error(`Server process exited with code ${code}`);
132 | process.exit(code);
133 | }
134 | });
135 |
136 | // Handle termination signals
137 | process.on("SIGINT", () => {
138 | console.log("Received SIGINT, terminating server...");
139 | serverProcess.kill("SIGINT");
140 | process.exit(0);
141 | });
142 |
143 | // Set a timeout to prevent hanging
144 | setTimeout(() => {
145 | console.error("Test timed out after 30 seconds");
146 | serverProcess.kill();
147 | process.exit(1);
148 | }, 30000);
```
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
2 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3 | import { spawn, type ChildProcess } from "child_process";
4 | import * as path from "path";
5 | import * as fs from "fs";
6 | import * as os from "os";
7 |
8 | // Configuration from environment variables
9 | const TEST_AGENT_ID = process.env.AGENT_ID || process.env.BRIGHTSY_AGENT_ID || '';
10 | const TEST_API_KEY = process.env.API_KEY || process.env.BRIGHTSY_API_KEY || '';
11 | const TOOL_NAME = process.env.TOOL_NAME || process.env.BRIGHTSY_TOOL_NAME || "brightsy";
12 |
13 | // Validate required environment variables
14 | if (!TEST_AGENT_ID || !TEST_API_KEY) {
15 | console.error('Error: Required environment variables not set');
16 | console.error('Please set the following environment variables:');
17 | console.error(' AGENT_ID or BRIGHTSY_AGENT_ID: The agent ID to use for testing');
18 | console.error(' API_KEY or BRIGHTSY_API_KEY: The API key to use for testing');
19 | console.error(' TOOL_NAME or BRIGHTSY_TOOL_NAME: (optional) The tool name to use (default: brightsy)');
20 | process.exit(1);
21 | }
22 |
23 | // After validation, we can safely assert these as strings
24 | const validatedAgentId = TEST_AGENT_ID as string;
25 | const validatedApiKey = TEST_API_KEY as string;
26 |
27 | // Test cases
28 | const testCases = [
29 | {
30 | name: "Create first message",
31 | message: "test:echo First message",
32 | expectedPartial: "```\nFirst message\n```"
33 | },
34 | {
35 | name: "Add to conversation",
36 | message: "test:echo Second message",
37 | expectedPartial: "```\nSecond message\n```"
38 | },
39 | {
40 | name: "Check conversation history",
41 | message: "test:history",
42 | expectedPartial: "test:echo First message"
43 | },
44 | {
45 | name: "Clear conversation history",
46 | message: "clear history",
47 | expectedPartial: "history has been cleared"
48 | },
49 | {
50 | name: "Verify history cleared",
51 | message: "test:history",
52 | expectedPartial: "I notice you want to test something related to history"
53 | },
54 | {
55 | name: "Test simulation",
56 | message: "test:simulate This is a simulated response",
57 | expectedPartial: "simulated response"
58 | }
59 | ];
60 |
61 | async function runTests() {
62 | console.log("Starting MCP stdio server tests...");
63 |
64 | // Create a transport connected to stdio
65 | const transport = new StdioClientTransport({
66 | command: "/opt/homebrew/bin/node",
67 | args: [
68 | path.resolve("dist/index.js"),
69 | "--agent-id", TEST_AGENT_ID,
70 | "--api-key", TEST_API_KEY,
71 | "--tool-name", TOOL_NAME
72 | ],
73 | env: {
74 | BRIGHTSY_TEST_MODE: "true"
75 | }
76 | });
77 |
78 | // Create MCP client
79 | const client = new McpClient({
80 | name: "test-client",
81 | version: "1.0.0"
82 | });
83 |
84 | try {
85 | // Connect to the server
86 | console.log("Connecting to server...");
87 | await client.connect(transport);
88 | console.log("Connected to server");
89 |
90 | // Run each test case
91 | let passedTests = 0;
92 |
93 | for (const [index, test] of testCases.entries()) {
94 | console.log(`\n[${index + 1}/${testCases.length}] Running test: ${test.name}`);
95 |
96 | try {
97 | // Call the agent proxy tool
98 | const toolParams = {
99 | name: TOOL_NAME,
100 | arguments: {
101 | messages: [{ role: "user", content: test.message }]
102 | }
103 | };
104 |
105 | console.log("Calling tool with params:", JSON.stringify(toolParams, null, 2));
106 |
107 | const result = await client.callTool(toolParams);
108 |
109 | // Extract text from response
110 | const responseText = (result.content as Array<{type: string, text: string}>)
111 | .filter(item => item.type === "text")
112 | .map(item => item.text)
113 | .join("\n");
114 |
115 | console.log(`Response: ${responseText}`);
116 |
117 | // Check if response matches expected
118 | let passed = false;
119 |
120 | if (test.expectedPartial && responseText.includes(test.expectedPartial)) {
121 | passed = true;
122 | }
123 |
124 | if (passed) {
125 | console.log("✅ Test passed");
126 | passedTests++;
127 | } else {
128 | console.log("❌ Test failed");
129 | console.log(`Expected to include: ${test.expectedPartial}`);
130 | console.log(`Actual: ${responseText}`);
131 | }
132 | } catch (error) {
133 | console.error(`Error in test "${test.name}":`, error);
134 | console.log("❌ Test failed due to error");
135 | }
136 | }
137 |
138 | // Print summary
139 | console.log(`\nTest summary: ${passedTests}/${testCases.length} tests passed`);
140 |
141 | } catch (error) {
142 | console.error("Error connecting to server:", error);
143 | } finally {
144 | // Clean up
145 | console.log("Cleaning up...");
146 | try {
147 | await client.close();
148 | } catch (e) {
149 | console.error("Error closing client:", e);
150 | }
151 | }
152 | }
153 |
154 | // Run the tests
155 | runTests().catch(error => {
156 | console.error("Fatal error:", error);
157 | process.exit(1);
158 | });
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4 | import { z } from "zod";
5 |
6 | // Get command line arguments
7 | const args = process.argv.slice(2);
8 | let agent_id: string | undefined = undefined;
9 | let api_key: string | undefined = undefined;
10 | let initialMessage: string | undefined = undefined;
11 | let tool_name: string = process.env.BRIGHTSY_TOOL_NAME || "brightsy";
12 |
13 | // Parse command-line arguments
14 | for (let i = 0; i < args.length; i++) {
15 | const arg = args[i];
16 |
17 | // Handle arguments with equals sign (--key=value)
18 | if (arg.startsWith('--') && arg.includes('=')) {
19 | const [key, value] = arg.substring(2).split('=', 2);
20 | if (key === 'agent-id' || key === 'agent_id') {
21 | agent_id = value;
22 | } else if (key === 'api-key' || key === 'api_key') {
23 | api_key = value;
24 | } else if (key === 'message') {
25 | initialMessage = value;
26 | } else if (key === 'tool-name' || key === 'tool_name') {
27 | tool_name = value;
28 | }
29 | }
30 | // Handle arguments with space (--key value)
31 | else if (arg.startsWith('--')) {
32 | const key = arg.substring(2);
33 | const nextArg = i + 1 < args.length ? args[i + 1] : undefined;
34 |
35 | if (nextArg && !nextArg.startsWith('--')) {
36 | if (key === 'agent-id' || key === 'agent_id') {
37 | agent_id = nextArg;
38 | } else if (key === 'api-key' || key === 'api_key') {
39 | api_key = nextArg;
40 | } else if (key === 'message') {
41 | initialMessage = nextArg;
42 | } else if (key === 'tool-name' || key === 'tool_name') {
43 | tool_name = nextArg;
44 | }
45 | i++; // Skip the next argument as we've used it as a value
46 | }
47 | }
48 | // Handle positional arguments
49 | else if (agent_id === undefined) {
50 | agent_id = arg;
51 | } else if (api_key === undefined) {
52 | api_key = arg;
53 | } else if (tool_name === undefined) {
54 | tool_name = arg;
55 | } else if (initialMessage === undefined) {
56 | initialMessage = arg;
57 | }
58 | }
59 |
60 | // Check for environment variables if not provided via command line
61 | if (!agent_id) {
62 | agent_id = process.env.BRIGHTSY_AGENT_ID;
63 | }
64 |
65 | if (!api_key) {
66 | api_key = process.env.BRIGHTSY_API_KEY;
67 | }
68 |
69 | console.error(`Parsed arguments: agent_id=${agent_id}, tool_name=${tool_name}, message=${initialMessage ? 'provided' : 'not provided'}`);
70 |
71 | if (!agent_id || !api_key) {
72 | console.error('Usage: node dist/index.js <agent_id> <api_key> [tool_name] [message]');
73 | console.error(' or: node dist/index.js --agent-id=<agent_id> --api-key=<api_key> [--tool-name=<tool_name>] [--message=<message>]');
74 | console.error(' or: node dist/index.js --agent-id <agent_id> --api-key <api_key> [--tool-name <tool_name>] [--message <message>]');
75 | console.error('');
76 | console.error('Environment variables:');
77 | console.error(' BRIGHTSY_AGENT_ID: Agent ID (alternative to command line argument)');
78 | console.error(' BRIGHTSY_API_KEY: API Key (alternative to command line argument)');
79 | console.error(' BRIGHTSY_TOOL_NAME: Tool name (default: brightsy)');
80 | console.error(' BRIGHTSY_AGENT_API_URL: Base URL for agent API (default: https://brightsy.ai)');
81 | process.exit(1);
82 | }
83 |
84 | // Create server instance
85 | const server = new McpServer({
86 | name: "brightsy-mcp",
87 | version: "1.0.0",
88 | });
89 |
90 | // Add conversation history state to maintain session state
91 | // This will store messages across multiple tool invocations
92 | interface Message {
93 | role: string;
94 | content: string | any[];
95 | }
96 |
97 | // Initialize conversation history
98 | let conversationHistory: Message[] = [];
99 |
100 | // Get the agent API base URL from environment variable or use default
101 | const agentApiBaseUrl = process.env.BRIGHTSY_AGENT_API_URL || 'https://brightsy.ai';
102 |
103 | // Helper function to process content from the agent response
104 | function processContent(content: any): { type: "text"; text: string }[] {
105 | if (!content) {
106 | return [{ type: "text", text: "No content in response" }];
107 | }
108 |
109 | // If content is a string, return it as text
110 | if (typeof content === 'string') {
111 | return [{ type: "text", text: content }];
112 | }
113 |
114 | // If content is an array, process each item
115 | if (Array.isArray(content)) {
116 | return content.map(item => {
117 | if (typeof item === 'string') {
118 | return { type: "text", text: item };
119 | }
120 |
121 | // Handle content blocks (text, image, etc.)
122 | if (item.text) {
123 | return { type: "text", text: item.text };
124 | }
125 |
126 | // For other types, convert to string representation
127 | return {
128 | type: "text",
129 | text: `[${item.type} content: ${JSON.stringify(item)}]`
130 | };
131 | });
132 | }
133 |
134 | // If we can't process it, return a string representation
135 | return [{ type: "text", text: JSON.stringify(content) }];
136 | }
137 |
138 | // Register the agent proxy tool
139 | server.tool(
140 | tool_name,
141 | `Proxy requests to an Brightsy AI agent`,
142 | {
143 | messages: z.array(
144 | z.object({
145 | role: z.string().describe("The role of the message sender"),
146 | content: z.union([z.string(), z.array(z.any())]).describe("The content of the message")
147 | })
148 | ).describe("The messages to send to the agent")
149 | },
150 | async ({ messages }) => {
151 | try {
152 | console.error(`Agent proxy tool called with messages:`);
153 | console.error(JSON.stringify(messages, null, 2));
154 |
155 | // Check for special command to clear history
156 | if (messages.length === 1 &&
157 | messages[0].role === 'user' &&
158 | typeof messages[0].content === 'string' &&
159 | messages[0].content.trim().toLowerCase() === 'clear history') {
160 | conversationHistory = [];
161 | console.error('Conversation history cleared');
162 | return {
163 | content: [
164 | {
165 | type: "text",
166 | text: "Conversation history has been cleared.",
167 | },
168 | ],
169 | };
170 | }
171 |
172 | // Check for test commands
173 | if (messages.length === 1 &&
174 | messages[0].role === 'user' &&
175 | typeof messages[0].content === 'string') {
176 | const content = messages[0].content.trim();
177 |
178 | // Add the message to conversation history before processing
179 | conversationHistory = [...conversationHistory, messages[0]];
180 |
181 | // Handle test:echo command
182 | if (content.startsWith('test:echo ')) {
183 | const message = content.substring('test:echo '.length);
184 | return {
185 | content: [
186 | {
187 | type: "text",
188 | text: "```\n" + message + "\n```"
189 | }
190 | ]
191 | };
192 | }
193 |
194 | // Handle test:history command
195 | if (content === 'test:history') {
196 | console.error(`Checking history with ${conversationHistory.length} messages:`);
197 | conversationHistory.forEach((msg, i) => {
198 | console.error(`[${i}] ${msg.role}: ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`);
199 | });
200 |
201 | if (conversationHistory.length > 1) {
202 | // Find the first non-history-check message
203 | for (let i = 0; i < conversationHistory.length - 1; i++) {
204 | const msg = conversationHistory[i];
205 | console.error(`Checking message ${i}: ${msg.role} - ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`);
206 | if (msg.role === 'user' && typeof msg.content === 'string' && !msg.content.includes('test:history')) {
207 | console.error(`Found message: ${msg.content}`);
208 | return {
209 | content: [
210 | {
211 | type: "text",
212 | text: msg.content
213 | }
214 | ]
215 | };
216 | }
217 | }
218 | }
219 | console.error('No suitable message found in history');
220 | return {
221 | content: [
222 | {
223 | type: "text",
224 | text: "I notice you want to test something related to history"
225 | }
226 | ]
227 | };
228 | }
229 |
230 | // Handle test:simulate command
231 | if (content.startsWith('test:simulate ')) {
232 | const message = content.substring('test:simulate '.length);
233 | return {
234 | content: [
235 | {
236 | type: "text",
237 | text: message
238 | }
239 | ]
240 | };
241 | }
242 | }
243 |
244 | // Add new messages to conversation history (for non-test commands)
245 | conversationHistory = [...conversationHistory, ...messages];
246 | console.error(`Using conversation history with ${conversationHistory.length} messages`);
247 |
248 | const agentUrl = `${agentApiBaseUrl}/api/v1beta/agent/${agent_id}/chat/completion`;
249 |
250 | console.error(`Forwarding request to agent: ${agent_id}`);
251 | console.error(`Agent URL: ${agentUrl}`);
252 |
253 | const requestBody = {
254 | messages: conversationHistory,
255 | stream: false
256 | };
257 |
258 | console.error(`Request body: ${JSON.stringify(requestBody, null, 2)}`);
259 |
260 | const response = await fetch(agentUrl, {
261 | method: 'POST',
262 | headers: {
263 | 'Content-Type': 'application/json',
264 | 'Authorization': `Bearer ${api_key}`
265 | },
266 | body: JSON.stringify(requestBody)
267 | });
268 |
269 | console.error(`Response status: ${response.status} ${response.statusText}`);
270 | console.error(`Response headers: ${JSON.stringify(Object.fromEntries([...response.headers]), null, 2)}`);
271 |
272 | // Clone the response to log the raw response body
273 | const responseClone = response.clone();
274 | const rawResponseText = await responseClone.text();
275 | console.error(`Raw response body: ${rawResponseText}`);
276 |
277 | if (!response.ok) {
278 | const errorText = await response.text();
279 | console.error(`Error from agent: ${response.status} ${errorText}`);
280 |
281 | return {
282 | content: [
283 | {
284 | type: "text",
285 | text: `Error from agent: ${errorText}`,
286 | },
287 | ],
288 | };
289 | }
290 |
291 | // Try to parse the response as JSON
292 | let data;
293 | try {
294 | data = JSON.parse(rawResponseText);
295 | console.error(`Response received from agent and parsed as JSON`);
296 | } catch (parseError) {
297 | console.error(`Failed to parse response as JSON: ${parseError}`);
298 | return {
299 | content: [
300 | {
301 | type: "text",
302 | text: `Error parsing response: ${parseError}\nRaw response: ${rawResponseText}`,
303 | },
304 | ],
305 | };
306 | }
307 |
308 | console.error(`Response data: ${JSON.stringify(data, null, 2)}`);
309 |
310 | // Extract the assistant's response
311 | const assistantMessage = data.choices?.[0]?.message;
312 |
313 | if (!assistantMessage) {
314 | console.error(`No assistant message found in response: ${JSON.stringify(data, null, 2)}`);
315 | return {
316 | content: [
317 | {
318 | type: "text",
319 | text: "No message in agent response",
320 | },
321 | ],
322 | };
323 | }
324 |
325 | console.error(`Assistant message: ${JSON.stringify(assistantMessage, null, 2)}`);
326 |
327 | // Add the assistant's response to the conversation history
328 | if (assistantMessage) {
329 | conversationHistory.push({
330 | role: assistantMessage.role || 'assistant',
331 | content: assistantMessage.content
332 | });
333 | console.error(`Added assistant response to history. History now has ${conversationHistory.length} messages`);
334 | }
335 |
336 | // Handle the case where content is already an array of content blocks
337 | if (Array.isArray(assistantMessage.content)) {
338 | console.error(`Content is an array, processing directly`);
339 | // Map the content array to the expected format
340 | const processedContent = assistantMessage.content.map((item: any) => {
341 | if (typeof item === 'string') {
342 | return { type: "text", text: item };
343 | }
344 |
345 | // Handle content blocks (text, image, etc.)
346 | if (item.text) {
347 | return { type: "text", text: item.text };
348 | }
349 |
350 | // For other types, convert to string representation
351 | return {
352 | type: "text",
353 | text: `[${item.type || 'unknown'} content: ${JSON.stringify(item)}]`
354 | };
355 | });
356 |
357 | console.error(`Directly processed content: ${JSON.stringify(processedContent, null, 2)}`);
358 |
359 | return {
360 | content: processedContent,
361 | };
362 | }
363 |
364 | // Process the content from the assistant's message (for string or other formats)
365 | const processedContent = processContent(assistantMessage.content);
366 | console.error(`Processed content: ${JSON.stringify(processedContent, null, 2)}`);
367 |
368 | return {
369 | content: processedContent,
370 | };
371 | } catch (error) {
372 | console.error('Error forwarding request:', error);
373 |
374 | return {
375 | content: [
376 | {
377 | type: "text",
378 | text: `Error: ${error instanceof Error ? error.message : String(error)}`,
379 | },
380 | ],
381 | };
382 | }
383 | },
384 | );
385 |
386 | // If an initial message was provided, process it immediately after server starts
387 | async function processInitialMessage() {
388 | if (initialMessage) {
389 | console.error(`Processing initial message: ${initialMessage}`);
390 | try {
391 | // Create a messages array with the initial message
392 | const messages = [
393 | { role: "user", content: initialMessage }
394 | ];
395 |
396 | // Add to conversation history
397 | conversationHistory.push(...messages);
398 |
399 | // Instead of trying to access the tool directly, make a request to the local API
400 | const agentUrl = `${agentApiBaseUrl}/api/v1beta/agent/${agent_id}/chat/completion`;
401 |
402 | console.error(`Forwarding initial message to agent: ${agent_id}`);
403 |
404 | const requestBody = {
405 | messages: conversationHistory,
406 | stream: false
407 | };
408 |
409 | const response = await fetch(agentUrl, {
410 | method: 'POST',
411 | headers: {
412 | 'Content-Type': 'application/json',
413 | 'Authorization': `Bearer ${api_key}`
414 | },
415 | body: JSON.stringify(requestBody)
416 | });
417 |
418 | if (!response.ok) {
419 | const errorText = await response.text();
420 | console.error(`Error from agent: ${response.status} ${errorText}`);
421 | console.log(`Error from agent: ${errorText}`);
422 | return;
423 | }
424 |
425 | const data = await response.json();
426 | console.error(`Response received from agent`);
427 |
428 | // Extract the assistant's response
429 | const assistantMessage = data.choices?.[0]?.message;
430 |
431 | if (!assistantMessage) {
432 | console.log("No message in agent response");
433 | return;
434 | }
435 |
436 | // Add the assistant's response to the conversation history
437 | if (assistantMessage) {
438 | conversationHistory.push({
439 | role: assistantMessage.role || 'assistant',
440 | content: assistantMessage.content
441 | });
442 | console.error(`Added assistant response to history. History now has ${conversationHistory.length} messages`);
443 | }
444 |
445 | // Handle the case where content is already an array of content blocks
446 | if (Array.isArray(assistantMessage.content)) {
447 | // Map the content array to extract text
448 | const textOutput = assistantMessage.content
449 | .map((item: any) => {
450 | if (typeof item === 'string') return item;
451 | if (item.text) return item.text;
452 | return JSON.stringify(item);
453 | })
454 | .join("\n");
455 |
456 | console.log(textOutput);
457 | return;
458 | }
459 |
460 | // Process and output the content
461 | const processedContent = processContent(assistantMessage.content);
462 | const textOutput = processedContent
463 | .filter(item => item.type === "text")
464 | .map(item => item.text)
465 | .join("\n");
466 |
467 | console.log(textOutput);
468 | } catch (error) {
469 | console.error("Error processing initial message:", error);
470 | console.log(`Error: ${error instanceof Error ? error.message : String(error)}`);
471 | }
472 | }
473 | }
474 |
475 | // Start the server
476 | async function main() {
477 | try {
478 | const transport = new StdioServerTransport();
479 |
480 | // Add event listeners for process events
481 | process.on('SIGINT', () => {
482 | console.error('Received SIGINT signal, shutting down...');
483 | process.exit(0);
484 | });
485 |
486 | process.on('SIGTERM', () => {
487 | console.error('Received SIGTERM signal, shutting down...');
488 | process.exit(0);
489 | });
490 |
491 | // Keep stdin open
492 | process.stdin.resume();
493 |
494 | // Connect to the transport
495 | console.error(`Connecting to transport...`);
496 | await server.connect(transport);
497 | console.error(`Brightsy MCP Server running on stdio`);
498 | console.error(`Connected to agent: ${agent_id}`);
499 | console.error(`Registered tool name: ${tool_name}`);
500 | console.error(`Agent API URL: ${agentApiBaseUrl}`);
501 | console.error(`Ready to receive requests`);
502 |
503 | // Process initial message if provided
504 | await processInitialMessage();
505 | } catch (error) {
506 | console.error("Error starting server:", error);
507 | process.exit(1);
508 | }
509 | }
510 |
511 | main().catch((error) => {
512 | console.error("Fatal error in main():", error);
513 | process.exit(1);
514 | });
```