opik-mcp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +203 -0
- package/README.md +203 -0
- package/build/cli.js +72 -0
- package/build/client/index.html +323 -0
- package/build/config.js +205 -0
- package/build/debug-log.js +64 -0
- package/build/index.js +1847 -0
- package/build/mcp-server.js +96 -0
- package/build/test-client.js +436 -0
- package/build/transports/sse-transport.js +169 -0
- package/build/transports/types.js +4 -0
- package/build/types.js +4 -0
- package/build/utils/capabilities.js +303 -0
- package/build/utils/env.js +52 -0
- package/build/utils/examples.js +414 -0
- package/build/utils/metrics-info.js +263 -0
- package/build/utils/tracing-info.js +119 -0
- package/package.json +79 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>MCP SSE Client</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
10
|
+
line-height: 1.6;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
color: #333;
|
|
14
|
+
max-width: 1200px;
|
|
15
|
+
margin: 0 auto;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
h1, h2, h3 {
|
|
19
|
+
color: #0066cc;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#messages {
|
|
23
|
+
height: 300px;
|
|
24
|
+
overflow-y: auto;
|
|
25
|
+
border: 1px solid #ddd;
|
|
26
|
+
padding: 10px;
|
|
27
|
+
margin-bottom: 20px;
|
|
28
|
+
background-color: #f9f9f9;
|
|
29
|
+
border-radius: 4px;
|
|
30
|
+
font-family: monospace;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#message-form {
|
|
34
|
+
margin-bottom: 20px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
textarea {
|
|
38
|
+
width: 100%;
|
|
39
|
+
padding: 10px;
|
|
40
|
+
border: 1px solid #ddd;
|
|
41
|
+
border-radius: 4px;
|
|
42
|
+
min-height: 100px;
|
|
43
|
+
font-family: monospace;
|
|
44
|
+
margin-bottom: 10px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
button {
|
|
48
|
+
background-color: #0066cc;
|
|
49
|
+
color: white;
|
|
50
|
+
border: none;
|
|
51
|
+
padding: 10px 20px;
|
|
52
|
+
border-radius: 4px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
font-size: 16px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
button:hover {
|
|
58
|
+
background-color: #0055aa;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.message {
|
|
62
|
+
margin-bottom: 8px;
|
|
63
|
+
border-bottom: 1px solid #eee;
|
|
64
|
+
padding-bottom: 8px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.message-time {
|
|
68
|
+
color: #666;
|
|
69
|
+
font-size: 12px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.message-direction {
|
|
73
|
+
display: inline-block;
|
|
74
|
+
padding: 2px 6px;
|
|
75
|
+
border-radius: 3px;
|
|
76
|
+
font-size: 12px;
|
|
77
|
+
margin-right: 8px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.direction-incoming {
|
|
81
|
+
background-color: #e7f3ff;
|
|
82
|
+
color: #0066cc;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.direction-outgoing {
|
|
86
|
+
background-color: #e7ffe7;
|
|
87
|
+
color: #007700;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.message-content {
|
|
91
|
+
white-space: pre-wrap;
|
|
92
|
+
background-color: #fff;
|
|
93
|
+
padding: 8px;
|
|
94
|
+
border-radius: 4px;
|
|
95
|
+
overflow-x: auto;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.connection-info {
|
|
99
|
+
margin-bottom: 20px;
|
|
100
|
+
padding: 10px;
|
|
101
|
+
background-color: #f0f0f0;
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.status {
|
|
106
|
+
display: inline-block;
|
|
107
|
+
width: 12px;
|
|
108
|
+
height: 12px;
|
|
109
|
+
border-radius: 50%;
|
|
110
|
+
margin-right: 8px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.status-connected {
|
|
114
|
+
background-color: #00cc00;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.status-disconnected {
|
|
118
|
+
background-color: #cc0000;
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
121
|
+
</head>
|
|
122
|
+
<body>
|
|
123
|
+
<h1>MCP SSE Client</h1>
|
|
124
|
+
|
|
125
|
+
<div class="connection-info">
|
|
126
|
+
<div id="connection-status">
|
|
127
|
+
<span class="status status-disconnected"></span> Disconnected
|
|
128
|
+
</div>
|
|
129
|
+
<div>
|
|
130
|
+
<label for="server-url">Server URL:</label>
|
|
131
|
+
<input type="text" id="server-url" value="http://localhost:3001" style="width: 250px;">
|
|
132
|
+
<button id="connect-btn">Connect</button>
|
|
133
|
+
<button id="disconnect-btn" disabled>Disconnect</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<h2>Send Message</h2>
|
|
138
|
+
<div id="message-form">
|
|
139
|
+
<textarea id="message-input" placeholder="Enter your JSON message here...">
|
|
140
|
+
{
|
|
141
|
+
"jsonrpc": "2.0",
|
|
142
|
+
"method": "mcp__get_server_info",
|
|
143
|
+
"id": "1",
|
|
144
|
+
"params": {}
|
|
145
|
+
}
|
|
146
|
+
</textarea>
|
|
147
|
+
<button id="send-btn" disabled>Send Message</button>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<h2>Messages</h2>
|
|
151
|
+
<div id="messages"></div>
|
|
152
|
+
|
|
153
|
+
<script>
|
|
154
|
+
let eventSource = null;
|
|
155
|
+
const messagesContainer = document.getElementById('messages');
|
|
156
|
+
const messageInput = document.getElementById('message-input');
|
|
157
|
+
const sendBtn = document.getElementById('send-btn');
|
|
158
|
+
const connectBtn = document.getElementById('connect-btn');
|
|
159
|
+
const disconnectBtn = document.getElementById('disconnect-btn');
|
|
160
|
+
const serverUrlInput = document.getElementById('server-url');
|
|
161
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
162
|
+
|
|
163
|
+
// Function to connect to the SSE server
|
|
164
|
+
function connect() {
|
|
165
|
+
const serverUrl = serverUrlInput.value.trim();
|
|
166
|
+
if (!serverUrl) {
|
|
167
|
+
alert('Please enter a valid server URL');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
// Generate a unique client ID
|
|
173
|
+
const clientId = 'client_' + Date.now();
|
|
174
|
+
|
|
175
|
+
// Create a new EventSource
|
|
176
|
+
eventSource = new EventSource(`${serverUrl}/events?clientId=${clientId}`);
|
|
177
|
+
|
|
178
|
+
// Listen for messages
|
|
179
|
+
eventSource.onmessage = (event) => {
|
|
180
|
+
const data = event.data;
|
|
181
|
+
addMessage('incoming', data);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Handle connection open
|
|
185
|
+
eventSource.onopen = () => {
|
|
186
|
+
connectionStatus.innerHTML = '<span class="status status-connected"></span> Connected';
|
|
187
|
+
connectBtn.disabled = true;
|
|
188
|
+
disconnectBtn.disabled = false;
|
|
189
|
+
sendBtn.disabled = false;
|
|
190
|
+
addMessage('system', 'Connected to server');
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Handle errors
|
|
194
|
+
eventSource.onerror = (error) => {
|
|
195
|
+
console.error('EventSource error:', error);
|
|
196
|
+
addMessage('system', 'Connection error. Reconnecting...');
|
|
197
|
+
connectionStatus.innerHTML = '<span class="status status-disconnected"></span> Error connecting';
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Error connecting to server:', error);
|
|
201
|
+
addMessage('system', `Error connecting: ${error.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Function to disconnect from the SSE server
|
|
206
|
+
function disconnect() {
|
|
207
|
+
if (eventSource) {
|
|
208
|
+
eventSource.close();
|
|
209
|
+
eventSource = null;
|
|
210
|
+
|
|
211
|
+
connectionStatus.innerHTML = '<span class="status status-disconnected"></span> Disconnected';
|
|
212
|
+
connectBtn.disabled = false;
|
|
213
|
+
disconnectBtn.disabled = true;
|
|
214
|
+
sendBtn.disabled = true;
|
|
215
|
+
|
|
216
|
+
addMessage('system', 'Disconnected from server');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Function to send a message to the server
|
|
221
|
+
async function sendMessage() {
|
|
222
|
+
const message = messageInput.value.trim();
|
|
223
|
+
if (!message) {
|
|
224
|
+
alert('Please enter a message to send');
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
// Parse message to validate JSON
|
|
230
|
+
const jsonMessage = JSON.parse(message);
|
|
231
|
+
|
|
232
|
+
// Send the message to the server
|
|
233
|
+
const serverUrl = serverUrlInput.value.trim();
|
|
234
|
+
const response = await fetch(`${serverUrl}/send`, {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: {
|
|
237
|
+
'Content-Type': 'application/json',
|
|
238
|
+
},
|
|
239
|
+
body: message,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const responseData = await response.json();
|
|
243
|
+
|
|
244
|
+
if (response.ok) {
|
|
245
|
+
addMessage('outgoing', message);
|
|
246
|
+
} else {
|
|
247
|
+
addMessage('system', `Error sending message: ${responseData.message || 'Unknown error'}`);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('Error sending message:', error);
|
|
251
|
+
addMessage('system', `Error sending message: ${error.message}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Function to add a message to the messages container
|
|
256
|
+
function addMessage(direction, content) {
|
|
257
|
+
const messageDiv = document.createElement('div');
|
|
258
|
+
messageDiv.className = 'message';
|
|
259
|
+
|
|
260
|
+
const now = new Date();
|
|
261
|
+
const time = now.toLocaleTimeString();
|
|
262
|
+
|
|
263
|
+
let directionLabel = '';
|
|
264
|
+
let directionClass = '';
|
|
265
|
+
|
|
266
|
+
switch (direction) {
|
|
267
|
+
case 'incoming':
|
|
268
|
+
directionLabel = 'Received';
|
|
269
|
+
directionClass = 'direction-incoming';
|
|
270
|
+
break;
|
|
271
|
+
case 'outgoing':
|
|
272
|
+
directionLabel = 'Sent';
|
|
273
|
+
directionClass = 'direction-outgoing';
|
|
274
|
+
break;
|
|
275
|
+
case 'system':
|
|
276
|
+
directionLabel = 'System';
|
|
277
|
+
directionClass = '';
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let contentDisplay = content;
|
|
282
|
+
|
|
283
|
+
// Try to pretty-print JSON if it's not a system message
|
|
284
|
+
if (direction !== 'system') {
|
|
285
|
+
try {
|
|
286
|
+
contentDisplay = JSON.stringify(JSON.parse(content), null, 2);
|
|
287
|
+
} catch (e) {
|
|
288
|
+
// Not valid JSON, just use as-is
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
messageDiv.innerHTML = `
|
|
293
|
+
<div>
|
|
294
|
+
<span class="message-time">${time}</span>
|
|
295
|
+
<span class="message-direction ${directionClass}">${directionLabel}</span>
|
|
296
|
+
</div>
|
|
297
|
+
<div class="message-content">${direction === 'system' ? content : contentDisplay}</div>
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
messagesContainer.appendChild(messageDiv);
|
|
301
|
+
|
|
302
|
+
// Auto-scroll to bottom
|
|
303
|
+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Set up event listeners
|
|
307
|
+
connectBtn.addEventListener('click', connect);
|
|
308
|
+
disconnectBtn.addEventListener('click', disconnect);
|
|
309
|
+
sendBtn.addEventListener('click', sendMessage);
|
|
310
|
+
|
|
311
|
+
// Allow pressing Enter in the input to send a message
|
|
312
|
+
messageInput.addEventListener('keydown', (event) => {
|
|
313
|
+
if (event.key === 'Enter' && event.ctrlKey) {
|
|
314
|
+
sendMessage();
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Add a welcome message
|
|
320
|
+
addMessage('system', 'Welcome to the MCP SSE Client. Click Connect to start.');
|
|
321
|
+
</script>
|
|
322
|
+
</body>
|
|
323
|
+
</html>
|
package/build/config.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader for Opik MCP server
|
|
3
|
+
* Loads and validates environment variables from .env file
|
|
4
|
+
* and/or command-line arguments
|
|
5
|
+
*/
|
|
6
|
+
import yargs from 'yargs';
|
|
7
|
+
import { hideBin } from 'yargs/helpers';
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
/**
|
|
10
|
+
* File-based logger
|
|
11
|
+
* Only writes if debug mode is enabled or being set to enabled
|
|
12
|
+
*/
|
|
13
|
+
function writeToLogFile(message, forceWrite = false) {
|
|
14
|
+
try {
|
|
15
|
+
// Check if debug mode is enabled or being set to enabled
|
|
16
|
+
// This check is special because we're in the process of parsing args
|
|
17
|
+
const debugArg = process.argv.includes('--debug') &&
|
|
18
|
+
process.argv[process.argv.indexOf('--debug') + 1] === 'true';
|
|
19
|
+
const debugEnv = process.env.DEBUG_MODE === 'true';
|
|
20
|
+
if (debugArg || debugEnv || forceWrite) {
|
|
21
|
+
const logFile = '/tmp/opik-mcp.log';
|
|
22
|
+
if (!fs.existsSync(logFile)) {
|
|
23
|
+
fs.writeFileSync(logFile, `Opik MCP Server Started: ${new Date().toISOString()}\n`);
|
|
24
|
+
}
|
|
25
|
+
fs.appendFileSync(logFile, `[${new Date().toISOString()}] [config] ${message}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// Silently fail if we can't write to the log file
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse command-line arguments
|
|
34
|
+
*/
|
|
35
|
+
function parseCommandLineArgs() {
|
|
36
|
+
return yargs(hideBin(process.argv))
|
|
37
|
+
// API Configuration
|
|
38
|
+
.option('apiUrl', {
|
|
39
|
+
alias: 'url',
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'API base URL',
|
|
42
|
+
})
|
|
43
|
+
.option('apiKey', {
|
|
44
|
+
alias: 'key',
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'API key for authentication',
|
|
47
|
+
})
|
|
48
|
+
.option('workspace', {
|
|
49
|
+
alias: 'ws',
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Workspace name',
|
|
52
|
+
})
|
|
53
|
+
.option('selfHosted', {
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
description: 'Whether the instance is self-hosted',
|
|
56
|
+
})
|
|
57
|
+
.option('debug', {
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
description: 'Enable debug mode',
|
|
60
|
+
})
|
|
61
|
+
// Transport Configuration
|
|
62
|
+
.option('transport', {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'Transport type (stdio or sse)',
|
|
65
|
+
choices: ['stdio', 'sse'],
|
|
66
|
+
default: 'stdio',
|
|
67
|
+
})
|
|
68
|
+
.option('ssePort', {
|
|
69
|
+
type: 'number',
|
|
70
|
+
description: 'Port for SSE transport',
|
|
71
|
+
default: 3001,
|
|
72
|
+
})
|
|
73
|
+
.option('sseHost', {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Host for SSE transport',
|
|
76
|
+
default: 'localhost',
|
|
77
|
+
})
|
|
78
|
+
.option('sseLogPath', {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Log file path for SSE transport',
|
|
81
|
+
default: '/tmp/opik-mcp-sse.log',
|
|
82
|
+
})
|
|
83
|
+
// MCP Configuration
|
|
84
|
+
.option('mcpName', {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'MCP server name',
|
|
87
|
+
})
|
|
88
|
+
.option('mcpVersion', {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'MCP server version',
|
|
91
|
+
})
|
|
92
|
+
.option('mcpPort', {
|
|
93
|
+
type: 'number',
|
|
94
|
+
description: 'MCP server port',
|
|
95
|
+
})
|
|
96
|
+
.option('mcpLogging', {
|
|
97
|
+
type: 'boolean',
|
|
98
|
+
description: 'Enable MCP server logging',
|
|
99
|
+
})
|
|
100
|
+
.option('mcpDefaultWorkspace', {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: 'Default workspace name',
|
|
103
|
+
})
|
|
104
|
+
// Tool enablement
|
|
105
|
+
.option('disablePromptTools', {
|
|
106
|
+
type: 'boolean',
|
|
107
|
+
description: 'Disable prompt-related tools',
|
|
108
|
+
})
|
|
109
|
+
.option('disableProjectTools', {
|
|
110
|
+
type: 'boolean',
|
|
111
|
+
description: 'Disable project-related tools',
|
|
112
|
+
})
|
|
113
|
+
.option('disableTraceTools', {
|
|
114
|
+
type: 'boolean',
|
|
115
|
+
description: 'Disable trace-related tools',
|
|
116
|
+
})
|
|
117
|
+
.option('disableMetricTools', {
|
|
118
|
+
type: 'boolean',
|
|
119
|
+
description: 'Disable metric-related tools',
|
|
120
|
+
})
|
|
121
|
+
.help()
|
|
122
|
+
.parse();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Load environment variables with fallbacks
|
|
126
|
+
*/
|
|
127
|
+
function loadConfig() {
|
|
128
|
+
// Parse command-line arguments first
|
|
129
|
+
const args = parseCommandLineArgs();
|
|
130
|
+
// Try to load from process.env and command-line args, with command-line taking precedence
|
|
131
|
+
const config = {
|
|
132
|
+
// API configuration with fallbacks - with much more forgiving defaults
|
|
133
|
+
apiBaseUrl: args.apiUrl || process.env.OPIK_API_BASE_URL || 'https://www.comet.com/opik/api',
|
|
134
|
+
workspaceName: (args.workspace || process.env.OPIK_WORKSPACE_NAME || 'default').replace(/^['"](.*)['"]$/, '$1'), // Remove any quotes
|
|
135
|
+
apiKey: args.apiKey || process.env.OPIK_API_KEY || '',
|
|
136
|
+
isSelfHosted: args.selfHosted !== undefined
|
|
137
|
+
? args.selfHosted
|
|
138
|
+
: process.env.OPIK_SELF_HOSTED === 'true' || false,
|
|
139
|
+
debugMode: args.debug !== undefined ? args.debug : process.env.DEBUG_MODE === 'true' || false,
|
|
140
|
+
// Transport configuration
|
|
141
|
+
transport: (args.transport || process.env.TRANSPORT || 'stdio'),
|
|
142
|
+
ssePort: args.ssePort || (process.env.SSE_PORT ? parseInt(process.env.SSE_PORT, 10) : 3001),
|
|
143
|
+
sseHost: args.sseHost || process.env.SSE_HOST || 'localhost',
|
|
144
|
+
sseLogPath: args.sseLogPath || process.env.SSE_LOG_PATH || '/tmp/opik-mcp-sse.log',
|
|
145
|
+
// MCP configuration with fallbacks
|
|
146
|
+
mcpName: args.mcpName || process.env.MCP_NAME || 'opik-manager',
|
|
147
|
+
mcpVersion: args.mcpVersion || process.env.MCP_VERSION || '1.0.0',
|
|
148
|
+
mcpPort: args.mcpPort || (process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : undefined),
|
|
149
|
+
mcpLogging: args.mcpLogging !== undefined ? args.mcpLogging : process.env.MCP_LOGGING === 'true' || false,
|
|
150
|
+
mcpDefaultWorkspace: args.mcpDefaultWorkspace || process.env.MCP_DEFAULT_WORKSPACE || 'default',
|
|
151
|
+
// Tool enablement with fallbacks - note the logic reversal for the command-line args
|
|
152
|
+
mcpEnablePromptTools: args.disablePromptTools
|
|
153
|
+
? false
|
|
154
|
+
: process.env.MCP_ENABLE_PROMPT_TOOLS !== 'false', // Enable by default
|
|
155
|
+
mcpEnableProjectTools: args.disableProjectTools
|
|
156
|
+
? false
|
|
157
|
+
: process.env.MCP_ENABLE_PROJECT_TOOLS !== 'false', // Enable by default
|
|
158
|
+
mcpEnableTraceTools: args.disableTraceTools
|
|
159
|
+
? false
|
|
160
|
+
: process.env.MCP_ENABLE_TRACE_TOOLS !== 'false', // Enable by default
|
|
161
|
+
mcpEnableMetricTools: args.disableMetricTools
|
|
162
|
+
? false
|
|
163
|
+
: process.env.MCP_ENABLE_METRIC_TOOLS !== 'false', // Enable by default
|
|
164
|
+
};
|
|
165
|
+
// Validate required fields but be much more forgiving
|
|
166
|
+
if (!config.apiKey) {
|
|
167
|
+
// Only warn about missing API key, don't throw an error
|
|
168
|
+
writeToLogFile(`Warning: No API key provided - some functionality will be limited`, true);
|
|
169
|
+
// Still allow the server to start even without an API key
|
|
170
|
+
}
|
|
171
|
+
// Log configuration if in debug mode
|
|
172
|
+
if (config.debugMode) {
|
|
173
|
+
writeToLogFile('Opik MCP Configuration:');
|
|
174
|
+
writeToLogFile(`- API Base URL: ${config.apiBaseUrl}`);
|
|
175
|
+
writeToLogFile(`- Self-hosted: ${config.isSelfHosted ? 'Yes' : 'No'}`);
|
|
176
|
+
if (!config.isSelfHosted) {
|
|
177
|
+
writeToLogFile(`- Workspace: ${config.workspaceName}`);
|
|
178
|
+
}
|
|
179
|
+
writeToLogFile(`- Debug mode: ${config.debugMode ? 'Enabled' : 'Disabled'}`);
|
|
180
|
+
// Log transport configuration
|
|
181
|
+
writeToLogFile('\nTransport Configuration:');
|
|
182
|
+
writeToLogFile(`- Transport: ${config.transport}`);
|
|
183
|
+
if (config.transport === 'sse') {
|
|
184
|
+
writeToLogFile(`- SSE Port: ${config.ssePort}`);
|
|
185
|
+
writeToLogFile(`- SSE Host: ${config.sseHost}`);
|
|
186
|
+
writeToLogFile(`- SSE Log Path: ${config.sseLogPath}`);
|
|
187
|
+
}
|
|
188
|
+
// Log MCP configuration
|
|
189
|
+
writeToLogFile('\nMCP Configuration:');
|
|
190
|
+
writeToLogFile(`- MCP Name: ${config.mcpName}`);
|
|
191
|
+
writeToLogFile(`- MCP Version: ${config.mcpVersion}`);
|
|
192
|
+
if (config.mcpPort)
|
|
193
|
+
writeToLogFile(`- MCP Port: ${config.mcpPort}`);
|
|
194
|
+
writeToLogFile(`- MCP Logging: ${config.mcpLogging ? 'Enabled' : 'Disabled'}`);
|
|
195
|
+
writeToLogFile(`- MCP Default Workspace: ${config.mcpDefaultWorkspace}`);
|
|
196
|
+
writeToLogFile(`- Prompt Tools: ${config.mcpEnablePromptTools ? 'Enabled' : 'Disabled'}`);
|
|
197
|
+
writeToLogFile(`- Project Tools: ${config.mcpEnableProjectTools ? 'Enabled' : 'Disabled'}`);
|
|
198
|
+
writeToLogFile(`- Trace Tools: ${config.mcpEnableTraceTools ? 'Enabled' : 'Disabled'}`);
|
|
199
|
+
writeToLogFile(`- Metric Tools: ${config.mcpEnableMetricTools ? 'Enabled' : 'Disabled'}`);
|
|
200
|
+
}
|
|
201
|
+
return config;
|
|
202
|
+
}
|
|
203
|
+
// Export the configuration
|
|
204
|
+
const config = loadConfig();
|
|
205
|
+
export default config;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug logging utility for MCP server
|
|
3
|
+
*/
|
|
4
|
+
// Write directly to a file for debugging
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
export function initDebugLog() {
|
|
7
|
+
const logPath = '/tmp/opik-mcp-debug.log';
|
|
8
|
+
// Clear previous log
|
|
9
|
+
try {
|
|
10
|
+
fs.writeFileSync(logPath, 'MCP DEBUG LOG STARTED\n');
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
// If we can't write to the file, use console.error
|
|
14
|
+
console.error('Failed to write to debug log file:', error);
|
|
15
|
+
}
|
|
16
|
+
// Log startup information
|
|
17
|
+
logDebug('MCP Server starting initialization');
|
|
18
|
+
logDebug(`Process ID: ${process.pid}`);
|
|
19
|
+
logDebug(`Node Version: ${process.version}`);
|
|
20
|
+
logDebug(`Working Directory: ${process.cwd()}`);
|
|
21
|
+
// Log environment variables
|
|
22
|
+
logDebug('Environment Variables:');
|
|
23
|
+
Object.keys(process.env)
|
|
24
|
+
.filter(key => key.startsWith('OPIK_'))
|
|
25
|
+
.forEach(key => {
|
|
26
|
+
let value = process.env[key];
|
|
27
|
+
// Mask sensitive information
|
|
28
|
+
if (key === 'OPIK_API_KEY')
|
|
29
|
+
value = '***MASKED***';
|
|
30
|
+
logDebug(` ${key}: ${value}`);
|
|
31
|
+
});
|
|
32
|
+
// Log command line arguments
|
|
33
|
+
logDebug('Command Line Arguments:');
|
|
34
|
+
process.argv.forEach((arg, index) => {
|
|
35
|
+
logDebug(` ${index}: ${arg}`);
|
|
36
|
+
});
|
|
37
|
+
// Set up uncaught exception handler
|
|
38
|
+
process.on('uncaughtException', error => {
|
|
39
|
+
logDebug(`UNCAUGHT EXCEPTION: ${error.message}`);
|
|
40
|
+
logDebug(error.stack || 'No stack trace available');
|
|
41
|
+
});
|
|
42
|
+
// Set up unhandled rejection handler
|
|
43
|
+
process.on('unhandledRejection', (reason, _promise) => {
|
|
44
|
+
logDebug(`UNHANDLED REJECTION: ${reason}`);
|
|
45
|
+
});
|
|
46
|
+
// Set up exit handler
|
|
47
|
+
process.on('exit', code => {
|
|
48
|
+
logDebug(`Process exiting with code: ${code}`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function logDebug(message) {
|
|
52
|
+
const logPath = '/tmp/opik-mcp-debug.log';
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
const logMessage = `[${timestamp}] ${message}\n`;
|
|
55
|
+
return new Promise(resolve => {
|
|
56
|
+
try {
|
|
57
|
+
fs.appendFileSync(logPath, logMessage);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Failed to append to debug log file:', error);
|
|
61
|
+
}
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
}
|