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 +29 -5
- package/dist/cli.js +216 -41
- package/dist/cli.js.map +1 -1
- package/docs/agent-tool-compatibility.md +181 -0
- package/docs/architecture.md +12 -0
- package/docs/concept.md +19 -0
- package/docs/importing-mcp.md +123 -0
- package/docs/launch.md +24 -9
- package/docs/next-steps.md +18 -0
- package/package.json +1 -1
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
|
|
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
|
-
.
|
|
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
|
|
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((
|
|
81
|
-
this.pending.set(id, { resolve:
|
|
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((
|
|
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/
|
|
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({
|
|
141
|
-
|
|
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
|
-
|
|
160
|
-
|
|
283
|
+
join2(".toolcapsule", "profiles", `${profilePathOrName}.json`),
|
|
284
|
+
join2(".github", "skills", `${profilePathOrName}-mcp`, "toolcapsule.config.json")
|
|
161
285
|
];
|
|
162
|
-
const found = candidates.find((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
|
|
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
|
|
386
|
+
async function generateSkillAt(profile, outputDir) {
|
|
253
387
|
const skillName = profile.skill?.name || `${profile.name}-mcp`;
|
|
254
|
-
|
|
255
|
-
await mkdir2(
|
|
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(
|
|
266
|
-
await writeJson(
|
|
398
|
+
await writeFile2(join3(outputDir, "SKILL.md"), markdown);
|
|
399
|
+
await writeJson(join3(outputDir, "toolcapsule.config.json"), profile);
|
|
267
400
|
await writeFile2(
|
|
268
|
-
|
|
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
|
|
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 =
|
|
356
|
-
|
|
357
|
-
await
|
|
358
|
-
|
|
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
|
|
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 =
|
|
512
|
+
const dir = join5(baseDir, record.id);
|
|
369
513
|
await mkdir4(dir, { recursive: true });
|
|
370
|
-
await writeJson(
|
|
371
|
-
await writeJson(
|
|
372
|
-
if (record.response !== void 0) await writeJson(
|
|
373
|
-
if (record.error) await writeFile4(
|
|
374
|
-
await writeFile4(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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.
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
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
|
|
37
|
-
3. Terminal showing
|
|
38
|
-
4.
|
|
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
|
|
43
|
-
2.
|
|
44
|
-
3.
|
|
45
|
-
4.
|
|
46
|
-
5.
|
|
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.
|
package/docs/next-steps.md
CHANGED
|
@@ -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:
|