@julong/mono-rele2-utils 1.19.0 → 1.20.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 CHANGED
@@ -90,7 +90,7 @@ mono-rele2-utils-cli objectFlattenTool <json>
90
90
 
91
91
  | arg | type | description |
92
92
  |-----|------|-------------|
93
- | `json` | string | JSON string of a nested object to flatten (unlimited depth) |
93
+ | `json` | string \| JSON object | JSON string or parsed object to flatten (unlimited depth) |
94
94
 
95
95
  ```sh
96
96
  mono-rele2-utils-cli objectFlattenTool '{"user":{"name":"Alice","address":{"city":"Seoul","zip":"12345"}},"active":true}' # {
@@ -104,6 +104,70 @@ mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'
104
104
  }
105
105
  ```
106
106
 
107
+ #### `getUserTool`
108
+
109
+ RandomUser API 형식의 사용자 객체를 받아 이름과 거주 도시로 구성된 한글 문장을 반환합니다. JSON 문자열 또는 파싱된 객체를 입력받습니다..
110
+
111
+ ```sh
112
+ mono-rele2-utils-cli getUserTool <user>
113
+ ```
114
+
115
+ | arg | type | description |
116
+ |-----|------|-------------|
117
+ | `user` | RandomUser | RandomUser 형식의 JSON 문자열 또는 객체 — name.first / name.last / location.city 필수 |
118
+
119
+ **`user`** type definition:
120
+
121
+ ```typescript
122
+ interface RandomUser {
123
+ gender: "male" | "female";
124
+ name: {
125
+ title: string;
126
+ first: string;
127
+ last: string;
128
+ };
129
+ location: {
130
+ street: {
131
+ number: number;
132
+ name: string;
133
+ };
134
+ city: string;
135
+ state: string;
136
+ country: string;
137
+ postcode: string | number;
138
+ coordinates: {
139
+ latitude: string;
140
+ longitude: string;
141
+ };
142
+ timezone: {
143
+ offset: string;
144
+ description: string;
145
+ };
146
+ };
147
+ email: string;
148
+ login: {
149
+ uuid: string;
150
+ username: string;
151
+ };
152
+ dob: {
153
+ date: string;
154
+ age: number;
155
+ };
156
+ phone: string;
157
+ cell: string;
158
+ picture: {
159
+ large: string;
160
+ medium: string;
161
+ thumbnail: string;
162
+ };
163
+ nat: string;
164
+ }
165
+ ```
166
+
167
+ ```sh
168
+ mono-rele2-utils-cli getUserTool '{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}' # 이름은 Alice Kim 이고 현재 Seoul 에 살고 있습니다.
169
+ ```
170
+
107
171
  ## MCP Server
108
172
 
109
173
  ```sh
package/dist/cli.js CHANGED
@@ -1,10 +1,6 @@
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
+ function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as C}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as U}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as h}from"zod";async function T(e){let[,,t,...n]=process.argv;(!t||!(t in e))&&(t&&console.error(`Unknown skill: "${t}"
3
+ `),process.exit(t?1:0));let o=e[t],s=Object.keys(o.inputSchema),a={};for(let i=0;i<s.length;i++){let m=n[i];if(m!==void 0)try{a[s[i]]=JSON.parse(m)}catch{a[s[i]]=m}}let d=h.object(o.inputSchema).parse(a),f=await o.handler(d);for(let i of f.content)i.type==="text"&&console.log(i.text)}function b(e){e instanceof h.ZodError?console.error("Validation error:",e.issues.map(t=>`${t.path.join(".")}: ${t.message}`).join(", ")):console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}import{z as _}from"zod";import{z as l}from"zod";function x(...e){return e.filter(Boolean).join(" ")}var c={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:l.array(l.string()).describe("List of class names to merge")},handler:async({classes:e})=>r(x(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:l.string().describe("Text to convert"),to:l.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:t})=>r(A(e,t)),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:l.string().describe("Text to truncate"),maxLength:l.number().int().positive().describe("Maximum character length"),suffix:l.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:t,suffix:n})=>{let o=e.length<=t?e:e.slice(0,t-n.length)+n;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},$=c.cnTool,j=c.caseConvertTool,O=c.truncateTool;function A(e,t){switch(t){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,o)=>o.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 p}from"zod";function z(e){let t=typeof e=="string"?JSON.parse(e):e,n={};function o(s,a){if(s===null||typeof s!="object"||Array.isArray(s)){n[a]=s;return}for(let[d,f]of Object.entries(s)){let i=a?`${a}.${d}`:d;o(f,i)}}return o(t,""),n}var g={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:p.union([p.string(),p.record(p.string(),p.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let n=z(e);return r(JSON.stringify(n,null,2))}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${t.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.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},k=g.objectFlattenTool;import{z as u}from"zod";function v(e){let t=typeof e=="string"?JSON.parse(e):e,{first:n,last:o}=t.name,s=t.location.city;return`\uC774\uB984\uC740 ${n} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var y={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:u.union([u.string(),u.record(u.string(),u.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
4
+ `)},handler:async({user:e})=>{try{let n=v(e);return r(n)}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${t.message}
3
5
 
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(`
8
-
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);
6
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},D=y.getUserTool;var w={...c,...g,...y};T(w).catch(b);
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ type AnyToolDef = {
16
16
  handler: (args: any) => Promise<ToolResult>;
17
17
  examples?: ToolExample[];
18
18
  guidelines?: string[];
19
+ typeLabels?: Record<string, string>;
20
+ typeDefs?: Record<string, string>;
19
21
  };
20
22
 
21
23
  type SkillTools = Record<string, AnyToolDef>;
@@ -30,17 +32,41 @@ declare function generateReadmeSkills(opts: {
30
32
  }): string;
31
33
 
32
34
  declare const tools: {
35
+ getUserTool: {
36
+ name: string;
37
+ description: string;
38
+ inputSchema: {
39
+ readonly user: zod.ZodUnion<readonly [zod.ZodString, zod.ZodRecord<zod.ZodString, zod.ZodAny>]>;
40
+ };
41
+ handler: (input: {
42
+ user: string | Record<string, any>;
43
+ }) => Promise<ToolResult>;
44
+ examples?: ToolExample[];
45
+ guidelines?: string[];
46
+ typeLabels?: {
47
+ readonly user?: string | undefined;
48
+ } | undefined;
49
+ typeDefs?: {
50
+ readonly user?: string | undefined;
51
+ } | undefined;
52
+ };
33
53
  objectFlattenTool: {
34
54
  name: string;
35
55
  description: string;
36
56
  inputSchema: {
37
- readonly json: zod.ZodString;
57
+ readonly json: zod.ZodUnion<readonly [zod.ZodString, zod.ZodRecord<zod.ZodString, zod.ZodAny>]>;
38
58
  };
39
59
  handler: (input: {
40
- json: string;
60
+ json: string | Record<string, any>;
41
61
  }) => Promise<ToolResult>;
42
62
  examples?: ToolExample[];
43
63
  guidelines?: string[];
64
+ typeLabels?: {
65
+ readonly json?: string | undefined;
66
+ } | undefined;
67
+ typeDefs?: {
68
+ readonly json?: string | undefined;
69
+ } | undefined;
44
70
  };
45
71
  cnTool: {
46
72
  name: string;
@@ -53,6 +79,12 @@ declare const tools: {
53
79
  }) => Promise<ToolResult>;
54
80
  examples?: ToolExample[];
55
81
  guidelines?: string[];
82
+ typeLabels?: {
83
+ readonly classes?: string | undefined;
84
+ } | undefined;
85
+ typeDefs?: {
86
+ readonly classes?: string | undefined;
87
+ } | undefined;
56
88
  };
57
89
  caseConvertTool: {
58
90
  name: string;
@@ -74,6 +106,14 @@ declare const tools: {
74
106
  }) => Promise<ToolResult>;
75
107
  examples?: ToolExample[];
76
108
  guidelines?: string[];
109
+ typeLabels?: {
110
+ readonly input?: string | undefined;
111
+ readonly to?: string | undefined;
112
+ } | undefined;
113
+ typeDefs?: {
114
+ readonly input?: string | undefined;
115
+ readonly to?: string | undefined;
116
+ } | undefined;
77
117
  };
78
118
  truncateTool: {
79
119
  name: string;
@@ -90,6 +130,16 @@ declare const tools: {
90
130
  }) => Promise<ToolResult>;
91
131
  examples?: ToolExample[];
92
132
  guidelines?: string[];
133
+ typeLabels?: {
134
+ readonly input?: string | undefined;
135
+ readonly maxLength?: string | undefined;
136
+ readonly suffix?: string | undefined;
137
+ } | undefined;
138
+ typeDefs?: {
139
+ readonly input?: string | undefined;
140
+ readonly maxLength?: string | undefined;
141
+ readonly suffix?: string | undefined;
142
+ } | undefined;
93
143
  };
94
144
  };
95
145
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
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`---
1
+ function x(...t){return t.filter(Boolean).join(" ")}function c(t){return{content:[{type:"text",text:t}]}}import{McpServer as H}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as X}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as te}from"zod";import{z as i}from"zod";function k(t){let{binName:n,description:e,tools:o}=t;return`---
2
2
  name: ${n}
3
3
  description: ${e}
4
4
  ---
@@ -11,35 +11,46 @@ ${n} <toolName> [...args]
11
11
 
12
12
  ## Skills
13
13
 
14
- ${Z(t)}
14
+ ${v(o)}
15
15
 
16
16
  ## Examples
17
17
 
18
- ${j(n,t)}
18
+ ${R(n,o)}
19
19
 
20
20
  ## Guidelines
21
21
 
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}
22
+ ${J(n,o)}
23
+ `}function v(t){return Object.entries(t).map(([n,e])=>{let o=Object.entries(e.inputSchema).map(([a,r])=>{let l=e.typeLabels?.[a];return`| \`${a}\` | ${D(r,l)} |`}).join(`
24
+ `),s=e.typeDefs?Object.entries(e.typeDefs).map(([a,r])=>`
25
+
26
+ **\`${a}\`** type definition:
27
+ \`\`\`typescript
28
+ ${r}
29
+ \`\`\``).join(""):"";return`### ${n}
25
30
 
26
31
  ${e.description}
27
32
 
28
33
  | arg | description |
29
34
  |-----|-------------|
30
- ${t}`}).join(`
35
+ ${o}${s}`}).join(`
31
36
 
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(`
37
+ `)}function D(t,n){let e=t.description??"",o=t,s,a=!1;for(;o instanceof i.ZodOptional||o instanceof i.ZodDefault;){if(o instanceof i.ZodDefault){let l=o.def.defaultValue;s=typeof l=="function"?l():l}o instanceof i.ZodOptional&&(a=!0),o=o.unwrap()}let r=[];if(n&&r.push(`Type: ${n}`),e&&r.push(e),o instanceof i.ZodEnum){let l=o.options.map(m=>`\`${m}\``).join(" \\| ");r.push(l)}return s!==void 0?r.push(`default: \`${String(s)}\``):a&&r.push("optional"),r.join(" \u2014 ")}function R(t,n){let e=[];for(let[o,s]of Object.entries(n))if(s.examples)for(let a of s.examples){let r=[t,o,...a.args].join(" ");e.push(`- \`${r}\` => \`${a.result}\``)}return e.join(`
38
+ `)}function J(t,n){let e=[],o=new Set,s=r=>{o.has(r)||(o.add(r),e.push(r))};s("Arguments are positional \u2014 pass them in the order listed in each skill's table");let a=Object.values(n).flatMap(r=>Object.values(r.inputSchema));a.some(r=>w(r,i.ZodNumber))&&s("Numeric args are auto-parsed \u2014 pass as plain numbers (e.g. `10`)"),a.some(r=>w(r,i.ZodArray))&&s('Array args must be valid JSON \u2014 wrap in single quotes on Unix shells (e.g. `\'["a","b"]\'`)'),a.some(r=>r instanceof i.ZodOptional||r instanceof i.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 \`${t}\` with no args to list all available skills`),e.map(r=>`- ${r}`).join(`
39
+ `)}function w(t,n){let e=t;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof n}function N(t){let{binName:n,tools:e}=t;return Object.entries(e).map(([o,s])=>C(n,o,s)).join(`
35
40
 
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=`
41
+ `)}function C(t,n,e){let o=Object.entries(e.inputSchema),s=o.map(([p,u])=>u instanceof i.ZodOptional||u instanceof i.ZodDefault?`[${p}]`:`<${p}>`).join(" "),a=[t,n,s].filter(Boolean).join(" "),r=o.map(([p,u])=>{let T=e.typeLabels?.[p],d=$(u,T),b=E(u);return`| \`${p}\` | ${d} | ${b} |`}).join(`
42
+ `),l=e.examples??[],m="";if(l.length>0){let p=l.map(d=>[t,n,...d.args].join(" ")),u=Math.max(...p.map(d=>d.length));m=`
38
43
 
39
44
  \`\`\`sh
40
45
  ${l.map((d,b)=>`${p[b].padEnd(u)} # ${d.result}`).join(`
41
46
  `)}
42
- \`\`\``}let S=r?`
47
+ \`\`\``}let A=e.typeDefs?Object.entries(e.typeDefs).map(([p,u])=>`
48
+
49
+ **\`${p}\`** type definition:
50
+
51
+ \`\`\`typescript
52
+ ${u}
53
+ \`\`\``).join(""):"",z=r?`
43
54
 
44
55
  | arg | type | description |
45
56
  |-----|------|-------------|
@@ -48,5 +59,8 @@ ${r}`:"";return`#### \`${n}\`
48
59
  ${e.description}.
49
60
 
50
61
  \`\`\`sh
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};
62
+ ${a}
63
+ \`\`\`${z}${A}${m}`}function $(t,n){if(n)return n;let e=t;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;)e=e.unwrap();return e instanceof i.ZodEnum?e.options.map(o=>`\`${o}\``).join(" \\| "):e instanceof i.ZodNumber?"number":e instanceof i.ZodString?"string":e instanceof i.ZodBoolean?"boolean":e instanceof i.ZodArray?"JSON string (array)":e instanceof i.ZodUnion?e.options.map(s=>$(s)).join(" \\| "):e instanceof i.ZodRecord?"JSON object":"unknown"}function E(t){let n=t.description??"",e=t,o,s=!1;for(;e instanceof i.ZodOptional||e instanceof i.ZodDefault;){if(e instanceof i.ZodDefault){let a=e.def.defaultValue;o=typeof a=="function"?a():a}e instanceof i.ZodOptional&&(s=!0),e=e.unwrap()}return o!==void 0?`${n} (default: \`${String(o)}\`)`:s?`${n} (optional)`:n}import{z as f}from"zod";var g={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:t})=>c(x(...t)),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:t,to:n})=>c(V(t,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:t,maxLength:n,suffix:e})=>{let o=t.length<=n?t:t.slice(0,n-e.length)+e;return c(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},U=g.cnTool,M=g.caseConvertTool,L=g.truncateTool;function V(t,n){switch(n){case"upper":return t.toUpperCase();case"lower":return t.toLowerCase();case"capitalize":return t.charAt(0).toUpperCase()+t.slice(1).toLowerCase();case"camel":return t.replace(/[-_\s]+(.)/g,(e,o)=>o.toUpperCase()).replace(/^(.)/,e=>e.toLowerCase());case"snake":return t.replace(/([A-Z])/g,"_$1").replace(/[-\s]+/g,"_").toLowerCase().replace(/^_/,"");case"kebab":return t.replace(/([A-Z])/g,"-$1").replace(/[_\s]+/g,"-").toLowerCase().replace(/^-/,"")}}import{z as y}from"zod";function P(t){let n=typeof t=="string"?JSON.parse(t):t,e={};function o(s,a){if(s===null||typeof s!="object"||Array.isArray(s)){e[a]=s;return}for(let[r,l]of Object.entries(s)){let m=a?`${a}.${r}`:r;o(l,m)}}return o(n,""),e}var S={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:y.union([y.string(),y.record(y.string(),y.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:t})=>{try{let e=P(t);return c(JSON.stringify(e,null,2))}catch(n){return n instanceof SyntaxError?c("Error: invalid JSON string \u2014 unable to parse input"):c(`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.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},_=S.objectFlattenTool;import{z as h}from"zod";function I(t){let n=typeof t=="string"?JSON.parse(t):t,{first:e,last:o}=n.name,s=n.location.city;return`\uC774\uB984\uC740 ${e} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var Z={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:h.union([h.string(),h.record(h.string(),h.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
64
+ `)},handler:async({user:t})=>{try{let e=I(t);return c(e)}catch(n){return n instanceof SyntaxError?c("Error: invalid JSON string \u2014 unable to parse input"):c(`Error: failed to process user data \u2014 ${n.message}
65
+
66
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},F=Z.getUserTool;var B={...g,...S,...Z};export{x as cn,N as generateReadmeSkills,k as generateSkillMarkdown,B as tools};
package/dist/server.js CHANGED
@@ -1,2 +1,5 @@
1
1
  #!/usr/bin/env node
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)});
2
+ function r(e){return{content:[{type:"text",text:e}]}}import{McpServer as O}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as A}from"@modelcontextprotocol/sdk/server/stdio.js";function b(e,t){let n=new O(e);for(let o of t)n.registerTool(o.name,{description:o.description,inputSchema:o.inputSchema},o.handler);return n}async function S(e){let t=new A;await e.connect(t)}import{z as M}from"zod";import{z as P}from"zod";import{z as i}from"zod";function x(...e){return e.filter(Boolean).join(" ")}var a={cnTool:{name:"cn",description:"Merges class names, filtering out falsy values",inputSchema:{classes:i.array(i.string()).describe("List of class names to merge")},handler:async({classes:e})=>r(x(...e)),examples:[{args:[`'["btn","active","large"]'`],result:"btn active large"}]},caseConvertTool:{name:"case_convert",description:"Converts text to the specified case format",inputSchema:{input:i.string().describe("Text to convert"),to:i.enum(["upper","lower","capitalize","camel","snake","kebab"]).describe("Target case format")},handler:async({input:e,to:t})=>r(z(e,t)),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:i.string().describe("Text to truncate"),maxLength:i.number().int().positive().describe("Maximum character length"),suffix:i.string().default("...").describe("Suffix to append when truncated")},handler:async({input:e,maxLength:t,suffix:n})=>{let o=e.length<=t?e:e.slice(0,t-n.length)+n;return r(o)},examples:[{args:['"hello world long text"',"10"],result:"hello w..."},{args:['"hello world"',"8",'"\u2026"'],result:"hello w\u2026"}]}},u=a.cnTool,d=a.caseConvertTool,f=a.truncateTool;function z(e,t){switch(t){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,o)=>o.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 l}from"zod";function v(e){let t=typeof e=="string"?JSON.parse(e):e,n={};function o(s,p){if(s===null||typeof s!="object"||Array.isArray(s)){n[p]=s;return}for(let[h,j]of Object.entries(s)){let $=p?`${p}.${h}`:h;o(j,$)}}return o(t,""),n}var m={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:l.union([l.string(),l.record(l.string(),l.any())]).describe("JSON string or parsed object to flatten (unlimited depth)")},handler:async({json:e})=>{try{let n=v(e);return r(JSON.stringify(n,null,2))}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to flatten object \u2014 ${t.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.","Input is accepted as a JSON string (MCP/CLI) and parsed internally."]}},g=m.objectFlattenTool;import{z as c}from"zod";function k(e){let t=typeof e=="string"?JSON.parse(e):e,{first:n,last:o}=t.name,s=t.location.city;return`\uC774\uB984\uC740 ${n} ${o} \uC774\uACE0 \uD604\uC7AC ${s} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.`}var y={getUserTool:{name:"getUser",description:"RandomUser API \uD615\uC2DD\uC758 \uC0AC\uC6A9\uC790 \uAC1D\uCCB4\uB97C \uBC1B\uC544 \uC774\uB984\uACFC \uAC70\uC8FC \uB3C4\uC2DC\uB85C \uAD6C\uC131\uB41C \uD55C\uAE00 \uBB38\uC7A5\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uD30C\uC2F1\uB41C \uAC1D\uCCB4\uB97C \uC785\uB825\uBC1B\uC2B5\uB2C8\uB2E4.",inputSchema:{user:c.union([c.string(),c.record(c.string(),c.any())]).describe("RandomUser \uD615\uC2DD\uC758 JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4 \u2014 name.first / name.last / location.city \uD544\uC218")},typeLabels:{user:"RandomUser"},typeDefs:{user:["interface RandomUser {",' gender: "male" | "female";'," name: {"," title: string;"," first: string;"," last: string;"," };"," location: {"," street: {"," number: number;"," name: string;"," };"," city: string;"," state: string;"," country: string;"," postcode: string | number;"," coordinates: {"," latitude: string;"," longitude: string;"," };"," timezone: {"," offset: string;"," description: string;"," };"," };"," email: string;"," login: {"," uuid: string;"," username: string;"," };"," dob: {"," date: string;"," age: number;"," };"," phone: string;"," cell: string;"," picture: {"," large: string;"," medium: string;"," thumbnail: string;"," };"," nat: string;","}"].join(`
3
+ `)},handler:async({user:e})=>{try{let n=k(e);return r(n)}catch(t){return t instanceof SyntaxError?r("Error: invalid JSON string \u2014 unable to parse input"):r(`Error: failed to process user data \u2014 ${t.message}
4
+
5
+ Input must include \`name.first\`, \`name.last\`, and \`location.city\`.`)}},examples:[{args:[`'{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'`],result:"\uC774\uB984\uC740 Alice Kim \uC774\uACE0 \uD604\uC7AC Seoul \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4."}],guidelines:["\uC785\uB825\uC740 RandomUser API \uC2A4\uD399\uC744 \uB530\uB985\uB2C8\uB2E4. \uCD5C\uC18C\uD55C name.first, name.last, location.city \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.","JSON \uBB38\uC790\uC5F4(string)\uACFC \uD30C\uC2F1\uB41C \uAC1D\uCCB4 \uBAA8\uB450 \uD5C8\uC6A9\uB429\uB2C8\uB2E4 (CLI / MCP \uBAA8\uB450 \uB3D9\uC791).","\uACB0\uACFC\uB294 \uD56D\uC0C1 '\uC774\uB984\uC740 {first} {last} \uC774\uACE0 \uD604\uC7AC {city} \uC5D0 \uC0B4\uACE0 \uC788\uC2B5\uB2C8\uB2E4.' \uD615\uC2DD\uC758 \uD55C\uAE00 \uBB38\uC7A5\uC785\uB2C8\uB2E4."]}},T=y.getUserTool;var de={...a,...m,...y};var D=b({name:"mono-rele2-utils",version:"1.0.0"},[u,d,f,g,T]);S(D).catch(e=>{console.error("[utils] server error:",e),process.exit(1)});
@@ -44,7 +44,62 @@ Flattens a nested JSON object of any depth into dot-notation key-value pairs. Ac
44
44
 
45
45
  | arg | description |
46
46
  |-----|-------------|
47
- | `json` | JSON string of a nested object to flatten (unlimited depth) |
47
+ | `json` | JSON string or parsed object to flatten (unlimited depth) |
48
+
49
+ ### getUserTool
50
+
51
+ RandomUser API 형식의 사용자 객체를 받아 이름과 거주 도시로 구성된 한글 문장을 반환합니다. JSON 문자열 또는 파싱된 객체를 입력받습니다.
52
+
53
+ | arg | description |
54
+ |-----|-------------|
55
+ | `user` | Type: RandomUser — RandomUser 형식의 JSON 문자열 또는 객체 — name.first / name.last / location.city 필수 |
56
+
57
+ **`user`** type definition:
58
+ ```typescript
59
+ interface RandomUser {
60
+ gender: "male" | "female";
61
+ name: {
62
+ title: string;
63
+ first: string;
64
+ last: string;
65
+ };
66
+ location: {
67
+ street: {
68
+ number: number;
69
+ name: string;
70
+ };
71
+ city: string;
72
+ state: string;
73
+ country: string;
74
+ postcode: string | number;
75
+ coordinates: {
76
+ latitude: string;
77
+ longitude: string;
78
+ };
79
+ timezone: {
80
+ offset: string;
81
+ description: string;
82
+ };
83
+ };
84
+ email: string;
85
+ login: {
86
+ uuid: string;
87
+ username: string;
88
+ };
89
+ dob: {
90
+ date: string;
91
+ age: number;
92
+ };
93
+ phone: string;
94
+ cell: string;
95
+ picture: {
96
+ large: string;
97
+ medium: string;
98
+ thumbnail: string;
99
+ };
100
+ nat: string;
101
+ }
102
+ ```
48
103
 
49
104
  ## Examples
50
105
 
@@ -63,6 +118,7 @@ Flattens a nested JSON object of any depth into dot-notation key-value pairs. Ac
63
118
  - `mono-rele2-utils-cli objectFlattenTool '{"a":{"b":{"c":{"d":{"e":"deep"}}}}}'` => `{
64
119
  "a.b.c.d.e": "deep"
65
120
  }`
121
+ - `mono-rele2-utils-cli getUserTool '{"name":{"title":"Mr","first":"Alice","last":"Kim"},"location":{"city":"Seoul"},"gender":"female","email":"alice@example.com","nat":"KR"}'` => `이름은 Alice Kim 이고 현재 Seoul 에 살고 있습니다.`
66
122
 
67
123
  ## Guidelines
68
124
 
@@ -73,4 +129,8 @@ Flattens a nested JSON object of any depth into dot-notation key-value pairs. Ac
73
129
  - Arrays and primitives at any level are always treated as leaf values — they are never traversed.
74
130
  - The result is always a flat JSON object with dot-notation keys.
75
131
  - There is no depth limit — objects of any nesting level are fully flattened.
132
+ - Input is accepted as a JSON string (MCP/CLI) and parsed internally.
133
+ - 입력은 RandomUser API 스펙을 따릅니다. 최소한 name.first, name.last, location.city 필드가 필요합니다.
134
+ - JSON 문자열(string)과 파싱된 객체 모두 허용됩니다 (CLI / MCP 모두 동작).
135
+ - 결과는 항상 '이름은 {first} {last} 이고 현재 {city} 에 살고 있습니다.' 형식의 한글 문장입니다.
76
136
  - 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.19.0",
3
+ "version": "1.20.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",