german-rhymes-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 +131 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
- package/src/index.ts +304 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# German Rhymes MCP Server 🎤
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that provides German rhyme search capabilities using the [double-rhyme.com](https://double-rhyme.com/) API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔍 **Find Rhymes**: Search for German rhymes for any word or phrase
|
|
8
|
+
- 🎯 **Double Rhymes**: Support for multi-word phrases (Doppelreime)
|
|
9
|
+
- 📊 **Multiple Match Types**: Exact rhymes, vowel matches, ending rhymes
|
|
10
|
+
- 🚀 **No Authentication Required**: The underlying API needs no API key
|
|
11
|
+
- 💨 **Fast**: Simple GET requests to the API
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd rhyme-mcp
|
|
17
|
+
pnpm install
|
|
18
|
+
pnpm build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage with Cursor
|
|
22
|
+
|
|
23
|
+
Add to your Cursor settings (`~/.cursor/mcp.json` or Cursor Settings → MCP):
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"german-rhymes": {
|
|
29
|
+
"command": "node",
|
|
30
|
+
"args": ["/path/to/rhyme-mcp/dist/index.js"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or for development:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"german-rhymes": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["tsx", "/path/to/rhyme-mcp/src/index.ts"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Available Tools
|
|
50
|
+
|
|
51
|
+
### `find_rhymes`
|
|
52
|
+
|
|
53
|
+
Finds German rhymes for a word or phrase with detailed output.
|
|
54
|
+
|
|
55
|
+
**Parameters:**
|
|
56
|
+
- `word` (required): The word or phrase to find rhymes for
|
|
57
|
+
- `match_type` (optional): Type of matching - `best` (default), `vowel`, or `ending`
|
|
58
|
+
- `limit` (optional): Maximum number of rhymes to return
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
```
|
|
62
|
+
Find rhymes for "Liebe"
|
|
63
|
+
Find rhymes for "kiefer knochen" (double rhyme)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `find_rhymes_json`
|
|
67
|
+
|
|
68
|
+
Same as `find_rhymes` but returns JSON output for programmatic use.
|
|
69
|
+
|
|
70
|
+
### `quick_rhymes`
|
|
71
|
+
|
|
72
|
+
Quick rhyme suggestions - returns only the best rhymes as a simple list.
|
|
73
|
+
|
|
74
|
+
**Parameters:**
|
|
75
|
+
- `word` (required): The word to find rhymes for
|
|
76
|
+
- `count` (optional): Number of rhymes (default: 10)
|
|
77
|
+
|
|
78
|
+
## Example Output
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
🎤 Reime für "Haus"
|
|
82
|
+
|
|
83
|
+
Gefunden: 48 Reime insgesamt
|
|
84
|
+
|
|
85
|
+
✨ **Exakte Reime** (10):
|
|
86
|
+
aus, raus, draus, maus, baus, strauß, graus, braus, staus, blaus
|
|
87
|
+
|
|
88
|
+
🎯 **Kuratierte Reime** (3):
|
|
89
|
+
schmaus, laus, kraus
|
|
90
|
+
|
|
91
|
+
📝 **Ähnliche Reime** (15):
|
|
92
|
+
applaus, voraus, hinaus, heraus, daraus, durchaus, hieraus...
|
|
93
|
+
|
|
94
|
+
🔊 **Vokal-Übereinstimmungen** (20):
|
|
95
|
+
auf, auch, drauf, glaub, schau, sound, faust, raum...
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Run in development mode
|
|
102
|
+
pnpm dev
|
|
103
|
+
|
|
104
|
+
# Build for production
|
|
105
|
+
pnpm build
|
|
106
|
+
|
|
107
|
+
# Watch mode
|
|
108
|
+
pnpm watch
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## API Reference
|
|
112
|
+
|
|
113
|
+
This MCP server uses the double-rhyme.com API:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
GET https://us-central1-double-rhyme-website.cloudfunctions.net/rhyme-proxy
|
|
117
|
+
?searchterm=<word>
|
|
118
|
+
&language=de
|
|
119
|
+
&matchType=vowel_rhyme_consonantal_ending
|
|
120
|
+
&genre=rap
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Match Types:**
|
|
124
|
+
- `vowel_rhyme_consonantal_ending` - Best for rap/poetry
|
|
125
|
+
- `vowel_rhyme` - Vowel sound matches only
|
|
126
|
+
- `ending_rhyme` - Traditional end rhymes
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
|
131
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
// API Configuration
|
|
6
|
+
const API_URL = "https://us-central1-double-rhyme-website.cloudfunctions.net/rhyme-proxy";
|
|
7
|
+
/**
|
|
8
|
+
* Fetch rhymes from the double-rhyme.com API
|
|
9
|
+
*/
|
|
10
|
+
async function fetchRhymes(searchTerm, matchType = "vowel_rhyme_consonantal_ending") {
|
|
11
|
+
const params = new URLSearchParams({
|
|
12
|
+
searchterm: searchTerm,
|
|
13
|
+
language: "de",
|
|
14
|
+
matchType,
|
|
15
|
+
genre: "rap",
|
|
16
|
+
});
|
|
17
|
+
const response = await fetch(`${API_URL}?${params}`);
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
|
|
20
|
+
}
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
if (data.errors) {
|
|
23
|
+
throw new Error(`API error: ${data.errors}`);
|
|
24
|
+
}
|
|
25
|
+
const results = data.typeToResultMap || {};
|
|
26
|
+
const exactRhymes = results.ending2 || [];
|
|
27
|
+
const closeRhymes = results.ending1 || [];
|
|
28
|
+
const vowelMatches = results.v || [];
|
|
29
|
+
const curatedRhymes = results.custom || [];
|
|
30
|
+
const userSubmitted = results.user || [];
|
|
31
|
+
// Combine all rhymes and deduplicate
|
|
32
|
+
const allRhymes = [...new Set([
|
|
33
|
+
...exactRhymes,
|
|
34
|
+
...curatedRhymes,
|
|
35
|
+
...closeRhymes,
|
|
36
|
+
...vowelMatches,
|
|
37
|
+
...userSubmitted,
|
|
38
|
+
])];
|
|
39
|
+
return {
|
|
40
|
+
exactRhymes,
|
|
41
|
+
closeRhymes,
|
|
42
|
+
vowelMatches,
|
|
43
|
+
curatedRhymes,
|
|
44
|
+
userSubmitted,
|
|
45
|
+
allRhymes,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format rhyme results for display
|
|
50
|
+
*/
|
|
51
|
+
function formatRhymeResults(word, results) {
|
|
52
|
+
const sections = [];
|
|
53
|
+
sections.push(`🎤 Reime für "${word}"\n`);
|
|
54
|
+
sections.push(`Gefunden: ${results.allRhymes.length} Reime insgesamt\n`);
|
|
55
|
+
if (results.exactRhymes.length > 0) {
|
|
56
|
+
sections.push(`\n✨ **Exakte Reime** (${results.exactRhymes.length}):`);
|
|
57
|
+
sections.push(results.exactRhymes.slice(0, 20).join(", "));
|
|
58
|
+
if (results.exactRhymes.length > 20) {
|
|
59
|
+
sections.push(`... und ${results.exactRhymes.length - 20} weitere`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (results.curatedRhymes.length > 0) {
|
|
63
|
+
sections.push(`\n🎯 **Kuratierte Reime** (${results.curatedRhymes.length}):`);
|
|
64
|
+
sections.push(results.curatedRhymes.slice(0, 15).join(", "));
|
|
65
|
+
}
|
|
66
|
+
if (results.closeRhymes.length > 0) {
|
|
67
|
+
sections.push(`\n📝 **Ähnliche Reime** (${results.closeRhymes.length}):`);
|
|
68
|
+
sections.push(results.closeRhymes.slice(0, 15).join(", "));
|
|
69
|
+
if (results.closeRhymes.length > 15) {
|
|
70
|
+
sections.push(`... und ${results.closeRhymes.length - 15} weitere`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (results.vowelMatches.length > 0) {
|
|
74
|
+
sections.push(`\n🔊 **Vokal-Übereinstimmungen** (${results.vowelMatches.length}):`);
|
|
75
|
+
sections.push(results.vowelMatches.slice(0, 20).join(", "));
|
|
76
|
+
if (results.vowelMatches.length > 20) {
|
|
77
|
+
sections.push(`... und ${results.vowelMatches.length - 20} weitere`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (results.userSubmitted.length > 0) {
|
|
81
|
+
sections.push(`\n👥 **Community Reime** (${results.userSubmitted.length}):`);
|
|
82
|
+
sections.push(results.userSubmitted.slice(0, 10).join(", "));
|
|
83
|
+
}
|
|
84
|
+
return sections.join("\n");
|
|
85
|
+
}
|
|
86
|
+
// Create the MCP server
|
|
87
|
+
const server = new McpServer({
|
|
88
|
+
name: "german-rhymes",
|
|
89
|
+
version: "1.0.0",
|
|
90
|
+
});
|
|
91
|
+
// Register the rhyme search tool
|
|
92
|
+
server.tool("find_rhymes", "Findet deutsche Reime für ein Wort oder eine Phrase. Unterstützt auch Doppelreime (mehrere Wörter).", {
|
|
93
|
+
word: z.string().describe("Das Wort oder die Phrase, für die Reime gesucht werden sollen"),
|
|
94
|
+
match_type: z.enum(["best", "vowel", "ending"]).optional().describe("Art der Reim-Übereinstimmung: 'best' (Standard, beste Ergebnisse), 'vowel' (nur Vokalübereinstimmung), 'ending' (nur Endreime)"),
|
|
95
|
+
limit: z.number().optional().describe("Maximale Anzahl der zurückgegebenen Reime (Standard: alle)"),
|
|
96
|
+
}, async ({ word, match_type, limit }) => {
|
|
97
|
+
try {
|
|
98
|
+
// Map match_type to API matchType
|
|
99
|
+
const matchTypeMap = {
|
|
100
|
+
best: "vowel_rhyme_consonantal_ending",
|
|
101
|
+
vowel: "vowel_rhyme",
|
|
102
|
+
ending: "ending_rhyme",
|
|
103
|
+
};
|
|
104
|
+
const apiMatchType = matchTypeMap[match_type || "best"] || "vowel_rhyme_consonantal_ending";
|
|
105
|
+
const results = await fetchRhymes(word, apiMatchType);
|
|
106
|
+
// Apply limit if specified
|
|
107
|
+
if (limit && limit > 0) {
|
|
108
|
+
results.allRhymes = results.allRhymes.slice(0, limit);
|
|
109
|
+
results.exactRhymes = results.exactRhymes.slice(0, Math.ceil(limit / 3));
|
|
110
|
+
results.vowelMatches = results.vowelMatches.slice(0, Math.ceil(limit / 3));
|
|
111
|
+
results.closeRhymes = results.closeRhymes.slice(0, Math.ceil(limit / 3));
|
|
112
|
+
}
|
|
113
|
+
const formattedOutput = formatRhymeResults(word, results);
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: formattedOutput,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `❌ Fehler beim Suchen von Reimen für "${word}": ${errorMessage}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Register a tool to get rhymes as JSON (for programmatic use)
|
|
137
|
+
server.tool("find_rhymes_json", "Findet deutsche Reime und gibt sie als JSON zurück (für programmatische Verwendung).", {
|
|
138
|
+
word: z.string().describe("Das Wort oder die Phrase, für die Reime gesucht werden sollen"),
|
|
139
|
+
match_type: z.enum(["best", "vowel", "ending"]).optional().describe("Art der Reim-Übereinstimmung"),
|
|
140
|
+
}, async ({ word, match_type }) => {
|
|
141
|
+
try {
|
|
142
|
+
const matchTypeMap = {
|
|
143
|
+
best: "vowel_rhyme_consonantal_ending",
|
|
144
|
+
vowel: "vowel_rhyme",
|
|
145
|
+
ending: "ending_rhyme",
|
|
146
|
+
};
|
|
147
|
+
const apiMatchType = matchTypeMap[match_type || "best"] || "vowel_rhyme_consonantal_ending";
|
|
148
|
+
const results = await fetchRhymes(word, apiMatchType);
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: JSON.stringify({
|
|
154
|
+
query: word,
|
|
155
|
+
matchType: match_type || "best",
|
|
156
|
+
results: {
|
|
157
|
+
exactRhymes: results.exactRhymes,
|
|
158
|
+
closeRhymes: results.closeRhymes,
|
|
159
|
+
vowelMatches: results.vowelMatches,
|
|
160
|
+
curatedRhymes: results.curatedRhymes,
|
|
161
|
+
userSubmitted: results.userSubmitted,
|
|
162
|
+
total: results.allRhymes.length,
|
|
163
|
+
},
|
|
164
|
+
}, null, 2),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: JSON.stringify({ error: errorMessage, query: word }, null, 2),
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
isError: true,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// Register a tool for quick rhyme suggestions (concise output)
|
|
183
|
+
server.tool("quick_rhymes", "Schnelle Reim-Vorschläge - gibt nur die besten Reime als Liste zurück.", {
|
|
184
|
+
word: z.string().describe("Das Wort für das Reime gesucht werden"),
|
|
185
|
+
count: z.number().optional().describe("Anzahl der Reime (Standard: 10)"),
|
|
186
|
+
}, async ({ word, count }) => {
|
|
187
|
+
try {
|
|
188
|
+
const results = await fetchRhymes(word);
|
|
189
|
+
const limit = count || 10;
|
|
190
|
+
// Get the best rhymes (prioritize exact, then curated, then close)
|
|
191
|
+
const bestRhymes = [
|
|
192
|
+
...results.exactRhymes,
|
|
193
|
+
...results.curatedRhymes,
|
|
194
|
+
...results.closeRhymes,
|
|
195
|
+
].slice(0, limit);
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `Reime für "${word}": ${bestRhymes.join(", ")}`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: "text",
|
|
211
|
+
text: `Fehler: ${errorMessage}`,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
isError: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
// Main function to start the server
|
|
219
|
+
async function main() {
|
|
220
|
+
const transport = new StdioServerTransport();
|
|
221
|
+
await server.connect(transport);
|
|
222
|
+
console.error("German Rhymes MCP Server running on stdio");
|
|
223
|
+
}
|
|
224
|
+
main().catch((error) => {
|
|
225
|
+
console.error("Failed to start server:", error);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAwBxB,oBAAoB;AACpB,MAAM,OAAO,GAAG,yEAAyE,CAAC;AAI1F;;GAEG;AACH,KAAK,UAAU,WAAW,CACxB,UAAkB,EAClB,YAAuB,gCAAgC;IAEvD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,IAAI;QACd,SAAS;QACT,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;IAErD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,IAAI,GAAqB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAErD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC;IAE3C,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IAC3C,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IAEzC,qCAAqC;IACrC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC;YAC5B,GAAG,WAAW;YACd,GAAG,aAAa;YAChB,GAAG,WAAW;YACd,GAAG,YAAY;YACf,GAAG,aAAa;SACjB,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,WAAW;QACX,WAAW;QACX,YAAY;QACZ,aAAa;QACb,aAAa;QACb,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAY,EAAE,OAAoB;IAC5D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,KAAK,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,SAAS,CAAC,MAAM,oBAAoB,CAAC,CAAC;IAEzE,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE,UAAU,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,CAAC;QAC9E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CAAC,4BAA4B,OAAO,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE,UAAU,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,qCAAqC,OAAO,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;QACpF,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,EAAE,UAAU,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,6BAA6B,OAAO,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,CAAC;QAC7E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,wBAAwB;AACxB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,iCAAiC;AACjC,MAAM,CAAC,IAAI,CACT,aAAa,EACb,qGAAqG,EACrG;IACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;IAC1F,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CACjE,gIAAgI,CACjI;IACD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4DAA4D,CAAC;CACpG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE;IACpC,IAAI,CAAC;QACH,kCAAkC;QAClC,MAAM,YAAY,GAA8B;YAC9C,IAAI,EAAE,gCAAgC;YACtC,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,cAAc;SACvB,CAAC;QAEF,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,gCAAgC,CAAC;QAE5F,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAEtD,2BAA2B;QAC3B,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACtD,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YACzE,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAC3E,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE1D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,eAAe;iBACtB;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;QACnF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,wCAAwC,IAAI,MAAM,YAAY,EAAE;iBACvE;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,sFAAsF,EACtF;IACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;IAC1F,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;CACpG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;IAC7B,IAAI,CAAC;QACH,MAAM,YAAY,GAA8B;YAC9C,IAAI,EAAE,gCAAgC;YACtC,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,cAAc;SACvB,CAAC;QAEF,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,gCAAgC,CAAC;QAE5F,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAEtD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,KAAK,EAAE,IAAI;wBACX,SAAS,EAAE,UAAU,IAAI,MAAM;wBAC/B,OAAO,EAAE;4BACP,WAAW,EAAE,OAAO,CAAC,WAAW;4BAChC,WAAW,EAAE,OAAO,CAAC,WAAW;4BAChC,YAAY,EAAE,OAAO,CAAC,YAAY;4BAClC,aAAa,EAAE,OAAO,CAAC,aAAa;4BACpC,aAAa,EAAE,OAAO,CAAC,aAAa;4BACpC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;yBAChC;qBACF,EAAE,IAAI,EAAE,CAAC,CAAC;iBACZ;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;QACnF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;iBACpE;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,IAAI,CACT,cAAc,EACd,wEAAwE,EACxE;IACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAClE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;CACzE,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;QAE1B,mEAAmE;QACnE,MAAM,UAAU,GAAG;YACjB,GAAG,OAAO,CAAC,WAAW;YACtB,GAAG,OAAO,CAAC,aAAa;YACxB,GAAG,OAAO,CAAC,WAAW;SACvB,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAElB,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,cAAc,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;iBACtD;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;QACnF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,WAAW,YAAY,EAAE;iBAChC;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,oCAAoC;AACpC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;AAC7D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "german-rhymes-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for finding German rhymes using double-rhyme.com API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"german-rhymes-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"watch": "tsc --watch"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"rhymes",
|
|
19
|
+
"german",
|
|
20
|
+
"poetry",
|
|
21
|
+
"rap"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
|
+
"zod": "^3.23.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"tsx": "^4.0.0",
|
|
32
|
+
"typescript": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
// Types for the API response
|
|
8
|
+
interface RhymeApiResponse {
|
|
9
|
+
typeToResultMap: {
|
|
10
|
+
ending2?: string[]; // Best exact rhymes
|
|
11
|
+
ending1?: string[]; // Close ending matches
|
|
12
|
+
v?: string[]; // Vowel sound matches
|
|
13
|
+
custom?: string[]; // Curated rhymes
|
|
14
|
+
user?: string[]; // User-submitted rhymes
|
|
15
|
+
};
|
|
16
|
+
requestType: string;
|
|
17
|
+
errors: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface RhymeResult {
|
|
21
|
+
exactRhymes: string[];
|
|
22
|
+
closeRhymes: string[];
|
|
23
|
+
vowelMatches: string[];
|
|
24
|
+
curatedRhymes: string[];
|
|
25
|
+
userSubmitted: string[];
|
|
26
|
+
allRhymes: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// API Configuration
|
|
30
|
+
const API_URL = "https://us-central1-double-rhyme-website.cloudfunctions.net/rhyme-proxy";
|
|
31
|
+
|
|
32
|
+
type MatchType = "vowel_rhyme_consonantal_ending" | "vowel_rhyme" | "ending_rhyme";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetch rhymes from the double-rhyme.com API
|
|
36
|
+
*/
|
|
37
|
+
async function fetchRhymes(
|
|
38
|
+
searchTerm: string,
|
|
39
|
+
matchType: MatchType = "vowel_rhyme_consonantal_ending"
|
|
40
|
+
): Promise<RhymeResult> {
|
|
41
|
+
const params = new URLSearchParams({
|
|
42
|
+
searchterm: searchTerm,
|
|
43
|
+
language: "de",
|
|
44
|
+
matchType,
|
|
45
|
+
genre: "rap",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const response = await fetch(`${API_URL}?${params}`);
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data: RhymeApiResponse = await response.json();
|
|
55
|
+
|
|
56
|
+
if (data.errors) {
|
|
57
|
+
throw new Error(`API error: ${data.errors}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const results = data.typeToResultMap || {};
|
|
61
|
+
|
|
62
|
+
const exactRhymes = results.ending2 || [];
|
|
63
|
+
const closeRhymes = results.ending1 || [];
|
|
64
|
+
const vowelMatches = results.v || [];
|
|
65
|
+
const curatedRhymes = results.custom || [];
|
|
66
|
+
const userSubmitted = results.user || [];
|
|
67
|
+
|
|
68
|
+
// Combine all rhymes and deduplicate
|
|
69
|
+
const allRhymes = [...new Set([
|
|
70
|
+
...exactRhymes,
|
|
71
|
+
...curatedRhymes,
|
|
72
|
+
...closeRhymes,
|
|
73
|
+
...vowelMatches,
|
|
74
|
+
...userSubmitted,
|
|
75
|
+
])];
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
exactRhymes,
|
|
79
|
+
closeRhymes,
|
|
80
|
+
vowelMatches,
|
|
81
|
+
curatedRhymes,
|
|
82
|
+
userSubmitted,
|
|
83
|
+
allRhymes,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Format rhyme results for display
|
|
89
|
+
*/
|
|
90
|
+
function formatRhymeResults(word: string, results: RhymeResult): string {
|
|
91
|
+
const sections: string[] = [];
|
|
92
|
+
|
|
93
|
+
sections.push(`🎤 Reime für "${word}"\n`);
|
|
94
|
+
sections.push(`Gefunden: ${results.allRhymes.length} Reime insgesamt\n`);
|
|
95
|
+
|
|
96
|
+
if (results.exactRhymes.length > 0) {
|
|
97
|
+
sections.push(`\n✨ **Exakte Reime** (${results.exactRhymes.length}):`);
|
|
98
|
+
sections.push(results.exactRhymes.slice(0, 20).join(", "));
|
|
99
|
+
if (results.exactRhymes.length > 20) {
|
|
100
|
+
sections.push(`... und ${results.exactRhymes.length - 20} weitere`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (results.curatedRhymes.length > 0) {
|
|
105
|
+
sections.push(`\n🎯 **Kuratierte Reime** (${results.curatedRhymes.length}):`);
|
|
106
|
+
sections.push(results.curatedRhymes.slice(0, 15).join(", "));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (results.closeRhymes.length > 0) {
|
|
110
|
+
sections.push(`\n📝 **Ähnliche Reime** (${results.closeRhymes.length}):`);
|
|
111
|
+
sections.push(results.closeRhymes.slice(0, 15).join(", "));
|
|
112
|
+
if (results.closeRhymes.length > 15) {
|
|
113
|
+
sections.push(`... und ${results.closeRhymes.length - 15} weitere`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (results.vowelMatches.length > 0) {
|
|
118
|
+
sections.push(`\n🔊 **Vokal-Übereinstimmungen** (${results.vowelMatches.length}):`);
|
|
119
|
+
sections.push(results.vowelMatches.slice(0, 20).join(", "));
|
|
120
|
+
if (results.vowelMatches.length > 20) {
|
|
121
|
+
sections.push(`... und ${results.vowelMatches.length - 20} weitere`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (results.userSubmitted.length > 0) {
|
|
126
|
+
sections.push(`\n👥 **Community Reime** (${results.userSubmitted.length}):`);
|
|
127
|
+
sections.push(results.userSubmitted.slice(0, 10).join(", "));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return sections.join("\n");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Create the MCP server
|
|
134
|
+
const server = new McpServer({
|
|
135
|
+
name: "german-rhymes",
|
|
136
|
+
version: "1.0.0",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Register the rhyme search tool
|
|
140
|
+
server.tool(
|
|
141
|
+
"find_rhymes",
|
|
142
|
+
"Findet deutsche Reime für ein Wort oder eine Phrase. Unterstützt auch Doppelreime (mehrere Wörter).",
|
|
143
|
+
{
|
|
144
|
+
word: z.string().describe("Das Wort oder die Phrase, für die Reime gesucht werden sollen"),
|
|
145
|
+
match_type: z.enum(["best", "vowel", "ending"]).optional().describe(
|
|
146
|
+
"Art der Reim-Übereinstimmung: 'best' (Standard, beste Ergebnisse), 'vowel' (nur Vokalübereinstimmung), 'ending' (nur Endreime)"
|
|
147
|
+
),
|
|
148
|
+
limit: z.number().optional().describe("Maximale Anzahl der zurückgegebenen Reime (Standard: alle)"),
|
|
149
|
+
},
|
|
150
|
+
async ({ word, match_type, limit }) => {
|
|
151
|
+
try {
|
|
152
|
+
// Map match_type to API matchType
|
|
153
|
+
const matchTypeMap: Record<string, MatchType> = {
|
|
154
|
+
best: "vowel_rhyme_consonantal_ending",
|
|
155
|
+
vowel: "vowel_rhyme",
|
|
156
|
+
ending: "ending_rhyme",
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const apiMatchType = matchTypeMap[match_type || "best"] || "vowel_rhyme_consonantal_ending";
|
|
160
|
+
|
|
161
|
+
const results = await fetchRhymes(word, apiMatchType);
|
|
162
|
+
|
|
163
|
+
// Apply limit if specified
|
|
164
|
+
if (limit && limit > 0) {
|
|
165
|
+
results.allRhymes = results.allRhymes.slice(0, limit);
|
|
166
|
+
results.exactRhymes = results.exactRhymes.slice(0, Math.ceil(limit / 3));
|
|
167
|
+
results.vowelMatches = results.vowelMatches.slice(0, Math.ceil(limit / 3));
|
|
168
|
+
results.closeRhymes = results.closeRhymes.slice(0, Math.ceil(limit / 3));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const formattedOutput = formatRhymeResults(word, results);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "text" as const,
|
|
177
|
+
text: formattedOutput,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
183
|
+
return {
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text" as const,
|
|
187
|
+
text: `❌ Fehler beim Suchen von Reimen für "${word}": ${errorMessage}`,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
isError: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Register a tool to get rhymes as JSON (for programmatic use)
|
|
197
|
+
server.tool(
|
|
198
|
+
"find_rhymes_json",
|
|
199
|
+
"Findet deutsche Reime und gibt sie als JSON zurück (für programmatische Verwendung).",
|
|
200
|
+
{
|
|
201
|
+
word: z.string().describe("Das Wort oder die Phrase, für die Reime gesucht werden sollen"),
|
|
202
|
+
match_type: z.enum(["best", "vowel", "ending"]).optional().describe("Art der Reim-Übereinstimmung"),
|
|
203
|
+
},
|
|
204
|
+
async ({ word, match_type }) => {
|
|
205
|
+
try {
|
|
206
|
+
const matchTypeMap: Record<string, MatchType> = {
|
|
207
|
+
best: "vowel_rhyme_consonantal_ending",
|
|
208
|
+
vowel: "vowel_rhyme",
|
|
209
|
+
ending: "ending_rhyme",
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const apiMatchType = matchTypeMap[match_type || "best"] || "vowel_rhyme_consonantal_ending";
|
|
213
|
+
|
|
214
|
+
const results = await fetchRhymes(word, apiMatchType);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text" as const,
|
|
220
|
+
text: JSON.stringify({
|
|
221
|
+
query: word,
|
|
222
|
+
matchType: match_type || "best",
|
|
223
|
+
results: {
|
|
224
|
+
exactRhymes: results.exactRhymes,
|
|
225
|
+
closeRhymes: results.closeRhymes,
|
|
226
|
+
vowelMatches: results.vowelMatches,
|
|
227
|
+
curatedRhymes: results.curatedRhymes,
|
|
228
|
+
userSubmitted: results.userSubmitted,
|
|
229
|
+
total: results.allRhymes.length,
|
|
230
|
+
},
|
|
231
|
+
}, null, 2),
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
237
|
+
return {
|
|
238
|
+
content: [
|
|
239
|
+
{
|
|
240
|
+
type: "text" as const,
|
|
241
|
+
text: JSON.stringify({ error: errorMessage, query: word }, null, 2),
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
isError: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Register a tool for quick rhyme suggestions (concise output)
|
|
251
|
+
server.tool(
|
|
252
|
+
"quick_rhymes",
|
|
253
|
+
"Schnelle Reim-Vorschläge - gibt nur die besten Reime als Liste zurück.",
|
|
254
|
+
{
|
|
255
|
+
word: z.string().describe("Das Wort für das Reime gesucht werden"),
|
|
256
|
+
count: z.number().optional().describe("Anzahl der Reime (Standard: 10)"),
|
|
257
|
+
},
|
|
258
|
+
async ({ word, count }) => {
|
|
259
|
+
try {
|
|
260
|
+
const results = await fetchRhymes(word);
|
|
261
|
+
const limit = count || 10;
|
|
262
|
+
|
|
263
|
+
// Get the best rhymes (prioritize exact, then curated, then close)
|
|
264
|
+
const bestRhymes = [
|
|
265
|
+
...results.exactRhymes,
|
|
266
|
+
...results.curatedRhymes,
|
|
267
|
+
...results.closeRhymes,
|
|
268
|
+
].slice(0, limit);
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: "text" as const,
|
|
274
|
+
text: `Reime für "${word}": ${bestRhymes.join(", ")}`,
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
} catch (error) {
|
|
279
|
+
const errorMessage = error instanceof Error ? error.message : "Unbekannter Fehler";
|
|
280
|
+
return {
|
|
281
|
+
content: [
|
|
282
|
+
{
|
|
283
|
+
type: "text" as const,
|
|
284
|
+
text: `Fehler: ${errorMessage}`,
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
isError: true,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Main function to start the server
|
|
294
|
+
async function main() {
|
|
295
|
+
const transport = new StdioServerTransport();
|
|
296
|
+
await server.connect(transport);
|
|
297
|
+
console.error("German Rhymes MCP Server running on stdio");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
main().catch((error) => {
|
|
301
|
+
console.error("Failed to start server:", error);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
});
|
|
304
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
|
19
|
+
|