transformatron
Run and develop Maltego transforms through a local transform server that can be managed, inspected, and fed scaffolds from API specs.
Start, stop, restart, and check status/logs of the transform server, including HTTPS mode after generating certs.
List all advertised transforms and entity types, and get full detail for a specific transform.
Run a transform against an input entity with optional settings and timeout, then inspect returned entities (check the entity count, not just success).
Retrieve the seed URL and registration steps to connect the server to a Maltego desktop client.
Generate a self-signed certificate for HTTPS, which the Maltego client requires.
Scaffold new transform modules from a cURL command, OpenAPI spec, and/or sample JSON response.
All operations are available identically via MCP tools and the CLI.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@transformatronBuild a transform for urlscan.io's search endpoint, then run it and show me the entities"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
transformatron
Build Maltego transforms with a coding agent, against a real server.
Maltego transforms are small functions that take one entity (an IP address, a domain, a person) and return related entities, building up a graph. This project gives an agent the two halves it needs to build them: a local transform server it can drive — start, reload after an edit, run a transform and read back what it returned — and the authoring guidance and scaffolding to write the transform in the first place.
That combination is the point. An agent that can only write code guesses at what the API returns; an agent that can only run a server has nothing to run. Together they close the loop: scaffold from a spec, restart, run it, see the entities, fix what the spec got wrong.
Everything works two ways: an MCP server for agents that speak it, and a CLI for humans and any agent that can run commands. Both call the same code, so they behave identically.
Status: early. Verified on macOS and Linux: CI runs the full suite on Ubuntu and macOS with Python 3.13 and 3.14, and a daily job starts a live server on Ubuntu and runs the keyless transforms against their real APIs. The Maltego desktop client has been exercised on macOS only. Windows is not supported — the lifecycle relies on POSIX signals, and its
os.kill(pid, 0)liveness probe would terminate the server there. Not published to PyPI. Expect rough edges.
Requirements
Python 3.13+
uv for dependency management
A Maltego client — Maltego Desktop to actually use the transforms. Not needed to run the tests.
OpenSSL on
PATH(ships with macOS and most Linux distributions) — only for HTTPS certs.
Related MCP server: mcdev-mcp
Quick start
git clone https://github.com/hexacron/transformatron.git
cd transformatron
uv sync
uv run pytest -qThat verifies the control plane. Then go to Build your first transform — with an agent or by hand — and, if you have API keys to test against, Credentials.
Connecting to the Maltego desktop client
The desktop client refuses plain-HTTP transform servers, so HTTPS is not optional:
Only HTTPS (SSL/TLS) Transform Servers are allowed, but found: http://127.0.0.1:3000Note that this failure happens inside the client — the request never reaches the server, so nothing appears in the server log. An empty log alongside a client-side error is the signature of this problem, not a sign the server is broken.
Generate a self-signed certificate (
generate_certs).Trust it. This changes system trust, so the tool prints the command rather than running it:
sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain .transformatron/certs/cert.pemThat command is macOS-specific; on Linux, install the cert into your distribution's CA store.
Start the server with SSL (
server_restart(ssl=True)).In Maltego: Transforms → Transform Hub → add a local hub item, and paste the seed URL
https://127.0.0.1:3000/seed.Install the hub item, then right-click a matching entity to run a transform.
The server binds to 127.0.0.1 by default, which reaches a client on the same machine and nothing
else. host and port are set in transformatron.toml (see Naming). Binding a
non-loopback address such as 0.0.0.0 makes the server reachable from other machines, but two
things do not follow it:
The certificate from
generate_certscovers onlylocalhostand127.0.0.1, so a client dialling any other address rejects it. A remote client needs a certificate for the address it dials, placed at.transformatron/certs/cert.pemandkey.pem.The seed URL is built from the bind address. With
0.0.0.0it readshttps://0.0.0.0:3000/seed, which another machine cannot dial — substitute the machine's reachable address when registering the hub item.
Build your first transform
Assumes uv sync has run.
With an agent
Open a coding agent in the clone and start the server:
uv run python scripts/transformatron_cli.py startClaude Code picks up .claude/skills/maltego-transform-author/ from the clone; other agents read
AGENTS.md. Nothing to invoke — the guidance is already loaded. Then describe what you
want:
Build a transform for urlscan.io's search endpoint, then run it and show me the entities:
curl "https://urlscan.io/api/v1/search/?q=domain:example.com"
Here is the whole session, and what the agent is doing at each step.
1. It reads docs/transform-authoring.md first. Two SDK
behaviours fail silently — see Writing transforms — and the skill treats
reading those corrections as a precondition for writing code.
2. It scaffolds:
Scaffolded 'Urlscan' (urlscan):
Created files:
- server/transforms/urlscan/__init__.py
- server/transforms/urlscan/api.py
- server/transforms/urlscan/lookup.py
Transforms:
- Urlscan: Lookup (Phrase -> Phrase, GET /api/v1/search/)
No API key: the request showed no authentication.
Imports added to server/project.py.The input side is right: q carries a search query, so the transform takes a Phrase. The output
side is a placeholder — the scaffolder had a URL and no response, so it has nothing to map. Passing
--sample-response with real JSON fixes most of that up front, but only a live run settles it.
3. It restarts and confirms registration — restart, then list. The server only loads code
at startup, so an edit without a restart changes nothing. A transform missing from list almost
always means a missing import in project.py, which scaffold writes for you.
4. It runs the transform and reads the output:
State: COMPLETED (success)
Messages:
- Urlscan returned no mapped entities for this input
Entities (0):This is the step that makes the difference. COMPLETED (success) is not the answer — the
entity count is. Zero entities from a search that has results is a broken transform, and nothing
but the count says so.
5. It corrects the mapping and goes round again. urlscan returns a list of scans under
results[], each with a nested page object. Walk results[], map page.url to a URL,
page.ip to an IPv4Address, page.domain to a Domain and page.asn to an AS. Restart,
run, read the entities again. Two or three passes is normal.
6. It gates the result:
uv run python scripts/smoke_test_transforms.pyRuns every registered transform and fails on zero entities or an output type of NONE.
What you decide
The agent handles the mechanics. The judgement calls are yours:
Which API, and which endpoints deserve transforms.
How responses map to entities. Is
page.asnanASentity or aPhrase? That choice decides whether the graph can pivot on it.When it is actually done. The agent may accept one entity; you know the query should have returned twenty.
One habit is worth more than the rest: when an agent says a transform works, ask what the entity count was. That single question catches the failure mode this project exists to prevent.
If the MCP tools are missing
A fresh clone prompts once for approval of the transformatron MCP server and needs a session
restart to load it. Until then the agent falls back to the CLI, which does exactly the same things.
Seeing it shell out to transformatron_cli.py instead of calling run_transform is expected, not
a fault.
By hand
The same loop, driven yourself. Scaffold from a spec:
uv run python scripts/transformatron_cli.py scaffold \
--curl 'curl https://internetdb.shodan.io/8.8.8.8' \
--sample-response "$(curl -s https://internetdb.shodan.io/8.8.8.8)"Or write a module under server/transforms/ directly — this is the minimal shape:
# server/transforms/hello.py
from maltego.entities import Domain, IPv4Address
from maltego.model.context import MaltegoContext
from maltego.server import register_transform
@register_transform(display_name="Hello: IP to Domain", transform_set="hello")
async def ip_to_domain(input_entity: IPv4Address, context: MaltegoContext) -> list[Domain]:
"""Return a fixed domain, to prove the loop works."""
return [Domain(value="example.com")]Both annotations matter: IPv4Address declares the input type, list[Domain] the output. A bare
-> list registers the transform with output NONE and the client cannot route it.
A hand-written module needs its import in server/project.py, next to the existing ones
(scaffold does this for you):
from transforms.hello import * # noqa: F401,F403The server only loads what project.py imports, and the import must sit with the others at the
top — appending it to the end of the file puts it after the if __name__ == "__main__" block,
where it still runs but registers nothing you can see. A transform that never appears in list is
almost always this.
# Reload and confirm.
uv run python scripts/transformatron_cli.py restart
uv run python scripts/transformatron_cli.py list
# Run it, and check the entity count — not just the success state.
uv run python scripts/transformatron_cli.py run \
acme.new_maltego_integration.ip_to_domain maltego.IPv4Address 8.8.8.8
# Gate the whole set.
uv run python scripts/smoke_test_transforms.pyThen connect the desktop client and run it on a real graph.
Scaffolding from a spec
scaffold turns a cURL command or an OpenAPI document into a working module package: an api.py
client with the auth and validation wired up, one module per transform, and the import added to
server/project.py.
# From a cURL command, with a real response to infer output entities from.
uv run python scripts/transformatron_cli.py scaffold --service demo \
--curl 'curl -H "X-API-KEY: k" https://api.demo.com/v1/ip/8.8.8.8' \
--sample-response "$(curl -s -H 'X-API-KEY: k' https://api.demo.com/v1/ip/8.8.8.8)"
# From an OpenAPI spec — a URL, a path, or the document itself.
uv run python scripts/transformatron_cli.py scaffold --service demo \
--openapi https://api.demo.com/openapi.json
# POST operations are skipped unless named, because they may create or change data.
uv run python scripts/transformatron_cli.py scaffold --service demo \
--openapi ./demo-openapi.json --operation searchHostsWhat it works out:
The input. Whichever part of the request — a path segment, a query parameter, or a JSON or form body field — holds something that looks like an IP, domain, URL, email, hash or CVE, by name or by value. Failing that, a lone query parameter or the last path segment becomes a
Phrase. Other query parameters and body fields are kept and sent with every request.The method and body. A cURL command with
-d,--jsonor-X POSTscaffolds a POST that sends the body with the input substituted in.Authentication. A key-like header, a bearer token, or a key-like query parameter (
apikey,token, …) becomes aTransformSetting. A request with none of these scaffolds a client that needs no key. OpenAPI schemes it cannot express (OAuth2, basic) are reported, not guessed.The outputs. Every field of the sample response (or the OpenAPI response schema, with
$refs resolved) maps to an entity by name, or by value where the value is unambiguously an address, domain, URL or email. Nested objects and lists of objects are followed one level down; a response that is itself a list is walked record by record.
Anything it skipped or could not express — an operation with no input, a path parameter with no default, an unsupported auth scheme — is listed under Notes in its output.
Treat the result as a first draft. Every field is mapped, so expect to delete the ones an
investigator does not need, and expect the live API to disagree with the spec about which fields
are present, what a 404 means, and how errors are shaped. Run the transform, read the entities, and
correct the mapping — the loop above exists for exactly this. An existing service is never
overwritten unless you pass --force, so hand-written code that has absorbed those corrections is
safe from a stray re-run.
Writing transforms
Start with docs/transform-authoring.md.
The SDK ships its own authoring guidance in server/.agents/skills/, versioned with the package,
and this project does not restate it — a copy would go stale. But two of its examples are wrong in
ways that fail silently: the code looks right, the run reports success, and no entities come back.
docs/transform-authoring.md corrects those, then routes to the SDK guidance for everything else.
Need | Use |
Generate a starting point from an API spec |
|
Write or change transform code |
|
Run, reload, or inspect the server | The CLI or the MCP tools |
The loop
scaffolda module package from a spec, or add or edit a module underserver/transforms/.Import it in
server/project.py(from transforms.my_module import *);scaffolddoes this for you. The server only discovers whatproject.pyimports — this is the most common reason a new transform never shows up. (server/transforms/local/is the exception; see below.)server_restart— this is the reload path. It keeps the scheme the server is already running under, so an HTTPS server stays on HTTPS.list_transformsto confirm it registered, thenrun_transformto exercise it — and read the entity count, not the state.Delete the fields nobody needs, fix the mapping, and go round again. Finish with the smoke test.
Commands
Every operation is available as a CLI command and as an MCP tool. Both call the same
transformatron.operations module, so output is identical.
Purpose | CLI | MCP tool |
Start the server |
|
|
Stop it |
|
|
Reload after a code change |
|
|
Running, healthy, transform count |
|
|
Recent log output |
|
|
Advertised transforms and their types |
|
|
Detail document for one transform |
|
|
Advertised entity types |
|
|
Run one transform |
|
|
Seed URL and registration steps |
|
|
Self-signed cert for HTTPS |
|
|
Scaffold from cURL / OpenAPI |
|
|
CLI commands are prefixed uv run python scripts/transformatron_cli.py:
uv run python scripts/transformatron_cli.py restart
uv run python scripts/transformatron_cli.py list
uv run python scripts/transformatron_cli.py run <id> maltego.IPv4Address 8.8.8.8Pass settings with repeated --setting KEY=VALUE. --help works on any subcommand.
The CLI exits 0 on success and 1 when the operation failed: a start, stop, restart or certificate
generation that failed, a server it could not reach (so status exits 1 when nothing is serving,
and status || start works), a run that did not end in a success state, a refused scaffold, or an
unusable transformatron.toml (reported as one error: line on stderr, without a traceback). One
case does not count as failure: a run that completes with zero entities exits 0. The count in
the output is the signal — for a gate that fails on it, use the
smoke test.
The MCP tools read transformatron.toml on every call, as the CLI does, so an edit applies on the
next tool call with no MCP restart. A malformed file comes back as the tool's result rather than
stopping the MCP server loading.
Adding an operation? Put it in src/transformatron/operations.py and both front ends get it.
Credentials
In real use, an API key belongs in the Maltego client's transform settings — declared with
TransformSetting(auth=True, is_global=True), entered once, reused by every transform in the set.
Nothing needs configuring on the server for that path to work.
For headless runs there is no client to enter it into, so transforms fall back to the process environment. Copy the template and fill in what you have:
cp .env.example .env.env is read when the server starts and merged into its environment, so a key written once
survives restarts. The smoke test reads it too, which is the difference between a credential-gated
transform being exercised and being reported SKIP. In both, an exported shell variable beats the
file, and for the smoke test an explicit --setting beats both.
.env is gitignored. .env.example is the committed template and holds no values. This is a
development convenience: it puts keys in the server process's environment, visible to anyone who
can read ps. Restart after editing it — the environment is read once, at start.
Transforms you do not want to publish
Anything under server/transforms/local/ is gitignored and discovered automatically when the
server starts. Use it for integrations that should not be committed — an internal API, a
client-specific lookup, work in progress.
Both shapes are picked up: a package (local/<service>/ with an __init__.py), from which every
module except api.py is imported, and a single-file module directly under local/
(local/lookup.py). Names starting with _ are skipped at either level, so helpers can sit
alongside. The server log records what was loaded at startup — Local transforms loaded: ... —
so check logs when a local transform is missing from list.
It is discovered rather than imported by name, because a clone without the private modules must
still boot, and a static import of a missing module stops the server booting. The committed
examples alongside it keep their explicit imports in project.py.
Using it with a coding agent
An agent working in this repository can scaffold a transform from an API spec, restart the server to load it, run it against a real input, read back the entities it produced, and gate the whole set with the smoke test. That is the loop — and because every step reports what actually happened, the agent can tell a working transform from one that reports success and returns nothing.
What it reads:
Agent | Entry point |
Claude Code |
|
Codex, Gemini CLI, others |
CLAUDE.md points at AGENTS.md so the two cannot drift. Both route to
docs/transform-authoring.md for the SDK corrections.
Any agent that can run shell commands can drive the server through the CLI — no MCP required.
For Claude Code, the MCP server is registered at project scope in .mcp.json, so a fresh clone
picks it up automatically. It needs a session restart to load and prompts once for approval. That
approval is recorded in .claude/settings.local.json, which is per-machine and gitignored, so a
fresh clone prompts again — expected, not a bug. If the tools are unavailable, the CLI does
everything they do.
If you are modifying this project's own code under src/transformatron/, note that the MCP tools
run the version loaded when the session started, so your edits will not show up there until you
relaunch. The CLI always runs current code. AGENTS.md has the details — this does not affect
editing transforms under server/transforms/, nor transformatron.toml, which the tools read on
every call.
Using it from Python
The same operations work directly:
import asyncio
from transformatron import lifecycle
from transformatron.client import TransformClient
from transformatron.config import load_config
config = load_config()
lifecycle.start(config, ssl=True)
async def main() -> None:
client = TransformClient(lifecycle.resolve_config(config))
for transform in await client.list_transforms():
print(transform["name"])
asyncio.run(main())Run it with uv run python your_script.py.
The sample transforms
Several worked examples ship with the project. All are illustrative samples, not maintained
integrations — delete whichever you do not need, along with its import in server/project.py.
Example | Shows | Key |
| Registration data, no API key — pivotable output, redirect handling | no |
| Single module, no API key — the minimal shape | no |
| Multi-module, shared client, upstream field drift | yes |
| 404 as a verdict, silent key acceptance, tight quota | yes |
| Bearer auth, one response fanned out to several entities | yes |
| Minimal authenticated lookup | yes |
Start from ffraud.py if you are learning the shape. Start from ransomwarelive/ if your API
needs a key — it is documented in docs/ransomware-live.md and shows
the parts the simple example cannot: declaring one credential across a whole transform set,
keeping validation and error handling in a shared api.py, capping result sizes so a large
upstream response does not flood the graph, and normalising a schema whose field names differ
between endpoints. It needs a ransomware.live API key to run.
greynoise/ is worth reading for what a spec cannot tell you: an unrecognised key is accepted
silently, so a successful lookup is not evidence the key is valid; HTTP 404 is a real verdict
("never observed scanning") rather than a failure; and the free tier allows roughly 25 lookups a
week, which the smoke test can spend in one pass. Every one of those was found by running it, not
by reading the documentation.
RDAP
Against RDAP, the IETF protocol that replaced WHOIS. Registries serve it themselves, so the data is authoritative rather than scraped, and it needs no API key or registration — these run on a fresh clone.
Three transforms, all taking maltego.Domain:
Transform | Returns |
RDAP: Domain to Registration |
|
RDAP: Domain to Nameservers |
|
RDAP: Domain to Abuse Contact |
|
Start here if you are learning the shape. It is the closest of the samples to real investigative work: nameservers shared across unrelated domains are a standard way to group infrastructure, and the abuse contact is where a takedown request goes.
Two things it demonstrates that the simpler sample cannot:
Following a redirect safely.
rdap.orgholds no data; it answers with a 302 to whichever registry owns the TLD. The SDK's client setsfollow_redirects=Falsedeliberately, because following one silently would send your headers to a host the transform never chose. The hop is taken explicitly, once, and only to anhttpstarget.Reading a response off an exception. The client returns only 2xx and raises on everything else, so that 302 arrives as
MaltegoHTTPDataProviderInvalidResponserather than as a response you can inspect. Catching and logging it — the natural thing to write — yields zero entities and still reports success. The redirect target has to come off the exception'sresponse.
Note that example.com is registered through IANA's reserved-name process and publishes no abuse
contact, which is why the smoke test pins python.org for that transform.
ffraud
Against ffraud.com, a third-party IP reputation API this project has no affiliation with. It needs no API key, so it runs on a fresh clone.
Three transforms, all taking maltego.IPv4Address:
Transform | Returns |
ffraud: IP Reputation |
|
ffraud: IP to Network Details |
|
ffraud: IP to Abuse Contact |
|
Worth reading for the patterns it demonstrates: validating input_entity.value before
interpolating it into a URL, catching MaltegoException so an upstream outage degrades to an
empty result instead of a crash, and using .get() throughout because the API omits fields rather
than nulling them.
Caveat on the data: upstream coverage is uneven. A known Tor exit node returned
fraud_score: 0, risk: none with tor: false during testing, and sample responses carried
data_completeness: 0.5. Treat the scores as illustrative. To delete the sample, remove the
directory and its import in server/project.py.
Layout
src/transformatron/
operations.py server operations shared by both front ends — add new ones here
config.py TransformatronConfig — host, port, scheme, derived URLs and state paths
client.py async v3 protocol client; the run→poll→flatten state machine
lifecycle.py start/stop/restart/status/logs over a detached subprocess
certs.py self-signed certificate generation
envfile.py reads .env so headless runs can reach credentials
scaffold/ spec → module package: parser.py, generator.py, schema.py
mcp.py MCP front end
scripts/
transformatron_cli.py CLI front end
smoke_test_transforms.py runs every transform, fails on zero entities
server/ SDK-generated (`maltego-transforms start server --with-skills`).
.agents/, project.py and __init__.py are upstream-owned and excluded from
ruff so regeneration does not churn; the rest is linted.
project.py entrypoint; imports decide what gets registered
discovery.py finds and imports the modules under transforms/local/
transforms/ your transform modules go here — linted like the rest of the project
local/ gitignored; packages and single modules discovered at startup
.claude/skills/
maltego-transform-author/ authoring checklist; points at docs/, not a second copy
docs/
transform-authoring.md read before writing a transform
ransomware-live.md the authenticated worked example
ipinfo.md bearer auth, one response to several entities
upstream-sdk-issue.md draft bug report, not yet filed
tests/ unit tests for the control plane and the scaffolder
transformatron.toml server name, namespace, author, host, port. Gitignored; copy the .example.
.env API keys for headless runs. Gitignored; .env.example is the template.
.transformatron/ runtime state — PID, log, certs, recorded scheme. Gitignored.Gotchas
Things that cost real debugging time, recorded so they cost you less:
A transform missing from
list_transforms, or showing output typeNONE, almost always means a missing or untyped annotation. Input type comes from the parameter annotation, output from the return annotation. A bare-> listadvertises no output type and breaks client routing.Returning a
MaltegoGraphfrom anasynctransform silently yields zero entities. The run still reports success. In the SDK's__handle_async_result, a returned graph fails bothisinstancebranches and is dropped; the generator branch skips graphs too, so this affects every async path. Return a list —-> list[AS | ISP]still publishes every output type to discovery. This contradicts the SDK's own shipped guidance, which teaches the broken pattern; seedocs/transform-authoring.mdand the draft report indocs/upstream-sdk-issue.md.A success state is not proof a transform works. Check the entity count, or run
scripts/smoke_test_transforms.py.An API key entered in the client can stop reaching the transform after a seed re-import. The symptom is a transform reporting the key as missing while the client's settings field still looks populated. Re-importing after the seed URL changes — switching the server between HTTP and HTTPS does this — rewrites the transform definitions and orphans the stored global value. Clear the field, apply, then re-enter the key. See
docs/transform-authoring.mdfor the environment-variable fallback that avoids this during development.A successful lookup is not proof the API key is valid. Some upstreams accept an unrecognised key and answer normally — GreyNoise does — so there is no auth-failure path to catch and no signal that the key is wrong until a quota or a permission boundary exposes it. Verify a key against something that requires it, not against a call that happens to succeed.
The desktop client requires HTTPS (see above).
The server runs on port 3000 unless
transformatron.tomlsets another. The SDK's own skill scripts default to 8080 — pass--portwith your port if you invoke them directly.MALTEGO_SERVER_*environment variables take precedence over the values hardcoded inproject.py. That is how the lifecycle tools set host, port, and scheme without editing the generated file.The scheme is runtime state, recorded in
.transformatron/server.scheme.project.pydefaults tohttps, but the lifecycle tools override the scheme per start, so that file is the only way the control plane knows which scheme to address a running server over.
Development
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run ty check src tests scriptsserver/.agents/, server/project.py and server/__init__.py are excluded from linting: the SDK
owns them, and regenerating would churn against upstream. The rest of server/ —
transforms/, middleware.py, discovery.py — is your code and is linted like the rest of the
project, including anything scaffold generates.
Smoke-testing transforms
uv run python scripts/smoke_test_transforms.py
uv run python scripts/smoke_test_transforms.py --setting API_KEY=xxxRuns every registered transform against a sample input and fails on zero entities or an output
type of NONE — the failure modes that otherwise report success. Exits non-zero, so it works as a
gate. Run it after any change under server/transforms/.
Transforms needing credentials take them through repeated --setting KEY=VALUE, or from .env
(see Credentials). Four outcomes report SKIP rather than FAIL — a missing
credential, an upstream rate limit, a sample input the transform's own validation rejects, and an
input the upstream has no match for — so neither an unconfigured key nor a spent quota is mistaken
for broken code. A SKIP is not evidence a transform works; it means the gate could not judge it.
Add a TRANSFORM_SAMPLES entry when the per-entity-type sample does not suit a transform.
Transforms calling third-party APIs make live network requests, so a failure can mean an upstream
outage rather than broken code; check the reported message. Override the input with --value, or
check one transform with --transform <id>.
Naming
A fresh clone runs under the SDK's placeholder identity — New Maltego Integration,
acme.new_maltego_integration, Acme Corp. Change it before publishing anything: the namespace
is part of every transform's fully qualified ID, so two servers that share one collide in the
same client.
Copy the template and edit it:
cp transformatron.toml.example transformatron.toml[server]
server_name = "Acme Threat Intel"
namespace = "acme.threat_intel"
author = "Acme Corp"transformatron.toml is gitignored, so your identity does not travel with a fork. The CLI and
MCP server read it on every command or tool call and pass it to the transform server as
MALTEGO_SERVER_* variables, which outrank the values in server/project.py — so you never
edit project.py to rename a server. The same file sets host and port (see
transformatron.toml.example). Nothing needs relaunching to pick up an edit except the transform
server itself, which takes these values at start. Restart it to apply, then confirm:
uv run python scripts/transformatron_cli.py restart
uv run python scripts/transformatron_cli.py list # ids now carry your namespaceThe values in server/project.py remain as fallbacks for running that file directly.
License
MIT — see LICENSE.
Maltego is a trademark of Maltego Technologies GmbH. This is an independent project, not affiliated with or endorsed by Maltego.
Available Tools
12 toolsgenerate_certsA
Generate a self-signed certificate for serving over HTTPS.
Args: force: Overwrite an existing certificate pair.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses the 'force' parameter's overwrite behavior, which is useful, but it doesn't state what happens when a certificate pair already exists and force is false, nor where files are written. Some behavioral context is missing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise: two sentences plus a brief args block. The main purpose is front-loaded, and every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple with one parameter and has an output schema, so the description needn't explain return values. It covers the tool's purpose and the parameter adequately. Minor gaps remain about default behavior when force is false, but overall it is sufficiently complete for a straightforward tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage for the 'force' parameter. The description fully compensates by documenting its meaning and effect ('Overwrite an existing certificate pair'), which is the only explanation of this parameter's semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's specific action ('Generate') and resource ('self-signed certificate') with a context ('for serving over HTTPS'). This distinguishes it from sibling server management tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when to use (setting up HTTPS serving), but does not explicitly discuss alternatives or when not to use. No alternative certificate tools exist among siblings, so exclusions are unnecessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_seed_urlA
Return the seed URL and the steps to register this server with Maltego.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description's 'Return' clearly indicates a read-only operation with no side effects. It does not mention authentication or error handling, but for a simple zero-parameter getter this is adequate. The behavioral profile is transparent enough.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, focused sentence that conveys the essential information with no unnecessary words. It is well-structured and front-loaded with the key verb and resource.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter getter, the description is fully complete. It states exactly what is returned, and the presence of an output schema handles return value details. No significant context is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the description correctly adds no parameter-specific information. The schema is trivially covered (100%), and the baseline for 0 params is 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns the seed URL and registration steps. It uses a specific verb 'Return' and identifies the exact resource, distinguishing it from sibling tools focused on transforms, server management, or entities.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is used to retrieve registration information, but it does not explicitly state when to use it relative to siblings. No exclusions or alternative tool references are provided, so the usage context is clear but not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_transformA
Return the full detail document for one transform.
Args: transform_id: Fully qualified transform name from list_transforms.
| Name | Required | Description | Default |
|---|---|---|---|
| transform_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It only says it returns a document; it does not disclose error behavior, permissions, or other operation traits. This is a minimal disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences plus an argument line. It is front-loaded, free of unnecessary words, and every sentence provides useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple fetch-by-ID tool with an output schema, the description is mostly complete: it explains what is returned and the parameter. It lacks error/not-found behavior, but given the low complexity this is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, transform_id, is described as a 'Fully qualified transform name from list_transforms', which adds essential meaning beyond the bare string type in the schema. This fully compensates for the 0% schema description coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Return') and resource ('full detail document for one transform'), and the singular 'one' distinguishes it from sibling list_transforms. This is a specific verb+resource pair that leaves no ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'from list_transforms' implies a workflow (call list_transforms first to get the ID), giving some usage context. However, it does not explicitly state when to use this tool versus alternatives or any exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_entitiesA
List the entity types the running server advertises.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of exposing behavior. 'List the entity types the running server advertises' conveys that this is a read-only operation and that results are dynamic based on the server's advertised capabilities, which is useful context beyond simply stating the action.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence with no redundant words. Every word contributes meaning, making it highly concise and well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has no parameters and an output schema is present, the description need not explain return values. The statement fully covers the tool's purpose and source of truth ('running server'), making it complete for a list operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so per rubric the baseline is 4. There is nothing to clarify, and the description does not add unnecessary parameter-related noise.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and a precise resource ('entity types') scoped to the running server. This clearly distinguishes it from sibling tools like list_transforms, as 'entity types' is a distinct concept.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is used to discover available entity types on the current server, but provides no explicit guidance on when to use it versus alternatives like list_transforms. For a simple zero-parameter list operation, the implied usage is adequate but lacks direct comparison.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_transformsA
List every transform the running server advertises, with input and output types.
An output type of NONE means the transform function is missing a return annotation, which stops the Maltego client from routing to it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the responsibility. It discloses a notable behavioral trait: an output type of NONE indicates a missing return annotation, which prevents Maltego client routing. This adds valuable context beyond a generic list and helps interpret results. Side effects aren't mentioned, but as a read-only listing operation, this is adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences: the first front-loads the main purpose, and the second adds a specific caveat about the NONE type. Both sentences are informative with no unnecessary words, making it highly concise and well structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (no params, output schema present), the description is complete. It states what is listed, includes the NONE-type explanation which aids output interpretation, and mentions the 'running server' context. No important details are missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline score of 4 applies. The description mentions input and output types, giving the user an expectation of what the tool reports without needing parameter definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists every advertised transform with input and output types, using a specific verb ('list') and resource ('transforms'). It distinguishes from siblings like get_transform (single transform) and list_entities (different entity type), making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage context: discovering available transforms on the running server. It doesn't explicitly name alternatives or state when not to use it, but the purpose is self-evident and the tool is simple enough that no further exclusions are needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_transformA
Run a transform against one input entity and return its results.
Check the reported entity count: a transform can report success while producing nothing. See docs/transform-authoring.md.
Args: transform_id: Fully qualified transform name from list_transforms. entity_type: Maltego type of the input entity, e.g. maltego.Domain. entity_value: Value carried by the input entity. settings: Optional transform settings, keyed by setting name. timeout: Seconds to wait before cancelling the run.
| Name | Required | Description | Default |
|---|---|---|---|
| timeout | No | ||
| settings | No | ||
| entity_type | Yes | ||
| entity_value | Yes | ||
| transform_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full transparency burden. It does disclose an important behavioral trait: a transform can report success while producing no entities, and it points to further documentation. However, it does not clarify potential side effects, permission requirements, or error/return behavior beyond the schema. The added warning is helpful but not comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-organized: a one-sentence purpose, a brief cautionary note, and a clear Args section. Every part serves a purpose, and it is not overly verbose. It could be slightly more concise by merging the warning into the prose, but it remains efficient and readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (5 params, no annotations, output schema present), the description covers the essential context: what the tool does, the meaning of all parameters, a critical usage caveat, and a pointer to further documentation. It does not cover alternatives, but the output schema handles return structure, and the doc reference fills gaps. This is strong but not exhaustive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema provides only types and defaults, with 0% description coverage. The description compensates fully by explaining each parameter meaningfully: transform_id is 'fully qualified' and sourced from list_transforms, entity_type includes an example (maltego.Domain), entity_value is described as the 'value carried by the input entity,' settings are keyed, and timeout is in seconds. This adds substantial semantic value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with 'Run a transform against one input entity and return its results,' which clearly states the action (run), the resource (transform), and the scope (one input entity). This distinguishes it from sibling tools like list_transforms or get_transform, and the rest of the text reinforces the purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a useful caution about checking entity count, which indirectly guides behavior when using the tool. However, it does not explicitly state when to choose run_transform over alternatives (e.g., get_transform or list_transforms), nor does it mention exclusions or preconditions. Usage context is implied but not fully articulated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scaffold_transformA
Scaffold a new Maltego transform module from a cURL command or OpenAPI specification.
Args: service: Optional service name slug (e.g. 'greynoise', 'threatfox'). curl: A full cURL command string demonstrating an API request. openapi: An OpenAPI/Swagger spec string or file path. sample_response: Optional sample JSON response string to infer output fields.
| Name | Required | Description | Default |
|---|---|---|---|
| curl | No | ||
| openapi | No | ||
| service | No | ||
| sample_response | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behavioral traits, but it only mentions the generation action and the ability to infer output fields from sample_response. It does not mention side effects like file creation, required network access, or constraints such as whether at least one of curl/openapi is mandatory.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise: one opening sentence and a well-structured Args list. It avoids fluff and is easily scannable, with each parameter explanation earning its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema covers return values, but the description omits critical operational details: the relationship between curl and openapi, whether both can be provided, and expected behavior when neither is given. For a scaffolding tool with optional parameters, this ambiguity leaves the description incomplete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 0% description coverage, so the description compensates by explaining each parameter: service (optional slug), curl (full command string), openapi (spec string/path), and sample_response (optional JSON). This adds meaningful context beyond parameter names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool scaffolds a new Maltego transform module from a cURL command or OpenAPI specification. This is a specific verb+resource and distinguishes it from sibling tools like list_transforms or run_transform, which operate on existing transforms.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool: when you need to generate a new transform module from an API specification. It doesn't explicitly exclude alternatives, but the purpose is distinct enough that no exclusions are necessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_logsA
Return recent server log output.
Args: lines: Number of trailing log lines to return.
| Name | Required | Description | Default |
|---|---|---|---|
| lines | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It only says 'return' logs, not whether this is safe/read-only, whether auth is needed, or how recent is defined. No caveats or side effects are disclosed, making it insufficiently transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise: two sentences, purpose first, then parameter explanation. No wasted words, and the structure is front-loaded with the primary function.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter tool with an output schema, the description is adequate but not fully complete. It lacks usage guidance and behavioral transparency (e.g., whether the server must be running, what 'recent' means, output format beyond schema). It's a minimal viable description, not a rich one.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has zero description coverage, but the description adds 'Number of trailing log lines to return,' giving meaning to the 'lines' parameter. This directly compensates for the schema gap, though it's the only parameter and the added detail is minimal.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Return recent server log output,' using a specific verb and resource. It distinguishes from sibling tools like server_start, server_stop, server_status, which are about control/status, not logs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: use this tool to fetch server logs. However, there's no explicit 'when to use' or mention of alternatives. No exclusions or conditions are stated, so it's not fully transparent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_restartA
Restart the server to pick up new or edited transform modules.
Args: ssl: Serve over HTTPS after restarting. Left unset, the scheme the server is already running under is preserved, so reloading an HTTPS server keeps the Maltego desktop client working.
| Name | Required | Description | Default |
|---|---|---|---|
| ssl | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It usefully discloses the non-obvious ssl behavior (preserving the current scheme), but it does not mention potential downtime or other side effects of a restart. This goes beyond a bare statement but lacks rich detail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact: one sentence defining purpose and a brief parameter explanation. No padding, every sentence contributes to understanding the tool and its single argument.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple tool with one optional parameter, the description covers the purpose and parameter semantics effectively. Since an output schema exists, return values need not be described in the text. Minor omissions like downtime impact are acceptable given the tool's simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description's 'Args' section fully compensates by explaining the ssl parameter in detail: it can enable HTTPS, and when left unset it preserves the existing scheme. This adds substantial meaning beyond the schema's bare type definition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Restart the server to pick up new or edited transform modules,' clearly stating the verb, resource, and purpose. This distinguishes server_restart from sibling tools like server_start, server_stop, or server_status, which have different actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'to pick up new or edited transform modules' provides clear context for when to use this tool. It does not explicitly name alternatives or exclusions, but the purpose is clear enough to differentiate it from related server operations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_startA
Start the local transform server and wait until it answers.
Args: ssl: Serve over HTTPS. Requires generate_certs first. Both the Maltego desktop client and the Graph Browser require it.
| Name | Required | Description | Default |
|---|---|---|---|
| ssl | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses a meaningful behavioral trait: the tool blocks until the server answers. It also discloses the dependency on generate_certs for SSL mode, adding useful operational context beyond a trivial 'start' statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the primary purpose, followed by a concise Args block. Every sentence and clause adds value—there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter and an output schema, the description is complete enough. It covers the core behavior, the parameter meaning, and a prerequisite. The output format is not explained, but the presence of an output schema makes that unnecessary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only provides the parameter name and type, with 0% description coverage. The description's Args section fully explains ssl: it means 'Serve over HTTPS', requires generate_certs first, and is necessary for the Maltego desktop client and Graph Browser. This adds complete semantic meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Start the local transform server') and even adds the readiness condition ('wait until it answers'). This distinguishes it from sibling tools like server_stop, server_restart, and server_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives specific usage context for the ssl parameter: it requires generate_certs first, and both Maltego desktop client and Graph Browser require HTTPS. However, it does not explicitly state when to use server_start instead of server_restart or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_statusA
Report whether the server is running, healthy, and how many transforms it serves.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of conveying behavioral traits. The word 'report' clearly indicates a read-only operation, and the explicit listing of what is reported (running, healthy, transform count) gives the agent concrete behavioral expectations. It does not discuss side effects, auth, or errors, but for a simple status query this level is reasonably complete.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, tightly written sentence that front-loads the verb and immediately specifies the three outputs of interest. Every word contributes meaning; there is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, status-reporting tool with an output schema, the description is complete. It names all the reported aspects (running, health, transform count) without needing to explain return values, as those are covered by the output schema. The simplicity of the operation does not require further elaboration.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4. The description adds no parameter-level details, but none are needed since there is nothing to configure. It correctly focuses on the output dimensions instead.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'report' paired with the resource 'server' and clearly enumerates the exact outputs (running, healthy, transform count). This fully distinguishes it from sibling tools like server_start, server_stop, and server_logs, which are also self-descriptive in their names.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage as a health/status check before or during server operations, but it does not explicitly state when to use it versus alternatives. There is no mention of exclusions or alternative tools, though the context of sibling tool names provides implicit differentiation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
server_stopB
Stop the running transform server.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full responsibility for behavioral disclosure. 'Stop the running transform server' conveys only the action itself, with no info about side effects (e.g., whether running transforms are interrupted, if shutdown is graceful or forced, or if the operation is reversible). This is a significant gap for a state-changing operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, clear sentence with no fluff or repetition. It is front-loaded with the verb and resource, maximizing information density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (no params, likely void output), so minimal description might suffice. However, given no annotations, the description omits critical behavioral context—what happens to ongoing work, whether the server must be running, and error conditions. It is adequate but leaves gaps, so it does not earn a higher score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, and schema coverage is 100% (vacuously). The description adds no parameter semantics because none exist. Since 0 params has a baseline of 4 per rubric, this score is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Stop' and a clear resource 'the running transform server', making it obvious what the tool does. It also distinguishes itself from sibling tools like server_start, server_restart, and server_status by naming a distinct action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided about when to use this tool versus alternatives. It does not mention prerequisites (server must be running), complementary tools (server_start), or scenarios where stop vs. restart or stop vs. status is appropriate. The usage context is only implied by the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
12 tool updates
v0.1.0- First observed
generate_certs - First observed
get_seed_url - First observed
get_transform - First observed
list_entities - First observed
list_transforms - First observed
run_transform - First observed
scaffold_transform - First observed
server_logs - First observed
server_restart - First observed
server_start - First observed
server_status - First observed
server_stop
TDQS
Scored across 12 tools
Each tool targets a distinct action: server lifecycle, transform metadata, execution, or setup. There is no overlap between get_transform and run_transform, nor between the server_* commands.
Most tools follow a verb_noun pattern (get_transform, list_entities, run_transform), while server management tools use a server_ prefix (server_start, server_stop). This is a minor stylistic deviation but still predictable and readable.
12 tools is well within the ideal range for a server management and transform toolkit. Each tool serves a clear purpose without redundancy, covering discovery, execution, and server control.
The surface covers the full server lifecycle (start/stop/restart/status/logs), transform introspection (list/get), execution (run), and setup (seed URL, certs, scaffold). No essential operations are missing for the domain.
Maintenance
Related MCP Connectors
MCP server for building and testing AI agents with multi-model experimentation and insights.
The MCP server for Azure DevOps, bringing the power of Azure DevOps directly to your agents.
MCP Server for an Agent Task Marketplace
A MCP server built for developers enabling Git based project management with project and personal…
Related MCP Servers
- AlicenseCqualityBmaintenancePrototype MCP server enabling coding agents to manage Amigo Agent Forge operations, including org credentials, entity configurations, conversation simulations, and version sets.3711 npmISC
- AlicenseAqualityBmaintenanceAn MCP server that empowers AI coding agents to work effectively with Minecraft mod development, providing static analysis of decompiled source code and runtime interaction with a running Minecraft instance.3130 npm15MIT
- AlicenseAqualityDmaintenanceAn MCP server that enables coding agents to autonomously test, evaluate, and tune other MCP servers by acting as a proxy and providing linting, trace recording, evaluation, comparison, and reporting tools.7MIT
- AlicenseNot gradedqualityCmaintenanceLocal-first MCP server that provides project context, verification gates, and structured tools for coding agents to discover knowledge, run diagnostics, and execute allowlisted commands within a repository.16 npmMIT