CrewAI tool for EnigmAgent — agents resolve {{PLACEHOLDER}} secrets at the call boundary so LLMs never see real API keys
Project description
crewai-tools-enigmagent
A CrewAI crew of three agents — researcher, writer, GitHub-poster — needs a
GITHUB_TOKENto publish the final post to a private repo. CrewAI's recommended pattern is to attach the token to the Agent's LLM config, or pass it through the task description. Both options bake the real token into the prompt context that travels through every model call. If any of those LLM calls is logged, traced, or shipped to a managed provider, the token is now in someone else's database.
crewai-tools-enigmagent is the alternative.
Your crew agent emits {{GITHUB_TOKEN}}. The placeholder rides through the LLM context, through the tool input, through every CrewAI trace. Only when the agent calls EnigmAgentTool does the placeholder get swapped for the real ghp_... value — locally, against an AES-256-GCM encrypted vault, and only if the requesting origin matches the secret's bound domain.
pip install crewai-tools-enigmagent
In another terminal, next to your crew:
npx enigmagent-mcp --mode rest --port 3737
That's the install. The Python package talks to the local EnigmAgent REST server over loopback; secrets stay in the encrypted vault on disk.
Star the main project if you've ever pasted a token you regretted.
The problem (in CrewAI terms)
When you give a CrewAI agent a credential, you have three options and all three leak:
| Option | What happens |
|---|---|
Put the token into the Agent's goal or task description |
It lands in every LLM call the agent makes, in the trace, in the provider's logs |
| Bake the token into a custom tool at construction time | The agent can call the tool with arbitrary inputs and exfiltrate the value indirectly |
| Inject via env var read inside the tool | Works, but you have one long-lived plaintext token sitting in os.environ for the whole crew run |
crewai-tools-enigmagent is option D. Your tasks, your prompts, your traces all carry only {{PLACEHOLDER}} strings. The real value is resolved at the boundary, by a process the model cannot see, against a vault on the user's machine.
How it works
┌──────────────────┐ emits {{GITHUB_TOKEN}} ┌─────────────────────┐
│ CrewAI Agent │ ───────────────────────▶ │ EnigmAgentTool │
│ (any LLM) │ + origin parameter │ ._run(...) │
└──────────────────┘ └──────────┬──────────┘
│ HTTP POST /resolve
▼
┌─────────────────────────┐
│ EnigmAgent │
│ validates origin, │
│ decrypts AES-256-GCM │
│ → ghp_xxx │
└──────────┬──────────────┘
│ real value
▼
┌─────────────────────────┐
│ Agent uses value in │
│ next tool / API call │
└─────────────────────────┘
The model emits a placeholder name + the destination origin. The placeholder lives in the prompt and the trace. EnigmAgentTool._run() asks the local EnigmAgent REST server to swap it for the real value — but only if the request's origin matches the domain that secret was bound to. Wrong domain → the resolver refuses.
Usage
from crewai import Agent, Crew, Task
from crewai_tools_enigmagent import EnigmAgentTool
enigmagent = EnigmAgentTool()
github_op = Agent(
role="GitHub Operator",
goal=(
"Open a GitHub issue on https://api.github.com using the credentials "
"stored under the placeholder GITHUB_TOKEN. "
"Always call the EnigmAgent tool with placeholder='GITHUB_TOKEN' and "
"origin='https://api.github.com' to retrieve the token; never paste "
"the raw token into your reasoning."
),
backstory="You operate at the call boundary — secrets stay in the vault.",
tools=[enigmagent],
)
task = Task(
description="Open issue 'CI is red' in repo Agnuxo1/example.",
expected_output="The URL of the created issue.",
agent=github_op,
)
crew = Crew(agents=[github_op], tasks=[task])
result = crew.kickoff()
The agent's reasoning now contains only the placeholder name GITHUB_TOKEN and the origin https://api.github.com. The plaintext ghp_... exists for the duration of the upstream HTTP call and nowhere else.
Custom client config
from crewai_tools_enigmagent import EnigmAgentClient, EnigmAgentTool
client = EnigmAgentClient(
base_url="http://127.0.0.1:9999", # custom port
timeout=5.0,
shared_secret="my-loopback-token", # sent as X-EnigmAgent-Auth header
)
enigmagent = EnigmAgentTool(client=client)
To run the EnigmAgent REST server with a shared secret:
npx enigmagent-mcp --mode rest --port 3737 --auth my-loopback-token
The vault
This package is a thin CrewAI wrapper. The real work — Argon2id key derivation, AES-256-GCM encryption, origin binding, audit logging — lives in EnigmAgent, the npm package that backs it.
# Create a vault interactively (one-time)
npx enigmagent-mcp --new-vault ./my.vault.json
# Add a secret bound to a domain
npx enigmagent-mcp --vault ./my.vault.json --add GITHUB_TOKEN ghp_xxx --origin https://api.github.com
# Run as REST server next to your CrewAI app
npx enigmagent-mcp --mode rest --port 3737 --vault ./my.vault.json
Security model
- Loopback only. The REST server binds to
127.0.0.1. Only processes on the same machine can reach it. - Origin binding. Every secret is bound to one or more origins (e.g.
https://api.github.com). Resolving a secret for a different origin is refused. - Argon2id + AES-256-GCM. The vault file is encrypted at rest with a passphrase-derived key.
- No plaintext in prompts or traces. The agent's LLM context contains only
{{PLACEHOLDER}}strings. Resolved values exist only inside the tool's return value, briefly, in the agent's local memory. - Optional shared secret. Pass
--authto require anX-EnigmAgent-Authheader on every REST call, so unauthorised local processes can't query the vault.
Full threat model: EnigmAgent THREAT_MODEL.md
Compatibility
- Python: 3.10, 3.11, 3.12, 3.13
crewai >= 0.40pydantic >= 2- Any LLM provider supported by CrewAI (OpenAI, Anthropic, Mistral, Gemini, local via Ollama)
Roadmap
-
EnigmAgentSubstituteToolthat takes a templated string with multiple{{PLACEHOLDERS}}and substitutes them all in one call - Optional integration with CrewAI Flows to inject secrets at flow-state boundaries
- Upstream proposal to
crewai-toolsonce this package has real users
PRs welcome.
License
MIT (c) 2026 Francisco Angulo de Lafuente
Links
- Main project: github.com/Agnuxo1/EnigmAgent
- npm package: enigmagent-mcp
- LangChain port: github.com/Agnuxo1/langchain-enigmagent
- Issues: github.com/Agnuxo1/crewai-tools-enigmagent/issues
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file crewai_tools_enigmagent-0.1.0.tar.gz.
File metadata
- Download URL: crewai_tools_enigmagent-0.1.0.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3429b651b2431b5b150b81ec385a2cc8e6465134ee77d64de16c53e60bb250f2
|
|
| MD5 |
fc81bce91a4d89248bfd9aacd1806a28
|
|
| BLAKE2b-256 |
509c788ff440afb4c8a8252af10cb1e708fbe0aa99d3f0746b161c1848fa592f
|
File details
Details for the file crewai_tools_enigmagent-0.1.0-py3-none-any.whl.
File metadata
- Download URL: crewai_tools_enigmagent-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fda8678ad85cfa24c2541979709d36f8ab48378adecc8e1b560b0920a6d65bf
|
|
| MD5 |
74fc9705353d2b65e60cedac3ce923ac
|
|
| BLAKE2b-256 |
9d30cc2c1a102e918715979939302af8c4e300a4c3e395464e04eb961a7f799d
|