clementine-agent 1.0.39 → 1.0.41
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/agent/assistant.js +50 -3
- package/dist/agent/mcp-bridge.d.ts +15 -0
- package/dist/agent/mcp-bridge.js +60 -1
- package/package.json +1 -1
package/dist/agent/assistant.js
CHANGED
|
@@ -694,10 +694,29 @@ export class PersonalAssistant {
|
|
|
694
694
|
/** Capture MCP server status from system init messages. */
|
|
695
695
|
captureMcpStatus(message) {
|
|
696
696
|
const sysMsg = message;
|
|
697
|
-
if (sysMsg.subtype
|
|
697
|
+
if (sysMsg.subtype !== 'init')
|
|
698
|
+
return;
|
|
699
|
+
if (sysMsg.mcp_servers) {
|
|
698
700
|
this._lastMcpStatus = sysMsg.mcp_servers;
|
|
699
701
|
this._lastMcpStatusTime = new Date().toISOString();
|
|
700
702
|
}
|
|
703
|
+
// Auto-register Claude Desktop integrations from the authoritative tool
|
|
704
|
+
// list the SDK reports on init. Previously the claude-integrations file
|
|
705
|
+
// was written reactively — only after the agent successfully called an
|
|
706
|
+
// integration tool — which meant a freshly-connected Google Drive or
|
|
707
|
+
// Gmail was invisible until used. Now every session-init rewrites the
|
|
708
|
+
// list to match reality.
|
|
709
|
+
if (Array.isArray(sysMsg.tools) && sysMsg.tools.length > 0 && _mcpBridge) {
|
|
710
|
+
try {
|
|
711
|
+
const { added, updated } = _mcpBridge.registerClaudeIntegrationsFromToolList(sysMsg.tools);
|
|
712
|
+
if (added.length > 0 || updated.length > 0) {
|
|
713
|
+
logger.info({ added, updated }, 'Registered Claude Desktop integrations from SDK tool inventory');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
catch (err) {
|
|
717
|
+
logger.debug({ err }, 'Integration auto-registration failed (non-fatal)');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
701
720
|
}
|
|
702
721
|
setUnleashedCompleteCallback(cb) {
|
|
703
722
|
this.onUnleashedComplete = cb;
|
|
@@ -1046,12 +1065,40 @@ Delegate data-heavy work (SEO, analytics, bulk API calls for 3+ entities) to sub
|
|
|
1046
1065
|
Never spawn a sub-agent with vague instructions like "handle this brief" — tell it exactly what to read, what to change, and where to write the result.
|
|
1047
1066
|
`);
|
|
1048
1067
|
}
|
|
1049
|
-
// Inject Claude Desktop integration awareness
|
|
1068
|
+
// Inject Claude Desktop integration awareness. Two lists:
|
|
1069
|
+
// - Known-confirmed: integrations the agent has actually used before
|
|
1070
|
+
// (persisted in claude-integrations.json). High confidence.
|
|
1071
|
+
// - Possibly-available: common integrations the owner may have connected
|
|
1072
|
+
// at claude.ai level that we don't have a usage record for yet. The
|
|
1073
|
+
// integrations-file is written reactively — only entries with
|
|
1074
|
+
// successful tool uses get captured — so a freshly-connected
|
|
1075
|
+
// Google Drive / Gmail / Slack connector is invisible to us until we
|
|
1076
|
+
// try it. Hint the agent that it can try these blindly; the tool
|
|
1077
|
+
// call will either succeed or return an auth error, at which point
|
|
1078
|
+
// the record gets captured.
|
|
1050
1079
|
try {
|
|
1051
1080
|
const integrations = _mcpBridge?.getClaudeIntegrations() ?? [];
|
|
1081
|
+
const knownNames = new Set(integrations.map(ig => ig.name));
|
|
1052
1082
|
if (integrations.length > 0) {
|
|
1053
1083
|
const names = integrations.map(ig => ig.label).join(', ');
|
|
1054
|
-
parts.push(`**Connected via Claude Desktop:** ${names}. Use their \`mcp__claude_ai_*\` tools
|
|
1084
|
+
parts.push(`**Connected via Claude Desktop:** ${names}. Use their \`mcp__claude_ai_*\` tools (e.g. \`mcp__claude_ai_Google_Drive__search_files\`).`);
|
|
1085
|
+
}
|
|
1086
|
+
// Common integrations to speculatively mention. If the owner has
|
|
1087
|
+
// connected any of these at claude.ai, the corresponding tool names
|
|
1088
|
+
// will work even if no prior record exists.
|
|
1089
|
+
const SPECULATIVE = [
|
|
1090
|
+
['Google_Drive', 'Google Drive'], ['Gmail', 'Gmail'],
|
|
1091
|
+
['Google_Calendar', 'Google Calendar'], ['Google_Workspace', 'Google Workspace'],
|
|
1092
|
+
['Slack', 'Slack'], ['Notion', 'Notion'], ['GitHub', 'GitHub'],
|
|
1093
|
+
['Linear', 'Linear'], ['Asana', 'Asana'], ['Jira', 'Jira'],
|
|
1094
|
+
['Dropbox', 'Dropbox'], ['Salesforce', 'Salesforce'],
|
|
1095
|
+
['Microsoft_365', 'Microsoft 365'],
|
|
1096
|
+
];
|
|
1097
|
+
const maybe = SPECULATIVE.filter(([name]) => !knownNames.has(name)).map(([, label]) => label);
|
|
1098
|
+
if (maybe.length > 0) {
|
|
1099
|
+
parts.push(`**Possibly connected (try them — they'll either work or return an auth error):** ${maybe.join(', ')}. ` +
|
|
1100
|
+
`Tool names follow \`mcp__claude_ai_<IntegrationName>__<tool>\` — e.g. \`mcp__claude_ai_Google_Drive__search_files\`, \`mcp__claude_ai_Gmail__authenticate\`. ` +
|
|
1101
|
+
`Don't tell ${owner} an integration is "not available" without first attempting the tool call — a fresh connection won't be in your recorded list until after first use.`);
|
|
1055
1102
|
}
|
|
1056
1103
|
}
|
|
1057
1104
|
catch { /* non-fatal */ }
|
|
@@ -54,6 +54,21 @@ export declare function loadClaudeIntegrations(): Record<string, ClaudeIntegrati
|
|
|
54
54
|
export declare function recordClaudeIntegrationUse(toolName: string): void;
|
|
55
55
|
/** Get all discovered Claude Desktop integrations as a list. */
|
|
56
56
|
export declare function getClaudeIntegrations(): ClaudeIntegration[];
|
|
57
|
+
/**
|
|
58
|
+
* Register every integration found in a tool inventory. The SDK's system
|
|
59
|
+
* init message (subtype='init') includes a `tools: string[]` with the full
|
|
60
|
+
* set of tools the agent actually has access to this session — including
|
|
61
|
+
* every mcp__claude_ai_* tool Claude Desktop is surfacing. Walking that
|
|
62
|
+
* list on init gives us the authoritative, up-to-date integration set
|
|
63
|
+
* without waiting for the agent to blindly try each one.
|
|
64
|
+
*
|
|
65
|
+
* Idempotent: if an entry already exists, we merge new tool names into it
|
|
66
|
+
* and bump `connected = true` without touching firstSeen/lastUsed.
|
|
67
|
+
*/
|
|
68
|
+
export declare function registerClaudeIntegrationsFromToolList(tools: string[]): {
|
|
69
|
+
added: string[];
|
|
70
|
+
updated: string[];
|
|
71
|
+
};
|
|
57
72
|
/**
|
|
58
73
|
* Bootstrap integrations from the audit log.
|
|
59
74
|
* Call once on startup to seed the integrations file from historical data.
|
package/dist/agent/mcp-bridge.js
CHANGED
|
@@ -329,7 +329,12 @@ function parseClaudeDesktopTool(toolName) {
|
|
|
329
329
|
const match = toolName.match(/^mcp__claude_ai_([^_]+(?:_[^_]+)*)__(.+)$/);
|
|
330
330
|
if (!match)
|
|
331
331
|
return null;
|
|
332
|
-
|
|
332
|
+
const raw = match[1];
|
|
333
|
+
// Normalize against the known canonical set (case-insensitive) so we don't
|
|
334
|
+
// create both `Microsoft_365` and `microsoft_365` entries when the SDK
|
|
335
|
+
// occasionally hands us a lowercased tool name.
|
|
336
|
+
const canonical = Object.keys(INTEGRATION_LABELS).find(k => k.toLowerCase() === raw.toLowerCase()) ?? raw;
|
|
337
|
+
return { integration: canonical, tool: match[2] };
|
|
333
338
|
}
|
|
334
339
|
/** Load persisted integrations from disk. */
|
|
335
340
|
export function loadClaudeIntegrations() {
|
|
@@ -379,6 +384,60 @@ export function recordClaudeIntegrationUse(toolName) {
|
|
|
379
384
|
export function getClaudeIntegrations() {
|
|
380
385
|
return Object.values(loadClaudeIntegrations());
|
|
381
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Register every integration found in a tool inventory. The SDK's system
|
|
389
|
+
* init message (subtype='init') includes a `tools: string[]` with the full
|
|
390
|
+
* set of tools the agent actually has access to this session — including
|
|
391
|
+
* every mcp__claude_ai_* tool Claude Desktop is surfacing. Walking that
|
|
392
|
+
* list on init gives us the authoritative, up-to-date integration set
|
|
393
|
+
* without waiting for the agent to blindly try each one.
|
|
394
|
+
*
|
|
395
|
+
* Idempotent: if an entry already exists, we merge new tool names into it
|
|
396
|
+
* and bump `connected = true` without touching firstSeen/lastUsed.
|
|
397
|
+
*/
|
|
398
|
+
export function registerClaudeIntegrationsFromToolList(tools) {
|
|
399
|
+
const added = [];
|
|
400
|
+
const updated = [];
|
|
401
|
+
if (!Array.isArray(tools) || tools.length === 0)
|
|
402
|
+
return { added, updated };
|
|
403
|
+
const integrations = loadClaudeIntegrations();
|
|
404
|
+
const now = new Date().toISOString();
|
|
405
|
+
let dirty = false;
|
|
406
|
+
for (const toolName of tools) {
|
|
407
|
+
const parsed = parseClaudeDesktopTool(toolName);
|
|
408
|
+
if (!parsed)
|
|
409
|
+
continue;
|
|
410
|
+
const existing = integrations[parsed.integration];
|
|
411
|
+
if (existing) {
|
|
412
|
+
if (!existing.tools.includes(parsed.tool)) {
|
|
413
|
+
existing.tools.push(parsed.tool);
|
|
414
|
+
existing.tools.sort();
|
|
415
|
+
dirty = true;
|
|
416
|
+
}
|
|
417
|
+
if (!existing.connected) {
|
|
418
|
+
existing.connected = true;
|
|
419
|
+
dirty = true;
|
|
420
|
+
}
|
|
421
|
+
if (!updated.includes(parsed.integration))
|
|
422
|
+
updated.push(parsed.integration);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
integrations[parsed.integration] = {
|
|
426
|
+
name: parsed.integration,
|
|
427
|
+
label: INTEGRATION_LABELS[parsed.integration] ?? parsed.integration.replace(/_/g, ' '),
|
|
428
|
+
tools: [parsed.tool],
|
|
429
|
+
firstSeen: now,
|
|
430
|
+
lastUsed: now,
|
|
431
|
+
connected: true,
|
|
432
|
+
};
|
|
433
|
+
added.push(parsed.integration);
|
|
434
|
+
dirty = true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (dirty)
|
|
438
|
+
saveClaudeIntegrations(integrations);
|
|
439
|
+
return { added, updated };
|
|
440
|
+
}
|
|
382
441
|
/**
|
|
383
442
|
* Bootstrap integrations from the audit log.
|
|
384
443
|
* Call once on startup to seed the integrations file from historical data.
|