argus-aml 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -6
  3. package/bin/init.js +121 -35
  4. package/package.json +10 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daertho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,15 +1,69 @@
1
1
  # argus-aml
2
2
 
3
- Add the Argus API's compliance tools to your AI agent.
3
+ Add the [Argus API](https://daertho.com/founders/)'s compliance tools to your AI agent: sanctions and PEP screening,
4
+ adverse media, face checks, e-signatures and financial-crime typologies, as MCP tools.
4
5
 
5
6
  ```bash
6
7
  npx argus-aml init
7
8
  ```
8
9
 
9
- This creates a sandbox key, then adds Argus to Claude Code and Cursor if it finds them. Sandbox results are invented and
10
- cost nothing.
10
+ It starts you on a sandbox key. Sandbox results are invented, cost nothing, and need no account.
11
11
 
12
- Use your live key with `npx argus-aml init --key ak_...`.
12
+ ## What this script does
13
13
 
14
- Your key is saved to `~/.config/argus/key`, readable only by you. For any other MCP client, point it at
15
- `https://argus.unbackoffice.com/mcp` and send your key in the `X-API-Key` header.
14
+ Exactly this, and nothing else:
15
+
16
+ 1. **One request** to `POST https://argus.unbackoffice.com/v1/founder/sandbox-key` to create a sandbox key.
17
+ No account, no email, no personal data. Skipped when you pass `--key` or a key is already saved.
18
+ 2. **Writes one file**, `~/.config/argus/key`, with mode 600 so only you can read it.
19
+ 3. **Adds the Argus server** to the agents it finds on this machine, and only those:
20
+
21
+ | Agent | How it's detected | What is touched |
22
+ | ---------- | -------------------------- | --------------------------------------------------- |
23
+ | Claude Code | `claude` CLI on your PATH | `claude mcp add --scope user argus …` |
24
+ | Cursor | `~/.cursor/` exists | `argus` entry in `~/.cursor/mcp.json` |
25
+ | Codex | `~/.codex/` exists | `[mcp_servers.argus]` in `~/.codex/config.toml` |
26
+ | Windsurf | `~/.codeium/windsurf/` | `argus` entry in `~/.codeium/windsurf/mcp_config.json` |
27
+ | Gemini CLI | `~/.gemini/` exists | `argus` entry in `~/.gemini/settings.json` |
28
+
29
+ Only the `argus` entry is written; the rest of each file is left as it was. A config file that isn't valid JSON is
30
+ left alone and reported.
31
+
32
+ It sends nothing else anywhere and collects no telemetry. The whole script is [`bin/init.js`](bin/init.js), about
33
+ 200 lines with no dependencies. To see what it would do on your machine before it does it:
34
+
35
+ ```bash
36
+ npx argus-aml init --print
37
+ ```
38
+
39
+ ## Going live
40
+
41
+ When Daertho issues your live key, run the same command with it:
42
+
43
+ ```bash
44
+ npx argus-aml init --key ak_your_live_key
45
+ ```
46
+
47
+ Same URL, same tools. The only change is that results stop carrying `"sandbox": true`. Running `init` again later
48
+ keeps the saved key: it never swaps a live key for a sandbox one.
49
+
50
+ ## Any other MCP client
51
+
52
+ Point it at `https://argus.unbackoffice.com/mcp` and send your key in the `X-API-Key` header. The Claude desktop app
53
+ adds remote servers through its own Connectors settings rather than a config file, so this script can't set it up;
54
+ use the URL there.
55
+
56
+ ## Plain HTTP
57
+
58
+ ```bash
59
+ curl https://argus.unbackoffice.com/v1/screen \
60
+ -H "X-API-Key: $(cat ~/.config/argus/key)" \
61
+ -H "Content-Type: application/json" \
62
+ -d '{"name": "Sandbox Sanctioned"}'
63
+ ```
64
+
65
+ API reference: https://argus.unbackoffice.com/developers/reference
66
+
67
+ ## Licence
68
+
69
+ The installer in this package is MIT licensed. The Argus API it connects to is a Daertho service under its own terms.
package/bin/init.js CHANGED
@@ -1,6 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  // npx argus-aml init: get a sandbox key (or take --key) and add the Argus MCP server to the agents on this machine.
4
+ //
5
+ // Everything this script does, and nothing else:
6
+ // 1. POST https://argus.unbackoffice.com/v1/founder/sandbox-key, only when no key is given or already saved
7
+ // 2. write ~/.config/argus/key, readable only by you
8
+ // 3. add the Argus server to the agents it finds on this machine:
9
+ // Claude Code `claude mcp add` (needs the Claude Code CLI on PATH)
10
+ // Cursor ~/.cursor/mcp.json
11
+ // Codex ~/.codex/config.toml
12
+ // Windsurf ~/.codeium/windsurf/mcp_config.json
13
+ // Gemini CLI ~/.gemini/settings.json
14
+ // It sends nothing else anywhere and collects no telemetry. `--print` shows these steps without doing them.
4
15
  const fs = require("fs");
5
16
  const os = require("os");
6
17
  const path = require("path");
@@ -8,8 +19,11 @@ const { spawnSync } = require("child_process");
8
19
 
9
20
  const API = (process.env.ARGUS_API_URL || "https://argus.unbackoffice.com").replace(/\/$/, "");
10
21
  const MCP = API + "/mcp";
22
+ const HOME = os.homedir();
23
+ const KEY_FILE = path.join(HOME, ".config", "argus", "key");
11
24
  const ok = (s) => console.log("✓ " + s);
12
25
  const fail = (s) => { console.error("✗ " + s); process.exit(1); };
26
+ const mask = (key) => (key ? key.slice(0, 6) + "…" : "");
13
27
 
14
28
  function argValue(args, name) {
15
29
  const i = args.indexOf(name);
@@ -32,41 +46,25 @@ async function sandboxKey() {
32
46
 
33
47
  function savedKey() {
34
48
  try {
35
- return fs.readFileSync(path.join(os.homedir(), ".config", "argus", "key"), "utf8").trim();
49
+ return fs.readFileSync(KEY_FILE, "utf8").trim();
36
50
  } catch (e) {
37
51
  return "";
38
52
  }
39
53
  }
40
54
 
41
55
  function saveKey(key) {
42
- const dir = path.join(os.homedir(), ".config", "argus");
43
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
44
- const file = path.join(dir, "key");
45
- fs.writeFileSync(file, key + "\n", { mode: 0o600 });
46
- fs.chmodSync(file, 0o600);
47
- return file;
56
+ fs.mkdirSync(path.dirname(KEY_FILE), { recursive: true, mode: 0o700 });
57
+ fs.writeFileSync(KEY_FILE, key + "\n", { mode: 0o600 });
58
+ fs.chmodSync(KEY_FILE, 0o600);
59
+ return KEY_FILE;
48
60
  }
49
61
 
50
62
  function onPath(cmd) {
51
63
  return spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
52
64
  }
53
65
 
54
- function addToClaude(key) {
55
- if (!onPath("claude")) return false;
56
- spawnSync("claude", ["mcp", "remove", "argus", "--scope", "user"], { stdio: "ignore" });
57
- const r = spawnSync("claude", ["mcp", "add", "--transport", "http", "--scope", "user", "argus", MCP,
58
- "--header", "X-API-Key: " + key], { encoding: "utf8" });
59
- if (r.status !== 0) {
60
- console.error("✗ Couldn't add Argus to Claude Code: " + ((r.stderr || "").replace(key, "***").trim() || "unknown error"));
61
- return false;
62
- }
63
- return true;
64
- }
65
-
66
- function addToCursor(key) {
67
- const dir = path.join(os.homedir(), ".cursor");
68
- if (!fs.existsSync(dir)) return false;
69
- const file = path.join(dir, "mcp.json");
66
+ // Replace or add the "argus" entry under mcpServers in a JSON config file. A file that isn't valid JSON is left alone.
67
+ function writeJsonEntry(file, entry) {
70
68
  let cfg = {};
71
69
  if (fs.existsSync(file)) {
72
70
  try {
@@ -76,39 +74,127 @@ function addToCursor(key) {
76
74
  return false;
77
75
  }
78
76
  }
79
- cfg.mcpServers = { ...(cfg.mcpServers || {}), argus: { url: MCP, headers: { "X-API-Key": key } } };
77
+ cfg.mcpServers = { ...(cfg.mcpServers || {}), argus: entry };
78
+ fs.mkdirSync(path.dirname(file), { recursive: true });
80
79
  fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + "\n", { mode: 0o600 });
81
80
  fs.chmodSync(file, 0o600);
82
81
  return true;
83
82
  }
84
83
 
84
+ // Replace or add the [mcp_servers.argus] table in a TOML file, without touching anything else in it.
85
+ function writeTomlTable(file, lines) {
86
+ let text = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
87
+ const block = "[mcp_servers.argus]\n" + lines.join("\n") + "\n";
88
+ const re = /^\[mcp_servers\.argus\]\n(?:(?!\[)[^\n]*\n?)*/m;
89
+ if (re.test(text)) text = text.replace(re, (m, at) => block + (at + m.length < text.length ? "\n" : ""));
90
+ else text = text.replace(/\s*$/, "") + (text.trim() ? "\n\n" : "") + block;
91
+ fs.mkdirSync(path.dirname(file), { recursive: true });
92
+ fs.writeFileSync(file, text, { mode: 0o600 });
93
+ fs.chmodSync(file, 0o600);
94
+ return true;
95
+ }
96
+
97
+ // Each agent: how it's detected, what gets touched, and how to add the server.
98
+ const AGENTS = [
99
+ {
100
+ name: "Claude Code",
101
+ found: () => onPath("claude"),
102
+ touches: "`claude mcp add --scope user argus` (your Claude Code user config)",
103
+ add(key) {
104
+ spawnSync("claude", ["mcp", "remove", "argus", "--scope", "user"], { stdio: "ignore" });
105
+ const r = spawnSync("claude", ["mcp", "add", "--transport", "http", "--scope", "user", "argus", MCP,
106
+ "--header", "X-API-Key: " + key], { encoding: "utf8" });
107
+ if (r.status !== 0) {
108
+ console.error("✗ Couldn't add Argus to Claude Code: " + ((r.stderr || "").split(key).join("***").trim() || "unknown error"));
109
+ return false;
110
+ }
111
+ return true;
112
+ },
113
+ },
114
+ {
115
+ name: "Cursor",
116
+ file: path.join(HOME, ".cursor", "mcp.json"),
117
+ found() { return fs.existsSync(path.dirname(this.file)); },
118
+ add(key) { return writeJsonEntry(this.file, { url: MCP, headers: { "X-API-Key": key } }); },
119
+ },
120
+ {
121
+ name: "Codex",
122
+ file: path.join(HOME, ".codex", "config.toml"),
123
+ found() { return fs.existsSync(path.dirname(this.file)); },
124
+ add(key) {
125
+ return writeTomlTable(this.file, ['url = "' + MCP + '"', 'http_headers = { "X-API-Key" = "' + key + '" }']);
126
+ },
127
+ },
128
+ {
129
+ name: "Windsurf",
130
+ file: path.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
131
+ found() { return fs.existsSync(path.dirname(this.file)); },
132
+ add(key) { return writeJsonEntry(this.file, { serverUrl: MCP, headers: { "X-API-Key": key } }); },
133
+ },
134
+ {
135
+ name: "Gemini CLI",
136
+ file: path.join(HOME, ".gemini", "settings.json"),
137
+ found() { return fs.existsSync(path.dirname(this.file)); },
138
+ add(key) { return writeJsonEntry(this.file, { httpUrl: MCP, headers: { "X-API-Key": key } }); },
139
+ },
140
+ ];
141
+
142
+ function manualInstructions(file) {
143
+ console.log("\nAdd this to any MCP client:");
144
+ console.log(" URL: " + MCP);
145
+ console.log(" Header: X-API-Key: " + (file ? "(the key in " + file + ")" : "your key"));
146
+ }
147
+
148
+ function printPlan(given) {
149
+ console.log("argus-aml init would do this, and nothing else:\n");
150
+ if (given) console.log(" 1. Use the key you gave (" + mask(given) + "); no request is made to create one");
151
+ else console.log(" 1. POST " + API + "/v1/founder/sandbox-key to create a sandbox key (no account, no personal data)");
152
+ console.log(" 2. Write " + KEY_FILE + " (mode 600: readable only by you)");
153
+ console.log(" 3. Add the server " + MCP + " with header X-API-Key to the agents found on this machine:");
154
+ let any = false;
155
+ for (const a of AGENTS) {
156
+ const here = a.found();
157
+ any = any || here;
158
+ console.log(" " + (here ? "found " : "not found") + " " + a.name + " → " + (a.touches || a.file));
159
+ }
160
+ if (!any) console.log(" (none found: it would print the URL and header for you to add by hand)");
161
+ console.log("\nNothing else is sent or stored. Run without --print to do it.");
162
+ }
163
+
85
164
  async function main() {
86
165
  const args = process.argv.slice(2);
87
166
  if (args[0] !== "init") {
88
- console.log("Usage: npx argus-aml init [--key ak_...]");
167
+ console.log("Usage: npx argus-aml init [--key ak_...] [--print]");
168
+ console.log(" --key use a key you already have (your live key, after Daertho issues one)");
169
+ console.log(" --print show what init would do on this machine without doing it");
89
170
  process.exit(args.length && !["-h", "--help"].includes(args[0]) ? 1 : 0);
90
171
  }
91
172
  // a key saved by an earlier run is kept: running init again must never swap a live key for a sandbox one
92
173
  const given = argValue(args, "--key") || process.env.ARGUS_API_KEY || savedKey();
174
+ if (args.includes("--print")) return printPlan(given);
175
+
93
176
  const key = given || (await sandboxKey());
94
- ok(given ? "Using your API key" : "Sandbox key created");
177
+ ok(given ? "Using your API key (" + mask(key) + ")" : "Sandbox key created");
95
178
  const file = saveKey(key);
96
179
  ok("Key saved to " + file);
97
180
 
98
181
  const added = [];
99
- if (addToClaude(key)) added.push("Claude Code");
100
- if (addToCursor(key)) added.push("Cursor");
182
+ for (const a of AGENTS) if (a.found() && a.add(key)) added.push(a.name);
101
183
  if (added.length) {
102
- ok("Argus tools added to " + added.join(" and "));
184
+ ok("Argus tools added to " + added.join(", "));
185
+ manualInstructions(file);
186
+ console.log("\nRestart your agent, then try:");
103
187
  } else {
104
- console.log("\nNo supported agent found. Add this to any MCP client:");
105
- console.log(" URL: " + MCP);
106
- console.log(" Header: X-API-Key: (the key in " + file + ")");
188
+ console.log("\nNo supported agent found on this machine.");
189
+ if (!onPath("claude")) {
190
+ console.log(" The Claude Code CLI isn't on your PATH (the Claude desktop app alone can't be set up by this script).");
191
+ console.log(" Install it with: npm install -g @anthropic-ai/claude-code then run this again.");
192
+ }
193
+ manualInstructions(file);
194
+ console.log("\nThen try:");
107
195
  }
108
- if (added.length) console.log("\nRestart your agent, then try:");
109
- else console.log("\nThen try:");
110
196
  console.log(" Screen Sandbox Sanctioned and tell me what happens next.");
111
197
  if (!given) console.log("\nSandbox results are invented. Ask for a live key at " + API + "/developers");
112
198
  }
113
199
 
114
- main().catch(() => fail("Something went wrong. Try again."));
200
+ main().catch((e) => fail("Something went wrong: " + (e && e.message ? e.message : e)));
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "argus-aml",
3
- "version": "0.1.0",
4
- "description": "Add the Argus API's compliance tools to your AI agent.",
3
+ "version": "0.1.1",
4
+ "description": "Add the Argus API's compliance tools (sanctions and PEP screening, adverse media, face checks, e-signatures, typologies) to your AI agent.",
5
+ "keywords": ["aml", "sanctions", "pep", "kyc", "compliance", "mcp", "claude-code", "cursor", "codex", "argus", "daertho"],
6
+ "homepage": "https://daertho.com/founders/",
7
+ "repository": { "type": "git", "url": "git+https://github.com/sect0rial/argus-aml.git" },
8
+ "bugs": { "url": "https://github.com/sect0rial/argus-aml/issues", "email": "1@daertho.com" },
9
+ "author": "Daertho <1@daertho.com> (https://daertho.com)",
10
+ "license": "MIT",
5
11
  "bin": { "argus-aml": "bin/init.js" },
6
- "files": ["bin", "README.md"],
12
+ "files": ["bin", "README.md", "LICENSE"],
7
13
  "engines": { "node": ">=18" },
8
- "license": "UNLICENSED"
14
+ "publishConfig": { "access": "public", "provenance": true }
9
15
  }