skilldb 0.3.0 → 0.4.1
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 +34 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +325 -0
- package/dist/mcp.js.map +1 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
# skilldb
|
|
2
2
|
|
|
3
|
-
CLI
|
|
3
|
+
CLI, TypeScript SDK, and **MCP Server** for [SkillDB](https://skilldb.dev) — discover, install, and manage AI agent skills.
|
|
4
4
|
|
|
5
|
-
5,000+ expert skills for Claude Code, Cursor,
|
|
5
|
+
5,000+ expert skills across 327 packs for Claude Code, Cursor, Windsurf, and any MCP-compatible AI tool.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## MCP Server (NEW in v0.4.0)
|
|
8
|
+
|
|
9
|
+
Connect SkillDB directly to your AI coding tool via the Model Context Protocol:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Claude Code
|
|
13
|
+
claude mcp add skilldb -- npx skilldb-mcp
|
|
14
|
+
|
|
15
|
+
# With API key (for full skill content)
|
|
16
|
+
claude mcp add skilldb -- npx skilldb-mcp --api-key sk_live_xxx
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Cursor** — add to `.cursor/mcp.json`:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"skilldb": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["skilldb-mcp"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Once connected, your AI assistant can search and load skills natively. Just ask:
|
|
32
|
+
- *"Search SkillDB for React performance patterns"*
|
|
33
|
+
- *"Load the code review skill"*
|
|
34
|
+
- *"What skills should I use for this project?"*
|
|
35
|
+
|
|
36
|
+
**5 tools exposed:** `skilldb_search`, `skilldb_get`, `skilldb_list`, `skilldb_suggest`, `skilldb_recommend`
|
|
37
|
+
|
|
38
|
+
## Quick Start (CLI)
|
|
8
39
|
|
|
9
40
|
```bash
|
|
10
41
|
# Search for skills (with inline preview)
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/cache.ts"],"sourcesContent":["export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n} from './types.js';\nexport { resolveApiKey, resolveBaseUrl, saveApiKey } from './config.js';\nexport { initCache, cacheSkill, isCached, getCachedPath, listCached } from './cache.js';\n\nimport type { ClientConfig } from './types.js';\nimport { SkillDBClient } from './client.js';\n\n/** Create a SkillDB client. Auto-loads API key from env/config. */\nexport function createClient(config?: ClientConfig): SkillDBClient {\n return new SkillDBClient(config);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.request('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest, Skill } from './types.js';\n\nconst SKILLDB_DIR = '.skilldb';\nconst SKILLS_DIR = 'skills';\nconst MANIFEST_FILE = 'manifest.json';\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction skilldbRoot(cwd?: string): string {\n return path.join(cwd ?? process.cwd(), SKILLDB_DIR);\n}\n\n/** Ensure .skilldb/ directory structure exists. */\nexport function initCache(cwd?: string): string {\n const root = skilldbRoot(cwd);\n ensureDir(path.join(root, SKILLS_DIR));\n\n const manifestPath = path.join(root, MANIFEST_FILE);\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + '\\n');\n }\n\n return root;\n}\n\n/** Read the local manifest. */\nexport function readManifest(cwd?: string): Manifest {\n const manifestPath = path.join(skilldbRoot(cwd), MANIFEST_FILE);\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n } catch {\n return { installed: {} };\n }\n}\n\n/** Write the local manifest. */\nexport function writeManifest(manifest: Manifest, cwd?: string): void {\n const root = skilldbRoot(cwd);\n ensureDir(root);\n fs.writeFileSync(\n path.join(root, MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n}\n\n/** Save a skill to the local cache. */\nexport function cacheSkill(skill: Skill, cwd?: string): string {\n const root = skilldbRoot(cwd);\n const skillDir = path.join(root, SKILLS_DIR, skill.pack);\n ensureDir(skillDir);\n\n const safeName = skill.name.replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skillDir, `${safeName}.md`);\n fs.writeFileSync(filePath, skill.content ?? `# ${skill.title}\\n\\n${skill.description}\\n`);\n\n // Update manifest\n const manifest = readManifest(cwd);\n manifest.installed[skill.id] = {\n addedAt: new Date().toISOString(),\n lines: skill.lines,\n };\n writeManifest(manifest, cwd);\n\n return filePath;\n}\n\n/** Check if a skill is already cached. */\nexport function isCached(skillId: string, cwd?: string): boolean {\n const manifest = readManifest(cwd);\n return skillId in manifest.installed;\n}\n\n/** Get the local path for a cached skill. */\nexport function getCachedPath(skillId: string, cwd?: string): string | null {\n if (!isCached(skillId, cwd)) return null;\n const [pack, file] = skillId.split('/');\n const name = file.replace('.md', '').replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);\n return fs.existsSync(filePath) ? filePath : null;\n}\n\n/** List all cached skills. */\nexport function listCached(cwd?: string): Manifest {\n return readManifest(cwd);\n}\n\n/** Ensure .skilldb/ and .skilldbrc are in .gitignore. */\nexport function updateGitignore(cwd?: string): void {\n const root = cwd ?? process.cwd();\n const gitignorePath = path.join(root, '.gitignore');\n\n const entries = ['.skilldb/', '.skilldbrc'];\n let content = '';\n\n if (fs.existsSync(gitignorePath)) {\n content = fs.readFileSync(gitignorePath, 'utf-8');\n }\n\n const toAdd = entries.filter(e => !content.includes(e));\n if (toAdd.length === 0) return;\n\n const suffix = (content && !content.endsWith('\\n') ? '\\n' : '') +\n '\\n# SkillDB\\n' + toAdd.join('\\n') + '\\n';\n\n fs.writeFileSync(gitignorePath, content + suffix);\n}\n"],"mappings":"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","names":["fs","path","os","import_node_fs","import_node_path","fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/cache.ts"],"sourcesContent":["export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n ApiKeyScope,\n ApiKeyInfo,\n} from './types.js';\nexport { resolveApiKey, resolveBaseUrl, saveApiKey } from './config.js';\nexport { initCache, cacheSkill, isCached, getCachedPath, listCached } from './cache.js';\n\nimport type { ClientConfig } from './types.js';\nimport { SkillDBClient } from './client.js';\n\n/** Create a SkillDB client. Auto-loads API key from env/config. */\nexport function createClient(config?: ClientConfig): SkillDBClient {\n return new SkillDBClient(config);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.request('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest, Skill } from './types.js';\n\nconst SKILLDB_DIR = '.skilldb';\nconst SKILLS_DIR = 'skills';\nconst MANIFEST_FILE = 'manifest.json';\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction skilldbRoot(cwd?: string): string {\n return path.join(cwd ?? process.cwd(), SKILLDB_DIR);\n}\n\n/** Ensure .skilldb/ directory structure exists. */\nexport function initCache(cwd?: string): string {\n const root = skilldbRoot(cwd);\n ensureDir(path.join(root, SKILLS_DIR));\n\n const manifestPath = path.join(root, MANIFEST_FILE);\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + '\\n');\n }\n\n return root;\n}\n\n/** Read the local manifest. */\nexport function readManifest(cwd?: string): Manifest {\n const manifestPath = path.join(skilldbRoot(cwd), MANIFEST_FILE);\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n } catch {\n return { installed: {} };\n }\n}\n\n/** Write the local manifest. */\nexport function writeManifest(manifest: Manifest, cwd?: string): void {\n const root = skilldbRoot(cwd);\n ensureDir(root);\n fs.writeFileSync(\n path.join(root, MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n}\n\n/** Save a skill to the local cache. */\nexport function cacheSkill(skill: Skill, cwd?: string): string {\n const root = skilldbRoot(cwd);\n const skillDir = path.join(root, SKILLS_DIR, skill.pack);\n ensureDir(skillDir);\n\n const safeName = skill.name.replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skillDir, `${safeName}.md`);\n fs.writeFileSync(filePath, skill.content ?? `# ${skill.title}\\n\\n${skill.description}\\n`);\n\n // Update manifest\n const manifest = readManifest(cwd);\n manifest.installed[skill.id] = {\n addedAt: new Date().toISOString(),\n lines: skill.lines,\n };\n writeManifest(manifest, cwd);\n\n return filePath;\n}\n\n/** Check if a skill is already cached. */\nexport function isCached(skillId: string, cwd?: string): boolean {\n const manifest = readManifest(cwd);\n return skillId in manifest.installed;\n}\n\n/** Get the local path for a cached skill. */\nexport function getCachedPath(skillId: string, cwd?: string): string | null {\n if (!isCached(skillId, cwd)) return null;\n const [pack, file] = skillId.split('/');\n const name = file.replace('.md', '').replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);\n return fs.existsSync(filePath) ? filePath : null;\n}\n\n/** List all cached skills. */\nexport function listCached(cwd?: string): Manifest {\n return readManifest(cwd);\n}\n\n/** Ensure .skilldb/ and .skilldbrc are in .gitignore. */\nexport function updateGitignore(cwd?: string): void {\n const root = cwd ?? process.cwd();\n const gitignorePath = path.join(root, '.gitignore');\n\n const entries = ['.skilldb/', '.skilldbrc'];\n let content = '';\n\n if (fs.existsSync(gitignorePath)) {\n content = fs.readFileSync(gitignorePath, 'utf-8');\n }\n\n const toAdd = entries.filter(e => !content.includes(e));\n if (toAdd.length === 0) return;\n\n const suffix = (content && !content.endsWith('\\n') ? '\\n' : '') +\n '\\n# SkillDB\\n' + toAdd.join('\\n') + '\\n';\n\n fs.writeFileSync(gitignorePath, content + suffix);\n}\n"],"mappings":"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","names":["fs","path","os","import_node_fs","import_node_path","fs","path"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -53,6 +53,18 @@ interface ClientConfig {
|
|
|
53
53
|
apiKey?: string;
|
|
54
54
|
baseUrl?: string;
|
|
55
55
|
}
|
|
56
|
+
/** API key scopes for permission control. */
|
|
57
|
+
type ApiKeyScope = 'read' | 'write' | 'community';
|
|
58
|
+
/** API key information returned from key management endpoints. */
|
|
59
|
+
interface ApiKeyInfo {
|
|
60
|
+
id: string;
|
|
61
|
+
key: string;
|
|
62
|
+
name: string;
|
|
63
|
+
plan: string;
|
|
64
|
+
scopes: ApiKeyScope[];
|
|
65
|
+
monthlyLimit: number;
|
|
66
|
+
rateLimit: number;
|
|
67
|
+
}
|
|
56
68
|
/** Local manifest tracking installed skills. */
|
|
57
69
|
interface Manifest {
|
|
58
70
|
installed: Record<string, {
|
|
@@ -118,4 +130,4 @@ declare function listCached(cwd?: string): Manifest;
|
|
|
118
130
|
/** Create a SkillDB client. Auto-loads API key from env/config. */
|
|
119
131
|
declare function createClient(config?: ClientConfig): SkillDBClient;
|
|
120
132
|
|
|
121
|
-
export { type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
|
|
133
|
+
export { type ApiKeyInfo, type ApiKeyScope, type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,18 @@ interface ClientConfig {
|
|
|
53
53
|
apiKey?: string;
|
|
54
54
|
baseUrl?: string;
|
|
55
55
|
}
|
|
56
|
+
/** API key scopes for permission control. */
|
|
57
|
+
type ApiKeyScope = 'read' | 'write' | 'community';
|
|
58
|
+
/** API key information returned from key management endpoints. */
|
|
59
|
+
interface ApiKeyInfo {
|
|
60
|
+
id: string;
|
|
61
|
+
key: string;
|
|
62
|
+
name: string;
|
|
63
|
+
plan: string;
|
|
64
|
+
scopes: ApiKeyScope[];
|
|
65
|
+
monthlyLimit: number;
|
|
66
|
+
rateLimit: number;
|
|
67
|
+
}
|
|
56
68
|
/** Local manifest tracking installed skills. */
|
|
57
69
|
interface Manifest {
|
|
58
70
|
installed: Record<string, {
|
|
@@ -118,4 +130,4 @@ declare function listCached(cwd?: string): Manifest;
|
|
|
118
130
|
/** Create a SkillDB client. Auto-loads API key from env/config. */
|
|
119
131
|
declare function createClient(config?: ClientConfig): SkillDBClient;
|
|
120
132
|
|
|
121
|
-
export { type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
|
|
133
|
+
export { type ApiKeyInfo, type ApiKeyScope, type CategoryInfo, type ClientConfig, type Manifest, type PackInfo, type SearchOptions, type Skill, SkillDBClient, type SkillsResponse, cacheSkill, createClient, getCachedPath, initCache, isCached, listCached, resolveApiKey, resolveBaseUrl, saveApiKey };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/client.ts","../src/cache.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.request('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest, Skill } from './types.js';\n\nconst SKILLDB_DIR = '.skilldb';\nconst SKILLS_DIR = 'skills';\nconst MANIFEST_FILE = 'manifest.json';\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction skilldbRoot(cwd?: string): string {\n return path.join(cwd ?? process.cwd(), SKILLDB_DIR);\n}\n\n/** Ensure .skilldb/ directory structure exists. */\nexport function initCache(cwd?: string): string {\n const root = skilldbRoot(cwd);\n ensureDir(path.join(root, SKILLS_DIR));\n\n const manifestPath = path.join(root, MANIFEST_FILE);\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + '\\n');\n }\n\n return root;\n}\n\n/** Read the local manifest. */\nexport function readManifest(cwd?: string): Manifest {\n const manifestPath = path.join(skilldbRoot(cwd), MANIFEST_FILE);\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n } catch {\n return { installed: {} };\n }\n}\n\n/** Write the local manifest. */\nexport function writeManifest(manifest: Manifest, cwd?: string): void {\n const root = skilldbRoot(cwd);\n ensureDir(root);\n fs.writeFileSync(\n path.join(root, MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n}\n\n/** Save a skill to the local cache. */\nexport function cacheSkill(skill: Skill, cwd?: string): string {\n const root = skilldbRoot(cwd);\n const skillDir = path.join(root, SKILLS_DIR, skill.pack);\n ensureDir(skillDir);\n\n const safeName = skill.name.replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skillDir, `${safeName}.md`);\n fs.writeFileSync(filePath, skill.content ?? `# ${skill.title}\\n\\n${skill.description}\\n`);\n\n // Update manifest\n const manifest = readManifest(cwd);\n manifest.installed[skill.id] = {\n addedAt: new Date().toISOString(),\n lines: skill.lines,\n };\n writeManifest(manifest, cwd);\n\n return filePath;\n}\n\n/** Check if a skill is already cached. */\nexport function isCached(skillId: string, cwd?: string): boolean {\n const manifest = readManifest(cwd);\n return skillId in manifest.installed;\n}\n\n/** Get the local path for a cached skill. */\nexport function getCachedPath(skillId: string, cwd?: string): string | null {\n if (!isCached(skillId, cwd)) return null;\n const [pack, file] = skillId.split('/');\n const name = file.replace('.md', '').replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);\n return fs.existsSync(filePath) ? filePath : null;\n}\n\n/** List all cached skills. */\nexport function listCached(cwd?: string): Manifest {\n return readManifest(cwd);\n}\n\n/** Ensure .skilldb/ and .skilldbrc are in .gitignore. */\nexport function updateGitignore(cwd?: string): void {\n const root = cwd ?? process.cwd();\n const gitignorePath = path.join(root, '.gitignore');\n\n const entries = ['.skilldb/', '.skilldbrc'];\n let content = '';\n\n if (fs.existsSync(gitignorePath)) {\n content = fs.readFileSync(gitignorePath, 'utf-8');\n }\n\n const toAdd = entries.filter(e => !content.includes(e));\n if (toAdd.length === 0) return;\n\n const suffix = (content && !content.endsWith('\\n') ? '\\n' : '') +\n '\\n# SkillDB\\n' + toAdd.join('\\n') + '\\n';\n\n fs.writeFileSync(gitignorePath, content + suffix);\n}\n","export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n} from './types.js';\nexport { resolveApiKey, resolveBaseUrl, saveApiKey } from './config.js';\nexport { initCache, cacheSkill, isCached, getCachedPath, listCached } from './cache.js';\n\nimport type { ClientConfig } from './types.js';\nimport { SkillDBClient } from './client.js';\n\n/** Create a SkillDB client. Auto-loads API key from env/config. */\nexport function createClient(config?: ClientConfig): SkillDBClient {\n return new SkillDBClient(config);\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/client.ts","../src/cache.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.request('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Manifest, Skill } from './types.js';\n\nconst SKILLDB_DIR = '.skilldb';\nconst SKILLS_DIR = 'skills';\nconst MANIFEST_FILE = 'manifest.json';\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction skilldbRoot(cwd?: string): string {\n return path.join(cwd ?? process.cwd(), SKILLDB_DIR);\n}\n\n/** Ensure .skilldb/ directory structure exists. */\nexport function initCache(cwd?: string): string {\n const root = skilldbRoot(cwd);\n ensureDir(path.join(root, SKILLS_DIR));\n\n const manifestPath = path.join(root, MANIFEST_FILE);\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, JSON.stringify({ installed: {} }, null, 2) + '\\n');\n }\n\n return root;\n}\n\n/** Read the local manifest. */\nexport function readManifest(cwd?: string): Manifest {\n const manifestPath = path.join(skilldbRoot(cwd), MANIFEST_FILE);\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n } catch {\n return { installed: {} };\n }\n}\n\n/** Write the local manifest. */\nexport function writeManifest(manifest: Manifest, cwd?: string): void {\n const root = skilldbRoot(cwd);\n ensureDir(root);\n fs.writeFileSync(\n path.join(root, MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n}\n\n/** Save a skill to the local cache. */\nexport function cacheSkill(skill: Skill, cwd?: string): string {\n const root = skilldbRoot(cwd);\n const skillDir = path.join(root, SKILLS_DIR, skill.pack);\n ensureDir(skillDir);\n\n const safeName = skill.name.replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skillDir, `${safeName}.md`);\n fs.writeFileSync(filePath, skill.content ?? `# ${skill.title}\\n\\n${skill.description}\\n`);\n\n // Update manifest\n const manifest = readManifest(cwd);\n manifest.installed[skill.id] = {\n addedAt: new Date().toISOString(),\n lines: skill.lines,\n };\n writeManifest(manifest, cwd);\n\n return filePath;\n}\n\n/** Check if a skill is already cached. */\nexport function isCached(skillId: string, cwd?: string): boolean {\n const manifest = readManifest(cwd);\n return skillId in manifest.installed;\n}\n\n/** Get the local path for a cached skill. */\nexport function getCachedPath(skillId: string, cwd?: string): string | null {\n if (!isCached(skillId, cwd)) return null;\n const [pack, file] = skillId.split('/');\n const name = file.replace('.md', '').replace(/[/\\\\:*?\"<>|]/g, '-');\n const filePath = path.join(skilldbRoot(cwd), SKILLS_DIR, pack, `${name}.md`);\n return fs.existsSync(filePath) ? filePath : null;\n}\n\n/** List all cached skills. */\nexport function listCached(cwd?: string): Manifest {\n return readManifest(cwd);\n}\n\n/** Ensure .skilldb/ and .skilldbrc are in .gitignore. */\nexport function updateGitignore(cwd?: string): void {\n const root = cwd ?? process.cwd();\n const gitignorePath = path.join(root, '.gitignore');\n\n const entries = ['.skilldb/', '.skilldbrc'];\n let content = '';\n\n if (fs.existsSync(gitignorePath)) {\n content = fs.readFileSync(gitignorePath, 'utf-8');\n }\n\n const toAdd = entries.filter(e => !content.includes(e));\n if (toAdd.length === 0) return;\n\n const suffix = (content && !content.endsWith('\\n') ? '\\n' : '') +\n '\\n# SkillDB\\n' + toAdd.join('\\n') + '\\n';\n\n fs.writeFileSync(gitignorePath, content + suffix);\n}\n","export { SkillDBClient } from './client.js';\nexport type {\n Skill,\n PackInfo,\n CategoryInfo,\n SkillsResponse,\n SearchOptions,\n ClientConfig,\n Manifest,\n ApiKeyScope,\n ApiKeyInfo,\n} from './types.js';\nexport { resolveApiKey, resolveBaseUrl, saveApiKey } from './config.js';\nexport { initCache, cacheSkill, isCached, getCachedPath, listCached } from './cache.js';\n\nimport type { ClientConfig } from './types.js';\nimport { SkillDBClient } from './client.js';\n\n/** Create a SkillDB client. Auto-loads API key from env/config. */\nexport function createClient(config?: ClientConfig): SkillDBClient {\n return new SkillDBClient(config);\n}\n"],"mappings":"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","names":["fs","path"]}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/config.ts
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
var RC_FILE = ".skilldbrc";
|
|
13
|
+
var DEFAULT_BASE_URL = "https://skilldb.dev/api/v1";
|
|
14
|
+
function readJson(filePath) {
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
17
|
+
return JSON.parse(raw);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function resolveApiKey() {
|
|
23
|
+
if (process.env.SKILLDB_API_KEY) {
|
|
24
|
+
return process.env.SKILLDB_API_KEY;
|
|
25
|
+
}
|
|
26
|
+
const projectRc = path.join(process.cwd(), RC_FILE);
|
|
27
|
+
const projectConfig = readJson(projectRc);
|
|
28
|
+
if (projectConfig?.apiKey) return projectConfig.apiKey;
|
|
29
|
+
const homeRc = path.join(os.homedir(), RC_FILE);
|
|
30
|
+
const homeConfig = readJson(homeRc);
|
|
31
|
+
if (homeConfig?.apiKey) return homeConfig.apiKey;
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
function resolveBaseUrl() {
|
|
35
|
+
return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/client.ts
|
|
39
|
+
var SkillDBClient = class {
|
|
40
|
+
apiKey;
|
|
41
|
+
baseUrl;
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.apiKey = config?.apiKey ?? resolveApiKey();
|
|
44
|
+
this.baseUrl = config?.baseUrl ?? resolveBaseUrl();
|
|
45
|
+
}
|
|
46
|
+
headers() {
|
|
47
|
+
const h = { "Content-Type": "application/json" };
|
|
48
|
+
if (this.apiKey) {
|
|
49
|
+
h["Authorization"] = `Bearer ${this.apiKey}`;
|
|
50
|
+
}
|
|
51
|
+
return h;
|
|
52
|
+
}
|
|
53
|
+
async request(endpoint, params) {
|
|
54
|
+
const url = new URL(`${this.baseUrl}${endpoint}`);
|
|
55
|
+
if (params) {
|
|
56
|
+
for (const [k, v] of Object.entries(params)) {
|
|
57
|
+
if (v !== void 0 && v !== "") url.searchParams.set(k, v);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const res = await fetch(url.toString(), { headers: this.headers() });
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const body = await res.json().catch(() => ({}));
|
|
63
|
+
const msg = body.error || `HTTP ${res.status}`;
|
|
64
|
+
throw new Error(msg);
|
|
65
|
+
}
|
|
66
|
+
return res.json();
|
|
67
|
+
}
|
|
68
|
+
/** Search skills by keyword. */
|
|
69
|
+
async search(query, options) {
|
|
70
|
+
return this.request("/skills", {
|
|
71
|
+
search: query,
|
|
72
|
+
category: options?.category ?? "",
|
|
73
|
+
pack: options?.pack ?? "",
|
|
74
|
+
sort: options?.sort ?? "",
|
|
75
|
+
limit: String(options?.limit ?? 20),
|
|
76
|
+
offset: String(options?.offset ?? 0),
|
|
77
|
+
include_content: options?.includeContent ? "true" : ""
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** List skills with optional filters and sorting. */
|
|
81
|
+
async list(options) {
|
|
82
|
+
return this.request("/skills", {
|
|
83
|
+
category: options?.category ?? "",
|
|
84
|
+
pack: options?.pack ?? "",
|
|
85
|
+
search: options?.search ?? "",
|
|
86
|
+
sort: options?.sort ?? "",
|
|
87
|
+
limit: String(options?.limit ?? 50),
|
|
88
|
+
offset: String(options?.offset ?? 0),
|
|
89
|
+
include_content: options?.includeContent ? "true" : ""
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Get a single skill by ID (e.g. "software-skills/code-review.md"). */
|
|
93
|
+
async get(id) {
|
|
94
|
+
const encoded = encodeURIComponent(id);
|
|
95
|
+
const res = await this.request(`/skills/${encoded}`, {
|
|
96
|
+
include_content: "true"
|
|
97
|
+
});
|
|
98
|
+
return "skill" in res ? res.skill : res;
|
|
99
|
+
}
|
|
100
|
+
/** Batch retrieve multiple skills by IDs (max 50). */
|
|
101
|
+
async batch(ids) {
|
|
102
|
+
return this.request("/skills", {
|
|
103
|
+
ids: ids.slice(0, 50).join(","),
|
|
104
|
+
include_content: "true"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Get search autocomplete suggestions. */
|
|
108
|
+
async suggest(query) {
|
|
109
|
+
return this.request("/skills/suggest", { q: query });
|
|
110
|
+
}
|
|
111
|
+
/** Validate that the configured API key works. */
|
|
112
|
+
async validate() {
|
|
113
|
+
try {
|
|
114
|
+
const url = `${this.baseUrl}/keys/usage`;
|
|
115
|
+
const res = await fetch(url, { headers: this.headers() });
|
|
116
|
+
return res.ok;
|
|
117
|
+
} catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/mcp.ts
|
|
124
|
+
var args = process.argv.slice(2);
|
|
125
|
+
var apiKeyIdx = args.indexOf("--api-key");
|
|
126
|
+
var apiKey = apiKeyIdx >= 0 ? args[apiKeyIdx + 1] : resolveApiKey();
|
|
127
|
+
var baseUrl = args.includes("--base-url") ? args[args.indexOf("--base-url") + 1] : void 0;
|
|
128
|
+
var client = new SkillDBClient({ apiKey, baseUrl });
|
|
129
|
+
var server = new McpServer({
|
|
130
|
+
name: "skilldb",
|
|
131
|
+
version: "0.4.0"
|
|
132
|
+
});
|
|
133
|
+
server.registerTool(
|
|
134
|
+
"skilldb_search",
|
|
135
|
+
{
|
|
136
|
+
title: "Search SkillDB Skills",
|
|
137
|
+
description: "Search the SkillDB library of 5,000+ AI agent skills by keyword. Returns skill metadata including name, description, pack, category, and line count. Use this to find relevant skills for any coding task.",
|
|
138
|
+
inputSchema: z.object({
|
|
139
|
+
query: z.string().describe("Search query (e.g. 'code review', 'react hooks', 'security')"),
|
|
140
|
+
category: z.string().optional().describe("Filter by category name"),
|
|
141
|
+
pack: z.string().optional().describe("Filter by pack name"),
|
|
142
|
+
limit: z.number().optional().default(10).describe("Max results (1-50)")
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
async ({ query, category, pack, limit }) => {
|
|
146
|
+
try {
|
|
147
|
+
const res = await client.search(query, {
|
|
148
|
+
category,
|
|
149
|
+
pack,
|
|
150
|
+
limit: Math.min(limit || 10, 50),
|
|
151
|
+
includeContent: !!apiKey
|
|
152
|
+
});
|
|
153
|
+
const text = res.skills.length === 0 ? `No skills found for "${query}".` : res.skills.map(
|
|
154
|
+
(s, i) => `${i + 1}. **${s.title}** (${s.pack})
|
|
155
|
+
${s.description}
|
|
156
|
+
ID: ${s.id} | ${s.lines} lines | Category: ${s.category}${s.content ? "\n Content: " + s.content.substring(0, 200) + "..." : ""}`
|
|
157
|
+
).join("\n\n") + `
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
Found ${res.pagination.total} total results. Showing ${res.skills.length}.`;
|
|
161
|
+
return { content: [{ type: "text", text }] };
|
|
162
|
+
} catch (e) {
|
|
163
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
server.registerTool(
|
|
168
|
+
"skilldb_get",
|
|
169
|
+
{
|
|
170
|
+
title: "Get Skill Content",
|
|
171
|
+
description: "Retrieve the full content of a specific SkillDB skill by its ID. Returns the complete markdown skill file with instructions, best practices, and patterns that can be used as agent context. Requires a Pro or Studio API key for full content.",
|
|
172
|
+
inputSchema: z.object({
|
|
173
|
+
id: z.string().describe("Skill ID (e.g. 'software-skills/code-review.md')")
|
|
174
|
+
})
|
|
175
|
+
},
|
|
176
|
+
async ({ id }) => {
|
|
177
|
+
try {
|
|
178
|
+
const skill = await client.get(id);
|
|
179
|
+
const text = skill.content ? `# ${skill.title}
|
|
180
|
+
**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}
|
|
181
|
+
|
|
182
|
+
${skill.content}` : `# ${skill.title}
|
|
183
|
+
**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}
|
|
184
|
+
|
|
185
|
+
${skill.description}
|
|
186
|
+
|
|
187
|
+
_Full content requires a Pro API key. Get one at https://skilldb.dev/api-access_`;
|
|
188
|
+
return { content: [{ type: "text", text }] };
|
|
189
|
+
} catch (e) {
|
|
190
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
server.registerTool(
|
|
195
|
+
"skilldb_list",
|
|
196
|
+
{
|
|
197
|
+
title: "List SkillDB Skills",
|
|
198
|
+
description: "Browse all available skills with optional filtering by category or pack. Use this to explore what's available in the SkillDB library.",
|
|
199
|
+
inputSchema: z.object({
|
|
200
|
+
category: z.string().optional().describe("Filter by category (e.g. 'Software Engineering', 'Security')"),
|
|
201
|
+
pack: z.string().optional().describe("Filter by pack (e.g. 'software-skills', 'react-patterns-skills')"),
|
|
202
|
+
sort: z.enum(["name", "-name", "lines", "-lines", "pack", "category"]).optional().describe("Sort order"),
|
|
203
|
+
limit: z.number().optional().default(20).describe("Max results (1-100)"),
|
|
204
|
+
offset: z.number().optional().default(0).describe("Pagination offset")
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
async ({ category, pack, sort, limit, offset }) => {
|
|
208
|
+
try {
|
|
209
|
+
const res = await client.list({ category, pack, sort, limit, offset });
|
|
210
|
+
const text = res.skills.length === 0 ? "No skills found with those filters." : `## SkillDB Skills${category ? ` \u2014 ${category}` : ""}${pack ? ` \u2014 ${pack}` : ""}
|
|
211
|
+
|
|
212
|
+
` + res.skills.map((s) => `- **${s.title}** (${s.id}) \u2014 ${s.lines} lines
|
|
213
|
+
${s.description}`).join("\n") + `
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
${res.pagination.total} total | Showing ${res.skills.length} (offset ${offset || 0})
|
|
217
|
+
Categories: ${res.meta.categories.join(", ")}`;
|
|
218
|
+
return { content: [{ type: "text", text }] };
|
|
219
|
+
} catch (e) {
|
|
220
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
server.registerTool(
|
|
225
|
+
"skilldb_suggest",
|
|
226
|
+
{
|
|
227
|
+
title: "Suggest Skills",
|
|
228
|
+
description: "Get autocomplete suggestions for skill names. Fast, lightweight \u2014 useful for quick lookups.",
|
|
229
|
+
inputSchema: z.object({
|
|
230
|
+
query: z.string().min(2).describe("Partial skill name (min 2 chars)")
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
async ({ query }) => {
|
|
234
|
+
try {
|
|
235
|
+
const res = await client.suggest(query);
|
|
236
|
+
const text = res.suggestions.length === 0 ? `No suggestions for "${query}".` : res.suggestions.map((s) => `- **${s.title}** (${s.pack} / ${s.category})
|
|
237
|
+
ID: ${s.id}`).join("\n");
|
|
238
|
+
return { content: [{ type: "text", text }] };
|
|
239
|
+
} catch (e) {
|
|
240
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
server.registerTool(
|
|
245
|
+
"skilldb_recommend",
|
|
246
|
+
{
|
|
247
|
+
title: "Recommend Skills for Project",
|
|
248
|
+
description: "Analyze the current project context and recommend relevant SkillDB skill packs. Provide information about the tech stack (languages, frameworks, tools) and get tailored skill pack recommendations.",
|
|
249
|
+
inputSchema: z.object({
|
|
250
|
+
technologies: z.array(z.string()).describe("Technologies in your project (e.g. ['react', 'typescript', 'docker', 'postgresql'])"),
|
|
251
|
+
role: z.string().optional().describe("Your role or focus area (e.g. 'frontend', 'backend', 'devops', 'security')")
|
|
252
|
+
})
|
|
253
|
+
},
|
|
254
|
+
async ({ technologies, role }) => {
|
|
255
|
+
const TECH_MAP = {
|
|
256
|
+
react: ["react-patterns-skills", "web-polish-skills"],
|
|
257
|
+
vue: ["vue-skills", "web-polish-skills"],
|
|
258
|
+
angular: ["angular-skills", "web-polish-skills"],
|
|
259
|
+
svelte: ["svelte-skills", "web-polish-skills"],
|
|
260
|
+
next: ["react-patterns-skills", "web-polish-skills"],
|
|
261
|
+
typescript: ["typescript-skills"],
|
|
262
|
+
javascript: ["software-skills", "typescript-skills"],
|
|
263
|
+
python: ["python-skills"],
|
|
264
|
+
rust: ["rust-skills"],
|
|
265
|
+
go: ["go-skills"],
|
|
266
|
+
java: ["java-skills"],
|
|
267
|
+
docker: ["devops-skills", "docker-skills"],
|
|
268
|
+
kubernetes: ["devops-skills", "infrastructure-skills"],
|
|
269
|
+
aws: ["cloud-skills", "infrastructure-skills"],
|
|
270
|
+
gcp: ["cloud-skills"],
|
|
271
|
+
azure: ["cloud-skills"],
|
|
272
|
+
postgresql: ["database-skills", "sql-skills"],
|
|
273
|
+
mongodb: ["database-skills"],
|
|
274
|
+
redis: ["database-skills"],
|
|
275
|
+
graphql: ["api-design-skills"],
|
|
276
|
+
rest: ["api-design-skills"],
|
|
277
|
+
express: ["nodejs-skills", "api-design-skills"],
|
|
278
|
+
fastify: ["nodejs-skills"],
|
|
279
|
+
nestjs: ["nodejs-skills", "api-design-skills"],
|
|
280
|
+
prisma: ["database-skills"],
|
|
281
|
+
"react-native": ["react-native-skills", "mobile-skills"],
|
|
282
|
+
flutter: ["mobile-skills"],
|
|
283
|
+
openai: ["ai-agent-skills", "prompt-engineering-skills"],
|
|
284
|
+
langchain: ["ai-agent-skills", "llm-skills"],
|
|
285
|
+
claude: ["ai-agent-skills", "prompt-engineering-skills"]
|
|
286
|
+
};
|
|
287
|
+
const ROLE_MAP = {
|
|
288
|
+
frontend: ["css-skills", "web-polish-skills", "typescript-skills"],
|
|
289
|
+
backend: ["software-skills", "api-design-skills", "database-skills"],
|
|
290
|
+
devops: ["devops-skills", "ci-cd-skills", "infrastructure-skills"],
|
|
291
|
+
security: ["security-skills", "appsec-skills", "auth-skills"],
|
|
292
|
+
fullstack: ["software-skills", "react-patterns-skills", "api-design-skills"],
|
|
293
|
+
data: ["data-engineering-skills", "sql-skills", "analytics-skills"],
|
|
294
|
+
mobile: ["mobile-skills", "react-native-skills"],
|
|
295
|
+
ai: ["ai-agent-skills", "prompt-engineering-skills", "llm-skills"]
|
|
296
|
+
};
|
|
297
|
+
const recommended = /* @__PURE__ */ new Set(["software-skills"]);
|
|
298
|
+
for (const tech of technologies) {
|
|
299
|
+
const key = tech.toLowerCase().replace(/[^a-z]/g, "");
|
|
300
|
+
for (const [pattern, packs] of Object.entries(TECH_MAP)) {
|
|
301
|
+
if (key.includes(pattern) || pattern.includes(key)) {
|
|
302
|
+
packs.forEach((p) => recommended.add(p));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (role) {
|
|
307
|
+
const rolePacks = ROLE_MAP[role.toLowerCase()] || [];
|
|
308
|
+
rolePacks.forEach((p) => recommended.add(p));
|
|
309
|
+
}
|
|
310
|
+
const packList = [...recommended];
|
|
311
|
+
const text = `## Recommended Skill Packs
|
|
312
|
+
|
|
313
|
+
Based on: ${technologies.join(", ")}${role ? ` (${role})` : ""}
|
|
314
|
+
|
|
315
|
+
` + packList.map((p) => `- \`${p}\` \u2014 install with \`skilldb add ${p}\``).join("\n") + `
|
|
316
|
+
|
|
317
|
+
**Install all:** \`${packList.map((p) => `skilldb add ${p}`).join(" && ")}\`
|
|
318
|
+
|
|
319
|
+
Use \`skilldb_search\` to explore individual skills within each pack.`;
|
|
320
|
+
return { content: [{ type: "text", text }] };
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
var transport = new StdioServerTransport();
|
|
324
|
+
await server.connect(transport);
|
|
325
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts","../src/config.ts","../src/client.ts"],"sourcesContent":["/**\n * SkillDB MCP Server\n *\n * Model Context Protocol server that exposes SkillDB skills\n * to any MCP-compatible AI coding tool (Claude Code, Cursor,\n * Windsurf, VS Code Copilot, and 30+ others).\n *\n * Installation:\n * claude mcp add skilldb -- npx skilldb-mcp\n * # or with API key:\n * claude mcp add skilldb -- npx skilldb-mcp --api-key sk_live_xxx\n *\n * Cursor (settings.json):\n * { \"mcpServers\": { \"skilldb\": { \"command\": \"npx\", \"args\": [\"skilldb-mcp\"] } } }\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { SkillDBClient } from \"./client.js\";\nimport { resolveApiKey } from \"./config.js\";\n\n// Parse CLI args for API key\nconst args = process.argv.slice(2);\nconst apiKeyIdx = args.indexOf(\"--api-key\");\nconst apiKey = apiKeyIdx >= 0 ? args[apiKeyIdx + 1] : resolveApiKey();\nconst baseUrl = args.includes(\"--base-url\")\n ? args[args.indexOf(\"--base-url\") + 1]\n : undefined;\n\nconst client = new SkillDBClient({ apiKey, baseUrl });\n\nconst server = new McpServer({\n name: \"skilldb\",\n version: \"0.4.0\",\n});\n\n// ─── Tool: Search Skills ───\nserver.registerTool(\n \"skilldb_search\",\n {\n title: \"Search SkillDB Skills\",\n description:\n \"Search the SkillDB library of 5,000+ AI agent skills by keyword. Returns skill metadata including name, description, pack, category, and line count. Use this to find relevant skills for any coding task.\",\n inputSchema: z.object({\n query: z.string().describe(\"Search query (e.g. 'code review', 'react hooks', 'security')\"),\n category: z.string().optional().describe(\"Filter by category name\"),\n pack: z.string().optional().describe(\"Filter by pack name\"),\n limit: z.number().optional().default(10).describe(\"Max results (1-50)\"),\n }),\n },\n async ({ query, category, pack, limit }) => {\n try {\n const res = await client.search(query, {\n category,\n pack,\n limit: Math.min(limit || 10, 50),\n includeContent: !!apiKey,\n });\n\n const text = res.skills.length === 0\n ? `No skills found for \"${query}\".`\n : res.skills\n .map(\n (s, i) =>\n `${i + 1}. **${s.title}** (${s.pack})\\n ${s.description}\\n ID: ${s.id} | ${s.lines} lines | Category: ${s.category}${s.content ? \"\\n Content: \" + s.content.substring(0, 200) + \"...\" : \"\"}`\n )\n .join(\"\\n\\n\") +\n `\\n\\n---\\nFound ${res.pagination.total} total results. Showing ${res.skills.length}.`;\n\n return { content: [{ type: \"text\", text }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\n }\n }\n);\n\n// ─── Tool: Get Skill Content ───\nserver.registerTool(\n \"skilldb_get\",\n {\n title: \"Get Skill Content\",\n description:\n \"Retrieve the full content of a specific SkillDB skill by its ID. Returns the complete markdown skill file with instructions, best practices, and patterns that can be used as agent context. Requires a Pro or Studio API key for full content.\",\n inputSchema: z.object({\n id: z.string().describe(\"Skill ID (e.g. 'software-skills/code-review.md')\"),\n }),\n },\n async ({ id }) => {\n try {\n const skill = await client.get(id);\n const text = skill.content\n ? `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.content}`\n : `# ${skill.title}\\n**Pack:** ${skill.pack} | **Category:** ${skill.category} | **Lines:** ${skill.lines}\\n\\n${skill.description}\\n\\n_Full content requires a Pro API key. Get one at https://skilldb.dev/api-access_`;\n\n return { content: [{ type: \"text\", text }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\n }\n }\n);\n\n// ─── Tool: List Skills ───\nserver.registerTool(\n \"skilldb_list\",\n {\n title: \"List SkillDB Skills\",\n description:\n \"Browse all available skills with optional filtering by category or pack. Use this to explore what's available in the SkillDB library.\",\n inputSchema: z.object({\n category: z.string().optional().describe(\"Filter by category (e.g. 'Software Engineering', 'Security')\"),\n pack: z.string().optional().describe(\"Filter by pack (e.g. 'software-skills', 'react-patterns-skills')\"),\n sort: z.enum([\"name\", \"-name\", \"lines\", \"-lines\", \"pack\", \"category\"]).optional().describe(\"Sort order\"),\n limit: z.number().optional().default(20).describe(\"Max results (1-100)\"),\n offset: z.number().optional().default(0).describe(\"Pagination offset\"),\n }),\n },\n async ({ category, pack, sort, limit, offset }) => {\n try {\n const res = await client.list({ category, pack, sort, limit, offset });\n\n const text = res.skills.length === 0\n ? \"No skills found with those filters.\"\n : `## SkillDB Skills${category ? ` — ${category}` : \"\"}${pack ? ` — ${pack}` : \"\"}\\n\\n` +\n res.skills\n .map((s) => `- **${s.title}** (${s.id}) — ${s.lines} lines\\n ${s.description}`)\n .join(\"\\n\") +\n `\\n\\n---\\n${res.pagination.total} total | Showing ${res.skills.length} (offset ${offset || 0})\\nCategories: ${res.meta.categories.join(\", \")}`;\n\n return { content: [{ type: \"text\", text }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\n }\n }\n);\n\n// ─── Tool: Suggest / Autocomplete ───\nserver.registerTool(\n \"skilldb_suggest\",\n {\n title: \"Suggest Skills\",\n description:\n \"Get autocomplete suggestions for skill names. Fast, lightweight — useful for quick lookups.\",\n inputSchema: z.object({\n query: z.string().min(2).describe(\"Partial skill name (min 2 chars)\"),\n }),\n },\n async ({ query }) => {\n try {\n const res = await client.suggest(query);\n const text = res.suggestions.length === 0\n ? `No suggestions for \"${query}\".`\n : res.suggestions\n .map((s) => `- **${s.title}** (${s.pack} / ${s.category})\\n ID: ${s.id}`)\n .join(\"\\n\");\n\n return { content: [{ type: \"text\", text }] };\n } catch (e) {\n return { content: [{ type: \"text\", text: `Error: ${(e as Error).message}` }], isError: true };\n }\n }\n);\n\n// ─── Tool: Recommend Skills for Project ───\nserver.registerTool(\n \"skilldb_recommend\",\n {\n title: \"Recommend Skills for Project\",\n description:\n \"Analyze the current project context and recommend relevant SkillDB skill packs. Provide information about the tech stack (languages, frameworks, tools) and get tailored skill pack recommendations.\",\n inputSchema: z.object({\n technologies: z.array(z.string()).describe(\"Technologies in your project (e.g. ['react', 'typescript', 'docker', 'postgresql'])\"),\n role: z.string().optional().describe(\"Your role or focus area (e.g. 'frontend', 'backend', 'devops', 'security')\"),\n }),\n },\n async ({ technologies, role }) => {\n // Map technologies to known skill packs\n const TECH_MAP: Record<string, string[]> = {\n react: [\"react-patterns-skills\", \"web-polish-skills\"],\n vue: [\"vue-skills\", \"web-polish-skills\"],\n angular: [\"angular-skills\", \"web-polish-skills\"],\n svelte: [\"svelte-skills\", \"web-polish-skills\"],\n next: [\"react-patterns-skills\", \"web-polish-skills\"],\n typescript: [\"typescript-skills\"],\n javascript: [\"software-skills\", \"typescript-skills\"],\n python: [\"python-skills\"],\n rust: [\"rust-skills\"],\n go: [\"go-skills\"],\n java: [\"java-skills\"],\n docker: [\"devops-skills\", \"docker-skills\"],\n kubernetes: [\"devops-skills\", \"infrastructure-skills\"],\n aws: [\"cloud-skills\", \"infrastructure-skills\"],\n gcp: [\"cloud-skills\"],\n azure: [\"cloud-skills\"],\n postgresql: [\"database-skills\", \"sql-skills\"],\n mongodb: [\"database-skills\"],\n redis: [\"database-skills\"],\n graphql: [\"api-design-skills\"],\n rest: [\"api-design-skills\"],\n express: [\"nodejs-skills\", \"api-design-skills\"],\n fastify: [\"nodejs-skills\"],\n nestjs: [\"nodejs-skills\", \"api-design-skills\"],\n prisma: [\"database-skills\"],\n \"react-native\": [\"react-native-skills\", \"mobile-skills\"],\n flutter: [\"mobile-skills\"],\n openai: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\n langchain: [\"ai-agent-skills\", \"llm-skills\"],\n claude: [\"ai-agent-skills\", \"prompt-engineering-skills\"],\n };\n\n const ROLE_MAP: Record<string, string[]> = {\n frontend: [\"css-skills\", \"web-polish-skills\", \"typescript-skills\"],\n backend: [\"software-skills\", \"api-design-skills\", \"database-skills\"],\n devops: [\"devops-skills\", \"ci-cd-skills\", \"infrastructure-skills\"],\n security: [\"security-skills\", \"appsec-skills\", \"auth-skills\"],\n fullstack: [\"software-skills\", \"react-patterns-skills\", \"api-design-skills\"],\n data: [\"data-engineering-skills\", \"sql-skills\", \"analytics-skills\"],\n mobile: [\"mobile-skills\", \"react-native-skills\"],\n ai: [\"ai-agent-skills\", \"prompt-engineering-skills\", \"llm-skills\"],\n };\n\n const recommended = new Set<string>([\"software-skills\"]); // always recommend baseline\n for (const tech of technologies) {\n const key = tech.toLowerCase().replace(/[^a-z]/g, \"\");\n for (const [pattern, packs] of Object.entries(TECH_MAP)) {\n if (key.includes(pattern) || pattern.includes(key)) {\n packs.forEach((p) => recommended.add(p));\n }\n }\n }\n if (role) {\n const rolePacks = ROLE_MAP[role.toLowerCase()] || [];\n rolePacks.forEach((p) => recommended.add(p));\n }\n\n const packList = [...recommended];\n const text =\n `## Recommended Skill Packs\\n\\n` +\n `Based on: ${technologies.join(\", \")}${role ? ` (${role})` : \"\"}\\n\\n` +\n packList.map((p) => `- \\`${p}\\` — install with \\`skilldb add ${p}\\``).join(\"\\n\") +\n `\\n\\n**Install all:** \\`${packList.map((p) => `skilldb add ${p}`).join(\" && \")}\\`\\n` +\n `\\nUse \\`skilldb_search\\` to explore individual skills within each pack.`;\n\n return { content: [{ type: \"text\", text }] };\n }\n);\n\n// ─── Start server ───\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst RC_FILE = '.skilldbrc';\n\nexport const DEFAULT_BASE_URL = 'https://skilldb.dev/api/v1';\n\ninterface RcConfig {\n apiKey?: string;\n}\n\nfunction readJson(filePath: string): RcConfig | null {\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve API key from (in priority order):\n * 1. SKILLDB_API_KEY env var\n * 2. .skilldbrc in project root (cwd)\n * 3. ~/.skilldbrc in home dir\n */\nexport function resolveApiKey(): string | undefined {\n if (process.env.SKILLDB_API_KEY) {\n return process.env.SKILLDB_API_KEY;\n }\n\n const projectRc = path.join(process.cwd(), RC_FILE);\n const projectConfig = readJson(projectRc);\n if (projectConfig?.apiKey) return projectConfig.apiKey;\n\n const homeRc = path.join(os.homedir(), RC_FILE);\n const homeConfig = readJson(homeRc);\n if (homeConfig?.apiKey) return homeConfig.apiKey;\n\n return undefined;\n}\n\n/**\n * Resolve base URL from env or default.\n */\nexport function resolveBaseUrl(): string {\n return process.env.SKILLDB_API_URL || DEFAULT_BASE_URL;\n}\n\n/**\n * Save API key to ~/.skilldbrc (user-wide) or project .skilldbrc.\n */\nexport function saveApiKey(apiKey: string, global = true): string {\n const target = global\n ? path.join(os.homedir(), RC_FILE)\n : path.join(process.cwd(), RC_FILE);\n\n const existing = readJson(target) || {};\n fs.writeFileSync(target, JSON.stringify({ ...existing, apiKey }, null, 2) + '\\n', 'utf-8');\n return target;\n}\n","import type { ClientConfig, Skill, SkillsResponse, SearchOptions } from './types.js';\nimport { resolveApiKey, resolveBaseUrl } from './config.js';\n\nexport class SkillDBClient {\n private apiKey?: string;\n private baseUrl: string;\n\n constructor(config?: ClientConfig) {\n this.apiKey = config?.apiKey ?? resolveApiKey();\n this.baseUrl = config?.baseUrl ?? resolveBaseUrl();\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.apiKey) {\n h['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return h;\n }\n\n private async request<T>(endpoint: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== '') url.searchParams.set(k, v);\n }\n }\n\n const res = await fetch(url.toString(), { headers: this.headers() });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n const msg = (body as Record<string, string>).error || `HTTP ${res.status}`;\n throw new Error(msg);\n }\n\n return res.json() as Promise<T>;\n }\n\n /** Search skills by keyword. */\n async search(query: string, options?: Omit<SearchOptions, 'search'>): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n search: query,\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 20),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** List skills with optional filters and sorting. */\n async list(options?: SearchOptions): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n category: options?.category ?? '',\n pack: options?.pack ?? '',\n search: options?.search ?? '',\n sort: options?.sort ?? '',\n limit: String(options?.limit ?? 50),\n offset: String(options?.offset ?? 0),\n include_content: options?.includeContent ? 'true' : '',\n });\n }\n\n /** Get a single skill by ID (e.g. \"software-skills/code-review.md\"). */\n async get(id: string): Promise<Skill> {\n const encoded = encodeURIComponent(id);\n const res = await this.request<Skill | { skill: Skill }>(`/skills/${encoded}`, {\n include_content: 'true',\n });\n // Handle both direct and wrapped responses\n return 'skill' in res ? res.skill : res;\n }\n\n /** Batch retrieve multiple skills by IDs (max 50). */\n async batch(ids: string[]): Promise<SkillsResponse> {\n return this.request<SkillsResponse>('/skills', {\n ids: ids.slice(0, 50).join(','),\n include_content: 'true',\n });\n }\n\n /** Get search autocomplete suggestions. */\n async suggest(query: string): Promise<{ suggestions: Array<{ title: string; pack: string; category: string; id: string }> }> {\n return this.request('/skills/suggest', { q: query });\n }\n\n /** Validate that the configured API key works. */\n async validate(): Promise<boolean> {\n try {\n const url = `${this.baseUrl}/keys/usage`;\n const res = await fetch(url, { headers: this.headers() });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilldb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "SDK and CLI for SkillDB — discover, install, and manage AI agent skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"bin": {
|
|
17
|
-
"skilldb": "./dist/cli.js"
|
|
17
|
+
"skilldb": "./dist/cli.js",
|
|
18
|
+
"skilldb-mcp": "./dist/mcp.js"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"dist",
|
|
@@ -34,12 +35,17 @@
|
|
|
34
35
|
"claude",
|
|
35
36
|
"cursor",
|
|
36
37
|
"codex",
|
|
37
|
-
"agent"
|
|
38
|
+
"agent",
|
|
39
|
+
"mcp",
|
|
40
|
+
"model-context-protocol",
|
|
41
|
+
"mcp-server"
|
|
38
42
|
],
|
|
39
43
|
"license": "MIT",
|
|
40
44
|
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
41
46
|
"commander": "^13.1.0",
|
|
42
|
-
"picocolors": "^1.1.1"
|
|
47
|
+
"picocolors": "^1.1.1",
|
|
48
|
+
"zod": "^3.25.0"
|
|
43
49
|
},
|
|
44
50
|
"devDependencies": {
|
|
45
51
|
"@types/node": "^25.5.0",
|