openads-ai 0.2.8 → 0.2.10

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/cli.js CHANGED
@@ -12,7 +12,6 @@ import { runSetup } from './setup.js';
12
12
  import { runDoctor } from './doctor.js';
13
13
  import { runScheduleManager, runScheduledTask, openReportInBrowser, listReports } from './schedule.js';
14
14
  import enquirer from 'enquirer';
15
- import { hasGlobalRtk } from './token-optimizer.js';
16
15
  const __filename = fileURLToPath(import.meta.url);
17
16
  const __dirname = path.dirname(__filename);
18
17
  const pkgDir = path.resolve(__dirname, '..');
@@ -381,22 +380,15 @@ async function main() {
381
380
  baseDelayMs: 15000,
382
381
  provider: { maxRetryDelayMs: 120000 }
383
382
  };
384
- // Setup MCP Servers inside settings.json
385
- settings.mcpServers = settings.mcpServers || {};
386
- const useRtk = hasGlobalRtk();
387
- // Google Ads integration
388
- if (config.connectGoogle) {
389
- settings.mcpServers['google-ads'] = {
390
- command: useRtk ? 'rtk' : 'uvx',
391
- args: useRtk ? ['uvx', 'adloop'] : ['adloop']
392
- };
383
+ // Load custom MCP Extension to register Google Ads & Meta Ads tools
384
+ const mcpExtensionPath = path.resolve(pkgDir, 'dist', 'mcp-extension.js');
385
+ settings.extensions = settings.extensions || [];
386
+ if (!settings.extensions.includes(mcpExtensionPath)) {
387
+ settings.extensions.push(mcpExtensionPath);
393
388
  }
394
- // Inject Meta MCP server (remote SSE server) if token is present
395
- if (config.metaToken) {
396
- settings.mcpServers['meta-ads'] = {
397
- url: 'https://mcp.facebook.com/ads/sse',
398
- env: { META_ACCESS_TOKEN: config.metaToken }
399
- };
389
+ // Clean up legacy built-in mcpServers block if present to prevent config clutter
390
+ if (settings.mcpServers) {
391
+ delete settings.mcpServers;
400
392
  }
401
393
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
402
394
  // ─── Write models.json for Local AI ─────────────────────────────
@@ -0,0 +1,137 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { hasGlobalRtk } from "./token-optimizer.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import os from "os";
7
+ export default async function (pi) {
8
+ const configDir = path.join(os.homedir(), '.openads');
9
+ const configPath = path.join(configDir, 'openads.config.json');
10
+ if (!fs.existsSync(configPath)) {
11
+ return;
12
+ }
13
+ let config = {};
14
+ try {
15
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
16
+ }
17
+ catch (e) {
18
+ return;
19
+ }
20
+ const clients = [];
21
+ // ─── Google Ads MCP ───────────────────────────────────────────────
22
+ if (config.connectGoogle) {
23
+ try {
24
+ const useRtk = hasGlobalRtk();
25
+ const command = useRtk ? 'rtk' : 'uvx';
26
+ const args = useRtk ? ['uvx', 'adloop'] : ['adloop'];
27
+ const googleTransport = new StdioClientTransport({
28
+ command,
29
+ args,
30
+ });
31
+ const googleClient = new Client({ name: "openads-google-ads-client", version: "0.2.9" }, { capabilities: {} });
32
+ await googleClient.connect(googleTransport);
33
+ clients.push({ name: "google-ads", client: googleClient });
34
+ }
35
+ catch (err) {
36
+ console.error(`[OpenAds MCP] Failed to connect to Google Ads: ${err.message}`);
37
+ }
38
+ }
39
+ // ─── Meta Ads MCP (Local Stdio Server via meta-ads-mcp) ───────────
40
+ if (config.metaToken) {
41
+ try {
42
+ const useRtk = hasGlobalRtk();
43
+ const command = useRtk ? 'rtk' : 'npx';
44
+ const args = useRtk ? ['npx', '-y', 'meta-ads-mcp'] : ['-y', 'meta-ads-mcp'];
45
+ const metaTransport = new StdioClientTransport({
46
+ command,
47
+ args,
48
+ env: {
49
+ ...process.env,
50
+ "META_ACCESS_TOKEN": config.metaToken
51
+ }
52
+ });
53
+ const metaClient = new Client({ name: "openads-meta-ads-client", version: "0.2.9" }, { capabilities: {} });
54
+ await metaClient.connect(metaTransport);
55
+ clients.push({ name: "meta-ads", client: metaClient });
56
+ }
57
+ catch (err) {
58
+ console.error(`[OpenAds MCP] Failed to connect to Meta Ads: ${err.message}`);
59
+ }
60
+ }
61
+ // ─── Dynamically Register Tools ──────────────────────────────────
62
+ for (const { name: serverName, client } of clients) {
63
+ try {
64
+ const toolsResult = await client.listTools();
65
+ for (const tool of toolsResult.tools) {
66
+ pi.registerTool({
67
+ name: tool.name,
68
+ label: tool.name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
69
+ description: tool.description || `MCP Tool: ${tool.name}`,
70
+ parameters: tool.inputSchema,
71
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
72
+ // Check for Campaign modification write tools
73
+ const isWriteTool = /create|update|delete|set|pause|resume|scale|modify|change/i.test(tool.name);
74
+ if (isWriteTool) {
75
+ if (config.mode === 'audit') {
76
+ return {
77
+ content: [{
78
+ type: "text",
79
+ text: `Blocking execution of '${tool.name}' because OpenAds is in Audit Mode (Safe/Read-only). Under Audit Mode, campaign write operations are disabled. To execute active changes, please toggle to Launch Mode in Settings (\`openads setup\`).`
80
+ }],
81
+ details: {},
82
+ isError: true,
83
+ };
84
+ }
85
+ if (config.mode === 'launch') {
86
+ const confirmMessage = `You are about to execute a campaign write operation:\n\nTool: ${tool.name}\nParameters:\n${JSON.stringify(params, null, 2)}\n\nDo you want to authorize this change?`;
87
+ const ok = await ctx.ui.confirm("Confirm Campaign Action ⚠️", confirmMessage);
88
+ if (!ok) {
89
+ return {
90
+ content: [{
91
+ type: "text",
92
+ text: `Action cancelled: execution of '${tool.name}' was not confirmed.`
93
+ }],
94
+ details: {},
95
+ isError: true,
96
+ };
97
+ }
98
+ }
99
+ }
100
+ try {
101
+ const response = await client.callTool({
102
+ name: tool.name,
103
+ arguments: params,
104
+ });
105
+ return {
106
+ content: response.content,
107
+ details: {},
108
+ };
109
+ }
110
+ catch (err) {
111
+ return {
112
+ content: [{
113
+ type: "text",
114
+ text: `Error executing MCP tool ${tool.name}: ${err.message}`
115
+ }],
116
+ details: {},
117
+ isError: true,
118
+ };
119
+ }
120
+ }
121
+ });
122
+ }
123
+ }
124
+ catch (err) {
125
+ console.error(`[OpenAds MCP] Failed to register tools for ${serverName}: ${err.message}`);
126
+ }
127
+ }
128
+ // Register clean shutdown on session exit
129
+ pi.on("session_shutdown", async () => {
130
+ for (const { client } of clients) {
131
+ try {
132
+ await client.close();
133
+ }
134
+ catch (e) { }
135
+ }
136
+ });
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openads-ai",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Open-source AI command center for digital marketers. Audit campaigns, write ad copy, and build strategies — from your terminal.",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -64,6 +64,7 @@
64
64
  },
65
65
  "dependencies": {
66
66
  "@earendil-works/pi-coding-agent": "^0.75.5",
67
+ "@modelcontextprotocol/sdk": "^1.29.0",
67
68
  "boxen": "^8.0.1",
68
69
  "chalk": "^5.3.0",
69
70
  "enquirer": "^2.4.1",