@ikhono/mcp 0.2.1 → 0.2.6

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
@@ -132,6 +132,34 @@ Rate a skill after using it.
132
132
  | `stars` | number | Yes | Rating from 1 to 5 |
133
133
  | `review` | string | No | Optional text review |
134
134
 
135
+ ## Streamable HTTP Transport
136
+
137
+ The iKhono MCP server is also available as a hosted HTTP endpoint, useful for MCP registries (Smithery, etc.) and web-based clients:
138
+
139
+ ```
140
+ POST https://ikhono.io/mcp
141
+ ```
142
+
143
+ Headers:
144
+ - `Content-Type: application/json`
145
+ - `Accept: application/json, text/event-stream`
146
+ - `Authorization: Bearer YOUR_TOKEN` (optional, for authenticated operations)
147
+
148
+ Sessions are stateful — the server returns an `Mcp-Session-Id` header to include in subsequent requests.
149
+
150
+ ### Programmatic Usage
151
+
152
+ The server factory and client are available as package exports for embedding in your own server:
153
+
154
+ ```typescript
155
+ import { createMcpServer } from '@ikhono/mcp/server';
156
+ import { IkhonoClient } from '@ikhono/mcp/client';
157
+
158
+ const client = new IkhonoClient({ apiUrl: 'https://ikhono.io', token: 'optional' });
159
+ const server = createMcpServer(client);
160
+ // Connect your own transport...
161
+ ```
162
+
135
163
  ## How It Works
136
164
 
137
165
  1. Your AI agent receives a task (e.g., "review this code")
@@ -0,0 +1,24 @@
1
+ interface IkhonoClientConfig {
2
+ apiUrl: string;
3
+ token?: string;
4
+ }
5
+ declare class IkhonoClient {
6
+ private apiUrl;
7
+ private token?;
8
+ constructor(config: IkhonoClientConfig);
9
+ private headers;
10
+ private handleResponse;
11
+ searchSkills(query: string, options?: {
12
+ category?: string;
13
+ author?: string;
14
+ mine?: boolean;
15
+ limit?: number;
16
+ }): Promise<unknown>;
17
+ getSkill(slug: string): Promise<unknown>;
18
+ pinSkill(slug: string): Promise<unknown>;
19
+ unpinSkill(slug: string): Promise<unknown>;
20
+ listPinned(): Promise<unknown>;
21
+ rateSkill(slug: string, stars: number, review?: string): Promise<unknown>;
22
+ }
23
+
24
+ export { IkhonoClient, type IkhonoClientConfig };
package/dist/client.js ADDED
@@ -0,0 +1,72 @@
1
+ // src/client.ts
2
+ var IkhonoClient = class {
3
+ apiUrl;
4
+ token;
5
+ constructor(config) {
6
+ this.apiUrl = config.apiUrl.replace(/\/$/, "");
7
+ this.token = config.token;
8
+ }
9
+ headers() {
10
+ const h = { "Content-Type": "application/json" };
11
+ if (this.token) {
12
+ h["Authorization"] = `Bearer ${this.token}`;
13
+ }
14
+ return h;
15
+ }
16
+ async handleResponse(res, action) {
17
+ if (!res.ok) {
18
+ let message = res.statusText;
19
+ try {
20
+ const body = await res.json();
21
+ if (body.error) message = typeof body.error === "string" ? body.error : JSON.stringify(body.error);
22
+ } catch {
23
+ }
24
+ throw new Error(`${action}: ${message}`);
25
+ }
26
+ const json = await res.json();
27
+ return json.data;
28
+ }
29
+ async searchSkills(query, options) {
30
+ const params = new URLSearchParams();
31
+ if (query) params.set("q", query);
32
+ if (options?.category) params.set("category", options.category);
33
+ if (options?.author) params.set("author", options.author);
34
+ if (options?.mine) params.set("mine", "true");
35
+ if (options?.limit) params.set("limit", String(options.limit));
36
+ const res = await fetch(`${this.apiUrl}/api/skills?${params}`, { headers: this.headers() });
37
+ return this.handleResponse(res, "Search failed");
38
+ }
39
+ async getSkill(slug) {
40
+ const res = await fetch(`${this.apiUrl}/api/skills/${slug}?use=true`, { headers: this.headers() });
41
+ return this.handleResponse(res, "Get skill failed");
42
+ }
43
+ async pinSkill(slug) {
44
+ const res = await fetch(`${this.apiUrl}/api/skills/${slug}/pin`, {
45
+ method: "POST",
46
+ headers: this.headers()
47
+ });
48
+ return this.handleResponse(res, "Pin failed");
49
+ }
50
+ async unpinSkill(slug) {
51
+ const res = await fetch(`${this.apiUrl}/api/skills/${slug}/pin`, {
52
+ method: "DELETE",
53
+ headers: this.headers()
54
+ });
55
+ return this.handleResponse(res, "Unpin failed");
56
+ }
57
+ async listPinned() {
58
+ const res = await fetch(`${this.apiUrl}/api/skills/pinned`, { headers: this.headers() });
59
+ return this.handleResponse(res, "List pins failed");
60
+ }
61
+ async rateSkill(slug, stars, review) {
62
+ const res = await fetch(`${this.apiUrl}/api/skills/${slug}/rate`, {
63
+ method: "POST",
64
+ headers: this.headers(),
65
+ body: JSON.stringify({ stars, review })
66
+ });
67
+ return this.handleResponse(res, "Rate failed");
68
+ }
69
+ };
70
+ export {
71
+ IkhonoClient
72
+ };
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
  import { readFileSync } from "fs";
5
5
  import { join } from "path";
6
6
  import { homedir } from "os";
7
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
8
 
10
9
  // src/client.ts
@@ -77,6 +76,9 @@ var IkhonoClient = class {
77
76
  }
78
77
  };
79
78
 
79
+ // src/server.ts
80
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
81
+
80
82
  // src/tools/search.ts
81
83
  import { z } from "zod";
82
84
  var searchToolName = "ikhono_skill_search";
@@ -168,12 +170,101 @@ async function handleRate(client2, args2) {
168
170
  // src/version.ts
169
171
  import { createRequire } from "module";
170
172
  function getVersion() {
171
- if (true) return "0.2.1";
173
+ if (true) return "0.2.6";
172
174
  const require2 = createRequire(import.meta.url);
173
175
  return require2("../package.json").version;
174
176
  }
175
177
  var VERSION = getVersion();
176
178
 
179
+ // src/server.ts
180
+ function createMcpServer(client2) {
181
+ const server2 = new McpServer({
182
+ name: "iKhono",
183
+ version: VERSION
184
+ }, {
185
+ 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.`
186
+ });
187
+ server2.registerTool(searchToolName, {
188
+ description: searchToolDescription,
189
+ inputSchema: searchToolSchema.shape
190
+ }, async (args2) => {
191
+ try {
192
+ const results = await handleSearch(client2, searchToolSchema.parse(args2));
193
+ return { content: [{ type: "text", text: formatSearchResults(results) }] };
194
+ } catch (err) {
195
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
196
+ }
197
+ });
198
+ server2.registerTool(getSkillToolName, {
199
+ description: getSkillToolDescription,
200
+ inputSchema: getSkillToolSchema.shape
201
+ }, async (args2) => {
202
+ try {
203
+ const parsed = getSkillToolSchema.parse(args2);
204
+ const skill = await handleGetSkill(client2, parsed);
205
+ const slug = parsed.slug;
206
+ const prompt = `IMPORTANT: After you finish the task using this skill, you MUST ask the user:
207
+ "Would you like to pin or rate the skill ${slug}? Rate it 1-5 stars."
208
+ If they respond with a rating, call ikhono_skill_rate with slug "${slug}" and their star count.
209
+ If they want to pin it, call ikhono_skill_pin with slug "${slug}".
210
+ This helps skill creators improve their work.`;
211
+ return {
212
+ content: [
213
+ { type: "text", text: JSON.stringify(skill, null, 2) },
214
+ { type: "text", text: prompt }
215
+ ]
216
+ };
217
+ } catch (err) {
218
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
219
+ }
220
+ });
221
+ server2.registerTool(pinToolName, {
222
+ description: pinToolDescription,
223
+ inputSchema: pinToolSchema.shape
224
+ }, async (args2) => {
225
+ try {
226
+ const result = await handlePin(client2, pinToolSchema.parse(args2));
227
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
228
+ } catch (err) {
229
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
230
+ }
231
+ });
232
+ server2.registerTool(unpinToolName, {
233
+ description: unpinToolDescription,
234
+ inputSchema: unpinToolSchema.shape
235
+ }, async (args2) => {
236
+ try {
237
+ const result = await handleUnpin(client2, unpinToolSchema.parse(args2));
238
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
239
+ } catch (err) {
240
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
241
+ }
242
+ });
243
+ server2.registerTool(listPinnedToolName, {
244
+ description: listPinnedToolDescription,
245
+ inputSchema: listPinnedToolSchema.shape
246
+ }, async () => {
247
+ try {
248
+ const result = await handleListPinned(client2);
249
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
250
+ } catch (err) {
251
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
252
+ }
253
+ });
254
+ server2.registerTool(rateToolName, {
255
+ description: rateToolDescription,
256
+ inputSchema: rateToolSchema.shape
257
+ }, async (args2) => {
258
+ try {
259
+ const result = await handleRate(client2, rateToolSchema.parse(args2));
260
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
261
+ } catch (err) {
262
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
263
+ }
264
+ });
265
+ return server2;
266
+ }
267
+
177
268
  // src/index.ts
178
269
  function readCliConfig() {
179
270
  try {
@@ -199,89 +290,6 @@ for (let i = 0; i < args.length; i++) {
199
290
  apiUrl = process.env.IKHONO_API_URL || apiUrl || cliConfig.apiUrl || "https://ikhono.io";
200
291
  token = process.env.IKHONO_API_TOKEN || token || cliConfig.token;
201
292
  var client = new IkhonoClient({ apiUrl, token });
202
- var server = new McpServer({
203
- name: "iKhono",
204
- version: VERSION
205
- }, {
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.`
207
- });
208
- server.registerTool(searchToolName, {
209
- description: searchToolDescription,
210
- inputSchema: searchToolSchema.shape
211
- }, async (args2) => {
212
- try {
213
- const results = await handleSearch(client, searchToolSchema.parse(args2));
214
- return { content: [{ type: "text", text: formatSearchResults(results) }] };
215
- } catch (err) {
216
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
217
- }
218
- });
219
- server.registerTool(getSkillToolName, {
220
- description: getSkillToolDescription,
221
- inputSchema: getSkillToolSchema.shape
222
- }, async (args2) => {
223
- try {
224
- const parsed = getSkillToolSchema.parse(args2);
225
- const skill = await handleGetSkill(client, parsed);
226
- const slug = parsed.slug;
227
- const prompt = `IMPORTANT: After you finish the task using this skill, you MUST ask the user:
228
- "Would you like to pin or rate the skill ${slug}? Rate it 1-5 stars."
229
- If they respond with a rating, call ikhono_skill_rate with slug "${slug}" and their star count.
230
- If they want to pin it, call ikhono_skill_pin with slug "${slug}".
231
- This helps skill creators improve their work.`;
232
- return {
233
- content: [
234
- { type: "text", text: JSON.stringify(skill, null, 2) },
235
- { type: "text", text: prompt }
236
- ]
237
- };
238
- } catch (err) {
239
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
240
- }
241
- });
242
- server.registerTool(pinToolName, {
243
- description: pinToolDescription,
244
- inputSchema: pinToolSchema.shape
245
- }, async (args2) => {
246
- try {
247
- const result = await handlePin(client, pinToolSchema.parse(args2));
248
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
249
- } catch (err) {
250
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
251
- }
252
- });
253
- server.registerTool(unpinToolName, {
254
- description: unpinToolDescription,
255
- inputSchema: unpinToolSchema.shape
256
- }, async (args2) => {
257
- try {
258
- const result = await handleUnpin(client, unpinToolSchema.parse(args2));
259
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
260
- } catch (err) {
261
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
262
- }
263
- });
264
- server.registerTool(listPinnedToolName, {
265
- description: listPinnedToolDescription,
266
- inputSchema: listPinnedToolSchema.shape
267
- }, async () => {
268
- try {
269
- const result = await handleListPinned(client);
270
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
271
- } catch (err) {
272
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
273
- }
274
- });
275
- server.registerTool(rateToolName, {
276
- description: rateToolDescription,
277
- inputSchema: rateToolSchema.shape
278
- }, async (args2) => {
279
- try {
280
- const result = await handleRate(client, rateToolSchema.parse(args2));
281
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
282
- } catch (err) {
283
- return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
284
- }
285
- });
293
+ var server = createMcpServer(client);
286
294
  var transport = new StdioServerTransport();
287
295
  await server.connect(transport);
@@ -0,0 +1,6 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { IkhonoClient } from './client.js';
3
+
4
+ declare function createMcpServer(client: IkhonoClient): McpServer;
5
+
6
+ export { createMcpServer };
package/dist/server.js ADDED
@@ -0,0 +1,191 @@
1
+ // src/server.ts
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+
4
+ // src/tools/search.ts
5
+ import { z } from "zod";
6
+ var searchToolName = "ikhono_skill_search";
7
+ var searchToolSchema = z.object({
8
+ query: z.string().optional().default("").describe('Search query to find relevant skills (e.g., "security review", "write tests", "api docs"). Leave empty when using mine or author filters to list all matching skills.'),
9
+ category: z.string().optional().describe('Filter by category (e.g., "security", "testing", "documentation")'),
10
+ author: z.string().optional().describe('Filter by author username (e.g., "@alice" or "alice")'),
11
+ mine: z.boolean().optional().describe("Set to true to show only your own skills (requires authentication)"),
12
+ limit: z.number().optional().default(3).describe("Maximum number of results to return")
13
+ });
14
+ 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.`;
15
+ function formatSearchResults(results) {
16
+ if (results.length === 0) {
17
+ return "No skills found matching your query.";
18
+ }
19
+ const lines = [`Found ${results.length} skill${results.length === 1 ? "" : "s"}:
20
+ `];
21
+ for (let i = 0; i < results.length; i++) {
22
+ const r = results[i];
23
+ const rating = r.avgRating > 0 ? `${r.avgRating.toFixed(1)}/5 (${r.ratingCount} rating${r.ratingCount === 1 ? "" : "s"})` : "no ratings yet";
24
+ lines.push(`${i + 1}. ${r.slug} (v${r.latestVersion})`);
25
+ lines.push(` ${r.description}`);
26
+ lines.push(` ${rating} \xB7 ${r.totalUses} uses \xB7 ${r.pinCount} pins`);
27
+ lines.push("");
28
+ }
29
+ lines.push("Load a skill with ikhono_skill_get using the slug.");
30
+ return lines.join("\n");
31
+ }
32
+ async function handleSearch(client, args) {
33
+ const results = await client.searchSkills(args.query, {
34
+ category: args.category,
35
+ author: args.author,
36
+ mine: args.mine,
37
+ limit: args.limit
38
+ });
39
+ return results;
40
+ }
41
+
42
+ // src/tools/get-skill.ts
43
+ import { z as z2 } from "zod";
44
+ var getSkillToolName = "ikhono_skill_get";
45
+ var getSkillToolSchema = z2.object({
46
+ slug: z2.string().describe('The skill slug (e.g., "@alice/security-reviewer"). Get this from ikhono_skill_search results.')
47
+ });
48
+ var getSkillToolDescription = `Load a skill from iKhono by its slug. Returns the full skill content (instructions, process, templates) that you should follow to complete the user's task. After searching with ikhono_skill_search, use this tool to load the best matching skill.`;
49
+ async function handleGetSkill(client, args) {
50
+ const skill = await client.getSkill(args.slug);
51
+ return skill;
52
+ }
53
+
54
+ // src/tools/pin.ts
55
+ import { z as z3 } from "zod";
56
+ var pinToolName = "ikhono_skill_pin";
57
+ var pinToolSchema = z3.object({
58
+ slug: z3.string().describe('The skill slug to pin (e.g., "@alice/security-reviewer")')
59
+ });
60
+ var pinToolDescription = `Pin a skill to the user's favorites so it's always available. Pinned skills are shown in the user's profile.`;
61
+ async function handlePin(client, args) {
62
+ return await client.pinSkill(args.slug);
63
+ }
64
+ var unpinToolName = "ikhono_skill_unpin";
65
+ var unpinToolSchema = z3.object({
66
+ slug: z3.string().describe("The skill slug to unpin")
67
+ });
68
+ var unpinToolDescription = `Remove a skill from the user's pinned favorites.`;
69
+ async function handleUnpin(client, args) {
70
+ return await client.unpinSkill(args.slug);
71
+ }
72
+ var listPinnedToolName = "ikhono_skill_list_pinned";
73
+ var listPinnedToolSchema = z3.object({});
74
+ var listPinnedToolDescription = `List all skills the user has pinned. Returns pinned skills with their names, descriptions, and ratings.`;
75
+ async function handleListPinned(client) {
76
+ return await client.listPinned();
77
+ }
78
+
79
+ // src/tools/rate.ts
80
+ import { z as z4 } from "zod";
81
+ var rateToolName = "ikhono_skill_rate";
82
+ var rateToolSchema = z4.object({
83
+ slug: z4.string().describe('The skill slug to rate (e.g., "@alice/security-reviewer")'),
84
+ stars: z4.number().min(1).max(5).describe("Rating from 1 to 5 stars"),
85
+ review: z4.string().optional().describe("Optional text review")
86
+ });
87
+ var rateToolDescription = `Rate a skill after using it. Helps the community discover the best skills.`;
88
+ async function handleRate(client, args) {
89
+ return await client.rateSkill(args.slug, args.stars, args.review);
90
+ }
91
+
92
+ // src/version.ts
93
+ import { createRequire } from "module";
94
+ function getVersion() {
95
+ if (true) return "0.2.6";
96
+ const require2 = createRequire(import.meta.url);
97
+ return require2("../package.json").version;
98
+ }
99
+ var VERSION = getVersion();
100
+
101
+ // src/server.ts
102
+ function createMcpServer(client) {
103
+ const server = new McpServer({
104
+ name: "iKhono",
105
+ version: VERSION
106
+ }, {
107
+ 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.`
108
+ });
109
+ server.registerTool(searchToolName, {
110
+ description: searchToolDescription,
111
+ inputSchema: searchToolSchema.shape
112
+ }, async (args) => {
113
+ try {
114
+ const results = await handleSearch(client, searchToolSchema.parse(args));
115
+ return { content: [{ type: "text", text: formatSearchResults(results) }] };
116
+ } catch (err) {
117
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
118
+ }
119
+ });
120
+ server.registerTool(getSkillToolName, {
121
+ description: getSkillToolDescription,
122
+ inputSchema: getSkillToolSchema.shape
123
+ }, async (args) => {
124
+ try {
125
+ const parsed = getSkillToolSchema.parse(args);
126
+ const skill = await handleGetSkill(client, parsed);
127
+ const slug = parsed.slug;
128
+ const prompt = `IMPORTANT: After you finish the task using this skill, you MUST ask the user:
129
+ "Would you like to pin or rate the skill ${slug}? Rate it 1-5 stars."
130
+ If they respond with a rating, call ikhono_skill_rate with slug "${slug}" and their star count.
131
+ If they want to pin it, call ikhono_skill_pin with slug "${slug}".
132
+ This helps skill creators improve their work.`;
133
+ return {
134
+ content: [
135
+ { type: "text", text: JSON.stringify(skill, null, 2) },
136
+ { type: "text", text: prompt }
137
+ ]
138
+ };
139
+ } catch (err) {
140
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
141
+ }
142
+ });
143
+ server.registerTool(pinToolName, {
144
+ description: pinToolDescription,
145
+ inputSchema: pinToolSchema.shape
146
+ }, async (args) => {
147
+ try {
148
+ const result = await handlePin(client, pinToolSchema.parse(args));
149
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
150
+ } catch (err) {
151
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
152
+ }
153
+ });
154
+ server.registerTool(unpinToolName, {
155
+ description: unpinToolDescription,
156
+ inputSchema: unpinToolSchema.shape
157
+ }, async (args) => {
158
+ try {
159
+ const result = await handleUnpin(client, unpinToolSchema.parse(args));
160
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
161
+ } catch (err) {
162
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
163
+ }
164
+ });
165
+ server.registerTool(listPinnedToolName, {
166
+ description: listPinnedToolDescription,
167
+ inputSchema: listPinnedToolSchema.shape
168
+ }, async () => {
169
+ try {
170
+ const result = await handleListPinned(client);
171
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
172
+ } catch (err) {
173
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
174
+ }
175
+ });
176
+ server.registerTool(rateToolName, {
177
+ description: rateToolDescription,
178
+ inputSchema: rateToolSchema.shape
179
+ }, async (args) => {
180
+ try {
181
+ const result = await handleRate(client, rateToolSchema.parse(args));
182
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
183
+ } catch (err) {
184
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
185
+ }
186
+ });
187
+ return server;
188
+ }
189
+ export {
190
+ createMcpServer
191
+ };
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "@ikhono/mcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.6",
4
4
  "description": "iKhono MCP Server — runtime skill router for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "ikhono-mcp": "./dist/index.js"
8
8
  },
9
+ "exports": {
10
+ ".": "./dist/index.js",
11
+ "./server": {
12
+ "types": "./dist/server.d.ts",
13
+ "default": "./dist/server.js"
14
+ },
15
+ "./client": {
16
+ "types": "./dist/client.d.ts",
17
+ "default": "./dist/client.js"
18
+ }
19
+ },
9
20
  "files": [
10
21
  "dist"
11
22
  ],