apiskill 0.1.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.
@@ -0,0 +1,408 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ checkCache,
4
+ crawlOpenApiUrl,
5
+ createApi,
6
+ createDocument,
7
+ deleteApi,
8
+ editApi,
9
+ formatSavedResult,
10
+ getAiContext,
11
+ getEndpointDetails,
12
+ getSchemaDetails,
13
+ importOpenApiCurl,
14
+ importOpenApiFile,
15
+ importOpenApiUrl,
16
+ listVersions,
17
+ queryApi,
18
+ searchEndpoints,
19
+ } from './lib/apiskill-core.mjs';
20
+
21
+ const SERVER_VERSION = '0.1.0';
22
+
23
+ const tools = [
24
+ {
25
+ name: 'apiskill_check',
26
+ description: 'Check whether a usable cached OpenAPI document is configured. Returns import examples when no cache is available.',
27
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {},
30
+ },
31
+ },
32
+ {
33
+ name: 'apiskill_help',
34
+ description: 'Show API Skill MCP help, including available tools, read/write categories, and import examples.',
35
+ inputSchema: {
36
+ type: 'object',
37
+ properties: {},
38
+ },
39
+ },
40
+ {
41
+ name: 'apiskill_list_versions',
42
+ description: 'List cached OpenAPI/Swagger document versions available to this API Skill MCP server.',
43
+ inputSchema: {
44
+ type: 'object',
45
+ properties: {
46
+ limit: { type: 'number', description: 'Maximum versions to return. Default 20.' },
47
+ },
48
+ },
49
+ },
50
+ {
51
+ name: 'apiskill_search_endpoints',
52
+ description: 'Search cached API endpoints by path, summary, tag, method, or parameter text.',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ query: { type: 'string', description: 'Search keyword. Empty returns the first endpoints.' },
57
+ method: { type: 'string', description: 'Optional HTTP method filter such as GET or POST.' },
58
+ tag: { type: 'string', description: 'Optional tag filter.' },
59
+ versionId: { type: 'string', description: 'Optional cached version id. Defaults to latest.' },
60
+ limit: { type: 'number', description: 'Maximum results to return. Default 20.' },
61
+ },
62
+ },
63
+ },
64
+ {
65
+ name: 'apiskill_query_api',
66
+ description: 'CLI-equivalent endpoint query. A single match returns details or CLI config; multiple matches return candidates.',
67
+ inputSchema: {
68
+ type: 'object',
69
+ required: ['pathOrKeyword'],
70
+ properties: {
71
+ pathOrKeyword: { type: 'string', description: 'Exact API path or search keyword.' },
72
+ method: { type: 'string', description: 'Optional HTTP method filter such as GET or POST.' },
73
+ versionId: { type: 'string', description: 'Optional cached version id. Defaults to latest.' },
74
+ limit: { type: 'number', description: 'Maximum candidate rows when multiple endpoints match. Default 20.' },
75
+ format: { type: 'string', enum: ['json', 'cli', 'raw'], description: 'Single-match output format. Default json.' },
76
+ },
77
+ },
78
+ },
79
+ {
80
+ name: 'apiskill_get_endpoint',
81
+ description: 'Get one endpoint with request parameters, request body fields, response fields, manual config, and optional raw operation.',
82
+ inputSchema: {
83
+ type: 'object',
84
+ required: ['method', 'path'],
85
+ properties: {
86
+ method: { type: 'string', description: 'HTTP method, for example GET.' },
87
+ path: { type: 'string', description: 'Exact OpenAPI path.' },
88
+ versionId: { type: 'string', description: 'Optional cached version id. Defaults to latest.' },
89
+ includeRaw: { type: 'boolean', description: 'Whether to include raw OpenAPI operation JSON. Default false.' },
90
+ },
91
+ },
92
+ },
93
+ {
94
+ name: 'apiskill_get_ai_context',
95
+ description: 'Return AI-friendly Markdown for one endpoint, including parameters and response field descriptions.',
96
+ inputSchema: {
97
+ type: 'object',
98
+ required: ['method', 'path'],
99
+ properties: {
100
+ method: { type: 'string', description: 'HTTP method, for example POST.' },
101
+ path: { type: 'string', description: 'Exact OpenAPI path.' },
102
+ versionId: { type: 'string', description: 'Optional cached version id. Defaults to latest.' },
103
+ },
104
+ },
105
+ },
106
+ {
107
+ name: 'apiskill_get_schema',
108
+ description: 'Get a named schema from the cached OpenAPI document, with flattened field descriptions.',
109
+ inputSchema: {
110
+ type: 'object',
111
+ required: ['name'],
112
+ properties: {
113
+ name: { type: 'string', description: 'Schema name or suffix to resolve.' },
114
+ versionId: { type: 'string', description: 'Optional cached version id. Defaults to latest.' },
115
+ },
116
+ },
117
+ },
118
+ {
119
+ name: 'apiskill_import_url',
120
+ description: 'Import a direct OpenAPI/Swagger JSON or YAML URL into the local cache. This writes a new cached version.',
121
+ inputSchema: {
122
+ type: 'object',
123
+ required: ['url'],
124
+ properties: {
125
+ url: { type: 'string', description: 'Direct OpenAPI JSON/YAML URL.' },
126
+ auth: { type: 'string', description: 'Optional basic auth credentials in username:password format.' },
127
+ },
128
+ },
129
+ },
130
+ {
131
+ name: 'apiskill_crawl_openapi',
132
+ description: 'Crawl a Swagger UI / Knife4j / Redoc page and import the discovered OpenAPI document. This writes a new cached version.',
133
+ inputSchema: {
134
+ type: 'object',
135
+ required: ['url'],
136
+ properties: {
137
+ url: { type: 'string', description: 'Online API documentation page URL.' },
138
+ auth: { type: 'string', description: 'Optional basic auth credentials in username:password format.' },
139
+ },
140
+ },
141
+ },
142
+ {
143
+ name: 'apiskill_import_file',
144
+ description: 'Import a local OpenAPI/Swagger JSON or YAML file into the cache. This writes a new cached version.',
145
+ inputSchema: {
146
+ type: 'object',
147
+ required: ['file'],
148
+ properties: {
149
+ file: { type: 'string', description: 'Local JSON/YAML file path readable by the MCP server process.' },
150
+ },
151
+ },
152
+ },
153
+ {
154
+ name: 'apiskill_import_curl',
155
+ description: 'Execute a curl command or curl command file and import a JSON/YAML OpenAPI response. This writes a new cached version.',
156
+ inputSchema: {
157
+ type: 'object',
158
+ properties: {
159
+ curlText: { type: 'string', description: 'Curl command text.' },
160
+ curlFile: { type: 'string', description: 'Local file containing a curl command.' },
161
+ },
162
+ },
163
+ },
164
+ {
165
+ name: 'apiskill_create_document',
166
+ description: 'Create a blank local OpenAPI document version so agents can author API operations from scratch.',
167
+ inputSchema: {
168
+ type: 'object',
169
+ properties: {
170
+ title: { type: 'string', description: 'Document title. Default API Skill Document.' },
171
+ version: { type: 'string', description: 'OpenAPI info.version value. Default 1.0.0.' },
172
+ description: { type: 'string', description: 'Document description.' },
173
+ environmentName: { type: 'string', description: 'Optional environment name metadata.' },
174
+ environmentBaseUrl: { type: 'string', description: 'Optional environment base URL metadata.' },
175
+ },
176
+ },
177
+ },
178
+ {
179
+ name: 'apiskill_create_api',
180
+ description: 'Create a manual API operation. If versionId is omitted, this writes a new manual version.',
181
+ inputSchema: {
182
+ type: 'object',
183
+ properties: {
184
+ versionId: { type: 'string', description: 'Optional version id to append to. Defaults to a new manual version.' },
185
+ configText: { type: 'string', description: 'JSON/YAML/CLI config text. Use root key api, config, or operation.' },
186
+ config: { type: 'object', description: 'Manual API config object.' },
187
+ },
188
+ },
189
+ },
190
+ {
191
+ name: 'apiskill_edit_api',
192
+ description: 'Edit/replace one API operation in a cached version. Defaults to latest if versionId is omitted.',
193
+ inputSchema: {
194
+ type: 'object',
195
+ required: ['method', 'path'],
196
+ properties: {
197
+ method: { type: 'string', description: 'Original HTTP method.' },
198
+ path: { type: 'string', description: 'Original OpenAPI path.' },
199
+ versionId: { type: 'string', description: 'Optional version id. Defaults to latest.' },
200
+ configText: { type: 'string', description: 'Replacement JSON/YAML/CLI config text.' },
201
+ config: { type: 'object', description: 'Replacement manual API config object.' },
202
+ },
203
+ },
204
+ },
205
+ {
206
+ name: 'apiskill_delete_api',
207
+ description: 'Delete one API operation from a cached version. Defaults to latest if versionId is omitted.',
208
+ inputSchema: {
209
+ type: 'object',
210
+ required: ['method', 'path'],
211
+ properties: {
212
+ method: { type: 'string', description: 'HTTP method.' },
213
+ path: { type: 'string', description: 'OpenAPI path.' },
214
+ versionId: { type: 'string', description: 'Optional version id. Defaults to latest.' },
215
+ },
216
+ },
217
+ },
218
+ ];
219
+
220
+ const handlers = {
221
+ apiskill_check: checkCache,
222
+ apiskill_help: async () => buildHelp(),
223
+ apiskill_list_versions: listVersions,
224
+ apiskill_search_endpoints: searchEndpoints,
225
+ apiskill_query_api: queryApi,
226
+ apiskill_get_endpoint: getEndpointDetails,
227
+ apiskill_get_ai_context: getAiContext,
228
+ apiskill_get_schema: getSchemaDetails,
229
+ apiskill_import_url: async (args) => formatSavedResult(await importOpenApiUrl(args)),
230
+ apiskill_crawl_openapi: async (args) => formatSavedResult(await crawlOpenApiUrl(args)),
231
+ apiskill_import_file: async (args) => formatSavedResult(await importOpenApiFile(args)),
232
+ apiskill_import_curl: async (args) => formatSavedResult(await importOpenApiCurl(args)),
233
+ apiskill_create_document: async (args) => formatSavedResult(await createDocument(args)),
234
+ apiskill_create_api: async (args) => formatSavedResult(await createApi(args)),
235
+ apiskill_edit_api: async (args) => formatSavedResult(await editApi(args)),
236
+ apiskill_delete_api: async (args) => formatSavedResult(await deleteApi(args)),
237
+ };
238
+
239
+ function buildHelp() {
240
+ const rootDir = process.env.APISKILL_ROOT || '/Users/dobby/dev/apiskill';
241
+ const cacheDir = process.env.APISKILL_CACHE_DIR || `${rootDir}/cache`;
242
+ const readTools = [
243
+ 'apiskill_check',
244
+ 'apiskill_help',
245
+ 'apiskill_list_versions',
246
+ 'apiskill_search_endpoints',
247
+ 'apiskill_query_api',
248
+ 'apiskill_get_endpoint',
249
+ 'apiskill_get_ai_context',
250
+ 'apiskill_get_schema',
251
+ ];
252
+ const writeTools = [
253
+ 'apiskill_import_url',
254
+ 'apiskill_crawl_openapi',
255
+ 'apiskill_import_file',
256
+ 'apiskill_import_curl',
257
+ 'apiskill_create_document',
258
+ 'apiskill_create_api',
259
+ 'apiskill_edit_api',
260
+ 'apiskill_delete_api',
261
+ ];
262
+ const byName = new Map(tools.map((tool) => [tool.name, tool]));
263
+ return {
264
+ name: 'API Skill MCP',
265
+ description: 'Search, import, and maintain cached OpenAPI/Swagger documents for coding tasks.',
266
+ startup: {
267
+ command: `node ${rootDir}/scripts/mcp-server.mjs`,
268
+ env: {
269
+ APISKILL_ROOT: rootDir,
270
+ APISKILL_CACHE_DIR: cacheDir,
271
+ },
272
+ },
273
+ quickStart: [
274
+ 'Call apiskill_check first to verify that a usable cached OpenAPI document exists.',
275
+ 'If no cache is available, import one with apiskill_import_url, apiskill_crawl_openapi, apiskill_import_file, or apiskill_import_curl, or create a blank one with apiskill_create_document.',
276
+ 'Use apiskill_search_endpoints or apiskill_query_api to find endpoints.',
277
+ 'Use apiskill_get_endpoint or apiskill_get_ai_context before implementing API integration code.',
278
+ ],
279
+ readTools: readTools.map((name) => ({ name, description: byName.get(name)?.description })),
280
+ writeTools: writeTools.map((name) => ({ name, description: byName.get(name)?.description })),
281
+ importExamples: {
282
+ apiskill_import_url: { url: 'https://example.com/openapi.json' },
283
+ apiskill_crawl_openapi: { url: 'https://example.com/swagger' },
284
+ apiskill_import_file: { file: '/absolute/path/openapi.yaml' },
285
+ apiskill_import_curl: { curlText: 'curl https://example.com/openapi.json' },
286
+ apiskill_create_document: { title: 'My API', version: '1.0.0' },
287
+ },
288
+ docs: {
289
+ english: `${rootDir}/docs/mcp.md`,
290
+ chinese: `${rootDir}/docs/mcp.zh.md`,
291
+ },
292
+ };
293
+ }
294
+
295
+ let inputBuffer = Buffer.alloc(0);
296
+
297
+ process.stdin.on('data', (chunk) => {
298
+ inputBuffer = Buffer.concat([inputBuffer, chunk]);
299
+ parseMessages();
300
+ });
301
+
302
+ process.stdin.on('end', () => process.exit(0));
303
+
304
+ function parseMessages() {
305
+ while (true) {
306
+ const headerEnd = inputBuffer.indexOf('\r\n\r\n');
307
+ if (headerEnd === -1) return;
308
+
309
+ const header = inputBuffer.slice(0, headerEnd).toString('utf8');
310
+ const match = /content-length:\s*(\d+)/i.exec(header);
311
+ if (!match) {
312
+ inputBuffer = inputBuffer.slice(headerEnd + 4);
313
+ continue;
314
+ }
315
+
316
+ const length = Number(match[1]);
317
+ const bodyStart = headerEnd + 4;
318
+ const bodyEnd = bodyStart + length;
319
+ if (inputBuffer.length < bodyEnd) return;
320
+
321
+ const raw = inputBuffer.slice(bodyStart, bodyEnd).toString('utf8');
322
+ inputBuffer = inputBuffer.slice(bodyEnd);
323
+ handleMessage(JSON.parse(raw)).catch((error) => {
324
+ if (raw.includes('"id"')) {
325
+ const id = safeParseId(raw);
326
+ sendError(id, -32603, error instanceof Error ? error.message : 'Internal error');
327
+ }
328
+ });
329
+ }
330
+ }
331
+
332
+ async function handleMessage(message) {
333
+ if (!Object.prototype.hasOwnProperty.call(message, 'id')) return;
334
+
335
+ if (message.method === 'initialize') {
336
+ sendResult(message.id, {
337
+ protocolVersion: message.params?.protocolVersion || '2024-11-05',
338
+ capabilities: { tools: {} },
339
+ serverInfo: { name: 'apiskill-mcp', version: SERVER_VERSION },
340
+ });
341
+ return;
342
+ }
343
+
344
+ if (message.method === 'ping') {
345
+ sendResult(message.id, {});
346
+ return;
347
+ }
348
+
349
+ if (message.method === 'tools/list') {
350
+ sendResult(message.id, { tools });
351
+ return;
352
+ }
353
+
354
+ if (message.method === 'tools/call') {
355
+ const toolName = message.params?.name;
356
+ const handler = handlers[toolName];
357
+ if (!handler) {
358
+ sendError(message.id, -32602, `Unknown tool: ${toolName}`);
359
+ return;
360
+ }
361
+
362
+ try {
363
+ const result = await handler(message.params?.arguments ?? {});
364
+ sendResult(message.id, {
365
+ content: [
366
+ {
367
+ type: 'text',
368
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
369
+ },
370
+ ],
371
+ });
372
+ } catch (error) {
373
+ sendResult(message.id, {
374
+ isError: true,
375
+ content: [
376
+ {
377
+ type: 'text',
378
+ text: error instanceof Error ? error.message : 'Tool call failed',
379
+ },
380
+ ],
381
+ });
382
+ }
383
+ return;
384
+ }
385
+
386
+ sendError(message.id, -32601, `Unsupported method: ${message.method}`);
387
+ }
388
+
389
+ function sendResult(id, result) {
390
+ sendMessage({ jsonrpc: '2.0', id, result });
391
+ }
392
+
393
+ function sendError(id, code, message) {
394
+ sendMessage({ jsonrpc: '2.0', id, error: { code, message } });
395
+ }
396
+
397
+ function sendMessage(message) {
398
+ const body = JSON.stringify(message);
399
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`);
400
+ }
401
+
402
+ function safeParseId(raw) {
403
+ try {
404
+ return JSON.parse(raw).id ?? null;
405
+ } catch {
406
+ return null;
407
+ }
408
+ }
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: apiskill
3
+ description: Search, import, and maintain cached Swagger/OpenAPI documents through the API Skill MCP server. Use when a user asks to find an API endpoint, inspect request parameters, request body fields, response fields, schema definitions, compare cached API versions, import API docs, maintain manual API configs, or gather source-of-truth API context before implementing frontend/backend integration code.
4
+ ---
5
+
6
+ # API Skill
7
+
8
+ ## Overview
9
+
10
+ Use API Skill to query and maintain locally cached Swagger/OpenAPI documents through MCP tools. Prefer it whenever API contract details matter, especially before coding a request, mapping response fields, or validating whether a field exists.
11
+
12
+ ## Workflow
13
+
14
+ 1. Start with `apiskill_check` when cache availability is unknown. If it reports no usable cache, ask whether to import/crawl through MCP, CLI, or the web app.
15
+ 2. Use `apiskill_help` when you need a compact MCP tool guide inside the client.
16
+ 3. Check available versions with `apiskill_list_versions` when version freshness matters or the user asks about a specific import.
17
+ 4. Search candidates with `apiskill_search_endpoints` or use CLI-equivalent `apiskill_query_api` when exact-match behavior or CLI config output is useful.
18
+ 5. Inspect the selected endpoint with `apiskill_get_endpoint` for structured parameters, request bodies, responses, manual config, and optionally raw OpenAPI operation JSON.
19
+ 6. Use `apiskill_get_ai_context` when the next step is coding or explaining an endpoint; it returns concise Markdown suitable for implementation notes.
20
+ 7. Use `apiskill_get_schema` when the user names a schema/model or when an endpoint response refers to a schema that needs field-level expansion.
21
+ 8. Only use import/create/edit/delete tools when the user explicitly wants to update the local API cache or maintain manual API operations.
22
+
23
+ If the MCP tools are not visible in the current tool list, search for tools named `apiskill` and then call the matching MCP tool. If the server reports that no cached document exists, ask whether to import/crawl through MCP, CLI, or the API Skill web app.
24
+
25
+ ## Tool Guide
26
+
27
+ `apiskill_check`
28
+ : Check whether the current cache has a usable latest OpenAPI document. Use this before API work when cache state is unknown.
29
+
30
+ `apiskill_help`
31
+ : Show available MCP tools, read/write categories, import examples, and documentation paths.
32
+
33
+ `apiskill_list_versions`
34
+ : List cached OpenAPI/Swagger versions. Use this before answering questions about "latest", when comparing imports, or when a user provides a version id.
35
+
36
+ `apiskill_search_endpoints`
37
+ : Search endpoint path, summary, tag, method, and parameter text. Pass `method`, `tag`, `versionId`, and `limit` only when they help narrow results.
38
+
39
+ `apiskill_query_api`
40
+ : CLI-equivalent endpoint query. Use it when the user gives a path or keyword and expects either one exact API config or a candidate list. Set `format: "cli"` when the user wants CLI config text.
41
+
42
+ `apiskill_get_endpoint`
43
+ : Return structured endpoint details plus manual config. Set `includeRaw: true` only when raw OpenAPI metadata is needed for edge cases, vendor extensions, or exact source inspection.
44
+
45
+ `apiskill_get_ai_context`
46
+ : Return Markdown with request parameters, request body fields, and response fields. Use this output directly as the implementation contract when editing code.
47
+
48
+ `apiskill_get_schema`
49
+ : Resolve a schema by full name or suffix and return flattened fields plus raw schema. Use this for named models, DTOs, and response wrappers.
50
+
51
+ `apiskill_import_url`, `apiskill_crawl_openapi`, `apiskill_import_file`, `apiskill_import_curl`
52
+ : Import OpenAPI documents into the shared local cache. These tools write `cache/latest-import.json` and `cache/versions/`.
53
+
54
+ `apiskill_create_api`, `apiskill_edit_api`, `apiskill_delete_api`
55
+ : Maintain manual API operations in cached versions. These are write tools; use them only when the user asks to modify local API docs.
56
+
57
+ ## Usage Rules
58
+
59
+ - Treat cached OpenAPI data as the API contract source, but mention the selected `versionId` when the answer depends on a specific cached import.
60
+ - When multiple endpoints match, prefer exact path and method matches first, then use summaries/tags to disambiguate.
61
+ - Do not infer field names from nearby endpoints when the requested endpoint or schema can be queried directly.
62
+ - For implementation tasks, quote the method, path, required request fields, and response fields that drive the code change.
63
+ - Do not call write tools just to answer a read-only API question.
64
+ - Keep answers scoped to the API data returned by the tools unless the user asks for broader analysis.
65
+
66
+ ## Common Prompts
67
+
68
+ - "搜索会员列表相关接口。"
69
+ - "查一下 `POST /api/v1/member/create` 的请求体和响应字段。"
70
+ - "这个字段应该从哪个响应字段取?先查接口文档。"
71
+ - "把 `UserSearchRes` schema 展开成前端字段说明。"
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "API Skill"
3
+ short_description: "Search cached OpenAPI docs"
4
+ default_prompt: "Use $apiskill to search cached Swagger/OpenAPI endpoints and schemas before coding against an API."