@luquimbo/bi-superpowers 2.0.1 → 3.0.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.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +2 -24
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.claude-plugin/skill-manifest.json +2 -178
  4. package/.mcp.json +0 -16
  5. package/.plugin/plugin.json +1 -1
  6. package/AGENTS.md +37 -55
  7. package/CHANGELOG.md +44 -0
  8. package/README.md +74 -191
  9. package/bin/cli.js +42 -43
  10. package/bin/commands/install.js +37 -7
  11. package/bin/lib/generators/claude-plugin.js +6 -31
  12. package/bin/lib/generators/claude-plugin.test.js +12 -11
  13. package/bin/lib/mcp-config.js +242 -0
  14. package/bin/lib/mcp-config.test.js +184 -0
  15. package/bin/lib/microsoft-mcp.js +6 -20
  16. package/bin/lib/microsoft-mcp.test.js +25 -21
  17. package/bin/postinstall.js +18 -23
  18. package/bin/utils/mcp-detect.js +4 -20
  19. package/bin/utils/mcp-detect.test.js +9 -33
  20. package/package.json +1 -1
  21. package/skills/pbi-connect/SKILL.md +1 -1
  22. package/skills/project-kickoff/SKILL.md +1 -1
  23. package/commands/contributions.md +0 -265
  24. package/commands/data-model-design.md +0 -468
  25. package/commands/dax-doctor.md +0 -248
  26. package/commands/fabric-scripts.md +0 -452
  27. package/commands/migration-assistant.md +0 -290
  28. package/commands/model-documenter.md +0 -242
  29. package/commands/report-layout.md +0 -296
  30. package/commands/rls-design.md +0 -533
  31. package/commands/theme-tweaker.md +0 -624
  32. package/skills/contributions/SKILL.md +0 -267
  33. package/skills/data-model-design/SKILL.md +0 -470
  34. package/skills/data-modeling/SKILL.md +0 -280
  35. package/skills/data-quality/SKILL.md +0 -664
  36. package/skills/dax/SKILL.md +0 -746
  37. package/skills/dax-doctor/SKILL.md +0 -250
  38. package/skills/dax-udf/SKILL.md +0 -489
  39. package/skills/deployment/SKILL.md +0 -320
  40. package/skills/excel-formulas/SKILL.md +0 -463
  41. package/skills/fabric-scripts/SKILL.md +0 -454
  42. package/skills/fast-standard/SKILL.md +0 -509
  43. package/skills/governance/SKILL.md +0 -258
  44. package/skills/migration-assistant/SKILL.md +0 -292
  45. package/skills/model-documenter/SKILL.md +0 -244
  46. package/skills/power-query/SKILL.md +0 -406
  47. package/skills/query-performance/SKILL.md +0 -480
  48. package/skills/report-design/SKILL.md +0 -207
  49. package/skills/report-layout/SKILL.md +0 -298
  50. package/skills/rls-design/SKILL.md +0 -535
  51. package/skills/semantic-model/SKILL.md +0 -237
  52. package/skills/testing-validation/SKILL.md +0 -643
  53. package/skills/theme-tweaker/SKILL.md +0 -626
  54. package/src/content/skills/contributions.md +0 -259
  55. package/src/content/skills/data-model-design.md +0 -462
  56. package/src/content/skills/data-modeling.md +0 -272
  57. package/src/content/skills/data-quality.md +0 -656
  58. package/src/content/skills/dax-doctor.md +0 -242
  59. package/src/content/skills/dax-udf.md +0 -481
  60. package/src/content/skills/dax.md +0 -738
  61. package/src/content/skills/deployment.md +0 -312
  62. package/src/content/skills/excel-formulas.md +0 -455
  63. package/src/content/skills/fabric-scripts.md +0 -446
  64. package/src/content/skills/fast-standard.md +0 -501
  65. package/src/content/skills/governance.md +0 -250
  66. package/src/content/skills/migration-assistant.md +0 -284
  67. package/src/content/skills/model-documenter.md +0 -236
  68. package/src/content/skills/power-query.md +0 -398
  69. package/src/content/skills/query-performance.md +0 -472
  70. package/src/content/skills/report-design.md +0 -199
  71. package/src/content/skills/report-layout.md +0 -290
  72. package/src/content/skills/rls-design.md +0 -527
  73. package/src/content/skills/semantic-model.md +0 -229
  74. package/src/content/skills/testing-validation.md +0 -635
  75. package/src/content/skills/theme-tweaker.md +0 -618
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Multi-Agent MCP Configuration Writer
3
+ * =====================================
4
+ *
5
+ * Writes the bi-superpowers MCP config (2 servers: powerbi-modeling-mcp
6
+ * + microsoft-learn) into each supported agent's expected config location
7
+ * at the user level (home directory).
8
+ *
9
+ * Each agent has its own config format and path. This module normalizes
10
+ * the write operation across all 5 supported agents:
11
+ *
12
+ * | Agent | Path | Format |
13
+ * |-----------------|---------------------------------------|-------------------|
14
+ * | Claude Code | ~/.claude.json | JSON mcpServers |
15
+ * | GitHub Copilot | ~/.copilot/mcp-config.json | JSON servers |
16
+ * | Codex | ~/.codex/config.toml | TOML mcp_servers |
17
+ * | Gemini CLI | ~/.gemini/settings.json | JSON mcpServers |
18
+ * | Kilo Code | ~/.kilocode/mcp_settings.json | JSON mcpServers |
19
+ *
20
+ * The launcher path for the Power BI Modeling MCP is resolved to an
21
+ * absolute path at install time so it works in agents that don't
22
+ * support Claude Code's `${CLAUDE_PLUGIN_ROOT}` variable.
23
+ *
24
+ * @module lib/mcp-config
25
+ */
26
+
27
+ const fs = require('fs');
28
+ const path = require('path');
29
+ const os = require('os');
30
+
31
+ const MICROSOFT_LEARN_URL = 'https://learn.microsoft.com/api/mcp';
32
+ const MODELING_SERVER_NAME = 'powerbi-modeling';
33
+ const LEARN_SERVER_NAME = 'microsoft-learn';
34
+
35
+ /**
36
+ * Resolves the absolute path to the Power BI Modeling MCP launcher
37
+ * inside the installed npm package.
38
+ */
39
+ function getLauncherAbsolutePath(packageDir) {
40
+ return path.join(packageDir, 'bin', 'mcp', 'powerbi-modeling-launcher.js');
41
+ }
42
+
43
+ /**
44
+ * Safely read a JSON file. Returns null if missing or unparseable.
45
+ */
46
+ function readJsonSafe(filePath) {
47
+ if (!fs.existsSync(filePath)) return null;
48
+ try {
49
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
50
+ } catch (_) {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Write a JSON file, creating parent dirs if needed.
57
+ */
58
+ function writeJson(filePath, data) {
59
+ const dir = path.dirname(filePath);
60
+ if (!fs.existsSync(dir)) {
61
+ fs.mkdirSync(dir, { recursive: true });
62
+ }
63
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
64
+ }
65
+
66
+ /**
67
+ * Escape a string for use inside TOML double-quoted strings.
68
+ */
69
+ function tomlEscape(str) {
70
+ return String(str).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
71
+ }
72
+
73
+ // ============================================
74
+ // CLAUDE CODE
75
+ // ============================================
76
+ // Writes ~/.claude.json, adding to `mcpServers`. Preserves other user-level
77
+ // config in that file (projects, settings, etc.).
78
+ function writeClaudeCodeConfig(packageDir) {
79
+ const configPath = path.join(os.homedir(), '.claude.json');
80
+ const existing = readJsonSafe(configPath) || {};
81
+ const mcpServers = { ...(existing.mcpServers || {}) };
82
+
83
+ mcpServers[MODELING_SERVER_NAME] = {
84
+ command: 'node',
85
+ args: [getLauncherAbsolutePath(packageDir)],
86
+ };
87
+ mcpServers[LEARN_SERVER_NAME] = {
88
+ type: 'http',
89
+ url: MICROSOFT_LEARN_URL,
90
+ };
91
+
92
+ writeJson(configPath, { ...existing, mcpServers });
93
+ return configPath;
94
+ }
95
+
96
+ // ============================================
97
+ // GITHUB COPILOT CLI
98
+ // ============================================
99
+ // Writes ~/.copilot/mcp-config.json. Copilot uses `servers` (NOT mcpServers)
100
+ // and each server has an explicit `type` field.
101
+ function writeCopilotConfig(packageDir) {
102
+ const configPath = path.join(os.homedir(), '.copilot', 'mcp-config.json');
103
+ const existing = readJsonSafe(configPath) || {};
104
+ const servers = { ...(existing.servers || {}) };
105
+
106
+ servers[MODELING_SERVER_NAME] = {
107
+ type: 'stdio',
108
+ command: 'node',
109
+ args: [getLauncherAbsolutePath(packageDir)],
110
+ };
111
+ servers[LEARN_SERVER_NAME] = {
112
+ type: 'http',
113
+ url: MICROSOFT_LEARN_URL,
114
+ };
115
+
116
+ writeJson(configPath, { ...existing, servers });
117
+ return configPath;
118
+ }
119
+
120
+ // ============================================
121
+ // CODEX (OpenAI) — TOML format
122
+ // ============================================
123
+ // Writes ~/.codex/config.toml, appending [mcp_servers.*] sections.
124
+ // Preserves existing content by removing only our own sections before
125
+ // appending the fresh ones.
126
+ function writeCodexConfig(packageDir) {
127
+ const configPath = path.join(os.homedir(), '.codex', 'config.toml');
128
+ const launcher = getLauncherAbsolutePath(packageDir);
129
+
130
+ let existing = '';
131
+ if (fs.existsSync(configPath)) {
132
+ existing = fs.readFileSync(configPath, 'utf8');
133
+ }
134
+
135
+ // Remove any previous bi-superpowers MCP sections so re-running install
136
+ // doesn't duplicate them. Strips each section from its header to the
137
+ // next [ header or EOF.
138
+ const stripPattern = new RegExp(
139
+ `\\n?\\[mcp_servers\\.(${MODELING_SERVER_NAME}|${LEARN_SERVER_NAME})\\][\\s\\S]*?(?=\\n\\[|$)`,
140
+ 'g'
141
+ );
142
+ existing = existing.replace(stripPattern, '');
143
+
144
+ const newSections =
145
+ `\n\n[mcp_servers.${MODELING_SERVER_NAME}]\n` +
146
+ 'command = "node"\n' +
147
+ `args = ["${tomlEscape(launcher)}"]\n` +
148
+ `\n[mcp_servers.${LEARN_SERVER_NAME}]\n` +
149
+ `url = "${MICROSOFT_LEARN_URL}"\n`;
150
+
151
+ const content = existing.trimEnd() + newSections;
152
+
153
+ const dir = path.dirname(configPath);
154
+ if (!fs.existsSync(dir)) {
155
+ fs.mkdirSync(dir, { recursive: true });
156
+ }
157
+ fs.writeFileSync(configPath, content);
158
+ return configPath;
159
+ }
160
+
161
+ // ============================================
162
+ // GEMINI CLI
163
+ // ============================================
164
+ // Writes ~/.gemini/settings.json. Gemini uses `mcpServers` and `httpUrl`
165
+ // (not `url`) for HTTP transports.
166
+ function writeGeminiConfig(packageDir) {
167
+ const configPath = path.join(os.homedir(), '.gemini', 'settings.json');
168
+ const existing = readJsonSafe(configPath) || {};
169
+ const mcpServers = { ...(existing.mcpServers || {}) };
170
+
171
+ mcpServers[MODELING_SERVER_NAME] = {
172
+ command: 'node',
173
+ args: [getLauncherAbsolutePath(packageDir)],
174
+ };
175
+ mcpServers[LEARN_SERVER_NAME] = {
176
+ httpUrl: MICROSOFT_LEARN_URL,
177
+ };
178
+
179
+ writeJson(configPath, { ...existing, mcpServers });
180
+ return configPath;
181
+ }
182
+
183
+ // ============================================
184
+ // KILO CODE
185
+ // ============================================
186
+ // Writes ~/.kilocode/mcp_settings.json. Kilo uses `mcpServers` and `url`
187
+ // for HTTP transports.
188
+ function writeKiloConfig(packageDir) {
189
+ const configPath = path.join(os.homedir(), '.kilocode', 'mcp_settings.json');
190
+ const existing = readJsonSafe(configPath) || {};
191
+ const mcpServers = { ...(existing.mcpServers || {}) };
192
+
193
+ mcpServers[MODELING_SERVER_NAME] = {
194
+ command: 'node',
195
+ args: [getLauncherAbsolutePath(packageDir)],
196
+ };
197
+ mcpServers[LEARN_SERVER_NAME] = {
198
+ url: MICROSOFT_LEARN_URL,
199
+ };
200
+
201
+ writeJson(configPath, { ...existing, mcpServers });
202
+ return configPath;
203
+ }
204
+
205
+ /**
206
+ * Registry mapping agent IDs to their MCP config writers.
207
+ */
208
+ const MCP_WRITERS = {
209
+ 'claude-code': writeClaudeCodeConfig,
210
+ 'github-copilot': writeCopilotConfig,
211
+ codex: writeCodexConfig,
212
+ 'gemini-cli': writeGeminiConfig,
213
+ kilo: writeKiloConfig,
214
+ };
215
+
216
+ /**
217
+ * Write MCP config for a specific agent.
218
+ * @param {string} agentId - One of the supported agent IDs
219
+ * @param {string} packageDir - Absolute path to the installed package
220
+ * @returns {string|null} The config path written, or null if unknown agent
221
+ * @throws {Error} If the write fails
222
+ */
223
+ function writeMcpConfigForAgent(agentId, packageDir) {
224
+ const writer = MCP_WRITERS[agentId];
225
+ if (!writer) {
226
+ return null;
227
+ }
228
+ return writer(packageDir);
229
+ }
230
+
231
+ module.exports = {
232
+ writeMcpConfigForAgent,
233
+ MCP_WRITERS,
234
+ MODELING_SERVER_NAME,
235
+ LEARN_SERVER_NAME,
236
+ MICROSOFT_LEARN_URL,
237
+ // Exported for testing
238
+ readJsonSafe,
239
+ writeJson,
240
+ tomlEscape,
241
+ getLauncherAbsolutePath,
242
+ };
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Tests for the multi-agent MCP config writer.
3
+ */
4
+
5
+ const { test, describe, beforeEach, afterEach } = require('node:test');
6
+ const assert = require('node:assert');
7
+ const fs = require('node:fs');
8
+ const os = require('node:os');
9
+ const path = require('node:path');
10
+
11
+ const {
12
+ MCP_WRITERS,
13
+ MODELING_SERVER_NAME,
14
+ LEARN_SERVER_NAME,
15
+ MICROSOFT_LEARN_URL,
16
+ tomlEscape,
17
+ getLauncherAbsolutePath,
18
+ } = require('./mcp-config');
19
+
20
+ describe('mcp-config helpers', () => {
21
+ test('getLauncherAbsolutePath returns absolute launcher path', () => {
22
+ const pkgDir = '/tmp/fake-pkg';
23
+ const launcher = getLauncherAbsolutePath(pkgDir);
24
+ assert.strictEqual(launcher, path.join(pkgDir, 'bin', 'mcp', 'powerbi-modeling-launcher.js'));
25
+ });
26
+
27
+ test('tomlEscape escapes backslashes and quotes', () => {
28
+ assert.strictEqual(tomlEscape('plain'), 'plain');
29
+ assert.strictEqual(tomlEscape('C:\\path\\to\\file'), 'C:\\\\path\\\\to\\\\file');
30
+ assert.strictEqual(tomlEscape('say "hello"'), 'say \\"hello\\"');
31
+ });
32
+ });
33
+
34
+ describe('MCP_WRITERS registry', () => {
35
+ test('covers all 5 supported agents', () => {
36
+ const ids = Object.keys(MCP_WRITERS).sort();
37
+ assert.deepStrictEqual(ids, ['claude-code', 'codex', 'gemini-cli', 'github-copilot', 'kilo']);
38
+ });
39
+
40
+ test('each writer is a function', () => {
41
+ for (const [id, writer] of Object.entries(MCP_WRITERS)) {
42
+ assert.strictEqual(typeof writer, 'function', `${id} writer should be a function`);
43
+ }
44
+ });
45
+ });
46
+
47
+ describe('MCP writers — JSON agents', () => {
48
+ let tempHome;
49
+ let origHome;
50
+ const fakePkgDir = '/tmp/fake-bi-superpowers';
51
+
52
+ beforeEach(() => {
53
+ tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-mcp-home-'));
54
+ origHome = os.homedir;
55
+ os.homedir = () => tempHome;
56
+ });
57
+
58
+ afterEach(() => {
59
+ os.homedir = origHome;
60
+ fs.rmSync(tempHome, { recursive: true, force: true });
61
+ });
62
+
63
+ test('claude-code writer produces mcpServers JSON at ~/.claude.json', () => {
64
+ const configPath = MCP_WRITERS['claude-code'](fakePkgDir);
65
+
66
+ assert.strictEqual(configPath, path.join(tempHome, '.claude.json'));
67
+ assert.ok(fs.existsSync(configPath));
68
+
69
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
70
+ assert.ok(config.mcpServers);
71
+ assert.ok(config.mcpServers[MODELING_SERVER_NAME]);
72
+ assert.strictEqual(config.mcpServers[MODELING_SERVER_NAME].command, 'node');
73
+ assert.ok(
74
+ config.mcpServers[MODELING_SERVER_NAME].args[0].includes('powerbi-modeling-launcher.js')
75
+ );
76
+ assert.deepStrictEqual(config.mcpServers[LEARN_SERVER_NAME], {
77
+ type: 'http',
78
+ url: MICROSOFT_LEARN_URL,
79
+ });
80
+ });
81
+
82
+ test('github-copilot writer uses "servers" key (not mcpServers)', () => {
83
+ const configPath = MCP_WRITERS['github-copilot'](fakePkgDir);
84
+
85
+ assert.strictEqual(configPath, path.join(tempHome, '.copilot', 'mcp-config.json'));
86
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
87
+ assert.ok(config.servers);
88
+ assert.strictEqual(config.mcpServers, undefined);
89
+ assert.strictEqual(config.servers[MODELING_SERVER_NAME].type, 'stdio');
90
+ assert.strictEqual(config.servers[LEARN_SERVER_NAME].type, 'http');
91
+ });
92
+
93
+ test('gemini-cli writer uses httpUrl (not url)', () => {
94
+ const configPath = MCP_WRITERS['gemini-cli'](fakePkgDir);
95
+
96
+ assert.strictEqual(configPath, path.join(tempHome, '.gemini', 'settings.json'));
97
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
98
+ assert.ok(config.mcpServers);
99
+ assert.strictEqual(config.mcpServers[LEARN_SERVER_NAME].httpUrl, MICROSOFT_LEARN_URL);
100
+ assert.strictEqual(config.mcpServers[LEARN_SERVER_NAME].url, undefined);
101
+ });
102
+
103
+ test('kilo writer uses url (standard)', () => {
104
+ const configPath = MCP_WRITERS.kilo(fakePkgDir);
105
+
106
+ assert.strictEqual(configPath, path.join(tempHome, '.kilocode', 'mcp_settings.json'));
107
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
108
+ assert.ok(config.mcpServers);
109
+ assert.strictEqual(config.mcpServers[LEARN_SERVER_NAME].url, MICROSOFT_LEARN_URL);
110
+ });
111
+
112
+ test('claude-code writer preserves existing config', () => {
113
+ const configPath = path.join(tempHome, '.claude.json');
114
+ fs.writeFileSync(
115
+ configPath,
116
+ JSON.stringify({
117
+ otherSetting: 'value',
118
+ mcpServers: { 'existing-mcp': { command: 'echo' } },
119
+ })
120
+ );
121
+
122
+ MCP_WRITERS['claude-code'](fakePkgDir);
123
+
124
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
125
+ assert.strictEqual(config.otherSetting, 'value');
126
+ assert.ok(config.mcpServers['existing-mcp']);
127
+ assert.ok(config.mcpServers[MODELING_SERVER_NAME]);
128
+ assert.ok(config.mcpServers[LEARN_SERVER_NAME]);
129
+ });
130
+ });
131
+
132
+ describe('codex writer — TOML', () => {
133
+ let tempHome;
134
+ let origHome;
135
+
136
+ beforeEach(() => {
137
+ tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-mcp-codex-'));
138
+ origHome = os.homedir;
139
+ os.homedir = () => tempHome;
140
+ });
141
+
142
+ afterEach(() => {
143
+ os.homedir = origHome;
144
+ fs.rmSync(tempHome, { recursive: true, force: true });
145
+ });
146
+
147
+ test('writes TOML with [mcp_servers.*] sections', () => {
148
+ const configPath = MCP_WRITERS.codex('/tmp/fake-pkg');
149
+
150
+ assert.strictEqual(configPath, path.join(tempHome, '.codex', 'config.toml'));
151
+ const content = fs.readFileSync(configPath, 'utf8');
152
+
153
+ assert.ok(content.includes('[mcp_servers.powerbi-modeling]'));
154
+ assert.ok(content.includes('command = "node"'));
155
+ assert.ok(content.includes('[mcp_servers.microsoft-learn]'));
156
+ assert.ok(content.includes(`url = "${MICROSOFT_LEARN_URL}"`));
157
+ });
158
+
159
+ test('preserves existing non-MCP TOML content', () => {
160
+ const configPath = path.join(tempHome, '.codex', 'config.toml');
161
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
162
+ fs.writeFileSync(configPath, '[profile]\nname = "default"\n');
163
+
164
+ MCP_WRITERS.codex('/tmp/fake-pkg');
165
+
166
+ const content = fs.readFileSync(configPath, 'utf8');
167
+ assert.ok(content.includes('[profile]'));
168
+ assert.ok(content.includes('name = "default"'));
169
+ assert.ok(content.includes('[mcp_servers.powerbi-modeling]'));
170
+ });
171
+
172
+ test('re-running install replaces MCP sections, does not duplicate', () => {
173
+ MCP_WRITERS.codex('/tmp/fake-pkg');
174
+ MCP_WRITERS.codex('/tmp/fake-pkg');
175
+
176
+ const content = fs.readFileSync(path.join(tempHome, '.codex', 'config.toml'), 'utf8');
177
+
178
+ // Each section name should appear exactly once
179
+ const modelingCount = (content.match(/\[mcp_servers\.powerbi-modeling\]/g) || []).length;
180
+ const learnCount = (content.match(/\[mcp_servers\.microsoft-learn\]/g) || []).length;
181
+ assert.strictEqual(modelingCount, 1);
182
+ assert.strictEqual(learnCount, 1);
183
+ });
184
+ });
@@ -2,21 +2,20 @@
2
2
  * Official Microsoft MCP Configuration Helpers
3
3
  * ============================================
4
4
  *
5
- * Single source of truth for the Claude Code plugin and legacy adapter
6
- * configurations that point at the official Microsoft Power BI / Fabric
7
- * MCP servers.
5
+ * Single source of truth for the MCP servers that bi-superpowers ships.
6
+ * Currently ships 2 servers, both from Microsoft:
7
+ *
8
+ * - powerbi-modeling-mcp: local stdio launcher that talks to Power BI Desktop
9
+ * via XMLA on localhost. Requires Power BI Desktop running with a model open.
10
+ * - microsoft-learn: HTTP MCP that pipes Microsoft Learn docs into the agent.
8
11
  *
9
12
  * @module lib/microsoft-mcp
10
13
  */
11
14
 
12
15
  const path = require('path');
13
16
 
14
- const REMOTE_POWERBI_URL = 'https://api.fabric.microsoft.com/v1/mcp/powerbi';
15
- const FABRIC_MCP_PACKAGE = '@microsoft/fabric-mcp@latest';
16
17
  const MICROSOFT_LEARN_URL = 'https://learn.microsoft.com/api/mcp';
17
18
  const MODELING_SERVER_NAME = 'powerbi-modeling-mcp';
18
- const REMOTE_SERVER_NAME = 'powerbi-remote';
19
- const FABRIC_SERVER_NAME = 'fabric-mcp-server';
20
19
  const LEARN_SERVER_NAME = 'microsoft-learn';
21
20
  const ABSOLUTE_LAUNCHER_MODE = 'absolute';
22
21
  const PLUGIN_ROOT_LAUNCHER_MODE = 'plugin-root';
@@ -51,15 +50,6 @@ function createPluginMcpConfig(options = {}) {
51
50
  const launcherPath = resolveModelingLauncherPath(options);
52
51
 
53
52
  return {
54
- [REMOTE_SERVER_NAME]: {
55
- type: 'http',
56
- url: REMOTE_POWERBI_URL,
57
- },
58
- [FABRIC_SERVER_NAME]: {
59
- type: 'stdio',
60
- command: 'npx',
61
- args: ['-y', FABRIC_MCP_PACKAGE, 'server', 'start', '--mode', 'all'],
62
- },
63
53
  [MODELING_SERVER_NAME]: {
64
54
  type: 'stdio',
65
55
  command: 'node',
@@ -170,12 +160,8 @@ function mergeMcpConfig(existingConfig, newConfig, format) {
170
160
  module.exports = {
171
161
  ABSOLUTE_LAUNCHER_MODE,
172
162
  PLUGIN_ROOT_LAUNCHER_MODE,
173
- REMOTE_POWERBI_URL,
174
- FABRIC_MCP_PACKAGE,
175
163
  MICROSOFT_LEARN_URL,
176
164
  MODELING_SERVER_NAME,
177
- REMOTE_SERVER_NAME,
178
- FABRIC_SERVER_NAME,
179
165
  LEARN_SERVER_NAME,
180
166
  resolveModelingLauncherPath,
181
167
  createPluginMcpConfig,
@@ -9,8 +9,6 @@ const path = require('node:path');
9
9
  const {
10
10
  ABSOLUTE_LAUNCHER_MODE,
11
11
  PLUGIN_ROOT_LAUNCHER_MODE,
12
- REMOTE_POWERBI_URL,
13
- FABRIC_MCP_PACKAGE,
14
12
  MICROSOFT_LEARN_URL,
15
13
  createPluginMcpConfig,
16
14
  createMcpConfigForFormat,
@@ -41,29 +39,36 @@ describe('resolveModelingLauncherPath', () => {
41
39
  });
42
40
 
43
41
  describe('createPluginMcpConfig', () => {
44
- test('returns the official Microsoft MCP defaults', () => {
42
+ test('returns the 2 official Microsoft MCP defaults', () => {
45
43
  const config = createPluginMcpConfig({
46
44
  packageDir: '/tmp/bi-superpowers',
47
45
  launcherMode: ABSOLUTE_LAUNCHER_MODE,
48
46
  });
49
47
 
50
- assert.deepStrictEqual(config['powerbi-remote'], {
51
- type: 'http',
52
- url: REMOTE_POWERBI_URL,
53
- });
54
- assert.deepStrictEqual(config['fabric-mcp-server'], {
55
- type: 'stdio',
56
- command: 'npx',
57
- args: ['-y', FABRIC_MCP_PACKAGE, 'server', 'start', '--mode', 'all'],
58
- });
48
+ // Should have exactly 2 servers
49
+ assert.strictEqual(Object.keys(config).length, 2);
50
+
51
+ // powerbi-modeling-mcp: local stdio launcher
59
52
  assert.strictEqual(config['powerbi-modeling-mcp'].type, 'stdio');
60
53
  assert.strictEqual(config['powerbi-modeling-mcp'].command, 'node');
61
54
  assert.ok(config['powerbi-modeling-mcp'].args[0].endsWith('powerbi-modeling-launcher.js'));
55
+
56
+ // microsoft-learn: HTTP MCP
62
57
  assert.deepStrictEqual(config['microsoft-learn'], {
63
58
  type: 'http',
64
59
  url: MICROSOFT_LEARN_URL,
65
60
  });
66
61
  });
62
+
63
+ test('does not include the old removed servers', () => {
64
+ const config = createPluginMcpConfig({
65
+ packageDir: '/tmp/bi-superpowers',
66
+ launcherMode: ABSOLUTE_LAUNCHER_MODE,
67
+ });
68
+
69
+ assert.strictEqual(config['powerbi-remote'], undefined);
70
+ assert.strictEqual(config['fabric-mcp-server'], undefined);
71
+ });
67
72
  });
68
73
 
69
74
  describe('createMcpConfigForFormat', () => {
@@ -71,10 +76,9 @@ describe('createMcpConfigForFormat', () => {
71
76
  const pluginConfig = createMcpConfigForFormat('plugin');
72
77
  const cursorConfig = createMcpConfigForFormat('cursor');
73
78
 
74
- assert.ok(pluginConfig['powerbi-remote']);
75
- assert.ok(cursorConfig['powerbi-remote']);
76
- assert.strictEqual(pluginConfig['powerbi-remote'].type, 'http');
77
- assert.strictEqual(cursorConfig['powerbi-remote'].type, 'http');
79
+ assert.ok(pluginConfig['powerbi-modeling-mcp']);
80
+ assert.ok(cursorConfig['microsoft-learn']);
81
+ assert.strictEqual(pluginConfig['microsoft-learn'].type, 'http');
78
82
  });
79
83
 
80
84
  test('returns nested mcpServers config for claude-like outputs', () => {
@@ -82,7 +86,7 @@ describe('createMcpConfigForFormat', () => {
82
86
 
83
87
  assert.ok(config.mcpServers);
84
88
  assert.ok(config.mcpServers['powerbi-modeling-mcp']);
85
- assert.ok(config.mcpServers['fabric-mcp-server']);
89
+ assert.ok(config.mcpServers['microsoft-learn']);
86
90
  });
87
91
  });
88
92
 
@@ -90,22 +94,22 @@ describe('mergeMcpConfig', () => {
90
94
  test('merges plugin configs without dropping existing entries', () => {
91
95
  const merged = mergeMcpConfig(
92
96
  { existing: { type: 'http', url: 'https://example.com' } },
93
- { 'powerbi-remote': { type: 'http', url: REMOTE_POWERBI_URL } },
97
+ { 'microsoft-learn': { type: 'http', url: MICROSOFT_LEARN_URL } },
94
98
  'plugin'
95
99
  );
96
100
 
97
101
  assert.ok(merged.existing);
98
- assert.ok(merged['powerbi-remote']);
102
+ assert.ok(merged['microsoft-learn']);
99
103
  });
100
104
 
101
105
  test('merges claude configs inside mcpServers', () => {
102
106
  const merged = mergeMcpConfig(
103
107
  { mcpServers: { existing: { type: 'http', url: 'https://example.com' } } },
104
- { mcpServers: { 'powerbi-remote': { type: 'http', url: REMOTE_POWERBI_URL } } },
108
+ { mcpServers: { 'microsoft-learn': { type: 'http', url: MICROSOFT_LEARN_URL } } },
105
109
  'claude'
106
110
  );
107
111
 
108
112
  assert.ok(merged.mcpServers.existing);
109
- assert.ok(merged.mcpServers['powerbi-remote']);
113
+ assert.ok(merged.mcpServers['microsoft-learn']);
110
114
  });
111
115
  });
@@ -12,35 +12,30 @@ console.log(`
12
12
  ╔═══════════════════════════════════════════════════════════╗
13
13
  ║ ║
14
14
  ║ BI Agent Superpowers v${VERSION.padEnd(34)}║
15
- AI-powered toolkit for Power BI, Fabric & Excel
15
+ Plugin para Power BI Desktop open source (MIT)
16
16
  ║ Developed by Lucas Sanchez (@luquimbo) ║
17
- ║ Open source — MIT licensed ║
18
17
  ║ ║
19
18
  ╚═══════════════════════════════════════════════════════════╝
20
19
 
21
- Instalado correctamente!
20
+ Instalado correctamente.
22
21
 
23
22
  Quick Start:
24
- 1. super install Instalá los 24 skills en tus agentes AI
25
- 2. super kickoff Inicializá el plugin en un proyecto
26
- 3. super powers Ver todos los skills disponibles
27
-
28
- Works with:
29
- Claude Code Plugin nativo (skills + commands + MCP)
30
- GitHub Copilot Agent skills
31
- Codex (OpenAI) Agent skills
32
- • Gemini CLI Agent skills
33
- Kilo Code Agent skills
34
- Claude Desktop MCPB extension (super build-desktop)
35
-
36
- 24 skills incluidos:
37
- /project-kickoff Analizá y planificá proyectos BI
38
- /data-model-design Diseñá modelos estrella
39
- /dax-doctor Debuggeá y optimizá medidas DAX
40
- /dax Mejores prácticas de DAX
41
- /power-query Patrones de Power Query
42
- /report-design Diseño y visualización de reportes
43
- ... y 18 más
23
+ super install # Instalá skills + MCPs en tus agentes AI
24
+
25
+ Funciona con los 5 agentes:
26
+ • Claude Code
27
+ GitHub Copilot
28
+ Codex (OpenAI)
29
+ Gemini CLI
30
+ Kilo Code
31
+
32
+ 2 skills iniciales:
33
+ /project-kickoff — Analizá tu proyecto BI
34
+ /pbi-connect — Conectá tu agente a Power BI Desktop
35
+
36
+ 2 MCP servers:
37
+ powerbi-modeling — Conexión local a Power BI Desktop (XMLA)
38
+ microsoft-learn — Docs de Microsoft Learn en contexto
44
39
 
45
40
  Documentation: https://github.com/luquimbo/bi-superpowers
46
41
  `);