atracker-mcp-server 1.0.0 → 1.1.0
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.
- package/README.md +8 -0
- package/package.json +1 -1
- package/src/index.js +3 -1
- package/src/tools/geo.js +78 -0
package/README.md
CHANGED
|
@@ -105,6 +105,14 @@ npm install -g atracker-mcp-server
|
|
|
105
105
|
- `get_settings` — Get tracker settings
|
|
106
106
|
- `update_settings` — Update tracker settings
|
|
107
107
|
|
|
108
|
+
|
|
109
|
+
### Geo2IP Bundles
|
|
110
|
+
|
|
111
|
+
- `geo_status` — Current geo bundle versions, sizes, and last download status
|
|
112
|
+
- `geo_check_update` — Check the platform for newer bundle versions (returns delta/full info)
|
|
113
|
+
- `geo_update` — Download and apply latest bundles (`categories?`, `force?`); polls until done
|
|
114
|
+
- `geo_lookup` — Test IP lookup with resolved geo/asn/threat data and per-field sources
|
|
115
|
+
|
|
108
116
|
## Environment Variables
|
|
109
117
|
|
|
110
118
|
| Variable | Required | Description |
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { registerDomainTools } from './tools/domains.js';
|
|
|
11
11
|
import { registerReportTools } from './tools/reports.js';
|
|
12
12
|
import { registerStatusTools } from './tools/status.js';
|
|
13
13
|
import { registerSettingsTools } from './tools/settings.js';
|
|
14
|
+
import { registerGeoTools } from './tools/geo.js';
|
|
14
15
|
|
|
15
16
|
const url = process.env.ATRACKER_URL;
|
|
16
17
|
const token = process.env.ATRACKER_TOKEN;
|
|
@@ -26,7 +27,7 @@ const client = createClient(url, token);
|
|
|
26
27
|
|
|
27
28
|
const server = new McpServer({
|
|
28
29
|
name: 'atracker',
|
|
29
|
-
version: '1.
|
|
30
|
+
version: '1.1.0',
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
registerCampaignTools(server, client);
|
|
@@ -38,6 +39,7 @@ registerDomainTools(server, client);
|
|
|
38
39
|
registerReportTools(server, client);
|
|
39
40
|
registerStatusTools(server, client);
|
|
40
41
|
registerSettingsTools(server, client);
|
|
42
|
+
registerGeoTools(server, client);
|
|
41
43
|
|
|
42
44
|
const transport = new StdioServerTransport();
|
|
43
45
|
await server.connect(transport);
|
package/src/tools/geo.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const CATEGORIES = ['geo', 'asn', 'threat'];
|
|
2
|
+
const POLL_INTERVAL_MS = 1000;
|
|
3
|
+
const POLL_TIMEOUT_MS = 30 * 60 * 1000;
|
|
4
|
+
|
|
5
|
+
async function waitForDownload(client, categories) {
|
|
6
|
+
const targets = categories && categories.length ? categories : CATEGORIES;
|
|
7
|
+
const startedAt = Date.now();
|
|
8
|
+
// eslint-disable-next-line no-constant-condition
|
|
9
|
+
while (true) {
|
|
10
|
+
if (Date.now() - startedAt > POLL_TIMEOUT_MS) {
|
|
11
|
+
throw new Error('Timed out waiting for geo bundle download');
|
|
12
|
+
}
|
|
13
|
+
const state = await client.get('/geo2ip/download-status');
|
|
14
|
+
const summaries = targets.map((c) => ({ category: c, ...(state[c] || {}) }));
|
|
15
|
+
const errored = summaries.find((s) => s.status === 'error');
|
|
16
|
+
if (errored) throw new Error(`Download failed for ${errored.category}: ${errored.error || 'unknown'}`);
|
|
17
|
+
const active = summaries.find((s) => !['done', 'idle'].includes(s.status));
|
|
18
|
+
if (!active) return summaries;
|
|
19
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function registerGeoTools(server, client) {
|
|
24
|
+
server.tool(
|
|
25
|
+
'geo_status',
|
|
26
|
+
'Get current geo2ip bundle versions, sizes, and last download status for all three categories (geo, asn, threat).',
|
|
27
|
+
{},
|
|
28
|
+
async () => {
|
|
29
|
+
const data = await client.get('/geo2ip/bundle-status');
|
|
30
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
server.tool(
|
|
35
|
+
'geo_check_update',
|
|
36
|
+
'Check the ATracker platform for newer geo bundle versions. Returns current versions, available manifests (including delta info), and the list of categories that have updates.',
|
|
37
|
+
{},
|
|
38
|
+
async () => {
|
|
39
|
+
const data = await client.post('/geo2ip/check-updates', {});
|
|
40
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
server.tool(
|
|
45
|
+
'geo_update',
|
|
46
|
+
'Download and apply the latest geo bundles from the platform. Optionally limit to specific categories (geo|asn|threat). Polls until completion and returns the final bundle status.',
|
|
47
|
+
{
|
|
48
|
+
categories: { type: 'array', items: { type: 'string', enum: CATEGORIES }, description: 'Optional subset of categories to update' },
|
|
49
|
+
force: { type: 'boolean', description: 'Re-download even if bundles are up to date' },
|
|
50
|
+
},
|
|
51
|
+
async (args = {}) => {
|
|
52
|
+
const body = {};
|
|
53
|
+
if (Array.isArray(args.categories) && args.categories.length) body.categories = args.categories;
|
|
54
|
+
if (args.force) body.force = true;
|
|
55
|
+
await client.post('/geo2ip/download-bundle', body);
|
|
56
|
+
const summaries = await waitForDownload(client, body.categories);
|
|
57
|
+
const finalStatus = await client.get('/geo2ip/bundle-status');
|
|
58
|
+
return {
|
|
59
|
+
content: [{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: JSON.stringify({ summaries, status: finalStatus }, null, 2),
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
server.tool(
|
|
68
|
+
'geo_lookup',
|
|
69
|
+
'Look up geo, ASN, and threat info for an IPv4/IPv6 address using the locally loaded bundles. Returns resolved data with per-field source attribution.',
|
|
70
|
+
{
|
|
71
|
+
ip: { type: 'string', description: 'IPv4 or IPv6 address to look up' },
|
|
72
|
+
},
|
|
73
|
+
async ({ ip }) => {
|
|
74
|
+
const data = await client.post('/geo2ip/test-lookup', { ip });
|
|
75
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|