@julong/mono-rele2-utils 1.17.1 → 1.19.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 +28 -18
- package/dist/cli.js +7 -142
- package/dist/index.d.ts +19 -6
- package/dist/index.js +26 -247
- package/dist/server.js +1 -115
- package/dist/skills/mono-rele2-utils-cli/skill.md +21 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @julong/mono-rele2-utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.
|
|
4
4
|
|
|
5
5
|
## CLI
|
|
6
6
|
|
|
@@ -9,34 +9,22 @@ Text utility tools for the mono-rele2 monorepo. Available as an MCP server and a
|
|
|
9
9
|
```sh
|
|
10
10
|
npm install -g @julong/mono-rele2-utils
|
|
11
11
|
# or
|
|
12
|
-
npx @julong/mono-rele2-utils-cli <
|
|
12
|
+
npx @julong/mono-rele2-utils-cli <toolName> [...args]
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
### Usage
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
mono-rele2-utils-cli <
|
|
18
|
+
mono-rele2-utils-cli <toolName> [...args]
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Run without arguments to list all available
|
|
21
|
+
Run without arguments to list all available tools:
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
mono-rele2-utils-cli
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
Available skills:
|
|
29
|
-
|
|
30
|
-
caseConvertTool
|
|
31
|
-
Converts text to the specified case format
|
|
32
|
-
input Text to convert
|
|
33
|
-
to Target case format
|
|
34
|
-
...
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Skills
|
|
38
|
-
|
|
39
|
-
<!-- SKILLS:START -->
|
|
27
|
+
### Tools
|
|
40
28
|
|
|
41
29
|
#### `cnTool`
|
|
42
30
|
|
|
@@ -92,7 +80,29 @@ mono-rele2-utils-cli truncateTool "hello world long text" 10 # hello w...
|
|
|
92
80
|
mono-rele2-utils-cli truncateTool "hello world" 8 "…" # hello w…
|
|
93
81
|
```
|
|
94
82
|
|
|
95
|
-
|
|
83
|
+
#### `objectFlattenTool`
|
|
84
|
+
|
|
85
|
+
Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values..
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
mono-rele2-utils-cli objectFlattenTool <json>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| arg | type | description |
|
|
92
|
+
|-----|------|-------------|
|
|
93
|
+
| `json` | string | JSON string of a nested object to flatten (unlimited depth) |
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
mono-rele2-utils-cli objectFlattenTool '{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}' # {
|
|
97
|
+
"user.name": "Alice",
|
|
98
|
+
"user.address.city": "Seoul",
|
|
99
|
+
"user.address.zip": "12345",
|
|
100
|
+
"active": true
|
|
101
|
+
}
|
|
102
|
+
mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}' # {
|
|
103
|
+
"a.b.c.d.e": "deep"
|
|
104
|
+
}
|
|
105
|
+
```
|
|
96
106
|
|
|
97
107
|
## MCP Server
|
|
98
108
|
|
package/dist/cli.js
CHANGED
|
@@ -1,145 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
function a(e){return{content:[{type:"text",text:e}]}}import{McpServer as R}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as N}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as m}from"zod";function x(e){return`Available skills:
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
function defineTool(tool) {
|
|
8
|
-
return tool;
|
|
9
|
-
}
|
|
10
|
-
function text(content) {
|
|
11
|
-
return { content: [{ type: "text", text: content }] };
|
|
12
|
-
}
|
|
4
|
+
`+Object.entries(e).map(([o,t])=>{let r=Object.entries(t.inputSchema),i=Math.max(...r.map(([l])=>l.length)),p=r.map(([l,s])=>{let u=s.description??"";return` ${l.padEnd(i+2)}${u}`}).join(`
|
|
5
|
+
`);return` ${o}
|
|
6
|
+
${t.description}
|
|
7
|
+
${p}`}).join(`
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
-
|
|
18
|
-
// ../common/kit/cli.ts
|
|
19
|
-
import { z } from "zod";
|
|
20
|
-
function formatSkills(tools2) {
|
|
21
|
-
const sections = Object.entries(tools2).map(([key, tool]) => {
|
|
22
|
-
const params = Object.entries(tool.inputSchema);
|
|
23
|
-
const maxLen = Math.max(...params.map(([f]) => f.length));
|
|
24
|
-
const paramLines = params.map(([field, schema]) => {
|
|
25
|
-
const desc = schema.description ?? "";
|
|
26
|
-
return ` ${field.padEnd(maxLen + 2)}${desc}`;
|
|
27
|
-
}).join("\n");
|
|
28
|
-
return ` ${key}
|
|
29
|
-
${tool.description}
|
|
30
|
-
${paramLines}`;
|
|
31
|
-
});
|
|
32
|
-
return "Available skills:\n\n" + sections.join("\n\n");
|
|
33
|
-
}
|
|
34
|
-
async function runCli(tools2) {
|
|
35
|
-
const [, , toolName, ...rawArgs] = process.argv;
|
|
36
|
-
console.log(process.argv);
|
|
37
|
-
if (!toolName || !(toolName in tools2)) {
|
|
38
|
-
if (toolName) console.error(`Unknown skill: "${toolName}"
|
|
39
|
-
`);
|
|
40
|
-
console.log(formatSkills(tools2));
|
|
41
|
-
process.exit(toolName ? 1 : 0);
|
|
42
|
-
}
|
|
43
|
-
const tool = tools2[toolName];
|
|
44
|
-
const fieldNames = Object.keys(tool.inputSchema);
|
|
45
|
-
const rawInput = {};
|
|
46
|
-
for (let i = 0; i < fieldNames.length; i++) {
|
|
47
|
-
const raw = rawArgs[i];
|
|
48
|
-
if (raw === void 0) continue;
|
|
49
|
-
try {
|
|
50
|
-
rawInput[fieldNames[i]] = JSON.parse(raw);
|
|
51
|
-
} catch {
|
|
52
|
-
rawInput[fieldNames[i]] = raw;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const parsed = z.object(tool.inputSchema).parse(rawInput);
|
|
56
|
-
const result = await tool.handler(parsed);
|
|
57
|
-
for (const part of result.content) {
|
|
58
|
-
if (part.type === "text") console.log(part.text);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function handleCliError(err) {
|
|
62
|
-
if (err instanceof z.ZodError) {
|
|
63
|
-
console.error("Validation error:", err.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", "));
|
|
64
|
-
} else {
|
|
65
|
-
console.error("Error:", err instanceof Error ? err.message : String(err));
|
|
66
|
-
}
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ../common/kit/skill.ts
|
|
71
|
-
import { z as z2 } from "zod";
|
|
72
|
-
|
|
73
|
-
// src/tools/text.ts
|
|
74
|
-
import { z as z3 } from "zod";
|
|
75
|
-
|
|
76
|
-
// src/cn.ts
|
|
77
|
-
function cn(...classes) {
|
|
78
|
-
return classes.filter(Boolean).join(" ");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// src/tools/text.ts
|
|
82
|
-
var tools = {
|
|
83
|
-
cnTool: toolDef({
|
|
84
|
-
name: "cn",
|
|
85
|
-
description: "Merges class names, filtering out falsy values",
|
|
86
|
-
inputSchema: {
|
|
87
|
-
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
88
|
-
},
|
|
89
|
-
handler: async ({ classes }) => text(cn(...classes)),
|
|
90
|
-
examples: [{ args: [`'["btn","active","large"]'`], result: "btn active large" }]
|
|
91
|
-
}),
|
|
92
|
-
caseConvertTool: toolDef({
|
|
93
|
-
name: "case_convert",
|
|
94
|
-
description: "Converts text to the specified case format",
|
|
95
|
-
inputSchema: {
|
|
96
|
-
input: z3.string().describe("Text to convert"),
|
|
97
|
-
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
98
|
-
},
|
|
99
|
-
handler: async ({ input, to }) => text(convert(input, to)),
|
|
100
|
-
examples: [
|
|
101
|
-
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
102
|
-
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
103
|
-
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
104
|
-
]
|
|
105
|
-
}),
|
|
106
|
-
truncateTool: toolDef({
|
|
107
|
-
name: "truncate",
|
|
108
|
-
description: "Truncates text to a maximum length and appends a suffix",
|
|
109
|
-
inputSchema: {
|
|
110
|
-
input: z3.string().describe("Text to truncate"),
|
|
111
|
-
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
112
|
-
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
113
|
-
},
|
|
114
|
-
handler: async ({ input, maxLength, suffix }) => {
|
|
115
|
-
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
116
|
-
return text(result);
|
|
117
|
-
},
|
|
118
|
-
examples: [
|
|
119
|
-
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
120
|
-
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
121
|
-
]
|
|
122
|
-
})
|
|
123
|
-
};
|
|
124
|
-
var cnTool = defineTool(tools.cnTool);
|
|
125
|
-
var caseConvertTool = defineTool(tools.caseConvertTool);
|
|
126
|
-
var truncateTool = defineTool(tools.truncateTool);
|
|
127
|
-
function convert(input, to) {
|
|
128
|
-
switch (to) {
|
|
129
|
-
case "upper":
|
|
130
|
-
return input.toUpperCase();
|
|
131
|
-
case "lower":
|
|
132
|
-
return input.toLowerCase();
|
|
133
|
-
case "capitalize":
|
|
134
|
-
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
|
135
|
-
case "camel":
|
|
136
|
-
return input.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (c) => c.toLowerCase());
|
|
137
|
-
case "snake":
|
|
138
|
-
return input.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
|
|
139
|
-
case "kebab":
|
|
140
|
-
return input.replace(/([A-Z])/g, "-$1").replace(/[_\s]+/g, "-").toLowerCase().replace(/^-/, "");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// src/cli.ts
|
|
145
|
-
runCli(tools).catch(handleCliError);
|
|
9
|
+
`)}async function g(e){let[,,n,...o]=process.argv;console.log(process.argv),(!n||!(n in e))&&(n&&console.error(`Unknown skill: "${n}"
|
|
10
|
+
`),console.log(x(e)),process.exit(n?1:0));let t=e[n],r=Object.keys(t.inputSchema),i={};for(let s=0;s<r.length;s++){let u=o[s];if(u!==void 0)try{i[r[s]]=JSON.parse(u)}catch{i[r[s]]=u}}let p=m.object(t.inputSchema).parse(i),l=await t.handler(p);for(let s of l.content)s.type==="text"&&console.log(s.text)}function y(e){e instanceof m.ZodError?console.error("Validation error:",e.issues.map(n=>`${n.path.join(".")}: ${n.message}`).join(", ")):console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}import{z as L}from"zod";import{z as c}from"zod";function h(...e){return e.filter(Boolean).join(" ")}var d={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:c.array(c.string()).describe("List of class names to merge")},handler:async({classes:e})=>a(h(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:c.string().describe("Text to convert"),to:c.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:n})=>a(j(e,n)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:c.string().describe("Text to truncate"),maxLength:c.number().int().positive().describe("Maximum character length"),suffix:c.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:n,suffix:o})=>{let t=e.length<=n?e:e.slice(0,n-o.length)+o;return a(t)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},w=d.cnTool,Z=d.caseConvertTool,A=d.truncateTool;function j(e,n){switch(n){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(o,t)=>t.toUpperCase()).replace(/^(.)/,o=>o.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z}from"zod";function $(e){let n={};function o(t,r){if(t===null||typeof t!="object"||Array.isArray(t)){n[r]=t;return}for(let[i,p]of Object.entries(t)){let l=r?`${r}.${i}`:i;o(p,l)}}return o(e,""),n}var f={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:z.string().describe("JSON string of a nested object to flatten (unlimited depth)")},handler:async({json:e})=>{let n;try{let o=JSON.parse(e);if(typeof o!="object"||o===null||Array.isArray(o))return a(`Error: input must be a JSON object, got ${Array.isArray(o)?"array":typeof o}`);n=o}catch{return a("Error: invalid JSON string \u2014 unable to parse input")}try{let o=$(n);return a(JSON.stringify(o,null,2))}catch(o){return a(`Error: failed to flatten object \u2014 ${o.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened."]}},k=f.objectFlattenTool;var T={...d,...f};g(T).catch(y);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import * as zod from 'zod';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
|
|
4
5
|
declare function cn(...classes: (string | undefined | null | false)[]): string;
|
|
@@ -29,11 +30,23 @@ declare function generateReadmeSkills(opts: {
|
|
|
29
30
|
}): string;
|
|
30
31
|
|
|
31
32
|
declare const tools: {
|
|
33
|
+
objectFlattenTool: {
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
inputSchema: {
|
|
37
|
+
readonly json: zod.ZodString;
|
|
38
|
+
};
|
|
39
|
+
handler: (input: {
|
|
40
|
+
json: string;
|
|
41
|
+
}) => Promise<ToolResult>;
|
|
42
|
+
examples?: ToolExample[];
|
|
43
|
+
guidelines?: string[];
|
|
44
|
+
};
|
|
32
45
|
cnTool: {
|
|
33
46
|
name: string;
|
|
34
47
|
description: string;
|
|
35
48
|
inputSchema: {
|
|
36
|
-
readonly classes:
|
|
49
|
+
readonly classes: zod.ZodArray<zod.ZodString>;
|
|
37
50
|
};
|
|
38
51
|
handler: (input: {
|
|
39
52
|
classes: string[];
|
|
@@ -45,8 +58,8 @@ declare const tools: {
|
|
|
45
58
|
name: string;
|
|
46
59
|
description: string;
|
|
47
60
|
inputSchema: {
|
|
48
|
-
readonly input:
|
|
49
|
-
readonly to:
|
|
61
|
+
readonly input: zod.ZodString;
|
|
62
|
+
readonly to: zod.ZodEnum<{
|
|
50
63
|
upper: "upper";
|
|
51
64
|
lower: "lower";
|
|
52
65
|
capitalize: "capitalize";
|
|
@@ -66,9 +79,9 @@ declare const tools: {
|
|
|
66
79
|
name: string;
|
|
67
80
|
description: string;
|
|
68
81
|
inputSchema: {
|
|
69
|
-
readonly input:
|
|
70
|
-
readonly maxLength:
|
|
71
|
-
readonly suffix:
|
|
82
|
+
readonly input: zod.ZodString;
|
|
83
|
+
readonly maxLength: zod.ZodNumber;
|
|
84
|
+
readonly suffix: zod.ZodDefault<zod.ZodString>;
|
|
72
85
|
};
|
|
73
86
|
handler: (input: {
|
|
74
87
|
input: string;
|
package/dist/index.js
CHANGED
|
@@ -1,273 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
// ../common/kit/tool.ts
|
|
7
|
-
function toolDef(def) {
|
|
8
|
-
return def;
|
|
9
|
-
}
|
|
10
|
-
function defineTool(tool) {
|
|
11
|
-
return tool;
|
|
12
|
-
}
|
|
13
|
-
function text(content) {
|
|
14
|
-
return { content: [{ type: "text", text: content }] };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// ../common/kit/server.ts
|
|
18
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
|
-
|
|
21
|
-
// ../common/kit/cli.ts
|
|
22
|
-
import { z } from "zod";
|
|
23
|
-
|
|
24
|
-
// ../common/kit/skill.ts
|
|
25
|
-
import { z as z2 } from "zod";
|
|
26
|
-
function generateSkillMarkdown(opts) {
|
|
27
|
-
const { binName, description, tools: tools2 } = opts;
|
|
28
|
-
return `---
|
|
29
|
-
name: ${binName}
|
|
30
|
-
description: ${description}
|
|
1
|
+
function y(...o){return o.filter(Boolean).join(" ")}function c(o){return{content:[{type:"text",text:o}]}}import{McpServer as G}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as W}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as K}from"zod";import{z as a}from"zod";function w(o){let{binName:n,description:e,tools:t}=o;return`---
|
|
2
|
+
name: ${n}
|
|
3
|
+
description: ${e}
|
|
31
4
|
---
|
|
32
5
|
|
|
33
|
-
# ${
|
|
6
|
+
# ${n}
|
|
34
7
|
|
|
35
8
|
\`\`\`sh
|
|
36
|
-
${
|
|
9
|
+
${n} <toolName> [...args]
|
|
37
10
|
\`\`\`
|
|
38
11
|
|
|
39
12
|
## Skills
|
|
40
13
|
|
|
41
|
-
${
|
|
14
|
+
${Z(t)}
|
|
42
15
|
|
|
43
16
|
## Examples
|
|
44
17
|
|
|
45
|
-
${
|
|
18
|
+
${j(n,t)}
|
|
46
19
|
|
|
47
20
|
## Guidelines
|
|
48
21
|
|
|
49
|
-
${
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
function renderSkills(tools2) {
|
|
53
|
-
return Object.entries(tools2).map(([key, tool]) => {
|
|
54
|
-
const rows = Object.entries(tool.inputSchema).map(([field, schema]) => `| \`${field}\` | ${describeField(schema)} |`).join("\n");
|
|
55
|
-
return `### ${key}
|
|
22
|
+
${k(n,t)}
|
|
23
|
+
`}function Z(o){return Object.entries(o).map(([n,e])=>{let t=Object.entries(e.inputSchema).map(([s,i])=>`| \`${s}\` | ${A(i)} |`).join(`
|
|
24
|
+
`);return`### ${n}
|
|
56
25
|
|
|
57
|
-
${
|
|
26
|
+
${e.description}
|
|
58
27
|
|
|
59
28
|
| arg | description |
|
|
60
29
|
|-----|-------------|
|
|
61
|
-
${
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
70
|
-
if (inner instanceof z2.ZodDefault) {
|
|
71
|
-
const raw = inner.def.defaultValue;
|
|
72
|
-
defaultValue = typeof raw === "function" ? raw() : raw;
|
|
73
|
-
}
|
|
74
|
-
if (inner instanceof z2.ZodOptional) isOptional = true;
|
|
75
|
-
inner = inner.unwrap();
|
|
76
|
-
}
|
|
77
|
-
const parts = [];
|
|
78
|
-
if (baseDesc) parts.push(baseDesc);
|
|
79
|
-
if (inner instanceof z2.ZodEnum) {
|
|
80
|
-
const values = inner.options.map((v) => `\`${v}\``).join(" \\| ");
|
|
81
|
-
parts.push(values);
|
|
82
|
-
}
|
|
83
|
-
if (defaultValue !== void 0) parts.push(`default: \`${String(defaultValue)}\``);
|
|
84
|
-
else if (isOptional) parts.push("optional");
|
|
85
|
-
return parts.join(" \u2014 ");
|
|
86
|
-
}
|
|
87
|
-
function renderExamples(binName, tools2) {
|
|
88
|
-
const lines = [];
|
|
89
|
-
for (const [key, tool] of Object.entries(tools2)) {
|
|
90
|
-
if (!tool.examples) continue;
|
|
91
|
-
for (const ex of tool.examples) {
|
|
92
|
-
const cmd = [binName, key, ...ex.args].join(" ");
|
|
93
|
-
lines.push(`- \`${cmd}\` => \`${ex.result}\``);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return lines.join("\n");
|
|
97
|
-
}
|
|
98
|
-
function renderGuidelines(binName, tools2) {
|
|
99
|
-
const items = [];
|
|
100
|
-
const seen = /* @__PURE__ */ new Set();
|
|
101
|
-
const push = (g) => {
|
|
102
|
-
if (seen.has(g)) return;
|
|
103
|
-
seen.add(g);
|
|
104
|
-
items.push(g);
|
|
105
|
-
};
|
|
106
|
-
push("Arguments are positional \u2014 pass them in the order listed in each skill's table");
|
|
107
|
-
const allSchemas = Object.values(tools2).flatMap((t) => Object.values(t.inputSchema));
|
|
108
|
-
if (allSchemas.some((s) => containsType(s, z2.ZodNumber))) {
|
|
109
|
-
push("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)");
|
|
110
|
-
}
|
|
111
|
-
if (allSchemas.some((s) => containsType(s, z2.ZodArray))) {
|
|
112
|
-
push('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)');
|
|
113
|
-
}
|
|
114
|
-
if (allSchemas.some((s) => s instanceof z2.ZodOptional || s instanceof z2.ZodDefault)) {
|
|
115
|
-
push("Optional args with defaults may be omitted");
|
|
116
|
-
}
|
|
117
|
-
for (const tool of Object.values(tools2)) {
|
|
118
|
-
if (!tool.guidelines) continue;
|
|
119
|
-
for (const g of tool.guidelines) push(g);
|
|
120
|
-
}
|
|
121
|
-
push(`Run \`${binName}\` with no args to list all available skills`);
|
|
122
|
-
return items.map((g) => `- ${g}`).join("\n");
|
|
123
|
-
}
|
|
124
|
-
function containsType(schema, ctor) {
|
|
125
|
-
let inner = schema;
|
|
126
|
-
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
127
|
-
inner = inner.unwrap();
|
|
128
|
-
}
|
|
129
|
-
return inner instanceof ctor;
|
|
130
|
-
}
|
|
131
|
-
function generateReadmeSkills(opts) {
|
|
132
|
-
console.log(opts.binName);
|
|
133
|
-
const { binName, tools: tools2 } = opts;
|
|
134
|
-
return Object.entries(tools2).map(([key, tool]) => renderReadmeSkill(binName, key, tool)).join("\n\n");
|
|
135
|
-
}
|
|
136
|
-
function renderReadmeSkill(binName, key, tool) {
|
|
137
|
-
const fields = Object.entries(tool.inputSchema);
|
|
138
|
-
const usageArgs = fields.map(([field, schema]) => {
|
|
139
|
-
const isOpt = schema instanceof z2.ZodOptional || schema instanceof z2.ZodDefault;
|
|
140
|
-
return isOpt ? `[${field}]` : `<${field}>`;
|
|
141
|
-
}).join(" ");
|
|
142
|
-
const usage = [binName, key, usageArgs].filter(Boolean).join(" ");
|
|
143
|
-
const rows = fields.map(([field, schema]) => {
|
|
144
|
-
const t = describeReadmeType(schema);
|
|
145
|
-
const d = describeReadmeDesc(schema);
|
|
146
|
-
return `| \`${field}\` | ${t} | ${d} |`;
|
|
147
|
-
}).join("\n");
|
|
148
|
-
const examples = tool.examples ?? [];
|
|
149
|
-
let exampleBlock = "";
|
|
150
|
-
if (examples.length > 0) {
|
|
151
|
-
const cmds = examples.map((ex) => [binName, key, ...ex.args].join(" "));
|
|
152
|
-
const width = Math.max(...cmds.map((c) => c.length));
|
|
153
|
-
const lines = examples.map((ex, i) => `${cmds[i].padEnd(width)} # ${ex.result}`);
|
|
154
|
-
exampleBlock = `
|
|
30
|
+
${t}`}).join(`
|
|
31
|
+
|
|
32
|
+
`)}function A(o){let n=o.description??"",e=o,t,s=!1;for(;e instanceof a.ZodOptional||e instanceof a.ZodDefault;){if(e instanceof a.ZodDefault){let r=e.def.defaultValue;t=typeof r=="function"?r():r}e instanceof a.ZodOptional&&(s=!0),e=e.unwrap()}let i=[];if(n&&i.push(n),e instanceof a.ZodEnum){let r=e.options.map(l=>`\`${l}\``).join(" \\| ");i.push(r)}return t!==void 0?i.push(`default: \`${String(t)}\``):s&&i.push("optional"),i.join(" \u2014 ")}function j(o,n){let e=[];for(let[t,s]of Object.entries(n))if(s.examples)for(let i of s.examples){let r=[o,t,...i.args].join(" ");e.push(`- \`${r}\` => \`${i.result}\``)}return e.join(`
|
|
33
|
+
`)}function k(o,n){let e=[],t=new Set,s=r=>{t.has(r)||(t.add(r),e.push(r))};s("Arguments are positional \u2014 pass them in the order listed in each skill's table");let i=Object.values(n).flatMap(r=>Object.values(r.inputSchema));i.some(r=>x(r,a.ZodNumber))&&s("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)"),i.some(r=>x(r,a.ZodArray))&&s('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)'),i.some(r=>r instanceof a.ZodOptional||r instanceof a.ZodDefault)&&s("Optional args with defaults may be omitted");for(let r of Object.values(n))if(r.guidelines)for(let l of r.guidelines)s(l);return s(`Run \`${o}\` with no args to list all available skills`),e.map(r=>`- ${r}`).join(`
|
|
34
|
+
`)}function x(o,n){let e=o;for(;e instanceof a.ZodOptional||e instanceof a.ZodDefault;)e=e.unwrap();return e instanceof n}function z(o){console.log(o.binName);let{binName:n,tools:e}=o;return Object.entries(e).map(([t,s])=>$(n,t,s)).join(`
|
|
35
|
+
|
|
36
|
+
`)}function $(o,n,e){let t=Object.entries(e.inputSchema),s=t.map(([p,u])=>u instanceof a.ZodOptional||u instanceof a.ZodDefault?`[${p}]`:`<${p}>`).join(" "),i=[o,n,s].filter(Boolean).join(" "),r=t.map(([p,u])=>{let g=v(u),d=O(u);return`| \`${p}\` | ${g} | ${d} |`}).join(`
|
|
37
|
+
`),l=e.examples??[],T="";if(l.length>0){let p=l.map(d=>[o,n,...d.args].join(" ")),u=Math.max(...p.map(d=>d.length));T=`
|
|
155
38
|
|
|
156
39
|
\`\`\`sh
|
|
157
|
-
${
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const tableBlock = rows ? `
|
|
40
|
+
${l.map((d,b)=>`${p[b].padEnd(u)} # ${d.result}`).join(`
|
|
41
|
+
`)}
|
|
42
|
+
\`\`\``}let S=r?`
|
|
161
43
|
|
|
162
44
|
| arg | type | description |
|
|
163
45
|
|-----|------|-------------|
|
|
164
|
-
${
|
|
165
|
-
return `#### \`${key}\`
|
|
46
|
+
${r}`:"";return`#### \`${n}\`
|
|
166
47
|
|
|
167
|
-
${
|
|
48
|
+
${e.description}.
|
|
168
49
|
|
|
169
50
|
\`\`\`sh
|
|
170
|
-
${
|
|
171
|
-
\`\`\`${
|
|
172
|
-
}
|
|
173
|
-
function describeReadmeType(schema) {
|
|
174
|
-
let inner = schema;
|
|
175
|
-
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
176
|
-
inner = inner.unwrap();
|
|
177
|
-
}
|
|
178
|
-
if (inner instanceof z2.ZodEnum) {
|
|
179
|
-
return inner.options.map((v) => `\`${v}\``).join(" \\| ");
|
|
180
|
-
}
|
|
181
|
-
if (inner instanceof z2.ZodNumber) return "number";
|
|
182
|
-
if (inner instanceof z2.ZodString) return "string";
|
|
183
|
-
if (inner instanceof z2.ZodBoolean) return "boolean";
|
|
184
|
-
if (inner instanceof z2.ZodArray) return "JSON string (array)";
|
|
185
|
-
return "unknown";
|
|
186
|
-
}
|
|
187
|
-
function describeReadmeDesc(schema) {
|
|
188
|
-
const baseDesc = schema.description ?? "";
|
|
189
|
-
let inner = schema;
|
|
190
|
-
let defaultValue;
|
|
191
|
-
let isOptional = false;
|
|
192
|
-
while (inner instanceof z2.ZodOptional || inner instanceof z2.ZodDefault) {
|
|
193
|
-
if (inner instanceof z2.ZodDefault) {
|
|
194
|
-
const raw = inner.def.defaultValue;
|
|
195
|
-
defaultValue = typeof raw === "function" ? raw() : raw;
|
|
196
|
-
}
|
|
197
|
-
if (inner instanceof z2.ZodOptional) isOptional = true;
|
|
198
|
-
inner = inner.unwrap();
|
|
199
|
-
}
|
|
200
|
-
if (defaultValue !== void 0) return `${baseDesc} (default: \`${String(defaultValue)}\`)`;
|
|
201
|
-
if (isOptional) return `${baseDesc} (optional)`;
|
|
202
|
-
return baseDesc;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// src/tools/text.ts
|
|
206
|
-
import { z as z3 } from "zod";
|
|
207
|
-
var tools = {
|
|
208
|
-
cnTool: toolDef({
|
|
209
|
-
name: "cn",
|
|
210
|
-
description: "Merges class names, filtering out falsy values",
|
|
211
|
-
inputSchema: {
|
|
212
|
-
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
213
|
-
},
|
|
214
|
-
handler: async ({ classes }) => text(cn(...classes)),
|
|
215
|
-
examples: [{ args: [`'["btn","active","large"]'`], result: "btn active large" }]
|
|
216
|
-
}),
|
|
217
|
-
caseConvertTool: toolDef({
|
|
218
|
-
name: "case_convert",
|
|
219
|
-
description: "Converts text to the specified case format",
|
|
220
|
-
inputSchema: {
|
|
221
|
-
input: z3.string().describe("Text to convert"),
|
|
222
|
-
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
223
|
-
},
|
|
224
|
-
handler: async ({ input, to }) => text(convert(input, to)),
|
|
225
|
-
examples: [
|
|
226
|
-
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
227
|
-
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
228
|
-
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
229
|
-
]
|
|
230
|
-
}),
|
|
231
|
-
truncateTool: toolDef({
|
|
232
|
-
name: "truncate",
|
|
233
|
-
description: "Truncates text to a maximum length and appends a suffix",
|
|
234
|
-
inputSchema: {
|
|
235
|
-
input: z3.string().describe("Text to truncate"),
|
|
236
|
-
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
237
|
-
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
238
|
-
},
|
|
239
|
-
handler: async ({ input, maxLength, suffix }) => {
|
|
240
|
-
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
241
|
-
return text(result);
|
|
242
|
-
},
|
|
243
|
-
examples: [
|
|
244
|
-
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
245
|
-
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
246
|
-
]
|
|
247
|
-
})
|
|
248
|
-
};
|
|
249
|
-
var cnTool = defineTool(tools.cnTool);
|
|
250
|
-
var caseConvertTool = defineTool(tools.caseConvertTool);
|
|
251
|
-
var truncateTool = defineTool(tools.truncateTool);
|
|
252
|
-
function convert(input, to) {
|
|
253
|
-
switch (to) {
|
|
254
|
-
case "upper":
|
|
255
|
-
return input.toUpperCase();
|
|
256
|
-
case "lower":
|
|
257
|
-
return input.toLowerCase();
|
|
258
|
-
case "capitalize":
|
|
259
|
-
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
|
260
|
-
case "camel":
|
|
261
|
-
return input.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (c) => c.toLowerCase());
|
|
262
|
-
case "snake":
|
|
263
|
-
return input.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
|
|
264
|
-
case "kebab":
|
|
265
|
-
return input.replace(/([A-Z])/g, "-$1").replace(/[_\s]+/g, "-").toLowerCase().replace(/^-/, "");
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
export {
|
|
269
|
-
cn,
|
|
270
|
-
generateReadmeSkills,
|
|
271
|
-
generateSkillMarkdown,
|
|
272
|
-
tools
|
|
273
|
-
};
|
|
51
|
+
${i}
|
|
52
|
+
\`\`\`${S}${T}`}function v(o){let n=o;for(;n instanceof a.ZodOptional||n instanceof a.ZodDefault;)n=n.unwrap();return n instanceof a.ZodEnum?n.options.map(e=>`\`${e}\``).join(" \\| "):n instanceof a.ZodNumber?"number":n instanceof a.ZodString?"string":n instanceof a.ZodBoolean?"boolean":n instanceof a.ZodArray?"JSON string (array)":"unknown"}function O(o){let n=o.description??"",e=o,t,s=!1;for(;e instanceof a.ZodOptional||e instanceof a.ZodDefault;){if(e instanceof a.ZodDefault){let i=e.def.defaultValue;t=typeof i=="function"?i():i}e instanceof a.ZodOptional&&(s=!0),e=e.unwrap()}return t!==void 0?`${n} (default: \`${String(t)}\`)`:s?`${n} (optional)`:n}import{z as f}from"zod";var m={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:f.array(f.string()).describe("List of class names to merge")},handler:async({classes:o})=>c(y(...o)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:f.string().describe("Text to convert"),to:f.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:o,to:n})=>c(J(o,n)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:f.string().describe("Text to truncate"),maxLength:f.number().int().positive().describe("Maximum character length"),suffix:f.string().default("...").describe("Suffix to append when truncated")},handler:async({input:o,maxLength:n,suffix:e})=>{let t=o.length<=n?o:o.slice(0,n-e.length)+e;return c(t)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},N=m.cnTool,C=m.caseConvertTool,E=m.truncateTool;function J(o,n){switch(n){case"upper":return o.toUpperCase();case"lower":return o.toLowerCase();case"capitalize":return o.charAt(0).toUpperCase()+o.slice(1).toLowerCase();case"camel":return o.replace(/[-_\s]+(.)/g,(e,t)=>t.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase());case"snake":return o.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return o.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as M}from"zod";function _(o){let n={};function e(t,s){if(t===null||typeof t!="object"||Array.isArray(t)){n[s]=t;return}for(let[i,r]of Object.entries(t)){let l=s?`${s}.${i}`:i;e(r,l)}}return e(o,""),n}var h={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:M.string().describe("JSON string of a nested object to flatten (unlimited depth)")},handler:async({json:o})=>{let n;try{let e=JSON.parse(o);if(typeof e!="object"||e===null||Array.isArray(e))return c(`Error: input must be a JSON object, got ${Array.isArray(e)?"array":typeof e}`);n=e}catch{return c("Error: invalid JSON string \u2014 unable to parse input")}try{let e=_(n);return c(JSON.stringify(e,null,2))}catch(e){return c(`Error: failed to flatten object \u2014 ${e.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened."]}},L=h.objectFlattenTool;var V={...m,...h};export{y as cn,z as generateReadmeSkills,w as generateSkillMarkdown,V as tools};
|
package/dist/server.js
CHANGED
|
@@ -1,116 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// ../common/kit/tool.ts
|
|
4
|
-
function toolDef(def) {
|
|
5
|
-
return def;
|
|
6
|
-
}
|
|
7
|
-
function defineTool(tool) {
|
|
8
|
-
return tool;
|
|
9
|
-
}
|
|
10
|
-
function text(content) {
|
|
11
|
-
return { content: [{ type: "text", text: content }] };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// ../common/kit/server.ts
|
|
15
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
-
function createMcpServer(config, tools2) {
|
|
18
|
-
const server2 = new McpServer(config);
|
|
19
|
-
for (const tool of tools2) {
|
|
20
|
-
server2.registerTool(
|
|
21
|
-
tool.name,
|
|
22
|
-
{ description: tool.description, inputSchema: tool.inputSchema },
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
-
tool.handler
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
return server2;
|
|
28
|
-
}
|
|
29
|
-
async function startServer(server2) {
|
|
30
|
-
const transport = new StdioServerTransport();
|
|
31
|
-
await server2.connect(transport);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ../common/kit/cli.ts
|
|
35
|
-
import { z } from "zod";
|
|
36
|
-
|
|
37
|
-
// ../common/kit/skill.ts
|
|
38
|
-
import { z as z2 } from "zod";
|
|
39
|
-
|
|
40
|
-
// src/tools/text.ts
|
|
41
|
-
import { z as z3 } from "zod";
|
|
42
|
-
|
|
43
|
-
// src/cn.ts
|
|
44
|
-
function cn(...classes) {
|
|
45
|
-
return classes.filter(Boolean).join(" ");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// src/tools/text.ts
|
|
49
|
-
var tools = {
|
|
50
|
-
cnTool: toolDef({
|
|
51
|
-
name: "cn",
|
|
52
|
-
description: "Merges class names, filtering out falsy values",
|
|
53
|
-
inputSchema: {
|
|
54
|
-
classes: z3.array(z3.string()).describe("List of class names to merge")
|
|
55
|
-
},
|
|
56
|
-
handler: async ({ classes }) => text(cn(...classes)),
|
|
57
|
-
examples: [{ args: [`'["btn","active","large"]'`], result: "btn active large" }]
|
|
58
|
-
}),
|
|
59
|
-
caseConvertTool: toolDef({
|
|
60
|
-
name: "case_convert",
|
|
61
|
-
description: "Converts text to the specified case format",
|
|
62
|
-
inputSchema: {
|
|
63
|
-
input: z3.string().describe("Text to convert"),
|
|
64
|
-
to: z3.enum(["upper", "lower", "capitalize", "camel", "snake", "kebab"]).describe("Target case format")
|
|
65
|
-
},
|
|
66
|
-
handler: async ({ input, to }) => text(convert(input, to)),
|
|
67
|
-
examples: [
|
|
68
|
-
{ args: [`"hello world"`, "camel"], result: "helloWorld" },
|
|
69
|
-
{ args: [`"helloWorld"`, "snake"], result: "hello_world" },
|
|
70
|
-
{ args: [`"hello world"`, "kebab"], result: "hello-world" }
|
|
71
|
-
]
|
|
72
|
-
}),
|
|
73
|
-
truncateTool: toolDef({
|
|
74
|
-
name: "truncate",
|
|
75
|
-
description: "Truncates text to a maximum length and appends a suffix",
|
|
76
|
-
inputSchema: {
|
|
77
|
-
input: z3.string().describe("Text to truncate"),
|
|
78
|
-
maxLength: z3.number().int().positive().describe("Maximum character length"),
|
|
79
|
-
suffix: z3.string().default("...").describe("Suffix to append when truncated")
|
|
80
|
-
},
|
|
81
|
-
handler: async ({ input, maxLength, suffix }) => {
|
|
82
|
-
const result = input.length <= maxLength ? input : input.slice(0, maxLength - suffix.length) + suffix;
|
|
83
|
-
return text(result);
|
|
84
|
-
},
|
|
85
|
-
examples: [
|
|
86
|
-
{ args: [`"hello world long text"`, "10"], result: "hello w..." },
|
|
87
|
-
{ args: [`"hello world"`, "8", `"\u2026"`], result: "hello w\u2026" }
|
|
88
|
-
]
|
|
89
|
-
})
|
|
90
|
-
};
|
|
91
|
-
var cnTool = defineTool(tools.cnTool);
|
|
92
|
-
var caseConvertTool = defineTool(tools.caseConvertTool);
|
|
93
|
-
var truncateTool = defineTool(tools.truncateTool);
|
|
94
|
-
function convert(input, to) {
|
|
95
|
-
switch (to) {
|
|
96
|
-
case "upper":
|
|
97
|
-
return input.toUpperCase();
|
|
98
|
-
case "lower":
|
|
99
|
-
return input.toLowerCase();
|
|
100
|
-
case "capitalize":
|
|
101
|
-
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
|
102
|
-
case "camel":
|
|
103
|
-
return input.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (c) => c.toLowerCase());
|
|
104
|
-
case "snake":
|
|
105
|
-
return input.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
|
|
106
|
-
case "kebab":
|
|
107
|
-
return input.replace(/([A-Z])/g, "-$1").replace(/[_\s]+/g, "-").toLowerCase().replace(/^-/, "");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/server.ts
|
|
112
|
-
var server = createMcpServer({ name: "mono-rele2-utils", version: "1.0.0" }, [cnTool, caseConvertTool, truncateTool]);
|
|
113
|
-
startServer(server).catch((err) => {
|
|
114
|
-
console.error("[utils] server error:", err);
|
|
115
|
-
process.exit(1);
|
|
116
|
-
});
|
|
2
|
+
function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as S}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as x}from"@modelcontextprotocol/sdk/server/stdio.js";function m(e,o){let n=new S(e);for(let t of o)n.registerTool(t.name,{description:t.description,inputSchema:t.inputSchema},t.handler);return n}async function g(e){let o=new x;await e.connect(o)}import{z as N}from"zod";import{z as J}from"zod";import{z as s}from"zod";function y(...e){return e.filter(Boolean).join(" ")}var a={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:s.array(s.string()).describe("List of class names to merge")},handler:async({classes:e})=>r(y(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:s.string().describe("Text to convert"),to:s.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:o})=>r(Z(e,o)),examples:[{args:['"hello world"',"camel"],result:"helloWorld"},{args:['"helloWorld"',"snake"],result:"hello_world"},{args:['"hello world"',"kebab"],result:"hello-world"}]},truncateTool:{name:"truncate",description:"Truncates text to a maximum length and appends a suffix",inputSchema:{input:s.string().describe("Text to truncate"),maxLength:s.number().int().positive().describe("Maximum character length"),suffix:s.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:o,suffix:n})=>{let t=e.length<=o?e:e.slice(0,o-n.length)+n;return r(t)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},l=a.cnTool,c=a.caseConvertTool,p=a.truncateTool;function Z(e,o){switch(o){case"upper":return e.toUpperCase();case"lower":return e.toLowerCase();case"capitalize":return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase();case"camel":return e.replace(/[-_\s]+(.)/g,(n,t)=>t.toUpperCase()).replace(/^(.)/,n=>n.toLowerCase());case"snake":return e.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return e.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as A}from"zod";function j(e){let o={};function n(t,i){if(t===null||typeof t!="object"||Array.isArray(t)){o[i]=t;return}for(let[f,T]of Object.entries(t)){let h=i?`${i}.${f}`:f;n(T,h)}}return n(e,""),o}var u={objectFlattenTool:{name:"object_flatten",description:"Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.",inputSchema:{json:A.string().describe("JSON string of a nested object to flatten (unlimited depth)")},handler:async({json:e})=>{let o;try{let n=JSON.parse(e);if(typeof n!="object"||n===null||Array.isArray(n))return r(`Error: input must be a JSON object, got ${Array.isArray(n)?"array":typeof n}`);o=n}catch{return r("Error: invalid JSON string \u2014 unable to parse input")}try{let n=j(o);return r(JSON.stringify(n,null,2))}catch(n){return r(`Error: failed to flatten object \u2014 ${n.message}`)}},examples:[{args:[`'{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'`],result:JSON.stringify({"user.name":"Alice","user.address.city":"Seoul","user.address.zip":"12345",active:!0},null,2)},{args:[`'{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'`],result:JSON.stringify({"a.b.c.d.e":"deep"},null,2)}],guidelines:["Arrays and primitives at any level are always treated as leaf values \u2014 they are never traversed.","The result is always a flat JSON object with dot-notation keys.","There is no depth limit \u2014 objects of any nesting level are fully flattened."]}},d=u.objectFlattenTool;var te={...a,...u};var v=m({name:"mono-rele2-utils",version:"1.0.0"},[l,c,p,d]);g(v).catch(e=>{console.error("[utils] server error:",e),process.exit(1)});
|
|
@@ -6,7 +6,7 @@ description: Use this skill to invoke text utility functions via the mono-rele2-
|
|
|
6
6
|
# mono-rele2-utils-cli
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
mono-rele2-utils-cli <
|
|
9
|
+
mono-rele2-utils-cli <toolName> [...args]
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
## Skills
|
|
@@ -38,6 +38,14 @@ Truncates text to a maximum length and appends a suffix
|
|
|
38
38
|
| `maxLength` | Maximum character length |
|
|
39
39
|
| `suffix` | Suffix to append when truncated — default: `...` |
|
|
40
40
|
|
|
41
|
+
### objectFlattenTool
|
|
42
|
+
|
|
43
|
+
Flattens a nested JSON object of any depth into dot-notation key-value pairs. Accepts a JSON string and recursively flattens all levels. Arrays and primitives at any level are treated as leaf values.
|
|
44
|
+
|
|
45
|
+
| arg | description |
|
|
46
|
+
|-----|-------------|
|
|
47
|
+
| `json` | JSON string of a nested object to flatten (unlimited depth) |
|
|
48
|
+
|
|
41
49
|
## Examples
|
|
42
50
|
|
|
43
51
|
- `mono-rele2-utils-cli cnTool '["btn","active","large"]'` => `btn active large`
|
|
@@ -46,6 +54,15 @@ Truncates text to a maximum length and appends a suffix
|
|
|
46
54
|
- `mono-rele2-utils-cli caseConvertTool "hello world" kebab` => `hello-world`
|
|
47
55
|
- `mono-rele2-utils-cli truncateTool "hello world long text" 10` => `hello w...`
|
|
48
56
|
- `mono-rele2-utils-cli truncateTool "hello world" 8 "…"` => `hello w…`
|
|
57
|
+
- `mono-rele2-utils-cli objectFlattenTool '{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}'` => `{
|
|
58
|
+
"user.name": "Alice",
|
|
59
|
+
"user.address.city": "Seoul",
|
|
60
|
+
"user.address.zip": "12345",
|
|
61
|
+
"active": true
|
|
62
|
+
}`
|
|
63
|
+
- `mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'` => `{
|
|
64
|
+
"a.b.c.d.e": "deep"
|
|
65
|
+
}`
|
|
49
66
|
|
|
50
67
|
## Guidelines
|
|
51
68
|
|
|
@@ -53,4 +70,7 @@ Truncates text to a maximum length and appends a suffix
|
|
|
53
70
|
- Numeric args are auto-parsed — pass as plain numbers (e.g. `10`)
|
|
54
71
|
- Array args must be valid JSON — wrap in single quotes on Unix shells (e.g. `'["a","b"]'`)
|
|
55
72
|
- Optional args with defaults may be omitted
|
|
73
|
+
- Arrays and primitives at any level are always treated as leaf values — they are never traversed.
|
|
74
|
+
- The result is always a flat JSON object with dot-notation keys.
|
|
75
|
+
- There is no depth limit — objects of any nesting level are fully flattened.
|
|
56
76
|
- Run `mono-rele2-utils-cli` with no args to list all available skills
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@julong/mono-rele2-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Use this skill to invoke text utility functions via the mono-rele2-utils CLI. Handles class name merging, case conversion, and text truncation.",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"build": "tsup",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
26
|
"clean": "rimraf dist",
|
|
27
|
-
"readme": "
|
|
27
|
+
"readme": "bun ../common/build/update-readme.mjs"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.29.0",
|