mcp-use 1.3.9__py3-none-any.whl → 1.3.11__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.
- mcp_use/__init__.py +6 -2
- mcp_use/adapters/langchain_adapter.py +7 -5
- mcp_use/agents/mcpagent.py +115 -19
- mcp_use/agents/prompts/templates.py +1 -10
- mcp_use/agents/remote.py +50 -19
- mcp_use/auth/__init__.py +6 -0
- mcp_use/auth/bearer.py +17 -0
- mcp_use/auth/oauth.py +625 -0
- mcp_use/auth/oauth_callback.py +214 -0
- mcp_use/cli.py +581 -0
- mcp_use/client.py +1 -1
- mcp_use/config.py +2 -2
- mcp_use/connectors/base.py +17 -12
- mcp_use/connectors/http.py +117 -21
- mcp_use/connectors/websocket.py +14 -5
- mcp_use/exceptions.py +31 -0
- mcp_use/logging.py +27 -12
- mcp_use/managers/base.py +36 -0
- mcp_use/managers/server_manager.py +2 -1
- mcp_use/observability/__init__.py +2 -1
- mcp_use/observability/callbacks_manager.py +162 -0
- mcp_use/observability/laminar.py +24 -3
- mcp_use/observability/langfuse.py +27 -3
- mcp_use/task_managers/base.py +13 -23
- mcp_use/task_managers/sse.py +5 -0
- mcp_use/task_managers/streamable_http.py +5 -0
- {mcp_use-1.3.9.dist-info → mcp_use-1.3.11.dist-info}/METADATA +22 -26
- mcp_use-1.3.11.dist-info/RECORD +60 -0
- mcp_use-1.3.11.dist-info/entry_points.txt +2 -0
- mcp_use-1.3.9.dist-info/RECORD +0 -51
- {mcp_use-1.3.9.dist-info → mcp_use-1.3.11.dist-info}/WHEEL +0 -0
- {mcp_use-1.3.9.dist-info → mcp_use-1.3.11.dist-info}/licenses/LICENSE +0 -0
mcp_use/__init__.py
CHANGED
|
@@ -7,12 +7,16 @@ to MCP tools through existing LangChain adapters.
|
|
|
7
7
|
|
|
8
8
|
from importlib.metadata import version
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# Import logging FIRST to ensure it's configured before other modules
|
|
11
|
+
# This MUST happen before importing observability to ensure loggers are configured
|
|
12
|
+
from .logging import MCP_USE_DEBUG, Logger, logger # isort: skip
|
|
13
|
+
|
|
14
|
+
# Now import other modules - observability must come after logging
|
|
15
|
+
from . import observability # noqa: E402
|
|
11
16
|
from .agents.mcpagent import MCPAgent
|
|
12
17
|
from .client import MCPClient
|
|
13
18
|
from .config import load_config_file
|
|
14
19
|
from .connectors import BaseConnector, HttpConnector, StdioConnector, WebSocketConnector
|
|
15
|
-
from .logging import MCP_USE_DEBUG, Logger, logger
|
|
16
20
|
from .session import MCPSession
|
|
17
21
|
|
|
18
22
|
__version__ = version("mcp-use")
|
|
@@ -39,7 +39,7 @@ class LangChainAdapter(BaseAdapter):
|
|
|
39
39
|
self._connector_tool_map: dict[BaseConnector, list[BaseTool]] = {}
|
|
40
40
|
|
|
41
41
|
def fix_schema(self, schema: dict) -> dict:
|
|
42
|
-
"""Convert JSON Schema 'type': ['string', 'null'] to 'anyOf' format.
|
|
42
|
+
"""Convert JSON Schema 'type': ['string', 'null'] to 'anyOf' format and fix enum handling.
|
|
43
43
|
|
|
44
44
|
Args:
|
|
45
45
|
schema: The JSON schema to fix.
|
|
@@ -51,6 +51,11 @@ class LangChainAdapter(BaseAdapter):
|
|
|
51
51
|
if "type" in schema and isinstance(schema["type"], list):
|
|
52
52
|
schema["anyOf"] = [{"type": t} for t in schema["type"]]
|
|
53
53
|
del schema["type"] # Remove 'type' and standardize to 'anyOf'
|
|
54
|
+
|
|
55
|
+
# Fix enum handling - ensure enum fields are properly typed as strings
|
|
56
|
+
if "enum" in schema and "type" not in schema:
|
|
57
|
+
schema["type"] = "string"
|
|
58
|
+
|
|
54
59
|
for key, value in schema.items():
|
|
55
60
|
schema[key] = self.fix_schema(value) # Apply recursively
|
|
56
61
|
return schema
|
|
@@ -71,11 +76,8 @@ class LangChainAdapter(BaseAdapter):
|
|
|
71
76
|
if tool_result.isError:
|
|
72
77
|
raise ToolException(f"Tool execution failed: {tool_result.content}")
|
|
73
78
|
|
|
74
|
-
if not tool_result.content:
|
|
75
|
-
raise ToolException("Tool execution returned no content")
|
|
76
|
-
|
|
77
79
|
decoded_result = ""
|
|
78
|
-
for item in tool_result.content:
|
|
80
|
+
for item in tool_result.content or []:
|
|
79
81
|
match item.type:
|
|
80
82
|
case "text":
|
|
81
83
|
item: TextContent
|
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -30,7 +30,11 @@ from mcp_use.telemetry.utils import extract_model_info
|
|
|
30
30
|
|
|
31
31
|
from ..adapters.langchain_adapter import LangChainAdapter
|
|
32
32
|
from ..logging import logger
|
|
33
|
+
from ..managers.base import BaseServerManager
|
|
33
34
|
from ..managers.server_manager import ServerManager
|
|
35
|
+
|
|
36
|
+
# Import observability manager
|
|
37
|
+
from ..observability import ObservabilityManager
|
|
34
38
|
from .prompts.system_prompt_builder import create_system_message
|
|
35
39
|
from .prompts.templates import DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
|
|
36
40
|
from .remote import RemoteAgent
|
|
@@ -62,10 +66,15 @@ class MCPAgent:
|
|
|
62
66
|
disallowed_tools: list[str] | None = None,
|
|
63
67
|
tools_used_names: list[str] | None = None,
|
|
64
68
|
use_server_manager: bool = False,
|
|
69
|
+
server_manager: BaseServerManager | None = None,
|
|
65
70
|
verbose: bool = False,
|
|
66
71
|
agent_id: str | None = None,
|
|
67
72
|
api_key: str | None = None,
|
|
68
73
|
base_url: str = "https://cloud.mcp-use.com",
|
|
74
|
+
callbacks: list | None = None,
|
|
75
|
+
chat_id: str | None = None,
|
|
76
|
+
retry_on_error: bool = True,
|
|
77
|
+
max_retries_per_step: int = 2,
|
|
69
78
|
):
|
|
70
79
|
"""Initialize a new MCPAgent instance.
|
|
71
80
|
|
|
@@ -84,10 +93,13 @@ class MCPAgent:
|
|
|
84
93
|
agent_id: Remote agent ID for remote execution. If provided, creates a remote agent.
|
|
85
94
|
api_key: API key for remote execution. If None, checks MCP_USE_API_KEY env var.
|
|
86
95
|
base_url: Base URL for remote API calls.
|
|
96
|
+
callbacks: List of LangChain callbacks to use. If None and Langfuse is configured, uses langfuse_handler.
|
|
97
|
+
retry_on_error: Whether to retry tool calls that fail due to validation errors.
|
|
98
|
+
max_retries_per_step: Maximum number of retries for validation errors per step.
|
|
87
99
|
"""
|
|
88
100
|
# Handle remote execution
|
|
89
101
|
if agent_id is not None:
|
|
90
|
-
self._remote_agent = RemoteAgent(agent_id=agent_id, api_key=api_key, base_url=base_url)
|
|
102
|
+
self._remote_agent = RemoteAgent(agent_id=agent_id, api_key=api_key, base_url=base_url, chat_id=chat_id)
|
|
91
103
|
self._is_remote = True
|
|
92
104
|
return
|
|
93
105
|
|
|
@@ -109,13 +121,20 @@ class MCPAgent:
|
|
|
109
121
|
self.disallowed_tools = disallowed_tools or []
|
|
110
122
|
self.tools_used_names = tools_used_names or []
|
|
111
123
|
self.use_server_manager = use_server_manager
|
|
124
|
+
self.server_manager = server_manager
|
|
112
125
|
self.verbose = verbose
|
|
126
|
+
self.retry_on_error = retry_on_error
|
|
127
|
+
self.max_retries_per_step = max_retries_per_step
|
|
113
128
|
# System prompt configuration
|
|
114
129
|
self.system_prompt = system_prompt # User-provided full prompt override
|
|
115
130
|
# User can provide a template override, otherwise use the imported default
|
|
116
131
|
self.system_prompt_template_override = system_prompt_template
|
|
117
132
|
self.additional_instructions = additional_instructions
|
|
118
133
|
|
|
134
|
+
# Set up observability callbacks using the ObservabilityManager
|
|
135
|
+
self.observability_manager = ObservabilityManager(custom_callbacks=callbacks)
|
|
136
|
+
self.callbacks = self.observability_manager.get_callbacks()
|
|
137
|
+
|
|
119
138
|
# Either client or connector must be provided
|
|
120
139
|
if not client and len(self.connectors) == 0:
|
|
121
140
|
raise ValueError("Either client or connector must be provided")
|
|
@@ -126,9 +145,7 @@ class MCPAgent:
|
|
|
126
145
|
# Initialize telemetry
|
|
127
146
|
self.telemetry = Telemetry()
|
|
128
147
|
|
|
129
|
-
|
|
130
|
-
self.server_manager = None
|
|
131
|
-
if self.use_server_manager:
|
|
148
|
+
if self.use_server_manager and self.server_manager is None:
|
|
132
149
|
if not self.client:
|
|
133
150
|
raise ValueError("Client must be provided when using server manager")
|
|
134
151
|
self.server_manager = ServerManager(self.client, self.adapter)
|
|
@@ -246,9 +263,15 @@ class MCPAgent:
|
|
|
246
263
|
# Use the standard create_tool_calling_agent
|
|
247
264
|
agent = create_tool_calling_agent(llm=self.llm, tools=self._tools, prompt=prompt)
|
|
248
265
|
|
|
249
|
-
# Use the standard AgentExecutor
|
|
250
|
-
executor = AgentExecutor(
|
|
251
|
-
|
|
266
|
+
# Use the standard AgentExecutor with callbacks
|
|
267
|
+
executor = AgentExecutor(
|
|
268
|
+
agent=agent,
|
|
269
|
+
tools=self._tools,
|
|
270
|
+
max_iterations=self.max_steps,
|
|
271
|
+
verbose=self.verbose,
|
|
272
|
+
callbacks=self.callbacks,
|
|
273
|
+
)
|
|
274
|
+
logger.debug(f"Created agent executor with max_iterations={self.max_steps} and {len(self.callbacks)} callbacks")
|
|
252
275
|
return executor
|
|
253
276
|
|
|
254
277
|
def get_conversation_history(self) -> list[BaseMessage]:
|
|
@@ -469,6 +492,26 @@ class MCPAgent:
|
|
|
469
492
|
|
|
470
493
|
logger.info(f"🏁 Starting agent execution with max_steps={steps}")
|
|
471
494
|
|
|
495
|
+
# Track whether agent finished successfully vs reached max iterations
|
|
496
|
+
agent_finished_successfully = False
|
|
497
|
+
result = None
|
|
498
|
+
|
|
499
|
+
# Create a run manager with our callbacks if we have any - ONCE for the entire execution
|
|
500
|
+
run_manager = None
|
|
501
|
+
if self.callbacks:
|
|
502
|
+
# Create an async callback manager with our callbacks
|
|
503
|
+
from langchain_core.callbacks.manager import AsyncCallbackManager
|
|
504
|
+
|
|
505
|
+
callback_manager = AsyncCallbackManager.configure(
|
|
506
|
+
inheritable_callbacks=self.callbacks,
|
|
507
|
+
local_callbacks=self.callbacks,
|
|
508
|
+
)
|
|
509
|
+
# Create a run manager for this chain execution
|
|
510
|
+
run_manager = await callback_manager.on_chain_start(
|
|
511
|
+
{"name": "MCPAgent (mcp-use)"},
|
|
512
|
+
inputs,
|
|
513
|
+
)
|
|
514
|
+
|
|
472
515
|
for step_num in range(steps):
|
|
473
516
|
steps_taken = step_num + 1
|
|
474
517
|
# --- Check for tool updates if using server manager ---
|
|
@@ -498,20 +541,52 @@ class MCPAgent:
|
|
|
498
541
|
|
|
499
542
|
# --- Plan and execute the next step ---
|
|
500
543
|
try:
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
544
|
+
retry_count = 0
|
|
545
|
+
next_step_output = None
|
|
546
|
+
|
|
547
|
+
while retry_count <= self.max_retries_per_step:
|
|
548
|
+
try:
|
|
549
|
+
# Use the internal _atake_next_step which handles planning and execution
|
|
550
|
+
# This requires providing the necessary context like maps and intermediate steps
|
|
551
|
+
next_step_output = await self._agent_executor._atake_next_step(
|
|
552
|
+
name_to_tool_map=name_to_tool_map,
|
|
553
|
+
color_mapping=color_mapping,
|
|
554
|
+
inputs=inputs,
|
|
555
|
+
intermediate_steps=intermediate_steps,
|
|
556
|
+
run_manager=run_manager,
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
# If we get here, the step succeeded, break out of retry loop
|
|
560
|
+
break
|
|
561
|
+
|
|
562
|
+
except Exception as e:
|
|
563
|
+
if not self.retry_on_error or retry_count >= self.max_retries_per_step:
|
|
564
|
+
logger.error(f"❌ Validation error during step {step_num + 1}: {e}")
|
|
565
|
+
result = f"Agent stopped due to a validation error: {str(e)}"
|
|
566
|
+
success = False
|
|
567
|
+
yield result
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
retry_count += 1
|
|
571
|
+
logger.warning(
|
|
572
|
+
f"⚠️ Validation error, retrying ({retry_count}/{self.max_retries_per_step}): {e}"
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
# Create concise feedback for the LLM about the validation error
|
|
576
|
+
error_message = f"Error: {str(e)}"
|
|
577
|
+
inputs["input"] = error_message
|
|
578
|
+
|
|
579
|
+
# Continue to next iteration of retry loop
|
|
580
|
+
continue
|
|
510
581
|
|
|
511
582
|
# Process the output
|
|
512
583
|
if isinstance(next_step_output, AgentFinish):
|
|
513
584
|
logger.info(f"✅ Agent finished at step {step_num + 1}")
|
|
585
|
+
agent_finished_successfully = True
|
|
514
586
|
result = next_step_output.return_values.get("output", "No output generated")
|
|
587
|
+
# End the chain if we have a run manager
|
|
588
|
+
if run_manager:
|
|
589
|
+
await run_manager.on_chain_end({"output": result})
|
|
515
590
|
|
|
516
591
|
# If structured output is requested, attempt to create it
|
|
517
592
|
if output_schema and structured_llm:
|
|
@@ -563,6 +638,12 @@ class MCPAgent:
|
|
|
563
638
|
for agent_step in next_step_output:
|
|
564
639
|
yield agent_step
|
|
565
640
|
action, observation = agent_step
|
|
641
|
+
reasoning = getattr(action, "log", "")
|
|
642
|
+
if reasoning:
|
|
643
|
+
reasoning_str = reasoning.replace("\n", " ")
|
|
644
|
+
if len(reasoning_str) > 300:
|
|
645
|
+
reasoning_str = reasoning_str[:297] + "..."
|
|
646
|
+
logger.info(f"💭 Reasoning: {reasoning_str}")
|
|
566
647
|
tool_name = action.tool
|
|
567
648
|
self.tools_used_names.append(tool_name)
|
|
568
649
|
tool_input_str = str(action.tool_input)
|
|
@@ -583,25 +664,39 @@ class MCPAgent:
|
|
|
583
664
|
tool_return = self._agent_executor._get_tool_return(last_step)
|
|
584
665
|
if tool_return is not None:
|
|
585
666
|
logger.info(f"🏆 Tool returned directly at step {step_num + 1}")
|
|
667
|
+
agent_finished_successfully = True
|
|
586
668
|
result = tool_return.return_values.get("output", "No output generated")
|
|
587
669
|
break
|
|
588
670
|
|
|
589
671
|
except OutputParserException as e:
|
|
590
672
|
logger.error(f"❌ Output parsing error during step {step_num + 1}: {e}")
|
|
591
673
|
result = f"Agent stopped due to a parsing error: {str(e)}"
|
|
674
|
+
if run_manager:
|
|
675
|
+
await run_manager.on_chain_error(e)
|
|
592
676
|
break
|
|
593
677
|
except Exception as e:
|
|
594
678
|
logger.error(f"❌ Error during agent execution step {step_num + 1}: {e}")
|
|
595
679
|
import traceback
|
|
596
680
|
|
|
597
681
|
traceback.print_exc()
|
|
682
|
+
# End the chain with error if we have a run manager
|
|
683
|
+
if run_manager:
|
|
684
|
+
await run_manager.on_chain_error(e)
|
|
598
685
|
result = f"Agent stopped due to an error: {str(e)}"
|
|
599
686
|
break
|
|
600
687
|
|
|
601
688
|
# --- Loop finished ---
|
|
602
689
|
if not result:
|
|
603
|
-
|
|
604
|
-
|
|
690
|
+
if agent_finished_successfully:
|
|
691
|
+
# Agent finished successfully but returned empty output
|
|
692
|
+
result = "Agent completed the task successfully."
|
|
693
|
+
logger.info("✅ Agent finished successfully with empty output")
|
|
694
|
+
else:
|
|
695
|
+
# Agent actually reached max iterations
|
|
696
|
+
logger.warning(f"⚠️ Agent stopped after reaching max iterations ({steps})")
|
|
697
|
+
result = f"Agent stopped after reaching the maximum number of steps ({steps})."
|
|
698
|
+
if run_manager:
|
|
699
|
+
await run_manager.on_chain_end({"output": result})
|
|
605
700
|
|
|
606
701
|
# If structured output was requested but not achieved, attempt one final time
|
|
607
702
|
if output_schema and structured_llm and not success:
|
|
@@ -738,7 +833,8 @@ class MCPAgent:
|
|
|
738
833
|
"""
|
|
739
834
|
# Delegate to remote agent if in remote mode
|
|
740
835
|
if self._is_remote and self._remote_agent:
|
|
741
|
-
|
|
836
|
+
result = await self._remote_agent.run(query, max_steps, external_history, output_schema)
|
|
837
|
+
return result
|
|
742
838
|
|
|
743
839
|
success = True
|
|
744
840
|
start_time = time.time()
|
|
@@ -5,16 +5,7 @@ You have access to the following tools:
|
|
|
5
5
|
|
|
6
6
|
{tool_descriptions}
|
|
7
7
|
|
|
8
|
-
Use
|
|
9
|
-
|
|
10
|
-
Question: the input question you must answer
|
|
11
|
-
Thought: you should always think about what to do
|
|
12
|
-
Action: the action to take, should be one of the available tools
|
|
13
|
-
Action Input: the input to the action
|
|
14
|
-
Observation: the result of the action
|
|
15
|
-
... (this Thought/Action/Action Input/Observation can repeat N times)
|
|
16
|
-
Thought: I now know the final answer
|
|
17
|
-
Final Answer: the final answer to the original input question"""
|
|
8
|
+
Use these tools to help answer questions and complete tasks as needed."""
|
|
18
9
|
|
|
19
10
|
|
|
20
11
|
SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE = """You are a helpful assistant designed
|
mcp_use/agents/remote.py
CHANGED
|
@@ -5,6 +5,7 @@ Remote agent implementation for executing agents via API.
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
from typing import Any, TypeVar
|
|
8
|
+
from uuid import UUID
|
|
8
9
|
|
|
9
10
|
import httpx
|
|
10
11
|
from langchain.schema import BaseMessage
|
|
@@ -15,25 +16,52 @@ from ..logging import logger
|
|
|
15
16
|
T = TypeVar("T", bound=BaseModel)
|
|
16
17
|
|
|
17
18
|
# API endpoint constants
|
|
18
|
-
API_CHATS_ENDPOINT = "/api/v1/chats"
|
|
19
|
+
API_CHATS_ENDPOINT = "/api/v1/chats/get-or-create"
|
|
19
20
|
API_CHAT_EXECUTE_ENDPOINT = "/api/v1/chats/{chat_id}/execute"
|
|
20
21
|
API_CHAT_DELETE_ENDPOINT = "/api/v1/chats/{chat_id}"
|
|
21
22
|
|
|
23
|
+
UUID_ERROR_MESSAGE = """A UUID is a 36 character string of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \n
|
|
24
|
+
Example: 123e4567-e89b-12d3-a456-426614174000
|
|
25
|
+
To generate a UUID, you can use the following command:
|
|
26
|
+
import uuid
|
|
27
|
+
|
|
28
|
+
# Generate a random UUID
|
|
29
|
+
my_uuid = uuid.uuid4()
|
|
30
|
+
print(my_uuid)
|
|
31
|
+
"""
|
|
32
|
+
|
|
22
33
|
|
|
23
34
|
class RemoteAgent:
|
|
24
35
|
"""Agent that executes remotely via API."""
|
|
25
36
|
|
|
26
|
-
def __init__(
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
agent_id: str,
|
|
40
|
+
chat_id: str | None = None,
|
|
41
|
+
api_key: str | None = None,
|
|
42
|
+
base_url: str = "https://cloud.mcp-use.com",
|
|
43
|
+
):
|
|
27
44
|
"""Initialize remote agent.
|
|
28
45
|
|
|
29
46
|
Args:
|
|
30
47
|
agent_id: The ID of the remote agent to execute
|
|
48
|
+
chat_id: The ID of the chat session to use. If None, a new chat session will be created.
|
|
31
49
|
api_key: API key for authentication. If None, will check MCP_USE_API_KEY env var
|
|
32
50
|
base_url: Base URL for the remote API
|
|
33
51
|
"""
|
|
52
|
+
|
|
53
|
+
if chat_id is not None:
|
|
54
|
+
try:
|
|
55
|
+
chat_id = str(UUID(chat_id))
|
|
56
|
+
except ValueError as e:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Invalid chat ID: {chat_id}, make sure to provide a valid UUID.\n{UUID_ERROR_MESSAGE}"
|
|
59
|
+
) from e
|
|
60
|
+
|
|
34
61
|
self.agent_id = agent_id
|
|
62
|
+
self.chat_id = chat_id
|
|
63
|
+
self._session_established = False
|
|
35
64
|
self.base_url = base_url
|
|
36
|
-
self._chat_id = None # Persistent chat session
|
|
37
65
|
|
|
38
66
|
# Handle API key validation
|
|
39
67
|
if api_key is None:
|
|
@@ -109,16 +137,14 @@ class RemoteAgent:
|
|
|
109
137
|
return output_schema.model_validate({"content": str(result_data)})
|
|
110
138
|
raise
|
|
111
139
|
|
|
112
|
-
async def
|
|
113
|
-
"""Create a persistent chat session for the agent.
|
|
114
|
-
|
|
115
|
-
query: The initial query (not used in title anymore)
|
|
140
|
+
async def _upsert_chat_session(self) -> str:
|
|
141
|
+
"""Create or resume a persistent chat session for the agent via upsert.
|
|
142
|
+
|
|
116
143
|
Returns:
|
|
117
|
-
The chat ID
|
|
118
|
-
Raises:
|
|
119
|
-
RuntimeError: If chat creation fails
|
|
144
|
+
The chat session ID
|
|
120
145
|
"""
|
|
121
146
|
chat_payload = {
|
|
147
|
+
"id": self.chat_id, # Include chat_id for resuming or None for creating
|
|
122
148
|
"title": f"Remote Agent Session - {self.agent_id}",
|
|
123
149
|
"agent_id": self.agent_id,
|
|
124
150
|
"type": "agent_execution",
|
|
@@ -127,7 +153,7 @@ class RemoteAgent:
|
|
|
127
153
|
headers = {"Content-Type": "application/json", "x-api-key": self.api_key}
|
|
128
154
|
chat_url = f"{self.base_url}{API_CHATS_ENDPOINT}"
|
|
129
155
|
|
|
130
|
-
logger.info(f"📝
|
|
156
|
+
logger.info(f"📝 Upserting chat session for agent {self.agent_id}")
|
|
131
157
|
|
|
132
158
|
try:
|
|
133
159
|
chat_response = await self._client.post(chat_url, json=chat_payload, headers=headers)
|
|
@@ -135,7 +161,11 @@ class RemoteAgent:
|
|
|
135
161
|
|
|
136
162
|
chat_data = chat_response.json()
|
|
137
163
|
chat_id = chat_data["id"]
|
|
138
|
-
|
|
164
|
+
if chat_response.status_code == 201:
|
|
165
|
+
logger.info(f"✅ New chat session created: {chat_id}")
|
|
166
|
+
else:
|
|
167
|
+
logger.info(f"✅ Resumed chat session: {chat_id}")
|
|
168
|
+
|
|
139
169
|
return chat_id
|
|
140
170
|
|
|
141
171
|
except httpx.HTTPStatusError as e:
|
|
@@ -156,7 +186,6 @@ class RemoteAgent:
|
|
|
156
186
|
self,
|
|
157
187
|
query: str,
|
|
158
188
|
max_steps: int | None = None,
|
|
159
|
-
manage_connector: bool = True,
|
|
160
189
|
external_history: list[BaseMessage] | None = None,
|
|
161
190
|
output_schema: type[T] | None = None,
|
|
162
191
|
) -> str | T:
|
|
@@ -165,8 +194,7 @@ class RemoteAgent:
|
|
|
165
194
|
Args:
|
|
166
195
|
query: The query to execute
|
|
167
196
|
max_steps: Maximum number of steps (default: 10)
|
|
168
|
-
|
|
169
|
-
external_history: Ignored for remote execution (not supported yet)
|
|
197
|
+
external_history: External history (not supported yet for remote execution)
|
|
170
198
|
output_schema: Optional Pydantic model for structured output
|
|
171
199
|
|
|
172
200
|
Returns:
|
|
@@ -178,11 +206,14 @@ class RemoteAgent:
|
|
|
178
206
|
try:
|
|
179
207
|
logger.info(f"🌐 Executing query on remote agent {self.agent_id}")
|
|
180
208
|
|
|
181
|
-
# Step 1:
|
|
182
|
-
|
|
183
|
-
|
|
209
|
+
# Step 1: Ensure chat session exists on the backend by upserting.
|
|
210
|
+
# This happens once per agent instance.
|
|
211
|
+
if not self._session_established:
|
|
212
|
+
logger.info(f"🔧 Establishing chat session for agent {self.agent_id}")
|
|
213
|
+
self.chat_id = await self._upsert_chat_session()
|
|
214
|
+
self._session_established = True
|
|
184
215
|
|
|
185
|
-
chat_id = self.
|
|
216
|
+
chat_id = self.chat_id
|
|
186
217
|
|
|
187
218
|
# Step 2: Execute the agent within the chat context
|
|
188
219
|
execution_payload = {"query": query, "max_steps": max_steps or 10}
|
mcp_use/auth/__init__.py
ADDED
mcp_use/auth/bearer.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Bearer token authentication support."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from pydantic import BaseModel, SecretStr
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BearerAuth(httpx.Auth, BaseModel):
|
|
10
|
+
"""Bearer token authentication for HTTP requests."""
|
|
11
|
+
|
|
12
|
+
token: SecretStr
|
|
13
|
+
|
|
14
|
+
def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:
|
|
15
|
+
"""Apply bearer token authentication to the request."""
|
|
16
|
+
request.headers["Authorization"] = f"Bearer {self.token.get_secret_value()}"
|
|
17
|
+
yield request
|