Connect an MCP client
How to point any MCP-compatible client at the SnapLogic MCP Server and authenticate using HTTP Basic.
The SnapLogic MCP Server uses the MCP Streamable HTTP transport. Point your client at your region's
Tectonic host and the path that matches your use case (/mcp,
/mcp/platform, or /mcp/pygen), then add a standard
Authorization header. For the full tool catalogue, base URLs, and
troubleshooting, see SnapLogic MCP Server tools.
Authentication
All MCP API requests must include an Authorization header. Use HTTP
Basic with your SnapLogic login credentials:
Authorization: Basic base64(user:password)
An MCP client config is long-lived. HTTP Basic does not expire, which makes it the reliable default for a persistent client configuration.
Client configurations
Claude Code (CLI)
claude mcp add --transport http platform-mcp \
https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp/platform \
--header "Authorization: Basic $(printf '%s' 'USER:PASSWORD' | base64)" \
--scope project
Claude Code (.mcp.json, project-scoped)
{
"mcpServers": {
"platform-mcp": {
"type": "http",
"url": "https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp/platform",
"headers": { "Authorization": "Basic BASE64_OF_USER_COLON_PASSWORD" }
}
}
}
Claude Desktop
Use the same JSON entry as above in
claude_desktop_config.json (type: http with a
headers object). No mcp-remote shim is needed
for a header-authenticated HTTP server.
Python MCP SDK
import base64
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
url = "https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp/platform"
headers = {"Authorization": "Basic " + base64.b64encode(b"USER:PASSWORD").decode()}
async with streamablehttp_client(url, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print(len(tools.tools), "tools")
cdn.elastic.snaplogic.com for US tenants and
cdn.emea.snaplogic.com for EU. Swap /mcp/platform
for /mcp/pygen or /mcp as needed.Choosing which tools you get: the X-MCP-Tool-Profile header
The tool list is not fixed. Two independent settings decide what
tools/list returns, and the result is the intersection of both:
- The path you connect to —
/mcp(all registered tools),/mcp/platform, or/mcp/pygen. Set this in your client config. - The profile you request — an optional
X-MCP-Tool-Profilerequest header. With no header you get the environment's configured default.
Built-in profiles:
| Profile | What it selects | Use it when |
|---|---|---|
dev |
No filtering — every registered tool. | You want the full surface, including tools tagged
NEW. |
snapcode |
Platform + PyGen + common, minus everything tagged
NEW. |
You want the stable, released surface only. |
platform |
Platform + common. PyGen excluded. | Control-plane work; no pipeline-generation helpers. |
pygen |
PyGen + common. | Pipeline-generation helpers only. |
tools/list is not a permission boundary — what a
caller can actually do is determined by their SnapLogic credentials and org RBAC. Do
not treat a narrow profile as a security control.Add the header alongside Authorization in your client config. For
example, to request the dev profile:
Claude Code (CLI)
claude mcp add --transport http platform-mcp \
https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp \
--header "Authorization: Basic $(printf '%s' 'USER:PASSWORD' | base64)" \
--header "X-MCP-Tool-Profile: dev" \
--scope project
.mcp.json
{
"mcpServers": {
"platform-mcp": {
"type": "http",
"url": "https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp",
"headers": {
"Authorization": "Basic BASE64_OF_USER_COLON_PASSWORD",
"X-MCP-Tool-Profile": "dev"
}
}
}
}
Verify the tool surface in your environment
Run two tools/list calls — one with and one without the
X-MCP-Tool-Profile header. If the counts match, the header is not
reaching the server.
import base64, json, requests
AUTH = "Basic " + base64.b64encode(b"USER:PASSWORD").decode()
URL = "https://cdn.elastic.snaplogic.com/api/1/rest/public/platform_mcp/mcp"
def count(profile=None):
h = {"Authorization": AUTH, "Content-Type": "application/json",
"Accept": "application/json, text/event-stream"}
if profile:
h["X-MCP-Tool-Profile"] = profile
init = {"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": "2025-03-26", "capabilities": {},
"clientInfo": {"name": "probe", "version": "1.0"}}}
r = requests.post(URL, headers=h, json=init, timeout=45)
h["Mcp-Session-Id"] = r.headers["Mcp-Session-Id"]
requests.post(URL, headers=h,
json={"jsonrpc": "2.0", "method": "notifications/initialized"},
timeout=45)
r = requests.post(URL, headers=h,
json={"jsonrpc": "2.0", "id": 2, "method": "tools/list"},
timeout=45)
n = 0
for line in r.text.splitlines(): # responses are SSE-framed
if line.startswith("data:"):
n += len(json.loads(line[5:]).get("result", {}).get("tools", []))
return n
print("default:", count(), " dev:", count("dev"), " snapcode:", count("snapcode"))
Known issues
- An unknown profile name silently gives you the environment default. The
request is not rejected — a client asking for a profile the environment has not
defined ends up with a wider surface while believing it is sandboxed. Profile names
are per-environment (
MCP_TOOL_PROFILES_JSON); confirm yours exists. The server logs a warning naming the profiles it knows. - Send the header on every request, not just
initialize— the profile is resolved per request, alongsideMcp-Session-Id. - Requires Tectonic 4.45 or later. On an earlier build the proxy does not forward the header downstream, so you silently receive the environment default with no error.
- Value is sanitized and capped at 64 characters, and dropped entirely if it contains CR/LF. Profile names are matched case-insensitively.
- STDIO/local runs ignore it — there is no HTTP request to carry a header.
Local development defaults to
dev(all tools) unlessMCP_DEFAULT_TOOL_PROFILEis set. export_projectis in the default surface and it writes. Despite the name it is not read-only: it stages a ZIP into the project via SLDB and returns a presigned URL valid for about an hour that requires no authentication. WithAccountassets opted in, that URL carries encrypted credential material. Treat it as a write operation, not a query. (export_pipeline, by contrast, is a read-only SLP export.)