@posthog/agent 2.1.152 → 2.1.156

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.1.152",
3
+ "version": "2.1.156",
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": {
@@ -595,7 +595,7 @@ export class AgentServer {
595
595
 
596
596
  const sessionResponse = await clientConnection.newSession({
597
597
  cwd: this.config.repositoryPath,
598
- mcpServers: [],
598
+ mcpServers: this.config.mcpServers ?? [],
599
599
  _meta: {
600
600
  sessionId: payload.run_id,
601
601
  taskRunId: payload.run_id,
package/src/server/bin.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { Command } from "commander";
3
3
  import { z } from "zod";
4
4
  import { AgentServer } from "./agent-server.js";
5
+ import { mcpServersSchema } from "./schemas.js";
5
6
 
6
7
  const envSchema = z.object({
7
8
  JWT_PUBLIC_KEY: z
@@ -45,6 +46,10 @@ program
45
46
  .requiredOption("--repositoryPath <path>", "Path to the repository")
46
47
  .requiredOption("--taskId <id>", "Task ID")
47
48
  .requiredOption("--runId <id>", "Task run ID")
49
+ .option(
50
+ "--mcpServers <json>",
51
+ "MCP servers config as JSON array (ACP McpServer[] format)",
52
+ )
48
53
  .action(async (options) => {
49
54
  const envResult = envSchema.safeParse(process.env);
50
55
 
@@ -60,6 +65,29 @@ program
60
65
 
61
66
  const mode = options.mode === "background" ? "background" : "interactive";
62
67
 
68
+ let mcpServers: z.infer<typeof mcpServersSchema> | undefined;
69
+ if (options.mcpServers) {
70
+ let parsed: unknown;
71
+ try {
72
+ parsed = JSON.parse(options.mcpServers);
73
+ } catch {
74
+ program.error("--mcpServers must be valid JSON");
75
+ return;
76
+ }
77
+
78
+ const result = mcpServersSchema.safeParse(parsed);
79
+ if (!result.success) {
80
+ const errors = result.error.issues
81
+ .map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`)
82
+ .join("\n");
83
+ program.error(
84
+ `--mcpServers validation failed (only remote http/sse servers are supported):\n${errors}`,
85
+ );
86
+ return;
87
+ }
88
+ mcpServers = result.data;
89
+ }
90
+
63
91
  const server = new AgentServer({
64
92
  port: parseInt(options.port, 10),
65
93
  jwtPublicKey: env.JWT_PUBLIC_KEY,
@@ -70,6 +98,7 @@ program
70
98
  mode,
71
99
  taskId: options.taskId,
72
100
  runId: options.runId,
101
+ mcpServers,
73
102
  });
74
103
 
75
104
  process.on("SIGINT", async () => {
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mcpServersSchema } from "./schemas.js";
3
+
4
+ describe("mcpServersSchema", () => {
5
+ it("accepts a valid HTTP server", () => {
6
+ const result = mcpServersSchema.safeParse([
7
+ {
8
+ type: "http",
9
+ name: "my-server",
10
+ url: "https://mcp.example.com",
11
+ headers: [{ name: "Authorization", value: "Bearer tok" }],
12
+ },
13
+ ]);
14
+ expect(result.success).toBe(true);
15
+ expect(result.data).toEqual([
16
+ {
17
+ type: "http",
18
+ name: "my-server",
19
+ url: "https://mcp.example.com",
20
+ headers: [{ name: "Authorization", value: "Bearer tok" }],
21
+ },
22
+ ]);
23
+ });
24
+
25
+ it("accepts a valid SSE server", () => {
26
+ const result = mcpServersSchema.safeParse([
27
+ {
28
+ type: "sse",
29
+ name: "sse-server",
30
+ url: "https://sse.example.com/events",
31
+ headers: [],
32
+ },
33
+ ]);
34
+ expect(result.success).toBe(true);
35
+ });
36
+
37
+ it("defaults headers to empty array when omitted", () => {
38
+ const result = mcpServersSchema.safeParse([
39
+ { type: "http", name: "no-headers", url: "https://example.com" },
40
+ ]);
41
+ expect(result.success).toBe(true);
42
+ expect(result.data?.[0].headers).toEqual([]);
43
+ });
44
+
45
+ it("accepts multiple servers", () => {
46
+ const result = mcpServersSchema.safeParse([
47
+ { type: "http", name: "a", url: "https://a.com" },
48
+ { type: "sse", name: "b", url: "https://b.com" },
49
+ ]);
50
+ expect(result.success).toBe(true);
51
+ expect(result.data).toHaveLength(2);
52
+ });
53
+
54
+ it("accepts an empty array", () => {
55
+ const result = mcpServersSchema.safeParse([]);
56
+ expect(result.success).toBe(true);
57
+ expect(result.data).toEqual([]);
58
+ });
59
+
60
+ it("rejects stdio servers", () => {
61
+ const result = mcpServersSchema.safeParse([
62
+ {
63
+ type: "stdio",
64
+ name: "local",
65
+ command: "/usr/bin/mcp",
66
+ args: [],
67
+ },
68
+ ]);
69
+ expect(result.success).toBe(false);
70
+ });
71
+
72
+ it("rejects servers with no type", () => {
73
+ const result = mcpServersSchema.safeParse([
74
+ { name: "missing-type", url: "https://example.com" },
75
+ ]);
76
+ expect(result.success).toBe(false);
77
+ });
78
+
79
+ it("rejects servers with empty name", () => {
80
+ const result = mcpServersSchema.safeParse([
81
+ { type: "http", name: "", url: "https://example.com" },
82
+ ]);
83
+ expect(result.success).toBe(false);
84
+ });
85
+
86
+ it("rejects servers with invalid url", () => {
87
+ const result = mcpServersSchema.safeParse([
88
+ { type: "http", name: "bad-url", url: "not-a-url" },
89
+ ]);
90
+ expect(result.success).toBe(false);
91
+ });
92
+
93
+ it("rejects servers with missing url", () => {
94
+ const result = mcpServersSchema.safeParse([
95
+ { type: "http", name: "no-url" },
96
+ ]);
97
+ expect(result.success).toBe(false);
98
+ });
99
+
100
+ it("rejects non-array input", () => {
101
+ expect(mcpServersSchema.safeParse("not-array").success).toBe(false);
102
+ expect(mcpServersSchema.safeParse({}).success).toBe(false);
103
+ expect(mcpServersSchema.safeParse(null).success).toBe(false);
104
+ });
105
+
106
+ it("rejects headers with missing fields", () => {
107
+ const result = mcpServersSchema.safeParse([
108
+ {
109
+ type: "http",
110
+ name: "bad-headers",
111
+ url: "https://example.com",
112
+ headers: [{ name: "X-Key" }],
113
+ },
114
+ ]);
115
+ expect(result.success).toBe(false);
116
+ });
117
+ });
@@ -1,5 +1,21 @@
1
1
  import { z } from "zod";
2
2
 
3
+ const httpHeaderSchema = z.object({
4
+ name: z.string(),
5
+ value: z.string(),
6
+ });
7
+
8
+ const remoteMcpServerSchema = z.object({
9
+ type: z.enum(["http", "sse"]),
10
+ name: z.string().min(1, "MCP server name is required"),
11
+ url: z.string().url("MCP server url must be a valid URL"),
12
+ headers: z.array(httpHeaderSchema).default([]),
13
+ });
14
+
15
+ export const mcpServersSchema = z.array(remoteMcpServerSchema);
16
+
17
+ export type RemoteMcpServer = z.infer<typeof remoteMcpServerSchema>;
18
+
3
19
  export const jsonRpcRequestSchema = z.object({
4
20
  jsonrpc: z.literal("2.0"),
5
21
  method: z.string(),
@@ -1,4 +1,5 @@
1
1
  import type { AgentMode } from "../types.js";
2
+ import type { RemoteMcpServer } from "./schemas.js";
2
3
 
3
4
  export interface AgentServerConfig {
4
5
  port: number;
@@ -11,4 +12,5 @@ export interface AgentServerConfig {
11
12
  taskId: string;
12
13
  runId: string;
13
14
  version?: string;
15
+ mcpServers?: RemoteMcpServer[];
14
16
  }