fix(mcp): authentication for mcp calls in remote runtime (#8856)

This commit is contained in:
Xingyao Wang
2025-06-02 17:41:47 -04:00
committed by GitHub
parent e49c984e9f
commit 17ae03857f
7 changed files with 34 additions and 9 deletions

View File

@@ -158,7 +158,7 @@ class OpenHandsMCPConfig:
return MCPStdioServerConfig(
name='tavily',
command='npx',
args=['-y', 'tavily-mcp@0.1.4'],
args=['-y', 'tavily-mcp@0.2.1'],
env={'TAVILY_API_KEY': app_config.search_api_key.get_secret_value()},
)
else:

View File

@@ -45,7 +45,15 @@ class MCPClient(BaseModel):
try:
# Use asyncio.wait_for to enforce the timeout
async def connect_with_timeout():
headers = {'Authorization': f'Bearer {api_key}'} if api_key else {}
headers = (
{
'Authorization': f'Bearer {api_key}',
's': api_key, # We need this for action execution server's MCP Router
'X-Session-API-Key': api_key, # We need this for Remote Runtime
}
if api_key
else {}
)
if conversation_id:
headers['X-OpenHands-Conversation-ID'] = conversation_id

View File

@@ -887,6 +887,14 @@ fi
"""Zip all files in the sandbox and return a path in the local filesystem."""
raise NotImplementedError('This method is not implemented in the base class.')
# ====================================================================
# Authentication
# ====================================================================
@property
def session_api_key(self) -> str | None:
return None
# ====================================================================
# VSCode
# ====================================================================

View File

@@ -436,8 +436,7 @@ class ActionExecutionClient(Runtime):
updated_mcp_config.sse_servers.append(
MCPSSEServerConfig(
url=self.action_execution_server_url.rstrip('/') + '/sse',
# No API key by default. Child runtime can override this when appropriate
api_key=None,
api_key=self.session_api_key,
)
)

View File

@@ -98,6 +98,7 @@ class RemoteRuntime(ActionExecutionClient):
self.session,
)
self.available_hosts: dict[str, int] = {}
self._session_api_key: str | None = None
def log(self, level: str, message: str, exc_info: bool | None = None) -> None:
getattr(logger, level)(
@@ -358,6 +359,15 @@ class RemoteRuntime(ActionExecutionClient):
self.session.headers.update(
{'X-Session-API-Key': start_response['session_api_key']}
)
self._session_api_key = start_response['session_api_key']
self.log(
'debug',
f'Session API key setted',
)
@property
def session_api_key(self) -> str | None:
return self._session_api_key
@property
def vscode_url(self) -> str | None:

8
poetry.lock generated
View File

@@ -2459,14 +2459,14 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc
[[package]]
name = "fastmcp"
version = "2.5.1"
version = "2.5.2"
description = "The fast, Pythonic way to build MCP servers."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "fastmcp-2.5.1-py3-none-any.whl", hash = "sha256:a6fe50693954a6aed89fc6e43f227dcd66e112e3d3a1d633ee22b4f435ee8aed"},
{file = "fastmcp-2.5.1.tar.gz", hash = "sha256:0d10ec65a362ae4f78bdf3b639faf35b36cc0a1c8f5461a54fac906fe821b84d"},
{file = "fastmcp-2.5.2-py3-none-any.whl", hash = "sha256:4ea46ef35c1308b369eff7c8a10e4c9639bed046fd646449c1227ac7c3856d83"},
{file = "fastmcp-2.5.2.tar.gz", hash = "sha256:761c92fb54f561136f631d7d98b4920152978f6f0a66a4cef689a7983fd05c8b"},
]
[package.dependencies]
@@ -11741,4 +11741,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.1"
python-versions = "^3.12,<3.14"
content-hash = "35d7c81745aee985169bd32fff6c02fbdb56d2902e630f89aa6e99f09f3c1c4e"
content-hash = "42d590343d06314e3e2b0b47c82eae65cd1255d1fed0b76a229c702add0a4f55"

View File

@@ -83,7 +83,7 @@ prompt-toolkit = "^3.0.50"
poetry = "^2.1.2"
anyio = "4.9.0"
pythonnet = "*"
fastmcp = "^2.3.3"
fastmcp = "^2.5.2"
mcpm = "1.12.0"
[tool.poetry.group.dev.dependencies]