@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.
- package/README.md +8 -6
- package/dist/index.js +46 -11
- 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:
|
|
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`
|
|
140
|
-
4. The agent follows
|
|
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(
|
|
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.
|
|
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 = "
|
|
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 || "
|
|
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.
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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) }] };
|