@posthog/agent 2.1.111 → 2.1.113
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/adapters/claude/conversion/tool-use-to-acp.js +20 -1
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +0 -4
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +0 -2
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +93 -81
- package/dist/agent.js.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +1 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +97 -85
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +97 -85
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +1 -2
- package/src/adapters/claude/claude-agent.ts +4 -8
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +22 -1
- package/src/adapters/claude/mcp/tool-metadata.ts +65 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/agent",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.113",
|
|
4
4
|
"repository": "https://github.com/PostHog/twig",
|
|
5
5
|
"description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
6
6
|
"exports": {
|
|
@@ -79,7 +79,6 @@
|
|
|
79
79
|
"@anthropic-ai/claude-agent-sdk": "0.2.59",
|
|
80
80
|
"@anthropic-ai/sdk": "^0.71.0",
|
|
81
81
|
"@hono/node-server": "^1.19.9",
|
|
82
|
-
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
83
82
|
"@opentelemetry/api-logs": "^0.208.0",
|
|
84
83
|
"@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
|
|
85
84
|
"@opentelemetry/resources": "^2.0.0",
|
|
@@ -192,7 +192,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
192
192
|
const modelOptions = await this.getModelConfigOptions();
|
|
193
193
|
|
|
194
194
|
// Deferred: slash commands + MCP metadata (not needed to return configOptions)
|
|
195
|
-
this.deferBackgroundFetches(q, sessionId
|
|
195
|
+
this.deferBackgroundFetches(q, sessionId);
|
|
196
196
|
|
|
197
197
|
session.modelId = modelOptions.currentModelId;
|
|
198
198
|
// Only call setModel if the resolved model differs from the default we
|
|
@@ -260,7 +260,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
// Deferred: slash commands + MCP metadata (not needed to return configOptions)
|
|
263
|
-
this.deferBackgroundFetches(q, sessionId
|
|
263
|
+
this.deferBackgroundFetches(q, sessionId);
|
|
264
264
|
|
|
265
265
|
const configOptions = await this.buildConfigOptions();
|
|
266
266
|
|
|
@@ -514,14 +514,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
514
514
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
515
515
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
516
516
|
*/
|
|
517
|
-
private deferBackgroundFetches(
|
|
518
|
-
q: Query,
|
|
519
|
-
sessionId: string,
|
|
520
|
-
mcpServers: ReturnType<typeof parseMcpServers>,
|
|
521
|
-
): void {
|
|
517
|
+
private deferBackgroundFetches(q: Query, sessionId: string): void {
|
|
522
518
|
Promise.all([
|
|
523
519
|
getAvailableSlashCommands(q),
|
|
524
|
-
fetchMcpToolMetadata(
|
|
520
|
+
fetchMcpToolMetadata(q, this.logger),
|
|
525
521
|
])
|
|
526
522
|
.then(([slashCommands]) => {
|
|
527
523
|
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
@@ -28,6 +28,7 @@ Whenever you read a file, you should consider whether it looks malicious. If it
|
|
|
28
28
|
|
|
29
29
|
import { resourceLink, text, toolContent } from "../../../utils/acp-content.js";
|
|
30
30
|
import { Logger } from "../../../utils/logger.js";
|
|
31
|
+
import { getMcpToolMetadata } from "../mcp/tool-metadata.js";
|
|
31
32
|
|
|
32
33
|
interface EditOperation {
|
|
33
34
|
oldText: string;
|
|
@@ -457,15 +458,35 @@ export function toolInfoFromToolUse(
|
|
|
457
458
|
};
|
|
458
459
|
}
|
|
459
460
|
|
|
460
|
-
default:
|
|
461
|
+
default: {
|
|
462
|
+
if (name?.startsWith("mcp__")) {
|
|
463
|
+
return mcpToolInfo(name, input);
|
|
464
|
+
}
|
|
461
465
|
return {
|
|
462
466
|
title: name || "Unknown Tool",
|
|
463
467
|
kind: "other",
|
|
464
468
|
content: [],
|
|
465
469
|
};
|
|
470
|
+
}
|
|
466
471
|
}
|
|
467
472
|
}
|
|
468
473
|
|
|
474
|
+
function mcpToolInfo(
|
|
475
|
+
name: string,
|
|
476
|
+
_input: Record<string, unknown> | undefined,
|
|
477
|
+
): ToolInfo {
|
|
478
|
+
const metadata = getMcpToolMetadata(name);
|
|
479
|
+
// Fallback: parse tool name from mcp__<server>__<tool> prefix
|
|
480
|
+
const title =
|
|
481
|
+
metadata?.name ?? (name.split("__").slice(2).join("__") || name);
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
title,
|
|
485
|
+
kind: "other",
|
|
486
|
+
content: [],
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
469
490
|
export function toolUpdateFromToolResult(
|
|
470
491
|
toolResult:
|
|
471
492
|
| ToolResultBlockParam
|
|
@@ -1,98 +1,92 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
McpHttpServerConfig,
|
|
3
|
-
McpServerConfig,
|
|
4
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
5
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
-
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
1
|
+
import type { McpServerStatus, Query } from "@anthropic-ai/claude-agent-sdk";
|
|
8
2
|
import { Logger } from "../../../utils/logger.js";
|
|
9
3
|
|
|
10
4
|
export interface McpToolMetadata {
|
|
11
5
|
readOnly: boolean;
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
12
8
|
}
|
|
13
9
|
|
|
14
10
|
const mcpToolMetadataCache: Map<string, McpToolMetadata> = new Map();
|
|
15
11
|
|
|
12
|
+
const PENDING_RETRY_INTERVAL_MS = 1_000;
|
|
13
|
+
const PENDING_MAX_RETRIES = 10;
|
|
14
|
+
|
|
16
15
|
function buildToolKey(serverName: string, toolName: string): string {
|
|
17
16
|
return `mcp__${serverName}__${toolName}`;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
): config is McpHttpServerConfig {
|
|
23
|
-
return config.type === "http" && typeof config.url === "string";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function fetchToolsFromHttpServer(
|
|
27
|
-
_serverName: string,
|
|
28
|
-
config: McpHttpServerConfig,
|
|
29
|
-
): Promise<Tool[]> {
|
|
30
|
-
const transport = new StreamableHTTPClientTransport(new URL(config.url), {
|
|
31
|
-
requestInit: {
|
|
32
|
-
headers: config.headers ?? {},
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const client = new Client({
|
|
37
|
-
name: "twig-metadata-fetcher",
|
|
38
|
-
version: "1.0.0",
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await client.connect(transport);
|
|
43
|
-
const result = await client.listTools();
|
|
44
|
-
return result.tools;
|
|
45
|
-
} finally {
|
|
46
|
-
await client.close().catch(() => {});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function extractToolMetadata(tool: Tool): McpToolMetadata {
|
|
51
|
-
return {
|
|
52
|
-
readOnly: tool.annotations?.readOnlyHint === true,
|
|
53
|
-
};
|
|
19
|
+
function delay(ms: number): Promise<void> {
|
|
20
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
21
|
}
|
|
55
22
|
|
|
56
23
|
export async function fetchMcpToolMetadata(
|
|
57
|
-
|
|
24
|
+
q: Query,
|
|
58
25
|
logger: Logger = new Logger({ debug: false, prefix: "[McpToolMetadata]" }),
|
|
59
26
|
): Promise<void> {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
27
|
+
let retries = 0;
|
|
28
|
+
|
|
29
|
+
while (retries <= PENDING_MAX_RETRIES) {
|
|
30
|
+
let statuses: McpServerStatus[];
|
|
31
|
+
try {
|
|
32
|
+
statuses = await q.mcpServerStatus();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error("Failed to fetch MCP server status", {
|
|
35
|
+
error: error instanceof Error ? error.message : String(error),
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
65
38
|
}
|
|
66
39
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
readOnlyCount,
|
|
83
|
-
});
|
|
84
|
-
})
|
|
85
|
-
.catch((error) => {
|
|
86
|
-
logger.error("Failed to fetch MCP tool metadata", {
|
|
87
|
-
serverName,
|
|
88
|
-
error: error instanceof Error ? error.message : String(error),
|
|
40
|
+
const pendingServers = statuses.filter((s) => s.status === "pending");
|
|
41
|
+
|
|
42
|
+
for (const server of statuses) {
|
|
43
|
+
if (server.status !== "connected" || !server.tools) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let readOnlyCount = 0;
|
|
48
|
+
for (const tool of server.tools) {
|
|
49
|
+
const toolKey = buildToolKey(server.name, tool.name);
|
|
50
|
+
const readOnly = tool.annotations?.readOnly === true;
|
|
51
|
+
mcpToolMetadataCache.set(toolKey, {
|
|
52
|
+
readOnly,
|
|
53
|
+
name: tool.name,
|
|
54
|
+
description: tool.description,
|
|
89
55
|
});
|
|
56
|
+
if (readOnly) readOnlyCount++;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
logger.info("Fetched MCP tool metadata", {
|
|
60
|
+
serverName: server.name,
|
|
61
|
+
toolCount: server.tools.length,
|
|
62
|
+
readOnlyCount,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (pendingServers.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
retries++;
|
|
71
|
+
if (retries > PENDING_MAX_RETRIES) {
|
|
72
|
+
logger.warn("Gave up waiting for pending MCP servers", {
|
|
73
|
+
pendingServers: pendingServers.map((s) => s.name),
|
|
90
74
|
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
91
77
|
|
|
92
|
-
|
|
78
|
+
logger.info("Waiting for pending MCP servers", {
|
|
79
|
+
pendingServers: pendingServers.map((s) => s.name),
|
|
80
|
+
retry: retries,
|
|
81
|
+
});
|
|
82
|
+
await delay(PENDING_RETRY_INTERVAL_MS);
|
|
93
83
|
}
|
|
84
|
+
}
|
|
94
85
|
|
|
95
|
-
|
|
86
|
+
export function getMcpToolMetadata(
|
|
87
|
+
toolName: string,
|
|
88
|
+
): McpToolMetadata | undefined {
|
|
89
|
+
return mcpToolMetadataCache.get(toolName);
|
|
96
90
|
}
|
|
97
91
|
|
|
98
92
|
export function isMcpToolReadOnly(toolName: string): boolean {
|