@olaservo/skill-jack-mcp 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Skill Jack MCP
2
2
 
3
- An MCP server that jacks [Agent Skills](https://agentskills.dev) directly into your LLM's brain.
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](https://modelcontextprotocol.io/clients) that supports server instructions. This allows the LLM to see available skills in its system prompt, enabling automatic skill discovery and activation. Without this support, the model will still be able to call these tools, but you might need to provide more explicit instructions on what skills are available and the intended activation patterns.
4
6
 
5
7
  ## Features
6
8
 
@@ -33,17 +35,22 @@ npm run build
33
35
 
34
36
  ## Usage
35
37
 
36
- Configure a skills directory containing your Agent Skills:
38
+ Configure one or more skills directories containing your Agent Skills:
37
39
 
38
40
  ```bash
39
- # Pass skills directory as argument
41
+ # Single directory
40
42
  skill-jack-mcp /path/to/skills
41
43
 
42
- # Or use environment variable
44
+ # Multiple directories (separate args or comma-separated)
45
+ skill-jack-mcp /path/to/skills /path/to/more/skills
46
+ skill-jack-mcp /path/to/skills,/path/to/more/skills
47
+
48
+ # Using environment variable (comma-separated for multiple)
43
49
  SKILLS_DIR=/path/to/skills skill-jack-mcp
50
+ SKILLS_DIR=/path/to/skills,/path/to/more/skills skill-jack-mcp
44
51
  ```
45
52
 
46
- The server scans the directory and its `.claude/skills/` and `skills/` subdirectories for skills.
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.
47
54
 
48
55
  **Windows note**: Use forward slashes in paths when using with MCP Inspector:
49
56
  ```bash
@@ -52,16 +59,16 @@ skill-jack-mcp "C:/Users/you/skills"
52
59
 
53
60
  ## How It Works
54
61
 
55
- The server implements the [Agent Skills](https://agentskills.dev) progressive disclosure pattern:
62
+ The server implements the [Agent Skills](https://agentskills.io) progressive disclosure pattern:
56
63
 
57
- 1. **At startup**: Discovers skills from configured directory
64
+ 1. **At startup**: Discovers skills from configured directories
58
65
  2. **On connection**: Server instructions (with skill metadata) are sent in the initialize response
59
66
  3. **On tool call**: Agent calls `skill` tool to load full SKILL.md content
60
67
 
61
68
  ```
62
69
  ┌─────────────────────────────────────────────────────────┐
63
70
  │ Server starts │
64
- │ • Discovers skills from configured directory
71
+ │ • Discovers skills from configured directories
65
72
  │ • Generates instructions with skill metadata │
66
73
  │ ↓ │
67
74
  │ MCP Client connects │
@@ -96,7 +103,7 @@ Read files within a skill's directory (`scripts/`, `references/`, `assets/`, `sn
96
103
 
97
104
  This follows the Agent Skills spec's progressive disclosure pattern - resources are loaded only when needed.
98
105
 
99
- **Input:**
106
+ **Read a single file:**
100
107
  ```json
101
108
  {
102
109
  "skill": "mcp-server-ts",
@@ -104,7 +111,14 @@ This follows the Agent Skills spec's progressive disclosure pattern - resources
104
111
  }
105
112
  ```
106
113
 
107
- **Output:** File content.
114
+ **Read all files in a directory:**
115
+ ```json
116
+ {
117
+ "skill": "algorithmic-art",
118
+ "path": "templates"
119
+ }
120
+ ```
121
+ Returns all files in the directory as multiple content items.
108
122
 
109
123
  **List available files** (pass empty path):
110
124
  ```json
@@ -194,7 +208,7 @@ These are loaded into the model's system prompt by [clients](https://modelcontex
194
208
 
195
209
  ## Skill Discovery
196
210
 
197
- Skills are discovered at startup from the configured directory. The server checks:
211
+ Skills are discovered at startup from the configured directories. For each directory, the server checks:
198
212
  - The directory itself for skill subdirectories
199
213
  - `.claude/skills/` subdirectory
200
214
  - `skills/` subdirectory
@@ -213,6 +227,6 @@ npx @modelcontextprotocol/inspector@latest node dist/index.js /path/to/skills
213
227
 
214
228
  ## Related
215
229
 
216
- - [Agent Skills Specification](https://agentskills.dev)
230
+ - [Agent Skills Specification](https://agentskills.io)
217
231
  - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
218
232
  - [Example MCP Clients](https://modelcontextprotocol.io/clients)
package/dist/index.d.ts CHANGED
@@ -6,7 +6,8 @@
6
6
  * Provides global skills with tools for progressive disclosure.
7
7
  *
8
8
  * Usage:
9
- * skill-jack-mcp /path/to/skills # Skills directory (required)
10
- * SKILLS_DIR=/path/to/skills skill-jack-mcp
9
+ * skill-jack-mcp /path/to/skills [/path2 ...] # One or more directories
10
+ * SKILLS_DIR=/path/to/skills skill-jack-mcp # Single directory via env
11
+ * SKILLS_DIR=/path1,/path2 skill-jack-mcp # Multiple (comma-separated)
11
12
  */
12
13
  export {};
package/dist/index.js CHANGED
@@ -6,8 +6,9 @@
6
6
  * Provides global skills with tools for progressive disclosure.
7
7
  *
8
8
  * Usage:
9
- * skill-jack-mcp /path/to/skills # Skills directory (required)
10
- * SKILLS_DIR=/path/to/skills skill-jack-mcp
9
+ * skill-jack-mcp /path/to/skills [/path2 ...] # One or more directories
10
+ * SKILLS_DIR=/path/to/skills skill-jack-mcp # Single directory via env
11
+ * SKILLS_DIR=/path1,/path2 skill-jack-mcp # Multiple (comma-separated)
11
12
  */
12
13
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
14
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -22,20 +23,40 @@ import { createSubscriptionManager, registerSubscriptionHandlers, } from "./subs
22
23
  */
23
24
  const SKILL_SUBDIRS = [".claude/skills", "skills"];
24
25
  /**
25
- * Get the skills directory from command line args or environment.
26
+ * Separator for multiple paths in SKILLS_DIR environment variable.
27
+ * Comma works cross-platform (not valid in file paths on any OS).
26
28
  */
27
- function getSkillsDir() {
28
- // Check command line argument first
29
+ const PATH_LIST_SEPARATOR = ",";
30
+ /**
31
+ * Get the skills directories from command line args and/or environment.
32
+ * Returns deduplicated, resolved paths.
33
+ */
34
+ function getSkillsDirs() {
35
+ const dirs = [];
36
+ // Collect all non-flag command-line arguments (comma-separated supported)
29
37
  const args = process.argv.slice(2);
30
- if (args.length > 0 && args[0] && !args[0].startsWith("-")) {
31
- return path.resolve(args[0]);
38
+ for (const arg of args) {
39
+ if (!arg.startsWith("-")) {
40
+ const paths = arg
41
+ .split(PATH_LIST_SEPARATOR)
42
+ .map((p) => p.trim())
43
+ .filter((p) => p.length > 0)
44
+ .map((p) => path.resolve(p));
45
+ dirs.push(...paths);
46
+ }
32
47
  }
33
- // Fall back to environment variable
48
+ // Also check environment variable (comma-separated supported)
34
49
  const envDir = process.env.SKILLS_DIR;
35
50
  if (envDir) {
36
- return path.resolve(envDir);
51
+ const envPaths = envDir
52
+ .split(PATH_LIST_SEPARATOR)
53
+ .map((p) => p.trim())
54
+ .filter((p) => p.length > 0)
55
+ .map((p) => path.resolve(p));
56
+ dirs.push(...envPaths);
37
57
  }
38
- return null;
58
+ // Deduplicate by resolved path
59
+ return [...new Set(dirs)];
39
60
  }
40
61
  /**
41
62
  * Shared state for skill management.
@@ -46,20 +67,37 @@ const skillState = {
46
67
  instructions: "",
47
68
  };
48
69
  /**
49
- * Discover skills from configured directory.
50
- * Checks both the directory itself and standard subdirectories.
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.
51
73
  */
52
- function discoverSkillsFromDir(skillsDir) {
74
+ function discoverSkillsFromDirs(skillsDirs) {
53
75
  const allSkills = [];
54
- // Check if the directory itself contains skills
55
- const directSkills = discoverSkills(skillsDir);
56
- allSkills.push(...directSkills);
57
- // Also check standard subdirectories
58
- for (const subdir of SKILL_SUBDIRS) {
59
- const subPath = path.join(skillsDir, subdir);
60
- if (fs.existsSync(subPath)) {
61
- const subdirSkills = discoverSkills(subPath);
62
- allSkills.push(...subdirSkills);
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);
63
101
  }
64
102
  }
65
103
  return allSkills;
@@ -69,16 +107,17 @@ function discoverSkillsFromDir(skillsDir) {
69
107
  */
70
108
  const subscriptionManager = createSubscriptionManager();
71
109
  async function main() {
72
- const skillsDir = getSkillsDir();
73
- if (!skillsDir) {
110
+ const skillsDirs = getSkillsDirs();
111
+ if (skillsDirs.length === 0) {
74
112
  console.error("No skills directory configured.");
75
- console.error("Usage: skill-jack-mcp /path/to/skills");
113
+ console.error("Usage: skill-jack-mcp /path/to/skills [/path/to/more/skills ...]");
76
114
  console.error(" or: SKILLS_DIR=/path/to/skills skill-jack-mcp");
115
+ console.error(" or: SKILLS_DIR=/path1,/path2 skill-jack-mcp");
77
116
  process.exit(1);
78
117
  }
79
- console.error(`Skills directory: ${skillsDir}`);
118
+ console.error(`Skills directories: ${skillsDirs.join(", ")}`);
80
119
  // Discover skills at startup
81
- const skills = discoverSkillsFromDir(skillsDir);
120
+ const skills = discoverSkillsFromDirs(skillsDirs);
82
121
  skillState.skillMap = createSkillMap(skills);
83
122
  skillState.instructions = generateInstructions(skills);
84
123
  console.error(`Discovered ${skills.length} skill(s)`);
@@ -87,7 +87,7 @@ const SkillResourceSchema = z.object({
87
87
  skill: z.string().describe("Skill name"),
88
88
  path: z
89
89
  .string()
90
- .describe("Relative path (e.g., 'snippets/tool.ts'). Empty string lists all files."),
90
+ .describe("Relative path to file or directory. Examples: 'snippets/tool.ts' (single file), 'templates' (all files in directory), '' (list available files)."),
91
91
  });
92
92
  // Security constants (exported for reuse in skill-resources.ts)
93
93
  export const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB max file size
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olaservo/skill-jack-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server that discovers and serves Agent Skills. I know kung fu.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",