Skip to main content

Atoti Intelligence SDK

This is part of the Atoti Intelligence SDK offer.
An Atoti Server can connect out to other Model Context Protocol (MCP) Servers, typically the other Atoti Servers of the same deployment, each exposing its own cube. It then uses their tools as its own, both in chat and on its own MCP endpoint. This page covers the behavior shared by both SDKs; declaring a connection is SDK-specific.
To follow along, first declare a connection: see Connect to other MCP Servers in Java or Connect to other MCP Servers in Python. Both assume the Atoti MCP Server is already set up; see How to set up the Atoti MCP Server.Connecting to other MCP Servers requires the Atoti Intelligence Extension tier, which itself requires Essentials, like the rest of Atoti’s MCP surface.

What does connecting to other MCP Servers provide?

  • A chat user on one Atoti Server can query cubes hosted on another, without duplicating tool logic across servers
  • Remote tools appear alongside local cube tools and custom tools, wherever tools are offered to the model
  • The remote cube’s own role-based data restrictions stay in force for every remote call
  • Remote tools are reported on this server’s own MCP endpoint too. One server therefore shows an external MCP client the tools of the whole deployment, each client told what its own credential can reach

Which authentication modes are available?

Each connection names its own mode, in its own declaration.
none is the default, and deliberately so. Declaring a connection says where a server is, never that it belongs to this deployment, and nothing else in the configuration can say it either. Writing atoti-jwt is that assertion, so make it only for servers under the same ownership: the minted token is accepted by every server sharing the deployment’s signing key. A third party given one holds a working credential for the whole deployment, in the name of a real user.
atoti-jwt mints its token from the authenticated principal rather than replaying one, so it works whichever way the calling user authenticated: Basic, JWT, OIDC, or LDAP. Both servers must share the signing key; see Java or Python. pass-through replays either kind of token this server accepts (an Atoti JWT, or an OAuth 2 access token). This brings two limits. A user who authenticated with Basic or form login presents no bearer token, so the call carries no credential and is rejected. A replayed OAuth 2 token also keeps the audience it was issued for, so the remote server accepts it only when configured to trust tokens issued for this server. A connection that runs the remote server as a local process, over standard input and output, identifies itself by being that process, so it accepts no authentication mode at all.

Why does the calling user’s identity carry through?

Every mode derives the outgoing credential from the calling user’s security context, evaluated on the caller’s own thread at the moment of the call. A remote tool therefore always runs under the identity of whoever triggered it. This is what keeps the remote cube’s role-based data restrictions in force: a user denied a measure or a member on the remote server gains nothing by asking through chat. Listing a remote server’s tools is an authenticated call of the same kind. Its result is cached per calling identity (the username together with its authorities), rather than once for the whole server. A remote server that varies its tool list by role therefore never has one user’s list served to another.

How are remote tools named?

Atoti names every remote tool <connection name>_<tool name>; the server’s own tools stay unprefixed. The prefix matters because every Atoti Server exposes the same cube tools. Unprefixed, a second connected server’s tools collide with the first one’s, and only the first occurrence of a name survives. Each remote tool’s description also states which server it runs on. The connection name is first normalized to the alphabet tool names may use: a hyphen becomes an underscore, and anything else outside letters, digits and underscores is dropped. fo ficc/server:1 becomes foficcserver1, and the run_mdx_query tool of a pnl-server connection becomes pnl_server_run_mdx_query. Two rules follow from that alphabet, and from the 64-character limit most model providers impose on a tool name:
  • Two names differing only by dropped characters normalize to the same prefix, for example pnl-server and pnl_server. Atoti checks the prefixes at startup and refuses to start rather than silently drop a server’s tools. The same check catches one name declared twice. Each transport has its own set of connections, and names are not compared across transports.
  • When a prefixed name exceeds 64 characters, the prefix is kept whole and the tool’s own name is shortened. A short hash is appended so two shortened names never collapse into one. A connection name of no more than 53 characters always keeps its prefix. Past that, the prefix is shortened instead, and getConnectedServers then reports no toolPrefix for that connection rather than one its tools do not carry.
The scheme can be replaced; see Connect to other MCP Servers in Java.

What does the getConnectedServers chat tool do?

A deployment of several connected servers gives the model tools from several cubes, and nothing in those tools says which server each one reaches. getConnectedServers closes that gap: it lists the server the chat runs on, then every connected server, whichever transport declares it. The model calls it to name the servers available, to attribute an answer to one of them, or to check that one is up. It requires the Atoti Intelligence Extension tier, like the rest of this page, and can be withheld; see Connect to other MCP Servers in Java.
getConnectedServers puts deployment-internal addresses into the chat prompt. Those addresses reach the model provider, and the model can repeat them to any user allowed to chat, whatever their role. Atoti registers the tool for chat only, never on this server’s own MCP endpoint.

What does the tool report?

The example below comes from a server named fo-ficc-server, declaring the connections pnl-server and sandbox, with sandbox down:
An entry describes a server; it is not a way to call one. The model reaches a connected server through that server’s own prefixed tools, never through the reported URL. The server running the chat always comes first, marked current: true, and is the only entry with no toolPrefix. This is what tells the model that its unprefixed tools are this server’s own. A server that declares no connection still reports itself, so a question about the available servers never gets an empty answer. Its url is the address the asking client used, up to and including the context path, read from the request being served rather than from configuration. Two clients of one server, one direct and one through an ingress, are therefore each told the address they used. Behind a reverse proxy the server has to be told to trust the forwarded headers, or the reported address is the proxy’s back-end hop; see Connect to other MCP Servers in Java. A connected server is reported with the URL its connection was declared with, whole and unchanged, with no MCP endpoint appended: url: https://pnl.example.com is reported as https://pnl.example.com, not https://pnl.example.com/mcp. What the report shows is the address a person recognizes the server by, and can open. Reachability is established per call and per user. The tool pings every connected server with the calling user’s own credential, exactly as a remote tool call does, so a server is reported reachable: false when it is down, when it rejects that credential, or when the user has no access to it. Such an entry keeps its name and its URL, so the model can say which server is unavailable. The pings run concurrently and the whole sweep is bounded to ten seconds, so an unreachable server costs a chat turn a bounded wait rather than its transport’s full request timeout.

How do remote tools appear on this server’s own MCP endpoint?

On their own, alongside the local cube tools, for every client that connects. There is nothing to enable and no identity to configure. No LLM is required either: an LLM is only required for chat. Each client is told the tools its own credential can reach. An MCP client authenticates to this server first (an unauthenticated request is answered with a 401 that starts the OAuth 2.1 flow), so by the time it asks for tools there is a real user to ask on behalf of. Two clients logged in as different users are therefore told different things. Calling a remote tool through this endpoint runs it as the calling user, exactly as from chat.

How resilient is a connection to an unreachable remote server?

An unreachable remote server costs only its own tools. Atoti lists every connected server separately, so one that is down or that rejects the credential it received leaves the other servers’ tools untouched. The failure is not cached: that server is retried on the next request, and its tools reappear as soon as it answers. An unreachable peer never stops this server from starting, and never keeps its MCP endpoint from answering.