@skilljack/mcp 0.3.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ola Hungerford
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # Skilljack MCP
2
+
3
+ An MCP server that jacks [Agent Skills](https://agentskills.io) directly into your LLM's brain.
4
+
5
+ > **Recommended:** For best results, use an MCP client that supports `tools/listChanged` notifications (e.g., Claude Code). This enables dynamic skill discovery - when skills are added or modified, the client automatically refreshes its understanding of available skills.
6
+
7
+ ## Features
8
+
9
+ - **Dynamic Skill Discovery** - Watches skill directories and automatically refreshes when skills change
10
+ - **Tool List Changed Notifications** - Sends `tools/listChanged` so clients can refresh available skills
11
+ - **Skill Tool** - Load full skill content on demand (progressive disclosure)
12
+ - **MCP Resources** - Access skills via `skill://` URIs with batch collection support
13
+ - **Resource Subscriptions** - Real-time file watching with `notifications/resources/updated`
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @skilljack/mcp
19
+ ```
20
+
21
+ Or run directly with npx:
22
+
23
+ ```bash
24
+ npx @skilljack/mcp /path/to/skills
25
+ ```
26
+
27
+ ### From Source
28
+
29
+ ```bash
30
+ git clone https://github.com/olaservo/skilljack-mcp.git
31
+ cd skilljack-mcp
32
+ npm install
33
+ npm run build
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ Configure one or more skills directories containing your Agent Skills:
39
+
40
+ ```bash
41
+ # Single directory
42
+ skilljack-mcp /path/to/skills
43
+
44
+ # Multiple directories (separate args or comma-separated)
45
+ skilljack-mcp /path/to/skills /path/to/more/skills
46
+ skilljack-mcp /path/to/skills,/path/to/more/skills
47
+
48
+ # Using environment variable (comma-separated for multiple)
49
+ SKILLS_DIR=/path/to/skills skilljack-mcp
50
+ SKILLS_DIR=/path/to/skills,/path/to/more/skills skilljack-mcp
51
+ ```
52
+
53
+ Each directory is scanned along with its `.claude/skills/` and `skills/` subdirectories for skills. Duplicate skill names are handled by keeping the first occurrence.
54
+
55
+ **Windows note**: Use forward slashes in paths when using with MCP Inspector:
56
+ ```bash
57
+ skilljack-mcp "C:/Users/you/skills"
58
+ ```
59
+
60
+ ## How It Works
61
+
62
+ The server implements the [Agent Skills](https://agentskills.io) progressive disclosure pattern with dynamic updates:
63
+
64
+ 1. **At startup**: Discovers skills from configured directories and starts file watchers
65
+ 2. **On connection**: Skill tool description includes available skills metadata
66
+ 3. **On file change**: Re-discovers skills, updates tool description, sends `tools/listChanged`
67
+ 4. **On tool call**: Agent calls `skill` tool to load full SKILL.md content
68
+ 5. **As needed**: Agent calls `skill-resource` to load additional files
69
+
70
+ ```
71
+ ┌─────────────────────────────────────────────────────────┐
72
+ │ Server starts │
73
+ │ • Discovers skills from configured directories │
74
+ │ • Starts watching for SKILL.md changes │
75
+ │ ↓ │
76
+ │ MCP Client connects │
77
+ │ • Skill tool description includes available skills │
78
+ │ ↓ │
79
+ │ LLM sees skill metadata in tool description │
80
+ │ ↓ │
81
+ │ SKILL.md added/modified/removed │
82
+ │ • Server re-discovers skills │
83
+ │ • Updates skill tool description │
84
+ │ • Sends tools/listChanged notification │
85
+ │ • Client refreshes tool definitions │
86
+ │ ↓ │
87
+ │ LLM calls "skill" tool with skill name │
88
+ │ ↓ │
89
+ │ Server returns full SKILL.md content │
90
+ │ ↓ │
91
+ │ LLM calls "skill-resource" for additional files │
92
+ │ • Scripts, snippets, references, assets, etc. │
93
+ └─────────────────────────────────────────────────────────┘
94
+ ```
95
+
96
+ ## Tools vs Resources
97
+
98
+ This server exposes skills via both **tools** and **resources**:
99
+
100
+ - **Tools** (`skill`, `skill-resource`) - For your agent to use autonomously. The LLM sees available skills in the tool description and calls them as needed.
101
+ - **Resources** (`skill://` URIs) - For manual selection in apps that support it (e.g., Claude Desktop's resource picker). Useful when you want to explicitly attach a skill to the conversation.
102
+
103
+ Most users will rely on tools for automatic skill activation. Resources provide an alternative for manual control.
104
+
105
+ ## Tools
106
+
107
+ ### `skill`
108
+
109
+ Load and activate an Agent Skill by name. Returns the full SKILL.md content.
110
+
111
+ **Input:**
112
+ ```json
113
+ {
114
+ "name": "skill-name"
115
+ }
116
+ ```
117
+
118
+ **Output:** Full SKILL.md content including frontmatter and instructions.
119
+
120
+ ### `skill-resource`
121
+
122
+ Read files within a skill's directory (`scripts/`, `references/`, `assets/`, `snippets/`, etc.).
123
+
124
+ This follows the Agent Skills spec's progressive disclosure pattern - resources are loaded only when needed.
125
+
126
+ **Read a single file:**
127
+ ```json
128
+ {
129
+ "skill": "mcp-server-ts",
130
+ "path": "snippets/tools/echo.ts"
131
+ }
132
+ ```
133
+
134
+ **Read all files in a directory:**
135
+ ```json
136
+ {
137
+ "skill": "algorithmic-art",
138
+ "path": "templates"
139
+ }
140
+ ```
141
+ Returns all files in the directory as multiple content items.
142
+
143
+ **List available files** (pass empty path):
144
+ ```json
145
+ {
146
+ "skill": "mcp-server-ts",
147
+ "path": ""
148
+ }
149
+ ```
150
+
151
+ **Security:** Path traversal is prevented - only files within the skill directory can be accessed.
152
+
153
+ ## Resources
154
+
155
+ Skills are also accessible via MCP [Resources](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resources) using `skill://` URIs.
156
+
157
+ ### URI Patterns
158
+
159
+ | URI | Returns |
160
+ |-----|---------|
161
+ | `skill://{name}` | Single skill's SKILL.md content |
162
+ | `skill://{name}/` | All files in skill directory (collection) |
163
+ | `skill://{name}/{path}` | Specific file within skill |
164
+
165
+ ### Resource Subscriptions
166
+
167
+ Clients can subscribe to resources for real-time updates when files change.
168
+
169
+ **Capability:** `resources: { subscribe: true, listChanged: true }`
170
+
171
+ **Subscribe to a resource:**
172
+ ```
173
+ → resources/subscribe { uri: "skill://mcp-server-ts" }
174
+ ← {} (success)
175
+ ```
176
+
177
+ **Receive notifications when files change:**
178
+ ```
179
+ ← notifications/resources/updated { uri: "skill://mcp-server-ts" }
180
+ ```
181
+
182
+ **Unsubscribe:**
183
+ ```
184
+ → resources/unsubscribe { uri: "skill://mcp-server-ts" }
185
+ ← {} (success)
186
+ ```
187
+
188
+ **How it works:**
189
+ 1. Client subscribes to a `skill://` URI
190
+ 2. Server resolves URI to file path(s) and starts watching with chokidar
191
+ 3. When files change, server debounces (100ms) and sends notification
192
+ 4. Client can re-read the resource to get updated content
193
+
194
+ ## Security
195
+
196
+ **Skills are treated as trusted content.** This server reads and serves skill files directly to clients without sanitization. Only configure skills directories containing content you trust.
197
+
198
+ Protections in place:
199
+ - Path traversal prevention (symlink-aware)
200
+ - File size limits (1MB default, configurable via `MAX_FILE_SIZE_MB` env var)
201
+ - Directory depth limits
202
+ - Skill content is confined to configured directories
203
+
204
+ Not protected against:
205
+ - Malicious content within trusted skill directories
206
+ - Prompt injection via skill instructions (skills can influence LLM behavior by design)
207
+
208
+ ## Dynamic Skill Discovery
209
+
210
+ The server watches skill directories for changes. When SKILL.md files are added, modified, or removed:
211
+
212
+ 1. Skills are re-discovered from all configured directories
213
+ 2. The `skill` tool's description is updated with current skill names and metadata
214
+ 3. `tools/listChanged` notification is sent to connected clients
215
+ 4. Clients that support this notification will refresh tool definitions
216
+
217
+ ## Skill Metadata Format
218
+
219
+ The `skill` tool description includes metadata for all available skills in XML format:
220
+
221
+ ```markdown
222
+ # Skills
223
+
224
+ When a user's task matches a skill description below: 1) activate it, 2) follow its instructions completely.
225
+
226
+ <available_skills>
227
+ <skill>
228
+ <name>mcp-server-ts</name>
229
+ <description>Build TypeScript MCP servers with composable code snippets...</description>
230
+ <location>C:/path/to/mcp-server-ts/SKILL.md</location>
231
+ </skill>
232
+ </available_skills>
233
+ ```
234
+
235
+ This metadata is dynamically updated when skills change - clients supporting `tools/listChanged` will automatically refresh.
236
+
237
+ ## Skill Discovery
238
+
239
+ Skills are discovered at startup from the configured directories. For each directory, the server checks:
240
+ - The directory itself for skill subdirectories
241
+ - `.claude/skills/` subdirectory
242
+ - `skills/` subdirectory
243
+
244
+ Each skill subdirectory must contain a `SKILL.md` file with YAML frontmatter including `name` and `description` fields.
245
+
246
+ ## Testing
247
+
248
+ ```bash
249
+ # Build first
250
+ npm run build
251
+
252
+ # Test with MCP Inspector
253
+ npx @modelcontextprotocol/inspector@latest node dist/index.js /path/to/skills
254
+ ```
255
+
256
+ ## Related
257
+
258
+ - [Agent Skills Specification](https://agentskills.io)
259
+ - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
260
+ - [Example MCP Clients](https://modelcontextprotocol.io/clients)
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Skilljack MCP - "I know kung fu."
4
+ *
5
+ * MCP server that jacks Agent Skills directly into your LLM's brain.
6
+ * Provides global skills with tools for progressive disclosure.
7
+ *
8
+ * Usage:
9
+ * skilljack-mcp /path/to/skills [/path2 ...] # One or more directories
10
+ * SKILLS_DIR=/path/to/skills skilljack-mcp # Single directory via env
11
+ * SKILLS_DIR=/path1,/path2 skilljack-mcp # Multiple (comma-separated)
12
+ */
13
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Skilljack MCP - "I know kung fu."
4
+ *
5
+ * MCP server that jacks Agent Skills directly into your LLM's brain.
6
+ * Provides global skills with tools for progressive disclosure.
7
+ *
8
+ * Usage:
9
+ * skilljack-mcp /path/to/skills [/path2 ...] # One or more directories
10
+ * SKILLS_DIR=/path/to/skills skilljack-mcp # Single directory via env
11
+ * SKILLS_DIR=/path1,/path2 skilljack-mcp # Multiple (comma-separated)
12
+ */
13
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import chokidar from "chokidar";
16
+ import * as fs from "node:fs";
17
+ import * as path from "node:path";
18
+ import { discoverSkills, createSkillMap } from "./skill-discovery.js";
19
+ import { registerSkillTool, getToolDescription } from "./skill-tool.js";
20
+ import { registerSkillResources } from "./skill-resources.js";
21
+ import { createSubscriptionManager, registerSubscriptionHandlers, refreshSubscriptions, } from "./subscriptions.js";
22
+ /**
23
+ * Subdirectories to check for skills within the configured directory.
24
+ */
25
+ const SKILL_SUBDIRS = [".claude/skills", "skills"];
26
+ /**
27
+ * Separator for multiple paths in SKILLS_DIR environment variable.
28
+ * Comma works cross-platform (not valid in file paths on any OS).
29
+ */
30
+ const PATH_LIST_SEPARATOR = ",";
31
+ /**
32
+ * Get the skills directories from command line args and/or environment.
33
+ * Returns deduplicated, resolved paths.
34
+ */
35
+ function getSkillsDirs() {
36
+ const dirs = [];
37
+ // Collect all non-flag command-line arguments (comma-separated supported)
38
+ const args = process.argv.slice(2);
39
+ for (const arg of args) {
40
+ if (!arg.startsWith("-")) {
41
+ const paths = arg
42
+ .split(PATH_LIST_SEPARATOR)
43
+ .map((p) => p.trim())
44
+ .filter((p) => p.length > 0)
45
+ .map((p) => path.resolve(p));
46
+ dirs.push(...paths);
47
+ }
48
+ }
49
+ // Also check environment variable (comma-separated supported)
50
+ const envDir = process.env.SKILLS_DIR;
51
+ if (envDir) {
52
+ const envPaths = envDir
53
+ .split(PATH_LIST_SEPARATOR)
54
+ .map((p) => p.trim())
55
+ .filter((p) => p.length > 0)
56
+ .map((p) => path.resolve(p));
57
+ dirs.push(...envPaths);
58
+ }
59
+ // Deduplicate by resolved path
60
+ return [...new Set(dirs)];
61
+ }
62
+ /**
63
+ * Shared state for skill management.
64
+ * Tools and resources reference this state.
65
+ */
66
+ const skillState = {
67
+ skillMap: new Map(),
68
+ };
69
+ /**
70
+ * Discover skills from multiple configured directories.
71
+ * Each directory is checked along with its standard subdirectories.
72
+ * Handles duplicate skill names by keeping first occurrence.
73
+ */
74
+ function discoverSkillsFromDirs(skillsDirs) {
75
+ const allSkills = [];
76
+ const seenNames = new Map(); // name -> source directory
77
+ for (const skillsDir of skillsDirs) {
78
+ if (!fs.existsSync(skillsDir)) {
79
+ console.error(`Warning: Skills directory not found: ${skillsDir}`);
80
+ continue;
81
+ }
82
+ console.error(`Scanning skills directory: ${skillsDir}`);
83
+ // Check if the directory itself contains skills
84
+ const dirSkills = discoverSkills(skillsDir);
85
+ // Also check standard subdirectories
86
+ for (const subdir of SKILL_SUBDIRS) {
87
+ const subPath = path.join(skillsDir, subdir);
88
+ if (fs.existsSync(subPath)) {
89
+ dirSkills.push(...discoverSkills(subPath));
90
+ }
91
+ }
92
+ // Add skills, checking for duplicates
93
+ for (const skill of dirSkills) {
94
+ if (seenNames.has(skill.name)) {
95
+ console.error(`Warning: Duplicate skill "${skill.name}" found in ${path.dirname(skill.path)} ` +
96
+ `(already loaded from ${seenNames.get(skill.name)})`);
97
+ continue; // Skip duplicate
98
+ }
99
+ seenNames.set(skill.name, path.dirname(skill.path));
100
+ allSkills.push(skill);
101
+ }
102
+ }
103
+ return allSkills;
104
+ }
105
+ /**
106
+ * Debounce delay for skill directory changes (ms).
107
+ * Multiple rapid changes are coalesced into one refresh.
108
+ */
109
+ const SKILL_REFRESH_DEBOUNCE_MS = 500;
110
+ /**
111
+ * Refresh skills and notify clients of changes.
112
+ * Called when skill files change on disk.
113
+ *
114
+ * @param skillsDirs - The configured skill directories
115
+ * @param server - The MCP server instance
116
+ * @param skillTool - The registered skill tool to update
117
+ * @param subscriptionManager - For refreshing resource subscriptions
118
+ */
119
+ function refreshSkills(skillsDirs, server, skillTool, subscriptionManager) {
120
+ console.error("Refreshing skills...");
121
+ // Re-discover all skills
122
+ const skills = discoverSkillsFromDirs(skillsDirs);
123
+ const oldCount = skillState.skillMap.size;
124
+ // Update shared state
125
+ skillState.skillMap = createSkillMap(skills);
126
+ console.error(`Skills refreshed: ${oldCount} -> ${skills.length} skill(s)`);
127
+ // Update the skill tool description with new instructions
128
+ skillTool.update({
129
+ description: getToolDescription(skillState),
130
+ });
131
+ // Refresh resource subscriptions to match new skill state
132
+ refreshSubscriptions(subscriptionManager, skillState, (uri) => {
133
+ server.server.notification({
134
+ method: "notifications/resources/updated",
135
+ params: { uri },
136
+ });
137
+ });
138
+ // Notify clients that tools have changed
139
+ // This prompts clients to call tools/list again
140
+ server.sendToolListChanged();
141
+ // Also notify that resources have changed
142
+ server.sendResourceListChanged();
143
+ }
144
+ /**
145
+ * Set up file watchers on skill directories to detect changes.
146
+ * Watches for SKILL.md additions, modifications, and deletions.
147
+ *
148
+ * @param skillsDirs - The configured skill directories
149
+ * @param server - The MCP server instance
150
+ * @param skillTool - The registered skill tool to update
151
+ * @param subscriptionManager - For refreshing subscriptions
152
+ */
153
+ function watchSkillDirectories(skillsDirs, server, skillTool, subscriptionManager) {
154
+ let refreshTimeout = null;
155
+ const debouncedRefresh = () => {
156
+ if (refreshTimeout) {
157
+ clearTimeout(refreshTimeout);
158
+ }
159
+ refreshTimeout = setTimeout(() => {
160
+ refreshTimeout = null;
161
+ refreshSkills(skillsDirs, server, skillTool, subscriptionManager);
162
+ }, SKILL_REFRESH_DEBOUNCE_MS);
163
+ };
164
+ // Build list of paths to watch
165
+ const watchPaths = [];
166
+ for (const dir of skillsDirs) {
167
+ if (fs.existsSync(dir)) {
168
+ watchPaths.push(dir);
169
+ // Also watch standard subdirectories
170
+ for (const subdir of SKILL_SUBDIRS) {
171
+ const subPath = path.join(dir, subdir);
172
+ if (fs.existsSync(subPath)) {
173
+ watchPaths.push(subPath);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ if (watchPaths.length === 0) {
179
+ console.error("No skill directories to watch");
180
+ return;
181
+ }
182
+ console.error(`Watching for skill changes in: ${watchPaths.join(", ")}`);
183
+ const watcher = chokidar.watch(watchPaths, {
184
+ persistent: true,
185
+ ignoreInitial: true,
186
+ depth: 2, // Watch skill subdirectories but not too deep
187
+ ignored: ["**/node_modules/**", "**/.git/**"],
188
+ awaitWriteFinish: {
189
+ stabilityThreshold: 200,
190
+ pollInterval: 50,
191
+ },
192
+ });
193
+ // Watch for SKILL.md changes specifically
194
+ watcher.on("add", (filePath) => {
195
+ if (path.basename(filePath).toLowerCase() === "skill.md") {
196
+ console.error(`Skill added: ${filePath}`);
197
+ debouncedRefresh();
198
+ }
199
+ });
200
+ watcher.on("change", (filePath) => {
201
+ if (path.basename(filePath).toLowerCase() === "skill.md") {
202
+ console.error(`Skill modified: ${filePath}`);
203
+ debouncedRefresh();
204
+ }
205
+ });
206
+ watcher.on("unlink", (filePath) => {
207
+ if (path.basename(filePath).toLowerCase() === "skill.md") {
208
+ console.error(`Skill removed: ${filePath}`);
209
+ debouncedRefresh();
210
+ }
211
+ });
212
+ // Also watch for directory additions (new skill folders)
213
+ watcher.on("addDir", (dirPath) => {
214
+ // Check if this might be a new skill directory
215
+ const skillMdPath = path.join(dirPath, "SKILL.md");
216
+ const skillMdPathLower = path.join(dirPath, "skill.md");
217
+ if (fs.existsSync(skillMdPath) || fs.existsSync(skillMdPathLower)) {
218
+ console.error(`Skill directory added: ${dirPath}`);
219
+ debouncedRefresh();
220
+ }
221
+ });
222
+ watcher.on("unlinkDir", (dirPath) => {
223
+ // A skill directory was removed
224
+ console.error(`Directory removed: ${dirPath}`);
225
+ debouncedRefresh();
226
+ });
227
+ }
228
+ /**
229
+ * Subscription manager for resource file watching.
230
+ */
231
+ const subscriptionManager = createSubscriptionManager();
232
+ async function main() {
233
+ const skillsDirs = getSkillsDirs();
234
+ if (skillsDirs.length === 0) {
235
+ console.error("No skills directory configured.");
236
+ console.error("Usage: skilljack-mcp /path/to/skills [/path/to/more/skills ...]");
237
+ console.error(" or: SKILLS_DIR=/path/to/skills skilljack-mcp");
238
+ console.error(" or: SKILLS_DIR=/path1,/path2 skilljack-mcp");
239
+ process.exit(1);
240
+ }
241
+ console.error(`Skills directories: ${skillsDirs.join(", ")}`);
242
+ // Discover skills at startup
243
+ const skills = discoverSkillsFromDirs(skillsDirs);
244
+ skillState.skillMap = createSkillMap(skills);
245
+ console.error(`Discovered ${skills.length} skill(s)`);
246
+ // Create the MCP server
247
+ const server = new McpServer({
248
+ name: "skilljack-mcp",
249
+ version: "1.0.0",
250
+ }, {
251
+ capabilities: {
252
+ tools: { listChanged: true },
253
+ resources: { subscribe: true, listChanged: true },
254
+ },
255
+ });
256
+ // Register tools and resources
257
+ const skillTool = registerSkillTool(server, skillState);
258
+ registerSkillResources(server, skillState);
259
+ // Register subscription handlers for resource file watching
260
+ registerSubscriptionHandlers(server, skillState, subscriptionManager);
261
+ // Set up file watchers for skill directory changes
262
+ watchSkillDirectories(skillsDirs, server, skillTool, subscriptionManager);
263
+ // Connect via stdio transport
264
+ const transport = new StdioServerTransport();
265
+ await server.connect(transport);
266
+ console.error("Skilljack ready. I know kung fu.");
267
+ }
268
+ main().catch((error) => {
269
+ console.error("Fatal error:", error);
270
+ process.exit(1);
271
+ });
@@ -0,0 +1,49 @@
1
+ /**
2
+ * MCP Roots handler for dynamic skill discovery.
3
+ *
4
+ * Requests roots from the client, scans for skills in each root,
5
+ * and handles root change notifications.
6
+ *
7
+ * Pattern adapted from:
8
+ * - .claude/skills/mcp-server-ts/snippets/server/index.ts (oninitialized, syncRoots)
9
+ * - .claude/skills/mcp-client-ts/snippets/handlers/roots.ts (URI conversion)
10
+ */
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { SkillMetadata } from "./skill-discovery.js";
13
+ /**
14
+ * Skill discovery locations within each root.
15
+ */
16
+ export declare const SKILL_SUBDIRS: string[];
17
+ /**
18
+ * Discover skills from all roots provided by the client.
19
+ *
20
+ * Scans each root for skill directories (.claude/skills/, skills/)
21
+ * and handles naming conflicts by prefixing with root name.
22
+ *
23
+ * @param roots - Array of Root objects from client's roots/list response
24
+ * @returns Object containing discovered skills
25
+ */
26
+ export declare function discoverSkillsFromRoots(roots: Array<{
27
+ uri: string;
28
+ name?: string;
29
+ }>): {
30
+ skills: SkillMetadata[];
31
+ rootSources: Map<string, string>;
32
+ };
33
+ /**
34
+ * Callback type for when skills are updated.
35
+ */
36
+ export type SkillsChangedCallback = (skillMap: Map<string, SkillMetadata>, instructions: string) => void;
37
+ /**
38
+ * Sync skills from roots or configured skills directory.
39
+ *
40
+ * Pattern from mcp-server-ts snippets/server/index.ts:
41
+ * - Check client capabilities
42
+ * - Request roots if supported
43
+ * - Use skills directory if not
44
+ *
45
+ * @param server - The McpServer instance
46
+ * @param skillsDir - Optional skills directory if client doesn't support roots
47
+ * @param onSkillsChanged - Callback when skills are updated
48
+ */
49
+ export declare function syncSkills(server: McpServer, skillsDir: string | null, onSkillsChanged: SkillsChangedCallback): Promise<void>;