@jurislm/judicial-mcp 1.3.0 → 1.6.3

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 CHANGED
@@ -1,6 +1,47 @@
1
1
  # @jurislm/judicial-mcp
2
2
 
3
- MCP (Model Context Protocol) server for [Taiwan Judicial Yuan](https://www.judicial.gov.tw) — provides 7 tools for accessing open data and judgments via natural language.
3
+ MCP (Model Context Protocol) server for Taiwan's Judicial Yuan public judgments and open data, plus current regulations from the Ministry of Justice National Laws and Regulations Database. It provides 10 tools.
4
+
5
+ ## Public Judgment Search
6
+
7
+ `search_judgments` searches the Judicial Yuan's public judgment website. `fetch_judgment_text` retrieves the full text for a result. Both tools use the public website and do not require an API token or Judicial Yuan account.
8
+
9
+ Search accepts any combination of these filters (at least one is required):
10
+
11
+ | Field | Meaning |
12
+ |-------|---------|
13
+ | `keyword` | Keyword in judgment text, up to 128 characters |
14
+ | `court` | Official court name or code, up to 80 characters |
15
+ | `year` | Decision year as a Gregorian or Minguo integer |
16
+ | `caseNumber` | Case number, either digits or a full form such as `113年度訴字第1234號` |
17
+ | `limit` | Results per page, 1–10; defaults to 5 |
18
+ | `page` | Official result page, 1–50; defaults to 1 |
19
+ | `offset` | Starting row within an official result page, 0–19; defaults to 0 |
20
+
21
+ Each search result includes `case_id` (pass this as `caseId` to `fetch_judgment_text`), `case_number`, `date` (ISO date or `null`), `court`, `cause`, an optional `summary`, `source_url`, `search_source_url`, `retrieved_at`, `cache`, `truncated`, `partial`, and `source_version`. The search response also includes `count`, `ambiguous`, `results`, `source_url`, `retrieved_at`, `cache`, `truncated`, `partial`, `next_page`, `next_offset`, and `source_version`. Follow both `next_page` and `next_offset` to retrieve every result; `next_offset` may point to another batch on the same official page. An `ambiguous` case-number search returns its candidates for you to choose from rather than selecting one.
22
+
23
+ The full-text response includes `case_id`, `date`, `full_text`, `source_url`, `retrieved_at`, `cache`, `truncated`, `partial`, and `source_version`. Text is capped at 40,000 characters; when that cap is reached, `truncated` and `partial` are `true`. `cache.status` is `not_cached`; `source_version` is `null` when the official source provides no version identifier.
24
+
25
+ Only an explicit no-matches response from the official site is returned as an empty result. Upstream denial or failure, an unrecognized page, or a missing judgment is returned as an MCP error (`isError: true`) with `error.code`, `error.message`, `error.source_url`, and `error.retrieved_at`.
26
+
27
+ ## Regulation Lookup
28
+
29
+ `query_regulation` looks up one article by law name and article number in the Ministry of Justice's [National Laws and Regulations Database](https://law.moj.gov.tw/). The official autocomplete lookup is tokenless. HTML pages are scraped only through a Firecrawl service configured with `FIRECRAWL_API_URL`; set `FIRECRAWL_API_KEY` when that service requires a key. If Firecrawl is unavailable, the tool returns a source error without a direct-HTTP HTML fallback.
30
+
31
+ ```json
32
+ {
33
+ "lawName": "民法",
34
+ "articleNumber": "1"
35
+ }
36
+ ```
37
+
38
+ The tool resolves only a unique exact law-name match after normalizing whitespace and the common `台` / `臺` spelling. It returns candidates with `status: "ambiguous"` when the name does not resolve uniquely; it never chooses a fuzzy match. Article numbers use Arabic numerals and may include a hyphen, such as `1` or `1-1`.
39
+
40
+ Successful results include the regulation name and official identifier, one article's text, the official page URL, the lookup URL, retrieval time, and version metadata. The official page supplies consolidated current text; this tool does not provide historical versions. Amendment, promulgation, and effective dates are returned as ISO dates when the official page states them. Missing dates are `null` and mark the result `partial`; a pending or mixed effective date is identified in `version.effective_status`. `source_version` is the published amendment date, falling back to the promulgation date.
41
+
42
+ Responses cap article text at 30,000 characters. `truncated` and `partial` are `true` when text is capped or required version details are missing. `cache.status` is `miss`, `hit`, `refreshed`, `revalidated`, or `not_cached`. A process-local least-recently-used cache holds at most 100 exact law/article lookups for 15 minutes. Set `refresh: true` to bypass the cache. Expired entries are looked up and scraped again through Firecrawl; if an upstream refresh fails, the tool returns an error and does not serve stale text as current.
43
+
44
+ Upstream failures and unrecognized page changes are MCP errors with `error.code`, `error.message`, `error.source_url`, and `error.retrieved_at`. Confirmed no-match, abolished-law, and missing-article responses have distinct codes.
4
45
 
5
46
  ## Tools
6
47
 
@@ -8,8 +49,15 @@ MCP (Model Context Protocol) server for [Taiwan Judicial Yuan](https://www.judic
8
49
  - `auth_token` — Get authorization token for judgment API (uses `JUDICIAL_USER` / `JUDICIAL_PASSWORD` env vars by default)
9
50
  - `member_token` — Get member token for open data API (uses `JUDICIAL_USER` / `JUDICIAL_PASSWORD` env vars by default)
10
51
 
11
- ### Judgments (2 tools)
12
- - `list_judgments` — Get judgment change list (requires `auth_token`)
52
+ ### Public Judgments (2 tools)
53
+ - `search_judgments` — Search public judgments without a token
54
+ - `fetch_judgment_text` — Get a judgment's full text by `case_id`, without a token
55
+
56
+ ### Regulations (1 tool)
57
+ - `query_regulation` — Get one current regulation article and official version metadata, without a token
58
+
59
+ ### Token-based Judgments (2 tools)
60
+ - `list_judgments` — Get the judgment change list (requires `auth_token`)
13
61
  - `get_judgment` — Get judgment content by JID (requires `auth_token`)
14
62
 
15
63
  ### Open Data (3 tools)
@@ -21,14 +69,23 @@ MCP (Model Context Protocol) server for [Taiwan Judicial Yuan](https://www.judic
21
69
 
22
70
  ### Environment Variables
23
71
 
72
+ The public judgment search tools do not require credentials. These variables are used by the existing token-based judgment and open-data tools:
73
+
24
74
  ```bash
25
75
  JUDICIAL_USER=your_username
26
76
  JUDICIAL_PASSWORD=your_password
27
77
  ```
28
78
 
79
+ Regulation lookup also needs a Firecrawl service base URL. The server appends `/v1/scrape` and sends raw-HTML scrape requests only for official Ministry of Justice pages. The key is read from the environment and is optional when Firecrawl is configured to allow keyless access. Use HTTPS when the configured service is remote; a key is sent over HTTP only to a loopback address.
80
+
81
+ ```bash
82
+ FIRECRAWL_API_URL=https://your-firecrawl-host
83
+ FIRECRAWL_API_KEY=your_service_key
84
+ ```
85
+
29
86
  ### Usage with Claude Code (via bunx)
30
87
 
31
- Add to your MCP configuration (`.mcp.json` or `~/.claude/settings.json`):
88
+ Install Bun first, then add this stdio server to your MCP configuration (`.mcp.json` or `~/.claude/settings.json`). The public judgment tools work without credentials; regulation lookup needs the Firecrawl environment settings above:
32
89
 
33
90
  ```json
34
91
  {
@@ -37,8 +94,8 @@ Add to your MCP configuration (`.mcp.json` or `~/.claude/settings.json`):
37
94
  "command": "bunx",
38
95
  "args": ["@jurislm/judicial-mcp@latest"],
39
96
  "env": {
40
- "JUDICIAL_USER": "your_username",
41
- "JUDICIAL_PASSWORD": "your_password"
97
+ "FIRECRAWL_API_URL": "https://your-firecrawl-host",
98
+ "FIRECRAWL_API_KEY": "your_service_key"
42
99
  }
43
100
  }
44
101
  }
@@ -63,9 +120,11 @@ export JUDICIAL_PASSWORD=your_password
63
120
  ## Development
64
121
 
65
122
  ```bash
66
- npm install
67
- npm test # Run tests
68
- npm run lint # oxlint
123
+ bun install --frozen-lockfile
124
+ bun run test
125
+ bun run lint
126
+ bun run typecheck
127
+ bun run build:types
69
128
  ```
70
129
 
71
130
  ## License
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import '../src/index.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jurislm/judicial-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.6.3",
4
4
  "description": "MCP server for accessing Taiwan Judicial Yuan open data and judgments.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -22,18 +22,20 @@
22
22
  "name": "Terry Chen",
23
23
  "email": "zxtw17985321@gmail.com"
24
24
  },
25
- "type": "commonjs",
26
- "main": "src/index.js",
25
+ "type": "module",
26
+ "main": "src/index.ts",
27
27
  "types": "types/index.d.ts",
28
28
  "scripts": {
29
- "start": "node src/index.js",
30
- "dev": "node src/index.js",
31
- "test": "jest",
32
- "test:watch": "jest --watch",
33
- "test:coverage": "jest --coverage",
34
- "lint": "oxlint",
35
- "lint:fix": "oxlint --fix",
36
- "prepublishOnly": "npm test && npm run lint"
29
+ "start": "bun src/index.ts",
30
+ "dev": "bun src/index.ts",
31
+ "typecheck": "tsc --noEmit -p tsconfig.check.json",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage",
35
+ "lint": "eslint --max-warnings=0",
36
+ "lint:fix": "eslint --fix --max-warnings=0",
37
+ "build:types": "tsc --emitDeclarationOnly",
38
+ "prepublishOnly": "bun run typecheck && bun run test && bun run lint && bun run build:types"
37
39
  },
38
40
  "files": [
39
41
  "src",
@@ -42,18 +44,29 @@
42
44
  "README.md"
43
45
  ],
44
46
  "bin": {
45
- "judicial-mcp": "bin/judicial-mcp.js"
47
+ "judicial-mcp": "bin/judicial-mcp.ts"
46
48
  },
47
49
  "dependencies": {
48
50
  "@modelcontextprotocol/sdk": "^1.29.0",
49
51
  "axios": "^1.6.0",
52
+ "cheerio": "^1.2.0",
50
53
  "dotenv": "^17.0.0"
51
54
  },
52
- "devDependencies": {
53
- "jest": "^29.7.0",
54
- "oxlint": "^0.15.0"
55
+ "engines": {
56
+ "bun": ">=1.1.0"
55
57
  },
56
- "jest": {
57
- "testEnvironment": "node"
58
+ "packageManager": "bun@1.4.3",
59
+ "devDependencies": {
60
+ "@eslint/js": "^9.0.0",
61
+ "@types/node": "^26.6.5",
62
+ "@typescript-eslint/eslint-plugin": "^8.62.0",
63
+ "@typescript-eslint/parser": "^8.62.0",
64
+ "@vitest/coverage-v8": "3.2.4",
65
+ "eslint": "^9.0.0",
66
+ "eslint-config-prettier": "^10.0.0",
67
+ "globals": "^16.0.0",
68
+ "prettier": "^3.0.0",
69
+ "typescript": "^5.0.0",
70
+ "vitest": "^3.0.0"
58
71
  }
59
72
  }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ // MCP 使用 stdio transport,stdout 只能輸出 JSON-RPC 訊息。
2
+ // 覆寫必須在所有 import 模組初始化之前生效。
3
+ // ESM static import 會被 hoist 至頂層程式碼之前執行,因此這裡不放任何 static import,
4
+ // 改用 dynamic import 確保覆寫先於所有依賴模組的初始化。
5
+ const safeSerialize = (value: unknown): string => {
6
+ if (typeof value === 'string') return value
7
+ try {
8
+ const s = JSON.stringify(value)
9
+ return s === undefined ? String(value) : s
10
+ } catch {
11
+ return String(value)
12
+ }
13
+ }
14
+
15
+ console.log = console.info = console.warn = (...args: unknown[]) =>
16
+ process.stderr.write(args.map(safeSerialize).join(' ') + '\n')
17
+
18
+ await import('./server.js')
19
+
20
+ export {}