jisho-mcp 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.
- package/README.md +47 -0
- package/dist/api.d.ts +30 -0
- package/dist/api.js +77 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +63 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# jisho mcp
|
|
2
|
+
|
|
3
|
+
The [Jisho.org](https://jisho.org) Japanese English dictionary. Words,
|
|
4
|
+
readings, senses, JLPT levels. No key.
|
|
5
|
+
|
|
6
|
+
## Tools
|
|
7
|
+
|
|
8
|
+
- `search_words`, Japanese text, romaji, or English keywords
|
|
9
|
+
- `search_by_tag`, feature searches: `#common`, `jlpt-n1`…`jlpt-n5`,
|
|
10
|
+
`wanikani10`, English meanings
|
|
11
|
+
|
|
12
|
+
## Run
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install && npm run build && node dist/index.js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
> "What does 大丈夫 mean?"
|
|
21
|
+
> `search_words("daijoubu")` → 大丈夫 (だいじょうぶ), safe; sound; problem free [Na adj] ⭐common, JLPT N5
|
|
22
|
+
|
|
23
|
+
> "Give me common JLPT N5 words"
|
|
24
|
+
> `search_by_tag("jlpt-n5")`
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install
|
|
30
|
+
npm run build
|
|
31
|
+
node dist/index.js
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The server uses stdio, so it can be connected to Claude Desktop, Cursor, VS Code, MCP Inspector, or another compatible MCP client.
|
|
35
|
+
|
|
36
|
+
## Tools at a glance
|
|
37
|
+
|
|
38
|
+
- `search_words`: Search the Jisho Japanese-English dictionary. Accepts Japanese text,
|
|
39
|
+
- `search_by_tag`: Search Jisho by feature or tag:
|
|
40
|
+
|
|
41
|
+
## Limits and privacy
|
|
42
|
+
|
|
43
|
+
This project is intentionally narrow. It should be treated as a practical helper, not a complete certification or security audit. Check the implementation and the returned data before using it with sensitive material. No credentials are required unless the project explicitly says otherwise.
|
|
44
|
+
|
|
45
|
+
## Try it
|
|
46
|
+
|
|
47
|
+
After building, connect the server through your MCP client. The repository root also contains `smoke-test.mjs` for projects covered by the shared harness. A typical tool call starts with `search_words`.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jisho.org API v1 client — Japanese-English dictionary.
|
|
3
|
+
* Docs: https://jisho.org/forum/54fefc1f6e71740b1f000000 (unofficial)
|
|
4
|
+
* Endpoints: /api/v1/search/words?keyword=... and /api/v1/search/sentences?keyword=...
|
|
5
|
+
*/
|
|
6
|
+
export declare class JishoError extends Error {
|
|
7
|
+
}
|
|
8
|
+
export interface WordResult {
|
|
9
|
+
slug: string;
|
|
10
|
+
isCommon: boolean;
|
|
11
|
+
jlpt: string[];
|
|
12
|
+
tags: string[];
|
|
13
|
+
japanese: {
|
|
14
|
+
word?: string;
|
|
15
|
+
reading?: string;
|
|
16
|
+
}[];
|
|
17
|
+
senses: {
|
|
18
|
+
englishDefinitions: string[];
|
|
19
|
+
partsOfSpeech: string[];
|
|
20
|
+
tags: string[];
|
|
21
|
+
seeAlso: string[];
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
export declare function searchWords(keyword: string, limit?: number): Promise<WordResult[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Tag/feature searches the words endpoint supports: "#common", "jlpt-n5",
|
|
27
|
+
* "wanikani10", English meanings, etc.
|
|
28
|
+
*/
|
|
29
|
+
export declare function searchByTag(keyword: string, limit?: number): Promise<WordResult[]>;
|
|
30
|
+
export declare function formatWord(w: WordResult, index?: number): string;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jisho.org API v1 client — Japanese-English dictionary.
|
|
3
|
+
* Docs: https://jisho.org/forum/54fefc1f6e71740b1f000000 (unofficial)
|
|
4
|
+
* Endpoints: /api/v1/search/words?keyword=... and /api/v1/search/sentences?keyword=...
|
|
5
|
+
*/
|
|
6
|
+
const BASE = "https://jisho.org/api/v1/search";
|
|
7
|
+
export class JishoError extends Error {
|
|
8
|
+
}
|
|
9
|
+
async function search(kind, keyword) {
|
|
10
|
+
const res = await fetch(`${BASE}/${kind}?keyword=${encodeURIComponent(keyword)}`, {
|
|
11
|
+
headers: { Accept: "application/json", "User-Agent": "jisho-mcp/1.0" },
|
|
12
|
+
});
|
|
13
|
+
if (!res.ok)
|
|
14
|
+
throw new JishoError(`Jisho API error ${res.status}: ${res.statusText}`);
|
|
15
|
+
const data = (await res.json());
|
|
16
|
+
return data.data ?? [];
|
|
17
|
+
}
|
|
18
|
+
export async function searchWords(keyword, limit = 10) {
|
|
19
|
+
const results = await search("words", keyword);
|
|
20
|
+
return results.slice(0, limit).map((r) => ({
|
|
21
|
+
slug: r.slug,
|
|
22
|
+
isCommon: r.is_common,
|
|
23
|
+
jlpt: r.jlpt ?? [],
|
|
24
|
+
tags: r.tags ?? [],
|
|
25
|
+
japanese: r.japanese ?? [],
|
|
26
|
+
senses: (r.senses ?? []).map((s) => ({
|
|
27
|
+
englishDefinitions: s.english_definitions ?? [],
|
|
28
|
+
partsOfSpeech: s.parts_of_speech ?? [],
|
|
29
|
+
tags: s.tags ?? [],
|
|
30
|
+
seeAlso: s.see_also ?? [],
|
|
31
|
+
})),
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Tag/feature searches the words endpoint supports: "#common", "jlpt-n5",
|
|
36
|
+
* "wanikani10", English meanings, etc.
|
|
37
|
+
*/
|
|
38
|
+
export async function searchByTag(keyword, limit = 10) {
|
|
39
|
+
const results = await search("words", keyword);
|
|
40
|
+
return results.slice(0, limit).map((r) => ({
|
|
41
|
+
slug: r.slug,
|
|
42
|
+
isCommon: r.is_common,
|
|
43
|
+
jlpt: r.jlpt ?? [],
|
|
44
|
+
tags: r.tags ?? [],
|
|
45
|
+
japanese: r.japanese ?? [],
|
|
46
|
+
senses: (r.senses ?? []).map((s) => ({
|
|
47
|
+
englishDefinitions: s.english_definitions ?? [],
|
|
48
|
+
partsOfSpeech: s.parts_of_speech ?? [],
|
|
49
|
+
tags: s.tags ?? [],
|
|
50
|
+
seeAlso: s.see_also ?? [],
|
|
51
|
+
})),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Formatting
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
export function formatWord(w, index = 0) {
|
|
58
|
+
const head = w.japanese
|
|
59
|
+
.map((j) => `${j.word ?? ""}${j.reading ? ` (${j.reading})` : ""}`.trim())
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(" / ");
|
|
62
|
+
const lines = [`${index > 0 ? `${index}. ` : ""}${head || w.slug}`];
|
|
63
|
+
const firstSense = w.senses[0];
|
|
64
|
+
if (firstSense) {
|
|
65
|
+
const pos = firstSense.partsOfSpeech.length ? ` [${firstSense.partsOfSpeech.join(", ")}]` : "";
|
|
66
|
+
const defs = firstSense.englishDefinitions.slice(0, 4).join("; ");
|
|
67
|
+
lines.push(` ${defs}${pos}`);
|
|
68
|
+
}
|
|
69
|
+
if (w.isCommon)
|
|
70
|
+
lines[lines.length - 1] += " ⭐common";
|
|
71
|
+
if (w.jlpt.length)
|
|
72
|
+
lines.push(` JLPT: ${w.jlpt.join(", ")}`);
|
|
73
|
+
if (w.senses.length > 1) {
|
|
74
|
+
lines.push(` (+${w.senses.length - 1} more senses)`);
|
|
75
|
+
}
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
async function main() {
|
|
4
|
+
const server = createServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
console.error("Jisho MCP server running on stdio");
|
|
8
|
+
}
|
|
9
|
+
main().catch((err) => {
|
|
10
|
+
console.error("Fatal error:", err);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { JishoError, formatWord, searchByTag, searchWords, } from "./api.js";
|
|
4
|
+
const text = (t) => ({ content: [{ type: "text", text: t }] });
|
|
5
|
+
const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
|
|
6
|
+
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
7
|
+
export function createServer() {
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: "jisho-mcp",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
});
|
|
12
|
+
server.registerTool("search_words", {
|
|
13
|
+
title: "Search words",
|
|
14
|
+
description: "Search the Jisho Japanese-English dictionary. Accepts Japanese text, " +
|
|
15
|
+
"romaji, or English keywords.",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
keyword: z.string().describe("e.g. 'daijoubu', '大丈夫', or 'friendship'"),
|
|
18
|
+
limit: z.number().int().min(1).max(15).default(10).describe("Max results"),
|
|
19
|
+
}),
|
|
20
|
+
annotations: READ_ONLY,
|
|
21
|
+
}, async ({ keyword, limit }) => {
|
|
22
|
+
try {
|
|
23
|
+
const words = await searchWords(keyword, limit);
|
|
24
|
+
if (words.length === 0)
|
|
25
|
+
return text(`No results for "${keyword}".`);
|
|
26
|
+
return text(`Jisho results for "${keyword}":\n` +
|
|
27
|
+
words.map((w, i) => formatWord(w, i + 1)).join("\n"));
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return textError(errorMessage(e));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
server.registerTool("search_by_tag", {
|
|
34
|
+
title: "Search by tag",
|
|
35
|
+
description: "Search Jisho by feature or tag: '#common' for common words, " +
|
|
36
|
+
"'jlpt-n5' through 'jlpt-n1' for JLPT levels, 'wanikani5' etc., " +
|
|
37
|
+
"or any English meaning.",
|
|
38
|
+
inputSchema: z.object({
|
|
39
|
+
keyword: z.string().describe("e.g. '#common', 'jlpt-n4', 'wanikani10', or 'friendship'"),
|
|
40
|
+
limit: z.number().int().min(1).max(15).default(10).describe("Max results"),
|
|
41
|
+
}),
|
|
42
|
+
annotations: READ_ONLY,
|
|
43
|
+
}, async ({ keyword, limit }) => {
|
|
44
|
+
try {
|
|
45
|
+
const words = await searchByTag(keyword, limit);
|
|
46
|
+
if (words.length === 0)
|
|
47
|
+
return text(`No results for "${keyword}".`);
|
|
48
|
+
return text(`Jisho results for "${keyword}":\n` +
|
|
49
|
+
words.map((w, i) => formatWord(w, i + 1)).join("\n"));
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return textError(errorMessage(e));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return server;
|
|
56
|
+
}
|
|
57
|
+
function errorMessage(e) {
|
|
58
|
+
if (e instanceof JishoError)
|
|
59
|
+
return `Error: ${e.message}`;
|
|
60
|
+
if (e instanceof Error)
|
|
61
|
+
return `Error: ${e.message}`;
|
|
62
|
+
return `Error: ${String(e)}`;
|
|
63
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jisho-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Use this MCP server to Jisho, the Japanese English dictionary. Words, kanji, readings, and example sentences",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"mcpName": "io.github.mrfentmen/jisho-mcp",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/mrfentmen/jisho-mcp.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"jisho-mcp": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"dev": "rm -rf dist && tsc -p tsconfig.json && node dist/index.js",
|
|
22
|
+
"inspect": "npx @modelcontextprotocol/inspector node dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"jisho",
|
|
27
|
+
"japanese",
|
|
28
|
+
"dictionary",
|
|
29
|
+
"kanji",
|
|
30
|
+
"language",
|
|
31
|
+
"weeb"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
36
|
+
"zod": "^3.23.8"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"typescript": "^5.6.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
}
|
|
45
|
+
}
|