Edison Watch
Developers

LlamaIndex

Connect a LlamaIndex agent to Edison Watch using BasicMCPClient and McpToolSpec, keeping a stable session per conversation so data-leak protection holds across every turn.

LlamaIndex connects to Edison with BasicMCPClient, which auto-selects streamable HTTP for an https:// URL like Edison's. McpToolSpec turns the server's tools into LlamaIndex tools. Your connection URL carries your API key, so no auth header is needed.

pip install llama-index-tools-mcp llama-index-llms-openai llama-index
import asyncio
import os

from llama_index.tools.mcp import BasicMCPClient, McpToolSpec
from llama_index.llms.openai import OpenAI
from llama_index.core.agent.workflow import FunctionAgent

async def main() -> None:
    client = BasicMCPClient(os.environ["EDISON_MCP_URL"])
    tools = await McpToolSpec(client=client).to_tool_list_async()

    agent = FunctionAgent(tools=tools, llm=OpenAI(model="gpt-5.4-mini"))
    response = await agent.run("List my available tools.")
    print(response)

asyncio.run(main())

Set EDISON_MCP_URL to your connection URL, e.g. https://mcp.edison.watch/mcp/<your-api-key>/?client=llamaindex.

llama-index-llms-openai resolves each model against a hardcoded context-window table and raises ValueError: Unknown model for anything not in it - so a brand-new model id can fail until you upgrade the integration. Pick a model your installed version knows (upgrade with pip install -U llama-index-llms-openai), or use OpenAILike from llama-index-llms-openai-like to skip the table entirely.

Keep a stable session across turns to preserve data-leak protection

Send a stable x-edison-conversation-id header on every turn of the same conversation. That header is what keeps Edison's data-leak protection intact across a multi-turn run: Edison tracks lethal-trifecta risk per session, so if each turn looks like a brand-new session, that protection resets - and a later turn can leak data that the accumulated risk should have blocked.

Hosted clients (Claude Code, VS Code) send it automatically. For a custom LlamaIndex agent, set the header on BasicMCPClient, keyed to your own conversation or thread id:

# Reuse one stable conversation_id for every turn of the same conversation.
client = BasicMCPClient(
    os.environ["EDISON_MCP_URL"],
    headers={"x-edison-conversation-id": conversation_id},
)

Without a stable x-edison-conversation-id, each connection is treated as a fresh session that starts with empty risk state - so risk accumulated on an earlier turn won't be there to block a later exfiltration. The ?client= label is only a dashboard tag, not a session key. Use a unique id per conversation (a UUID is ideal); ids are scoped to your API key, so don't reuse one string for two different conversations.

Optional: the encrypted-secrets header

For servers with zero-knowledge-encrypted secrets, BasicMCPClient accepts a headers dict, alongside x-edison-conversation-id:

client = BasicMCPClient(
    os.environ["EDISON_MCP_URL"],
    headers={
        "x-edison-conversation-id": conversation_id,
        "x-edison-secret-key": os.environ["EDISON_SECRET_KEY"],
    },
)

The convenience helpers get_tools_from_mcp_url / aget_tools_from_mcp_url don't take a headers argument. To send a custom header with them, pass a pre-built client: aget_tools_from_mcp_url(url, client=BasicMCPClient(url, headers=...)).