@sysvv/ai-skill 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/mcp.js +156 -38
- package/dist/index.js +73 -40
- package/package.json +3 -2
- package/schemas/mcp.schema.json +12 -16
- package/skills/ado-workflow/SKILL.md +163 -0
- package/skills/azure-devops/SKILL.md +0 -37
package/dist/core/mcp.js
CHANGED
|
@@ -1,60 +1,178 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import * as TOML from "smol-toml";
|
|
4
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// ── Targets por agent ───────────────────────────────────────────────────
|
|
7
|
+
const AGENT_MCP_TARGETS = {
|
|
8
|
+
// ── Claude ──────────────────────────────────────────────────────────
|
|
9
|
+
// .mcp.json → { mcpServers: { name: { command, args, env } } }
|
|
10
|
+
claude: {
|
|
11
|
+
format: "json",
|
|
12
|
+
file: ".mcp.json",
|
|
13
|
+
serversKey: "mcpServers",
|
|
14
|
+
buildServerConfig: (mcp) => {
|
|
15
|
+
if (mcp.transport === "stdio") {
|
|
16
|
+
const config = { command: mcp.command };
|
|
17
|
+
if (mcp.args?.length)
|
|
18
|
+
config.args = mcp.args;
|
|
19
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
20
|
+
config.env = mcp.env;
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
const config = { url: mcp.url };
|
|
24
|
+
if (mcp.headers && Object.keys(mcp.headers).length)
|
|
25
|
+
config.headers = mcp.headers;
|
|
26
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
27
|
+
config.env = mcp.env;
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
// ── Gemini ──────────────────────────────────────────────────────────
|
|
32
|
+
// .gemini/settings.json → { mcpServers: { name: { command, args, env, cwd, timeout } } }
|
|
33
|
+
// Gemini usa "httpUrl" para streamable-http
|
|
34
|
+
gemini: {
|
|
35
|
+
format: "json",
|
|
36
|
+
file: ".gemini/settings.json",
|
|
37
|
+
serversKey: "mcpServers",
|
|
38
|
+
buildServerConfig: (mcp) => {
|
|
39
|
+
const config = {};
|
|
40
|
+
if (mcp.transport === "stdio") {
|
|
41
|
+
config.command = mcp.command;
|
|
42
|
+
if (mcp.args?.length)
|
|
43
|
+
config.args = mcp.args;
|
|
44
|
+
if (mcp.cwd)
|
|
45
|
+
config.cwd = mcp.cwd;
|
|
46
|
+
}
|
|
47
|
+
else if (mcp.transport === "sse") {
|
|
48
|
+
config.url = mcp.url;
|
|
49
|
+
if (mcp.headers && Object.keys(mcp.headers).length)
|
|
50
|
+
config.headers = mcp.headers;
|
|
51
|
+
}
|
|
52
|
+
else if (mcp.transport === "streamable-http") {
|
|
53
|
+
config.httpUrl = mcp.url;
|
|
54
|
+
if (mcp.headers && Object.keys(mcp.headers).length)
|
|
55
|
+
config.headers = mcp.headers;
|
|
56
|
+
}
|
|
57
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
58
|
+
config.env = mcp.env;
|
|
59
|
+
if (mcp.timeout)
|
|
60
|
+
config.timeout = mcp.timeout;
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
// ── Codex ───────────────────────────────────────────────────────────
|
|
65
|
+
// .codex/config.toml → [mcp_servers.<name>]
|
|
66
|
+
// stdio: command, args, env, cwd
|
|
67
|
+
// streamable-http: url, bearer_token_env_var, http_headers
|
|
68
|
+
// extras: startup_timeout_sec, tool_timeout_sec, enabled
|
|
69
|
+
codex: {
|
|
70
|
+
format: "toml",
|
|
71
|
+
file: ".codex/config.toml",
|
|
72
|
+
buildServerEntry: (mcp) => {
|
|
73
|
+
const entry = {};
|
|
74
|
+
if (mcp.transport === "stdio") {
|
|
75
|
+
entry.command = mcp.command;
|
|
76
|
+
if (mcp.args?.length)
|
|
77
|
+
entry.args = mcp.args;
|
|
78
|
+
if (mcp.cwd)
|
|
79
|
+
entry.cwd = mcp.cwd;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// sse e streamable-http → Codex usa "url" para ambos
|
|
83
|
+
entry.url = mcp.url;
|
|
84
|
+
if (mcp.headers && Object.keys(mcp.headers).length) {
|
|
85
|
+
entry.http_headers = mcp.headers;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
89
|
+
entry.env = mcp.env;
|
|
90
|
+
if (mcp.timeout)
|
|
91
|
+
entry.tool_timeout_sec = Math.ceil(mcp.timeout / 1000);
|
|
92
|
+
return entry;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
// ── Copilot ─────────────────────────────────────────────────────────
|
|
96
|
+
// .vscode/mcp.json → { inputs: [], servers: { name: { command, args, env } } }
|
|
97
|
+
copilot: {
|
|
98
|
+
format: "json",
|
|
99
|
+
file: ".vscode/mcp.json",
|
|
100
|
+
serversKey: "servers",
|
|
101
|
+
buildServerConfig: (mcp) => {
|
|
102
|
+
if (mcp.transport === "stdio") {
|
|
103
|
+
const config = { command: mcp.command };
|
|
104
|
+
if (mcp.args?.length)
|
|
105
|
+
config.args = mcp.args;
|
|
106
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
107
|
+
config.env = mcp.env;
|
|
108
|
+
return config;
|
|
109
|
+
}
|
|
110
|
+
const config = { url: mcp.url };
|
|
111
|
+
if (mcp.headers && Object.keys(mcp.headers).length)
|
|
112
|
+
config.headers = mcp.headers;
|
|
113
|
+
if (mcp.env && Object.keys(mcp.env).length)
|
|
114
|
+
config.env = mcp.env;
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
11
118
|
};
|
|
12
|
-
|
|
13
|
-
function
|
|
14
|
-
|
|
119
|
+
// ── Writers ─────────────────────────────────────────────────────────────
|
|
120
|
+
async function writeJsonTarget(target, targetPath, mcp) {
|
|
121
|
+
let existing = {};
|
|
122
|
+
if (await fs.pathExists(targetPath)) {
|
|
123
|
+
existing = await fs.readJson(targetPath);
|
|
124
|
+
}
|
|
125
|
+
// Copilot: garante que "inputs" exista
|
|
126
|
+
if (target.serversKey === "servers" && !existing.inputs) {
|
|
127
|
+
existing.inputs = [];
|
|
128
|
+
}
|
|
129
|
+
const servers = existing[target.serversKey] ?? {};
|
|
130
|
+
servers[mcp.name] = target.buildServerConfig(mcp);
|
|
131
|
+
existing[target.serversKey] = servers;
|
|
132
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
133
|
+
await fs.writeJson(targetPath, existing, { spaces: 2 });
|
|
134
|
+
}
|
|
135
|
+
async function writeTomlTarget(target, targetPath, mcp) {
|
|
136
|
+
let existing = {};
|
|
137
|
+
if (await fs.pathExists(targetPath)) {
|
|
138
|
+
const content = await fs.readFile(targetPath, "utf8");
|
|
139
|
+
if (content.trim()) {
|
|
140
|
+
existing = TOML.parse(content);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const mcpServers = existing.mcp_servers ?? {};
|
|
144
|
+
mcpServers[mcp.name] = target.buildServerEntry(mcp);
|
|
145
|
+
existing.mcp_servers = mcpServers;
|
|
146
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
147
|
+
await fs.writeFile(targetPath, TOML.stringify(existing), "utf8");
|
|
15
148
|
}
|
|
149
|
+
// ── API pública ─────────────────────────────────────────────────────────
|
|
16
150
|
/** Carrega a definição de um MCP pelo nome */
|
|
17
151
|
export async function loadMcp(name) {
|
|
18
|
-
const mcpFile =
|
|
152
|
+
const mcpFile = path.resolve(__dirname, "../../mcps", `${name}.mcp.json`);
|
|
19
153
|
if (!(await fs.pathExists(mcpFile))) {
|
|
20
154
|
throw new Error(`MCP "${name}" não encontrado em mcps/${name}.mcp.json`);
|
|
21
155
|
}
|
|
22
156
|
return fs.readJson(mcpFile);
|
|
23
157
|
}
|
|
24
|
-
/** Extrai apenas os campos de server config (sem metadata) */
|
|
25
|
-
function extractServerConfig(mcp) {
|
|
26
|
-
if (mcp.transport === "stdio") {
|
|
27
|
-
const config = { command: mcp.command };
|
|
28
|
-
if (mcp.args?.length)
|
|
29
|
-
config.args = mcp.args;
|
|
30
|
-
if (mcp.env && Object.keys(mcp.env).length)
|
|
31
|
-
config.env = mcp.env;
|
|
32
|
-
return config;
|
|
33
|
-
}
|
|
34
|
-
// sse | streamable-http
|
|
35
|
-
const config = { url: mcp.url };
|
|
36
|
-
if (mcp.headers && Object.keys(mcp.headers).length)
|
|
37
|
-
config.headers = mcp.headers;
|
|
38
|
-
if (mcp.env && Object.keys(mcp.env).length)
|
|
39
|
-
config.env = mcp.env;
|
|
40
|
-
return config;
|
|
41
|
-
}
|
|
42
158
|
/** Escreve o MCP no arquivo alvo do agent, fazendo merge com configs existentes */
|
|
43
159
|
export async function writeMcp(agent, mcp) {
|
|
44
|
-
const
|
|
45
|
-
if (!
|
|
160
|
+
const target = AGENT_MCP_TARGETS[agent];
|
|
161
|
+
if (!target)
|
|
46
162
|
throw new Error(`Agent "${agent}" não tem target de MCP configurado.`);
|
|
47
|
-
const targetPath = path.resolve(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
163
|
+
const targetPath = path.resolve(target.file);
|
|
164
|
+
if (target.format === "json") {
|
|
165
|
+
await writeJsonTarget(target, targetPath, mcp);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
await writeTomlTarget(target, targetPath, mcp);
|
|
51
169
|
}
|
|
52
|
-
const mcpServers = existing.mcpServers ?? {};
|
|
53
|
-
mcpServers[mcp.name] = extractServerConfig(mcp);
|
|
54
|
-
await fs.ensureDir(path.dirname(targetPath));
|
|
55
|
-
await fs.writeJson(targetPath, { ...existing, mcpServers }, { spaces: 2 });
|
|
56
170
|
return targetPath;
|
|
57
171
|
}
|
|
172
|
+
/** Retorna o path do arquivo MCP de um agent */
|
|
173
|
+
export function getMcpTargetPath(agent) {
|
|
174
|
+
return AGENT_MCP_TARGETS[agent]?.file;
|
|
175
|
+
}
|
|
58
176
|
/** Lista todos os MCPs disponíveis */
|
|
59
177
|
export async function listMcps() {
|
|
60
178
|
const mcpsDir = path.resolve(__dirname, "../../mcps");
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { findSkillFiles } from "./core/registry.js";
|
|
8
8
|
import { parseSkillFile } from "./core/frontmatter.js";
|
|
9
9
|
import { loadSkill } from "./core/skill.js";
|
|
10
|
-
import { loadMcp, writeMcp } from "./core/mcp.js";
|
|
10
|
+
import { loadMcp, writeMcp, getMcpTargetPath } from "./core/mcp.js";
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
// ── ANSI ────────────────────────────────────────────────────────────────
|
|
13
13
|
const ANSI = {
|
|
@@ -24,9 +24,9 @@ const WHITE = '\x1b[97m';
|
|
|
24
24
|
// ── Inquirer Theme ──────────────────────────────────────────────────────
|
|
25
25
|
const sysTheme = {
|
|
26
26
|
icon: {
|
|
27
|
-
cursor: PINK + '❯' + ANSI.reset,
|
|
28
|
-
checked:
|
|
29
|
-
unchecked: ANSI.dim + '○' + ANSI.reset,
|
|
27
|
+
cursor: PINK + ' ❯' + ANSI.reset,
|
|
28
|
+
checked: GREEN + ' ✔' + ANSI.reset,
|
|
29
|
+
unchecked: ANSI.dim + ' ○' + ANSI.reset,
|
|
30
30
|
},
|
|
31
31
|
style: {
|
|
32
32
|
highlight: (text) => PINK + ANSI.bold + text + ANSI.reset,
|
|
@@ -146,7 +146,6 @@ async function installSkills(agent) {
|
|
|
146
146
|
]);
|
|
147
147
|
const destBase = path.resolve(AGENT_DIRS[agent]);
|
|
148
148
|
const mcpsToInstall = new Set();
|
|
149
|
-
// Instala skills
|
|
150
149
|
console.log('');
|
|
151
150
|
for (const file of selected) {
|
|
152
151
|
const skill = await loadSkill(file, agent);
|
|
@@ -154,19 +153,16 @@ async function installSkills(agent) {
|
|
|
154
153
|
await fs.ensureDir(skillDir);
|
|
155
154
|
await fs.writeFile(path.join(skillDir, "SKILL.md"), skill.renderedBody, "utf8");
|
|
156
155
|
console.log(` ${GREEN}✔${ANSI.reset} ${skill.metadata.name}`);
|
|
157
|
-
// Coleta MCPs necessários
|
|
158
156
|
for (const mcpName of skill.metadata.mcps ?? []) {
|
|
159
157
|
mcpsToInstall.add(mcpName);
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
|
-
// Instala MCPs automaticamente
|
|
163
160
|
if (mcpsToInstall.size > 0) {
|
|
164
161
|
console.log('');
|
|
165
|
-
let mcpTarget = '';
|
|
166
162
|
for (const mcpName of mcpsToInstall) {
|
|
167
163
|
try {
|
|
168
164
|
const mcp = await loadMcp(mcpName);
|
|
169
|
-
mcpTarget = await writeMcp(agent, mcp);
|
|
165
|
+
const mcpTarget = await writeMcp(agent, mcp);
|
|
170
166
|
console.log(` ${GREEN}✔${ANSI.reset} MCP ${ANSI.bold}${mcpName}${ANSI.reset} ${ANSI.dim}→ ${mcpTarget}${ANSI.reset}`);
|
|
171
167
|
}
|
|
172
168
|
catch (err) {
|
|
@@ -176,41 +172,75 @@ async function installSkills(agent) {
|
|
|
176
172
|
}
|
|
177
173
|
console.log(`\n Skills instaladas em ${ANSI.bold}${destBase}/${ANSI.reset}\n`);
|
|
178
174
|
}
|
|
179
|
-
// ── Clear
|
|
180
|
-
async function
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
gemini: ".gemini/mcp.json",
|
|
185
|
-
copilot: ".github/copilot/mcp.json",
|
|
186
|
-
};
|
|
187
|
-
const found = [];
|
|
175
|
+
// ── Clear ───────────────────────────────────────────────────────────────
|
|
176
|
+
async function clearFlow() {
|
|
177
|
+
// Descobre o que existe
|
|
178
|
+
const foundSkills = [];
|
|
179
|
+
const foundMcps = [];
|
|
188
180
|
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
189
181
|
const skillsFull = path.resolve(dir);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
if (await fs.pathExists(skillsFull)) {
|
|
183
|
+
foundSkills.push({ agent, dir: skillsFull });
|
|
184
|
+
}
|
|
185
|
+
const mcpTarget = getMcpTargetPath(agent);
|
|
186
|
+
if (mcpTarget) {
|
|
187
|
+
const mcpFull = path.resolve(mcpTarget);
|
|
188
|
+
if (await fs.pathExists(mcpFull)) {
|
|
189
|
+
foundMcps.push({ agent, file: mcpFull });
|
|
190
|
+
}
|
|
195
191
|
}
|
|
196
192
|
}
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
const hasSkills = foundSkills.length > 0;
|
|
194
|
+
const hasMcps = foundMcps.length > 0;
|
|
195
|
+
if (!hasSkills && !hasMcps) {
|
|
196
|
+
console.log(" Nenhuma skill ou MCP instalado.\n");
|
|
199
197
|
return;
|
|
200
198
|
}
|
|
199
|
+
// Mostra o que encontrou
|
|
201
200
|
console.log(` ${RED}⚠${ANSI.reset} Encontrado:\n`);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
if (hasSkills) {
|
|
202
|
+
console.log(` ${ANSI.bold}Skills${ANSI.reset}`);
|
|
203
|
+
for (const s of foundSkills) {
|
|
204
|
+
console.log(` ${ANSI.dim}${s.dir}${ANSI.reset}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (hasMcps) {
|
|
208
|
+
if (hasSkills)
|
|
209
|
+
console.log('');
|
|
210
|
+
console.log(` ${ANSI.bold}MCPs${ANSI.reset}`);
|
|
211
|
+
for (const m of foundMcps) {
|
|
212
|
+
console.log(` ${ANSI.dim}${m.file}${ANSI.reset}`);
|
|
213
|
+
}
|
|
207
214
|
}
|
|
208
215
|
console.log('');
|
|
216
|
+
const choices = [];
|
|
217
|
+
if (hasSkills && hasMcps) {
|
|
218
|
+
choices.push({ name: `${RED}Tudo${ANSI.reset} ${ANSI.dim}(skills + MCPs)${ANSI.reset}`, value: "all" }, { name: `Apenas Skills`, value: "skills" }, { name: `Apenas MCPs`, value: "mcps" });
|
|
219
|
+
}
|
|
220
|
+
else if (hasSkills) {
|
|
221
|
+
choices.push({ name: `Apagar Skills`, value: "skills" });
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
choices.push({ name: `Apagar MCPs`, value: "mcps" });
|
|
225
|
+
}
|
|
226
|
+
const { action } = await inquirer.prompt([
|
|
227
|
+
{
|
|
228
|
+
type: "list",
|
|
229
|
+
name: "action",
|
|
230
|
+
message: "O que deseja remover?",
|
|
231
|
+
choices,
|
|
232
|
+
theme: sysTheme
|
|
233
|
+
}
|
|
234
|
+
]);
|
|
235
|
+
const removeSkills = action === "all" || action === "skills";
|
|
236
|
+
const removeMcps = action === "all" || action === "mcps";
|
|
237
|
+
// Confirmação
|
|
238
|
+
const target = action === "all" ? "skills e MCPs" : action === "skills" ? "skills" : "MCPs";
|
|
209
239
|
const { confirm } = await inquirer.prompt([
|
|
210
240
|
{
|
|
211
241
|
type: "confirm",
|
|
212
242
|
name: "confirm",
|
|
213
|
-
message:
|
|
243
|
+
message: `Confirma a remoção de ${target}?`,
|
|
214
244
|
default: false,
|
|
215
245
|
theme: sysTheme
|
|
216
246
|
}
|
|
@@ -219,24 +249,27 @@ async function clearSkills() {
|
|
|
219
249
|
console.log("\n Operação cancelada.\n");
|
|
220
250
|
return;
|
|
221
251
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
252
|
+
console.log('');
|
|
253
|
+
if (removeSkills) {
|
|
254
|
+
for (const s of foundSkills) {
|
|
255
|
+
await fs.remove(s.dir);
|
|
256
|
+
console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${s.dir}${ANSI.reset}`);
|
|
226
257
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
258
|
+
}
|
|
259
|
+
if (removeMcps) {
|
|
260
|
+
for (const m of foundMcps) {
|
|
261
|
+
await fs.remove(m.file);
|
|
262
|
+
console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${m.file}${ANSI.reset}`);
|
|
230
263
|
}
|
|
231
264
|
}
|
|
232
|
-
console.log("\n
|
|
265
|
+
console.log("\n Removido com sucesso.\n");
|
|
233
266
|
}
|
|
234
267
|
// ── Main ────────────────────────────────────────────────────────────────
|
|
235
268
|
async function main() {
|
|
236
269
|
showBanner();
|
|
237
270
|
const { agent, clear } = parseArgs();
|
|
238
271
|
if (clear) {
|
|
239
|
-
await
|
|
272
|
+
await clearFlow();
|
|
240
273
|
return;
|
|
241
274
|
}
|
|
242
275
|
if (agent) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sysvv/ai-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Instale skills de IA direto no seu projeto. Escolha o agent, escolha as skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"fs-extra": "^11.3.0",
|
|
32
32
|
"gray-matter": "^4.0.3",
|
|
33
|
-
"inquirer": "^12.5.2"
|
|
33
|
+
"inquirer": "^12.5.2",
|
|
34
|
+
"smol-toml": "^1.6.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/fs-extra": "^11.0.4",
|
package/schemas/mcp.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://sysvv.dev/schemas/mcp.schema.json",
|
|
4
4
|
"title": "MCP Server Configuration",
|
|
5
|
-
"description": "Schema para configuração de servidores MCP (Model Context Protocol). Suporta os transportes stdio, sse e streamable-http.",
|
|
5
|
+
"description": "Schema para configuração de servidores MCP (Model Context Protocol). Suporta os transportes stdio, sse e streamable-http. Campos opcionais como timeout e cwd são aplicados conforme o suporte de cada agent.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
8
|
"$schema": {
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"items": { "type": "string" },
|
|
33
33
|
"description": "[stdio] Argumentos passados ao comando."
|
|
34
34
|
},
|
|
35
|
+
"cwd": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "[stdio] Diretório de trabalho para o processo do servidor."
|
|
38
|
+
},
|
|
35
39
|
"url": {
|
|
36
40
|
"type": "string",
|
|
37
41
|
"format": "uri",
|
|
@@ -46,6 +50,10 @@
|
|
|
46
50
|
"type": "object",
|
|
47
51
|
"additionalProperties": { "type": "string" },
|
|
48
52
|
"description": "Variáveis de ambiente. Use ${VAR} para referências dinâmicas."
|
|
53
|
+
},
|
|
54
|
+
"timeout": {
|
|
55
|
+
"type": "number",
|
|
56
|
+
"description": "Timeout da requisição em milissegundos. Padrão varia por agent."
|
|
49
57
|
}
|
|
50
58
|
},
|
|
51
59
|
"required": ["name", "description", "transport"],
|
|
@@ -56,11 +64,7 @@
|
|
|
56
64
|
"required": ["transport"]
|
|
57
65
|
},
|
|
58
66
|
"then": {
|
|
59
|
-
"required": ["command"]
|
|
60
|
-
"properties": {
|
|
61
|
-
"url": false,
|
|
62
|
-
"headers": false
|
|
63
|
-
}
|
|
67
|
+
"required": ["command"]
|
|
64
68
|
}
|
|
65
69
|
},
|
|
66
70
|
{
|
|
@@ -69,11 +73,7 @@
|
|
|
69
73
|
"required": ["transport"]
|
|
70
74
|
},
|
|
71
75
|
"then": {
|
|
72
|
-
"required": ["url"]
|
|
73
|
-
"properties": {
|
|
74
|
-
"command": false,
|
|
75
|
-
"args": false
|
|
76
|
-
}
|
|
76
|
+
"required": ["url"]
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
{
|
|
@@ -82,11 +82,7 @@
|
|
|
82
82
|
"required": ["transport"]
|
|
83
83
|
},
|
|
84
84
|
"then": {
|
|
85
|
-
"required": ["url"]
|
|
86
|
-
"properties": {
|
|
87
|
-
"command": false,
|
|
88
|
-
"args": false
|
|
89
|
-
}
|
|
85
|
+
"required": ["url"]
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
88
|
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ado-workflow
|
|
3
|
+
title: ADO Workflow
|
|
4
|
+
description: gerenciar work items do Azure DevOps integrado com Git local. Sincroniza estados, vincula commits e mantém ADO atualizado.
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
providers:
|
|
7
|
+
- claude
|
|
8
|
+
- codex
|
|
9
|
+
- gemini
|
|
10
|
+
- copilot
|
|
11
|
+
mcps:
|
|
12
|
+
- azure-devops
|
|
13
|
+
variables:
|
|
14
|
+
language: pt-BR
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# ADO Workflow Skill
|
|
18
|
+
|
|
19
|
+
Reusable skill that integrates Azure DevOps work items with local Git operations. Manages work item states, links commits to tasks, and keeps ADO in sync with development progress. Uses MCP tools as primary interface with CLI fallback.
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
1. Verify MCP server "azure-devops" is available (check `mcp__azure-devops__*` tools)
|
|
24
|
+
2. If MCP is NOT available: fall back to `az` CLI commands for all operations
|
|
25
|
+
3. Verify `git` is installed and repository is initialized
|
|
26
|
+
4. **NEVER** print or display the value of `AZURE_DEVOPS_PAT`
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
|
|
30
|
+
1. ALWAYS reference ADO work item IDs in commit messages (e.g., `feat: implement login #123`)
|
|
31
|
+
2. NEVER update work item state without verifying current state first
|
|
32
|
+
3. NEVER print or display PAT/token values
|
|
33
|
+
4. Make atomic commits — one logical change per commit
|
|
34
|
+
5. Use Conventional Commits format (`feat:`, `fix:`, `docs:`, `refactor:`, `chore:`, etc.)
|
|
35
|
+
6. Keep ADO work item state in sync with actual progress
|
|
36
|
+
7. ALWAYS confirm with the user before changing work item states
|
|
37
|
+
8. Organization, project, area paths, and states are project-specific — do not hardcode them
|
|
38
|
+
9. Work item states may use custom/localized names — always verify before updating
|
|
39
|
+
10. When creating Tasks: do NOT assign, ALWAYS ask Area Path, activity requires user selection
|
|
40
|
+
|
|
41
|
+
## Operations
|
|
42
|
+
|
|
43
|
+
### list
|
|
44
|
+
|
|
45
|
+
List work items assigned to the current user.
|
|
46
|
+
|
|
47
|
+
**MCP:** `mcp__azure-devops__wit_my_work_items` with `project: {project}`, `type: "assignedtome"`, `includeCompleted: false`
|
|
48
|
+
|
|
49
|
+
**Fallback:**
|
|
50
|
+
```bash
|
|
51
|
+
AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards query \
|
|
52
|
+
--wiql "SELECT [System.Id], [System.Title], [System.State], [System.WorkItemType] FROM WorkItems WHERE [System.AssignedTo] = @Me AND [System.State] <> 'Closed'" \
|
|
53
|
+
--project {project} --organization {organization} --output table
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Output:** Display ID, Title, State, Assigned To, Work Item Type as formatted table.
|
|
57
|
+
|
|
58
|
+
### show
|
|
59
|
+
|
|
60
|
+
Show full details of a work item by ID.
|
|
61
|
+
|
|
62
|
+
**Params:** `id` (required)
|
|
63
|
+
|
|
64
|
+
**MCP:** `mcp__azure-devops__wit_get_work_item` with `id: {id}`, `project: {project}`, `expand: "relations"`
|
|
65
|
+
|
|
66
|
+
**Fallback:**
|
|
67
|
+
```bash
|
|
68
|
+
AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item show --id {id} --organization {organization} --output json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Output:** Parse and display id, type, title, state, description, acceptance criteria, assignedTo, iterationPath, areaPath, priority, storyPoints, relations.
|
|
72
|
+
|
|
73
|
+
### start
|
|
74
|
+
|
|
75
|
+
Start working on a task — set state to active and begin implementation.
|
|
76
|
+
|
|
77
|
+
**Params:** `id` (required)
|
|
78
|
+
|
|
79
|
+
**Process:**
|
|
80
|
+
1. Fetch work item details using "show" operation
|
|
81
|
+
2. Display title, description, and acceptance criteria to user
|
|
82
|
+
3. Update state to the active state in ADO
|
|
83
|
+
4. Create implementation plan based on requirements
|
|
84
|
+
5. Ask user for confirmation before proceeding
|
|
85
|
+
|
|
86
|
+
**MCP:** `mcp__azure-devops__wit_update_work_item` with `id: {id}`, `updates: [{"path": "/fields/System.State", "value": "{active_state}"}]`
|
|
87
|
+
|
|
88
|
+
**Fallback:**
|
|
89
|
+
```bash
|
|
90
|
+
AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item update --id {id} --state "{active_state}" --organization {organization} --output json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Output:** Confirm state change. Show implementation plan. Wait for user approval.
|
|
94
|
+
|
|
95
|
+
### complete
|
|
96
|
+
|
|
97
|
+
Complete a task — verify criteria, final commit, set state to closed.
|
|
98
|
+
|
|
99
|
+
**Params:** `id` (required)
|
|
100
|
+
|
|
101
|
+
**Process:**
|
|
102
|
+
1. Fetch work item details — verify acceptance criteria are met
|
|
103
|
+
2. Run final tests if applicable
|
|
104
|
+
3. Make final commit referencing work item ID
|
|
105
|
+
4. Update state to "Closed" in ADO
|
|
106
|
+
5. Add completion comment with commit SHA
|
|
107
|
+
|
|
108
|
+
**MCP:** `mcp__azure-devops__wit_update_work_item` with `id: {id}`, `updates: [{"path": "/fields/System.State", "value": "Closed"}]`
|
|
109
|
+
|
|
110
|
+
**Fallback:**
|
|
111
|
+
```bash
|
|
112
|
+
AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item update --id {id} --state "Closed" --organization {organization} --output json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Output:** Confirm completion. Show final commit SHA, updated state, summary.
|
|
116
|
+
|
|
117
|
+
### commit
|
|
118
|
+
|
|
119
|
+
Make an incremental commit linked to a work item.
|
|
120
|
+
|
|
121
|
+
**Params:** `message` (optional), `work-item-id` (optional)
|
|
122
|
+
|
|
123
|
+
**Process:**
|
|
124
|
+
1. Delegate to git-cli skill `/git-commit`
|
|
125
|
+
2. Append work item reference to commit message (e.g., `#123`)
|
|
126
|
+
|
|
127
|
+
**Output:** Commit hash, message, files changed. Tip: use `/git-push` to push.
|
|
128
|
+
|
|
129
|
+
**Notes:** This operation delegates to the git-cli skill for actual Git operations.
|
|
130
|
+
|
|
131
|
+
### create
|
|
132
|
+
|
|
133
|
+
Create a new work item in ADO.
|
|
134
|
+
|
|
135
|
+
**Params:** `type` (required), `title` (required), `description` (optional), `area-path` (optional — ALWAYS ask user), `activity` (optional — present types to user)
|
|
136
|
+
|
|
137
|
+
**MCP:** `mcp__azure-devops__wit_create_work_item` with `project: {project}`, `type: "{type}"`, `title: "{title}"`, `additionalFields: {"System.Description": "{description}", "System.AreaPath": "{area-path}", "Microsoft.VSTS.Common.Activity": "{activity}", "Microsoft.VSTS.Scheduling.OriginalEstimate": 0, "Microsoft.VSTS.Scheduling.RemainingWork": 0, "Microsoft.VSTS.Scheduling.CompletedWork": 0}`
|
|
138
|
+
|
|
139
|
+
**Fallback:**
|
|
140
|
+
```bash
|
|
141
|
+
AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item create \
|
|
142
|
+
--type "{type}" --title "{title}" --project {project} \
|
|
143
|
+
--organization {organization} --area "{area-path}" \
|
|
144
|
+
--fields "Microsoft.VSTS.Common.Activity={activity}" \
|
|
145
|
+
--output json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Output:** Parse response: extract id, title, state, url.
|
|
149
|
+
|
|
150
|
+
**Notes:**
|
|
151
|
+
- Omit additionalFields entries for params not provided
|
|
152
|
+
- Do NOT assign unless explicitly requested
|
|
153
|
+
- ALWAYS ask user for Area Path before executing
|
|
154
|
+
|
|
155
|
+
## Error Handling
|
|
156
|
+
|
|
157
|
+
| Error | Cause | Resolution |
|
|
158
|
+
|-------|-------|------------|
|
|
159
|
+
| MCP server not available | Not configured or not running | Check `.mcp.json`, verify PAT in `.env`, restart AI tool |
|
|
160
|
+
| 401 / 403 | Invalid or expired PAT | Check PAT scopes: Work Items (Read, Write, Manage), Project (Read) |
|
|
161
|
+
| Work item type not found | Process template mismatch | Agile = "User Story", Scrum = "Product Backlog Item" |
|
|
162
|
+
| State transition not allowed | Invalid state change | Verify allowed transitions for the work item type |
|
|
163
|
+
| MCP tool call fails | Various | Fall back to `az` CLI equivalent command |
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: azure-devops
|
|
3
|
-
title: Azure DevOps
|
|
4
|
-
description: gerenciar work items, repos e pipelines no Azure DevOps.
|
|
5
|
-
version: 1.0.0
|
|
6
|
-
providers:
|
|
7
|
-
- claude
|
|
8
|
-
- codex
|
|
9
|
-
- gemini
|
|
10
|
-
mcps:
|
|
11
|
-
- azure-devops
|
|
12
|
-
variables:
|
|
13
|
-
language: pt-BR
|
|
14
|
-
provider_overrides:
|
|
15
|
-
claude:
|
|
16
|
-
style: "retornar bullets curtos e objetivos"
|
|
17
|
-
codex:
|
|
18
|
-
style: "retornar estrutura operacional"
|
|
19
|
-
gemini:
|
|
20
|
-
style: "retornar texto claro e organizado"
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
Você é um assistente especialista em Azure DevOps.
|
|
24
|
-
|
|
25
|
-
Objetivo:
|
|
26
|
-
Ajudar a gerenciar work items, repositórios e pipelines usando a integração com Azure DevOps.
|
|
27
|
-
|
|
28
|
-
Capacidades:
|
|
29
|
-
- Criar e atualizar work items
|
|
30
|
-
- Consultar status de pipelines
|
|
31
|
-
- Listar repositórios e branches
|
|
32
|
-
- Buscar PRs e reviews
|
|
33
|
-
|
|
34
|
-
Idioma: {{language}}
|
|
35
|
-
|
|
36
|
-
Instrução específica do provider:
|
|
37
|
-
{{provider_style}}
|