@hailer/mcp 0.1.3 → 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/.claude/agents/ada.md +36 -5
- package/.claude/agents/helga.md +9 -9
- package/.claude/agents/ingrid.md +4 -4
- package/.claude/assistant-knowledge.md +23 -0
- package/.claude/hooks/agent-failure-detector.cjs +27 -3
- package/.claude/hooks/app-edit-guard.cjs +33 -10
- package/.claude/hooks/builder-mode-manager.cjs +33 -5
- package/.claude/hooks/interactive-mode.cjs +29 -3
- package/.claude/hooks/mcp-server-guard.cjs +20 -4
- package/.claude/hooks/post-scaffold-hook.cjs +23 -4
- package/.claude/hooks/publish-template-guard.cjs +29 -11
- package/.claude/hooks/src-edit-guard.cjs +30 -6
- package/.claude/settings.json +0 -5
- package/.claude/skills/insight-join-patterns/SKILL.md +50 -88
- package/.claude/skills/json-only-output/SKILL.md +7 -3
- package/.claude/skills/optional-parameters/SKILL.md +7 -3
- package/.claude/skills/tool-response-verification/SKILL.md +7 -3
- package/dist/client/mcp-assistant.d.ts +21 -0
- package/dist/client/mcp-assistant.js +58 -0
- package/dist/client/mcp-client.js +8 -2
- package/dist/client/providers/assistant-provider.d.ts +17 -0
- package/dist/client/providers/assistant-provider.js +51 -0
- package/dist/client/providers/openai-provider.js +10 -1
- package/dist/client/tool-schema-loader.d.ts +1 -0
- package/dist/client/tool-schema-loader.js +9 -1
- package/dist/client/types.d.ts +2 -1
- package/dist/config.d.ts +5 -1
- package/dist/config.js +11 -5
- package/mcp-system-prompt.txt +127 -0
- package/package.json +1 -1
- package/.claude/hooks/sdk-delete-guard.cjs +0 -180
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: insight-join-patterns
|
|
3
|
+
description: Correct JOIN syntax for Hailer insights with ActivityLink fields
|
|
4
|
+
triggers: JOIN query errors, missing columns, NULL results in insight queries
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<problem>
|
|
4
8
|
When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
5
9
|
1. Include `_id` meta field in BOTH source definitions
|
|
6
10
|
2. Join ON the activitylink field value equals target _id
|
|
7
11
|
3. Use the activitylink fieldId (NOT the key) for the JOIN condition
|
|
12
|
+
</problem>
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**Always Required:**
|
|
14
|
+
<rules>
|
|
12
15
|
- Both workflows need `{ name: 'id', meta: '_id' }` in their fields array
|
|
13
16
|
- JOIN condition: `source1.activityLinkFieldName = source2.id`
|
|
14
17
|
- Use LEFT JOIN for optional relationships (activitylink can be null)
|
|
15
18
|
- Use INNER JOIN only when relationship must exist
|
|
19
|
+
</rules>
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### Basic ActivityLink JOIN
|
|
21
|
+
<correct>
|
|
20
22
|
```javascript
|
|
21
23
|
// Players workflow has "club" field (activitylink to Clubs workflow)
|
|
22
24
|
{
|
|
@@ -42,7 +44,38 @@ When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
|
42
44
|
query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.id'
|
|
43
45
|
}
|
|
44
46
|
```
|
|
47
|
+
</correct>
|
|
48
|
+
|
|
49
|
+
<wrong>
|
|
50
|
+
```javascript
|
|
51
|
+
// ❌ WRONG - Missing _id in clubs source
|
|
52
|
+
{
|
|
53
|
+
sources: [
|
|
54
|
+
{
|
|
55
|
+
name: 'p',
|
|
56
|
+
workflowId: 'players-id',
|
|
57
|
+
fields: [
|
|
58
|
+
{ name: 'player_name', meta: 'name' },
|
|
59
|
+
{ name: 'id', meta: '_id' },
|
|
60
|
+
{ name: 'club', fieldId: 'club-field-id' }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'c',
|
|
65
|
+
workflowId: 'clubs-id',
|
|
66
|
+
fields: [
|
|
67
|
+
{ name: 'club_name', meta: 'name' }
|
|
68
|
+
// Missing: { name: 'id', meta: '_id' }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.id'
|
|
73
|
+
}
|
|
74
|
+
// Error: "no such column: c.id"
|
|
75
|
+
```
|
|
76
|
+
</wrong>
|
|
45
77
|
|
|
78
|
+
<examples>
|
|
46
79
|
### Three-Way JOIN (Tasks -> Topics -> Projects)
|
|
47
80
|
```javascript
|
|
48
81
|
{
|
|
@@ -53,7 +86,7 @@ When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
|
53
86
|
fields: [
|
|
54
87
|
{ name: 'task_name', meta: 'name' },
|
|
55
88
|
{ name: 'id', meta: '_id' },
|
|
56
|
-
{ name: 'topic', fieldId: 'topic-field-id' }
|
|
89
|
+
{ name: 'topic', fieldId: 'topic-field-id' }
|
|
57
90
|
]
|
|
58
91
|
},
|
|
59
92
|
{
|
|
@@ -62,7 +95,7 @@ When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
|
62
95
|
fields: [
|
|
63
96
|
{ name: 'topic_name', meta: 'name' },
|
|
64
97
|
{ name: 'id', meta: '_id' },
|
|
65
|
-
{ name: 'project', fieldId: 'project-field-id' }
|
|
98
|
+
{ name: 'project', fieldId: 'project-field-id' }
|
|
66
99
|
]
|
|
67
100
|
},
|
|
68
101
|
{
|
|
@@ -75,10 +108,7 @@ When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
|
75
108
|
}
|
|
76
109
|
],
|
|
77
110
|
query: `
|
|
78
|
-
SELECT
|
|
79
|
-
t.task_name,
|
|
80
|
-
top.topic_name,
|
|
81
|
-
p.project_name
|
|
111
|
+
SELECT t.task_name, top.topic_name, p.project_name
|
|
82
112
|
FROM t
|
|
83
113
|
LEFT JOIN top ON t.topic = top.id
|
|
84
114
|
LEFT JOIN p ON top.project = p.id
|
|
@@ -109,76 +139,16 @@ When joining workflows with ActivityLink fields in Hailer insights, you must:
|
|
|
109
139
|
}
|
|
110
140
|
],
|
|
111
141
|
query: `
|
|
112
|
-
SELECT
|
|
113
|
-
teams.team_name,
|
|
114
|
-
COUNT(*) as match_count
|
|
142
|
+
SELECT teams.team_name, COUNT(*) as match_count
|
|
115
143
|
FROM matches
|
|
116
144
|
LEFT JOIN teams ON matches.home_team = teams.id
|
|
117
145
|
GROUP BY teams.team_name
|
|
118
146
|
`
|
|
119
147
|
}
|
|
120
148
|
```
|
|
149
|
+
</examples>
|
|
121
150
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### Missing _id Field
|
|
125
|
-
```javascript
|
|
126
|
-
// ❌ WRONG - Missing _id in clubs source
|
|
127
|
-
{
|
|
128
|
-
sources: [
|
|
129
|
-
{
|
|
130
|
-
name: 'p',
|
|
131
|
-
workflowId: 'players-id',
|
|
132
|
-
fields: [
|
|
133
|
-
{ name: 'player_name', meta: 'name' },
|
|
134
|
-
{ name: 'id', meta: '_id' },
|
|
135
|
-
{ name: 'club', fieldId: 'club-field-id' }
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
name: 'c',
|
|
140
|
-
workflowId: 'clubs-id',
|
|
141
|
-
fields: [
|
|
142
|
-
{ name: 'club_name', meta: 'name' }
|
|
143
|
-
// Missing: { name: 'id', meta: '_id' }
|
|
144
|
-
]
|
|
145
|
-
}
|
|
146
|
-
],
|
|
147
|
-
query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.id'
|
|
148
|
-
}
|
|
149
|
-
// Error: "no such column: c.id"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Using Key Instead of FieldId
|
|
153
|
-
```javascript
|
|
154
|
-
// ❌ WRONG - Using field key instead of fieldId
|
|
155
|
-
{
|
|
156
|
-
sources: [
|
|
157
|
-
{
|
|
158
|
-
name: 'p',
|
|
159
|
-
workflowId: 'players-id',
|
|
160
|
-
fields: [
|
|
161
|
-
{ name: 'player_name', meta: 'name' },
|
|
162
|
-
{ name: 'id', meta: '_id' },
|
|
163
|
-
{ name: 'club', key: 'club' } // Wrong! Use fieldId
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
]
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Wrong JOIN Syntax
|
|
171
|
-
```javascript
|
|
172
|
-
// ❌ WRONG - Trying to join on name instead of id
|
|
173
|
-
query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.club_name'
|
|
174
|
-
|
|
175
|
-
// ❌ WRONG - Using field key in JOIN instead of column name
|
|
176
|
-
query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.clubId = c.id'
|
|
177
|
-
// (Should use column name from sources definition: p.club = c.id)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Troubleshooting
|
|
181
|
-
|
|
151
|
+
<troubleshooting>
|
|
182
152
|
**Error: "no such column: c.id"**
|
|
183
153
|
- Missing `{ name: 'id', meta: '_id' }` in target workflow source
|
|
184
154
|
|
|
@@ -189,21 +159,13 @@ query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.clubId = c.id'
|
|
|
189
159
|
**NULL results for joined data**
|
|
190
160
|
- ActivityLink field is empty/null for some activities (expected with LEFT JOIN)
|
|
191
161
|
- Use INNER JOIN if you only want activities with relationships
|
|
162
|
+
</troubleshooting>
|
|
192
163
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
1. Get schema: `get_workflow_schema({ workflowId, phaseId })`
|
|
196
|
-
2. Find ActivityLink field ID and target workflow ID
|
|
197
|
-
3. Build sources with BOTH _id fields
|
|
198
|
-
4. Preview query: `preview_insight({ sources, query })`
|
|
199
|
-
5. Fix errors, re-preview
|
|
200
|
-
6. Create insight when preview succeeds
|
|
201
|
-
|
|
202
|
-
## Quick Checklist
|
|
203
|
-
|
|
164
|
+
<checklist>
|
|
204
165
|
Before creating an insight with JOINs:
|
|
205
166
|
- [ ] Both workflow sources include `{ name: 'id', meta: '_id' }`
|
|
206
167
|
- [ ] ActivityLink field uses `fieldId` (NOT `key`)
|
|
207
168
|
- [ ] JOIN condition uses column names from sources (e.g., `p.club = c.id`)
|
|
208
169
|
- [ ] Using LEFT JOIN (unless relationship required)
|
|
209
170
|
- [ ] Tested with `preview_insight` first
|
|
171
|
+
</checklist>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: json-only-output
|
|
3
|
+
description: Fix agents adding prose after JSON responses
|
|
4
|
+
triggers: Agent outputs explanation text after valid JSON
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
<
|
|
7
|
+
<problem>
|
|
4
8
|
Agents violate JSON-only protocol by adding prose explanations AFTER their JSON response.
|
|
5
|
-
</
|
|
9
|
+
</problem>
|
|
6
10
|
|
|
7
11
|
<correct>
|
|
8
12
|
```json
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: optional-parameters
|
|
3
|
+
description: Omit optional parameters instead of passing empty values
|
|
4
|
+
triggers: Tool error about empty array/string when parameter should be omitted
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
<
|
|
7
|
+
<problem>
|
|
4
8
|
Tool parameter is required in schema but should be OMITTED (not passed at all) when not needed, rather than passed as empty array/string.
|
|
5
|
-
</
|
|
9
|
+
</problem>
|
|
6
10
|
|
|
7
11
|
<correct>
|
|
8
12
|
```typescript
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: tool-response-verification
|
|
3
|
+
description: Ensure agents verify tool execution before claiming success
|
|
4
|
+
triggers: Agent returns success without checking tool result
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
<
|
|
7
|
+
<problem>
|
|
4
8
|
Agent fabricates success response without verifying actual tool execution result.
|
|
5
|
-
</
|
|
9
|
+
</problem>
|
|
6
10
|
|
|
7
11
|
<correct>
|
|
8
12
|
```typescript
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Assistant (Lightweight)
|
|
3
|
+
*
|
|
4
|
+
* Pure pass-through chatbot. System prompt is loaded on AI PC side.
|
|
5
|
+
* Just forwards user messages, no local processing.
|
|
6
|
+
*/
|
|
7
|
+
export declare class McpAssistant {
|
|
8
|
+
private client;
|
|
9
|
+
constructor(config: {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
baseURL?: string;
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Chat - pure pass-through to local LLM
|
|
15
|
+
*/
|
|
16
|
+
chat(userMessage: string, conversationHistory?: Array<{
|
|
17
|
+
role: 'user' | 'assistant';
|
|
18
|
+
content: string;
|
|
19
|
+
}>): Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=mcp-assistant.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Assistant (Lightweight)
|
|
4
|
+
*
|
|
5
|
+
* Pure pass-through chatbot. System prompt is loaded on AI PC side.
|
|
6
|
+
* Just forwards user messages, no local processing.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.McpAssistant = void 0;
|
|
13
|
+
const openai_1 = __importDefault(require("openai"));
|
|
14
|
+
const logger_1 = require("../lib/logger");
|
|
15
|
+
const logger = (0, logger_1.createLogger)({ component: 'McpAssistant' });
|
|
16
|
+
class McpAssistant {
|
|
17
|
+
client;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.client = new openai_1.default({
|
|
20
|
+
apiKey: config.apiKey,
|
|
21
|
+
baseURL: config.baseURL,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Chat - pure pass-through to local LLM
|
|
26
|
+
*/
|
|
27
|
+
async chat(userMessage, conversationHistory = []) {
|
|
28
|
+
const model = process.env.OPENAI_MODEL || 'gpt-4o';
|
|
29
|
+
try {
|
|
30
|
+
const messages = [
|
|
31
|
+
...conversationHistory.slice(-4).map(msg => ({
|
|
32
|
+
role: msg.role,
|
|
33
|
+
content: msg.content,
|
|
34
|
+
})),
|
|
35
|
+
{ role: 'user', content: userMessage },
|
|
36
|
+
];
|
|
37
|
+
logger.debug('Chat', { model, msgCount: messages.length });
|
|
38
|
+
const response = await this.client.chat.completions.create({
|
|
39
|
+
model,
|
|
40
|
+
messages,
|
|
41
|
+
max_tokens: 500,
|
|
42
|
+
temperature: 0.7,
|
|
43
|
+
});
|
|
44
|
+
const reply = response.choices[0]?.message?.content || 'Could not respond.';
|
|
45
|
+
logger.info('Response', {
|
|
46
|
+
in: response.usage?.prompt_tokens,
|
|
47
|
+
out: response.usage?.completion_tokens,
|
|
48
|
+
});
|
|
49
|
+
return reply;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.error('Chat failed', error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.McpAssistant = McpAssistant;
|
|
58
|
+
//# sourceMappingURL=mcp-assistant.js.map
|
|
@@ -8,6 +8,7 @@ exports.McpClient = void 0;
|
|
|
8
8
|
const message_processor_1 = require("./message-processor");
|
|
9
9
|
const openai_provider_1 = require("./providers/openai-provider");
|
|
10
10
|
const anthropic_provider_1 = require("./providers/anthropic-provider");
|
|
11
|
+
const assistant_provider_1 = require("./providers/assistant-provider");
|
|
11
12
|
const multi_bot_manager_1 = require("./multi-bot-manager");
|
|
12
13
|
const agent_tracker_1 = require("./agent-tracker");
|
|
13
14
|
const token_tracker_1 = require("./token-tracker");
|
|
@@ -102,6 +103,9 @@ class McpClient {
|
|
|
102
103
|
case "anthropic":
|
|
103
104
|
provider = new anthropic_provider_1.AnthropicProvider(providerConfig);
|
|
104
105
|
break;
|
|
106
|
+
case "assistant":
|
|
107
|
+
provider = new assistant_provider_1.AssistantProvider(providerConfig);
|
|
108
|
+
break;
|
|
105
109
|
case "gemini":
|
|
106
110
|
// TODO: Implement GeminiProvider
|
|
107
111
|
console.warn(`⚠️ Provider type ${providerConfig.type} not yet implemented`);
|
|
@@ -306,9 +310,11 @@ class McpClient {
|
|
|
306
310
|
}
|
|
307
311
|
return; // Skip LLM processing
|
|
308
312
|
}
|
|
309
|
-
// Generate and post personalized confirmation message
|
|
313
|
+
// Generate and post personalized confirmation message (skip if empty)
|
|
310
314
|
const confirmationMessage = await provider.generateConfirmationMessage(message);
|
|
311
|
-
|
|
315
|
+
if (confirmationMessage) {
|
|
316
|
+
await this.messageProcessor.postMessage(message.discussionId, confirmationMessage, message.workspaceId, message.mentionedOrDirectMessagedBotId);
|
|
317
|
+
}
|
|
312
318
|
const response = await this.processMessage(message, provider);
|
|
313
319
|
await this.handleResponse(message, response);
|
|
314
320
|
// LOG COMPLETION EVENT - Bot finished processing
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistant Provider
|
|
3
|
+
*
|
|
4
|
+
* Pure pass-through chatbot. No tools, no local system prompt.
|
|
5
|
+
* System prompt is loaded on AI PC side via llama-server.
|
|
6
|
+
*/
|
|
7
|
+
import { ChatMessage, McpResponse } from "../types";
|
|
8
|
+
import { LlmProvider } from "./llm-provider";
|
|
9
|
+
import { LlmProviderConfig } from "../types";
|
|
10
|
+
export declare class AssistantProvider extends LlmProvider {
|
|
11
|
+
private assistant;
|
|
12
|
+
constructor(config: LlmProviderConfig);
|
|
13
|
+
generateConfirmationMessage(_userMessage: ChatMessage): Promise<string>;
|
|
14
|
+
processMessage(userMessage: ChatMessage, _mcpServerUrl: string, _botMcpApiKey: string, _botEmail: string): Promise<McpResponse>;
|
|
15
|
+
protected callMcpTool(): Promise<any>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=assistant-provider.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Assistant Provider
|
|
4
|
+
*
|
|
5
|
+
* Pure pass-through chatbot. No tools, no local system prompt.
|
|
6
|
+
* System prompt is loaded on AI PC side via llama-server.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AssistantProvider = void 0;
|
|
10
|
+
const llm_provider_1 = require("./llm-provider");
|
|
11
|
+
const mcp_assistant_1 = require("../mcp-assistant");
|
|
12
|
+
class AssistantProvider extends llm_provider_1.LlmProvider {
|
|
13
|
+
assistant;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
super(config);
|
|
16
|
+
this.assistant = new mcp_assistant_1.McpAssistant({
|
|
17
|
+
apiKey: config.apiKey,
|
|
18
|
+
baseURL: config.baseURL,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async generateConfirmationMessage(_userMessage) {
|
|
22
|
+
// No confirmation message - direct response only
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
async processMessage(userMessage, _mcpServerUrl, _botMcpApiKey, _botEmail) {
|
|
26
|
+
if (!this.isEnabled()) {
|
|
27
|
+
return { success: false, error: "Assistant not enabled" };
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
const cleanedMessage = this.removeMentions(userMessage);
|
|
32
|
+
this.logger.debug('Chat request', { user: userMessage.userName });
|
|
33
|
+
const response = await this.assistant.chat(cleanedMessage);
|
|
34
|
+
this.logger.info('Chat done', { duration: Date.now() - startTime });
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
response,
|
|
38
|
+
toolCalls: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
this.logError(error, "processMessage");
|
|
43
|
+
return { success: false, error: error.message };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async callMcpTool() {
|
|
47
|
+
throw new Error("Assistant does not use tools");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.AssistantProvider = AssistantProvider;
|
|
51
|
+
//# sourceMappingURL=assistant-provider.js.map
|
|
@@ -20,6 +20,7 @@ class OpenAIProvider extends llm_provider_1.LlmProvider {
|
|
|
20
20
|
super(config);
|
|
21
21
|
this.client = new openai_1.default({
|
|
22
22
|
apiKey: config.apiKey,
|
|
23
|
+
baseURL: config.baseURL,
|
|
23
24
|
});
|
|
24
25
|
this.contextManager = (0, context_manager_1.getContextManager)({
|
|
25
26
|
openaiApiKey: config.apiKey,
|
|
@@ -72,11 +73,19 @@ class OpenAIProvider extends llm_provider_1.LlmProvider {
|
|
|
72
73
|
const startTime = Date.now();
|
|
73
74
|
// Load tool index with automatic filtering
|
|
74
75
|
// Chat bot only gets READ + WRITE tools (no PLAYGROUND tools)
|
|
76
|
+
// In 'minimal' mode, only load essential chat tools (~3 tools vs 40+)
|
|
75
77
|
const allowedGroups = [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE];
|
|
78
|
+
const minimalChatTools = [
|
|
79
|
+
'add_discussion_message',
|
|
80
|
+
'fetch_discussion_messages',
|
|
81
|
+
'search_workspace_users'
|
|
82
|
+
];
|
|
83
|
+
const isMinimalMode = process.env.CHAT_BOT_MODE === 'minimal';
|
|
76
84
|
const toolIndex = await this.toolSchemaLoader.loadToolIndex({
|
|
77
85
|
mcpServerUrl,
|
|
78
86
|
mcpServerApiKey: botMcpApiKey,
|
|
79
|
-
allowedGroups
|
|
87
|
+
allowedGroups,
|
|
88
|
+
allowedTools: isMinimalMode ? minimalChatTools : undefined
|
|
80
89
|
});
|
|
81
90
|
if (!toolIndex || toolIndex.length === 0) {
|
|
82
91
|
this.logger.warn("No MCP tools available");
|
|
@@ -22,9 +22,17 @@ class ToolSchemaLoader {
|
|
|
22
22
|
* Returns lightweight tool list with optional exclusions
|
|
23
23
|
*/
|
|
24
24
|
async loadToolIndex(options) {
|
|
25
|
-
const { mcpServerUrl, mcpServerApiKey, allowedGroups, excludeMessageFetchTools } = options;
|
|
25
|
+
const { mcpServerUrl, mcpServerApiKey, allowedGroups, allowedTools, excludeMessageFetchTools } = options;
|
|
26
26
|
// Fetch tool index from MCP server with group filtering
|
|
27
27
|
let toolIndex = await this.fetchMcpToolIndex(mcpServerUrl, mcpServerApiKey, allowedGroups);
|
|
28
|
+
// Filter by explicit whitelist if provided (overrides group filtering)
|
|
29
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
30
|
+
toolIndex = toolIndex.filter(tool => allowedTools.includes(tool.name));
|
|
31
|
+
logger.info("Filtered tools by whitelist", {
|
|
32
|
+
allowedTools,
|
|
33
|
+
resultCount: toolIndex.length
|
|
34
|
+
});
|
|
35
|
+
}
|
|
28
36
|
// Optionally exclude message fetch tools if explicitly requested
|
|
29
37
|
if (excludeMessageFetchTools) {
|
|
30
38
|
const excludedTools = ['fetch_discussion_messages', 'fetch_previous_discussion_messages'];
|
package/dist/client/types.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export interface McpClientConfig {
|
|
|
16
16
|
}
|
|
17
17
|
export interface LlmProviderConfig {
|
|
18
18
|
name: string;
|
|
19
|
-
type: "openai" | "anthropic" | "gemini";
|
|
19
|
+
type: "openai" | "anthropic" | "gemini" | "assistant";
|
|
20
20
|
apiKey: string;
|
|
21
|
+
baseURL?: string;
|
|
21
22
|
model: string;
|
|
22
23
|
enabled: boolean;
|
|
23
24
|
maxTokens?: number;
|
package/dist/config.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare const environment: {
|
|
|
35
35
|
MCP_CLIENT_AGENT_IDS: string[];
|
|
36
36
|
TOKEN_USAGE_BOT_ENABLED: boolean;
|
|
37
37
|
AGENT_ACTIVITY_BOT_ENABLED: boolean;
|
|
38
|
+
CHAT_BOT_MODE: "full" | "minimal" | "assistant";
|
|
38
39
|
ADAPTIVE_DOCUMENTATION_BOT_ENABLED: boolean;
|
|
39
40
|
ADAPTIVE_AUTO_UPDATE: boolean;
|
|
40
41
|
ADAPTIVE_UPDATE_INTERVAL: number;
|
|
@@ -49,6 +50,8 @@ export declare const environment: {
|
|
|
49
50
|
WORKSPACE_CONFIG_PATH?: string | undefined;
|
|
50
51
|
DEV_APPS_PATH?: string | undefined;
|
|
51
52
|
OPENAI_API_KEY?: string | undefined;
|
|
53
|
+
OPENAI_API_BASE?: string | undefined;
|
|
54
|
+
OPENAI_MODEL?: string | undefined;
|
|
52
55
|
ANTHROPIC_API_KEY?: string | undefined;
|
|
53
56
|
};
|
|
54
57
|
export interface HailerAccount {
|
|
@@ -60,8 +63,9 @@ export interface HailerAccount {
|
|
|
60
63
|
}
|
|
61
64
|
export interface LlmProvider {
|
|
62
65
|
name: string;
|
|
63
|
-
type: 'openai' | 'anthropic';
|
|
66
|
+
type: 'openai' | 'anthropic' | 'assistant';
|
|
64
67
|
apiKey: string;
|
|
68
|
+
baseURL?: string;
|
|
65
69
|
model: string;
|
|
66
70
|
enabled: boolean;
|
|
67
71
|
}
|
package/dist/config.js
CHANGED
|
@@ -102,6 +102,8 @@ const environmentSchema = zod_1.z.object({
|
|
|
102
102
|
DEV_APPS_PATH: zod_1.z.string().optional(),
|
|
103
103
|
// LLM providers
|
|
104
104
|
OPENAI_API_KEY: zod_1.z.string().min(1).optional(),
|
|
105
|
+
OPENAI_API_BASE: zod_1.z.string().url().optional(),
|
|
106
|
+
OPENAI_MODEL: zod_1.z.string().optional(),
|
|
105
107
|
ANTHROPIC_API_KEY: zod_1.z.string().min(1).optional(),
|
|
106
108
|
// MCP client settings
|
|
107
109
|
MCP_SERVER_URL: zod_1.z.string().url().default('http://localhost:3030/api/mcp'),
|
|
@@ -110,6 +112,8 @@ const environmentSchema = zod_1.z.object({
|
|
|
110
112
|
// Bot features
|
|
111
113
|
TOKEN_USAGE_BOT_ENABLED: zod_1.z.string().transform(v => v !== 'false').default('true'),
|
|
112
114
|
AGENT_ACTIVITY_BOT_ENABLED: zod_1.z.string().transform(v => v !== 'false').default('true'),
|
|
115
|
+
// Chat bot mode: 'full' = all tools, 'minimal' = chat tools only, 'assistant' = no tools (knowledge-based help)
|
|
116
|
+
CHAT_BOT_MODE: zod_1.z.enum(['full', 'minimal', 'assistant']).default('full'),
|
|
113
117
|
ADAPTIVE_DOCUMENTATION_BOT_ENABLED: zod_1.z.string().transform(v => v === 'true').default('false'),
|
|
114
118
|
ADAPTIVE_AUTO_UPDATE: zod_1.z.string().transform(v => v === 'true').default('false'),
|
|
115
119
|
ADAPTIVE_UPDATE_INTERVAL: zod_1.z.string().transform(v => parseInt(v) || 60000).default('60000'),
|
|
@@ -164,12 +168,14 @@ class ApplicationConfig {
|
|
|
164
168
|
}
|
|
165
169
|
get llmProviders() {
|
|
166
170
|
const providers = [];
|
|
167
|
-
if (exports.environment.OPENAI_API_KEY) {
|
|
171
|
+
if (exports.environment.OPENAI_API_KEY || exports.environment.OPENAI_API_BASE) {
|
|
172
|
+
const isAssistantMode = exports.environment.CHAT_BOT_MODE === 'assistant';
|
|
168
173
|
providers.push({
|
|
169
|
-
name: 'openai-gpt4o',
|
|
170
|
-
type: 'openai',
|
|
171
|
-
apiKey: exports.environment.OPENAI_API_KEY,
|
|
172
|
-
|
|
174
|
+
name: isAssistantMode ? 'mcp-assistant' : (exports.environment.OPENAI_API_BASE ? 'local-llm' : 'openai-gpt4o'),
|
|
175
|
+
type: isAssistantMode ? 'assistant' : 'openai',
|
|
176
|
+
apiKey: exports.environment.OPENAI_API_KEY || 'not-needed',
|
|
177
|
+
baseURL: exports.environment.OPENAI_API_BASE,
|
|
178
|
+
model: exports.environment.OPENAI_MODEL || 'gpt-4o',
|
|
173
179
|
enabled: true,
|
|
174
180
|
});
|
|
175
181
|
}
|