Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit 1a44c31

Browse files
committed
add observability to mcp agent through observability manager
1 parent 2cc3d2c commit 1a44c31

6 files changed

Lines changed: 549 additions & 4 deletions

File tree

src/agents/mcp_agent.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { BaseCallbackHandler } from '@langchain/core/callbacks/base'
12
import type { BaseLanguageModelInterface, LanguageModelLike } from '@langchain/core/language_models/base'
23
import type {
34
BaseMessage,
@@ -29,6 +30,7 @@ import { zodToJsonSchema } from 'zod-to-json-schema'
2930
import { LangChainAdapter } from '../adapters/langchain_adapter.js'
3031
import { logger } from '../logging.js'
3132
import { ServerManager } from '../managers/server_manager.js'
33+
import { ObservabilityManager } from '../observability/index.js'
3234
import { extractModelInfo, Telemetry } from '../telemetry/index.js'
3335
import { createSystemMessage } from './prompts/system_prompt_builder.js'
3436
import { DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE } from './prompts/templates.js'
@@ -61,6 +63,10 @@ export class MCPAgent {
6163
private modelProvider: string
6264
private modelName: string
6365

66+
// Observability support
67+
private observabilityManager: ObservabilityManager
68+
private callbacks: BaseCallbackHandler[] = []
69+
6470
// Remote agent support
6571
private isRemote = false
6672
private remoteAgent: RemoteAgent | null = null
@@ -81,6 +87,7 @@ export class MCPAgent {
8187
verbose?: boolean
8288
adapter?: LangChainAdapter
8389
serverManagerFactory?: (client: MCPClient) => ServerManager
90+
callbacks?: BaseCallbackHandler[]
8491
// Remote agent parameters
8592
agentId?: string
8693
apiKey?: string
@@ -107,6 +114,8 @@ export class MCPAgent {
107114
this.telemetry = Telemetry.getInstance()
108115
this.modelProvider = 'remote'
109116
this.modelName = 'remote-agent'
117+
this.observabilityManager = new ObservabilityManager({ customCallbacks: options.callbacks })
118+
this.callbacks = []
110119
return
111120
}
112121

@@ -159,6 +168,12 @@ export class MCPAgent {
159168
this.modelName = 'unknown'
160169
}
161170

171+
// Set up observability callbacks using the ObservabilityManager
172+
this.observabilityManager = new ObservabilityManager({
173+
customCallbacks: options.callbacks,
174+
verbose: this.verbose,
175+
})
176+
162177
// Make getters configurable for test mocking
163178
Object.defineProperty(this, 'agentExecutor', {
164179
get: () => this._agentExecutor,
@@ -183,6 +198,13 @@ export class MCPAgent {
183198

184199
logger.info('🚀 Initializing MCP agent and connecting to services...')
185200

201+
// Initialize observability callbacks
202+
this.callbacks = await this.observabilityManager.getCallbacks()
203+
const handlerNames = await this.observabilityManager.getHandlerNames()
204+
if (handlerNames.length > 0) {
205+
logger.info(`📊 Observability enabled with: ${handlerNames.join(', ')}`)
206+
}
207+
186208
// If using server manager, initialize it
187209
if (this.useServerManager && this.serverManager) {
188210
await this.serverManager.initialize()
@@ -202,7 +224,7 @@ export class MCPAgent {
202224
// Standard initialization - if using client, get or create sessions
203225
if (this.client) {
204226
// First try to get existing sessions
205-
this.sessions = await this.client.getAllActiveSessions()
227+
this.sessions = this.client.getAllActiveSessions()
206228
logger.info(`🔌 Found ${Object.keys(this.sessions).length} existing sessions`)
207229

208230
// If no active sessions exist, create new ones
@@ -294,6 +316,7 @@ export class MCPAgent {
294316
maxIterations: this.maxSteps,
295317
verbose: this.verbose,
296318
returnIntermediateSteps: true,
319+
callbacks: this.callbacks,
297320
})
298321
}
299322

@@ -642,7 +665,7 @@ export class MCPAgent {
642665

643666
let serverCount = 0
644667
if (this.client) {
645-
serverCount = Object.keys(await this.client.getAllActiveSessions()).length
668+
serverCount = Object.keys(this.client.getAllActiveSessions()).length
646669
}
647670
else if (this.connectors) {
648671
serverCount = this.connectors.length
@@ -690,6 +713,9 @@ export class MCPAgent {
690713
}
691714

692715
logger.info('🔌 Closing MCPAgent resources…')
716+
717+
// Shutdown observability handlers (important for serverless)
718+
await this.observabilityManager.shutdown()
693719
try {
694720
this._agentExecutor = null
695721
this._tools = []
@@ -779,7 +805,10 @@ export class MCPAgent {
779805
// Stream events from the agent executor
780806
const eventStream = agentExecutor.streamEvents(
781807
inputs,
782-
{ version: 'v2' },
808+
{
809+
version: 'v2',
810+
callbacks: this.callbacks.length > 0 ? this.callbacks : undefined,
811+
},
783812
)
784813

785814
// Yield each event
@@ -829,7 +858,7 @@ export class MCPAgent {
829858

830859
let serverCount = 0
831860
if (this.client) {
832-
serverCount = Object.keys(await this.client.getAllActiveSessions()).length
861+
serverCount = Object.keys(this.client.getAllActiveSessions()).length
833862
}
834863
else if (this.connectors) {
835864
serverCount = this.connectors.length

src/observability/README.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# Observability Module
2+
3+
This module provides comprehensive observability for MCP agents using LangChain, supporting multiple observability platforms including Langfuse and Laminar.
4+
5+
## Features
6+
7+
- **Multi-platform support**: Integrates with Langfuse and Laminar
8+
- **Automatic instrumentation**: Zero-code observability with environment variables
9+
- **Custom callbacks**: Support for custom LangChain callback handlers
10+
- **TypeScript-first**: Full TypeScript support with proper types
11+
- **Serverless-ready**: Proper shutdown handling for serverless environments
12+
13+
## Installation
14+
15+
Install the required packages based on your observability platform:
16+
17+
```bash
18+
# For Langfuse
19+
npm install langfuse langfuse-langchain
20+
21+
# For Laminar
22+
npm install @lmnr-ai/lmnr
23+
24+
# Or install both
25+
npm install langfuse langfuse-langchain @lmnr-ai/lmnr
26+
```
27+
28+
## Configuration
29+
30+
### Langfuse
31+
32+
Set the following environment variables:
33+
34+
```env
35+
LANGFUSE_PUBLIC_KEY=pk-lf-...
36+
LANGFUSE_SECRET_KEY=sk-lf-...
37+
LANGFUSE_HOST=https://cloud.langfuse.com # Optional
38+
MCP_USE_LANGFUSE=true # Set to false to disable
39+
```
40+
41+
### Laminar
42+
43+
Set the following environment variables:
44+
45+
```env
46+
LAMINAR_PROJECT_API_KEY=your_project_api_key
47+
# or
48+
LMNR_PROJECT_API_KEY=your_project_api_key
49+
MCP_USE_LAMINAR=true # Set to false to disable
50+
```
51+
52+
## Usage
53+
54+
### Basic Usage with MCPAgent
55+
56+
The observability is automatically integrated into MCPAgent:
57+
58+
```typescript
59+
import { MCPAgent } from 'mcp-use'
60+
61+
const agent = new MCPAgent({
62+
llm: myLLM,
63+
client: myMCPClient,
64+
// Observability is automatically enabled if environment variables are set
65+
})
66+
67+
// Initialize the agent
68+
await agent.initialize()
69+
70+
// Run queries - they will be automatically traced
71+
const result = await agent.run('What\'s the weather?')
72+
```
73+
74+
### Custom Callbacks
75+
76+
You can provide custom callbacks:
77+
78+
```typescript
79+
import { CallbackHandler } from 'langfuse-langchain'
80+
import { MCPAgent } from 'mcp-use'
81+
82+
const customHandler = new CallbackHandler({
83+
secretKey: 'custom-secret',
84+
publicKey: 'custom-public',
85+
})
86+
87+
const agent = new MCPAgent({
88+
llm: myLLM,
89+
client: myMCPClient,
90+
callbacks: [customHandler], // Use custom callbacks instead of auto-detected ones
91+
})
92+
```
93+
94+
### Direct ObservabilityManager Usage
95+
96+
For advanced use cases, you can use the ObservabilityManager directly:
97+
98+
```typescript
99+
import { ObservabilityManager } from 'mcp-use/observability'
100+
101+
// Create a manager
102+
const manager = new ObservabilityManager({
103+
verbose: true, // Enable verbose logging
104+
})
105+
106+
// Get available callbacks
107+
const callbacks = await manager.getCallbacks()
108+
109+
// Check available handlers
110+
const handlerNames = await manager.getHandlerNames()
111+
console.log('Available handlers:', handlerNames)
112+
113+
// Add custom callback
114+
manager.addCallback(myCustomCallback)
115+
116+
// Shutdown (important for serverless)
117+
await manager.shutdown()
118+
```
119+
120+
## Platform Features
121+
122+
### Langfuse Features
123+
124+
- Detailed LLM call tracing
125+
- Chain execution tracking
126+
- Tool usage monitoring
127+
- Cost tracking
128+
- Custom metadata and tags
129+
- Session management
130+
- User tracking
131+
132+
### Laminar Features
133+
134+
- Automatic OpenTelemetry instrumentation
135+
- Zero-code tracing
136+
- Real-time monitoring
137+
- Custom spans with `@observe`
138+
- Evaluation framework
139+
- Session grouping
140+
141+
## Serverless Considerations
142+
143+
For serverless environments (AWS Lambda, Vercel, etc.), ensure proper shutdown:
144+
145+
```typescript
146+
const agent = new MCPAgent({ /* ... */ })
147+
148+
try {
149+
await agent.initialize()
150+
const result = await agent.run(query)
151+
return result
152+
}
153+
finally {
154+
// Important: Ensure traces are flushed
155+
await agent.close()
156+
}
157+
```
158+
159+
## Debugging
160+
161+
Enable debug logging to see observability events:
162+
163+
```typescript
164+
import { logger } from 'mcp-use/logging'
165+
166+
// Set log level to debug
167+
process.env.LOG_LEVEL = 'debug'
168+
169+
// Now you'll see detailed observability logs
170+
```
171+
172+
## Environment Variables Reference
173+
174+
### Langfuse
175+
176+
- `LANGFUSE_PUBLIC_KEY` - Required: Your Langfuse public key
177+
- `LANGFUSE_SECRET_KEY` - Required: Your Langfuse secret key
178+
- `LANGFUSE_HOST` / `LANGFUSE_BASEURL` - Optional: Langfuse API URL (default: https://cloud.langfuse.com)
179+
- `LANGFUSE_RELEASE` - Optional: Release/version identifier
180+
- `LANGFUSE_FLUSH_AT` - Optional: Batch size for flushing (default: 15)
181+
- `LANGFUSE_FLUSH_INTERVAL` - Optional: Flush interval in ms (default: 10000)
182+
- `LANGFUSE_REQUEST_TIMEOUT` - Optional: Request timeout in ms (default: 10000)
183+
- `LANGFUSE_ENABLED` - Optional: Set to "false" to disable
184+
- `MCP_USE_LANGFUSE` - Optional: Set to "false" to disable Langfuse integration
185+
186+
### Laminar
187+
188+
- `LAMINAR_PROJECT_API_KEY` / `LMNR_PROJECT_API_KEY` - Required: Your Laminar project API key
189+
- `LAMINAR_BASE_URL` / `LMNR_BASE_URL` - Optional: Laminar API URL
190+
- `MCP_USE_LAMINAR` - Optional: Set to "false" to disable Laminar integration
191+
192+
## Examples
193+
194+
See the [examples](../../examples/) directory for complete working examples:
195+
196+
- Basic observability setup
197+
- Multi-platform configuration
198+
- Custom callback handlers
199+
- Serverless deployments

src/observability/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Observability module for MCP-use.
3+
*
4+
* This module provides centralized observability management for LangChain agents,
5+
* supporting multiple platforms like Langfuse and Laminar.
6+
*/
7+
8+
// Import observability providers - order matters for initialization
9+
import './laminar.js'
10+
import './langfuse.js'
11+
12+
// Re-export individual handlers for direct usage if needed
13+
export {
14+
getLaminarHandler,
15+
getLaminarInitPromise,
16+
isLaminarInitialized,
17+
} from './laminar.js'
18+
export {
19+
langfuseClient,
20+
langfuseHandler,
21+
langfuseInitPromise,
22+
} from './langfuse.js'
23+
24+
// Export the manager and its utilities
25+
export {
26+
createManager,
27+
getDefaultManager,
28+
type ObservabilityConfig,
29+
ObservabilityManager,
30+
} from './manager.js'

0 commit comments

Comments
 (0)