@ikhono/mcp 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +8 -6
  2. package/dist/index.js +46 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -83,7 +83,7 @@ IKHONO_API_TOKEN=YOUR_TOKEN npx @ikhono/mcp
83
83
 
84
84
  | Tool | Description |
85
85
  |------|-------------|
86
- | `ikhono_skill_search` | Search for skills by query, category, or author |
86
+ | `ikhono_skill_search` | Search for skills by query, category, or author (returns top 3 previews) |
87
87
  | `ikhono_skill_get` | Load a skill's full instructions by slug |
88
88
  | `ikhono_skill_pin` | Pin a skill to your favorites |
89
89
  | `ikhono_skill_unpin` | Remove a skill from your favorites |
@@ -92,7 +92,7 @@ IKHONO_API_TOKEN=YOUR_TOKEN npx @ikhono/mcp
92
92
 
93
93
  ### ikhono_skill_search
94
94
 
95
- Search for skills matching a query.
95
+ Search for skills matching a query. Returns a concise preview of the top results (default: 3) with name, description, rating, and usage stats. Use `ikhono_skill_get` to load the full skill instructions.
96
96
 
97
97
  | Parameter | Type | Required | Description |
98
98
  |-----------|------|----------|-------------|
@@ -100,7 +100,7 @@ Search for skills matching a query.
100
100
  | `category` | string | No | Filter by category |
101
101
  | `author` | string | No | Filter by author (e.g., "@alice") |
102
102
  | `mine` | boolean | No | Show only your skills |
103
- | `limit` | number | No | Max results (default: 5) |
103
+ | `limit` | number | No | Max results (default: 3) |
104
104
 
105
105
  ### ikhono_skill_get
106
106
 
@@ -135,9 +135,11 @@ Rate a skill after using it.
135
135
  ## How It Works
136
136
 
137
137
  1. Your AI agent receives a task (e.g., "review this code")
138
- 2. The agent calls `ikhono_skill_search` to find relevant skills
139
- 3. The agent calls `ikhono_skill_get` to load the skill's instructions
140
- 4. The agent follows those instructions for a better, more structured response
138
+ 2. The agent calls `ikhono_skill_search` to find relevant skills (top 3 previews)
139
+ 3. The user picks which skill to load — the agent calls `ikhono_skill_get` with that slug
140
+ 4. The agent follows the skill's instructions for a better, more structured response
141
+
142
+ If none of the results fit, the user can ask to search again with a higher `limit` or a different query.
141
143
 
142
144
  Skills are community-created Markdown documents with structured processes, checklists, and templates. The MCP server is a thin proxy to the iKhono API — no local storage or computation needed.
143
145
 
package/dist/index.js CHANGED
@@ -85,9 +85,26 @@ var searchToolSchema = z.object({
85
85
  category: z.string().optional().describe('Filter by category (e.g., "security", "testing", "documentation")'),
86
86
  author: z.string().optional().describe('Filter by author username (e.g., "@alice" or "alice")'),
87
87
  mine: z.boolean().optional().describe("Set to true to show only your own skills (requires authentication)"),
88
- limit: z.number().optional().default(5).describe("Maximum number of results to return")
88
+ limit: z.number().optional().default(3).describe("Maximum number of results to return")
89
89
  });
90
90
  var searchToolDescription = `Search iKhono for AI skills that match a query. Use this when the user asks you to do something that could benefit from specialized expertise. Returns a list of matching skills with their names, descriptions, ratings, usage counts, and pin counts.`;
91
+ function formatSearchResults(results) {
92
+ if (results.length === 0) {
93
+ return "No skills found matching your query.";
94
+ }
95
+ const lines = [`Found ${results.length} skill${results.length === 1 ? "" : "s"}:
96
+ `];
97
+ for (let i = 0; i < results.length; i++) {
98
+ const r = results[i];
99
+ const rating = r.avgRating > 0 ? `${r.avgRating.toFixed(1)}/5 (${r.ratingCount} rating${r.ratingCount === 1 ? "" : "s"})` : "no ratings yet";
100
+ lines.push(`${i + 1}. ${r.slug} (v${r.latestVersion})`);
101
+ lines.push(` ${r.description}`);
102
+ lines.push(` ${rating} \xB7 ${r.totalUses} uses \xB7 ${r.pinCount} pins`);
103
+ lines.push("");
104
+ }
105
+ lines.push("Load a skill with ikhono_skill_get using the slug.");
106
+ return lines.join("\n");
107
+ }
91
108
  async function handleSearch(client2, args2) {
92
109
  const results = await client2.searchSkills(args2.query, {
93
110
  category: args2.category,
@@ -151,7 +168,7 @@ async function handleRate(client2, args2) {
151
168
  // src/version.ts
152
169
  import { createRequire } from "module";
153
170
  function getVersion() {
154
- if (true) return "0.1.2";
171
+ if (true) return "0.1.4";
155
172
  const require2 = createRequire(import.meta.url);
156
173
  return require2("../package.json").version;
157
174
  }
@@ -168,7 +185,7 @@ function readCliConfig() {
168
185
  }
169
186
  var cliConfig = readCliConfig();
170
187
  var args = process.argv.slice(2);
171
- var apiUrl = "http://localhost:3456";
188
+ var apiUrl = "https://ikhono.io";
172
189
  var token;
173
190
  for (let i = 0; i < args.length; i++) {
174
191
  if (args[i] === "--api-url" && args[i + 1]) {
@@ -179,7 +196,7 @@ for (let i = 0; i < args.length; i++) {
179
196
  i++;
180
197
  }
181
198
  }
182
- apiUrl = process.env.IKHONO_API_URL || apiUrl || cliConfig.apiUrl || "http://localhost:3456";
199
+ apiUrl = process.env.IKHONO_API_URL || apiUrl || cliConfig.apiUrl || "https://ikhono.io";
183
200
  token = process.env.IKHONO_API_TOKEN || token || cliConfig.token;
184
201
  var client = new IkhonoClient({ apiUrl, token });
185
202
  var server = new McpServer({
@@ -188,15 +205,21 @@ var server = new McpServer({
188
205
  }, {
189
206
  instructions: `You have access to iKhono, a registry of AI skills that enhance your capabilities. When the user asks you to do something that could benefit from specialized expertise (code review, test writing, documentation, refactoring, etc.), use ikhono_skill_search to find relevant skills, then use ikhono_skill_get to load the skill's full instructions. Follow those instructions to give a better, more structured response. Always tell the user which skill you're using and its rating.`
190
207
  });
191
- server.tool(searchToolName, searchToolDescription, searchToolSchema.shape, async (args2) => {
208
+ server.registerTool(searchToolName, {
209
+ description: searchToolDescription,
210
+ inputSchema: searchToolSchema.shape
211
+ }, async (args2) => {
192
212
  try {
193
213
  const results = await handleSearch(client, searchToolSchema.parse(args2));
194
- return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
214
+ return { content: [{ type: "text", text: formatSearchResults(results) }] };
195
215
  } catch (err) {
196
216
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
197
217
  }
198
218
  });
199
- server.tool(getSkillToolName, getSkillToolDescription, getSkillToolSchema.shape, async (args2) => {
219
+ server.registerTool(getSkillToolName, {
220
+ description: getSkillToolDescription,
221
+ inputSchema: getSkillToolSchema.shape
222
+ }, async (args2) => {
200
223
  try {
201
224
  const parsed = getSkillToolSchema.parse(args2);
202
225
  const skill = await handleGetSkill(client, parsed);
@@ -216,7 +239,10 @@ This helps skill creators improve their work.`;
216
239
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
217
240
  }
218
241
  });
219
- server.tool(pinToolName, pinToolDescription, pinToolSchema.shape, async (args2) => {
242
+ server.registerTool(pinToolName, {
243
+ description: pinToolDescription,
244
+ inputSchema: pinToolSchema.shape
245
+ }, async (args2) => {
220
246
  try {
221
247
  const result = await handlePin(client, pinToolSchema.parse(args2));
222
248
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -224,7 +250,10 @@ server.tool(pinToolName, pinToolDescription, pinToolSchema.shape, async (args2)
224
250
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
225
251
  }
226
252
  });
227
- server.tool(unpinToolName, unpinToolDescription, unpinToolSchema.shape, async (args2) => {
253
+ server.registerTool(unpinToolName, {
254
+ description: unpinToolDescription,
255
+ inputSchema: unpinToolSchema.shape
256
+ }, async (args2) => {
228
257
  try {
229
258
  const result = await handleUnpin(client, unpinToolSchema.parse(args2));
230
259
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -232,7 +261,10 @@ server.tool(unpinToolName, unpinToolDescription, unpinToolSchema.shape, async (a
232
261
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
233
262
  }
234
263
  });
235
- server.tool(listPinnedToolName, listPinnedToolDescription, listPinnedToolSchema.shape, async () => {
264
+ server.registerTool(listPinnedToolName, {
265
+ description: listPinnedToolDescription,
266
+ inputSchema: listPinnedToolSchema.shape
267
+ }, async () => {
236
268
  try {
237
269
  const result = await handleListPinned(client);
238
270
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
@@ -240,7 +272,10 @@ server.tool(listPinnedToolName, listPinnedToolDescription, listPinnedToolSchema.
240
272
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
241
273
  }
242
274
  });
243
- server.tool(rateToolName, rateToolDescription, rateToolSchema.shape, async (args2) => {
275
+ server.registerTool(rateToolName, {
276
+ description: rateToolDescription,
277
+ inputSchema: rateToolSchema.shape
278
+ }, async (args2) => {
244
279
  try {
245
280
  const result = await handleRate(client, rateToolSchema.parse(args2));
246
281
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikhono/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "iKhono MCP Server — runtime skill router for AI agents",
5
5
  "type": "module",
6
6
  "bin": {