@harbinger-ai/harbinger 0.1.2 → 0.1.4
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/lib/bounty/actions.js +45 -0
- package/lib/chat/actions.js +715 -0
- package/lib/chat/components/agents-page.js +918 -0
- package/lib/chat/components/agents-page.jsx +869 -0
- package/lib/chat/components/app-sidebar.js +17 -1
- package/lib/chat/components/app-sidebar.jsx +19 -1
- package/lib/chat/components/icons.js +145 -0
- package/lib/chat/components/icons.jsx +171 -0
- package/lib/chat/components/index.js +2 -0
- package/lib/chat/components/mcp-page.js +383 -55
- package/lib/chat/components/mcp-page.jsx +404 -101
- package/lib/chat/components/page-layout.js +41 -2
- package/lib/chat/components/page-layout.jsx +40 -2
- package/lib/chat/components/settings-layout.js +3 -2
- package/lib/chat/components/settings-layout.jsx +2 -1
- package/lib/chat/components/settings-providers-page.js +872 -0
- package/lib/chat/components/settings-providers-page.jsx +917 -0
- package/lib/chat/components/settings-secrets-page.js +91 -66
- package/lib/chat/components/settings-secrets-page.jsx +83 -72
- package/lib/chat/components/targets-page.js +554 -96
- package/lib/chat/components/targets-page.jsx +464 -114
- package/lib/mcp/actions.js +120 -0
- package/lib/mcp/registry.js +164 -0
- package/package.json +1 -1
package/lib/mcp/actions.js
CHANGED
|
@@ -86,6 +86,126 @@ export async function testMcpTool(toolName, args = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Get the MCP registry of curated servers.
|
|
91
|
+
*/
|
|
92
|
+
export async function getMcpRegistry() {
|
|
93
|
+
await requireAuth();
|
|
94
|
+
try {
|
|
95
|
+
const { MCP_REGISTRY, MCP_CATEGORIES } = await import('./registry.js');
|
|
96
|
+
return { servers: MCP_REGISTRY, categories: MCP_CATEGORIES };
|
|
97
|
+
} catch {
|
|
98
|
+
return { servers: [], categories: [] };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Add an MCP server to MCP_SERVERS.json.
|
|
104
|
+
*/
|
|
105
|
+
export async function addMcpServer(serverConfig) {
|
|
106
|
+
await requireAuth();
|
|
107
|
+
try {
|
|
108
|
+
let servers = [];
|
|
109
|
+
if (fs.existsSync(mcpServersFile)) {
|
|
110
|
+
try { servers = JSON.parse(fs.readFileSync(mcpServersFile, 'utf8')); } catch {}
|
|
111
|
+
}
|
|
112
|
+
servers.push(serverConfig);
|
|
113
|
+
fs.writeFileSync(mcpServersFile, JSON.stringify(servers, null, 2));
|
|
114
|
+
|
|
115
|
+
// Reload MCP client
|
|
116
|
+
try {
|
|
117
|
+
const { resetMcpClient, loadMcpTools } = await import('./client.js');
|
|
118
|
+
const { resetAgent } = await import('../ai/agent.js');
|
|
119
|
+
await resetMcpClient();
|
|
120
|
+
resetAgent();
|
|
121
|
+
await loadMcpTools();
|
|
122
|
+
} catch {}
|
|
123
|
+
|
|
124
|
+
return { success: true };
|
|
125
|
+
} catch (err) {
|
|
126
|
+
return { error: err.message };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Remove an MCP server by name.
|
|
132
|
+
*/
|
|
133
|
+
export async function removeMcpServer(name) {
|
|
134
|
+
await requireAuth();
|
|
135
|
+
try {
|
|
136
|
+
if (!fs.existsSync(mcpServersFile)) return { error: 'No servers configured' };
|
|
137
|
+
let servers = JSON.parse(fs.readFileSync(mcpServersFile, 'utf8'));
|
|
138
|
+
servers = servers.filter(s => s.name !== name);
|
|
139
|
+
fs.writeFileSync(mcpServersFile, JSON.stringify(servers, null, 2));
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const { resetMcpClient, loadMcpTools } = await import('./client.js');
|
|
143
|
+
const { resetAgent } = await import('../ai/agent.js');
|
|
144
|
+
await resetMcpClient();
|
|
145
|
+
resetAgent();
|
|
146
|
+
await loadMcpTools();
|
|
147
|
+
} catch {}
|
|
148
|
+
|
|
149
|
+
return { success: true };
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return { error: err.message };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Toggle an MCP server enabled/disabled by name.
|
|
157
|
+
*/
|
|
158
|
+
export async function toggleMcpServer(name) {
|
|
159
|
+
await requireAuth();
|
|
160
|
+
try {
|
|
161
|
+
if (!fs.existsSync(mcpServersFile)) return { error: 'No servers configured' };
|
|
162
|
+
let servers = JSON.parse(fs.readFileSync(mcpServersFile, 'utf8'));
|
|
163
|
+
const server = servers.find(s => s.name === name);
|
|
164
|
+
if (!server) return { error: 'Server not found' };
|
|
165
|
+
server.enabled = server.enabled === false ? true : false;
|
|
166
|
+
fs.writeFileSync(mcpServersFile, JSON.stringify(servers, null, 2));
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const { resetMcpClient, loadMcpTools } = await import('./client.js');
|
|
170
|
+
const { resetAgent } = await import('../ai/agent.js');
|
|
171
|
+
await resetMcpClient();
|
|
172
|
+
resetAgent();
|
|
173
|
+
await loadMcpTools();
|
|
174
|
+
} catch {}
|
|
175
|
+
|
|
176
|
+
return { success: true, enabled: server.enabled };
|
|
177
|
+
} catch (err) {
|
|
178
|
+
return { error: err.message };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Update an MCP server config by name.
|
|
184
|
+
*/
|
|
185
|
+
export async function updateMcpServer(name, updates) {
|
|
186
|
+
await requireAuth();
|
|
187
|
+
try {
|
|
188
|
+
if (!fs.existsSync(mcpServersFile)) return { error: 'No servers configured' };
|
|
189
|
+
let servers = JSON.parse(fs.readFileSync(mcpServersFile, 'utf8'));
|
|
190
|
+
const idx = servers.findIndex(s => s.name === name);
|
|
191
|
+
if (idx === -1) return { error: 'Server not found' };
|
|
192
|
+
servers[idx] = { ...servers[idx], ...updates };
|
|
193
|
+
fs.writeFileSync(mcpServersFile, JSON.stringify(servers, null, 2));
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const { resetMcpClient, loadMcpTools } = await import('./client.js');
|
|
197
|
+
const { resetAgent } = await import('../ai/agent.js');
|
|
198
|
+
await resetMcpClient();
|
|
199
|
+
resetAgent();
|
|
200
|
+
await loadMcpTools();
|
|
201
|
+
} catch {}
|
|
202
|
+
|
|
203
|
+
return { success: true };
|
|
204
|
+
} catch (err) {
|
|
205
|
+
return { error: err.message };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
89
209
|
/**
|
|
90
210
|
* Reset the MCP client and agent singletons, then reload tools.
|
|
91
211
|
*/
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated registry of popular MCP servers.
|
|
3
|
+
* Used by the MCP Hub UI to browse and install servers.
|
|
4
|
+
*/
|
|
5
|
+
export const MCP_REGISTRY = [
|
|
6
|
+
{
|
|
7
|
+
id: 'brave-search',
|
|
8
|
+
name: 'Brave Search',
|
|
9
|
+
description: 'Web search and content extraction via Brave Search API',
|
|
10
|
+
category: 'search',
|
|
11
|
+
url: 'https://mcp.brave.com/sse',
|
|
12
|
+
transport: 'sse',
|
|
13
|
+
requiresKey: true,
|
|
14
|
+
keyEnvVar: 'BRAVE_API_KEY',
|
|
15
|
+
keyUrl: 'https://brave.com/search/api/',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'tavily',
|
|
19
|
+
name: 'Tavily Search',
|
|
20
|
+
description: 'AI-powered web search optimized for LLMs',
|
|
21
|
+
category: 'search',
|
|
22
|
+
url: 'https://mcp.tavily.com/mcp',
|
|
23
|
+
transport: 'http',
|
|
24
|
+
requiresKey: true,
|
|
25
|
+
keyEnvVar: 'TAVILY_API_KEY',
|
|
26
|
+
keyUrl: 'https://tavily.com/',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'exa',
|
|
30
|
+
name: 'Exa Search',
|
|
31
|
+
description: 'Neural search engine for finding relevant content',
|
|
32
|
+
category: 'search',
|
|
33
|
+
url: 'https://mcp.exa.ai/mcp',
|
|
34
|
+
transport: 'http',
|
|
35
|
+
requiresKey: true,
|
|
36
|
+
keyEnvVar: 'EXA_API_KEY',
|
|
37
|
+
keyUrl: 'https://exa.ai/',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'firecrawl',
|
|
41
|
+
name: 'Firecrawl',
|
|
42
|
+
description: 'Web scraping and crawling with structured output',
|
|
43
|
+
category: 'web',
|
|
44
|
+
url: 'https://mcp.firecrawl.dev/sse',
|
|
45
|
+
transport: 'sse',
|
|
46
|
+
requiresKey: true,
|
|
47
|
+
keyEnvVar: 'FIRECRAWL_API_KEY',
|
|
48
|
+
keyUrl: 'https://firecrawl.dev/',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'browserbase',
|
|
52
|
+
name: 'Browserbase',
|
|
53
|
+
description: 'Cloud browser automation for web scraping and testing',
|
|
54
|
+
category: 'web',
|
|
55
|
+
url: 'https://mcp.browserbase.com/mcp',
|
|
56
|
+
transport: 'http',
|
|
57
|
+
requiresKey: true,
|
|
58
|
+
keyEnvVar: 'BROWSERBASE_API_KEY',
|
|
59
|
+
keyUrl: 'https://browserbase.com/',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'github',
|
|
63
|
+
name: 'GitHub',
|
|
64
|
+
description: 'Access GitHub repos, issues, PRs, and code search',
|
|
65
|
+
category: 'dev',
|
|
66
|
+
npmPackage: '@modelcontextprotocol/server-github',
|
|
67
|
+
transport: 'stdio',
|
|
68
|
+
requiresKey: true,
|
|
69
|
+
keyEnvVar: 'GITHUB_TOKEN',
|
|
70
|
+
keyUrl: 'https://github.com/settings/tokens',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'linear',
|
|
74
|
+
name: 'Linear',
|
|
75
|
+
description: 'Project management: issues, projects, and cycles',
|
|
76
|
+
category: 'dev',
|
|
77
|
+
url: 'https://mcp.linear.app/sse',
|
|
78
|
+
transport: 'sse',
|
|
79
|
+
requiresKey: true,
|
|
80
|
+
keyEnvVar: 'LINEAR_API_KEY',
|
|
81
|
+
keyUrl: 'https://linear.app/settings/api',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'slack',
|
|
85
|
+
name: 'Slack',
|
|
86
|
+
description: 'Read and send Slack messages, manage channels',
|
|
87
|
+
category: 'communication',
|
|
88
|
+
npmPackage: '@modelcontextprotocol/server-slack',
|
|
89
|
+
transport: 'stdio',
|
|
90
|
+
requiresKey: true,
|
|
91
|
+
keyEnvVar: 'SLACK_BOT_TOKEN',
|
|
92
|
+
keyUrl: 'https://api.slack.com/apps',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'filesystem',
|
|
96
|
+
name: 'Filesystem',
|
|
97
|
+
description: 'Read and write files on the local filesystem',
|
|
98
|
+
category: 'system',
|
|
99
|
+
npmPackage: '@modelcontextprotocol/server-filesystem',
|
|
100
|
+
transport: 'stdio',
|
|
101
|
+
requiresKey: false,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'memory',
|
|
105
|
+
name: 'Memory',
|
|
106
|
+
description: 'Persistent knowledge graph memory for LLMs',
|
|
107
|
+
category: 'system',
|
|
108
|
+
npmPackage: '@modelcontextprotocol/server-memory',
|
|
109
|
+
transport: 'stdio',
|
|
110
|
+
requiresKey: false,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'postgres',
|
|
114
|
+
name: 'PostgreSQL',
|
|
115
|
+
description: 'Query and manage PostgreSQL databases',
|
|
116
|
+
category: 'database',
|
|
117
|
+
npmPackage: '@modelcontextprotocol/server-postgres',
|
|
118
|
+
transport: 'stdio',
|
|
119
|
+
requiresKey: true,
|
|
120
|
+
keyEnvVar: 'POSTGRES_URL',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'sqlite',
|
|
124
|
+
name: 'SQLite',
|
|
125
|
+
description: 'Query and manage SQLite databases',
|
|
126
|
+
category: 'database',
|
|
127
|
+
npmPackage: '@modelcontextprotocol/server-sqlite',
|
|
128
|
+
transport: 'stdio',
|
|
129
|
+
requiresKey: false,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'sentry',
|
|
133
|
+
name: 'Sentry',
|
|
134
|
+
description: 'Error tracking and performance monitoring',
|
|
135
|
+
category: 'monitoring',
|
|
136
|
+
url: 'https://mcp.sentry.dev/sse',
|
|
137
|
+
transport: 'sse',
|
|
138
|
+
requiresKey: true,
|
|
139
|
+
keyEnvVar: 'SENTRY_AUTH_TOKEN',
|
|
140
|
+
keyUrl: 'https://sentry.io/settings/auth-tokens/',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'cloudflare',
|
|
144
|
+
name: 'Cloudflare',
|
|
145
|
+
description: 'Manage Cloudflare Workers, KV, R2, and D1',
|
|
146
|
+
category: 'cloud',
|
|
147
|
+
npmPackage: '@cloudflare/mcp-server-cloudflare',
|
|
148
|
+
transport: 'stdio',
|
|
149
|
+
requiresKey: true,
|
|
150
|
+
keyEnvVar: 'CLOUDFLARE_API_TOKEN',
|
|
151
|
+
keyUrl: 'https://dash.cloudflare.com/profile/api-tokens',
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
export const MCP_CATEGORIES = [
|
|
156
|
+
{ id: 'search', name: 'Search', color: 'blue' },
|
|
157
|
+
{ id: 'web', name: 'Web', color: 'orange' },
|
|
158
|
+
{ id: 'dev', name: 'Developer', color: 'purple' },
|
|
159
|
+
{ id: 'communication', name: 'Communication', color: 'green' },
|
|
160
|
+
{ id: 'system', name: 'System', color: 'gray' },
|
|
161
|
+
{ id: 'database', name: 'Database', color: 'cyan' },
|
|
162
|
+
{ id: 'monitoring', name: 'Monitoring', color: 'yellow' },
|
|
163
|
+
{ id: 'cloud', name: 'Cloud', color: 'indigo' },
|
|
164
|
+
];
|