@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 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
- // Create new auth
75
- const userAccessKey = generateToken();
76
- const userOTT = generateToken();
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) || generateToken();
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 = await getOrCreateAuth(options.org);
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 mkdir(agentDir, { recursive: true });
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 mkdir(canonicalDir, { recursive: true });
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 mkdir(agentDir, { recursive: true });
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-s/skills",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "Install skills onto coding agents from MCP-S server",
5
5
  "type": "module",
6
6
  "files": [