@mcp-s/skills 1.0.2 → 1.0.5
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/dist/api.js +53 -0
- package/dist/auth.js +35 -16
- package/dist/index.js +15 -2
- package/dist/installer.js +53 -10
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -43,3 +43,56 @@ export async function fetchSkillContent(baseUrl, skillSlug, auth) {
|
|
|
43
43
|
const data = await response.json();
|
|
44
44
|
return data.content;
|
|
45
45
|
}
|
|
46
|
+
export async function fetchSkillFiles(baseUrl, skillSlug, auth) {
|
|
47
|
+
const headers = {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
};
|
|
50
|
+
if (auth) {
|
|
51
|
+
headers["Authorization"] = `${auth.userAccessKey}:${auth.userOTT}`;
|
|
52
|
+
}
|
|
53
|
+
const response = await fetch(`${baseUrl}/skills/${skillSlug}/files`, {
|
|
54
|
+
headers,
|
|
55
|
+
});
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const errorText = await response.text();
|
|
58
|
+
if (response.status === 401) {
|
|
59
|
+
const error = new Error("Unauthorized");
|
|
60
|
+
error.status = 401;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
if (response.status === 404) {
|
|
64
|
+
const error = new Error("Skill not found");
|
|
65
|
+
error.status = 404;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Failed to fetch skill files: ${response.status} ${response.statusText}\n${errorText}`);
|
|
69
|
+
}
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
return data.data;
|
|
72
|
+
}
|
|
73
|
+
export async function fetchSkillFile(baseUrl, skillSlug, filePath, auth) {
|
|
74
|
+
const headers = {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
};
|
|
77
|
+
if (auth) {
|
|
78
|
+
headers["Authorization"] = `${auth.userAccessKey}:${auth.userOTT}`;
|
|
79
|
+
}
|
|
80
|
+
const response = await fetch(`${baseUrl}/skills/${skillSlug}/file/${filePath}`, {
|
|
81
|
+
headers,
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const errorText = await response.text();
|
|
85
|
+
if (response.status === 401) {
|
|
86
|
+
const error = new Error("Unauthorized");
|
|
87
|
+
error.status = 401;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
if (response.status === 404) {
|
|
91
|
+
const error = new Error("File not found");
|
|
92
|
+
error.status = 404;
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`Failed to fetch skill file: ${response.status} ${response.statusText}\n${errorText}`);
|
|
96
|
+
}
|
|
97
|
+
return response.json();
|
|
98
|
+
}
|
package/dist/auth.js
CHANGED
|
@@ -52,8 +52,34 @@ export async function authenticate({ baseUrl, userAccessKey, userOTT, openBrowse
|
|
|
52
52
|
}
|
|
53
53
|
return { url, opened };
|
|
54
54
|
}
|
|
55
|
-
export async function getOrCreateAuth(org) {
|
|
55
|
+
export async function getOrCreateAuth(org, providedUserAccessKey) {
|
|
56
56
|
const existing = await loadAuthConfig();
|
|
57
|
+
// If a userAccessKey is provided via CLI, use it
|
|
58
|
+
if (providedUserAccessKey) {
|
|
59
|
+
// Check if it matches existing config
|
|
60
|
+
if (existing &&
|
|
61
|
+
existing.userAccessKey === providedUserAccessKey &&
|
|
62
|
+
existing.userOTT &&
|
|
63
|
+
existing.org === org) {
|
|
64
|
+
return {
|
|
65
|
+
userAccessKey: existing.userAccessKey,
|
|
66
|
+
userOTT: existing.userOTT,
|
|
67
|
+
isNew: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// New key provided or org changed, generate new OTT
|
|
71
|
+
const userOTT = generateToken();
|
|
72
|
+
await saveAuthConfig({
|
|
73
|
+
userAccessKey: providedUserAccessKey,
|
|
74
|
+
userOTT,
|
|
75
|
+
org,
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
userAccessKey: providedUserAccessKey,
|
|
79
|
+
userOTT,
|
|
80
|
+
isNew: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
57
83
|
if (existing && existing.userAccessKey && existing.userOTT) {
|
|
58
84
|
// If org changed, generate new OTT
|
|
59
85
|
if (org && existing.org !== org) {
|
|
@@ -71,23 +97,16 @@ export async function getOrCreateAuth(org) {
|
|
|
71
97
|
isNew: false,
|
|
72
98
|
};
|
|
73
99
|
}
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
await saveAuthConfig({
|
|
78
|
-
userAccessKey,
|
|
79
|
-
userOTT,
|
|
80
|
-
org,
|
|
81
|
-
});
|
|
82
|
-
return {
|
|
83
|
-
userAccessKey,
|
|
84
|
-
userOTT,
|
|
85
|
-
isNew: true,
|
|
86
|
-
};
|
|
100
|
+
// No existing auth and no key provided - this is an error
|
|
101
|
+
// The user must provide their userAccessKey
|
|
102
|
+
throw new Error("No user access key found. Please provide your MCP-S user access key with --key <userAccessKey>");
|
|
87
103
|
}
|
|
88
|
-
export async function refreshToken(org) {
|
|
104
|
+
export async function refreshToken(org, providedUserAccessKey) {
|
|
89
105
|
const existing = await loadAuthConfig();
|
|
90
|
-
const userAccessKey = (existing === null || existing === void 0 ? void 0 : existing.userAccessKey)
|
|
106
|
+
const userAccessKey = providedUserAccessKey || (existing === null || existing === void 0 ? void 0 : existing.userAccessKey);
|
|
107
|
+
if (!userAccessKey) {
|
|
108
|
+
throw new Error("No user access key found. Please provide your MCP-S user access key with --key <userAccessKey>");
|
|
109
|
+
}
|
|
91
110
|
const userOTT = generateToken();
|
|
92
111
|
await saveAuthConfig({
|
|
93
112
|
userAccessKey,
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ program
|
|
|
46
46
|
.option("-l, --list", "List available skills without installing")
|
|
47
47
|
.option("-y, --yes", "Skip confirmation prompts")
|
|
48
48
|
.option("--all", "Install all skills to all agents without any prompts (implies -y -g)")
|
|
49
|
+
.option("-k, --key <userAccessKey>", "Your MCP-S user access key (found in your MCP-S dashboard)")
|
|
49
50
|
.configureOutput({
|
|
50
51
|
outputError: (str, write) => {
|
|
51
52
|
if (str.includes("error: required option")) {
|
|
@@ -102,7 +103,19 @@ async function main(options) {
|
|
|
102
103
|
try {
|
|
103
104
|
// Get or create authentication
|
|
104
105
|
spinner.start("Checking authentication...");
|
|
105
|
-
let auth
|
|
106
|
+
let auth;
|
|
107
|
+
try {
|
|
108
|
+
auth = await getOrCreateAuth(options.org, options.key);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
spinner.stop(chalk.red("Authentication required"));
|
|
112
|
+
if (error instanceof Error && error.message.includes("No user access key")) {
|
|
113
|
+
p.log.error(error.message);
|
|
114
|
+
p.log.info(`You can find your user access key in your MCP-S dashboard at ${chalk.cyan("https://<org>.mcp-s.com/settings")}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
106
119
|
spinner.stop(auth.isNew
|
|
107
120
|
? "New authentication created"
|
|
108
121
|
: "Using existing authentication");
|
|
@@ -130,7 +143,7 @@ async function main(options) {
|
|
|
130
143
|
spinner.stop("Authentication required");
|
|
131
144
|
p.log.info("You need to authenticate to access skills.");
|
|
132
145
|
// Generate new token and authenticate
|
|
133
|
-
const newAuth = await refreshToken(options.org);
|
|
146
|
+
const newAuth = await refreshToken(options.org, options.key);
|
|
134
147
|
auth = Object.assign(Object.assign({}, newAuth), { isNew: true });
|
|
135
148
|
const { url, opened } = await authenticate({
|
|
136
149
|
baseUrl,
|
package/dist/installer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, access, symlink, lstat, rm, readlink, writeFile, } from "fs/promises";
|
|
2
|
-
import { join, normalize, resolve, sep, relative } from "path";
|
|
2
|
+
import { join, normalize, resolve, sep, relative, dirname } from "path";
|
|
3
3
|
import { homedir, platform } from "os";
|
|
4
4
|
import { agents } from "./agents.js";
|
|
5
5
|
const AGENTS_DIR = ".agents";
|
|
@@ -65,6 +65,55 @@ async function createSymlink(target, linkPath) {
|
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Write all skill files to a directory
|
|
70
|
+
* Structure:
|
|
71
|
+
* skill-name/
|
|
72
|
+
* ├── SKILL.md # Required: instructions + metadata
|
|
73
|
+
* ├── scripts/ # Optional: executable code
|
|
74
|
+
* ├── references/ # Optional: documentation
|
|
75
|
+
* └── assets/ # Optional: templates, resources
|
|
76
|
+
*/
|
|
77
|
+
async function writeSkillFiles(skill, targetDir) {
|
|
78
|
+
// Ensure the target directory exists
|
|
79
|
+
await mkdir(targetDir, { recursive: true });
|
|
80
|
+
// Write SKILL.md
|
|
81
|
+
const skillMdPath = join(targetDir, "SKILL.md");
|
|
82
|
+
await writeFile(skillMdPath, skill.content, "utf-8");
|
|
83
|
+
// Write scripts if present
|
|
84
|
+
if (skill.scripts && skill.scripts.length > 0) {
|
|
85
|
+
for (const script of skill.scripts) {
|
|
86
|
+
const scriptPath = join(targetDir, script.path);
|
|
87
|
+
const scriptDir = dirname(scriptPath);
|
|
88
|
+
await mkdir(scriptDir, { recursive: true });
|
|
89
|
+
await writeFile(scriptPath, script.content, "utf-8");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Write references if present
|
|
93
|
+
if (skill.references && skill.references.length > 0) {
|
|
94
|
+
for (const reference of skill.references) {
|
|
95
|
+
const refPath = join(targetDir, reference.path);
|
|
96
|
+
const refDir = dirname(refPath);
|
|
97
|
+
await mkdir(refDir, { recursive: true });
|
|
98
|
+
await writeFile(refPath, reference.content, "utf-8");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Write assets if present
|
|
102
|
+
if (skill.assets && skill.assets.length > 0) {
|
|
103
|
+
for (const asset of skill.assets) {
|
|
104
|
+
const assetPath = join(targetDir, asset.path);
|
|
105
|
+
const assetDir = dirname(assetPath);
|
|
106
|
+
await mkdir(assetDir, { recursive: true });
|
|
107
|
+
// Assets may be base64 encoded for binary files
|
|
108
|
+
if (asset.mimeType && !asset.mimeType.startsWith("text/")) {
|
|
109
|
+
await writeFile(assetPath, Buffer.from(asset.content, "base64"));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await writeFile(assetPath, asset.content, "utf-8");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
68
117
|
export async function installSkillForAgent(skill, agentType, options = {}) {
|
|
69
118
|
var _a, _b;
|
|
70
119
|
const agent = agents[agentType];
|
|
@@ -96,9 +145,7 @@ export async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
96
145
|
}
|
|
97
146
|
try {
|
|
98
147
|
if (installMode === "copy") {
|
|
99
|
-
await
|
|
100
|
-
const skillMdPath = join(agentDir, "SKILL.md");
|
|
101
|
-
await writeFile(skillMdPath, skill.content, "utf-8");
|
|
148
|
+
await writeSkillFiles(skill, agentDir);
|
|
102
149
|
return {
|
|
103
150
|
success: true,
|
|
104
151
|
path: agentDir,
|
|
@@ -106,9 +153,7 @@ export async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
106
153
|
};
|
|
107
154
|
}
|
|
108
155
|
// Symlink mode
|
|
109
|
-
await
|
|
110
|
-
const skillMdPath = join(canonicalDir, "SKILL.md");
|
|
111
|
-
await writeFile(skillMdPath, skill.content, "utf-8");
|
|
156
|
+
await writeSkillFiles(skill, canonicalDir);
|
|
112
157
|
const symlinkCreated = await createSymlink(canonicalDir, agentDir);
|
|
113
158
|
if (!symlinkCreated) {
|
|
114
159
|
// Fallback to copy
|
|
@@ -118,9 +163,7 @@ export async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
118
163
|
catch (_c) {
|
|
119
164
|
// Ignore
|
|
120
165
|
}
|
|
121
|
-
await
|
|
122
|
-
const agentSkillMdPath = join(agentDir, "SKILL.md");
|
|
123
|
-
await writeFile(agentSkillMdPath, skill.content, "utf-8");
|
|
166
|
+
await writeSkillFiles(skill, agentDir);
|
|
124
167
|
return {
|
|
125
168
|
success: true,
|
|
126
169
|
path: agentDir,
|