doer-agent 0.9.0 → 0.9.1

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.
@@ -13,6 +13,11 @@ function toTomlStringLiteral(value) {
13
13
  function toTomlStringArray(values) {
14
14
  return `[${values.map((value) => toTomlStringLiteral(value)).join(", ")}]`;
15
15
  }
16
+ function toTomlStringMap(values) {
17
+ return `{ ${Object.entries(values)
18
+ .map(([key, value]) => `${toTomlStringLiteral(key)} = ${toTomlStringLiteral(value)}`)
19
+ .join(", ")} }`;
20
+ }
16
21
  function buildMcpServerConfigArgs(args) {
17
22
  const serverName = args.serverName.trim();
18
23
  const configArgs = [
@@ -28,6 +33,25 @@ function buildMcpServerConfigArgs(args) {
28
33
  }
29
34
  return configArgs;
30
35
  }
36
+ function buildRemoteMcpServerConfigArgs(args) {
37
+ const prefix = `mcp_servers.${args.serverName.trim()}`;
38
+ const configArgs = [
39
+ "--config",
40
+ `${prefix}.url=${toTomlStringLiteral(args.url)}`,
41
+ "--config",
42
+ `${prefix}.enabled=${args.enabled === false ? "false" : "true"}`,
43
+ ];
44
+ if (args.bearerTokenEnvVar?.trim()) {
45
+ configArgs.push("--config", `${prefix}.bearer_token_env_var=${toTomlStringLiteral(args.bearerTokenEnvVar.trim())}`);
46
+ }
47
+ if (Object.keys(args.httpHeaders ?? {}).length > 0) {
48
+ configArgs.push("--config", `${prefix}.http_headers=${toTomlStringMap(args.httpHeaders ?? {})}`);
49
+ }
50
+ if (Object.keys(args.envHttpHeaders ?? {}).length > 0) {
51
+ configArgs.push("--config", `${prefix}.env_http_headers=${toTomlStringMap(args.envHttpHeaders ?? {})}`);
52
+ }
53
+ return configArgs;
54
+ }
31
55
  function hasDirectCodexBinary() {
32
56
  const result = spawnSync("bash", ["-lc", "command -v codex >/dev/null 2>&1"], {
33
57
  stdio: "ignore",
@@ -124,10 +148,22 @@ export function buildCustomMcpConfigArgs(servers) {
124
148
  const seenNames = new Set();
125
149
  for (const server of servers) {
126
150
  const serverName = server.name.trim();
127
- if (!server.enabled || !serverName || !server.command.trim() || reservedNames.has(serverName) || seenNames.has(serverName)) {
151
+ const hasEndpoint = server.transport === "streamable_http" ? server.url.trim() : server.command.trim();
152
+ if (!server.enabled || !serverName || !hasEndpoint || reservedNames.has(serverName) || seenNames.has(serverName)) {
128
153
  continue;
129
154
  }
130
155
  seenNames.add(serverName);
156
+ if (server.transport === "streamable_http") {
157
+ configArgs.push(...buildRemoteMcpServerConfigArgs({
158
+ serverName,
159
+ url: server.url,
160
+ bearerTokenEnvVar: server.bearerTokenEnvVar,
161
+ httpHeaders: Object.fromEntries(server.httpHeaders.map((header) => [header.key, header.value])),
162
+ envHttpHeaders: Object.fromEntries(server.envHttpHeaders.map((header) => [header.key, header.value])),
163
+ enabled: true,
164
+ }));
165
+ continue;
166
+ }
131
167
  configArgs.push(...buildMcpServerConfigArgs({
132
168
  serverName,
133
169
  command: server.command,
@@ -0,0 +1,44 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { buildCustomMcpConfigArgs } from "./agent-codex-cli.js";
4
+ test("buildCustomMcpConfigArgs builds streamable HTTP MCP overrides", () => {
5
+ const args = buildCustomMcpConfigArgs([{
6
+ name: "remote_docs",
7
+ transport: "streamable_http",
8
+ command: "",
9
+ args: [],
10
+ env: [],
11
+ url: "https://example.com/mcp",
12
+ bearerTokenEnvVar: "MCP_TOKEN",
13
+ httpHeaders: [{ key: "X-Region", value: "seoul" }],
14
+ envHttpHeaders: [{ key: "X-API-Key", value: "MCP_API_KEY" }],
15
+ enabled: true,
16
+ }]);
17
+ assert.deepEqual(args, [
18
+ "--config", 'mcp_servers.remote_docs.url="https://example.com/mcp"',
19
+ "--config", "mcp_servers.remote_docs.enabled=true",
20
+ "--config", 'mcp_servers.remote_docs.bearer_token_env_var="MCP_TOKEN"',
21
+ "--config", 'mcp_servers.remote_docs.http_headers={ "X-Region" = "seoul" }',
22
+ "--config", 'mcp_servers.remote_docs.env_http_headers={ "X-API-Key" = "MCP_API_KEY" }',
23
+ ]);
24
+ });
25
+ test("buildCustomMcpConfigArgs keeps stdio MCP overrides", () => {
26
+ const args = buildCustomMcpConfigArgs([{
27
+ name: "local_tools",
28
+ transport: "stdio",
29
+ command: "npx",
30
+ args: ["-y", "local-mcp"],
31
+ env: [{ key: "LOCAL_TOKEN", value: "secret" }],
32
+ url: "",
33
+ bearerTokenEnvVar: "",
34
+ httpHeaders: [],
35
+ envHttpHeaders: [],
36
+ enabled: true,
37
+ }]);
38
+ assert.deepEqual(args, [
39
+ "--config", 'mcp_servers.local_tools.command="npx"',
40
+ "--config", 'mcp_servers.local_tools.args=["-y", "local-mcp"]',
41
+ "--config", "mcp_servers.local_tools.enabled=true",
42
+ "--config", 'mcp_servers.local_tools.env.LOCAL_TOKEN="secret"',
43
+ ]);
44
+ });
@@ -172,7 +172,7 @@ function normalizeMcpServerName(value) {
172
172
  if (!trimmed || !/^[A-Za-z0-9_-]+$/.test(trimmed)) {
173
173
  return null;
174
174
  }
175
- if (trimmed === "doer_daemon" || trimmed === "doer_mobile") {
175
+ if (trimmed === "doer_daemon" || trimmed === "doer_mobile" || trimmed === "doer_threads") {
176
176
  return null;
177
177
  }
178
178
  return trimmed;
@@ -185,14 +185,38 @@ function normalizeStringArray(value) {
185
185
  .map((item) => (typeof item === "string" ? item.replace(/\r/g, "") : null))
186
186
  .filter((item) => item !== null && item.trim().length > 0);
187
187
  }
188
+ function normalizeMcpHeaderEntries(value) {
189
+ if (!Array.isArray(value)) {
190
+ return [];
191
+ }
192
+ const entries = [];
193
+ const seenKeys = new Set();
194
+ for (const item of value) {
195
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
196
+ continue;
197
+ }
198
+ const raw = item;
199
+ const key = typeof raw.key === "string" ? raw.key.trim() : "";
200
+ if (!key || typeof raw.value !== "string" || seenKeys.has(key)) {
201
+ continue;
202
+ }
203
+ seenKeys.add(key);
204
+ entries.push({ key, value: raw.value.replace(/\r/g, "") });
205
+ }
206
+ return entries;
207
+ }
188
208
  function normalizeAgentMcpServer(value) {
189
209
  if (!value || typeof value !== "object" || Array.isArray(value)) {
190
210
  return null;
191
211
  }
192
212
  const raw = value;
193
213
  const name = normalizeMcpServerName(raw.name);
214
+ const transport = raw.transport === "streamable_http" || (typeof raw.url === "string" && raw.url.trim())
215
+ ? "streamable_http"
216
+ : "stdio";
194
217
  const command = typeof raw.command === "string" ? raw.command.trim() : "";
195
- if (!name || !command) {
218
+ const url = typeof raw.url === "string" ? raw.url.trim() : "";
219
+ if (!name || (transport === "stdio" ? !command : !url)) {
196
220
  return null;
197
221
  }
198
222
  const env = [];
@@ -208,9 +232,14 @@ function normalizeAgentMcpServer(value) {
208
232
  }
209
233
  return {
210
234
  name,
235
+ transport,
211
236
  command,
212
237
  args: normalizeStringArray(raw.args),
213
238
  env,
239
+ url,
240
+ bearerTokenEnvVar: typeof raw.bearerTokenEnvVar === "string" ? raw.bearerTokenEnvVar.trim() : "",
241
+ httpHeaders: normalizeMcpHeaderEntries(raw.httpHeaders),
242
+ envHttpHeaders: normalizeMcpHeaderEntries(raw.envHttpHeaders),
214
243
  enabled: raw.enabled !== false,
215
244
  };
216
245
  }
@@ -386,12 +415,17 @@ export async function toAgentSettingsPublic(args) {
386
415
  mcp: {
387
416
  servers: args.config.mcp.servers.map((server) => ({
388
417
  name: server.name,
418
+ transport: server.transport,
389
419
  command: server.command,
390
420
  args: [...server.args],
391
421
  env: server.env.map((variable) => ({
392
422
  key: variable.key,
393
423
  value: variable.value,
394
424
  })),
425
+ url: server.url,
426
+ bearerTokenEnvVar: server.bearerTokenEnvVar,
427
+ httpHeaders: server.httpHeaders.map((header) => ({ ...header })),
428
+ envHttpHeaders: server.envHttpHeaders.map((header) => ({ ...header })),
395
429
  enabled: server.enabled,
396
430
  })),
397
431
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",