@sysvv/ai-skill 1.1.0 → 1.3.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 +72 -0
- package/dist/index.js +110 -40
- package/mcps/azure-devops.mcp.json +16 -0
- package/package.json +4 -2
- package/schemas/mcp.schema.json +93 -0
- package/skills/azure-devops/SKILL.md +37 -0
package/dist/core/mcp.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/** Onde cada agent espera o arquivo de MCP */
|
|
6
|
+
const MCP_TARGETS = {
|
|
7
|
+
claude: ".mcp.json",
|
|
8
|
+
codex: ".codex/mcp.json",
|
|
9
|
+
gemini: ".gemini/mcp.json",
|
|
10
|
+
copilot: ".github/copilot/mcp.json",
|
|
11
|
+
};
|
|
12
|
+
/** Resolve o caminho do .mcp.json dentro de /mcps/ */
|
|
13
|
+
function getMcpFilePath(name) {
|
|
14
|
+
return path.resolve(__dirname, "../../mcps", `${name}.mcp.json`);
|
|
15
|
+
}
|
|
16
|
+
/** Carrega a definição de um MCP pelo nome */
|
|
17
|
+
export async function loadMcp(name) {
|
|
18
|
+
const mcpFile = getMcpFilePath(name);
|
|
19
|
+
if (!(await fs.pathExists(mcpFile))) {
|
|
20
|
+
throw new Error(`MCP "${name}" não encontrado em mcps/${name}.mcp.json`);
|
|
21
|
+
}
|
|
22
|
+
return fs.readJson(mcpFile);
|
|
23
|
+
}
|
|
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
|
+
/** Escreve o MCP no arquivo alvo do agent, fazendo merge com configs existentes */
|
|
43
|
+
export async function writeMcp(agent, mcp) {
|
|
44
|
+
const targetFile = MCP_TARGETS[agent];
|
|
45
|
+
if (!targetFile)
|
|
46
|
+
throw new Error(`Agent "${agent}" não tem target de MCP configurado.`);
|
|
47
|
+
const targetPath = path.resolve(targetFile);
|
|
48
|
+
let existing = {};
|
|
49
|
+
if (await fs.pathExists(targetPath)) {
|
|
50
|
+
existing = await fs.readJson(targetPath);
|
|
51
|
+
}
|
|
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
|
+
return targetPath;
|
|
57
|
+
}
|
|
58
|
+
/** Lista todos os MCPs disponíveis */
|
|
59
|
+
export async function listMcps() {
|
|
60
|
+
const mcpsDir = path.resolve(__dirname, "../../mcps");
|
|
61
|
+
if (!(await fs.pathExists(mcpsDir)))
|
|
62
|
+
return [];
|
|
63
|
+
const files = await fs.readdir(mcpsDir);
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
if (file.endsWith(".mcp.json")) {
|
|
67
|
+
const mcp = await fs.readJson(path.join(mcpsDir, file));
|
|
68
|
+
results.push(mcp);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +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
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
// ── ANSI ────────────────────────────────────────────────────────────────
|
|
12
13
|
const ANSI = {
|
|
@@ -18,18 +19,34 @@ const PINK = '\x1b[38;2;238;54;141m';
|
|
|
18
19
|
const BLUE = '\x1b[38;2;14;93;171m';
|
|
19
20
|
const RED = '\x1b[38;2;255;70;70m';
|
|
20
21
|
const GREEN = '\x1b[38;2;80;200;120m';
|
|
21
|
-
|
|
22
|
+
const YELLOW = '\x1b[38;2;255;200;50m';
|
|
23
|
+
const WHITE = '\x1b[97m';
|
|
24
|
+
// ── Inquirer Theme ──────────────────────────────────────────────────────
|
|
25
|
+
const sysTheme = {
|
|
26
|
+
icon: {
|
|
27
|
+
cursor: PINK + '❯' + ANSI.reset,
|
|
28
|
+
checked: PINK + '◉' + ANSI.reset,
|
|
29
|
+
unchecked: ANSI.dim + '○' + ANSI.reset,
|
|
30
|
+
},
|
|
31
|
+
style: {
|
|
32
|
+
highlight: (text) => PINK + ANSI.bold + text + ANSI.reset,
|
|
33
|
+
answer: (text) => BLUE + ANSI.bold + text + ANSI.reset,
|
|
34
|
+
message: (text) => WHITE + ANSI.bold + text + ANSI.reset,
|
|
35
|
+
},
|
|
36
|
+
helpMode: 'always',
|
|
37
|
+
};
|
|
38
|
+
// ── ASCII Art Letters (7 lines, bigger) ─────────────────────────────────
|
|
22
39
|
const LETTERS = {
|
|
23
|
-
A: ['
|
|
24
|
-
I: ['██', '██', '██', '██', '██'],
|
|
25
|
-
K: ['██
|
|
26
|
-
L: ['██
|
|
27
|
-
S: ['
|
|
28
|
-
Y: ['██
|
|
40
|
+
A: [' ██████ ', ' ██ ██ ', '██ ██', '██████████', '██ ██', '██ ██', '██ ██'],
|
|
41
|
+
I: ['████', ' ██ ', ' ██ ', ' ██ ', ' ██ ', ' ██ ', '████'],
|
|
42
|
+
K: ['██ ██', '██ ██ ', '██ ██ ', '█████ ', '██ ██ ', '██ ██ ', '██ ██'],
|
|
43
|
+
L: ['██ ', '██ ', '██ ', '██ ', '██ ', '██ ', '█████████'],
|
|
44
|
+
S: [' ████████ ', '██ ', '██ ', ' ████████ ', ' ██ ', ' ██ ', ' ████████ '],
|
|
45
|
+
Y: ['██ ██', ' ██ ██ ', ' ██ ██ ', ' ███ ', ' ███ ', ' ███ ', ' ███ '],
|
|
29
46
|
};
|
|
30
|
-
function joinLetters(keys, gap = '
|
|
47
|
+
function joinLetters(keys, gap = ' ') {
|
|
31
48
|
const letters = keys.map(k => LETTERS[k]);
|
|
32
|
-
return Array.from({ length:
|
|
49
|
+
return Array.from({ length: 7 }, (_, i) => letters.map(l => l[i]).join(gap));
|
|
33
50
|
}
|
|
34
51
|
function centerPad(line, totalWidth) {
|
|
35
52
|
const spaces = Math.max(0, Math.floor((totalWidth - line.length) / 2));
|
|
@@ -46,28 +63,35 @@ function showBanner() {
|
|
|
46
63
|
const aiLines = joinLetters(['A', 'I']);
|
|
47
64
|
const skillsLines = joinLetters(['S', 'K', 'I', 'L', 'L', 'S']);
|
|
48
65
|
const maxWidth = Math.max(sysLines[0].length, aiLines[0].length, skillsLines[0].length);
|
|
49
|
-
const indent = '
|
|
66
|
+
const indent = ' ';
|
|
67
|
+
const separator = ANSI.dim + indent + centerPad('─'.repeat(maxWidth), maxWidth) + ANSI.reset;
|
|
68
|
+
console.log('');
|
|
50
69
|
console.log('');
|
|
51
70
|
for (const line of sysLines) {
|
|
52
71
|
console.log(indent + PINK + ANSI.bold + centerPad(line, maxWidth) + ANSI.reset);
|
|
53
72
|
}
|
|
54
73
|
console.log('');
|
|
55
74
|
for (const line of aiLines) {
|
|
56
|
-
console.log(indent + BLUE + centerPad(line, maxWidth) + ANSI.reset);
|
|
75
|
+
console.log(indent + BLUE + ANSI.bold + centerPad(line, maxWidth) + ANSI.reset);
|
|
57
76
|
}
|
|
77
|
+
console.log('');
|
|
58
78
|
for (const line of skillsLines) {
|
|
59
79
|
console.log(indent + BLUE + centerPad(line, maxWidth) + ANSI.reset);
|
|
60
80
|
}
|
|
61
81
|
console.log('');
|
|
62
|
-
console.log(
|
|
82
|
+
console.log(separator);
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log(indent + WHITE + ANSI.bold + centerPad(`v${version}`, maxWidth) + ANSI.reset);
|
|
63
85
|
console.log('');
|
|
64
86
|
const desc = 'Skills para turbinar seus agentes de codigo';
|
|
65
|
-
const boxInner = desc.length +
|
|
87
|
+
const boxInner = desc.length + 4;
|
|
66
88
|
console.log(indent + ANSI.dim + centerPad('┌' + '─'.repeat(boxInner) + '┐', maxWidth) + ANSI.reset);
|
|
67
|
-
console.log(indent + ANSI.dim + centerPad('│
|
|
89
|
+
console.log(indent + ANSI.dim + centerPad('│ ' + WHITE + desc + ANSI.reset + ANSI.dim + ' │', maxWidth) + ANSI.reset);
|
|
68
90
|
console.log(indent + ANSI.dim + centerPad('└' + '─'.repeat(boxInner) + '┘', maxWidth) + ANSI.reset);
|
|
69
91
|
console.log('');
|
|
70
|
-
console.log(indent + ANSI.dim + '
|
|
92
|
+
console.log(indent + ANSI.dim + centerPad('npx @sysvv/ai-skill --claude | --codex | --gemini | --copilot | --clear', maxWidth) + ANSI.reset);
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log(separator);
|
|
71
95
|
console.log('');
|
|
72
96
|
}
|
|
73
97
|
// ── Config ──────────────────────────────────────────────────────────────
|
|
@@ -75,6 +99,7 @@ const AGENT_DIRS = {
|
|
|
75
99
|
claude: ".claude/skills",
|
|
76
100
|
codex: ".codex/skills",
|
|
77
101
|
gemini: ".gemini/skills",
|
|
102
|
+
copilot: ".github/skills",
|
|
78
103
|
};
|
|
79
104
|
// ── Parse args ──────────────────────────────────────────────────────────
|
|
80
105
|
function parseArgs() {
|
|
@@ -87,7 +112,7 @@ function parseArgs() {
|
|
|
87
112
|
}
|
|
88
113
|
return {};
|
|
89
114
|
}
|
|
90
|
-
// ── Install skills for agent
|
|
115
|
+
// ── Install skills + MCPs for agent ─────────────────────────────────────
|
|
91
116
|
async function installSkills(agent) {
|
|
92
117
|
const skillFiles = await findSkillFiles();
|
|
93
118
|
const available = [];
|
|
@@ -98,71 +123,113 @@ async function installSkills(agent) {
|
|
|
98
123
|
file,
|
|
99
124
|
name: metadata.name,
|
|
100
125
|
title: metadata.title ?? metadata.name,
|
|
101
|
-
description: metadata.description
|
|
126
|
+
description: metadata.description,
|
|
127
|
+
mcps: metadata.mcps ?? []
|
|
102
128
|
});
|
|
103
129
|
}
|
|
104
130
|
if (available.length === 0) {
|
|
105
|
-
console.log("Nenhuma skill encontrada.");
|
|
131
|
+
console.log(" Nenhuma skill encontrada.");
|
|
106
132
|
process.exit(1);
|
|
107
133
|
}
|
|
108
134
|
const { selected } = await inquirer.prompt([
|
|
109
135
|
{
|
|
110
136
|
type: "checkbox",
|
|
111
137
|
name: "selected",
|
|
112
|
-
message: "Escolha as skills
|
|
138
|
+
message: "Escolha as skills:",
|
|
113
139
|
choices: available.map((s) => ({
|
|
114
|
-
name: `${s.title} — ${s.description}`,
|
|
140
|
+
name: `${s.title} ${ANSI.dim}— ${s.description}${s.mcps.length > 0 ? ` ${YELLOW}[MCP]${ANSI.reset}${ANSI.dim}` : ''}${ANSI.reset}`,
|
|
115
141
|
value: s.file
|
|
116
142
|
})),
|
|
117
|
-
validate: (answer) => answer.length > 0 ? true : "Selecione pelo menos uma skill."
|
|
143
|
+
validate: (answer) => answer.length > 0 ? true : "Selecione pelo menos uma skill.",
|
|
144
|
+
theme: sysTheme
|
|
118
145
|
}
|
|
119
146
|
]);
|
|
120
147
|
const destBase = path.resolve(AGENT_DIRS[agent]);
|
|
148
|
+
const mcpsToInstall = new Set();
|
|
149
|
+
// Instala skills
|
|
150
|
+
console.log('');
|
|
121
151
|
for (const file of selected) {
|
|
122
152
|
const skill = await loadSkill(file, agent);
|
|
123
153
|
const skillDir = path.join(destBase, skill.metadata.name);
|
|
124
154
|
await fs.ensureDir(skillDir);
|
|
125
155
|
await fs.writeFile(path.join(skillDir, "SKILL.md"), skill.renderedBody, "utf8");
|
|
126
156
|
console.log(` ${GREEN}✔${ANSI.reset} ${skill.metadata.name}`);
|
|
157
|
+
// Coleta MCPs necessários
|
|
158
|
+
for (const mcpName of skill.metadata.mcps ?? []) {
|
|
159
|
+
mcpsToInstall.add(mcpName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Instala MCPs automaticamente
|
|
163
|
+
if (mcpsToInstall.size > 0) {
|
|
164
|
+
console.log('');
|
|
165
|
+
let mcpTarget = '';
|
|
166
|
+
for (const mcpName of mcpsToInstall) {
|
|
167
|
+
try {
|
|
168
|
+
const mcp = await loadMcp(mcpName);
|
|
169
|
+
mcpTarget = await writeMcp(agent, mcp);
|
|
170
|
+
console.log(` ${GREEN}✔${ANSI.reset} MCP ${ANSI.bold}${mcpName}${ANSI.reset} ${ANSI.dim}→ ${mcpTarget}${ANSI.reset}`);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.log(` ${RED}✖${ANSI.reset} MCP ${mcpName}: ${err.message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
127
176
|
}
|
|
128
177
|
console.log(`\n Skills instaladas em ${ANSI.bold}${destBase}/${ANSI.reset}\n`);
|
|
129
178
|
}
|
|
130
|
-
// ── Clear all skills
|
|
179
|
+
// ── Clear all skills + MCPs ─────────────────────────────────────────────
|
|
131
180
|
async function clearSkills() {
|
|
132
|
-
const
|
|
181
|
+
const MCP_TARGETS = {
|
|
182
|
+
claude: ".mcp.json",
|
|
183
|
+
codex: ".codex/mcp.json",
|
|
184
|
+
gemini: ".gemini/mcp.json",
|
|
185
|
+
copilot: ".github/copilot/mcp.json",
|
|
186
|
+
};
|
|
187
|
+
const found = [];
|
|
133
188
|
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
189
|
+
const skillsFull = path.resolve(dir);
|
|
190
|
+
const mcpFull = path.resolve(MCP_TARGETS[agent]);
|
|
191
|
+
const hasSkills = await fs.pathExists(skillsFull);
|
|
192
|
+
const hasMcp = await fs.pathExists(mcpFull);
|
|
193
|
+
if (hasSkills || hasMcp) {
|
|
194
|
+
found.push({ agent, skillsDir: skillsFull, mcpFile: mcpFull, hasSkills, hasMcp });
|
|
137
195
|
}
|
|
138
196
|
}
|
|
139
|
-
if (
|
|
140
|
-
console.log(" Nenhuma pasta de skills encontrada.\n");
|
|
197
|
+
if (found.length === 0) {
|
|
198
|
+
console.log(" Nenhuma pasta de skills ou MCP encontrada.\n");
|
|
141
199
|
return;
|
|
142
200
|
}
|
|
143
|
-
console.log(` ${RED}⚠${ANSI.reset}
|
|
144
|
-
for (const
|
|
145
|
-
|
|
201
|
+
console.log(` ${RED}⚠${ANSI.reset} Encontrado:\n`);
|
|
202
|
+
for (const f of found) {
|
|
203
|
+
if (f.hasSkills)
|
|
204
|
+
console.log(` ${ANSI.dim}${f.skillsDir}${ANSI.reset}`);
|
|
205
|
+
if (f.hasMcp)
|
|
206
|
+
console.log(` ${ANSI.dim}${f.mcpFile}${ANSI.reset}`);
|
|
146
207
|
}
|
|
147
208
|
console.log('');
|
|
148
209
|
const { confirm } = await inquirer.prompt([
|
|
149
210
|
{
|
|
150
211
|
type: "confirm",
|
|
151
212
|
name: "confirm",
|
|
152
|
-
message: "Tem certeza que deseja apagar
|
|
153
|
-
default: false
|
|
213
|
+
message: "Tem certeza que deseja apagar TUDO (skills + MCPs)?",
|
|
214
|
+
default: false,
|
|
215
|
+
theme: sysTheme
|
|
154
216
|
}
|
|
155
217
|
]);
|
|
156
218
|
if (!confirm) {
|
|
157
219
|
console.log("\n Operação cancelada.\n");
|
|
158
220
|
return;
|
|
159
221
|
}
|
|
160
|
-
for (const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
222
|
+
for (const f of found) {
|
|
223
|
+
if (f.hasSkills) {
|
|
224
|
+
await fs.remove(f.skillsDir);
|
|
225
|
+
console.log(` ${RED}✖${ANSI.reset} ${f.skillsDir}`);
|
|
226
|
+
}
|
|
227
|
+
if (f.hasMcp) {
|
|
228
|
+
await fs.remove(f.mcpFile);
|
|
229
|
+
console.log(` ${RED}✖${ANSI.reset} ${f.mcpFile}`);
|
|
230
|
+
}
|
|
164
231
|
}
|
|
165
|
-
console.log("\n
|
|
232
|
+
console.log("\n Tudo removido.\n");
|
|
166
233
|
}
|
|
167
234
|
// ── Main ────────────────────────────────────────────────────────────────
|
|
168
235
|
async function main() {
|
|
@@ -176,13 +243,16 @@ async function main() {
|
|
|
176
243
|
await installSkills(agent);
|
|
177
244
|
return;
|
|
178
245
|
}
|
|
179
|
-
// Modo interativo
|
|
180
246
|
const { chosenAgent } = await inquirer.prompt([
|
|
181
247
|
{
|
|
182
248
|
type: "list",
|
|
183
249
|
name: "chosenAgent",
|
|
184
250
|
message: "Qual IA você usa?",
|
|
185
|
-
choices: Object.keys(AGENT_DIRS)
|
|
251
|
+
choices: Object.keys(AGENT_DIRS).map((key) => ({
|
|
252
|
+
name: `${ANSI.bold}${key.charAt(0).toUpperCase() + key.slice(1)}${ANSI.reset}`,
|
|
253
|
+
value: key
|
|
254
|
+
})),
|
|
255
|
+
theme: sysTheme
|
|
186
256
|
}
|
|
187
257
|
]);
|
|
188
258
|
await installSkills(chosenAgent);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schemas/mcp.schema.json",
|
|
3
|
+
"name": "azure-devops",
|
|
4
|
+
"description": "Integração com Azure DevOps para work items, repos e pipelines.",
|
|
5
|
+
"transport": "stdio",
|
|
6
|
+
"command": "npx",
|
|
7
|
+
"args": [
|
|
8
|
+
"-y",
|
|
9
|
+
"@azure-devops/mcp",
|
|
10
|
+
"sysmanagerdevops",
|
|
11
|
+
"-d", "core", "work", "work-items"
|
|
12
|
+
],
|
|
13
|
+
"env": {
|
|
14
|
+
"AZURE_DEVOPS_PAT": "${AZURE_DEVOPS_PAT}"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sysvv/ai-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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": {
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
-
"skills"
|
|
11
|
+
"skills",
|
|
12
|
+
"mcps",
|
|
13
|
+
"schemas"
|
|
12
14
|
],
|
|
13
15
|
"scripts": {
|
|
14
16
|
"build": "tsc",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://sysvv.dev/schemas/mcp.schema.json",
|
|
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.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Referência ao JSON Schema para validação e autocomplete."
|
|
11
|
+
},
|
|
12
|
+
"name": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
15
|
+
"description": "Identificador único do MCP. Deve ser slug (lowercase, hífens). Ex: azure-devops"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Descrição curta do que o MCP faz."
|
|
20
|
+
},
|
|
21
|
+
"transport": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"enum": ["stdio", "sse", "streamable-http"],
|
|
24
|
+
"description": "Tipo de transporte do MCP."
|
|
25
|
+
},
|
|
26
|
+
"command": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "[stdio] Comando para iniciar o servidor MCP."
|
|
29
|
+
},
|
|
30
|
+
"args": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": { "type": "string" },
|
|
33
|
+
"description": "[stdio] Argumentos passados ao comando."
|
|
34
|
+
},
|
|
35
|
+
"url": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"format": "uri",
|
|
38
|
+
"description": "[sse | streamable-http] URL do servidor MCP."
|
|
39
|
+
},
|
|
40
|
+
"headers": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": { "type": "string" },
|
|
43
|
+
"description": "[sse | streamable-http] Headers HTTP enviados nas requisições."
|
|
44
|
+
},
|
|
45
|
+
"env": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": { "type": "string" },
|
|
48
|
+
"description": "Variáveis de ambiente. Use ${VAR} para referências dinâmicas."
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": ["name", "description", "transport"],
|
|
52
|
+
"allOf": [
|
|
53
|
+
{
|
|
54
|
+
"if": {
|
|
55
|
+
"properties": { "transport": { "const": "stdio" } },
|
|
56
|
+
"required": ["transport"]
|
|
57
|
+
},
|
|
58
|
+
"then": {
|
|
59
|
+
"required": ["command"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"url": false,
|
|
62
|
+
"headers": false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"if": {
|
|
68
|
+
"properties": { "transport": { "const": "sse" } },
|
|
69
|
+
"required": ["transport"]
|
|
70
|
+
},
|
|
71
|
+
"then": {
|
|
72
|
+
"required": ["url"],
|
|
73
|
+
"properties": {
|
|
74
|
+
"command": false,
|
|
75
|
+
"args": false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"if": {
|
|
81
|
+
"properties": { "transport": { "const": "streamable-http" } },
|
|
82
|
+
"required": ["transport"]
|
|
83
|
+
},
|
|
84
|
+
"then": {
|
|
85
|
+
"required": ["url"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"command": false,
|
|
88
|
+
"args": false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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}}
|