@codemcp/skills 1.7.1 → 1.8.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/dist/cli.mjs CHANGED
@@ -549,13 +549,17 @@ async function parseSkillMd(skillMdPath, options) {
549
549
  if (typeof data.name !== "string" || typeof data.description !== "string") return null;
550
550
  if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
551
551
  const rawMcpDeps = data["requires-mcp-servers"];
552
+ const rawAllowedTools = data["allowed-tools"];
553
+ let allowedTools;
554
+ if (typeof rawAllowedTools === "string") allowedTools = rawAllowedTools.split(/\s+/).filter(Boolean);
552
555
  return {
553
556
  name: data.name,
554
557
  description: data.description,
555
558
  path: dirname(skillMdPath),
556
559
  rawContent: content,
557
560
  metadata: data.metadata,
558
- requiresMcpServers: Array.isArray(rawMcpDeps) ? rawMcpDeps : void 0
561
+ requiresMcpServers: Array.isArray(rawMcpDeps) ? rawMcpDeps : void 0,
562
+ allowedTools
559
563
  };
560
564
  } catch {
561
565
  return null;
@@ -2352,7 +2356,7 @@ function createEmptyLocalLock() {
2352
2356
  skills: {}
2353
2357
  };
2354
2358
  }
2355
- var version$1 = "1.7.1";
2359
+ var version$1 = "1.8.0";
2356
2360
  const isCancelled$1 = (value) => typeof value === "symbol";
2357
2361
  /**
2358
2362
  * Check if a source identifier (owner/repo format) represents a private GitHub repo.
@@ -4837,17 +4841,39 @@ async function generateSkillsMcpAgent(agentType, cwd, scope = "local", extraServ
4837
4841
  */
4838
4842
  /**
4839
4843
  * Load all installed skills for the given scope and return the unique set of
4840
- * MCP server dependencies declared across them.
4844
+ * MCP server dependencies declared across them, along with per-server allowed
4845
+ * tool restrictions derived from skill `allowed-tools` frontmatter.
4841
4846
  */
4842
4847
  async function loadInstalledSkillMcpDeps(cwd, scope) {
4843
4848
  const isGlobal = scope === "global";
4844
4849
  const searchDirs = [getCanonicalSkillsDir(isGlobal, cwd), getMCPCanonicalSkillsDir(isGlobal, cwd)];
4845
4850
  const seen = /* @__PURE__ */ new Map();
4851
+ const serverToolSets = /* @__PURE__ */ new Map();
4846
4852
  for (const dir of searchDirs) try {
4847
4853
  const skills = await discoverSkills(dir, void 0, { fullDepth: true });
4848
- for (const skill of skills) for (const dep of skill.requiresMcpServers ?? []) if (!seen.has(dep.name)) seen.set(dep.name, dep);
4854
+ for (const skill of skills) for (const dep of skill.requiresMcpServers ?? []) {
4855
+ if (!seen.has(dep.name)) seen.set(dep.name, dep);
4856
+ const skillAllowedTools = skill.allowedTools;
4857
+ const serverName = dep.name;
4858
+ if (!skillAllowedTools || skillAllowedTools.length === 0) serverToolSets.set(serverName, "wildcard");
4859
+ else if (serverToolSets.get(serverName) !== "wildcard") {
4860
+ const prefix = `@${serverName}/`;
4861
+ const serverTools = skillAllowedTools.filter((t) => t.startsWith(prefix)).map((t) => t.slice(prefix.length));
4862
+ if (serverTools.length === 0) serverToolSets.set(serverName, "wildcard");
4863
+ else {
4864
+ const existing = serverToolSets.get(serverName);
4865
+ if (existing instanceof Set) for (const t of serverTools) existing.add(t);
4866
+ else serverToolSets.set(serverName, new Set(serverTools));
4867
+ }
4868
+ }
4869
+ }
4849
4870
  } catch {}
4850
- return [...seen.values()];
4871
+ const allowedToolsByServer = {};
4872
+ for (const [serverName, toolsOrWildcard] of serverToolSets.entries()) if (toolsOrWildcard instanceof Set) allowedToolsByServer[serverName] = [...toolsOrWildcard];
4873
+ return {
4874
+ deps: [...seen.values()],
4875
+ allowedToolsByServer
4876
+ };
4851
4877
  }
4852
4878
  /** Replace `{{PARAM_NAME}}` placeholders with resolved values. */
4853
4879
  function substituteParam(value, params) {
@@ -4896,13 +4922,17 @@ async function resolveParameters(dep) {
4896
4922
  * In all cases the diff rule is: only check presence of the server key —
4897
4923
  * never modify an existing server's configuration.
4898
4924
  *
4899
- * @param deps MCP server dependencies collected from installed skills.
4900
- * @param agentTypes Agents that were just configured by `mcp setup`.
4901
- * @param configCwd Base directory for agent config files.
4902
- * @param scope 'local' or 'global'.
4903
- * @param configMode Whether generator-backed agents use agent-config or mcp-json.
4925
+ * @param deps MCP server dependencies collected from installed skills.
4926
+ * @param agentTypes Agents that were just configured by `mcp setup`.
4927
+ * @param configCwd Base directory for agent config files.
4928
+ * @param scope 'local' or 'global'.
4929
+ * @param configMode Whether generator-backed agents use agent-config or mcp-json.
4930
+ * @param allowedToolsByServer Per-server tool restrictions derived from skill allowedTools.
4931
+ * When provided and a server has an entry, only those specific
4932
+ * tools are whitelisted in the generated agent config instead of
4933
+ * the default wildcard.
4904
4934
  */
4905
- async function configureSkillMcpDepsForAgents(deps, agentTypes, configCwd, scope, configMode = "agent-config") {
4935
+ async function configureSkillMcpDepsForAgents(deps, agentTypes, configCwd, scope, configMode = "agent-config", allowedToolsByServer = {}) {
4906
4936
  if (deps.length === 0 || agentTypes.length === 0) return;
4907
4937
  const resolvedConfigs = /* @__PURE__ */ new Map();
4908
4938
  for (const dep of deps) {
@@ -4915,11 +4945,13 @@ async function configureSkillMcpDepsForAgents(deps, agentTypes, configCwd, scope
4915
4945
  const missingServers = {};
4916
4946
  for (const dep of deps) {
4917
4947
  const resolved = resolvedConfigs.get(dep.name);
4948
+ const restrictedTools = allowedToolsByServer[dep.name];
4918
4949
  missingServers[dep.name] = {
4919
4950
  command: resolved.command,
4920
4951
  args: resolved.args,
4921
4952
  env: resolved.env,
4922
- ...resolved.cwd ? { cwd: resolved.cwd } : {}
4953
+ ...resolved.cwd ? { cwd: resolved.cwd } : {},
4954
+ ...restrictedTools ? { tools: restrictedTools } : {}
4923
4955
  };
4924
4956
  }
4925
4957
  try {
@@ -5106,7 +5138,10 @@ async function setupTuiMode(cwd, scope = "local", forcedConfigMode) {
5106
5138
  console.log("");
5107
5139
  const configuredAgents = [];
5108
5140
  for (const agentType of selectedAgents) if (await configureOneAgent(agentType, configCwd, scope, configMode) !== null) configuredAgents.push(agentType);
5109
- if (configuredAgents.length > 0) await configureSkillMcpDepsForAgents(await loadInstalledSkillMcpDeps(cwd, scope), configuredAgents, configCwd, scope, configMode);
5141
+ if (configuredAgents.length > 0) {
5142
+ const { deps: skillDeps, allowedToolsByServer } = await loadInstalledSkillMcpDeps(cwd, scope);
5143
+ await configureSkillMcpDepsForAgents(skillDeps, configuredAgents, configCwd, scope, configMode, allowedToolsByServer);
5144
+ }
5110
5145
  const scopeLabel = scope === "global" ? "global (home directory)" : "local (project directory)";
5111
5146
  const failCount = selectedAgents.length - configuredAgents.length;
5112
5147
  console.log("");
@@ -5124,17 +5159,17 @@ async function setupCliMode(agentTypes, cwd, scope = "local", forcedConfigMode)
5124
5159
  const configuredAgents = [];
5125
5160
  for (const agentType of agentTypes) if (await configureOneAgent(agentType, cwd, scope, forcedConfigMode ?? (isGeneratorBacked(agentType) ? "agent-config" : "mcp-json")) !== null) configuredAgents.push(agentType);
5126
5161
  if (configuredAgents.length > 0) {
5127
- const skillDeps = await loadInstalledSkillMcpDeps(cwd, scope);
5162
+ const { deps: skillDeps, allowedToolsByServer } = await loadInstalledSkillMcpDeps(cwd, scope);
5128
5163
  if (forcedConfigMode) {
5129
5164
  const generatorBacked = configuredAgents.filter((a) => isGeneratorBacked(a));
5130
5165
  const rawMcp = configuredAgents.filter((a) => !isGeneratorBacked(a));
5131
- if (generatorBacked.length > 0) await configureSkillMcpDepsForAgents(skillDeps, generatorBacked, cwd, scope, forcedConfigMode);
5132
- if (rawMcp.length > 0) await configureSkillMcpDepsForAgents(skillDeps, rawMcp, cwd, scope, "mcp-json");
5166
+ if (generatorBacked.length > 0) await configureSkillMcpDepsForAgents(skillDeps, generatorBacked, cwd, scope, forcedConfigMode, allowedToolsByServer);
5167
+ if (rawMcp.length > 0) await configureSkillMcpDepsForAgents(skillDeps, rawMcp, cwd, scope, "mcp-json", allowedToolsByServer);
5133
5168
  } else {
5134
5169
  const generatorBacked = configuredAgents.filter((a) => isGeneratorBacked(a));
5135
5170
  const rawMcp = configuredAgents.filter((a) => !isGeneratorBacked(a));
5136
- if (generatorBacked.length > 0) await configureSkillMcpDepsForAgents(skillDeps, generatorBacked, cwd, scope, "agent-config");
5137
- if (rawMcp.length > 0) await configureSkillMcpDepsForAgents(skillDeps, rawMcp, cwd, scope, "mcp-json");
5171
+ if (generatorBacked.length > 0) await configureSkillMcpDepsForAgents(skillDeps, generatorBacked, cwd, scope, "agent-config", allowedToolsByServer);
5172
+ if (rawMcp.length > 0) await configureSkillMcpDepsForAgents(skillDeps, rawMcp, cwd, scope, "mcp-json", allowedToolsByServer);
5138
5173
  }
5139
5174
  }
5140
5175
  const failCount = agentTypes.length - configuredAgents.length;