Skip to content

Commit 6d54fb1

Browse files
committed
Telemetry
1 parent 87ba143 commit 6d54fb1

File tree

12 files changed

+560
-0
lines changed

12 files changed

+560
-0
lines changed

index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ export { BaseAdapter, LangChainAdapter } from './src/adapters/index.js'
1313
export { ServerManager } from './src/managers/server_manager.js'
1414
export * from './src/managers/tools/index.js'
1515

16+
// Export telemetry utilities
17+
export { setTelemetrySource, Telemetry } from './src/telemetry/index.js'
18+
1619
export { BaseConnector, HttpConnector, loadConfigFile, Logger, logger, MCPAgent, MCPClient, MCPSession, StdioConnector, WebSocketConnector }

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@
7575
"@langchain/core": "0.3.58",
7676
"@langchain/openai": "^0.5.15",
7777
"@modelcontextprotocol/sdk": "1.12.1",
78+
"@scarf/scarf": "^1.4.0",
7879
"dotenv": "^16.5.0",
7980
"eventsource": "^3.0.6",
8081
"fastembed": "^1.14.4",
8182
"langchain": "^0.3.27",
8283
"lodash-es": "^4.17.21",
84+
"posthog-node": "^5.1.1",
8385
"uuid": "^11.1.0",
8486
"winston": "^3.17.0",
8587
"ws": "^8.18.2",

src/agents/mcp_agent.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { LangChainAdapter } from '../adapters/langchain_adapter.js'
2525
import { logger } from '../logging.js'
2626
import { ServerManager } from '../managers/server_manager.js'
27+
import { extractModelInfo, Telemetry } from '../telemetry/index.js'
2728
import { createSystemMessage } from './prompts/system_prompt_builder.js'
2829
import { DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE } from './prompts/templates.js'
2930

@@ -50,6 +51,9 @@ export class MCPAgent {
5051
private tools: StructuredToolInterface[] = []
5152
private adapter: LangChainAdapter
5253
private serverManager: ServerManager | null = null
54+
private telemetry: Telemetry
55+
private modelProvider: string
56+
private modelName: string
5357

5458
constructor(options: {
5559
llm: BaseLanguageModelInterface
@@ -98,6 +102,13 @@ export class MCPAgent {
98102
else {
99103
this.adapter = options.adapter ?? new LangChainAdapter(this.disallowedTools)
100104
}
105+
106+
// Initialize telemetry
107+
this.telemetry = Telemetry.getInstance()
108+
// Track model info for telemetry
109+
const [provider, name] = extractModelInfo(this.llm as any)
110+
this.modelProvider = provider
111+
this.modelName = name
101112
}
102113

103114
public async initialize(): Promise<void> {
@@ -298,6 +309,10 @@ export class MCPAgent {
298309
): AsyncGenerator<AgentStep, string, void> {
299310
let result = ''
300311
let initializedHere = false
312+
const startTime = Date.now()
313+
const toolsUsedNames: string[] = []
314+
let stepsTaken = 0
315+
let success = false
301316

302317
try {
303318
if (manageConnector && !this.initialized) {
@@ -340,6 +355,7 @@ export class MCPAgent {
340355
logger.info(`🏁 Starting agent execution with max_steps=${steps}`)
341356

342357
for (let stepNum = 0; stepNum < steps; stepNum++) {
358+
stepsTaken = stepNum + 1
343359
if (this.useServerManager && this.serverManager) {
344360
const currentTools = this.serverManager.tools
345361
const currentToolNames = new Set(currentTools.map(t => t.name))
@@ -385,6 +401,7 @@ export class MCPAgent {
385401
yield step
386402
const { action, observation } = step
387403
const toolName = action.tool
404+
toolsUsedNames.push(toolName)
388405
let toolInputStr = String(action.toolInput)
389406
if (toolInputStr.length > 100)
390407
toolInputStr = `${toolInputStr.slice(0, 97)}...`
@@ -432,6 +449,7 @@ export class MCPAgent {
432449
}
433450

434451
logger.info('🎉 Agent execution complete')
452+
success = true
435453
return result
436454
}
437455
catch (e) {
@@ -443,6 +461,44 @@ export class MCPAgent {
443461
throw e
444462
}
445463
finally {
464+
// Track comprehensive execution data
465+
const executionTimeMs = Date.now() - startTime
466+
467+
let serverCount = 0
468+
if (this.client) {
469+
serverCount = Object.keys(await this.client.getAllActiveSessions()).length
470+
}
471+
else if (this.connectors) {
472+
serverCount = this.connectors.length
473+
}
474+
475+
const conversationHistoryLength = this.memoryEnabled ? this.conversationHistory.length : 0
476+
477+
await this.telemetry.trackAgentExecution({
478+
executionMethod: 'stream',
479+
query,
480+
success,
481+
modelProvider: this.modelProvider,
482+
modelName: this.modelName,
483+
serverCount,
484+
serverIdentifiers: this.connectors.map(connector => connector.publicIdentifier),
485+
totalToolsAvailable: this.tools.length,
486+
toolsAvailableNames: this.tools.map(t => t.name),
487+
maxStepsConfigured: this.maxSteps,
488+
memoryEnabled: this.memoryEnabled,
489+
useServerManager: this.useServerManager,
490+
maxStepsUsed: maxSteps ?? null,
491+
manageConnector,
492+
externalHistoryUsed: externalHistory !== undefined,
493+
stepsTaken,
494+
toolsUsedCount: toolsUsedNames.length,
495+
toolsUsedNames,
496+
response: result,
497+
executionTimeMs,
498+
errorType: success ? null : 'execution_error',
499+
conversationHistoryLength,
500+
})
501+
446502
if (manageConnector && !this.client && initializedHere) {
447503
logger.info('🧹 Closing agent after query completion')
448504
await this.close()

src/connectors/base.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export abstract class BaseConnector {
3333
/** Establish the connection and create the SDK client. */
3434
abstract connect(): Promise<void>
3535

36+
/** Get the identifier for the connector. */
37+
abstract get publicIdentifier(): Record<string, string>
38+
3639
/** Disconnect and release resources. */
3740
async disconnect(): Promise<void> {
3841
if (!this.connected) {

src/connectors/http.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,11 @@ export class HttpConnector extends BaseConnector {
7070
throw err
7171
}
7272
}
73+
74+
get publicIdentifier(): Record<string, string> {
75+
return {
76+
type: 'http',
77+
url: this.baseUrl,
78+
}
79+
}
7380
}

src/connectors/stdio.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,11 @@ export class StdioConnector extends BaseConnector {
9191
throw err
9292
}
9393
}
94+
95+
get publicIdentifier(): Record<string, string> {
96+
return {
97+
'type': 'stdio',
98+
'command&args': `${this.command} ${this.args.join(' ')}`,
99+
}
100+
}
94101
}

src/connectors/websocket.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,11 @@ export class WebSocketConnector extends BaseConnector {
183183

184184
this.toolsCache = null
185185
}
186+
187+
get publicIdentifier(): Record<string, string> {
188+
return {
189+
type: 'websocket',
190+
url: this.url,
191+
}
192+
}
186193
}

src/telemetry/events.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
export abstract class BaseTelemetryEvent {
2+
abstract get name(): string
3+
abstract get properties(): Record<string, any>
4+
}
5+
6+
export interface MCPAgentExecutionEventData {
7+
// Execution method and context
8+
executionMethod: string // "run" or "astream"
9+
query: string // The actual user query
10+
success: boolean
11+
12+
// Agent configuration
13+
modelProvider: string
14+
modelName: string
15+
serverCount: number
16+
serverIdentifiers: Array<Record<string, string>>
17+
totalToolsAvailable: number
18+
toolsAvailableNames: string[]
19+
maxStepsConfigured: number
20+
memoryEnabled: boolean
21+
useServerManager: boolean
22+
23+
// Execution PARAMETERS
24+
maxStepsUsed: number | null
25+
manageConnector: boolean
26+
externalHistoryUsed: boolean
27+
28+
// Execution results
29+
stepsTaken?: number | null
30+
toolsUsedCount?: number | null
31+
toolsUsedNames?: string[] | null
32+
response?: string | null // The actual response
33+
executionTimeMs?: number | null
34+
errorType?: string | null
35+
36+
// Context
37+
conversationHistoryLength?: number | null
38+
}
39+
40+
export class MCPAgentExecutionEvent extends BaseTelemetryEvent {
41+
constructor(private data: MCPAgentExecutionEventData) {
42+
super()
43+
}
44+
45+
get name(): string {
46+
return 'mcp_agent_execution'
47+
}
48+
49+
get properties(): Record<string, any> {
50+
return {
51+
// Core execution info
52+
execution_method: this.data.executionMethod,
53+
query: this.data.query,
54+
query_length: this.data.query.length,
55+
success: this.data.success,
56+
// Agent configuration
57+
model_provider: this.data.modelProvider,
58+
model_name: this.data.modelName,
59+
server_count: this.data.serverCount,
60+
server_identifiers: this.data.serverIdentifiers,
61+
total_tools_available: this.data.totalToolsAvailable,
62+
tools_available_names: this.data.toolsAvailableNames,
63+
max_steps_configured: this.data.maxStepsConfigured,
64+
memory_enabled: this.data.memoryEnabled,
65+
use_server_manager: this.data.useServerManager,
66+
// Execution parameters (always include, even if null)
67+
max_steps_used: this.data.maxStepsUsed,
68+
manage_connector: this.data.manageConnector,
69+
external_history_used: this.data.externalHistoryUsed,
70+
// Execution results (always include, even if null)
71+
steps_taken: this.data.stepsTaken ?? null,
72+
tools_used_count: this.data.toolsUsedCount ?? null,
73+
tools_used_names: this.data.toolsUsedNames ?? null,
74+
response: this.data.response ?? null,
75+
response_length: this.data.response ? this.data.response.length : null,
76+
execution_time_ms: this.data.executionTimeMs ?? null,
77+
error_type: this.data.errorType ?? null,
78+
conversation_history_length: this.data.conversationHistoryLength ?? null,
79+
}
80+
}
81+
}

src/telemetry/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Telemetry } from './telemetry.js'
2+
3+
export { BaseTelemetryEvent, MCPAgentExecutionEvent, MCPAgentExecutionEventData } from './events.js'
4+
export { Telemetry } from './telemetry.js'
5+
export { extractModelInfo, getModelName, getModelProvider, getPackageVersion } from './utils.js'
6+
7+
// Convenience function to set telemetry source globally
8+
export function setTelemetrySource(source: string): void {
9+
Telemetry.getInstance().setSource(source)
10+
}

0 commit comments

Comments
 (0)