@unclick/ipapi-mcp 0.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Apache License 2.0
2
+
3
+ Copyright (c) 2026 UnClick / malamutemayhem
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # IP Geolocation MCP by UnClick
2
+
3
+ IP address geolocation and batch lookup with country, city, region, ISP, and timezone.
4
+
5
+ > By UnClick. 180+ tools plus persistent agent memory in one install: https://unclick.world
6
+
7
+ ## Install
8
+
9
+ Installs straight from GitHub, no npm account needed.
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "ipapi": {
15
+ "command": "npx",
16
+ "args": ["-y", "https://github.com/malamutemayhem/unclick/releases/download/standalone-mcps-latest/ipapi.tgz"]
17
+ }
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## Tools
23
+
24
+ - `ip_lookup`
25
+ - `ip_batch`
26
+
27
+ ## Want the rest?
28
+
29
+ This is one connector. [UnClick](https://unclick.world) bundles 180+ tools plus
30
+ persistent cross-session agent memory in a single install.
31
+
32
+ ## License
33
+
34
+ Apache-2.0
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ // IP Geolocation MCP. Standalone MCP server by UnClick.
3
+ // By UnClick. 180+ tools plus persistent agent memory in one install: https://unclick.world
4
+ //
5
+ // Generated from the UnClick connector by scripts/generate-standalone-mcp.mjs.
6
+ // Edit the connector in the UnClick monorepo, not here.
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
10
+ import { ipLookup, ipBatch, } from "./ipapi-tool.js";
11
+ const TOOLS = [
12
+ {
13
+ name: "ip_lookup",
14
+ description: "Look up geolocation for an IP address.",
15
+ inputSchema: {
16
+ type: "object",
17
+ additionalProperties: false,
18
+ properties: {
19
+ ip: { type: "string" },
20
+ api_key: { type: "string" },
21
+ },
22
+ required: ["ip"],
23
+ },
24
+ },
25
+ {
26
+ name: "ip_batch",
27
+ description: "Batch IP address geolocation lookup.",
28
+ inputSchema: {
29
+ type: "object",
30
+ additionalProperties: false,
31
+ properties: {
32
+ ips: { type: "array", items: {}, description: "Array of IP address strings" },
33
+ api_key: { type: "string" },
34
+ },
35
+ required: ["ips"],
36
+ },
37
+ }
38
+ ];
39
+ const HANDLERS = {
40
+ ip_lookup: (args) => ipLookup(args),
41
+ ip_batch: (args) => ipBatch(args),
42
+ };
43
+ const server = new Server({ name: "io.github.malamutemayhem/ipapi", version: "0.1.0" }, { capabilities: { tools: {} } });
44
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
45
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
46
+ const handler = HANDLERS[req.params.name];
47
+ if (!handler) {
48
+ return { content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }], isError: true };
49
+ }
50
+ try {
51
+ const result = await handler((req.params.arguments ?? {}));
52
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
53
+ }
54
+ catch (err) {
55
+ const message = err instanceof Error ? err.message : String(err);
56
+ return { content: [{ type: "text", text: message }], isError: true };
57
+ }
58
+ });
59
+ async function main() {
60
+ const transport = new StdioServerTransport();
61
+ await server.connect(transport);
62
+ }
63
+ main().catch((err) => {
64
+ process.stderr.write(`[ipapi-mcp] fatal: ${err instanceof Error ? err.message : String(err)}\n`);
65
+ process.exit(1);
66
+ });
@@ -0,0 +1,77 @@
1
+ // IP geolocation integration for the UnClick MCP server.
2
+ // Uses the ip-api.com free tier via fetch - no auth required (100 req/min).
3
+ // Returns country, region, city, lat/lon, ISP, timezone, and more.
4
+ const IPAPI_BASE = "http://ip-api.com";
5
+ // --- API helper ---
6
+ async function ipapiGet(ip) {
7
+ const path = ip ? `/${encodeURIComponent(ip)}` : "/";
8
+ const url = `${IPAPI_BASE}/json${path}?fields=66846719`;
9
+ const response = await fetch(url, { headers: { "Accept": "application/json" } });
10
+ if (!response.ok) {
11
+ throw new Error(`HTTP ${response.status} from ip-api.com`);
12
+ }
13
+ const data = await response.json();
14
+ if (data.status === "fail") {
15
+ throw new Error(`ip-api error: ${data.message ?? "Query failed"}`);
16
+ }
17
+ return data;
18
+ }
19
+ function normalizeIpData(data) {
20
+ return {
21
+ ip: data.query,
22
+ status: data.status,
23
+ country: data.country,
24
+ country_code: data.countryCode,
25
+ region: data.regionName,
26
+ region_code: data.region,
27
+ city: data.city,
28
+ zip: data.zip,
29
+ lat: data.lat,
30
+ lon: data.lon,
31
+ timezone: data.timezone,
32
+ isp: data.isp,
33
+ org: data.org,
34
+ as: data.as,
35
+ mobile: data.mobile,
36
+ proxy: data.proxy,
37
+ hosting: data.hosting,
38
+ };
39
+ }
40
+ // --- Operations ---
41
+ export async function ipLookup(args) {
42
+ const ip = String(args.ip ?? "").trim();
43
+ // Empty ip will look up the caller's IP
44
+ const data = await ipapiGet(ip);
45
+ return normalizeIpData(data);
46
+ }
47
+ export async function ipBatch(args) {
48
+ const addresses = args.addresses;
49
+ if (!Array.isArray(addresses) || addresses.length === 0) {
50
+ throw new Error("addresses is required and must be a non-empty array of IP strings.");
51
+ }
52
+ if (addresses.length > 100) {
53
+ throw new Error("addresses array must contain 100 or fewer IPs per request.");
54
+ }
55
+ const body = addresses.map((a) => ({
56
+ query: String(a).trim(),
57
+ fields: "66846719",
58
+ }));
59
+ const response = await fetch(`${IPAPI_BASE}/batch`, {
60
+ method: "POST",
61
+ headers: { "Content-Type": "application/json", "Accept": "application/json" },
62
+ body: JSON.stringify(body),
63
+ });
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status} from ip-api.com batch endpoint`);
66
+ }
67
+ const results = await response.json();
68
+ return {
69
+ count: results.length,
70
+ results: results.map((r) => {
71
+ if (r.status === "fail") {
72
+ return { ip: r.query, error: r.message ?? "Query failed" };
73
+ }
74
+ return normalizeIpData(r);
75
+ }),
76
+ };
77
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@unclick/ipapi-mcp",
3
+ "version": "0.1.0",
4
+ "mcpName": "io.github.malamutemayhem/ipapi",
5
+ "description": "IP address geolocation and batch lookup with country, city, region, ISP, and timezone. By UnClick (https://unclick.world).",
6
+ "keywords": [
7
+ "mcp",
8
+ "model-context-protocol",
9
+ "unclick",
10
+ "ip",
11
+ "geolocation",
12
+ "network"
13
+ ],
14
+ "author": "UnClick (https://unclick.world)",
15
+ "type": "module",
16
+ "bin": {
17
+ "ipapi-mcp": "./dist/index.js"
18
+ },
19
+ "main": "./dist/index.js",
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "server.json"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "start": "node dist/index.js",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.15.1"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.6.0",
35
+ "@types/node": "^22.0.0"
36
+ },
37
+ "license": "Apache-2.0",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/malamutemayhem/unclick.git",
41
+ "directory": "packages/standalone/ipapi-mcp"
42
+ },
43
+ "homepage": "https://unclick.world"
44
+ }
package/server.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.malamutemayhem/ipapi",
4
+ "title": "IP Geolocation MCP by UnClick",
5
+ "description": "IP address geolocation and batch lookup with country, city, region, ISP, and timezone. By UnClick.",
6
+ "version": "0.1.0",
7
+ "websiteUrl": "https://unclick.world",
8
+ "icons": [
9
+ {
10
+ "src": "https://unclick.world/favicon.png",
11
+ "mimeType": "image/png",
12
+ "sizes": [
13
+ "512x512"
14
+ ]
15
+ }
16
+ ],
17
+ "repository": {
18
+ "url": "https://github.com/malamutemayhem/unclick.git",
19
+ "source": "github",
20
+ "subfolder": "packages/standalone/ipapi-mcp"
21
+ },
22
+ "packages": [
23
+ {
24
+ "registryType": "npm",
25
+ "identifier": "@unclick/ipapi-mcp",
26
+ "version": "0.1.0",
27
+ "runtimeHint": "npx",
28
+ "transport": {
29
+ "type": "stdio"
30
+ }
31
+ }
32
+ ]
33
+ }