mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 09:13:14 +00:00
* feat: server-side Datadog/LangSmith observability tools + team creds Add team-wide observability credential settings (Datadog DD_SITE/API/APP keys, LangSmith API key) stored encrypted server-side, with an admin dashboard section to connect/disconnect each provider. When connected, get_agent loads read-only observability tools server-side: Datadog via its hosted MCP server (langchain-mcp-adapters, toolsets=core) and LangSmith read tools (langsmith_get_trace, langsmith_list_runs). Credentials live in the LangGraph server process and are never exposed to the sandbox. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> * fix: address review on observability tools Address PR review feedback: - Authorize observability tools per triggering user (admins + the OBSERVABILITY_AUTHORIZED_EMAILS allowlist) so prompt-injected runs from untrusted contributors can't reach team Datadog/LangSmith data. - Use the documented Datadog MCP auth headers DD_API_KEY / DD_APPLICATION_KEY. - Store each provider's credentials under its own store key to avoid a read-modify-write race dropping the other provider on concurrent saves. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> * fix: async email resolution in observability authorization gate --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""Server-side Datadog tools backed by Datadog's hosted MCP server.
|
|
|
|
Credentials live in team settings (encrypted at rest) and are attached as
|
|
``DD_API_KEY`` / ``DD_APPLICATION_KEY`` headers to the MCP connection, which runs
|
|
in the LangGraph server process. The sandbox never holds Datadog credentials.
|
|
|
|
The default toolset is ``core`` (query-oriented: logs, metrics, traces,
|
|
dashboards, monitors, incidents, hosts, services, events). Override via
|
|
``DATADOG_MCP_TOOLSETS``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from datetime import timedelta
|
|
|
|
from langchain_core.tools import BaseTool
|
|
|
|
from ..dashboard.team_credentials import DatadogCredentials, get_datadog_credentials
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DEFAULT_DATADOG_TOOLSETS = "core"
|
|
_MCP_TIMEOUT_SECONDS = 30.0
|
|
|
|
|
|
def _toolsets() -> str:
|
|
return os.environ.get("DATADOG_MCP_TOOLSETS", DEFAULT_DATADOG_TOOLSETS).strip() or (
|
|
DEFAULT_DATADOG_TOOLSETS
|
|
)
|
|
|
|
|
|
async def _build_mcp_tools(creds: DatadogCredentials) -> list[BaseTool]:
|
|
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
|
|
client = MultiServerMCPClient(
|
|
{
|
|
"datadog": {
|
|
"transport": "streamable_http",
|
|
"url": creds.mcp_url(_toolsets()),
|
|
"headers": {
|
|
"DD_API_KEY": creds.api_key,
|
|
"DD_APPLICATION_KEY": creds.app_key,
|
|
},
|
|
"timeout": timedelta(seconds=_MCP_TIMEOUT_SECONDS),
|
|
}
|
|
}
|
|
)
|
|
return await client.get_tools()
|
|
|
|
|
|
async def load_datadog_tools() -> list[BaseTool]:
|
|
"""Return Datadog MCP tools when the team has connected Datadog, else ``[]``.
|
|
|
|
Failures (no credentials, unreachable MCP server) degrade to an empty list so
|
|
the agent still starts without Datadog tools.
|
|
"""
|
|
creds = await get_datadog_credentials()
|
|
if creds is None:
|
|
return []
|
|
try:
|
|
tools = await _build_mcp_tools(creds)
|
|
except Exception: # noqa: BLE001
|
|
logger.warning("Failed to load Datadog MCP tools", exc_info=True)
|
|
return []
|
|
logger.info("Loaded %d Datadog MCP tool(s) (toolsets=%s)", len(tools), _toolsets())
|
|
return tools
|