ATaC is a tool runtime where agents write graph code that calls back into registered tools
Record and Replay AI Agent Trajectories.
At a glance
- What is it?
- ATaC is a small Python package whose job is to give an agent one place to register tools and then write ordinary graph code that calls them. Local tools are declared with a LangChain decorator, MCP tools with the official adapter, and both go in through a single registration call. The details worth reading before you install are the Python 3.13 floor, the UI build that ships inside the wheel, and the two documentation bugs.
- Who is it for?
- ATaC fits a team already using LangGraph and MCP who wants tool registration in one place and graph code that stays ordinary Python rather than framework glue. It does not fit anyone on a Python older than 3.13, since that is the only declared and classified version, and it does not fit someone who expects the examples in the readme to run as written.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 171 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The repository description and the readme describe different projects
The one-line repository description says this records and replays AI agent trajectories.
The readme describes something else. ATaC is a tool runtime for agent workflows, and its stated purpose is to let an agent organise registered tools into workflows as code, gradually turning repeated tasks into reusable graph workflows.
Nothing in the visible documentation covers recording or replaying trajectories. There is no store of runs, no format for a trace, and no command to re-execute one.
For anyone evaluating the project this matters more than a wording quibble, because the description is what appears in search results and in dependency listings. If you arrived looking for an evaluation harness for agents and found a runtime instead, the mismatch is real rather than a translation artefact: the Chinese section of the readme says the same thing as the English one.
The package metadata agrees with the readme rather than the description, describing it as a graph-first tool runtime for agent workflows at version 1.0.3.
Registration is one call, and both tool kinds use their official decorators
The design is that ATaC does not define its own tool abstraction, and that is the shortest way to describe it.
Local tools use the native LangChain decorator, so a tool is a normal function with a docstring and a type annotation. MCP tools use the official multi-server MCP client from the LangChain adapters package. Both are then registered through a single call on the service, which is the only ATaC-specific line in the whole integration.
from atac import get_serviceThe runtime is reached either through a service instance you construct or through a module-level accessor, and the two are used interchangeably in the examples: the integration snippet constructs one to register tools, while graph code calls the accessor.
That indirection is what lets the graph file stay decoupled. A graph module should not have to know how the tool was declared, so it looks a tool up by name at call time.
The documented advice is to use the native decorators rather than wrapping tools in an ATaC-specific type, which means migrating an existing LangGraph project is mostly a matter of moving registration into one place.
Graph code calls back into the runtime by tool name
The second half of the integration is a graph node that asks the runtime to run a tool.
def run_bash(state: dict) -> dict:
output = get_service().tool_call("bash", {"command": f"echo {state['who']}"})
return {"output": output}The call takes a tool name and a dictionary of arguments, and returns the tool result into the graph state under a key of your choosing. There is no typed handle and no import of the tool module inside the graph, which is what lets an agent author or edit graph code without touching the tool definitions.
The graph itself is built with LangGraph. A state schema is declared, nodes are added, edges are wired from the start node to the first node and from there to the end, and the result is compiled. In the documented example that is one node and two edges, which is the smallest thing that proves the loop closes.
Execution has the same string-addressed shape. A graph is loaded by its import path and a call name, `run_graph` taking a target such as a module path with the builder function after a colon, plus the input state dictionary. So both halves of the contract are strings at the boundary: tool names inside a run, graph paths outside one.
The English example is missing the return the Chinese example has
The readme is published twice, once in Chinese and once in English, and the two versions of the same snippet do not match.
In the Chinese section, the sample bash tool runs the command, raises a runtime error with the captured standard error if the return code is non-zero, and then returns the captured standard output with trailing whitespace stripped.
The English section has the same imports, the same decorator, and the same failure branch, and then stops. The final line returning the output is absent.
A reader copying the English snippet gets a function that raises on failure and returns nothing on success, so the graph node downstream receives an empty value. The type annotation still promises a string, so nothing complains until the output is used.
This is the kind of defect worth reporting rather than working around, and it is worth knowing which version to trust if you are reading both. The tool registration, the accessor, and the graph construction are identical in the two; only the return differs.
Everything else in the two sections matches, including the recommended registration path and the quick start.
The example links point at a developer's home directory
The examples list is the second documentation problem, and it affects every entry.
Five files are listed as runnable examples: a bootstrap module, a tool-call graph, an agent workflow, a shell script to run a graph demo, and an SDK runner. Each link is an absolute filesystem path under a specific developer's home directory rather than a repository-relative path.
So none of them resolves in a clone. A reader has to guess that the tail of each path maps to the example directory in the repository, which happens to be discoverable because the repository does contain an example directory, split into a demo part and a langgraph part.
The file names themselves are informative about the intended usage. The bootstrap module is where the service is constructed and tools registered. The tool-call graph is the minimal one from the readme. The agent workflow is presumably the case where an agent writes the graph rather than a human. The SDK runner is the programmatic counterpart to the shell script.
For a package whose whole pitch is reuse of graph workflows, having the reference implementations unreachable from the documentation is the gap most likely to slow adoption.
A Python 3.13 floor, a FastAPI dependency, and a CLI entry point
The packaging is short enough to read in one sitting, and three details stand out.
The first is the interpreter. The project requires Python 3.13 or newer and classifies only 3.13, and the repository carries a `.python-version` file at the top level, so the intended interpreter is pinned rather than merely recommended. That is a defensible choice for a new package and an adoption constraint for anyone on 3.12.
The second is the dependency list. Nine runtime dependencies, of which two stand out: FastAPI and uvicorn are present in a package whose user-facing entry point is a command line tool, and the Model Context Protocol SDK plus the LangChain core and MCP adapters form the other cluster. Pydantic, PyYAML, and Click complete the set.
The third is the entry point, declared as `atac` mapping to a module-level CLI function, with a src layout and package data that includes a built UI directory and its assets.
The test configuration is also opinionated: the source directory is added to the path so tests import the package without installing it, and the asyncio mode is set to strict rather than auto, which means every async test declares its own marker instead of being collected implicitly.
The packaged audit UI is built by pnpm and shipped inside the wheel
The Makefile is where the build story becomes visible, and it is short.
uv tool install atacFour working targets: lint runs Ruff through uv, test runs pytest through uv, ui-build runs a package manager build inside the audit UI directory, and package-build runs the build backend. A fifth target, release-check, chains all four in order.
That third target is the notable one. The audit UI is a separate front end built with pnpm, and the wheel's package data lists the built UI directory and its assets, so the compiled interface is bundled into the Python distribution. Installing ATaC therefore means shipping a wheel that contains both Python and a built web asset tree, and building it means having a Node package manager available in addition to Python tooling.
Two details make the release path safer than it looks. Lint and test are separate steps rather than part of the build, so a packaging failure cannot hide a test failure. And the release check runs the UI build before the package build, which means a broken front end fails the chain before a wheel is produced from stale assets.
The lint configuration is a curated rule set rather than a default one: a handful of rule families are selected, with two specific rules disabled, one of them the line-length check.
Editorial conclusion
ATaC fits a team already using LangGraph and MCP who wants tool registration in one place and graph code that stays ordinary Python rather than framework glue. It does not fit anyone on a Python older than 3.13, since that is the only declared and classified version, and it does not fit someone who expects the examples in the readme to run as written. Before adopting it, check the interpreter version, expect a Node toolchain as well as Python because the packaged UI is built with pnpm, and fix the example code yourself rather than copying it, since the English snippet is missing the line that returns the command output.
Frequently asked questions
What is ATaC and what does it do?
It is a Python tool runtime for agent workflows with three capabilities: unified registration of local agent tools and MCP tools in one runtime, agent-authored graph workflows that reuse those registered tools, and one-command execution of those graphs. Registered tools are called back from graph code by name, so tool definitions and workflow code stay separate.
How do I install ATaC?
Run uv tool install atac. The project requires Python 3.13 or newer and classifies only 3.13, and the repository pins the interpreter in a .python-version file, so anything older will not be supported.
How do I register tools with ATaC?
Declare local tools with the native langchain_core.tools.tool decorator and MCP tools with the official MultiServerMCPClient from the LangChain MCP adapters, then register everything through a single service.register_langgraph_tools call. ATaC does not define its own tool abstraction.
How does graph code call a tool registered with ATaC?
Through a string name and an argument dictionary, for example get_service().tool_call with the tool name and a dict of arguments, returning the result into graph state. Graphs are built with LangGraph by declaring a state schema, adding nodes and edges, and compiling, and a graph is executed by its import path and builder function name.
What does the ATaC release check run?
Four steps in order: Ruff through uv, pytest through uv, a pnpm build of the audit UI, and then the package build. Because the UI output is declared as package data, the built front end ships inside the Python wheel, so the release chain needs a Node package manager as well as Python tooling.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/atac-team-atac)