mcp-texttools 1.0.4 → 1.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.
Files changed (3) hide show
  1. package/README.md +3 -79
  2. package/index.js +140 -1
  3. package/package.json +16 -3
package/README.md CHANGED
@@ -1,84 +1,8 @@
1
- # mcp-texttools
2
1
 
3
- MCP server with 10 text transformation tools for AI assistants (Claude, Cursor, etc).
4
-
5
- ## Install
6
-
7
- ```bash
8
- npx mcp-texttools
9
- ```
10
-
11
- ### Claude Desktop
12
-
13
- Add to `claude_desktop_config.json`:
14
-
15
- ```json
16
- {
17
- "mcpServers": {
18
- "texttools": {
19
- "command": "npx",
20
- "args": ["-y", "mcp-texttools"]
21
- }
22
- }
23
- }
24
- ```
25
-
26
- ### Cursor
27
-
28
- Add to `.cursor/mcp.json`:
29
-
30
- ```json
31
- {
32
- "mcpServers": {
33
- "texttools": {
34
- "command": "npx",
35
- "args": ["-y", "mcp-texttools"]
36
- }
37
- }
38
- }
39
- ```
40
-
41
- ## Tools
42
-
43
- | Tool | Description |
44
- |------|-------------|
45
- | `case_convert` | Convert between camelCase, snake_case, kebab-case, PascalCase, CONSTANT_CASE, Title Case, dot.case |
46
- | `slugify` | Convert text to URL-friendly slug |
47
- | `word_count` | Count words, characters, sentences, paragraphs + reading time |
48
- | `lorem_ipsum` | Generate placeholder text (words, sentences, or paragraphs) |
49
- | `truncate` | Smart truncation at word/sentence/character boundaries |
50
- | `regex_replace` | Find & replace with regex (supports capture groups) |
51
- | `markdown_strip` | Strip markdown formatting to plain text |
52
- | `text_reverse` | Reverse by characters, words, or lines |
53
- | `line_sort` | Sort lines (alpha, numeric, length, random) + dedup |
54
- | `text_diff` | Compare two texts line by line |
55
-
56
- ## Examples
57
-
58
- **Convert case:**
59
- - Input: `"myVariableName"` → snake → `my_variable_name`
60
- - Input: `"my-api-endpoint"` → pascal → `MyApiEndpoint`
61
-
62
- **Slugify:**
63
- - Input: `"Hello World! This is a Test"` → `hello-world-this-is-a-test`
64
-
65
- **Word count:**
66
- - Returns words, characters, sentences, paragraphs, reading time
67
-
68
- ## Part of the MCP Dev Tools Suite
69
-
70
- - [mcp-devutils](https://www.npmjs.com/package/mcp-devutils) — UUID, hash, base64, timestamps, JWT decode, and more
71
- - [mcp-apitools](https://www.npmjs.com/package/mcp-apitools) — HTTP status, MIME types, JWT create, mock data, CORS headers
72
- - **mcp-texttools** — Text transformation and analysis (this package)
73
- - [mcp-mathtools](https://www.npmjs.com/package/mcp-mathtools) — 12 math & statistics tools: arithmetic, statistics, unit conversion, financial calculations
74
- - [mcp-datetime](https://www.npmjs.com/package/mcp-datetime) — 10 date & time tools: timezone conversion, date math, cron explanation, business days
75
- - [mcp-quick-calc](https://www.npmjs.com/package/mcp-quick-calc) — 5 calculator tools: currency conversion, percentages, compound interest, unit conversion, loan payments
76
- - **[mcp-all-tools](https://www.npmjs.com/package/mcp-all-tools)** — All 54+ tools in a single MCP server
77
2
 
78
3
  ## Support
79
4
 
80
- If you find this useful, consider [buying me a coffee](https://buymeacoffee.com/gl89tu25lp)!
81
-
82
- ## License
5
+ If this tool saves you time, consider supporting development:
83
6
 
84
- MIT
7
+ - [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
8
+ - [Tip via Stripe ($3)](https://buy.stripe.com/dRm8wP8R295Z9VyeN59Zm00)
package/index.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
5
 
6
6
  const server = new Server(
7
- { name: "mcp-texttools", version: "1.0.0" },
7
+ { name: "mcp-texttools", version: "1.1.0" },
8
8
  { capabilities: { tools: {} } }
9
9
  );
10
10
 
@@ -152,6 +152,69 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
152
152
  },
153
153
  required: ["text1", "text2"]
154
154
  }
155
+ },
156
+ {
157
+ name: "extract_emails",
158
+ description: "Extract all email addresses from text, with optional deduplication",
159
+ inputSchema: {
160
+ type: "object",
161
+ properties: {
162
+ text: { type: "string", description: "Text to extract emails from" },
163
+ unique: { type: "boolean", description: "Return only unique emails (default: true)" }
164
+ },
165
+ required: ["text"]
166
+ }
167
+ },
168
+ {
169
+ name: "extract_urls",
170
+ description: "Extract all URLs (http/https) from text, with optional deduplication",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ text: { type: "string", description: "Text to extract URLs from" },
175
+ unique: { type: "boolean", description: "Return only unique URLs (default: true)" }
176
+ },
177
+ required: ["text"]
178
+ }
179
+ },
180
+ {
181
+ name: "text_pad",
182
+ description: "Pad text to a fixed width — left, right, or center aligned",
183
+ inputSchema: {
184
+ type: "object",
185
+ properties: {
186
+ text: { type: "string", description: "Text to pad" },
187
+ width: { type: "number", description: "Target width in characters" },
188
+ align: { type: "string", enum: ["left", "right", "center"], description: "Alignment (default: right)" },
189
+ char: { type: "string", description: "Padding character (default: space)" }
190
+ },
191
+ required: ["text", "width"]
192
+ }
193
+ },
194
+ {
195
+ name: "text_wrap",
196
+ description: "Wrap text to a specified line width, breaking at word boundaries",
197
+ inputSchema: {
198
+ type: "object",
199
+ properties: {
200
+ text: { type: "string", description: "Text to wrap" },
201
+ width: { type: "number", description: "Maximum line width (default: 80)" }
202
+ },
203
+ required: ["text"]
204
+ }
205
+ },
206
+ {
207
+ name: "string_repeat",
208
+ description: "Repeat a string N times with an optional separator",
209
+ inputSchema: {
210
+ type: "object",
211
+ properties: {
212
+ text: { type: "string", description: "Text to repeat" },
213
+ count: { type: "number", description: "Number of repetitions (1-1000, default: 2)" },
214
+ separator: { type: "string", description: "Separator between repetitions (default: empty)" }
215
+ },
216
+ required: ["text"]
217
+ }
155
218
  }
156
219
  ]
157
220
  };
@@ -447,6 +510,82 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
447
510
  return { content: [{ type: "text", text: output.join("\n") + summary }] };
448
511
  }
449
512
 
513
+ case "extract_emails": {
514
+ const { text, unique = true } = args;
515
+ const emailRegex = /[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g;
516
+ let emails = text.match(emailRegex) || [];
517
+ if (unique) emails = [...new Set(emails.map(e => e.toLowerCase()))];
518
+ if (emails.length === 0) {
519
+ return { content: [{ type: "text", text: "No email addresses found." }] };
520
+ }
521
+ return { content: [{ type: "text", text: `Found ${emails.length} email(s):\n\n${emails.join("\n")}` }] };
522
+ }
523
+
524
+ case "extract_urls": {
525
+ const { text, unique = true } = args;
526
+ const urlRegex = /https?:\/\/[^\s<>\"')\]]+/g;
527
+ let urls = text.match(urlRegex) || [];
528
+ if (unique) urls = [...new Set(urls)];
529
+ if (urls.length === 0) {
530
+ return { content: [{ type: "text", text: "No URLs found." }] };
531
+ }
532
+ return { content: [{ type: "text", text: `Found ${urls.length} URL(s):\n\n${urls.join("\n")}` }] };
533
+ }
534
+
535
+ case "text_pad": {
536
+ const { text, width, align = "right", char = " " } = args;
537
+ const padChar = char.charAt(0) || " ";
538
+ const lines = text.split("\n");
539
+ const padded = lines.map(line => {
540
+ if (line.length >= width) return line;
541
+ const diff = width - line.length;
542
+ switch (align) {
543
+ case "left":
544
+ return line + padChar.repeat(diff);
545
+ case "center": {
546
+ const left = Math.floor(diff / 2);
547
+ const right = diff - left;
548
+ return padChar.repeat(left) + line + padChar.repeat(right);
549
+ }
550
+ case "right":
551
+ default:
552
+ return padChar.repeat(diff) + line;
553
+ }
554
+ });
555
+ return { content: [{ type: "text", text: padded.join("\n") }] };
556
+ }
557
+
558
+ case "text_wrap": {
559
+ const { text, width = 80 } = args;
560
+ const paragraphs = text.split("\n");
561
+ const wrapped = paragraphs.map(para => {
562
+ if (para.trim() === "") return "";
563
+ const words = para.split(/\s+/);
564
+ const lines = [];
565
+ let current = "";
566
+ for (const word of words) {
567
+ if (current.length === 0) {
568
+ current = word;
569
+ } else if (current.length + 1 + word.length <= width) {
570
+ current += " " + word;
571
+ } else {
572
+ lines.push(current);
573
+ current = word;
574
+ }
575
+ }
576
+ if (current) lines.push(current);
577
+ return lines.join("\n");
578
+ });
579
+ return { content: [{ type: "text", text: wrapped.join("\n") }] };
580
+ }
581
+
582
+ case "string_repeat": {
583
+ const { text, count = 2, separator = "" } = args;
584
+ const n = Math.max(1, Math.min(1000, count));
585
+ const result = Array(n).fill(text).join(separator);
586
+ return { content: [{ type: "text", text: result }] };
587
+ }
588
+
450
589
  default:
451
590
  throw new Error(`Unknown tool: ${name}`);
452
591
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-texttools",
3
- "version": "1.0.4",
4
- "description": "MCP server with 10 text transformation tools case conversion, slug, word count, lorem ipsum, truncate, regex replace, markdown strip, character count, reverse, line sort",
3
+ "version": "1.1.0",
4
+ "description": "MCP server with 15 text transformation tools \u2014 case conversion, slug, word count, lorem ipsum, truncate, regex replace, markdown strip, reverse, line sort, text diff, extract emails, extract URLs, text pad, text wrap, string repeat",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -24,7 +24,12 @@
24
24
  "claude",
25
25
  "cursor",
26
26
  "ai-tools",
27
- "string-utils"
27
+ "string-utils",
28
+ "extract-email",
29
+ "extract-url",
30
+ "text-wrap",
31
+ "string-repeat",
32
+ "text-padding"
28
33
  ],
29
34
  "author": "Hong Teoh",
30
35
  "license": "MIT",
@@ -34,5 +39,13 @@
34
39
  },
35
40
  "dependencies": {
36
41
  "@modelcontextprotocol/sdk": "^1.0.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/hlteoh37/mcp-texttools.git"
46
+ },
47
+ "homepage": "https://github.com/hlteoh37/mcp-texttools#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/hlteoh37/mcp-texttools/issues"
37
50
  }
38
51
  }