toolcapsule 0.1.0-alpha.8 → 0.1.0-alpha.9

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
@@ -33,6 +33,24 @@ But large MCP servers can be expensive in agent contexts:
33
33
  ToolCapsule keeps the MCP server as the source of truth, but exposes it through a lightweight Skill and local artifacts.
34
34
  Transport logs are quiet by default so remote MCP URLs are not printed during normal use. Set `TOOLCAPSULE_DEBUG=1` only when debugging.
35
35
 
36
+ ## Import your existing MCP setup
37
+
38
+ Already configured MCP in Claude Code, GitHub Copilot / VS Code, OpenCode, Gemini CLI, or Cursor? Import it instead of retyping URLs and commands:
39
+
40
+ ```bash
41
+ tcap import --dry-run
42
+ tcap import --name github --target claude
43
+ ```
44
+
45
+ ToolCapsule scans common workspace MCP config files such as `.mcp.json`, `.vscode/mcp.json`, `opencode.json`, `.gemini/settings.json`, and `.cursor/mcp.json`, then creates:
46
+
47
+ ```text
48
+ .toolcapsule/profiles/<server>.json
49
+ .claude/skills/<server>-mcp/SKILL.md
50
+ ```
51
+
52
+ Use `--target copilot`, `--target opencode`, `--target agents`, or `--target all` to write skills for other agents. User-level MCP configs are only inspected when you opt in with `--include-user`.
53
+
36
54
  ## What is a ToolCapsule?
37
55
 
38
56
  A ToolCapsule is a file-first tool call package:
@@ -49,7 +67,8 @@ Instead of making the model hold everything in the prompt, ToolCapsule stores he
49
67
  npm i -g toolcapsule
50
68
 
51
69
  toolcapsule install-skill
52
- toolcapsule init feishu --url https://mcp.example.com/mcp/xxx
70
+ toolcapsule import --dry-run
71
+ toolcapsule import --name feishu --target claude
53
72
  toolcapsule tools feishu --brief
54
73
  toolcapsule schema feishu create-doc
55
74
  toolcapsule call feishu create-doc @args.json --save-run
@@ -66,7 +85,7 @@ tcap call feishu create-doc @args.json --save-run
66
85
  ## What it generates
67
86
 
68
87
  ```text
69
- .github/skills/feishu-mcp/
88
+ .claude/skills/feishu-mcp/
70
89
  SKILL.md # lightweight Agent Skill entrypoint
71
90
  toolcapsule.config.json # MCP transport/profile config
72
91
  scripts/README.md
@@ -115,9 +134,12 @@ Results vary by MCP server, model, and host.
115
134
  ## CLI
116
135
 
117
136
  ```text
118
- toolcapsule init <name> --url <remote-mcp-url>
119
- toolcapsule init <name> --command <stdio-command> --arg <arg>
120
- toolcapsule install-skill
137
+ toolcapsule init <name> --url <remote-mcp-url> --target claude
138
+ toolcapsule init <name> --command <stdio-command> --arg <arg> --target claude
139
+ toolcapsule install-skill --target claude
140
+ toolcapsule import --dry-run
141
+ toolcapsule import --name <server> --target claude
142
+ toolcapsule import --all --target all
121
143
  toolcapsule tools <profile> --brief
122
144
  toolcapsule describe <profile> <tool> --brief
123
145
  toolcapsule schema <profile> <tool>
@@ -149,6 +171,7 @@ Early alpha. APIs may change before v1.0.
149
171
 
150
172
  - [Concept](docs/concept.md)
151
173
  - [Architecture](docs/architecture.md)
174
+ - [Import existing MCP configuration](docs/importing-mcp.md)
152
175
  - [Patch and retry](docs/patch-and-retry.md)
153
176
  - [Benchmark methodology](docs/benchmark-methodology.md)
154
177
  - [Releasing](docs/releasing.md)
@@ -156,6 +179,7 @@ Early alpha. APIs may change before v1.0.
156
179
  - [Screenshots and recordings](docs/screenshots.md)
157
180
  - [Next steps](docs/next-steps.md)
158
181
  - [Release checklist](docs/release-checklist.md)
182
+ - [Agent tool compatibility research](docs/agent-tool-compatibility.md)
159
183
  - [Roadmap](ROADMAP.md)
160
184
 
161
185
  ## License
package/dist/cli.js CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { readFile as readFile3 } from "fs/promises";
5
- import { join as join5 } from "path";
5
+ import { join as join6 } from "path";
6
6
  import { cac } from "cac";
7
7
  import pc from "picocolors";
8
8
 
9
9
  // src/mcp/client.ts
10
10
  import { spawn } from "child_process";
11
11
  import { once } from "events";
12
+ import { resolve } from "path";
12
13
  var McpClient = class {
13
14
  child;
14
15
  nextId = 1;
@@ -22,12 +23,15 @@ var McpClient = class {
22
23
  this.debug = opts.debug ?? process.env.TOOLCAPSULE_DEBUG === "1";
23
24
  this.clientVersion = opts.clientVersion ?? "0.0.0";
24
25
  if (profile.transport.type === "remote") {
25
- this.child = spawn("npx", ["-y", "mcp-remote", profile.transport.url], {
26
- stdio: ["pipe", "pipe", "pipe"]
26
+ this.child = spawn("npx", ["-y", "mcp-remote", profile.transport.url, ...headersToArgs(profile.transport.headers)], {
27
+ stdio: ["pipe", "pipe", "pipe"],
28
+ env: { ...process.env, ...profile.transport.env }
27
29
  });
28
30
  } else {
29
31
  this.child = spawn(profile.transport.command, profile.transport.args ?? [], {
30
- stdio: ["pipe", "pipe", "pipe"]
32
+ stdio: ["pipe", "pipe", "pipe"],
33
+ env: { ...process.env, ...profile.transport.env },
34
+ cwd: profile.transport.cwd ? resolve(profile.transport.cwd) : void 0
31
35
  });
32
36
  }
33
37
  this.child.stdout.setEncoding("utf8");
@@ -77,8 +81,8 @@ var McpClient = class {
77
81
  }
78
82
  async request(method, params) {
79
83
  const id = this.nextId++;
80
- const responsePromise = new Promise((resolve2, reject) => {
81
- this.pending.set(id, { resolve: resolve2, reject });
84
+ const responsePromise = new Promise((resolve3, reject) => {
85
+ this.pending.set(id, { resolve: resolve3, reject });
82
86
  });
83
87
  this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
84
88
  `);
@@ -98,7 +102,7 @@ var McpClient = class {
98
102
  }
99
103
  async close() {
100
104
  if (!this.child.killed) this.child.kill();
101
- if (this.child.exitCode === null) await Promise.race([once(this.child, "exit"), new Promise((resolve2) => setTimeout(resolve2, 500))]);
105
+ if (this.child.exitCode === null) await Promise.race([once(this.child, "exit"), new Promise((resolve3) => setTimeout(resolve3, 500))]);
102
106
  }
103
107
  async withTimeout(promise, label) {
104
108
  let timer;
@@ -112,18 +116,22 @@ var McpClient = class {
112
116
  }
113
117
  }
114
118
  };
119
+ function headersToArgs(headers) {
120
+ if (!headers) return [];
121
+ return Object.entries(headers).flatMap(([key, value]) => ["--header", `${key}:${value}`]);
122
+ }
115
123
  function redactSecrets(text) {
116
124
  return text.replace(/https:\/\/mcp\.feishu\.cn\/mcp\/[^\s"']+/g, "https://mcp.feishu.cn/mcp/[redacted]").replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, "$1[redacted]").replace(/(token=)[^\s&"']+/gi, "$1[redacted]").replace(/(api[_-]?key=)[^\s&"']+/gi, "$1[redacted]");
117
125
  }
118
126
 
119
- // src/profile.ts
127
+ // src/mcp/importer.ts
120
128
  import { existsSync } from "fs";
129
+ import { homedir } from "os";
121
130
  import { join } from "path";
122
- import { z } from "zod";
123
131
 
124
132
  // src/utils/fs.ts
125
133
  import { mkdir, readFile, writeFile } from "fs/promises";
126
- import { dirname, resolve } from "path";
134
+ import { dirname, resolve as resolve2 } from "path";
127
135
  async function readJson(path) {
128
136
  return JSON.parse(await readFile(path, "utf8"));
129
137
  }
@@ -133,12 +141,128 @@ async function writeJson(path, value) {
133
141
  `);
134
142
  }
135
143
 
144
+ // src/mcp/importer.ts
145
+ var workspaceSources = [
146
+ { tool: "vscode", path: join(".vscode", "mcp.json") },
147
+ { tool: "claude", path: ".mcp.json" },
148
+ { tool: "opencode", path: "opencode.json" },
149
+ { tool: "gemini", path: join(".gemini", "settings.json") },
150
+ { tool: "cursor", path: join(".cursor", "mcp.json") }
151
+ ];
152
+ var userSources = [
153
+ { tool: "claude", path: join(homedir(), ".claude.json"), userLevel: true },
154
+ { tool: "opencode", path: join(homedir(), ".config", "opencode", "opencode.json"), userLevel: true },
155
+ { tool: "gemini", path: join(homedir(), ".gemini", "settings.json"), userLevel: true }
156
+ ];
157
+ function asRecord(value) {
158
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
159
+ }
160
+ function stringArray(value) {
161
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
162
+ }
163
+ function stringRecord(value) {
164
+ const record = asRecord(value);
165
+ if (!record) return void 0;
166
+ const entries = Object.entries(record).filter((entry) => typeof entry[1] === "string");
167
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
168
+ }
169
+ function cleanName(name) {
170
+ const cleaned = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
171
+ return cleaned || "imported-mcp";
172
+ }
173
+ function mapServer(name, source, raw) {
174
+ const server = asRecord(raw);
175
+ if (!server) return void 0;
176
+ const warnings = [];
177
+ let transport;
178
+ const type = typeof server.type === "string" ? server.type : void 0;
179
+ const url = typeof server.url === "string" ? server.url : typeof server.httpUrl === "string" ? server.httpUrl : void 0;
180
+ const commandValue = server.command;
181
+ const headers = stringRecord(server.headers);
182
+ const env = stringRecord(server.env) ?? stringRecord(server.environment);
183
+ const cwd = typeof server.cwd === "string" ? server.cwd : void 0;
184
+ if (url && (!type || ["http", "streamable-http", "remote", "sse", "ws"].includes(type))) {
185
+ transport = { type: "remote", url, ...headers ? { headers } : {}, ...env ? { env } : {} };
186
+ if (type === "sse" || type === "ws") warnings.push(`Imported ${type} server as remote URL; verify transport compatibility.`);
187
+ } else if (typeof commandValue === "string") {
188
+ transport = {
189
+ type: "stdio",
190
+ command: commandValue,
191
+ args: stringArray(server.args) ?? [],
192
+ ...env ? { env } : {},
193
+ ...cwd ? { cwd } : {}
194
+ };
195
+ } else if (Array.isArray(commandValue) && commandValue.every((item) => typeof item === "string") && commandValue.length > 0) {
196
+ transport = { type: "stdio", command: commandValue[0], args: commandValue.slice(1), ...env ? { env } : {}, ...cwd ? { cwd } : {} };
197
+ }
198
+ if (!transport) return void 0;
199
+ if (server.headers && !headers) warnings.push("Headers were present but not copied because they were not string values.");
200
+ if ((server.env || server.environment) && !env) warnings.push("Environment variables were present but not copied because they were not string values.");
201
+ if (server.includeTools || server.excludeTools) warnings.push("Tool include/exclude filters were not copied; use ToolCapsule brief/schema commands to choose tools manually.");
202
+ const profileName = cleanName(name);
203
+ return {
204
+ name: profileName,
205
+ source,
206
+ warnings,
207
+ profile: {
208
+ name: profileName,
209
+ transport
210
+ }
211
+ };
212
+ }
213
+ function serverEntries(source, config) {
214
+ if (source.tool === "vscode" || source.tool === "cursor") return Object.entries(asRecord(config.servers) ?? {});
215
+ if (source.tool === "opencode") return Object.entries(asRecord(config.mcp) ?? {});
216
+ if (source.tool === "claude") {
217
+ const projectEntries = Object.entries(asRecord(config.mcpServers) ?? {});
218
+ const projectConfigs = Object.entries(asRecord(config.projects) ?? {}).flatMap(
219
+ ([, project]) => Object.entries(asRecord(asRecord(project)?.mcpServers) ?? {})
220
+ );
221
+ return [...projectEntries, ...projectConfigs];
222
+ }
223
+ return Object.entries(asRecord(config.mcpServers) ?? {});
224
+ }
225
+ async function discoverMcpServers(opts = {}) {
226
+ const sources = opts.includeUser ? [...workspaceSources, ...userSources] : workspaceSources;
227
+ const discovered = [];
228
+ for (const source of sources) {
229
+ if (!existsSync(source.path)) continue;
230
+ const config = asRecord(await readJson(source.path));
231
+ if (!config) continue;
232
+ for (const [name, raw] of serverEntries(source, config)) {
233
+ const imported = mapServer(name, source, raw);
234
+ if (imported) discovered.push(imported);
235
+ }
236
+ }
237
+ return discovered;
238
+ }
239
+ function selectImportedServers(servers, name, all) {
240
+ if (all) return servers;
241
+ if (!name) return servers.length === 1 ? servers : [];
242
+ const normalized = cleanName(name);
243
+ return servers.filter((server) => server.name === normalized || server.name === name);
244
+ }
245
+
136
246
  // src/profile.ts
247
+ import { existsSync as existsSync2 } from "fs";
248
+ import { join as join2 } from "path";
249
+ import { z } from "zod";
137
250
  var profileSchema = z.object({
138
251
  name: z.string().min(1),
139
252
  transport: z.union([
140
- z.object({ type: z.literal("remote"), url: z.string().url() }),
141
- z.object({ type: z.literal("stdio"), command: z.string().min(1), args: z.array(z.string()).optional() })
253
+ z.object({
254
+ type: z.literal("remote"),
255
+ url: z.string().url(),
256
+ headers: z.record(z.string()).optional(),
257
+ env: z.record(z.string()).optional()
258
+ }),
259
+ z.object({
260
+ type: z.literal("stdio"),
261
+ command: z.string().min(1),
262
+ args: z.array(z.string()).optional(),
263
+ env: z.record(z.string()).optional(),
264
+ cwd: z.string().optional()
265
+ })
142
266
  ]),
143
267
  skill: z.object({
144
268
  name: z.string().optional(),
@@ -156,10 +280,10 @@ async function loadProfile(profilePathOrName) {
156
280
  const candidates = [
157
281
  profilePathOrName,
158
282
  `${profilePathOrName}.json`,
159
- join(".toolcapsule", "profiles", `${profilePathOrName}.json`),
160
- join(".github", "skills", `${profilePathOrName}-mcp`, "toolcapsule.config.json")
283
+ join2(".toolcapsule", "profiles", `${profilePathOrName}.json`),
284
+ join2(".github", "skills", `${profilePathOrName}-mcp`, "toolcapsule.config.json")
161
285
  ];
162
- const found = candidates.find((path) => existsSync(path));
286
+ const found = candidates.find((path) => existsSync2(path));
163
287
  if (!found) throw new Error(`Profile not found: ${profilePathOrName}`);
164
288
  return profileSchema.parse(await readJson(found));
165
289
  }
@@ -206,8 +330,18 @@ function summarizeTools(tools) {
206
330
 
207
331
  // src/skill/generator.ts
208
332
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
209
- import { dirname as dirname2, join as join2 } from "path";
333
+ import { dirname as dirname2, join as join3 } from "path";
210
334
  import Handlebars from "handlebars";
335
+ var defaultSkillTarget = "claude";
336
+ function skillOutputDir(skillName, target) {
337
+ if (target === "copilot") return join3(".github", "skills", skillName);
338
+ if (target === "claude") return join3(".claude", "skills", skillName);
339
+ if (target === "opencode") return join3(".opencode", "skills", skillName);
340
+ return join3(".agents", "skills", skillName);
341
+ }
342
+ function expandSkillTargets(target) {
343
+ return target === "all" ? ["copilot", "claude", "opencode", "agents"] : [target];
344
+ }
211
345
  var skillTemplate = `---
212
346
  name: {{skillName}}
213
347
  description: '{{description}}'
@@ -249,11 +383,10 @@ toolcapsule retry <run-dir>
249
383
  - Do not print secrets.
250
384
  - Review destructive tools before calling.
251
385
  `;
252
- async function generateSkill(profile, opts = {}) {
386
+ async function generateSkillAt(profile, outputDir) {
253
387
  const skillName = profile.skill?.name || `${profile.name}-mcp`;
254
- const outputDir = opts.outputDir || join2(".github", "skills", skillName);
255
- await mkdir2(join2(outputDir, "scripts"), { recursive: true });
256
- await mkdir2(join2(outputDir, "runs"), { recursive: true });
388
+ await mkdir2(join3(outputDir, "scripts"), { recursive: true });
389
+ await mkdir2(join3(outputDir, "runs"), { recursive: true });
257
390
  const template = Handlebars.compile(skillTemplate);
258
391
  const description = profile.skill?.description || `Use when operating tools from the ${profile.name} MCP server. Lazy-load schemas, call tools through local files, and retry failed calls by patching artifacts.`;
259
392
  const markdown = template({
@@ -262,10 +395,10 @@ async function generateSkill(profile, opts = {}) {
262
395
  title: `${profile.name} MCP Skill`,
263
396
  description: description.replace(/'/g, "''")
264
397
  });
265
- await writeFile2(join2(outputDir, "SKILL.md"), markdown);
266
- await writeJson(join2(outputDir, "toolcapsule.config.json"), profile);
398
+ await writeFile2(join3(outputDir, "SKILL.md"), markdown);
399
+ await writeJson(join3(outputDir, "toolcapsule.config.json"), profile);
267
400
  await writeFile2(
268
- join2(outputDir, "scripts", "README.md"),
401
+ join3(outputDir, "scripts", "README.md"),
269
402
  `# Scripts
270
403
 
271
404
  This skill uses the project-level \`toolcapsule\` CLI.
@@ -273,6 +406,12 @@ This skill uses the project-level \`toolcapsule\` CLI.
273
406
  );
274
407
  return outputDir;
275
408
  }
409
+ async function generateSkill(profile, opts = {}) {
410
+ const skillName = profile.skill?.name || `${profile.name}-mcp`;
411
+ const target = opts.target || defaultSkillTarget;
412
+ const outputs = opts.outputDir ? [await generateSkillAt(profile, opts.outputDir)] : await Promise.all(expandSkillTargets(target).map((item) => generateSkillAt(profile, skillOutputDir(skillName, item))));
413
+ return outputs.join(", ");
414
+ }
276
415
  async function writeProfile(path, profile) {
277
416
  await mkdir2(dirname2(path), { recursive: true });
278
417
  await writeJson(path, profile);
@@ -280,7 +419,7 @@ async function writeProfile(path, profile) {
280
419
 
281
420
  // src/skill/installer.ts
282
421
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
283
- import { join as join3 } from "path";
422
+ import { join as join4 } from "path";
284
423
  var agentSkill = `---
285
424
  name: toolcapsule
286
425
  description: 'Use when: converting MCP servers into lightweight Agent Skills, installing ToolCapsule, lazy-loading MCP schemas, calling MCP tools through local args files, or using patch-and-retry workflows for heavy MCP tools.'
@@ -352,31 +491,36 @@ Use ToolCapsule for MCP servers with:
352
491
  - document, ticket, wiki, dashboard, or batch workflows;
353
492
  - failures that benefit from patching local artifacts instead of regenerating a full tool call.
354
493
  `;
355
- async function installAgentSkill(outputDir = ".github/skills/toolcapsule") {
356
- await mkdir3(outputDir, { recursive: true });
357
- await writeFile3(join3(outputDir, "SKILL.md"), agentSkill);
358
- return outputDir;
494
+ async function installAgentSkill(outputDir, target = defaultSkillTarget) {
495
+ const outputDirs = outputDir ? [outputDir] : expandSkillTargets(target).map((item) => skillOutputDir("toolcapsule", item));
496
+ await Promise.all(
497
+ outputDirs.map(async (dir) => {
498
+ await mkdir3(dir, { recursive: true });
499
+ await writeFile3(join4(dir, "SKILL.md"), agentSkill);
500
+ })
501
+ );
502
+ return outputDirs.join(", ");
359
503
  }
360
504
 
361
505
  // src/runs/recorder.ts
362
506
  import { mkdir as mkdir4, readFile as readFile2, writeFile as writeFile4 } from "fs/promises";
363
- import { join as join4 } from "path";
507
+ import { join as join5 } from "path";
364
508
  function createRunId() {
365
509
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
366
510
  }
367
511
  async function saveRun(baseDir, record) {
368
- const dir = join4(baseDir, record.id);
512
+ const dir = join5(baseDir, record.id);
369
513
  await mkdir4(dir, { recursive: true });
370
- await writeJson(join4(dir, "run.json"), record);
371
- await writeJson(join4(dir, "request.json"), record.request);
372
- if (record.response !== void 0) await writeJson(join4(dir, "response.json"), record.response);
373
- if (record.error) await writeFile4(join4(dir, "error.txt"), record.error);
374
- await writeFile4(join4(dir, "command.txt"), `${record.command}
514
+ await writeJson(join5(dir, "run.json"), record);
515
+ await writeJson(join5(dir, "request.json"), record.request);
516
+ if (record.response !== void 0) await writeJson(join5(dir, "response.json"), record.response);
517
+ if (record.error) await writeFile4(join5(dir, "error.txt"), record.error);
518
+ await writeFile4(join5(dir, "command.txt"), `${record.command}
375
519
  `);
376
520
  return dir;
377
521
  }
378
522
  async function loadRun(runDir) {
379
- return JSON.parse(await readFile2(join4(runDir, "run.json"), "utf8"));
523
+ return JSON.parse(await readFile2(join5(runDir, "run.json"), "utf8"));
380
524
  }
381
525
 
382
526
  // src/cli.ts
@@ -414,17 +558,48 @@ async function withClient(profile, fn) {
414
558
  function readArgsPath(raw) {
415
559
  return raw.startsWith("@") ? raw.slice(1) : raw;
416
560
  }
417
- cli.command("init <name>", "Create a profile and generated Agent Skill for an MCP server").option("--url <url>", "Remote MCP URL").option("--command <command>", "stdio MCP command").option("--arg <arg>", "stdio MCP argument, repeatable", { type: [String] }).option("--output <dir>", "Skill output directory").action(async (name, options) => {
561
+ function readSkillTarget(raw) {
562
+ const target = raw || defaultSkillTarget;
563
+ if (["copilot", "claude", "opencode", "agents", "all"].includes(target)) return target;
564
+ throw new Error("Invalid --target. Use one of: copilot, claude, opencode, agents, all");
565
+ }
566
+ cli.command("init <name>", "Create a profile and generated Agent Skill for an MCP server").option("--url <url>", "Remote MCP URL").option("--command <command>", "stdio MCP command").option("--arg <arg>", "stdio MCP argument, repeatable", { type: [String] }).option("--output <dir>", "Skill output directory").option("--target <target>", "Skill target: copilot, claude, opencode, agents, all", { default: defaultSkillTarget }).action(async (name, options) => {
418
567
  if (!options.url && !options.command) throw new Error("Provide --url for remote MCP or --command for stdio MCP");
419
568
  const profile = options.url ? { name, transport: { type: "remote", url: options.url } } : { name, transport: { type: "stdio", command: options.command, args: options.arg ?? [] } };
420
- await writeProfile(join5(".toolcapsule", "profiles", `${name}.json`), profile);
421
- const out = await generateSkill(profile, options.output ? { outputDir: options.output } : {});
569
+ await writeProfile(join6(".toolcapsule", "profiles", `${name}.json`), profile);
570
+ const out = await generateSkill(profile, options.output ? { outputDir: options.output } : { target: readSkillTarget(options.target) });
422
571
  console.log(pc.green(`Created profile and skill at ${out}`));
423
572
  });
424
- cli.command("install-skill", "Install the generic ToolCapsule Agent Skill into this workspace").option("--output <dir>", "Skill output directory", { default: ".github/skills/toolcapsule" }).action(async (options) => {
425
- const out = await installAgentSkill(options.output);
573
+ cli.command("install-skill", "Install the generic ToolCapsule Agent Skill into this workspace").option("--output <dir>", "Skill output directory").option("--target <target>", "Skill target: copilot, claude, opencode, agents, all", { default: defaultSkillTarget }).action(async (options) => {
574
+ const out = await installAgentSkill(options.output, readSkillTarget(options.target));
426
575
  console.log(pc.green(`Installed ToolCapsule Agent Skill at ${out}`));
427
576
  });
577
+ cli.command("import", "Import existing MCP configuration into ToolCapsule profiles and skills").option("--include-user", "Also inspect user-level MCP config files").option("--name <name>", "Import only one MCP server by name").option("--all", "Import all discovered MCP servers").option("--target <target>", "Skill target: copilot, claude, opencode, agents, all", { default: defaultSkillTarget }).option("--dry-run", "List importable MCP servers without writing files").action(
578
+ async (options) => {
579
+ const discovered = await discoverMcpServers(options.includeUser ? { includeUser: true } : {});
580
+ if (discovered.length === 0) {
581
+ console.log("No importable MCP servers found.");
582
+ return;
583
+ }
584
+ if (options.dryRun) {
585
+ for (const server of discovered) {
586
+ console.log(`${server.name} ${server.source.tool} ${server.source.path}`);
587
+ for (const warning of server.warnings) console.log(pc.yellow(` warning: ${warning}`));
588
+ }
589
+ return;
590
+ }
591
+ const selected = selectImportedServers(discovered, options.name, options.all);
592
+ if (selected.length === 0) {
593
+ throw new Error("Multiple MCP servers found. Re-run with --dry-run, then pass --name <server> or --all.");
594
+ }
595
+ for (const server of selected) {
596
+ await writeProfile(join6(".toolcapsule", "profiles", `${server.profile.name}.json`), server.profile);
597
+ const out = await generateSkill(server.profile, { target: readSkillTarget(options.target) });
598
+ console.log(pc.green(`Imported ${server.name} from ${server.source.path} -> ${out}`));
599
+ for (const warning of server.warnings) console.log(pc.yellow(` warning: ${warning}`));
600
+ }
601
+ }
602
+ );
428
603
  cli.command("tools <profile>", "List MCP tools").option("--brief", "Print compact tool summaries").option("--names", "Print tool names only").option("--json", "Print raw JSON").action(async (profileName, options) => {
429
604
  const profile = await loadProfile(profileName);
430
605
  const result = await withClient(profile, (client) => client.listTools());
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/mcp/client.ts","../src/profile.ts","../src/utils/fs.ts","../src/schema/brief.ts","../src/schema/summarize.ts","../src/skill/generator.ts","../src/skill/installer.ts","../src/runs/recorder.ts","../src/utils/tokens.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { cac } from \"cac\";\nimport pc from \"picocolors\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { McpClient } from \"./mcp/client.js\";\nimport { loadProfile } from \"./profile.js\";\nimport { briefTools } from \"./schema/brief.js\";\nimport { summarizeTool, summarizeTools } from \"./schema/summarize.js\";\nimport { generateSkill, writeProfile } from \"./skill/generator.js\";\nimport { installAgentSkill } from \"./skill/installer.js\";\nimport { createRunId, loadRun, saveRun } from \"./runs/recorder.js\";\nimport { writeFile } from \"node:fs/promises\";\nimport { readJson } from \"./utils/fs.js\";\nimport { percentReduction, roughTokens } from \"./utils/tokens.js\";\n\nconst cli = cac(\"toolcapsule\");\n\nasync function readPackageVersion(): Promise<string> {\n try {\n const pkg = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst packageVersion = await readPackageVersion();\n\nasync function withClient<T>(profile: ProfileConfig, fn: (client: McpClient) => Promise<T>): Promise<T> {\n const client = new McpClient(profile, { clientVersion: packageVersion });\n try {\n await client.init();\n return await fn(client);\n } finally {\n await client.close();\n }\n}\n\nfunction readArgsPath(raw: string): string {\n return raw.startsWith(\"@\") ? raw.slice(1) : raw;\n}\n\ncli\n .command(\"init <name>\", \"Create a profile and generated Agent Skill for an MCP server\")\n .option(\"--url <url>\", \"Remote MCP URL\")\n .option(\"--command <command>\", \"stdio MCP command\")\n .option(\"--arg <arg>\", \"stdio MCP argument, repeatable\", { type: [String] })\n .option(\"--output <dir>\", \"Skill output directory\")\n .action(async (name: string, options: { url?: string; command?: string; arg?: string[]; output?: string }) => {\n if (!options.url && !options.command) throw new Error(\"Provide --url for remote MCP or --command for stdio MCP\");\n const profile: ProfileConfig = options.url\n ? { name, transport: { type: \"remote\", url: options.url } }\n : { name, transport: { type: \"stdio\", command: options.command!, args: options.arg ?? [] } };\n await writeProfile(join(\".toolcapsule\", \"profiles\", `${name}.json`), profile);\n const out = await generateSkill(profile, options.output ? { outputDir: options.output } : {});\n console.log(pc.green(`Created profile and skill at ${out}`));\n });\n\ncli\n .command(\"install-skill\", \"Install the generic ToolCapsule Agent Skill into this workspace\")\n .option(\"--output <dir>\", \"Skill output directory\", { default: \".github/skills/toolcapsule\" })\n .action(async (options: { output: string }) => {\n const out = await installAgentSkill(options.output);\n console.log(pc.green(`Installed ToolCapsule Agent Skill at ${out}`));\n });\n\ncli\n .command(\"tools <profile>\", \"List MCP tools\")\n .option(\"--brief\", \"Print compact tool summaries\")\n .option(\"--names\", \"Print tool names only\")\n .option(\"--json\", \"Print raw JSON\")\n .action(async (profileName: string, options: { brief?: boolean; names?: boolean; json?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n if (options.json) console.log(JSON.stringify(result, null, 2));\n else if (options.names) console.log(result.tools.map((tool) => tool.name).join(\"\\n\"));\n else console.log(briefTools(result.tools));\n });\n\ncli\n .command(\"describe <profile> <tool>\", \"Describe one MCP tool\")\n .option(\"--brief\", \"Print summarized schema instead of raw schema\")\n .action(async (profileName: string, toolName: string, options: { brief?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const tool = result.tools.find((item) => item.name === toolName);\n if (!tool) throw new Error(`Tool not found: ${toolName}`);\n console.log(JSON.stringify(options.brief ? summarizeTool(tool) : tool, null, 2));\n });\n\ncli.command(\"schema <profile> <tool>\", \"Print a compact schema for one MCP tool\").action(async (profileName: string, toolName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const tool = result.tools.find((item) => item.name === toolName);\n if (!tool) throw new Error(`Tool not found: ${toolName}`);\n console.log(JSON.stringify(summarizeTool(tool), null, 2));\n});\n\ncli\n .command(\"call <profile> <tool> <args>\", \"Call an MCP tool with JSON args or @args.json\")\n .option(\"--save-run\", \"Save request, response, and command under runs/\")\n .action(async (profileName: string, toolName: string, argsRaw: string, options: { saveRun?: boolean }) => {\n const argsPath = readArgsPath(argsRaw);\n const toolArgs = argsRaw.startsWith(\"@\") ? await readJson(argsPath) : JSON.parse(argsRaw);\n const profile = await loadProfile(profileName);\n const request = { name: toolName, arguments: toolArgs };\n const command = `toolcapsule call ${profileName} ${toolName} ${argsRaw}`;\n const runId = createRunId();\n try {\n const response = await withClient(profile, (client) => client.callTool(toolName, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"success\",\n command,\n request,\n response,\n });\n console.error(pc.green(`Saved run: ${dir}`));\n }\n } catch (error) {\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"error\",\n command,\n request,\n error: error instanceof Error ? error.message : String(error),\n });\n console.error(pc.yellow(`Saved failed run: ${dir}`));\n }\n throw error;\n }\n });\n\ncli.command(\"retry <runDir>\", \"Retry a saved run, re-reading the args file\").action(async (runDir: string) => {\n const run = await loadRun(runDir);\n const toolArgs = await readJson(run.argsFile);\n const profile = await loadProfile(run.profile);\n const response = await withClient(profile, (client) => client.callTool(run.tool, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n});\n\ncli.command(\"summarize <profile>\", \"Summarize all tools into compact JSON\").action(async (profileName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n console.log(JSON.stringify(summarizeTools(result.tools), null, 2));\n});\n\ncli\n .command(\"benchmark <profile>\", \"Estimate schema savings for a profile\")\n .option(\"--markdown\", \"Print a Markdown report\")\n .option(\"--out <file>\", \"Write report to a file\")\n .action(async (profileName: string, options: { markdown?: boolean; out?: string }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const nativeTokens = roughTokens(result);\n const brief = summarizeTools(result.tools);\n const briefTokens = roughTokens(brief);\n const summary = {\n profile: profileName,\n tools: result.tools.length,\n nativeToolsRoughTokens: nativeTokens,\n summarizedToolsRoughTokens: briefTokens,\n estimatedReductionPct: Number(percentReduction(nativeTokens, briefTokens).toFixed(2)),\n };\n const output = options.markdown\n ? `# ToolCapsule benchmark: ${profileName}\\n\\n| Metric | Value |\\n|---|---:|\\n| MCP tools | ${summary.tools} |\\n| Native MCP schema rough tokens | ${summary.nativeToolsRoughTokens} |\\n| ToolCapsule summary rough tokens | ${summary.summarizedToolsRoughTokens} |\\n| Estimated reduction | ${summary.estimatedReductionPct}% |\\n\\n> Rough tokens are estimated from serialized schema length. Use this report to compare schema footprint before and after capsule summaries.\\n`\n : JSON.stringify(summary, null, 2);\n if (options.out) await writeFile(options.out, output);\n console.log(output);\n});\n\ncli.command(\"render-readme\", \"Print website hero copy snippets\").action(async () => {\n console.log(await readFile(new URL(\"../docs/hero-copy.md\", import.meta.url), \"utf8\"));\n});\n\ncli.help();\ncli.version(packageVersion);\n\ntry {\n cli.parse();\n} catch (error) {\n console.error(pc.red(error instanceof Error ? error.message : String(error)));\n process.exit(1);\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport type { JsonRpcMessage, ProfileConfig, ToolsListResult } from \"../types.js\";\n\nexport type McpClientOptions = {\n timeoutMs?: number;\n debug?: boolean;\n clientVersion?: string;\n};\n\nexport class McpClient {\n private child: ChildProcessWithoutNullStreams;\n private nextId = 1;\n private buffer = \"\";\n private pending = new Map<\n number,\n { resolve: (value: JsonRpcMessage) => void; reject: (error: Error) => void }\n >();\n private timeoutMs: number;\n private debug: boolean;\n private clientVersion: string;\n\n constructor(profile: ProfileConfig, opts: McpClientOptions = {}) {\n this.timeoutMs = opts.timeoutMs ?? Number(process.env.TOOLCAPSULE_TIMEOUT_MS || \"45000\");\n this.debug = opts.debug ?? process.env.TOOLCAPSULE_DEBUG === \"1\";\n this.clientVersion = opts.clientVersion ?? \"0.0.0\";\n if (profile.transport.type === \"remote\") {\n this.child = spawn(\"npx\", [\"-y\", \"mcp-remote\", profile.transport.url], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n } else {\n this.child = spawn(profile.transport.command, profile.transport.args ?? [], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n }\n this.child.stdout.setEncoding(\"utf8\");\n this.child.stdout.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.stderr.on(\"data\", (chunk: Buffer) => this.onStderr(chunk));\n this.child.on(\"exit\", (code, signal) => {\n const error = new Error(`MCP process exited early (code=${code}, signal=${signal})`);\n for (const waiter of this.pending.values()) waiter.reject(error);\n this.pending.clear();\n });\n }\n\n private onStderr(chunk: Buffer): void {\n if (!this.debug) return;\n process.stderr.write(redactSecrets(chunk.toString(\"utf8\")));\n }\n\n private onStdout(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex: number;\n while ((newlineIndex = this.buffer.indexOf(\"\\n\")) >= 0) {\n const line = this.buffer.slice(0, newlineIndex).trim();\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (!line) continue;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(line) as JsonRpcMessage;\n } catch {\n process.stderr.write(`${line}\\n`);\n continue;\n }\n if (typeof message.id === \"number\") {\n const waiter = this.pending.get(message.id);\n if (waiter) {\n this.pending.delete(message.id);\n waiter.resolve(message);\n }\n }\n }\n }\n\n async init(): Promise<void> {\n await this.request(\"initialize\", {\n protocolVersion: \"2025-03-26\",\n capabilities: {},\n clientInfo: { name: \"toolcapsule\", version: this.clientVersion },\n });\n this.notify(\"notifications/initialized\", {});\n }\n\n async request(method: string, params?: unknown): Promise<unknown> {\n const id = this.nextId++;\n const responsePromise = new Promise<JsonRpcMessage>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n });\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", id, method, params })}\\n`);\n const response = await this.withTimeout(responsePromise, method);\n if (response.error) throw new Error(`${method} failed: ${JSON.stringify(response.error, null, 2)}`);\n return response.result;\n }\n\n notify(method: string, params?: unknown): void {\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", method, params })}\\n`);\n }\n\n async listTools(): Promise<ToolsListResult> {\n return (await this.request(\"tools/list\", {})) as ToolsListResult;\n }\n\n async callTool(name: string, args: unknown): Promise<unknown> {\n return await this.request(\"tools/call\", { name, arguments: args });\n }\n\n async close(): Promise<void> {\n if (!this.child.killed) this.child.kill();\n if (this.child.exitCode === null) await Promise.race([once(this.child, \"exit\"), new Promise((resolve) => setTimeout(resolve, 500))]);\n }\n\n private async withTimeout<T>(promise: Promise<T>, label: string): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`${label} timed out after ${this.timeoutMs}ms`)), this.timeoutMs);\n });\n try {\n return await Promise.race([promise, timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n\nfunction redactSecrets(text: string): string {\n return text\n .replace(/https:\\/\\/mcp\\.feishu\\.cn\\/mcp\\/[^\\s\"']+/g, \"https://mcp.feishu.cn/mcp/[redacted]\")\n .replace(/(Bearer\\s+)[A-Za-z0-9._~+/=-]+/gi, \"$1[redacted]\")\n .replace(/(token=)[^\\s&\"']+/gi, \"$1[redacted]\")\n .replace(/(api[_-]?key=)[^\\s&\"']+/gi, \"$1[redacted]\");\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { readJson } from \"./utils/fs.js\";\n\nconst profileSchema = z.object({\n name: z.string().min(1),\n transport: z.union([\n z.object({ type: z.literal(\"remote\"), url: z.string().url() }),\n z.object({ type: z.literal(\"stdio\"), command: z.string().min(1), args: z.array(z.string()).optional() }),\n ]),\n skill: z\n .object({\n name: z.string().optional(),\n description: z.string().optional(),\n })\n .optional(),\n shortcuts: z\n .record(\n z.object({\n tool: z.string(),\n description: z.string().optional(),\n args: z.record(z.enum([\"string\", \"file\", \"json\", \"boolean\", \"number\"])).optional(),\n }),\n )\n .optional(),\n});\n\nexport async function loadProfile(profilePathOrName: string): Promise<ProfileConfig> {\n const candidates = [\n profilePathOrName,\n `${profilePathOrName}.json`,\n join(\".toolcapsule\", \"profiles\", `${profilePathOrName}.json`),\n join(\".github\", \"skills\", `${profilePathOrName}-mcp`, \"toolcapsule.config.json\"),\n ];\n const found = candidates.find((path) => existsSync(path));\n if (!found) throw new Error(`Profile not found: ${profilePathOrName}`);\n return profileSchema.parse(await readJson(found)) as ProfileConfig;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readFile(path, \"utf8\")) as T;\n}\n\nexport async function writeJson(path: string, value: unknown): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport async function writeText(path: string, value: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, value);\n}\n\nexport function abs(path: string): string {\n return resolve(process.cwd(), path);\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function toolTitle(tool: McpTool): string {\n return tool.annotations?.title || tool.description?.split(/\\n/)[0]?.slice(0, 100) || tool.name;\n}\n\nexport function briefTool(tool: McpTool): string {\n const schema = tool.inputSchema as any;\n const required = Array.isArray(schema?.required) ? schema.required : [];\n const properties = schema?.properties && typeof schema.properties === \"object\" ? Object.keys(schema.properties) : [];\n const flags = [\n tool.annotations?.readOnlyHint ? \"read-only\" : undefined,\n tool.annotations?.destructiveHint ? \"destructive\" : undefined,\n ].filter(Boolean);\n return `- ${tool.name}: ${toolTitle(tool)}${flags.length ? ` [${flags.join(\", \")}]` : \"\"}${required.length ? ` required=${required.join(\"|\")}` : \"\"}${properties.length ? ` args=${properties.slice(0, 12).join(\"|\")}` : \"\"}`;\n}\n\nexport function briefTools(tools: McpTool[]): string {\n return tools.map(briefTool).join(\"\\n\");\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function summarizeTool(tool: McpTool): unknown {\n const schema = tool.inputSchema as any;\n const properties = schema?.properties && typeof schema.properties === \"object\" ? schema.properties : {};\n return {\n name: tool.name,\n title: tool.annotations?.title,\n description: tool.description?.split(/\\n\\n/)[0]?.slice(0, 500),\n annotations: tool.annotations,\n required: Array.isArray(schema?.required) ? schema.required : [],\n arguments: Object.fromEntries(\n Object.entries(properties).map(([key, value]: [string, any]) => [\n key,\n { type: value?.type, enum: value?.enum, description: value?.description?.slice?.(0, 160) },\n ]),\n ),\n };\n}\n\nexport function summarizeTools(tools: McpTool[]): unknown[] {\n return tools.map(summarizeTool);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport Handlebars from \"handlebars\";\nimport type { ProfileConfig } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nconst skillTemplate = `---\nname: {{skillName}}\ndescription: '{{description}}'\nargument-hint: 'MCP task, tool name, or args file to run'\n---\n\n# {{title}}\n\nThis skill wraps an MCP server as a lightweight, lazy-loaded, file-first workflow.\n\n## Why use this skill\n\n- Keep full MCP tool schemas out of the model context until needed.\n- Use brief tool summaries for everyday work.\n- Store large payloads in local files such as \\`args.json\\` or \\`content.md\\`.\n- Patch local artifacts and retry failed calls deterministically.\n\n## Commands\n\n\\`\\`\\`bash\ntoolcapsule tools {{profileName}} --brief\ntoolcapsule describe {{profileName}} <tool>\ntoolcapsule call {{profileName}} <tool> @args.json\ntoolcapsule retry <run-dir>\n\\`\\`\\`\n\n## Workflow\n\n1. Use \\`tools --brief\\` to find the likely tool.\n2. Use \\`describe <tool>\\` only when the brief schema is insufficient.\n3. Put complex arguments in a local JSON file.\n4. Call with \\`@args.json\\` and save the run.\n5. On failure, patch the local file and run \\`retry\\`.\n\n## Safety\n\n- Do not fabricate IDs, URLs, user IDs, or opaque tokens.\n- Prefer local files for large payloads.\n- Do not print secrets.\n- Review destructive tools before calling.\n`;\n\nexport type GenerateSkillOptions = {\n outputDir?: string;\n};\n\nexport async function generateSkill(profile: ProfileConfig, opts: GenerateSkillOptions = {}): Promise<string> {\n const skillName = profile.skill?.name || `${profile.name}-mcp`;\n const outputDir = opts.outputDir || join(\".github\", \"skills\", skillName);\n await mkdir(join(outputDir, \"scripts\"), { recursive: true });\n await mkdir(join(outputDir, \"runs\"), { recursive: true });\n\n const template = Handlebars.compile(skillTemplate);\n const description =\n profile.skill?.description ||\n `Use when operating tools from the ${profile.name} MCP server. Lazy-load schemas, call tools through local files, and retry failed calls by patching artifacts.`;\n const markdown = template({\n skillName,\n profileName: profile.name,\n title: `${profile.name} MCP Skill`,\n description: description.replace(/'/g, \"''\"),\n });\n await writeFile(join(outputDir, \"SKILL.md\"), markdown);\n await writeJson(join(outputDir, \"toolcapsule.config.json\"), profile);\n await writeFile(\n join(outputDir, \"scripts\", \"README.md\"),\n `# Scripts\\n\\nThis skill uses the project-level \\`toolcapsule\\` CLI.\\n`,\n );\n return outputDir;\n}\n\nexport async function writeProfile(path: string, profile: ProfileConfig): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeJson(path, profile);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nconst agentSkill = `---\nname: toolcapsule\ndescription: 'Use when: converting MCP servers into lightweight Agent Skills, installing ToolCapsule, lazy-loading MCP schemas, calling MCP tools through local args files, or using patch-and-retry workflows for heavy MCP tools.'\nargument-hint: 'MCP server URL/command, tool name, args file, or retry task'\n---\n\n# ToolCapsule Agent Skill\n\nUse ToolCapsule when an agent needs to work with heavy MCP servers without carrying every tool schema in the prompt.\n\n## Install\n\nIf \\`toolcapsule\\` or \\`tcap\\` is missing:\n\n\\`\\`\\`bash\nnpm install -g toolcapsule\n\\`\\`\\`\n\n## Core workflow\n\n1. Initialize a profile and generated Skill:\n\n\\`\\`\\`bash\ntcap init <name> --url <remote-mcp-url>\n# or\ntcap init <name> --command <stdio-command> --arg <arg>\n\\`\\`\\`\n\n2. Discover tools briefly:\n\n\\`\\`\\`bash\ntcap tools <name> --brief\n\\`\\`\\`\n\n3. Inspect one tool only when needed:\n\n\\`\\`\\`bash\ntcap schema <name> <tool>\n\\`\\`\\`\n\n4. Put complex arguments in a local JSON file.\n\n5. Call the MCP tool through the local args file:\n\n\\`\\`\\`bash\ntcap call <name> <tool> @args.json --save-run\n\\`\\`\\`\n\n6. If the call fails, patch the local file and retry:\n\n\\`\\`\\`bash\ntcap retry runs/<run-id>\n\\`\\`\\`\n\n## Safety\n\n- Do not print or commit private MCP URLs, tokens, API keys, user IDs, or document IDs.\n- Keep generated profiles and run artifacts local unless reviewed.\n- Use \\`TOOLCAPSULE_DEBUG=1\\` only when debugging; normal transport logs are quiet by default.\n- Prefer \\`--brief\\` and \\`schema\\` before reading full MCP schemas.\n\n## When to use\n\nUse ToolCapsule for MCP servers with:\n\n- many tools;\n- long schemas;\n- large Markdown/JSON payloads;\n- document, ticket, wiki, dashboard, or batch workflows;\n- failures that benefit from patching local artifacts instead of regenerating a full tool call.\n`;\n\nexport async function installAgentSkill(outputDir = \".github/skills/toolcapsule\"): Promise<string> {\n await mkdir(outputDir, { recursive: true });\n await writeFile(join(outputDir, \"SKILL.md\"), agentSkill);\n return outputDir;\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { RunRecord } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nexport function createRunId(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nexport async function saveRun(baseDir: string, record: RunRecord): Promise<string> {\n const dir = join(baseDir, record.id);\n await mkdir(dir, { recursive: true });\n await writeJson(join(dir, \"run.json\"), record);\n await writeJson(join(dir, \"request.json\"), record.request);\n if (record.response !== undefined) await writeJson(join(dir, \"response.json\"), record.response);\n if (record.error) await writeFile(join(dir, \"error.txt\"), record.error);\n await writeFile(join(dir, \"command.txt\"), `${record.command}\\n`);\n return dir;\n}\n\nexport async function loadRun(runDir: string): Promise<RunRecord> {\n return JSON.parse(await readFile(join(runDir, \"run.json\"), \"utf8\")) as RunRecord;\n}\n","export function roughTokens(value: unknown): number {\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return Math.ceil(text.length / 4);\n}\n\nexport function percentReduction(before: number, after: number): number {\n return before === 0 ? 0 : ((before - after) / before) * 100;\n}\n"],"mappings":"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","names":["readFile","join","resolve","mkdir","writeFile","dirname","join","join","mkdir","writeFile","dirname","mkdir","writeFile","join","mkdir","readFile","writeFile","join","join","mkdir","writeFile","readFile","writeFile","readFile","join","writeFile"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/mcp/client.ts","../src/mcp/importer.ts","../src/utils/fs.ts","../src/profile.ts","../src/schema/brief.ts","../src/schema/summarize.ts","../src/skill/generator.ts","../src/skill/installer.ts","../src/runs/recorder.ts","../src/utils/tokens.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { cac } from \"cac\";\nimport pc from \"picocolors\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { McpClient } from \"./mcp/client.js\";\nimport { discoverMcpServers, selectImportedServers } from \"./mcp/importer.js\";\nimport { loadProfile } from \"./profile.js\";\nimport { briefTools } from \"./schema/brief.js\";\nimport { summarizeTool, summarizeTools } from \"./schema/summarize.js\";\nimport { defaultSkillTarget, generateSkill, type SkillTarget, writeProfile } from \"./skill/generator.js\";\nimport { installAgentSkill } from \"./skill/installer.js\";\nimport { createRunId, loadRun, saveRun } from \"./runs/recorder.js\";\nimport { writeFile } from \"node:fs/promises\";\nimport { readJson } from \"./utils/fs.js\";\nimport { percentReduction, roughTokens } from \"./utils/tokens.js\";\n\nconst cli = cac(\"toolcapsule\");\n\nasync function readPackageVersion(): Promise<string> {\n try {\n const pkg = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst packageVersion = await readPackageVersion();\n\nasync function withClient<T>(profile: ProfileConfig, fn: (client: McpClient) => Promise<T>): Promise<T> {\n const client = new McpClient(profile, { clientVersion: packageVersion });\n try {\n await client.init();\n return await fn(client);\n } finally {\n await client.close();\n }\n}\n\nfunction readArgsPath(raw: string): string {\n return raw.startsWith(\"@\") ? raw.slice(1) : raw;\n}\n\nfunction readSkillTarget(raw: string | undefined): SkillTarget {\n const target = raw || defaultSkillTarget;\n if ([\"copilot\", \"claude\", \"opencode\", \"agents\", \"all\"].includes(target)) return target as SkillTarget;\n throw new Error(\"Invalid --target. Use one of: copilot, claude, opencode, agents, all\");\n}\n\ncli\n .command(\"init <name>\", \"Create a profile and generated Agent Skill for an MCP server\")\n .option(\"--url <url>\", \"Remote MCP URL\")\n .option(\"--command <command>\", \"stdio MCP command\")\n .option(\"--arg <arg>\", \"stdio MCP argument, repeatable\", { type: [String] })\n .option(\"--output <dir>\", \"Skill output directory\")\n .option(\"--target <target>\", \"Skill target: copilot, claude, opencode, agents, all\", { default: defaultSkillTarget })\n .action(async (name: string, options: { url?: string; command?: string; arg?: string[]; output?: string; target?: string }) => {\n if (!options.url && !options.command) throw new Error(\"Provide --url for remote MCP or --command for stdio MCP\");\n const profile: ProfileConfig = options.url\n ? { name, transport: { type: \"remote\", url: options.url } }\n : { name, transport: { type: \"stdio\", command: options.command!, args: options.arg ?? [] } };\n await writeProfile(join(\".toolcapsule\", \"profiles\", `${name}.json`), profile);\n const out = await generateSkill(profile, options.output ? { outputDir: options.output } : { target: readSkillTarget(options.target) });\n console.log(pc.green(`Created profile and skill at ${out}`));\n });\n\ncli\n .command(\"install-skill\", \"Install the generic ToolCapsule Agent Skill into this workspace\")\n .option(\"--output <dir>\", \"Skill output directory\")\n .option(\"--target <target>\", \"Skill target: copilot, claude, opencode, agents, all\", { default: defaultSkillTarget })\n .action(async (options: { output?: string; target?: string }) => {\n const out = await installAgentSkill(options.output, readSkillTarget(options.target));\n console.log(pc.green(`Installed ToolCapsule Agent Skill at ${out}`));\n });\n\ncli\n .command(\"import\", \"Import existing MCP configuration into ToolCapsule profiles and skills\")\n .option(\"--include-user\", \"Also inspect user-level MCP config files\")\n .option(\"--name <name>\", \"Import only one MCP server by name\")\n .option(\"--all\", \"Import all discovered MCP servers\")\n .option(\"--target <target>\", \"Skill target: copilot, claude, opencode, agents, all\", { default: defaultSkillTarget })\n .option(\"--dry-run\", \"List importable MCP servers without writing files\")\n .action(\n async (options: { includeUser?: boolean; name?: string; all?: boolean; target?: string; dryRun?: boolean }) => {\n const discovered = await discoverMcpServers(options.includeUser ? { includeUser: true } : {});\n if (discovered.length === 0) {\n console.log(\"No importable MCP servers found.\");\n return;\n }\n\n if (options.dryRun) {\n for (const server of discovered) {\n console.log(`${server.name}\\t${server.source.tool}\\t${server.source.path}`);\n for (const warning of server.warnings) console.log(pc.yellow(` warning: ${warning}`));\n }\n return;\n }\n\n const selected = selectImportedServers(discovered, options.name, options.all);\n if (selected.length === 0) {\n throw new Error(\"Multiple MCP servers found. Re-run with --dry-run, then pass --name <server> or --all.\");\n }\n\n for (const server of selected) {\n await writeProfile(join(\".toolcapsule\", \"profiles\", `${server.profile.name}.json`), server.profile);\n const out = await generateSkill(server.profile, { target: readSkillTarget(options.target) });\n console.log(pc.green(`Imported ${server.name} from ${server.source.path} -> ${out}`));\n for (const warning of server.warnings) console.log(pc.yellow(` warning: ${warning}`));\n }\n },\n );\n\ncli\n .command(\"tools <profile>\", \"List MCP tools\")\n .option(\"--brief\", \"Print compact tool summaries\")\n .option(\"--names\", \"Print tool names only\")\n .option(\"--json\", \"Print raw JSON\")\n .action(async (profileName: string, options: { brief?: boolean; names?: boolean; json?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n if (options.json) console.log(JSON.stringify(result, null, 2));\n else if (options.names) console.log(result.tools.map((tool) => tool.name).join(\"\\n\"));\n else console.log(briefTools(result.tools));\n });\n\ncli\n .command(\"describe <profile> <tool>\", \"Describe one MCP tool\")\n .option(\"--brief\", \"Print summarized schema instead of raw schema\")\n .action(async (profileName: string, toolName: string, options: { brief?: boolean }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const tool = result.tools.find((item) => item.name === toolName);\n if (!tool) throw new Error(`Tool not found: ${toolName}`);\n console.log(JSON.stringify(options.brief ? summarizeTool(tool) : tool, null, 2));\n });\n\ncli.command(\"schema <profile> <tool>\", \"Print a compact schema for one MCP tool\").action(async (profileName: string, toolName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const tool = result.tools.find((item) => item.name === toolName);\n if (!tool) throw new Error(`Tool not found: ${toolName}`);\n console.log(JSON.stringify(summarizeTool(tool), null, 2));\n});\n\ncli\n .command(\"call <profile> <tool> <args>\", \"Call an MCP tool with JSON args or @args.json\")\n .option(\"--save-run\", \"Save request, response, and command under runs/\")\n .action(async (profileName: string, toolName: string, argsRaw: string, options: { saveRun?: boolean }) => {\n const argsPath = readArgsPath(argsRaw);\n const toolArgs = argsRaw.startsWith(\"@\") ? await readJson(argsPath) : JSON.parse(argsRaw);\n const profile = await loadProfile(profileName);\n const request = { name: toolName, arguments: toolArgs };\n const command = `toolcapsule call ${profileName} ${toolName} ${argsRaw}`;\n const runId = createRunId();\n try {\n const response = await withClient(profile, (client) => client.callTool(toolName, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"success\",\n command,\n request,\n response,\n });\n console.error(pc.green(`Saved run: ${dir}`));\n }\n } catch (error) {\n if (options.saveRun) {\n const dir = await saveRun(\"runs\", {\n id: runId,\n createdAt: new Date().toISOString(),\n profile: profileName,\n tool: toolName,\n argsFile: argsPath,\n status: \"error\",\n command,\n request,\n error: error instanceof Error ? error.message : String(error),\n });\n console.error(pc.yellow(`Saved failed run: ${dir}`));\n }\n throw error;\n }\n });\n\ncli.command(\"retry <runDir>\", \"Retry a saved run, re-reading the args file\").action(async (runDir: string) => {\n const run = await loadRun(runDir);\n const toolArgs = await readJson(run.argsFile);\n const profile = await loadProfile(run.profile);\n const response = await withClient(profile, (client) => client.callTool(run.tool, toolArgs));\n console.log(JSON.stringify(response, null, 2));\n});\n\ncli.command(\"summarize <profile>\", \"Summarize all tools into compact JSON\").action(async (profileName: string) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n console.log(JSON.stringify(summarizeTools(result.tools), null, 2));\n});\n\ncli\n .command(\"benchmark <profile>\", \"Estimate schema savings for a profile\")\n .option(\"--markdown\", \"Print a Markdown report\")\n .option(\"--out <file>\", \"Write report to a file\")\n .action(async (profileName: string, options: { markdown?: boolean; out?: string }) => {\n const profile = await loadProfile(profileName);\n const result = await withClient(profile, (client) => client.listTools());\n const nativeTokens = roughTokens(result);\n const brief = summarizeTools(result.tools);\n const briefTokens = roughTokens(brief);\n const summary = {\n profile: profileName,\n tools: result.tools.length,\n nativeToolsRoughTokens: nativeTokens,\n summarizedToolsRoughTokens: briefTokens,\n estimatedReductionPct: Number(percentReduction(nativeTokens, briefTokens).toFixed(2)),\n };\n const output = options.markdown\n ? `# ToolCapsule benchmark: ${profileName}\\n\\n| Metric | Value |\\n|---|---:|\\n| MCP tools | ${summary.tools} |\\n| Native MCP schema rough tokens | ${summary.nativeToolsRoughTokens} |\\n| ToolCapsule summary rough tokens | ${summary.summarizedToolsRoughTokens} |\\n| Estimated reduction | ${summary.estimatedReductionPct}% |\\n\\n> Rough tokens are estimated from serialized schema length. Use this report to compare schema footprint before and after capsule summaries.\\n`\n : JSON.stringify(summary, null, 2);\n if (options.out) await writeFile(options.out, output);\n console.log(output);\n});\n\ncli.command(\"render-readme\", \"Print website hero copy snippets\").action(async () => {\n console.log(await readFile(new URL(\"../docs/hero-copy.md\", import.meta.url), \"utf8\"));\n});\n\ncli.help();\ncli.version(packageVersion);\n\ntry {\n cli.parse();\n} catch (error) {\n console.error(pc.red(error instanceof Error ? error.message : String(error)));\n process.exit(1);\n}\n","import { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport { resolve } from \"node:path\";\nimport type { JsonRpcMessage, ProfileConfig, ToolsListResult } from \"../types.js\";\n\nexport type McpClientOptions = {\n timeoutMs?: number;\n debug?: boolean;\n clientVersion?: string;\n};\n\nexport class McpClient {\n private child: ChildProcessWithoutNullStreams;\n private nextId = 1;\n private buffer = \"\";\n private pending = new Map<\n number,\n { resolve: (value: JsonRpcMessage) => void; reject: (error: Error) => void }\n >();\n private timeoutMs: number;\n private debug: boolean;\n private clientVersion: string;\n\n constructor(profile: ProfileConfig, opts: McpClientOptions = {}) {\n this.timeoutMs = opts.timeoutMs ?? Number(process.env.TOOLCAPSULE_TIMEOUT_MS || \"45000\");\n this.debug = opts.debug ?? process.env.TOOLCAPSULE_DEBUG === \"1\";\n this.clientVersion = opts.clientVersion ?? \"0.0.0\";\n if (profile.transport.type === \"remote\") {\n this.child = spawn(\"npx\", [\"-y\", \"mcp-remote\", profile.transport.url, ...headersToArgs(profile.transport.headers)], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...profile.transport.env },\n });\n } else {\n this.child = spawn(profile.transport.command, profile.transport.args ?? [], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...profile.transport.env },\n cwd: profile.transport.cwd ? resolve(profile.transport.cwd) : undefined,\n });\n }\n this.child.stdout.setEncoding(\"utf8\");\n this.child.stdout.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.stderr.on(\"data\", (chunk: Buffer) => this.onStderr(chunk));\n this.child.on(\"exit\", (code, signal) => {\n const error = new Error(`MCP process exited early (code=${code}, signal=${signal})`);\n for (const waiter of this.pending.values()) waiter.reject(error);\n this.pending.clear();\n });\n }\n\n private onStderr(chunk: Buffer): void {\n if (!this.debug) return;\n process.stderr.write(redactSecrets(chunk.toString(\"utf8\")));\n }\n\n private onStdout(chunk: string): void {\n this.buffer += chunk;\n let newlineIndex: number;\n while ((newlineIndex = this.buffer.indexOf(\"\\n\")) >= 0) {\n const line = this.buffer.slice(0, newlineIndex).trim();\n this.buffer = this.buffer.slice(newlineIndex + 1);\n if (!line) continue;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(line) as JsonRpcMessage;\n } catch {\n process.stderr.write(`${line}\\n`);\n continue;\n }\n if (typeof message.id === \"number\") {\n const waiter = this.pending.get(message.id);\n if (waiter) {\n this.pending.delete(message.id);\n waiter.resolve(message);\n }\n }\n }\n }\n\n async init(): Promise<void> {\n await this.request(\"initialize\", {\n protocolVersion: \"2025-03-26\",\n capabilities: {},\n clientInfo: { name: \"toolcapsule\", version: this.clientVersion },\n });\n this.notify(\"notifications/initialized\", {});\n }\n\n async request(method: string, params?: unknown): Promise<unknown> {\n const id = this.nextId++;\n const responsePromise = new Promise<JsonRpcMessage>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n });\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", id, method, params })}\\n`);\n const response = await this.withTimeout(responsePromise, method);\n if (response.error) throw new Error(`${method} failed: ${JSON.stringify(response.error, null, 2)}`);\n return response.result;\n }\n\n notify(method: string, params?: unknown): void {\n this.child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", method, params })}\\n`);\n }\n\n async listTools(): Promise<ToolsListResult> {\n return (await this.request(\"tools/list\", {})) as ToolsListResult;\n }\n\n async callTool(name: string, args: unknown): Promise<unknown> {\n return await this.request(\"tools/call\", { name, arguments: args });\n }\n\n async close(): Promise<void> {\n if (!this.child.killed) this.child.kill();\n if (this.child.exitCode === null) await Promise.race([once(this.child, \"exit\"), new Promise((resolve) => setTimeout(resolve, 500))]);\n }\n\n private async withTimeout<T>(promise: Promise<T>, label: string): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`${label} timed out after ${this.timeoutMs}ms`)), this.timeoutMs);\n });\n try {\n return await Promise.race([promise, timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n\nfunction headersToArgs(headers: Record<string, string> | undefined): string[] {\n if (!headers) return [];\n return Object.entries(headers).flatMap(([key, value]) => [\"--header\", `${key}:${value}`]);\n}\n\nfunction redactSecrets(text: string): string {\n return text\n .replace(/https:\\/\\/mcp\\.feishu\\.cn\\/mcp\\/[^\\s\"']+/g, \"https://mcp.feishu.cn/mcp/[redacted]\")\n .replace(/(Bearer\\s+)[A-Za-z0-9._~+/=-]+/gi, \"$1[redacted]\")\n .replace(/(token=)[^\\s&\"']+/gi, \"$1[redacted]\")\n .replace(/(api[_-]?key=)[^\\s&\"']+/gi, \"$1[redacted]\");\n}\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ProfileConfig, TransportConfig } from \"../types.js\";\nimport { readJson } from \"../utils/fs.js\";\n\nexport type McpConfigSource = {\n tool: \"vscode\" | \"claude\" | \"opencode\" | \"gemini\" | \"cursor\" | \"generic\";\n path: string;\n userLevel?: boolean;\n};\n\nexport type ImportedMcpServer = {\n name: string;\n source: McpConfigSource;\n profile: ProfileConfig;\n warnings: string[];\n};\n\ntype ServerRecord = Record<string, unknown>;\n\nconst workspaceSources: McpConfigSource[] = [\n { tool: \"vscode\", path: join(\".vscode\", \"mcp.json\") },\n { tool: \"claude\", path: \".mcp.json\" },\n { tool: \"opencode\", path: \"opencode.json\" },\n { tool: \"gemini\", path: join(\".gemini\", \"settings.json\") },\n { tool: \"cursor\", path: join(\".cursor\", \"mcp.json\") },\n];\n\nconst userSources: McpConfigSource[] = [\n { tool: \"claude\", path: join(homedir(), \".claude.json\"), userLevel: true },\n { tool: \"opencode\", path: join(homedir(), \".config\", \"opencode\", \"opencode.json\"), userLevel: true },\n { tool: \"gemini\", path: join(homedir(), \".gemini\", \"settings.json\"), userLevel: true },\n];\n\nfunction asRecord(value: unknown): ServerRecord | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as ServerRecord) : undefined;\n}\n\nfunction stringArray(value: unknown): string[] | undefined {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\") ? value : undefined;\n}\n\nfunction stringRecord(value: unknown): Record<string, string> | undefined {\n const record = asRecord(value);\n if (!record) return undefined;\n const entries = Object.entries(record).filter((entry): entry is [string, string] => typeof entry[1] === \"string\");\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nfunction cleanName(name: string): string {\n const cleaned = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\");\n return cleaned || \"imported-mcp\";\n}\n\nfunction mapServer(name: string, source: McpConfigSource, raw: unknown): ImportedMcpServer | undefined {\n const server = asRecord(raw);\n if (!server) return undefined;\n\n const warnings: string[] = [];\n let transport: TransportConfig | undefined;\n const type = typeof server.type === \"string\" ? server.type : undefined;\n const url = typeof server.url === \"string\" ? server.url : typeof server.httpUrl === \"string\" ? server.httpUrl : undefined;\n const commandValue = server.command;\n const headers = stringRecord(server.headers);\n const env = stringRecord(server.env) ?? stringRecord(server.environment);\n const cwd = typeof server.cwd === \"string\" ? server.cwd : undefined;\n\n if (url && (!type || [\"http\", \"streamable-http\", \"remote\", \"sse\", \"ws\"].includes(type))) {\n transport = { type: \"remote\", url, ...(headers ? { headers } : {}), ...(env ? { env } : {}) };\n if (type === \"sse\" || type === \"ws\") warnings.push(`Imported ${type} server as remote URL; verify transport compatibility.`);\n } else if (typeof commandValue === \"string\") {\n transport = {\n type: \"stdio\",\n command: commandValue,\n args: stringArray(server.args) ?? [],\n ...(env ? { env } : {}),\n ...(cwd ? { cwd } : {}),\n };\n } else if (Array.isArray(commandValue) && commandValue.every((item) => typeof item === \"string\") && commandValue.length > 0) {\n transport = { type: \"stdio\", command: commandValue[0]!, args: commandValue.slice(1), ...(env ? { env } : {}), ...(cwd ? { cwd } : {}) };\n }\n\n if (!transport) return undefined;\n if (server.headers && !headers) warnings.push(\"Headers were present but not copied because they were not string values.\");\n if ((server.env || server.environment) && !env) warnings.push(\"Environment variables were present but not copied because they were not string values.\");\n if (server.includeTools || server.excludeTools) warnings.push(\"Tool include/exclude filters were not copied; use ToolCapsule brief/schema commands to choose tools manually.\");\n\n const profileName = cleanName(name);\n return {\n name: profileName,\n source,\n warnings,\n profile: {\n name: profileName,\n transport,\n },\n };\n}\n\nfunction serverEntries(source: McpConfigSource, config: ServerRecord): Array<[string, unknown]> {\n if (source.tool === \"vscode\" || source.tool === \"cursor\") return Object.entries(asRecord(config.servers) ?? {});\n if (source.tool === \"opencode\") return Object.entries(asRecord(config.mcp) ?? {});\n if (source.tool === \"claude\") {\n const projectEntries = Object.entries(asRecord(config.mcpServers) ?? {});\n const projectConfigs = Object.entries(asRecord(config.projects) ?? {}).flatMap(([, project]) =>\n Object.entries(asRecord(asRecord(project)?.mcpServers) ?? {}),\n );\n return [...projectEntries, ...projectConfigs];\n }\n return Object.entries(asRecord(config.mcpServers) ?? {});\n}\n\nexport async function discoverMcpServers(opts: { includeUser?: boolean } = {}): Promise<ImportedMcpServer[]> {\n const sources = opts.includeUser ? [...workspaceSources, ...userSources] : workspaceSources;\n const discovered: ImportedMcpServer[] = [];\n\n for (const source of sources) {\n if (!existsSync(source.path)) continue;\n const config = asRecord(await readJson(source.path));\n if (!config) continue;\n for (const [name, raw] of serverEntries(source, config)) {\n const imported = mapServer(name, source, raw);\n if (imported) discovered.push(imported);\n }\n }\n\n return discovered;\n}\n\nexport function selectImportedServers(servers: ImportedMcpServer[], name?: string, all?: boolean): ImportedMcpServer[] {\n if (all) return servers;\n if (!name) return servers.length === 1 ? servers : [];\n const normalized = cleanName(name);\n return servers.filter((server) => server.name === normalized || server.name === name);\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readFile(path, \"utf8\")) as T;\n}\n\nexport async function writeJson(path: string, value: unknown): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport async function writeText(path: string, value: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, value);\n}\n\nexport function abs(path: string): string {\n return resolve(process.cwd(), path);\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ProfileConfig } from \"./types.js\";\nimport { readJson } from \"./utils/fs.js\";\n\nconst profileSchema = z.object({\n name: z.string().min(1),\n transport: z.union([\n z.object({\n type: z.literal(\"remote\"),\n url: z.string().url(),\n headers: z.record(z.string()).optional(),\n env: z.record(z.string()).optional(),\n }),\n z.object({\n type: z.literal(\"stdio\"),\n command: z.string().min(1),\n args: z.array(z.string()).optional(),\n env: z.record(z.string()).optional(),\n cwd: z.string().optional(),\n }),\n ]),\n skill: z\n .object({\n name: z.string().optional(),\n description: z.string().optional(),\n })\n .optional(),\n shortcuts: z\n .record(\n z.object({\n tool: z.string(),\n description: z.string().optional(),\n args: z.record(z.enum([\"string\", \"file\", \"json\", \"boolean\", \"number\"])).optional(),\n }),\n )\n .optional(),\n});\n\nexport async function loadProfile(profilePathOrName: string): Promise<ProfileConfig> {\n const candidates = [\n profilePathOrName,\n `${profilePathOrName}.json`,\n join(\".toolcapsule\", \"profiles\", `${profilePathOrName}.json`),\n join(\".github\", \"skills\", `${profilePathOrName}-mcp`, \"toolcapsule.config.json\"),\n ];\n const found = candidates.find((path) => existsSync(path));\n if (!found) throw new Error(`Profile not found: ${profilePathOrName}`);\n return profileSchema.parse(await readJson(found)) as ProfileConfig;\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function toolTitle(tool: McpTool): string {\n return tool.annotations?.title || tool.description?.split(/\\n/)[0]?.slice(0, 100) || tool.name;\n}\n\nexport function briefTool(tool: McpTool): string {\n const schema = tool.inputSchema as any;\n const required = Array.isArray(schema?.required) ? schema.required : [];\n const properties = schema?.properties && typeof schema.properties === \"object\" ? Object.keys(schema.properties) : [];\n const flags = [\n tool.annotations?.readOnlyHint ? \"read-only\" : undefined,\n tool.annotations?.destructiveHint ? \"destructive\" : undefined,\n ].filter(Boolean);\n return `- ${tool.name}: ${toolTitle(tool)}${flags.length ? ` [${flags.join(\", \")}]` : \"\"}${required.length ? ` required=${required.join(\"|\")}` : \"\"}${properties.length ? ` args=${properties.slice(0, 12).join(\"|\")}` : \"\"}`;\n}\n\nexport function briefTools(tools: McpTool[]): string {\n return tools.map(briefTool).join(\"\\n\");\n}\n","import type { McpTool } from \"../types.js\";\n\nexport function summarizeTool(tool: McpTool): unknown {\n const schema = tool.inputSchema as any;\n const properties = schema?.properties && typeof schema.properties === \"object\" ? schema.properties : {};\n return {\n name: tool.name,\n title: tool.annotations?.title,\n description: tool.description?.split(/\\n\\n/)[0]?.slice(0, 500),\n annotations: tool.annotations,\n required: Array.isArray(schema?.required) ? schema.required : [],\n arguments: Object.fromEntries(\n Object.entries(properties).map(([key, value]: [string, any]) => [\n key,\n { type: value?.type, enum: value?.enum, description: value?.description?.slice?.(0, 160) },\n ]),\n ),\n };\n}\n\nexport function summarizeTools(tools: McpTool[]): unknown[] {\n return tools.map(summarizeTool);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport Handlebars from \"handlebars\";\nimport type { ProfileConfig } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nexport type SkillTarget = \"copilot\" | \"claude\" | \"opencode\" | \"agents\" | \"all\";\n\nexport const defaultSkillTarget: SkillTarget = \"claude\";\n\nexport function skillOutputDir(skillName: string, target: Exclude<SkillTarget, \"all\">): string {\n if (target === \"copilot\") return join(\".github\", \"skills\", skillName);\n if (target === \"claude\") return join(\".claude\", \"skills\", skillName);\n if (target === \"opencode\") return join(\".opencode\", \"skills\", skillName);\n return join(\".agents\", \"skills\", skillName);\n}\n\nexport function expandSkillTargets(target: SkillTarget): Exclude<SkillTarget, \"all\">[] {\n return target === \"all\" ? [\"copilot\", \"claude\", \"opencode\", \"agents\"] : [target];\n}\n\nconst skillTemplate = `---\nname: {{skillName}}\ndescription: '{{description}}'\nargument-hint: 'MCP task, tool name, or args file to run'\n---\n\n# {{title}}\n\nThis skill wraps an MCP server as a lightweight, lazy-loaded, file-first workflow.\n\n## Why use this skill\n\n- Keep full MCP tool schemas out of the model context until needed.\n- Use brief tool summaries for everyday work.\n- Store large payloads in local files such as \\`args.json\\` or \\`content.md\\`.\n- Patch local artifacts and retry failed calls deterministically.\n\n## Commands\n\n\\`\\`\\`bash\ntoolcapsule tools {{profileName}} --brief\ntoolcapsule describe {{profileName}} <tool>\ntoolcapsule call {{profileName}} <tool> @args.json\ntoolcapsule retry <run-dir>\n\\`\\`\\`\n\n## Workflow\n\n1. Use \\`tools --brief\\` to find the likely tool.\n2. Use \\`describe <tool>\\` only when the brief schema is insufficient.\n3. Put complex arguments in a local JSON file.\n4. Call with \\`@args.json\\` and save the run.\n5. On failure, patch the local file and run \\`retry\\`.\n\n## Safety\n\n- Do not fabricate IDs, URLs, user IDs, or opaque tokens.\n- Prefer local files for large payloads.\n- Do not print secrets.\n- Review destructive tools before calling.\n`;\n\nexport type GenerateSkillOptions = {\n outputDir?: string;\n target?: SkillTarget;\n};\n\nasync function generateSkillAt(profile: ProfileConfig, outputDir: string): Promise<string> {\n const skillName = profile.skill?.name || `${profile.name}-mcp`;\n await mkdir(join(outputDir, \"scripts\"), { recursive: true });\n await mkdir(join(outputDir, \"runs\"), { recursive: true });\n\n const template = Handlebars.compile(skillTemplate);\n const description =\n profile.skill?.description ||\n `Use when operating tools from the ${profile.name} MCP server. Lazy-load schemas, call tools through local files, and retry failed calls by patching artifacts.`;\n const markdown = template({\n skillName,\n profileName: profile.name,\n title: `${profile.name} MCP Skill`,\n description: description.replace(/'/g, \"''\"),\n });\n await writeFile(join(outputDir, \"SKILL.md\"), markdown);\n await writeJson(join(outputDir, \"toolcapsule.config.json\"), profile);\n await writeFile(\n join(outputDir, \"scripts\", \"README.md\"),\n `# Scripts\\n\\nThis skill uses the project-level \\`toolcapsule\\` CLI.\\n`,\n );\n return outputDir;\n}\n\nexport async function generateSkill(profile: ProfileConfig, opts: GenerateSkillOptions = {}): Promise<string> {\n const skillName = profile.skill?.name || `${profile.name}-mcp`;\n const target = opts.target || defaultSkillTarget;\n const outputs = opts.outputDir\n ? [await generateSkillAt(profile, opts.outputDir)]\n : await Promise.all(expandSkillTargets(target).map((item) => generateSkillAt(profile, skillOutputDir(skillName, item))));\n return outputs.join(\", \");\n}\n\nexport async function writeProfile(path: string, profile: ProfileConfig): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeJson(path, profile);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { defaultSkillTarget, expandSkillTargets, skillOutputDir, type SkillTarget } from \"./generator.js\";\n\nconst agentSkill = `---\nname: toolcapsule\ndescription: 'Use when: converting MCP servers into lightweight Agent Skills, installing ToolCapsule, lazy-loading MCP schemas, calling MCP tools through local args files, or using patch-and-retry workflows for heavy MCP tools.'\nargument-hint: 'MCP server URL/command, tool name, args file, or retry task'\n---\n\n# ToolCapsule Agent Skill\n\nUse ToolCapsule when an agent needs to work with heavy MCP servers without carrying every tool schema in the prompt.\n\n## Install\n\nIf \\`toolcapsule\\` or \\`tcap\\` is missing:\n\n\\`\\`\\`bash\nnpm install -g toolcapsule\n\\`\\`\\`\n\n## Core workflow\n\n1. Initialize a profile and generated Skill:\n\n\\`\\`\\`bash\ntcap init <name> --url <remote-mcp-url>\n# or\ntcap init <name> --command <stdio-command> --arg <arg>\n\\`\\`\\`\n\n2. Discover tools briefly:\n\n\\`\\`\\`bash\ntcap tools <name> --brief\n\\`\\`\\`\n\n3. Inspect one tool only when needed:\n\n\\`\\`\\`bash\ntcap schema <name> <tool>\n\\`\\`\\`\n\n4. Put complex arguments in a local JSON file.\n\n5. Call the MCP tool through the local args file:\n\n\\`\\`\\`bash\ntcap call <name> <tool> @args.json --save-run\n\\`\\`\\`\n\n6. If the call fails, patch the local file and retry:\n\n\\`\\`\\`bash\ntcap retry runs/<run-id>\n\\`\\`\\`\n\n## Safety\n\n- Do not print or commit private MCP URLs, tokens, API keys, user IDs, or document IDs.\n- Keep generated profiles and run artifacts local unless reviewed.\n- Use \\`TOOLCAPSULE_DEBUG=1\\` only when debugging; normal transport logs are quiet by default.\n- Prefer \\`--brief\\` and \\`schema\\` before reading full MCP schemas.\n\n## When to use\n\nUse ToolCapsule for MCP servers with:\n\n- many tools;\n- long schemas;\n- large Markdown/JSON payloads;\n- document, ticket, wiki, dashboard, or batch workflows;\n- failures that benefit from patching local artifacts instead of regenerating a full tool call.\n`;\n\nexport async function installAgentSkill(outputDir?: string, target: SkillTarget = defaultSkillTarget): Promise<string> {\n const outputDirs = outputDir ? [outputDir] : expandSkillTargets(target).map((item) => skillOutputDir(\"toolcapsule\", item));\n await Promise.all(\n outputDirs.map(async (dir) => {\n await mkdir(dir, { recursive: true });\n await writeFile(join(dir, \"SKILL.md\"), agentSkill);\n }),\n );\n return outputDirs.join(\", \");\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { RunRecord } from \"../types.js\";\nimport { writeJson } from \"../utils/fs.js\";\n\nexport function createRunId(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nexport async function saveRun(baseDir: string, record: RunRecord): Promise<string> {\n const dir = join(baseDir, record.id);\n await mkdir(dir, { recursive: true });\n await writeJson(join(dir, \"run.json\"), record);\n await writeJson(join(dir, \"request.json\"), record.request);\n if (record.response !== undefined) await writeJson(join(dir, \"response.json\"), record.response);\n if (record.error) await writeFile(join(dir, \"error.txt\"), record.error);\n await writeFile(join(dir, \"command.txt\"), `${record.command}\\n`);\n return dir;\n}\n\nexport async function loadRun(runDir: string): Promise<RunRecord> {\n return JSON.parse(await readFile(join(runDir, \"run.json\"), \"utf8\")) as RunRecord;\n}\n","export function roughTokens(value: unknown): number {\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return Math.ceil(text.length / 4);\n}\n\nexport function percentReduction(before: number, after: number): number {\n return before === 0 ? 0 : ((before - after) / before) * 100;\n}\n"],"mappings":"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","names":["readFile","join","resolve","resolve","existsSync","join","join","existsSync","mkdir","writeFile","dirname","join","join","mkdir","writeFile","dirname","mkdir","writeFile","join","mkdir","writeFile","join","mkdir","readFile","writeFile","join","join","mkdir","writeFile","readFile","writeFile","readFile","join","writeFile"]}
@@ -0,0 +1,181 @@
1
+ # Agent tool compatibility research
2
+
3
+ This note summarizes how current coding agents discover Agent Skills, instructions, and MCP server configuration, and what that means for ToolCapsule onboarding.
4
+
5
+ Research date: 2026-06-06.
6
+
7
+ ## Current repository state
8
+
9
+ - There is no `.agents/` directory in this repository today.
10
+ - The only checked-in skill is `.github/skills/toolcapsule-release/SKILL.md`.
11
+ - That skill is valid for GitHub Copilot / VS Code discovery because:
12
+ - it lives under `.github/skills/<name>/SKILL.md`;
13
+ - the `name` field is `toolcapsule-release`;
14
+ - the parent directory name is also `toolcapsule-release`;
15
+ - the name uses lowercase letters and hyphens only.
16
+ - It is not project-discoverable by Claude Code, because Claude Code loads project skills from `.claude/skills/<name>/SKILL.md`.
17
+ - It is not project-discoverable by OpenCode unless duplicated or generated under `.opencode/skills/<name>/SKILL.md`, `.claude/skills/<name>/SKILL.md`, or `.agents/skills/<name>/SKILL.md`.
18
+
19
+ ## Compatibility matrix
20
+
21
+ | Tool | Skills support | Project skill locations | User skill locations | MCP config locations | Notes |
22
+ |---|---|---|---|---|---|
23
+ | GitHub Copilot in VS Code | Native Agent Skills | `.github/skills/`, `.claude/skills/`, `.agents/skills/` | `~/.copilot/skills/`, `~/.claude/skills/`, `~/.agents/skills/` | Workspace `.vscode/mcp.json`; user profile `mcp.json` | Skill `name` must match folder name and use lowercase/kebab-case. MCP config uses top-level `servers`. |
24
+ | Claude Code | Native Skills | `.claude/skills/<name>/SKILL.md` | `~/.claude/skills/<name>/SKILL.md` | Project `.mcp.json`; local/user in `~/.claude.json`; managed `/etc/claude-code/managed-mcp.json` on Linux | Skills follow Agent Skills standard with Claude extensions. Claude can import from Claude Desktop and can load Claude.ai connectors. |
25
+ | OpenCode | Native Skills | `.opencode/skills/`, `.claude/skills/`, `.agents/skills/` | `~/.config/opencode/skills/`, `~/.claude/skills/`, `~/.agents/skills/` | Project `opencode.json`; global `~/.config/opencode/opencode.json`; auth `~/.local/share/opencode/mcp-auth.json` | OpenCode explicitly supports `.agents/skills` compatibility. Unknown skill frontmatter is ignored; `name` and `description` are required. |
26
+ | Cursor | Rules, not Agent Skills | `.cursor/rules/*.mdc`, `AGENTS.md` | Cursor Settings → Rules | Commonly `.cursor/mcp.json` / Cursor settings depending version | Cursor rules are always/auto/manual context, not skill tool loading. Convert ToolCapsule guidance to `.mdc` or `AGENTS.md` for best results. |
27
+ | Gemini CLI | MCP and memory/instructions, no Agent Skills equivalent in core docs | `.gemini/settings.json`, `GEMINI.md` conventions | `~/.gemini/settings.json`, `~/.gemini/GEMINI.md` | `mcpServers` in `settings.json`; tokens `~/.gemini/mcp-oauth-tokens.json`; enablement `~/.gemini/mcp-server-enablement.json` | Supports MCP tools/resources/prompts and tool filtering. Tool names are namespaced as `mcp_<server>_<tool>`. |
28
+ | Aider | Conventions/read-only files, no MCP/Skills workflow layer | Project convention files loaded via `--read` or `.aider.conf.yml` | User config | No native MCP emphasis in current docs reviewed | Best integration is an instruction/conventions file that tells Aider to run `tcap`. |
29
+
30
+ ## `.agents/` directory usage
31
+
32
+ `.agents/skills/<name>/SKILL.md` is now useful as a cross-agent compatibility path:
33
+
34
+ - GitHub Copilot in VS Code discovers project skills from `.agents/skills/`.
35
+ - OpenCode discovers project skills from `.agents/skills/`.
36
+ - Some tools also use `AGENTS.md` as a shared always-on instruction file.
37
+
38
+ However, Claude Code does not list `.agents/skills/` as a native project skill location. For Claude Code users, generate or copy to `.claude/skills/`.
39
+
40
+ Implemented ToolCapsule output strategy:
41
+
42
+ 1. Default to `.claude/skills/<name>/` because Claude Code is currently the most complete native Agent Skills host.
43
+ 2. Support `--target` options:
44
+ - `--target copilot` → `.github/skills/<name>/`
45
+ - `--target claude` → `.claude/skills/<name>/`
46
+ - `--target opencode` → `.opencode/skills/<name>/`
47
+ - `--target agents` → `.agents/skills/<name>/`
48
+ - `--target all` → write multiple compatible copies.
49
+ 3. Ensure `name` always equals directory basename and matches `^[a-z0-9]+(-[a-z0-9]+)*$`.
50
+
51
+ ## Can ToolCapsule read existing MCP registrations?
52
+
53
+ Yes. A strong onboarding flow is to import existing MCP config files and convert each server entry into a ToolCapsule profile plus skill.
54
+
55
+ Initial support exists through:
56
+
57
+ ```bash
58
+ tcap import --dry-run
59
+ tcap import --name <server> --target claude
60
+ tcap import --all --target all
61
+ ```
62
+
63
+ The importer currently reads workspace MCP config by default and only reads user-level config with `--include-user`.
64
+
65
+ Recommended discovery order:
66
+
67
+ 1. Workspace-local, because it is most relevant and safest to share:
68
+ - `.vscode/mcp.json` for VS Code / Copilot.
69
+ - `.mcp.json` for Claude Code project MCP.
70
+ - `opencode.json` for OpenCode project MCP.
71
+ - `.gemini/settings.json` for Gemini CLI project MCP.
72
+ - `.cursor/mcp.json` when present.
73
+ 2. User-level, only after explicit user consent because it may contain private endpoints or env references:
74
+ - VS Code user profile `mcp.json`.
75
+ - `~/.claude.json`.
76
+ - `~/.config/opencode/opencode.json`.
77
+ - `~/.gemini/settings.json`.
78
+ - Cursor user settings.
79
+ 3. Managed/enterprise config should be read-only and policy-aware:
80
+ - Claude Code managed MCP on Linux: `/etc/claude-code/managed-mcp.json`.
81
+ - VS Code/GitHub enterprise policies may restrict MCP access.
82
+
83
+ Important: importing config is different from importing credentials. ToolCapsule should preserve environment variable references such as `${TOKEN}`, `$TOKEN`, and `{env:TOKEN}` instead of resolving or printing secrets.
84
+
85
+ ## Format mapping hints
86
+
87
+ ### VS Code / Copilot
88
+
89
+ ```json
90
+ {
91
+ "servers": {
92
+ "github": {
93
+ "type": "http",
94
+ "url": "https://api.githubcopilot.com/mcp"
95
+ },
96
+ "playwright": {
97
+ "command": "npx",
98
+ "args": ["-y", "@microsoft/mcp-server-playwright"]
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Map to ToolCapsule:
105
+
106
+ - `type: "http"` or URL entry → `transport.type = "remote"`, `url`.
107
+ - `command` + `args` → `transport.type = "stdio"`, `command`, `args`.
108
+
109
+ ### Claude Code / generic MCP
110
+
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "notion": {
115
+ "type": "http",
116
+ "url": "https://mcp.notion.com/mcp"
117
+ },
118
+ "local-db": {
119
+ "type": "stdio",
120
+ "command": "npx",
121
+ "args": ["-y", "@example/db-mcp"]
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ Map `http`, `streamable-http`, `sse`, and `ws` remote-style transports carefully. ToolCapsule currently supports remote URL and stdio, so SSE/WebSocket may need validation or future support.
128
+
129
+ ### OpenCode
130
+
131
+ ```json
132
+ {
133
+ "mcp": {
134
+ "context7": {
135
+ "type": "remote",
136
+ "url": "https://mcp.context7.com/mcp"
137
+ },
138
+ "local": {
139
+ "type": "local",
140
+ "command": ["npx", "-y", "@modelcontextprotocol/server-everything"]
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ Map `remote` to ToolCapsule remote. Map `local.command` array by splitting the first element as `command` and remaining elements as `args`.
147
+
148
+ ### Gemini CLI
149
+
150
+ ```json
151
+ {
152
+ "mcpServers": {
153
+ "httpServer": {
154
+ "httpUrl": "https://api.example.com/mcp",
155
+ "headers": {
156
+ "Authorization": "Bearer $TOKEN"
157
+ }
158
+ },
159
+ "pythonTools": {
160
+ "command": "python",
161
+ "args": ["-m", "my_mcp_server"]
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ Map `httpUrl` or `url` to remote URL. Preserve `headers` as future profile metadata if ToolCapsule adds header support.
168
+
169
+ ## Product recommendation
170
+
171
+ Add a visible onboarding button to the homepage: “Give this to your AI”. The modal should contain a copyable instruction block that asks the user’s current coding agent to:
172
+
173
+ 1. Inspect local MCP config files.
174
+ 2. Identify configured MCP servers without exposing secrets.
175
+ 3. Install ToolCapsule if missing.
176
+ 4. Run `tcap init` for the selected server.
177
+ 5. Generate the right skill target for the current agent.
178
+ 6. Use `tcap tools <profile> --brief` and `tcap schema <profile> <tool>` before making calls.
179
+ 7. Keep payloads in files and use `--save-run` plus `tcap retry`.
180
+
181
+ This lets users with existing MCP setup onboard without manually retyping server URLs or commands.
@@ -2,6 +2,7 @@
2
2
 
3
3
  ```mermaid
4
4
  flowchart TD
5
+ Z[Existing MCP config files] --> B[toolcapsule CLI]
5
6
  A[Remote or stdio MCP server] --> B[toolcapsule CLI]
6
7
  B --> C[Tool cache and brief schema]
7
8
  B --> D[Generated SKILL.md]
@@ -13,6 +14,17 @@ flowchart TD
13
14
 
14
15
  - `McpClient`: speaks JSON-RPC over stdio, using `mcp-remote` for remote endpoints.
15
16
  - `Profile`: stores transport and shortcut configuration.
17
+ - `MCP importer`: reads existing MCP registrations from common agent config files and converts them into ToolCapsule profiles.
16
18
  - `Skill generator`: writes `SKILL.md` and profile config.
17
19
  - `Run recorder`: stores request, response, command, and error files.
18
20
  - `Schema helpers`: produce compact tool summaries.
21
+
22
+ ## Import path
23
+
24
+ ```text
25
+ .mcp.json / .vscode/mcp.json / opencode.json / .gemini/settings.json / .cursor/mcp.json
26
+ → .toolcapsule/profiles/<server>.json
27
+ → .claude/skills/<server>-mcp/SKILL.md by default
28
+ ```
29
+
30
+ The importer preserves string-valued `headers`, `env` / `environment`, and `cwd` fields where possible. User-level config is intentionally opt-in through `--include-user` because it can contain private endpoints or credential references.
package/docs/concept.md CHANGED
@@ -10,6 +10,25 @@ MCP connects agents to tools. Skills package repeatable agent workflows. ToolCap
10
10
  MCP tool schema → compact Skill guidance → local capsule files → auditable run → patch and retry
11
11
  ```
12
12
 
13
+ ## Fast import
14
+
15
+ ToolCapsule can start from MCP servers users already registered in their coding tools:
16
+
17
+ ```bash
18
+ tcap import --dry-run
19
+ tcap import --name <server> --target claude
20
+ ```
21
+
22
+ The import flow reads common workspace MCP config files, creates a ToolCapsule profile, and writes an Agent Skill for the selected target. This makes onboarding a conversion step instead of a manual reconfiguration step.
23
+
24
+ Supported target outputs:
25
+
26
+ - `claude` → `.claude/skills/` (default)
27
+ - `copilot` → `.github/skills/`
28
+ - `opencode` → `.opencode/skills/`
29
+ - `agents` → `.agents/skills/`
30
+ - `all` → all compatible locations
31
+
13
32
  ## Principles
14
33
 
15
34
  1. Brief by default, full MCP schema on demand.
@@ -0,0 +1,123 @@
1
+ # Import existing MCP configuration
2
+
3
+ ToolCapsule can convert MCP servers already configured in other coding tools into ToolCapsule profiles and Agent Skills.
4
+
5
+ This is the fastest onboarding path when a user already has MCP working in Claude Code, GitHub Copilot / VS Code, OpenCode, Gemini CLI, or Cursor.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ tcap import --dry-run
11
+ tcap import --name <server> --target claude
12
+ ```
13
+
14
+ To write skills for multiple agents:
15
+
16
+ ```bash
17
+ tcap import --name <server> --target all
18
+ ```
19
+
20
+ ## What gets created
21
+
22
+ For an imported server named `github`, ToolCapsule writes:
23
+
24
+ ```text
25
+ .toolcapsule/profiles/github.json
26
+ .claude/skills/github-mcp/SKILL.md
27
+ ```
28
+
29
+ `claude` is the default target. Other targets:
30
+
31
+ | Target | Output |
32
+ |---|---|
33
+ | `claude` | `.claude/skills/<name>-mcp/` |
34
+ | `copilot` | `.github/skills/<name>-mcp/` |
35
+ | `opencode` | `.opencode/skills/<name>-mcp/` |
36
+ | `agents` | `.agents/skills/<name>-mcp/` |
37
+ | `all` | all of the above |
38
+
39
+ ## Config files scanned
40
+
41
+ Workspace config is scanned by default:
42
+
43
+ | Tool | File |
44
+ |---|---|
45
+ | GitHub Copilot / VS Code | `.vscode/mcp.json` |
46
+ | Claude Code | `.mcp.json` |
47
+ | OpenCode | `opencode.json` |
48
+ | Gemini CLI | `.gemini/settings.json` |
49
+ | Cursor | `.cursor/mcp.json` |
50
+
51
+ User-level config can contain private endpoints or credential references, so it is opt-in:
52
+
53
+ ```bash
54
+ tcap import --include-user --dry-run
55
+ ```
56
+
57
+ Currently checked user-level files:
58
+
59
+ | Tool | File |
60
+ |---|---|
61
+ | Claude Code | `~/.claude.json` |
62
+ | OpenCode | `~/.config/opencode/opencode.json` |
63
+ | Gemini CLI | `~/.gemini/settings.json` |
64
+
65
+ ## What is preserved
66
+
67
+ ToolCapsule imports:
68
+
69
+ - remote MCP URLs;
70
+ - stdio commands and args;
71
+ - string-valued `headers`;
72
+ - string-valued `env` or `environment`;
73
+ - stdio `cwd`.
74
+
75
+ Secrets should stay as environment variable references where possible, for example:
76
+
77
+ ```json
78
+ {
79
+ "headers": {
80
+ "Authorization": "Bearer ${GITHUB_TOKEN}"
81
+ }
82
+ }
83
+ ```
84
+
85
+ ToolCapsule stores the reference, not the resolved secret value.
86
+
87
+ ## Safety notes
88
+
89
+ - Run `--dry-run` first.
90
+ - Use `--include-user` only when you want ToolCapsule to inspect personal MCP config.
91
+ - Do not commit generated profiles if they contain private endpoints, document IDs, tokens, or machine-specific paths.
92
+ - Keep `.toolcapsule/`, generated skills, and run artifacts under review before sharing.
93
+ - Transport logs are quiet by default. Set `TOOLCAPSULE_DEBUG=1` only when debugging.
94
+
95
+ ## Example flows
96
+
97
+ ### Import one server for Claude Code
98
+
99
+ ```bash
100
+ tcap import --dry-run
101
+ tcap import --name notion --target claude
102
+ tcap tools notion --brief
103
+ ```
104
+
105
+ ### Import all workspace MCP servers for multiple agents
106
+
107
+ ```bash
108
+ tcap import --all --target all
109
+ ```
110
+
111
+ ### Use an imported server
112
+
113
+ ```bash
114
+ tcap tools github --brief
115
+ tcap schema github create-issue
116
+ tcap call github create-issue @args.json --save-run
117
+ ```
118
+
119
+ If the call fails, patch `args.json` and retry:
120
+
121
+ ```bash
122
+ tcap retry runs/<run-id>
123
+ ```
package/docs/launch.md CHANGED
@@ -23,24 +23,39 @@ npm i -g toolcapsule
23
23
  Try:
24
24
 
25
25
  ```bash
26
- tcap init feishu --url https://mcp.example.com/mcp/xxx
26
+ tcap import --dry-run
27
+ tcap import --name feishu --target claude
27
28
  tcap tools feishu --brief
28
29
  tcap call feishu create-doc @args.json --save-run
29
30
  ```
30
31
 
32
+ New onboarding angle:
33
+
34
+ > Already have MCP configured in Claude Code, VS Code, OpenCode, Gemini CLI, or Cursor? ToolCapsule can import it and generate the right Agent Skill.
35
+
36
+ Short demo:
37
+
38
+ ```bash
39
+ tcap import --dry-run
40
+ tcap import --name github --target all
41
+ ```
42
+
31
43
  ## Suggested screenshot checklist
32
44
 
33
45
  Ask a human/function-caller to capture:
34
46
 
35
47
  1. Hero section at `https://toolcapsule.studio` or `https://toolcapsule.vercel.app`.
36
- 2. Terminal showing `tcap tools feishu --brief`.
37
- 3. Terminal showing a saved run directory.
38
- 4. Before/after graphic: native MCP schema in prompt vs ToolCapsule local artifacts.
48
+ 2. Terminal showing `tcap import --dry-run` finding an existing MCP server.
49
+ 3. Terminal showing `tcap tools feishu --brief`.
50
+ 4. Terminal showing a saved run directory.
51
+ 5. Before/after graphic: native MCP schema in prompt vs ToolCapsule local artifacts.
39
52
 
40
53
  ## Suggested GIF flow
41
54
 
42
- 1. Run `tcap call ... --save-run`.
43
- 2. Show failure or saved run artifacts.
44
- 3. Patch `args.json`.
45
- 4. Run `tcap retry runs/<id>`.
46
- 5. Show success.
55
+ 1. Run `tcap import --dry-run`.
56
+ 2. Run `tcap import --name ... --target claude`.
57
+ 3. Run `tcap call ... --save-run`.
58
+ 4. Show failure or saved run artifacts.
59
+ 5. Patch `args.json`.
60
+ 6. Run `tcap retry runs/<id>`.
61
+ 7. Show success.
@@ -55,6 +55,24 @@ Add a better release note with:
55
55
 
56
56
  ## 2. Product usability
57
57
 
58
+ ### 2.0 Fast MCP import
59
+
60
+ Initial support exists:
61
+
62
+ ```bash
63
+ tcap import --dry-run
64
+ tcap import --name <server> --target claude
65
+ tcap import --all --target all
66
+ ```
67
+
68
+ Next improvements:
69
+
70
+ - Add clearer per-tool import summaries in CLI output.
71
+ - Add support for more user-level config locations after explicit confirmation.
72
+ - Add integration tests with fixture config files for Claude Code, VS Code, OpenCode, Gemini CLI, and Cursor.
73
+ - Document security behavior around `headers`, `env`, and user-level configs.
74
+ - Consider an interactive mode that prompts users to pick a server when multiple MCP servers are found.
75
+
58
76
  ### 2.1 Build a real Feishu demo
59
77
 
60
78
  The Feishu example should become a complete demo:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcapsule",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0-alpha.9",
4
4
  "description": "File-first, patchable Agent Skills for heavy MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {