legal-data-hunter 1.0.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.
Files changed (3) hide show
  1. package/README.md +60 -0
  2. package/bin/setup.js +306 -0
  3. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Legal Data Hunter — MCP Setup
2
+
3
+ Connect your AI to **18M+ legal documents worldwide** with one command.
4
+
5
+ Works with Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, and any MCP-compatible client.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx legal-data-hunter setup
11
+ ```
12
+
13
+ This detects your installed AI clients and configures them automatically. On first use, you'll be prompted to sign in via your browser — no API key needed.
14
+
15
+ ## Options
16
+
17
+ ```bash
18
+ npx legal-data-hunter setup # Interactive setup
19
+ npx legal-data-hunter setup --yes # Auto-configure all detected clients
20
+ npx legal-data-hunter setup -c cursor # Configure Cursor only
21
+ npx legal-data-hunter setup --list # List detected clients
22
+ ```
23
+
24
+ ## Alternative: `add-mcp`
25
+
26
+ ```bash
27
+ npx add-mcp https://legaldatahunter.com/mcp
28
+ ```
29
+
30
+ ## Alternative: Manual Config
31
+
32
+ Add to your client's MCP config (`.mcp.json`, `claude_desktop_config.json`, etc.):
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "legal-data-hunter": {
38
+ "type": "streamable-http",
39
+ "url": "https://legaldatahunter.com/mcp"
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## What You Get
46
+
47
+ | Tool | Description |
48
+ |------|-------------|
49
+ | `search` | Hybrid semantic + keyword search across 18M+ documents |
50
+ | `get_document` | Retrieve full text and metadata by source + ID |
51
+ | `resolve_reference` | Resolve loose citations (ECLI, article numbers, case numbers) |
52
+ | `discover_countries` | List available countries with document counts |
53
+ | `discover_sources` | List data sources for a specific country |
54
+ | `get_filters` | Get available filter values for a source |
55
+
56
+ ## Links
57
+
58
+ - [Connect Guide](https://legaldatahunter.com/connect)
59
+ - [API Docs](https://legaldatahunter.com/docs)
60
+ - [GitHub](https://github.com/worldwidelaw/legal-sources)
package/bin/setup.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+ const readline = require("readline");
8
+
9
+ const MCP_URL = "https://legaldatahunter.com/mcp";
10
+ const SERVER_NAME = "legal-data-hunter";
11
+ const MCP_ENTRY = {
12
+ type: "streamable-http",
13
+ url: MCP_URL,
14
+ };
15
+
16
+ // ── Client definitions ──────────────────────────────────────────────────────
17
+
18
+ const HOME = os.homedir();
19
+ const IS_WIN = process.platform === "win32";
20
+ const IS_MAC = process.platform === "darwin";
21
+
22
+ function appData() {
23
+ if (IS_WIN) return process.env.APPDATA || path.join(HOME, "AppData", "Roaming");
24
+ if (IS_MAC) return path.join(HOME, "Library", "Application Support");
25
+ return process.env.XDG_CONFIG_HOME || path.join(HOME, ".config");
26
+ }
27
+
28
+ const CLIENTS = [
29
+ {
30
+ id: "claude-code",
31
+ name: "Claude Code (global)",
32
+ path: path.join(HOME, ".claude.json"),
33
+ wrap: false, // top-level mcpServers
34
+ },
35
+ {
36
+ id: "claude-code-project",
37
+ name: "Claude Code (project)",
38
+ path: path.join(process.cwd(), ".mcp.json"),
39
+ wrap: false,
40
+ },
41
+ {
42
+ id: "claude-desktop",
43
+ name: "Claude Desktop",
44
+ path: IS_WIN
45
+ ? path.join(appData(), "Claude", "claude_desktop_config.json")
46
+ : IS_MAC
47
+ ? path.join(appData(), "Claude", "claude_desktop_config.json")
48
+ : path.join(appData(), "Claude", "claude_desktop_config.json"),
49
+ wrap: false,
50
+ },
51
+ {
52
+ id: "cursor",
53
+ name: "Cursor (global)",
54
+ path: path.join(HOME, ".cursor", "mcp.json"),
55
+ wrap: false,
56
+ },
57
+ {
58
+ id: "cursor-project",
59
+ name: "Cursor (project)",
60
+ path: path.join(process.cwd(), ".cursor", "mcp.json"),
61
+ wrap: false,
62
+ requireDir: true, // only if .cursor/ dir exists
63
+ },
64
+ {
65
+ id: "vscode",
66
+ name: "VS Code (project)",
67
+ path: path.join(process.cwd(), ".vscode", "mcp.json"),
68
+ wrap: false,
69
+ requireDir: true,
70
+ },
71
+ {
72
+ id: "lawvable",
73
+ name: "Lawvable (project)",
74
+ path: path.join(process.cwd(), ".vscode", "mcp.json"), // VS Code fork, same config path
75
+ wrap: false,
76
+ requireDir: true,
77
+ aliasOf: "vscode", // skip if VS Code already handled this path
78
+ },
79
+ {
80
+ id: "windsurf",
81
+ name: "Windsurf",
82
+ path: path.join(HOME, ".codeium", "windsurf", "mcp_config.json"),
83
+ wrap: false,
84
+ },
85
+ {
86
+ id: "copilot",
87
+ name: "GitHub Copilot CLI",
88
+ path: path.join(HOME, ".copilot", "mcp-config.json"),
89
+ wrap: false,
90
+ },
91
+ {
92
+ id: "junie",
93
+ name: "JetBrains Junie (project)",
94
+ path: path.join(process.cwd(), ".junie", "mcp", "mcp.json"),
95
+ wrap: false,
96
+ requireDir: true,
97
+ },
98
+ {
99
+ id: "junie-global",
100
+ name: "JetBrains Junie (global)",
101
+ path: path.join(HOME, ".junie", "mcp", "mcp.json"),
102
+ wrap: false,
103
+ },
104
+ ];
105
+
106
+ // ── Helpers ──────────────────────────────────────────────────────────────────
107
+
108
+ function detect() {
109
+ const seen = new Set();
110
+ return CLIENTS.filter((c) => {
111
+ // Deduplicate: skip if another client already claimed this config path
112
+ if (c.aliasOf && seen.has(c.path)) return false;
113
+
114
+ let detected = false;
115
+ if (c.requireDir) {
116
+ detected = fs.existsSync(path.dirname(c.path));
117
+ } else if (c.id === "claude-code") {
118
+ detected = fs.existsSync(path.join(HOME, ".claude")) || fs.existsSync(c.path);
119
+ } else if (["claude-desktop", "cursor", "windsurf", "copilot", "junie-global"].includes(c.id)) {
120
+ detected = fs.existsSync(path.dirname(c.path));
121
+ } else {
122
+ detected = fs.existsSync(c.path);
123
+ }
124
+
125
+ if (detected) seen.add(c.path);
126
+ return detected;
127
+ });
128
+ }
129
+
130
+ function readJson(filePath) {
131
+ try {
132
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
133
+ } catch {
134
+ return {};
135
+ }
136
+ }
137
+
138
+ function writeJson(filePath, data) {
139
+ const dir = path.dirname(filePath);
140
+ if (!fs.existsSync(dir)) {
141
+ fs.mkdirSync(dir, { recursive: true });
142
+ }
143
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
144
+ }
145
+
146
+ function configure(client) {
147
+ const config = readJson(client.path);
148
+ if (!config.mcpServers) config.mcpServers = {};
149
+
150
+ if (config.mcpServers[SERVER_NAME]) {
151
+ const existing = config.mcpServers[SERVER_NAME];
152
+ if (existing.url === MCP_URL) {
153
+ console.log(` \u2713 ${client.name} — already configured`);
154
+ return "skipped";
155
+ }
156
+ // Update URL if different
157
+ console.log(` \u2713 ${client.name} — updated (URL changed)`);
158
+ } else {
159
+ console.log(` \u2713 ${client.name} — configured`);
160
+ }
161
+
162
+ config.mcpServers[SERVER_NAME] = { ...MCP_ENTRY };
163
+ writeJson(client.path, config);
164
+ return "configured";
165
+ }
166
+
167
+ function ask(question) {
168
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
169
+ return new Promise((resolve) => {
170
+ rl.question(question, (answer) => {
171
+ rl.close();
172
+ resolve(answer.trim().toLowerCase());
173
+ });
174
+ });
175
+ }
176
+
177
+ // ── CLI ──────────────────────────────────────────────────────────────────────
178
+
179
+ async function main() {
180
+ const args = process.argv.slice(2);
181
+ const autoYes = args.includes("--yes") || args.includes("-y");
182
+ const clientFilter = (() => {
183
+ const idx = args.indexOf("--client");
184
+ if (idx !== -1 && args[idx + 1]) return args[idx + 1];
185
+ const idxC = args.indexOf("-c");
186
+ if (idxC !== -1 && args[idxC + 1]) return args[idxC + 1];
187
+ return null;
188
+ })();
189
+
190
+ // Help
191
+ if (args.includes("--help") || args.includes("-h")) {
192
+ console.log(`
193
+ legal-data-hunter setup
194
+
195
+ Connect your AI to 18M+ legal documents worldwide via MCP.
196
+ Detects installed AI agents and writes the config automatically.
197
+
198
+ Usage:
199
+ npx legal-data-hunter setup [options]
200
+
201
+ Options:
202
+ --yes, -y Non-interactive mode (configure all detected clients)
203
+ --client, -c Target a specific client (claude-code, cursor, vscode, etc.)
204
+ --list List detected clients without configuring
205
+ --help, -h Show this help message
206
+
207
+ Examples:
208
+ npx legal-data-hunter setup # Interactive setup
209
+ npx legal-data-hunter setup --yes # Auto-configure all detected clients
210
+ npx legal-data-hunter setup -c cursor # Configure Cursor only
211
+
212
+ On first use in any client, you'll be prompted to sign in with
213
+ GitHub or Google via your browser. No API key needed.
214
+
215
+ Learn more: https://legaldatahunter.com/connect
216
+ `);
217
+ return;
218
+ }
219
+
220
+ console.log();
221
+ console.log(" Legal Data Hunter — MCP Setup");
222
+ console.log(" Search 18M+ legal documents worldwide from your AI");
223
+ console.log();
224
+
225
+ // Detect clients
226
+ let clients = detect();
227
+
228
+ if (clientFilter) {
229
+ clients = clients.filter(
230
+ (c) => c.id === clientFilter || c.id.startsWith(clientFilter)
231
+ );
232
+ if (clients.length === 0) {
233
+ console.log(` No matching client found for "${clientFilter}".`);
234
+ console.log(` Available: ${CLIENTS.map((c) => c.id).join(", ")}`);
235
+ process.exit(1);
236
+ }
237
+ }
238
+
239
+ // List mode
240
+ if (args.includes("--list")) {
241
+ if (clients.length === 0) {
242
+ console.log(" No MCP-compatible AI clients detected.");
243
+ } else {
244
+ console.log(" Detected clients:");
245
+ clients.forEach((c) => console.log(` - ${c.name} (${c.path})`));
246
+ }
247
+ console.log();
248
+ return;
249
+ }
250
+
251
+ if (clients.length === 0) {
252
+ console.log(" No MCP-compatible AI clients detected.");
253
+ console.log();
254
+ console.log(" You can manually add this to your client's MCP config:");
255
+ console.log();
256
+ console.log(` {`);
257
+ console.log(` "mcpServers": {`);
258
+ console.log(` "${SERVER_NAME}": {`);
259
+ console.log(` "type": "streamable-http",`);
260
+ console.log(` "url": "${MCP_URL}"`);
261
+ console.log(` }`);
262
+ console.log(` }`);
263
+ console.log(` }`);
264
+ console.log();
265
+ console.log(" Learn more: https://legaldatahunter.com/connect");
266
+ console.log();
267
+ return;
268
+ }
269
+
270
+ console.log(" Detected clients:");
271
+ clients.forEach((c, i) => console.log(` ${i + 1}. ${c.name}`));
272
+ console.log();
273
+
274
+ if (!autoYes) {
275
+ const answer = await ask(" Configure all detected clients? [Y/n] ");
276
+ if (answer && answer !== "y" && answer !== "yes") {
277
+ console.log(" Aborted.");
278
+ return;
279
+ }
280
+ console.log();
281
+ }
282
+
283
+ // Configure
284
+ let configured = 0;
285
+ for (const client of clients) {
286
+ const result = configure(client);
287
+ if (result === "configured") configured++;
288
+ }
289
+
290
+ console.log();
291
+ if (configured > 0) {
292
+ console.log(" Done! On first use, you'll be prompted to sign in via your browser.");
293
+ console.log(" No API key needed — authentication is handled automatically (OAuth).");
294
+ } else {
295
+ console.log(" All clients were already configured. Nothing to do.");
296
+ }
297
+ console.log();
298
+ console.log(" MCP URL: " + MCP_URL);
299
+ console.log(" Docs: https://legaldatahunter.com/connect");
300
+ console.log();
301
+ }
302
+
303
+ main().catch((err) => {
304
+ console.error("Error:", err.message);
305
+ process.exit(1);
306
+ });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "legal-data-hunter",
3
+ "version": "1.0.0",
4
+ "description": "Connect your AI to 18M+ legal documents worldwide via MCP. One command to configure Claude, Cursor, VS Code, and more.",
5
+ "bin": {
6
+ "legal-data-hunter": "bin/setup.js"
7
+ },
8
+ "keywords": [
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "legal",
12
+ "ai",
13
+ "claude",
14
+ "cursor",
15
+ "vscode",
16
+ "law",
17
+ "case-law",
18
+ "legislation"
19
+ ],
20
+ "homepage": "https://legaldatahunter.com/connect",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/worldwidelaw/legal-sources"
24
+ },
25
+ "author": "Legal Data Hunter",
26
+ "license": "MIT",
27
+ "engines": {
28
+ "node": ">=16"
29
+ },
30
+ "files": [
31
+ "bin/setup.js",
32
+ "README.md"
33
+ ]
34
+ }