mcp-use 1.1.5__py3-none-any.whl → 1.2.5__py3-none-any.whl

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.

Potentially problematic release.


This version of mcp-use might be problematic. Click here for more details.

@@ -5,16 +5,29 @@ This module provides the main MCPAgent class that integrates all components
5
5
  to provide a simple interface for using MCP tools with different LLMs.
6
6
  """
7
7
 
8
+ import logging
9
+
10
+ from langchain.agents import AgentExecutor, create_tool_calling_agent
11
+ from langchain.globals import set_debug
12
+ from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
8
13
  from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
9
14
  from langchain.schema.language_model import BaseLanguageModel
15
+ from langchain_core.agents import AgentAction, AgentFinish
16
+ from langchain_core.exceptions import OutputParserException
17
+ from langchain_core.tools import BaseTool
18
+ from langchain_core.utils.input import get_color_mapping
10
19
 
11
20
  from mcp_use.client import MCPClient
12
21
  from mcp_use.connectors.base import BaseConnector
13
22
  from mcp_use.session import MCPSession
14
23
 
24
+ from ..adapters.langchain_adapter import LangChainAdapter
15
25
  from ..logging import logger
16
- from .langchain_agent import LangChainAgent
17
- from .prompts.default import DEFAULT_SYSTEM_PROMPT_TEMPLATE
26
+ from .prompts.system_prompt_builder import create_system_message
27
+ from .prompts.templates import DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
28
+ from .server_manager import ServerManager
29
+
30
+ set_debug(logger.level == logging.DEBUG)
18
31
 
19
32
 
20
33
  class MCPAgent:
@@ -24,9 +37,6 @@ class MCPAgent:
24
37
  through LangChain's agent framework, with customizable system prompts and conversation memory.
25
38
  """
26
39
 
27
- # Default system prompt template to use if none is provided
28
- DEFAULT_SYSTEM_PROMPT_TEMPLATE = DEFAULT_SYSTEM_PROMPT_TEMPLATE
29
-
30
40
  def __init__(
31
41
  self,
32
42
  llm: BaseLanguageModel,
@@ -37,9 +47,11 @@ class MCPAgent:
37
47
  auto_initialize: bool = False,
38
48
  memory_enabled: bool = True,
39
49
  system_prompt: str | None = None,
40
- system_prompt_template: str | None = None,
50
+ system_prompt_template: str | None = None, # User can still override the template
41
51
  additional_instructions: str | None = None,
42
52
  disallowed_tools: list[str] | None = None,
53
+ use_server_manager: bool = False,
54
+ verbose: bool = False,
43
55
  ):
44
56
  """Initialize a new MCPAgent instance.
45
57
 
@@ -55,10 +67,11 @@ class MCPAgent:
55
67
  system_prompt_template: Template for system prompt with {tool_descriptions} placeholder.
56
68
  additional_instructions: Extra instructions to append to the system prompt.
57
69
  disallowed_tools: List of tool names that should not be available to the agent.
70
+ use_server_manager: Whether to use server manager mode instead of exposing all tools.
58
71
  """
59
72
  self.llm = llm
60
73
  self.client = client
61
- self.connectors = connectors
74
+ self.connectors = connectors or []
62
75
  self.server_name = server_name
63
76
  self.max_steps = max_steps
64
77
  self.auto_initialize = auto_initialize
@@ -66,91 +79,145 @@ class MCPAgent:
66
79
  self._initialized = False
67
80
  self._conversation_history: list[BaseMessage] = []
68
81
  self.disallowed_tools = disallowed_tools or []
69
-
82
+ self.use_server_manager = use_server_manager
83
+ self.verbose = verbose
70
84
  # System prompt configuration
71
- self.system_prompt = system_prompt
72
- self.system_prompt_template = system_prompt_template or self.DEFAULT_SYSTEM_PROMPT_TEMPLATE
85
+ self.system_prompt = system_prompt # User-provided full prompt override
86
+ # User can provide a template override, otherwise use the imported default
87
+ self.system_prompt_template_override = system_prompt_template
73
88
  self.additional_instructions = additional_instructions
74
89
 
75
90
  # Either client or connector must be provided
76
- if not client and len(connectors) == 0:
91
+ if not client and len(self.connectors) == 0:
77
92
  raise ValueError("Either client or connector must be provided")
78
93
 
79
- self._agent: LangChainAgent | None = None
80
- self._sessions: dict[str, MCPSession] | None = None
94
+ # Create the adapter for tool conversion
95
+ self.adapter = LangChainAdapter(disallowed_tools=self.disallowed_tools)
96
+
97
+ # Initialize server manager if requested
98
+ self.server_manager = None
99
+ if self.use_server_manager:
100
+ if not self.client:
101
+ raise ValueError("Client must be provided when using server manager")
102
+ self.server_manager = ServerManager(self.client, self.adapter)
103
+
104
+ # State tracking
105
+ self._agent_executor: AgentExecutor | None = None
106
+ self._sessions: dict[str, MCPSession] = {}
81
107
  self._system_message: SystemMessage | None = None
108
+ self._tools: list[BaseTool] = []
82
109
 
83
110
  async def initialize(self) -> None:
84
111
  """Initialize the MCP client and agent."""
85
- # If using client, get or create a session
86
- if self.client:
87
- # First try to get existing sessions
88
- self._sessions = self.client.get_all_active_sessions()
89
-
90
- # If no active sessions exist, create new ones
91
- if not self._sessions:
92
- self._sessions = await self.client.create_all_sessions()
93
- connectors_to_use = [session.connector for session in self._sessions.values()]
112
+ logger.info("🚀 Initializing MCP agent and connecting to services...")
113
+ # If using server manager, initialize it
114
+ if self.use_server_manager and self.server_manager:
115
+ await self.server_manager.initialize()
116
+ # Get server management tools
117
+ management_tools = await self.server_manager.get_server_management_tools()
118
+ self._tools = management_tools
119
+ logger.info(
120
+ f"🔧 Server manager mode active with {len(management_tools)} management tools"
121
+ )
122
+
123
+ # Create the system message based on available tools
124
+ await self._create_system_message_from_tools(self._tools)
94
125
  else:
95
- # Using direct connector
96
- connectors_to_use = self.connectors
97
- await [c_to_use.connect() for c_to_use in connectors_to_use]
98
- await [c_to_use.initialize() for c_to_use in connectors_to_use]
99
- # Create the system message based on available tools
100
- await self._create_system_message(connectors_to_use)
126
+ # Standard initialization - if using client, get or create sessions
127
+ if self.client:
128
+ # First try to get existing sessions
129
+ self._sessions = self.client.get_all_active_sessions()
130
+ logger.info(f"🔌 Found {len(self._sessions)} existing sessions")
131
+
132
+ # If no active sessions exist, create new ones
133
+ if not self._sessions:
134
+ logger.info("🔄 No active sessions found, creating new ones...")
135
+ self._sessions = await self.client.create_all_sessions()
136
+ logger.info(f"✅ Created {len(self._sessions)} new sessions")
137
+ connectors_to_use = [session.connector for session in self._sessions.values()]
138
+ else:
139
+ # Using direct connector - only establish connection
140
+ # LangChainAdapter will handle initialization
141
+ connectors_to_use = self.connectors
142
+ logger.info(f"🔗 Connecting to {len(connectors_to_use)} direct connectors...")
143
+ for connector in connectors_to_use:
144
+ if not hasattr(connector, "client") or connector.client is None:
145
+ await connector.connect()
146
+
147
+ tools = [tool for connector in connectors_to_use for tool in connector.tools]
148
+ logger.info(f"🧰 Found {len(tools)} tools across all connectors")
149
+
150
+ # Create the system message based on available tools
151
+ await self._create_system_message_from_tools(tools)
152
+
153
+ # Create LangChain tools using the adapter
154
+ # (adapter will handle initialization if needed)
155
+ self._tools = await self.adapter.create_langchain_tools(connectors_to_use)
156
+ logger.info(f"🛠️ Created {len(self._tools)} LangChain tools")
101
157
 
102
158
  # Create the agent
103
- self._agent = LangChainAgent(
104
- connectors=connectors_to_use,
105
- llm=self.llm,
106
- max_steps=self.max_steps,
107
- system_message=(self._system_message.content if self._system_message else None),
159
+ self._agent_executor = self._create_agent()
160
+ self._initialized = True
161
+ logger.info("✨ Agent initialization complete")
162
+
163
+ async def _create_system_message_from_tools(self, tools: list[BaseTool]) -> None:
164
+ """Create the system message based on provided tools using the builder."""
165
+ # Use the override if provided, otherwise use the imported default
166
+ default_template = self.system_prompt_template_override or DEFAULT_SYSTEM_PROMPT_TEMPLATE
167
+ # Server manager template is now also imported
168
+ server_template = SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
169
+
170
+ # Delegate creation to the imported function
171
+ self._system_message = create_system_message(
172
+ tools=tools,
173
+ system_prompt_template=default_template,
174
+ server_manager_template=server_template, # Pass the imported template
175
+ use_server_manager=self.use_server_manager,
108
176
  disallowed_tools=self.disallowed_tools,
177
+ user_provided_prompt=self.system_prompt,
178
+ additional_instructions=self.additional_instructions,
109
179
  )
110
180
 
111
- # Initialize the agent
112
- await self._agent.initialize()
113
- self._initialized = True
181
+ # Update conversation history if memory is enabled
182
+ if self.memory_enabled:
183
+ history_without_system = [
184
+ msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
185
+ ]
186
+ self._conversation_history = [self._system_message] + history_without_system
114
187
 
115
- async def _create_system_message(self, connectors: list[BaseConnector]) -> None:
116
- """Create the system message based on available tools.
188
+ def _create_agent(self) -> AgentExecutor:
189
+ """Create the LangChain agent with the configured system message.
117
190
 
118
- Args:
119
- connector: The connector with available tools.
191
+ Returns:
192
+ An initialized AgentExecutor.
120
193
  """
121
- # If a complete system prompt was provided, use it directly
122
- if self.system_prompt:
123
- self._system_message = SystemMessage(content=self.system_prompt)
124
- return
125
-
126
- # Otherwise, build the system prompt from the template and tool descriptions
127
- tool_descriptions = []
128
- for connector in connectors:
129
- tools = connector.tools
130
- # Generate tool descriptions
131
- for tool in tools:
132
- # Skip disallowed tools
133
- if tool.name in self.disallowed_tools:
134
- continue
135
-
136
- # Escape curly braces in the description by doubling them
137
- # (sometimes e.g. blender mcp they are used in the description)
138
- description = (
139
- f"- {tool.name}: {tool.description.replace('{', '{{').replace('}', '}}')}"
140
- )
141
- tool_descriptions.append(description)
142
-
143
- # Format the system prompt template with tool descriptions
144
- system_prompt = self.system_prompt_template.format(
145
- tool_descriptions="\n".join(tool_descriptions)
194
+ logger.debug(f"Creating new agent with {len(self._tools)} tools")
195
+
196
+ system_content = "You are a helpful assistant"
197
+ if self._system_message:
198
+ system_content = self._system_message.content
199
+
200
+ prompt = ChatPromptTemplate.from_messages(
201
+ [
202
+ ("system", system_content),
203
+ MessagesPlaceholder(variable_name="chat_history"),
204
+ ("human", "{input}"),
205
+ MessagesPlaceholder(variable_name="agent_scratchpad"),
206
+ ]
146
207
  )
147
208
 
148
- # Add any additional instructions
149
- if self.additional_instructions:
150
- system_prompt += f"\n\n{self.additional_instructions}"
209
+ tool_names = [tool.name for tool in self._tools]
210
+ logger.info(f"🧠 Agent ready with tools: {', '.join(tool_names)}")
211
+
212
+ # Use the standard create_tool_calling_agent
213
+ agent = create_tool_calling_agent(llm=self.llm, tools=self._tools, prompt=prompt)
151
214
 
152
- # Create the system message
153
- self._system_message = SystemMessage(content=system_prompt)
215
+ # Use the standard AgentExecutor
216
+ executor = AgentExecutor(
217
+ agent=agent, tools=self._tools, max_iterations=self.max_steps, verbose=self.verbose
218
+ )
219
+ logger.debug(f"Created agent executor with max_iterations={self.max_steps}")
220
+ return executor
154
221
 
155
222
  def get_conversation_history(self) -> list[BaseMessage]:
156
223
  """Get the current conversation history.
@@ -165,7 +232,7 @@ class MCPAgent:
165
232
  self._conversation_history = []
166
233
 
167
234
  # Re-add the system message if it exists
168
- if self._system_message:
235
+ if self._system_message and self.memory_enabled:
169
236
  self._conversation_history = [self._system_message]
170
237
 
171
238
  def add_to_history(self, message: BaseMessage) -> None:
@@ -174,7 +241,8 @@ class MCPAgent:
174
241
  Args:
175
242
  message: The message to add.
176
243
  """
177
- self._conversation_history.append(message)
244
+ if self.memory_enabled:
245
+ self._conversation_history.append(message)
178
246
 
179
247
  def get_system_message(self) -> SystemMessage | None:
180
248
  """Get the current system message.
@@ -192,9 +260,21 @@ class MCPAgent:
192
260
  """
193
261
  self._system_message = SystemMessage(content=message)
194
262
 
195
- # Update the agent if initialized
196
- if self._agent:
197
- self._agent.set_system_message(message)
263
+ # Update conversation history if memory is enabled
264
+ if self.memory_enabled:
265
+ # Remove old system message if it exists
266
+ history_without_system = [
267
+ msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
268
+ ]
269
+ self._conversation_history = history_without_system
270
+
271
+ # Add new system message
272
+ self._conversation_history.insert(0, self._system_message)
273
+
274
+ # Recreate the agent with the new system message if initialized
275
+ if self._initialized and self._tools:
276
+ self._agent_executor = self._create_agent()
277
+ logger.debug("Agent recreated with new system message")
198
278
 
199
279
  def set_disallowed_tools(self, disallowed_tools: list[str]) -> None:
200
280
  """Set the list of tools that should not be available to the agent.
@@ -205,6 +285,7 @@ class MCPAgent:
205
285
  disallowed_tools: List of tool names that should not be available.
206
286
  """
207
287
  self.disallowed_tools = disallowed_tools
288
+ self.adapter.disallowed_tools = disallowed_tools
208
289
 
209
290
  # If the agent is already initialized, we need to reinitialize it
210
291
  # to apply the changes to the available tools
@@ -230,7 +311,7 @@ class MCPAgent:
230
311
  manage_connector: bool = True,
231
312
  external_history: list[BaseMessage] | None = None,
232
313
  ) -> str:
233
- """Run a query using the MCP tools.
314
+ """Run a query using the MCP tools with unified step-by-step execution.
234
315
 
235
316
  This method handles connecting to the MCP server, initializing the agent,
236
317
  running the query, and then cleaning up the connection.
@@ -253,19 +334,28 @@ class MCPAgent:
253
334
 
254
335
  try:
255
336
  # Initialize if needed
256
- if manage_connector and (not self._initialized or not self._agent):
257
- logger.debug("Initializing agent before running query")
337
+ if manage_connector and not self._initialized:
258
338
  await self.initialize()
259
339
  initialized_here = True
260
340
  elif not self._initialized and self.auto_initialize:
261
- logger.debug("Auto-initializing agent before running query")
262
341
  await self.initialize()
263
342
  initialized_here = True
264
343
 
265
344
  # Check if initialization succeeded
266
- if not self._agent:
345
+ if not self._agent_executor:
267
346
  raise RuntimeError("MCP agent failed to initialize")
268
347
 
348
+ steps = max_steps or self.max_steps
349
+ if self._agent_executor:
350
+ self._agent_executor.max_iterations = steps
351
+
352
+ display_query = (
353
+ query[:50].replace("\n", " ") + "..."
354
+ if len(query) > 50
355
+ else query.replace("\n", " ")
356
+ )
357
+ logger.info(f"💬 Received query: '{display_query}'")
358
+
269
359
  # Add the user query to conversation history if memory is enabled
270
360
  if self.memory_enabled:
271
361
  self.add_to_history(HumanMessage(content=query))
@@ -275,74 +365,160 @@ class MCPAgent:
275
365
  external_history if external_history is not None else self._conversation_history
276
366
  )
277
367
 
278
- # Convert messages to format expected by LangChain
368
+ # Convert messages to format expected by LangChain agent input
369
+ # Exclude the main system message as it's part of the agent's prompt
279
370
  langchain_history = []
280
371
  for msg in history_to_use:
281
372
  if isinstance(msg, HumanMessage):
282
- langchain_history.append({"type": "human", "content": msg.content})
373
+ langchain_history.append(msg)
283
374
  elif isinstance(msg, AIMessage):
284
- langchain_history.append({"type": "ai", "content": msg.content})
285
- elif isinstance(msg, SystemMessage) and msg != self._system_message:
286
- # Include system messages other than the main one
287
- # which is already included in the agent's prompt
288
- langchain_history.append({"type": "system", "content": msg.content})
289
- # Other message types can be handled here if needed
290
-
291
- # Run the query with the specified max_steps or default
292
- logger.debug(f"Running query with max_steps={max_steps or self.max_steps}")
293
- result = await self._agent.run(
294
- query=query,
295
- max_steps=max_steps,
296
- chat_history=langchain_history,
375
+ langchain_history.append(msg)
376
+
377
+ intermediate_steps: list[tuple[AgentAction, str]] = []
378
+ inputs = {"input": query, "chat_history": langchain_history}
379
+
380
+ # Construct a mapping of tool name to tool for easy lookup
381
+ name_to_tool_map = {tool.name: tool for tool in self._tools}
382
+ color_mapping = get_color_mapping(
383
+ [tool.name for tool in self._tools], excluded_colors=["green", "red"]
297
384
  )
298
385
 
299
- # Add the response to conversation history if memory is enabled
386
+ logger.info(f"🏁 Starting agent execution with max_steps={steps}")
387
+
388
+ for step_num in range(steps):
389
+ # --- Check for tool updates if using server manager ---
390
+ if self.use_server_manager and self.server_manager:
391
+ current_tools = await self.server_manager.get_all_tools()
392
+ current_tool_names = {tool.name for tool in current_tools}
393
+ existing_tool_names = {tool.name for tool in self._tools}
394
+
395
+ if current_tool_names != existing_tool_names:
396
+ logger.info(
397
+ f"🔄 Tools changed before step {step_num + 1}, updating agent. "
398
+ f"New tools: {', '.join(current_tool_names)}"
399
+ )
400
+ self._tools = current_tools
401
+ # Regenerate system message with ALL current tools
402
+ await self._create_system_message_from_tools(self._tools)
403
+ # Recreate the agent executor with the new tools and system message
404
+ self._agent_executor = self._create_agent()
405
+ self._agent_executor.max_iterations = steps
406
+ # Update maps for this iteration
407
+ name_to_tool_map = {tool.name: tool for tool in self._tools}
408
+ color_mapping = get_color_mapping(
409
+ [tool.name for tool in self._tools], excluded_colors=["green", "red"]
410
+ )
411
+
412
+ logger.info(f"🔍 Step {step_num + 1}/{steps}")
413
+
414
+ # --- Plan and execute the next step ---
415
+ try:
416
+ # Use the internal _atake_next_step which handles planning and execution
417
+ # This requires providing the necessary context like maps and intermediate steps
418
+ next_step_output = await self._agent_executor._atake_next_step(
419
+ name_to_tool_map=name_to_tool_map,
420
+ color_mapping=color_mapping,
421
+ inputs=inputs,
422
+ intermediate_steps=intermediate_steps,
423
+ run_manager=None,
424
+ )
425
+
426
+ # Process the output
427
+ if isinstance(next_step_output, AgentFinish):
428
+ logger.info(f"✅ Agent finished at step {step_num + 1}")
429
+ result = next_step_output.return_values.get("output", "No output generated")
430
+ break
431
+
432
+ # If it's actions/steps, add to intermediate steps
433
+ intermediate_steps.extend(next_step_output)
434
+
435
+ # Log tool calls
436
+ for action, output in next_step_output:
437
+ tool_name = action.tool
438
+ tool_input_str = str(action.tool_input)
439
+ # Truncate long inputs for readability
440
+ if len(tool_input_str) > 100:
441
+ tool_input_str = tool_input_str[:97] + "..."
442
+ logger.info(f"🔧 Tool call: {tool_name} with input: {tool_input_str}")
443
+ # Truncate long outputs for readability
444
+ output_str = str(output)
445
+ if len(output_str) > 100:
446
+ output_str = output_str[:97] + "..."
447
+ output_str = output_str.replace("\n", " ")
448
+ logger.info(f"📄 Tool result: {output_str}")
449
+
450
+ # Check for return_direct on the last action taken
451
+ if len(next_step_output) > 0:
452
+ last_step: tuple[AgentAction, str] = next_step_output[-1]
453
+ tool_return = self._agent_executor._get_tool_return(last_step)
454
+ if tool_return is not None:
455
+ logger.info(f"🏆 Tool returned directly at step {step_num + 1}")
456
+ result = tool_return.return_values.get("output", "No output generated")
457
+ break
458
+
459
+ except OutputParserException as e:
460
+ logger.error(f"❌ Output parsing error during step {step_num + 1}: {e}")
461
+ result = f"Agent stopped due to a parsing error: {str(e)}"
462
+ break
463
+ except Exception as e:
464
+ logger.error(f"❌ Error during agent execution step {step_num + 1}: {e}")
465
+ import traceback
466
+
467
+ traceback.print_exc()
468
+ result = f"Agent stopped due to an error: {str(e)}"
469
+ break
470
+
471
+ # --- Loop finished ---
472
+ if not result:
473
+ logger.warning(f"⚠️ Agent stopped after reaching max iterations ({steps})")
474
+ result = f"Agent stopped after reaching the maximum number of steps ({steps})."
475
+
476
+ # Add the final response to conversation history if memory is enabled
300
477
  if self.memory_enabled:
301
478
  self.add_to_history(AIMessage(content=result))
302
479
 
480
+ logger.info("🎉 Agent execution complete")
303
481
  return result
304
482
 
305
483
  except Exception as e:
306
- logger.error(f"Error running query: {e}")
307
- # If we initialized in this method and there was an error,
308
- # make sure to clean up
484
+ logger.error(f"Error running query: {e}")
309
485
  if initialized_here and manage_connector:
310
- logger.debug("Cleaning up resources after initialization error")
486
+ logger.info("🧹 Cleaning up resources after initialization error in run")
311
487
  await self.close()
312
488
  raise
313
489
 
314
490
  finally:
315
- # Clean up resources if we're managing the connector and
316
- # we're not using a client that manages sessions
491
+ # Clean up if necessary (e.g., if not using client-managed sessions)
317
492
  if manage_connector and not self.client and not initialized_here:
318
- logger.debug("Closing agent after query completion")
493
+ logger.info("🧹 Closing agent after query completion")
319
494
  await self.close()
320
495
 
321
496
  async def close(self) -> None:
322
497
  """Close the MCP connection with improved error handling."""
498
+ logger.info("🔌 Closing agent and cleaning up resources...")
323
499
  try:
324
- if self._agent:
325
- # Clean up the agent first
326
- logger.debug("Cleaning up agent")
327
- self._agent = None
500
+ # Clean up the agent first
501
+ self._agent_executor = None
502
+ self._tools = []
328
503
 
329
504
  # If using client with session, close the session through client
330
505
  if self.client and self._sessions:
331
- logger.debug("Closing session through client")
506
+ logger.info("🔄 Closing sessions through client")
332
507
  await self.client.close_all_sessions()
333
- self._session = None
508
+ self._sessions = {}
334
509
  # If using direct connector, disconnect
335
510
  elif self.connectors:
336
511
  for connector in self.connectors:
337
- logger.debug("Disconnecting connector")
512
+ logger.info("🔄 Disconnecting connector")
338
513
  await connector.disconnect()
339
514
 
340
515
  self._initialized = False
341
- logger.debug("Agent closed successfully")
516
+ logger.info("👋 Agent closed successfully")
342
517
 
343
518
  except Exception as e:
344
- logger.error(f"Error during agent closure: {e}")
519
+ logger.error(f"Error during agent closure: {e}")
345
520
  # Still try to clean up references even if there was an error
346
- self._agent = None
347
- self._session = None
521
+ self._agent_executor = None
522
+ self._tools = []
523
+ self._sessions = {}
348
524
  self._initialized = False