Client Configuration
The galois-edge MCP server speaks streamable-HTTP (transport revision 2025-03-26). Any MCP client that supports this transport works; this page collects copy-pasteable config for the common ones.
The endpoint pattern in every example below:
| Path | Use it for |
|---|---|
http://<edge>:8767/mcp (tailnet IP or loopback) | Direct dial. No per-call auth — tailnet membership is the boundary. |
https://cloud.galoislabs.ai/mcp/<edge_id> | Public-internet routing through the cloud relay with per-call JWT-scoped ACLs. Daemon side shipped; cloud side in PR review — rolling out. |
For the relay path, the authorization_token is a short-lived JWT minted by the cloud against the requesting user’s edge ACL. Where the cloud-side termination isn’t yet in place, all examples below default to the tailnet-direct form.
Claude Desktop
Section titled “Claude Desktop”Add the daemon to claude_desktop_config.json — macOS at ~/Library/Application Support/Claude/claude_desktop_config.json, Windows at %APPDATA%\Claude\claude_desktop_config.json.
{ "mcpServers": { "galois-edge": { "url": "http://lab-pi.tail-1234.ts.net:8767/mcp" } }}Restart Claude Desktop. The galois-edge tools appear in the tool picker; Claude will surface a confirmation prompt for any tool the daemon flagged with destructiveHint: true (anything whose underlying profile command is is_dangerous: true).
Anthropic API (mcp_servers connector)
Section titled “Anthropic API (mcp_servers connector)”The Anthropic API’s MCP connector (header mcp-client-2025-11-20) speaks streamable-HTTP and is the canonical path for cloud-routed agents.
import anthropicimport os
client = anthropic.Anthropic()
response = client.messages.create( model="claude-opus-4-7", max_tokens=2048, messages=[{ "role": "user", "content": "List instruments and run *IDN? on each one." }], mcp_servers=[{ "type": "url", "url": "https://cloud.galoislabs.ai/mcp/<edge_id>", "name": "galois-edge", "authorization_token": os.environ["GALOIS_MCP_JWT"], }], extra_headers={"mcp-client-2025-11-20": "true"},)print(response.content)curl https://api.anthropic.com/v1/messages \ -H "content-type: application/json" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "mcp-client-2025-11-20: true" \ -d '{ "model": "claude-opus-4-7", "max_tokens": 2048, "messages": [{"role": "user", "content": "List instruments."}], "mcp_servers": [{ "type": "url", "url": "https://cloud.galoislabs.ai/mcp/<edge_id>", "name": "galois-edge", "authorization_token": "'$GALOIS_MCP_JWT'" }] }'The JWT in authorization_token is the per-call Galois-Caller-JWT documented in the MCP Server Reference. It’s minted by the cloud against the requesting user’s edge ACL. The daemon validates it against the cloud’s JWKS and enforces tools_allow / danger_allow per call.
OpenAI Agents SDK
Section titled “OpenAI Agents SDK”The OpenAI Agents SDK supports MCP servers via its MCPServerStreamableHttp adapter.
from agents import Agent, Runnerfrom agents.mcp.server import MCPServerStreamableHttp
galois_mcp = MCPServerStreamableHttp( name="galois-edge", params={ "url": "http://lab-pi.tail-1234.ts.net:8767/mcp", },)
agent = Agent( name="lab-assistant", instructions=( "You drive lab instruments through the galois-edge MCP server. " "Always start with list_instruments and get_capabilities before " "calling typed per-instrument tools." ), mcp_servers=[galois_mcp],)
result = Runner.run_sync(agent, "Read the voltage on the DMM.")print(result.final_output)For the cloud-relay path, pass the JWT as a header in params:
galois_mcp = MCPServerStreamableHttp( name="galois-edge", params={ "url": "https://cloud.galoislabs.ai/mcp/<edge_id>", "headers": {"Authorization": f"Bearer {os.environ['GALOIS_MCP_JWT']}"}, },)Cursor
Section titled “Cursor”Cursor reads MCP server config from ~/.cursor/mcp.json (global) or .cursor/mcp.json in a workspace. Streamable-HTTP support landed in Cursor 0.42+.
{ "mcpServers": { "galois-edge": { "url": "http://lab-pi.tail-1234.ts.net:8767/mcp" } }}Open the Cursor settings → MCP panel after editing the file; the server should show Connected with the tool count from the daemon. If it doesn’t, check that the URL is reachable from the machine running Cursor (curl http://lab-pi.tail-1234.ts.net:8767/mcp should return a 4xx with an MCP error body, not a connection failure).
LangGraph
Section titled “LangGraph”LangGraph’s langchain-mcp-adapters exposes MCP tools as LangChain tools that any LangGraph agent can call.
from langchain_mcp_adapters.client import MultiServerMCPClientfrom langgraph.prebuilt import create_react_agentfrom langchain_anthropic import ChatAnthropic
client = MultiServerMCPClient({ "galois-edge": { "url": "http://lab-pi.tail-1234.ts.net:8767/mcp", "transport": "streamable_http", }})
tools = await client.get_tools()agent = create_react_agent( ChatAnthropic(model="claude-opus-4-7"), tools,)
result = await agent.ainvoke({ "messages": [("user", "List instruments and read voltage on the DMM.")]})Tool names from the daemon (e.g. keithley_2400__source_voltage) become the LangChain tool names — same shape, same input schema.
LlamaIndex
Section titled “LlamaIndex”LlamaIndex exposes MCP servers via BasicMCPClient and McpToolSpec.
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec
client = BasicMCPClient("http://lab-pi.tail-1234.ts.net:8767/mcp")tool_spec = McpToolSpec(client=client)
tools = await tool_spec.to_tool_list_async()# Pass tools to FunctionAgent / ReActAgent / a workflow.Verifying the connection
Section titled “Verifying the connection”Before wiring an agent, confirm the daemon is reachable. The MCP Python SDK ships a streamable-HTTP client suitable for one-shot smoke tests:
import asynciofrom mcp import ClientSessionfrom mcp.client.streamable_http import streamablehttp_client
async def main(): async with streamablehttp_client("http://lab-pi.tail-1234.ts.net:8767/mcp") as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() tools = await session.list_tools() print(f"Connected; {len(tools.tools)} tools available") for t in tools.tools[:5]: print(" -", t.name)
asyncio.run(main())Or with curl:
curl -X POST http://lab-pi.tail-1234.ts.net:8767/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'A 200 response with a JSON-RPC result body means the listener is up; a connection failure means MCP_ENABLED=false, the wrong port, or a network path issue.
Where to go next
Section titled “Where to go next”- MCP Server Reference — transport, tool taxonomy, JWT auth, hot-plug.
- Connecting Instruments — what populates the dynamic per-instrument tool surface.
- Configuration —
MCP_ENABLED,MCP_PORT,MCP_PATH.