@corva/ui 3.54.0-21 → 3.54.0-22
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/mcp-server/server.mjs +113 -9
- package/package.json +1 -1
package/mcp-server/server.mjs
CHANGED
|
@@ -11,9 +11,10 @@ import { randomUUID } from 'crypto';
|
|
|
11
11
|
import { Resource } from '@opentelemetry/resources';
|
|
12
12
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
13
13
|
import { readFile } from 'fs/promises';
|
|
14
|
-
import { join as join$1, dirname } from 'path';
|
|
14
|
+
import { join as join$1, dirname, resolve } from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { z } from 'zod';
|
|
17
|
+
import { readFileSync } from 'fs';
|
|
17
18
|
|
|
18
19
|
const LOG_SYMBOLS = {
|
|
19
20
|
success: '✓',
|
|
@@ -227,7 +228,7 @@ const validateDsn = (dsn) => {
|
|
|
227
228
|
|
|
228
229
|
const MCP_SERVER_VERSION = '1.1.0';
|
|
229
230
|
|
|
230
|
-
var version = "3.54.0-
|
|
231
|
+
var version = "3.54.0-22";
|
|
231
232
|
|
|
232
233
|
const CORVA_UI_VERSION = version;
|
|
233
234
|
|
|
@@ -268,16 +269,24 @@ const createMetricsClient = (meter) => {
|
|
|
268
269
|
recordToolEmptyResult: toolName => {
|
|
269
270
|
emptyResultCounter.add(1, { 'gen_ai.tool.name': toolName });
|
|
270
271
|
},
|
|
271
|
-
recordSession: (clientName, clientVersion) => {
|
|
272
|
+
recordSession: (clientName, clientVersion, projectIdentity) => {
|
|
272
273
|
sessionCounter.add(1, {
|
|
273
274
|
'mcp.client.name': clientName || UNKNOWN_CLIENT_ATTR,
|
|
274
275
|
'mcp.client.version': clientVersion || UNKNOWN_CLIENT_ATTR,
|
|
276
|
+
...(projectIdentity?.appKey ? { 'corva.app.key': projectIdentity.appKey } : {}),
|
|
277
|
+
...(projectIdentity?.packageName
|
|
278
|
+
? { 'corva.package.name': projectIdentity.packageName }
|
|
279
|
+
: {}),
|
|
275
280
|
});
|
|
276
281
|
},
|
|
277
|
-
recordSessionEnd: (durationMs, toolCount, clientName, clientVersion) => {
|
|
282
|
+
recordSessionEnd: (durationMs, toolCount, clientName, clientVersion, projectIdentity) => {
|
|
278
283
|
const attrs = {
|
|
279
284
|
'mcp.client.name': clientName || UNKNOWN_CLIENT_ATTR,
|
|
280
285
|
'mcp.client.version': clientVersion || UNKNOWN_CLIENT_ATTR,
|
|
286
|
+
...(projectIdentity?.appKey ? { 'corva.app.key': projectIdentity.appKey } : {}),
|
|
287
|
+
...(projectIdentity?.packageName
|
|
288
|
+
? { 'corva.package.name': projectIdentity.packageName }
|
|
289
|
+
: {}),
|
|
281
290
|
};
|
|
282
291
|
sessionDuration.record(durationMs, attrs);
|
|
283
292
|
sessionToolCount.record(toolCount, attrs);
|
|
@@ -375,6 +384,12 @@ const recordInitSpan = (tracer, attrs) => {
|
|
|
375
384
|
if (attrs.networkTransport) {
|
|
376
385
|
span.setAttribute('network.transport', attrs.networkTransport);
|
|
377
386
|
}
|
|
387
|
+
if (attrs.appKey) {
|
|
388
|
+
span.setAttribute('corva.app.key', attrs.appKey);
|
|
389
|
+
}
|
|
390
|
+
if (attrs.packageName) {
|
|
391
|
+
span.setAttribute('corva.package.name', attrs.packageName);
|
|
392
|
+
}
|
|
378
393
|
}
|
|
379
394
|
finally {
|
|
380
395
|
span.end();
|
|
@@ -94844,7 +94859,7 @@ const formatBytes = (bytes) => {
|
|
|
94844
94859
|
return `${mb.toFixed(1)} MB`;
|
|
94845
94860
|
};
|
|
94846
94861
|
const formatConfigSource = (source) => source === 'local' ? 'Local file' : 'Remote endpoint';
|
|
94847
|
-
const handleGetDiagnostics = (stats, telemetry) => {
|
|
94862
|
+
const handleGetDiagnostics = (stats, telemetry, identity) => {
|
|
94848
94863
|
const { heapUsed, heapTotal, rss } = process.memoryUsage();
|
|
94849
94864
|
const uptimeMs = Date.now() - stats.startTime;
|
|
94850
94865
|
let text = h1('MCP Server Diagnostics');
|
|
@@ -94877,11 +94892,82 @@ const handleGetDiagnostics = (stats, telemetry) => {
|
|
|
94877
94892
|
else {
|
|
94878
94893
|
text += `${bold('Status')}: Disabled\n`;
|
|
94879
94894
|
}
|
|
94895
|
+
text += h2('Project Identity');
|
|
94896
|
+
text += `${bold('App key')}: ${identity?.appKey ?? 'N/A'}\n`;
|
|
94897
|
+
text += `${bold('Package name')}: ${identity?.packageName ?? 'N/A'}\n`;
|
|
94880
94898
|
return {
|
|
94881
94899
|
response: createToolResponse(text),
|
|
94882
94900
|
};
|
|
94883
94901
|
};
|
|
94884
94902
|
|
|
94903
|
+
const readJsonField = (filePath, ...keys) => {
|
|
94904
|
+
try {
|
|
94905
|
+
let value = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
94906
|
+
for (const key of keys) {
|
|
94907
|
+
if (value && typeof value === 'object') {
|
|
94908
|
+
value = value[key];
|
|
94909
|
+
}
|
|
94910
|
+
else {
|
|
94911
|
+
return null;
|
|
94912
|
+
}
|
|
94913
|
+
}
|
|
94914
|
+
return typeof value === 'string' && value ? value : null;
|
|
94915
|
+
}
|
|
94916
|
+
catch {
|
|
94917
|
+
return null;
|
|
94918
|
+
}
|
|
94919
|
+
};
|
|
94920
|
+
const findUpJsonField = (startDir, fileName, ...keys) => {
|
|
94921
|
+
let dir = resolve(startDir);
|
|
94922
|
+
for (;;) {
|
|
94923
|
+
const value = readJsonField(join$1(dir, fileName), ...keys);
|
|
94924
|
+
if (value) {
|
|
94925
|
+
return value;
|
|
94926
|
+
}
|
|
94927
|
+
const parent = dirname(dir);
|
|
94928
|
+
if (parent === dir) {
|
|
94929
|
+
return null;
|
|
94930
|
+
}
|
|
94931
|
+
dir = parent;
|
|
94932
|
+
}
|
|
94933
|
+
};
|
|
94934
|
+
const getProjectIdentityAttrs = (identity) => {
|
|
94935
|
+
const attrs = {};
|
|
94936
|
+
if (identity?.appKey) {
|
|
94937
|
+
attrs['corva.app.key'] = identity.appKey;
|
|
94938
|
+
}
|
|
94939
|
+
if (identity?.packageName) {
|
|
94940
|
+
attrs['corva.package.name'] = identity.packageName;
|
|
94941
|
+
}
|
|
94942
|
+
return attrs;
|
|
94943
|
+
};
|
|
94944
|
+
const resolveProjectIdentity = async (server, logger) => {
|
|
94945
|
+
const empty = { appKey: null, packageName: null };
|
|
94946
|
+
try {
|
|
94947
|
+
const capabilities = server.getClientCapabilities();
|
|
94948
|
+
if (!capabilities?.roots) {
|
|
94949
|
+
return empty;
|
|
94950
|
+
}
|
|
94951
|
+
const { roots } = await server.listRoots();
|
|
94952
|
+
if (!roots.length) {
|
|
94953
|
+
return empty;
|
|
94954
|
+
}
|
|
94955
|
+
const rootPath = fileURLToPath(roots[0].uri);
|
|
94956
|
+
const appKey = findUpJsonField(rootPath, 'manifest.json', 'application', 'key');
|
|
94957
|
+
const packageName = findUpJsonField(rootPath, 'package.json', 'name');
|
|
94958
|
+
if (appKey) {
|
|
94959
|
+
logger.info(`Consuming app: ${appKey}`);
|
|
94960
|
+
}
|
|
94961
|
+
if (packageName) {
|
|
94962
|
+
logger.info(`Consuming package: ${packageName}`);
|
|
94963
|
+
}
|
|
94964
|
+
return { appKey, packageName };
|
|
94965
|
+
}
|
|
94966
|
+
catch {
|
|
94967
|
+
return empty;
|
|
94968
|
+
}
|
|
94969
|
+
};
|
|
94970
|
+
|
|
94885
94971
|
class CorvaUiMcpServer {
|
|
94886
94972
|
server;
|
|
94887
94973
|
mcpLogger;
|
|
@@ -94894,6 +94980,8 @@ class CorvaUiMcpServer {
|
|
|
94894
94980
|
telemetryInitPromise = null;
|
|
94895
94981
|
shutdownRequested = false;
|
|
94896
94982
|
pendingInitData = null;
|
|
94983
|
+
projectIdentity = null;
|
|
94984
|
+
identityPromise = null;
|
|
94897
94985
|
constructor() {
|
|
94898
94986
|
const baseLogger = createLogger({ prefix: '[CorvaUI MCP]' });
|
|
94899
94987
|
this.mcpLogger = createMcpNotificationLogger(baseLogger, () => this.isConnected ? this.server : null);
|
|
@@ -94926,7 +95014,16 @@ class CorvaUiMcpServer {
|
|
|
94926
95014
|
if (clientInfo?.name) {
|
|
94927
95015
|
this.mcpLogger.info(`Client connected: ${clientInfo.name}${clientInfo.version ? ` v${clientInfo.version}` : ''}`);
|
|
94928
95016
|
}
|
|
94929
|
-
this.
|
|
95017
|
+
this.identityPromise = resolveProjectIdentity(this.server.server, this.mcpLogger)
|
|
95018
|
+
.then(identity => {
|
|
95019
|
+
this.projectIdentity = identity;
|
|
95020
|
+
})
|
|
95021
|
+
.catch(error => {
|
|
95022
|
+
this.mcpLogger.error('Failed to resolve project identity', error);
|
|
95023
|
+
})
|
|
95024
|
+
.finally(() => {
|
|
95025
|
+
this.flushPendingInitData();
|
|
95026
|
+
});
|
|
94930
95027
|
};
|
|
94931
95028
|
}
|
|
94932
95029
|
flushPendingInitData() {
|
|
@@ -94979,6 +95076,7 @@ class CorvaUiMcpServer {
|
|
|
94979
95076
|
extraAttrs: {
|
|
94980
95077
|
...serializedResult.extraAttrs,
|
|
94981
95078
|
...getSpanAttributes?.(result),
|
|
95079
|
+
...getProjectIdentityAttrs(this.projectIdentity),
|
|
94982
95080
|
},
|
|
94983
95081
|
});
|
|
94984
95082
|
this.telemetry.metrics.recordToolInvocation(toolName, ms);
|
|
@@ -95010,7 +95108,10 @@ class CorvaUiMcpServer {
|
|
|
95010
95108
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
95011
95109
|
networkTransport: MCP_NETWORK_TRANSPORT,
|
|
95012
95110
|
parentContext,
|
|
95013
|
-
extraAttrs:
|
|
95111
|
+
extraAttrs: {
|
|
95112
|
+
...serializedError.extraAttrs,
|
|
95113
|
+
...getProjectIdentityAttrs(this.projectIdentity),
|
|
95114
|
+
},
|
|
95014
95115
|
});
|
|
95015
95116
|
this.telemetry.metrics.recordToolInvocation(toolName, ms);
|
|
95016
95117
|
this.telemetry.metrics.recordToolError(toolName, serializedError.errorType);
|
|
@@ -95150,7 +95251,7 @@ class CorvaUiMcpServer {
|
|
|
95150
95251
|
}, (extra) => {
|
|
95151
95252
|
const parentContext = extractContextFromMeta(extra?._meta);
|
|
95152
95253
|
try {
|
|
95153
|
-
return this.executeToolWithObservability(diagnosticsToolName, {}, () => handleGetDiagnostics(this.getStats(), this.getTelemetryStatus()), () => 'ok', { parentContext });
|
|
95254
|
+
return this.executeToolWithObservability(diagnosticsToolName, {}, () => handleGetDiagnostics(this.getStats(), this.getTelemetryStatus(), this.projectIdentity), () => 'ok', { parentContext });
|
|
95154
95255
|
}
|
|
95155
95256
|
catch (error) {
|
|
95156
95257
|
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -95185,10 +95286,13 @@ class CorvaUiMcpServer {
|
|
|
95185
95286
|
if (this.telemetryInitPromise) {
|
|
95186
95287
|
await withTimeout(this.telemetryInitPromise, TELEMETRY_INIT_TIMEOUT_MS);
|
|
95187
95288
|
}
|
|
95289
|
+
if (this.identityPromise) {
|
|
95290
|
+
await withTimeout(this.identityPromise, TELEMETRY_INIT_TIMEOUT_MS);
|
|
95291
|
+
}
|
|
95188
95292
|
if (this.telemetry.isEnabled()) {
|
|
95189
95293
|
const sessionDurationMs = Date.now() - this.startTime;
|
|
95190
95294
|
const clientInfo = this.server.server.getClientVersion();
|
|
95191
|
-
this.telemetry.metrics.recordSessionEnd(sessionDurationMs, this.requestCount, clientInfo?.name, clientInfo?.version);
|
|
95295
|
+
this.telemetry.metrics.recordSessionEnd(sessionDurationMs, this.requestCount, clientInfo?.name, clientInfo?.version, this.projectIdentity);
|
|
95192
95296
|
}
|
|
95193
95297
|
if (this.transport) {
|
|
95194
95298
|
await this.server.close();
|