Composing environments
Combine small environments into a multi env that an agent sees as one MCP server
A task often needs more than one system. Dana's email in
Creating your own environment asks for the Q3 numbers to go to Sam
Lee in finance, and nothing in the inbox says what Sam's address is; a contacts book does. Instead
of adding contacts to EmailEnv, you write a second small environment and compose the two.
A second environment
ContactsEnv follows the pattern of EmailEnv: one tool, contacts_lookup, and the three data
methods over its list of contacts:
import json
from pathlib import Path
from agentenv_protocol import (
AgentEnvEnvironment,
DataPart,
FilePart,
add_data,
environment_card,
get_data,
reset_data,
tool,
)
@environment_card(name="contacts")
class ContactsEnv(AgentEnvEnvironment):
def __init__(self) -> None:
self.contacts: list[dict] = []
@reset_data
async def _reset(self) -> None:
self.contacts.clear()
@add_data
async def _add(self, parts: list) -> None:
for part in parts:
if isinstance(part, DataPart):
self.contacts.extend(part.data.get("contacts", []))
elif isinstance(part, FilePart):
seed = Path(part.file.uri.removeprefix("file://"))
self.contacts.extend(json.loads(seed.read_text()).get("contacts", []))
@get_data
async def _state(self) -> list:
return [DataPart(data={"contacts": self.contacts})]
@tool(name="{environment_name}_lookup")
async def lookup(self, name: str) -> dict:
"""Find contacts whose name contains the given text."""
n = name.lower()
return {"contacts": [c for c in self.contacts if n in c["name"].lower()]}
if __name__ == "__main__":
ContactsEnv().serve()With a copy of the Dockerfile in contacts/, you register it with the command that
Register it uses for email:
agent-env env mcp-server put --id contacts --dockerfile contacts/DockerfileCompose the two
A multi env pins its children by id and version and gives them one name. It adds no server code
of its own, so each child stays a small server that you build and version on its own. This
command composes email and contacts into office:
agent-env env multi put --id office --name office \
--mcp-server email:1 --mcp-server contacts:1Fetching MCPServerEnv: id=email version=1...
Found: id=email version=1
Fetching MCPServerEnv: id=contacts version=1...
Found: id=contacts version=1
Creating MultiEnv...
Created MultiEnv: id=office version=1The stored multi env holds the two pinned references and the name. email:1 stays at version 1
even after you put email again. To take a newer child, you put office again, which appends a
new version of office.
You deploy office like any other env, with the command from
Deploy it:
agent-env env deploy --id officeDeployed!
Instance ID: office-jk24hzlq
Env MCP Url: http://localhost:61854/mcp
Env Gateway Url: http://localhost:61854
Env DB Web Url: http://localhost:61855/
Env DB MCP Url: http://localhost:61856/mcp
Expires At (UTC): 2026-09-29 09:25 UTCEvery env deploys in a topology, the shape its instances take. A single env such as email can
be registered in any of them, as Choose the environment's
topology covers, and a multi env always takes the default, the
gateway topology: each child runs in its own container, side
by side behind one gateway. An agent connected to the instance's MCP URL sees one MCP server named
office, with every child's tools in one list: email_search, send and contacts_lookup.
Last updated on