openskills-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/bin/cli.d.ts +10 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +133 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/modes/install.d.ts +32 -0
- package/dist/src/modes/install.d.ts.map +1 -0
- package/dist/src/modes/install.js +215 -0
- package/dist/src/modes/install.js.map +1 -0
- package/dist/src/modes/mcp-proxy.d.ts +16 -0
- package/dist/src/modes/mcp-proxy.d.ts.map +1 -0
- package/dist/src/modes/mcp-proxy.js +117 -0
- package/dist/src/modes/mcp-proxy.js.map +1 -0
- package/dist/src/services/downloader.d.ts +32 -0
- package/dist/src/services/downloader.d.ts.map +1 -0
- package/dist/src/services/downloader.js +125 -0
- package/dist/src/services/downloader.js.map +1 -0
- package/dist/src/services/extractor.d.ts +38 -0
- package/dist/src/services/extractor.d.ts.map +1 -0
- package/dist/src/services/extractor.js +82 -0
- package/dist/src/services/extractor.js.map +1 -0
- package/dist/src/services/symlink.d.ts +64 -0
- package/dist/src/services/symlink.d.ts.map +1 -0
- package/dist/src/services/symlink.js +153 -0
- package/dist/src/services/symlink.js.map +1 -0
- package/dist/src/types/index.d.ts +75 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/agent-dirs.d.ts +49 -0
- package/dist/src/utils/agent-dirs.d.ts.map +1 -0
- package/dist/src/utils/agent-dirs.js +116 -0
- package/dist/src/utils/agent-dirs.js.map +1 -0
- package/dist/src/utils/constants.d.ts +16 -0
- package/dist/src/utils/constants.d.ts.map +1 -0
- package/dist/src/utils/constants.js +21 -0
- package/dist/src/utils/constants.js.map +1 -0
- package/dist/src/utils/errors.d.ts +72 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +108 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/logger.d.ts +41 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +85 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/mode-detector.d.ts +74 -0
- package/dist/src/utils/mode-detector.d.ts.map +1 -0
- package/dist/src/utils/mode-detector.js +156 -0
- package/dist/src/utils/mode-detector.js.map +1 -0
- package/dist/src/utils/prompts.d.ts +22 -0
- package/dist/src/utils/prompts.d.ts.map +1 -0
- package/dist/src/utils/prompts.js +69 -0
- package/dist/src/utils/prompts.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenSkills
|
|
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,99 @@
|
|
|
1
|
+
# openskills-mcp
|
|
2
|
+
|
|
3
|
+
Discover and install AI agent skills via the [Model Context Protocol](https://modelcontextprotocol.io/).
|
|
4
|
+
|
|
5
|
+
OpenSkills indexes skills from trusted GitHub repositories and makes them searchable through MCP tools. This package connects your AI agent to the hosted OpenSkills server.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx openskills-mcp --api-key osk_your_key_here
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Get your API key at [openskills.dev/dashboard](https://openskills.dev/dashboard).
|
|
14
|
+
|
|
15
|
+
## MCP Configuration
|
|
16
|
+
|
|
17
|
+
### Claude Desktop / Claude Code
|
|
18
|
+
|
|
19
|
+
Add to your `claude_desktop_config.json` or `.claude/settings.json`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"openskills": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": [
|
|
27
|
+
"-y",
|
|
28
|
+
"openskills-mcp",
|
|
29
|
+
"--api-key",
|
|
30
|
+
"osk_your_key_here"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Cursor
|
|
38
|
+
|
|
39
|
+
Add to your MCP settings:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"openskills": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": [
|
|
47
|
+
"-y",
|
|
48
|
+
"openskills-mcp",
|
|
49
|
+
"--api-key",
|
|
50
|
+
"osk_your_key_here"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Available Tools
|
|
58
|
+
|
|
59
|
+
### search_skills
|
|
60
|
+
|
|
61
|
+
Search for skills using natural language.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
search_skills("process PDF files", max_results=5, verified_only=true)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Returns skill metadata: name, description, tags, stars, verified status, relevance score.
|
|
68
|
+
|
|
69
|
+
### get_skill
|
|
70
|
+
|
|
71
|
+
Get installation command for a skill.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
get_skill(skill="pdf-processing", agent="claude-code", global_install=false)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Returns a simple installation command:
|
|
78
|
+
```bash
|
|
79
|
+
npx openskills pdf-processing -a claude-code
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The openskills CLI handles downloading, extracting, and symlinking automatically.
|
|
83
|
+
|
|
84
|
+
## How It Works
|
|
85
|
+
|
|
86
|
+
This package runs a local MCP server (stdio transport) that proxies requests to the hosted OpenSkills server (HTTP transport) with your API key for authentication.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Your AI Agent <--stdio--> openskills-mcp <--HTTP--> OpenSkills Server
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Requirements
|
|
93
|
+
|
|
94
|
+
- Node.js >= 18.0.0
|
|
95
|
+
- An OpenSkills API key
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG"}
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenSkills CLI - Main entry point
|
|
4
|
+
*
|
|
5
|
+
* Dual-mode CLI:
|
|
6
|
+
* 1. Installation mode: npx openskills <skill> [options]
|
|
7
|
+
* 2. MCP proxy mode: npx openskills --api-key <key>
|
|
8
|
+
*/
|
|
9
|
+
import { detectMode, parseInstallOptions, parseApiKey } from '../src/utils/mode-detector.js';
|
|
10
|
+
import { install } from '../src/modes/install.js';
|
|
11
|
+
import { userError, ExitCode } from '../src/utils/errors.js';
|
|
12
|
+
import { readFileSync } from 'fs';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { dirname, join } from 'path';
|
|
15
|
+
// Get package.json for version
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../..', 'package.json'), 'utf-8'));
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
const mode = detectMode(args);
|
|
21
|
+
/**
|
|
22
|
+
* Show help message.
|
|
23
|
+
*/
|
|
24
|
+
function showHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
OpenSkills CLI v${packageJson.version}
|
|
27
|
+
|
|
28
|
+
Install AI agent skills locally or discover via MCP protocol.
|
|
29
|
+
|
|
30
|
+
INSTALLATION MODE:
|
|
31
|
+
npx openskills <skill> [options]
|
|
32
|
+
|
|
33
|
+
Positional:
|
|
34
|
+
<skill> Skill identifier (e.g., pdf-processing)
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
-a, --agent <name> Agent name (e.g., claude-code, cursor)
|
|
38
|
+
-g, --global Install globally to ~/.agents/skills/
|
|
39
|
+
-p, --project Install to project ./agents/skills/ (default if no flag)
|
|
40
|
+
-y Install for multiple agents (claude-code, codex, cursor, gemini-cli, qwen-code)
|
|
41
|
+
--force Overwrite existing installation
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
npx openskills pdf-processing -a claude-code -g
|
|
45
|
+
npx openskills pdf-processing -a claude-code -p
|
|
46
|
+
npx openskills pdf-processing -y
|
|
47
|
+
npx openskills pdf-processing
|
|
48
|
+
(Interactive: prompts for agent and scope)
|
|
49
|
+
|
|
50
|
+
Legacy syntax (still supported):
|
|
51
|
+
npx openskills -s pdf-processing -a claude-code
|
|
52
|
+
|
|
53
|
+
MCP PROXY MODE:
|
|
54
|
+
npx openskills --api-key <key>
|
|
55
|
+
|
|
56
|
+
Required:
|
|
57
|
+
--api-key <key> OpenSkills API key (format: osk_...)
|
|
58
|
+
|
|
59
|
+
Environment:
|
|
60
|
+
OPENSKILLS_BACKEND_URL Backend server URL (default: https://openskills-mcp.hf.space)
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
npx openskills --api-key osk_abc123def456
|
|
64
|
+
|
|
65
|
+
OTHER:
|
|
66
|
+
-h, --help Show this help message
|
|
67
|
+
-v, --version Show version number
|
|
68
|
+
|
|
69
|
+
SUPPORTED AGENTS:
|
|
70
|
+
claude-code, openclaw, cursor, windsurf, codex, cody, aider, continue,
|
|
71
|
+
gemini-cli, qwen-code, and 15+ more
|
|
72
|
+
|
|
73
|
+
For more information, visit: https://github.com/nadeemsangrasi/openskills-mcp
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Show version.
|
|
78
|
+
*/
|
|
79
|
+
function showVersion() {
|
|
80
|
+
console.log(`openskills v${packageJson.version}`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Main entry point.
|
|
84
|
+
*/
|
|
85
|
+
async function main() {
|
|
86
|
+
try {
|
|
87
|
+
switch (mode) {
|
|
88
|
+
case 'help':
|
|
89
|
+
showHelp();
|
|
90
|
+
process.exit(ExitCode.SUCCESS);
|
|
91
|
+
break;
|
|
92
|
+
case 'version':
|
|
93
|
+
showVersion();
|
|
94
|
+
process.exit(ExitCode.SUCCESS);
|
|
95
|
+
break;
|
|
96
|
+
case 'install': {
|
|
97
|
+
const options = parseInstallOptions(args);
|
|
98
|
+
// Validate that at least skill name is provided
|
|
99
|
+
if (!options.skill) {
|
|
100
|
+
userError('Skill name is required', [
|
|
101
|
+
'Usage: npx openskills <skill> [options]',
|
|
102
|
+
'Example: npx openskills pdf-processing -a claude-code',
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
// Pass options to install (may have null agent for interactive mode)
|
|
106
|
+
await install(options);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'mcp': {
|
|
110
|
+
const apiKey = parseApiKey(args);
|
|
111
|
+
if (!apiKey) {
|
|
112
|
+
userError('Missing required flag: --api-key', ['Usage: npx openskills --api-key <your-key>']);
|
|
113
|
+
}
|
|
114
|
+
if (!apiKey.startsWith('osk_')) {
|
|
115
|
+
userError("Invalid API key format. Keys start with 'osk_'", ['Get a valid key at: https://openskills.dev/dashboard']);
|
|
116
|
+
}
|
|
117
|
+
// Import and start MCP proxy
|
|
118
|
+
const { startProxy } = await import('../src/modes/mcp-proxy.js');
|
|
119
|
+
await startProxy(apiKey);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
default:
|
|
123
|
+
showHelp();
|
|
124
|
+
process.exit(ExitCode.SUCCESS);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error(`✗ Error: ${error.message}`);
|
|
129
|
+
process.exit(ExitCode.SYSTEM_ERROR);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
main();
|
|
133
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installation mode handler.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the complete skill installation flow:
|
|
5
|
+
* 1. Validate arguments or prompt for missing options
|
|
6
|
+
* 2. Check existing installation
|
|
7
|
+
* 3. Download skill zip from server (server does sparse checkout)
|
|
8
|
+
* 4. Extract to .agents/skills/
|
|
9
|
+
* 5. Create symlink(s) to agent directory(ies)
|
|
10
|
+
* 6. Log installation events
|
|
11
|
+
*
|
|
12
|
+
* Server handles sparse checkout - no git required on client.
|
|
13
|
+
*
|
|
14
|
+
* Supports:
|
|
15
|
+
* - Single-agent installation: npx openskills pdf-processing -a claude-code
|
|
16
|
+
* - Multi-agent installation: npx openskills pdf-processing -y
|
|
17
|
+
* - Interactive prompts: npx openskills pdf-processing
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Execute installation flow.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Installation options (may have null agent for interactive mode)
|
|
23
|
+
*/
|
|
24
|
+
export declare function install(options: {
|
|
25
|
+
skill: string | null;
|
|
26
|
+
agent: string | null;
|
|
27
|
+
global: boolean;
|
|
28
|
+
force: boolean;
|
|
29
|
+
multiAgent: boolean;
|
|
30
|
+
scopeExplicit: boolean;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/modes/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgFH;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE;IACrC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+ChB"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installation mode handler.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the complete skill installation flow:
|
|
5
|
+
* 1. Validate arguments or prompt for missing options
|
|
6
|
+
* 2. Check existing installation
|
|
7
|
+
* 3. Download skill zip from server (server does sparse checkout)
|
|
8
|
+
* 4. Extract to .agents/skills/
|
|
9
|
+
* 5. Create symlink(s) to agent directory(ies)
|
|
10
|
+
* 6. Log installation events
|
|
11
|
+
*
|
|
12
|
+
* Server handles sparse checkout - no git required on client.
|
|
13
|
+
*
|
|
14
|
+
* Supports:
|
|
15
|
+
* - Single-agent installation: npx openskills pdf-processing -a claude-code
|
|
16
|
+
* - Multi-agent installation: npx openskills pdf-processing -y
|
|
17
|
+
* - Interactive prompts: npx openskills pdf-processing
|
|
18
|
+
*/
|
|
19
|
+
import * as fs from 'fs';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as os from 'os';
|
|
22
|
+
import { fetchSkillZip } from '../services/downloader.js';
|
|
23
|
+
import { extractSkill, validateSkillMd, cleanup } from '../services/extractor.js';
|
|
24
|
+
import { createSymlink, resolveHome } from '../services/symlink.js';
|
|
25
|
+
import { getAgentDir } from '../utils/agent-dirs.js';
|
|
26
|
+
import { createInstallLogger } from '../utils/logger.js';
|
|
27
|
+
import { promptAgent, promptScope, showMultiAgentInfo } from '../utils/prompts.js';
|
|
28
|
+
import { DEFAULT_AGENTS } from '../utils/constants.js';
|
|
29
|
+
import { validateSkillSlug, validateAgentName, networkError, systemError, success, step, info, ExitCode, } from '../utils/errors.js';
|
|
30
|
+
/**
|
|
31
|
+
* Get installation base directory.
|
|
32
|
+
*
|
|
33
|
+
* @param isGlobal - Whether global or project installation
|
|
34
|
+
* @returns Base directory path
|
|
35
|
+
*/
|
|
36
|
+
function getBaseDir(isGlobal) {
|
|
37
|
+
if (isGlobal) {
|
|
38
|
+
return path.join(os.homedir(), '.agents', 'skills');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return path.join(process.cwd(), '.agents', 'skills');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if skill already exists in .agents/skills/.
|
|
46
|
+
*
|
|
47
|
+
* @param skillSlug - Skill identifier
|
|
48
|
+
* @param isGlobal - Whether global or project installation
|
|
49
|
+
* @returns True if skill exists
|
|
50
|
+
*/
|
|
51
|
+
function skillExists(skillSlug, isGlobal) {
|
|
52
|
+
const baseDir = getBaseDir(isGlobal);
|
|
53
|
+
const skillDir = path.join(baseDir, skillSlug);
|
|
54
|
+
return fs.existsSync(skillDir) && fs.existsSync(path.join(skillDir, 'SKILL.md'));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create symlink for a single agent.
|
|
58
|
+
*
|
|
59
|
+
* @param skillDir - Source skill directory
|
|
60
|
+
* @param agent - Agent name
|
|
61
|
+
* @param isGlobal - Whether global installation
|
|
62
|
+
* @param log - Logger function
|
|
63
|
+
*/
|
|
64
|
+
async function createAgentSymlink(skillDir, agent, isGlobal, log) {
|
|
65
|
+
const agentDir = getAgentDir(agent, isGlobal);
|
|
66
|
+
const agentPath = isGlobal ? resolveHome(`~/${agentDir}`) : agentDir;
|
|
67
|
+
const symlinkPath = path.join(agentPath, path.basename(skillDir));
|
|
68
|
+
step(`Creating symlink for ${agent} at ${symlinkPath}`);
|
|
69
|
+
try {
|
|
70
|
+
await createSymlink(skillDir, symlinkPath);
|
|
71
|
+
log('symlink_created', { agent, symlink_path: symlinkPath });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
log('error', { agent, error: error.message });
|
|
75
|
+
systemError(`Symlink creation failed for ${agent}: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Execute installation flow.
|
|
80
|
+
*
|
|
81
|
+
* @param options - Installation options (may have null agent for interactive mode)
|
|
82
|
+
*/
|
|
83
|
+
export async function install(options) {
|
|
84
|
+
const startTime = Date.now();
|
|
85
|
+
// Validate skill name
|
|
86
|
+
if (!options.skill) {
|
|
87
|
+
systemError('Skill name is required');
|
|
88
|
+
}
|
|
89
|
+
validateSkillSlug(options.skill);
|
|
90
|
+
// Handle multi-agent installation (-y flag)
|
|
91
|
+
if (options.multiAgent) {
|
|
92
|
+
showMultiAgentInfo(options.skill);
|
|
93
|
+
const installOpts = {
|
|
94
|
+
skill: options.skill,
|
|
95
|
+
agent: DEFAULT_AGENTS,
|
|
96
|
+
global: true, // -y always installs globally
|
|
97
|
+
force: options.force,
|
|
98
|
+
multiAgent: true,
|
|
99
|
+
};
|
|
100
|
+
await executeInstall(installOpts, startTime);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Interactive mode: prompt for missing options
|
|
104
|
+
let agent = options.agent;
|
|
105
|
+
let isGlobal = options.global;
|
|
106
|
+
if (!agent) {
|
|
107
|
+
agent = await promptAgent();
|
|
108
|
+
}
|
|
109
|
+
// If scope was not explicitly set (no -g or -p flag), prompt for scope
|
|
110
|
+
if (!options.scopeExplicit) {
|
|
111
|
+
isGlobal = await promptScope();
|
|
112
|
+
}
|
|
113
|
+
const installOpts = {
|
|
114
|
+
skill: options.skill,
|
|
115
|
+
agent: agent,
|
|
116
|
+
global: isGlobal,
|
|
117
|
+
force: options.force,
|
|
118
|
+
multiAgent: false,
|
|
119
|
+
};
|
|
120
|
+
await executeInstall(installOpts, startTime);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Execute the actual installation process.
|
|
124
|
+
*
|
|
125
|
+
* @param options - Validated installation options
|
|
126
|
+
* @param startTime - Installation start timestamp
|
|
127
|
+
*/
|
|
128
|
+
async function executeInstall(options, startTime) {
|
|
129
|
+
const agents = Array.isArray(options.agent) ? options.agent : [options.agent];
|
|
130
|
+
const installScope = options.global ? 'global' : 'project';
|
|
131
|
+
// Use first agent for logging (or 'multi' for multi-agent)
|
|
132
|
+
const logAgent = options.multiAgent ? 'multi-agent' : agents[0];
|
|
133
|
+
const log = createInstallLogger(options.skill, logAgent, installScope);
|
|
134
|
+
try {
|
|
135
|
+
log('start');
|
|
136
|
+
// Determine installation paths
|
|
137
|
+
const baseDir = getBaseDir(options.global);
|
|
138
|
+
const skillDir = path.join(baseDir, options.skill);
|
|
139
|
+
// Check existing installation
|
|
140
|
+
const exists = skillExists(options.skill, options.global);
|
|
141
|
+
if (exists && !options.force) {
|
|
142
|
+
// Skill exists, reuse files and create symlinks
|
|
143
|
+
info(`Skill ${options.skill} already exists, reusing files`);
|
|
144
|
+
for (const agent of agents) {
|
|
145
|
+
const validAgent = validateAgentName(agent);
|
|
146
|
+
await createAgentSymlink(skillDir, validAgent, options.global, log);
|
|
147
|
+
}
|
|
148
|
+
const duration = Date.now() - startTime;
|
|
149
|
+
success('Installation complete', duration);
|
|
150
|
+
log('complete', { duration_ms: duration });
|
|
151
|
+
process.exit(ExitCode.SUCCESS);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (exists && options.force) {
|
|
155
|
+
// Force reinstall: delete existing
|
|
156
|
+
step('Removing existing installation...');
|
|
157
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
158
|
+
}
|
|
159
|
+
// Download skill zip from server (server does sparse checkout)
|
|
160
|
+
step(`Downloading ${options.skill}...`);
|
|
161
|
+
log('download_begin');
|
|
162
|
+
// Create temp directory for zip
|
|
163
|
+
const tmpDir = path.join(os.tmpdir(), 'openskills');
|
|
164
|
+
if (!fs.existsSync(tmpDir)) {
|
|
165
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
166
|
+
}
|
|
167
|
+
let zipPath;
|
|
168
|
+
try {
|
|
169
|
+
zipPath = await fetchSkillZip(options.skill, tmpDir);
|
|
170
|
+
log('download_complete', { duration_ms: Date.now() - startTime });
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
log('error', { error: error.message });
|
|
174
|
+
networkError(error.message);
|
|
175
|
+
}
|
|
176
|
+
// Extract to .agents/skills/
|
|
177
|
+
step(`Extracting to ${skillDir}`);
|
|
178
|
+
log('extract_begin');
|
|
179
|
+
try {
|
|
180
|
+
await extractSkill(zipPath, skillDir);
|
|
181
|
+
log('extract_complete', { target_path: skillDir, duration_ms: Date.now() - startTime });
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
cleanup(zipPath, skillDir);
|
|
185
|
+
log('error', { error: error.message });
|
|
186
|
+
systemError(`Extraction failed: ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
// Validate SKILL.md
|
|
189
|
+
try {
|
|
190
|
+
validateSkillMd(skillDir);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
cleanup(zipPath, skillDir);
|
|
194
|
+
log('error', { error: error.message });
|
|
195
|
+
systemError(`Skill validation failed: ${error.message}`);
|
|
196
|
+
}
|
|
197
|
+
// Clean up zip file
|
|
198
|
+
cleanup(zipPath);
|
|
199
|
+
// Create symlinks for all agents
|
|
200
|
+
for (const agent of agents) {
|
|
201
|
+
const validAgent = validateAgentName(agent);
|
|
202
|
+
await createAgentSymlink(skillDir, validAgent, options.global, log);
|
|
203
|
+
}
|
|
204
|
+
// Success
|
|
205
|
+
const duration = Date.now() - startTime;
|
|
206
|
+
success('Installation complete', duration);
|
|
207
|
+
log('complete', { duration_ms: duration, agents: agents.length });
|
|
208
|
+
process.exit(ExitCode.SUCCESS);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
log('error', { error: error.message });
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/modes/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP proxy mode handler.
|
|
3
|
+
*
|
|
4
|
+
* Creates a local stdio MCP server that proxies all tool calls
|
|
5
|
+
* to the hosted OpenSkills MCP server with X-API-Key auth.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* AI Agent <--stdio--> this proxy <--HTTP+X-API-Key--> hosted server
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Start the stdio-to-HTTP MCP proxy.
|
|
12
|
+
*
|
|
13
|
+
* @param apiKey - OpenSkills API key (osk_xxx)
|
|
14
|
+
*/
|
|
15
|
+
export declare function startProxy(apiKey: string): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=mcp-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-proxy.d.ts","sourceRoot":"","sources":["../../../src/modes/mcp-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+G9D"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP proxy mode handler.
|
|
3
|
+
*
|
|
4
|
+
* Creates a local stdio MCP server that proxies all tool calls
|
|
5
|
+
* to the hosted OpenSkills MCP server with X-API-Key auth.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* AI Agent <--stdio--> this proxy <--HTTP+X-API-Key--> hosted server
|
|
9
|
+
*/
|
|
10
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
11
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
12
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
|
+
const BACKEND_URL = 'https://sangrasi-openskills-mcp.hf.space';
|
|
15
|
+
/**
|
|
16
|
+
* Start the stdio-to-HTTP MCP proxy.
|
|
17
|
+
*
|
|
18
|
+
* @param apiKey - OpenSkills API key (osk_xxx)
|
|
19
|
+
*/
|
|
20
|
+
export async function startProxy(apiKey) {
|
|
21
|
+
const remoteUrl = `${BACKEND_URL}/mcp`;
|
|
22
|
+
// 1. Connect to remote OpenSkills MCP server as a client
|
|
23
|
+
const remoteClient = new Client({
|
|
24
|
+
name: 'openskills-proxy',
|
|
25
|
+
version: '1.0.0',
|
|
26
|
+
});
|
|
27
|
+
const remoteTransport = new StreamableHTTPClientTransport(new URL(remoteUrl), {
|
|
28
|
+
requestInit: {
|
|
29
|
+
headers: {
|
|
30
|
+
'X-API-Key': apiKey,
|
|
31
|
+
'Accept': 'application/json, text/event-stream',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
await remoteClient.connect(remoteTransport);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const msg = err.message || String(err);
|
|
40
|
+
if (msg.includes('401') || msg.includes('Unauthorized')) {
|
|
41
|
+
console.error('Error: Invalid API key. Check your key at https://openskills.dev/dashboard');
|
|
42
|
+
process.exit(2);
|
|
43
|
+
}
|
|
44
|
+
console.error(`Error: Failed to connect to OpenSkills server: ${msg}`);
|
|
45
|
+
process.exit(3);
|
|
46
|
+
}
|
|
47
|
+
// 2. Discover remote tools
|
|
48
|
+
let tools;
|
|
49
|
+
try {
|
|
50
|
+
const result = await remoteClient.listTools();
|
|
51
|
+
tools = result.tools;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error(`Error: Failed to list remote tools: ${err.message}`);
|
|
55
|
+
process.exit(3);
|
|
56
|
+
}
|
|
57
|
+
// Log to stderr (stdout is reserved for MCP stdio protocol)
|
|
58
|
+
console.error(`OpenSkills: Connected to ${BACKEND_URL}. ${tools.length} tools available: ${tools.map((t) => t.name).join(', ')}`);
|
|
59
|
+
// 3. Create local stdio MCP server
|
|
60
|
+
const localServer = new McpServer({
|
|
61
|
+
name: 'openskills',
|
|
62
|
+
version: '1.0.0',
|
|
63
|
+
});
|
|
64
|
+
// 4. Register each remote tool as a local proxy tool
|
|
65
|
+
for (const tool of tools) {
|
|
66
|
+
localServer.tool(tool.name, tool.description || `Proxy to remote tool: ${tool.name}`, {}, async (args) => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await remoteClient.callTool({
|
|
69
|
+
name: tool.name,
|
|
70
|
+
arguments: args,
|
|
71
|
+
});
|
|
72
|
+
// Return result directly - MCP SDK handles the response format
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const msg = err.message || String(err);
|
|
77
|
+
if (msg.includes('401') || msg.includes('Unauthorized')) {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: 'Error: Invalid API key. Check your key at https://openskills.dev/dashboard',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: `Error calling ${tool.name}: ${msg}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
isError: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// 5. Start local stdio transport
|
|
101
|
+
const stdioTransport = new StdioServerTransport();
|
|
102
|
+
await localServer.connect(stdioTransport);
|
|
103
|
+
console.error('OpenSkills MCP proxy running (stdio -> remote)');
|
|
104
|
+
// Handle graceful shutdown
|
|
105
|
+
process.on('SIGINT', async () => {
|
|
106
|
+
console.error('OpenSkills: Shutting down...');
|
|
107
|
+
await localServer.close();
|
|
108
|
+
await remoteClient.close();
|
|
109
|
+
process.exit(0);
|
|
110
|
+
});
|
|
111
|
+
process.on('SIGTERM', async () => {
|
|
112
|
+
await localServer.close();
|
|
113
|
+
await remoteClient.close();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=mcp-proxy.js.map
|