poe-code 3.0.191 → 3.0.192

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.
Files changed (70) hide show
  1. package/dist/cli/commands/configure-payload.d.ts +1 -1
  2. package/dist/cli/commands/configure-payload.js +16 -9
  3. package/dist/cli/commands/configure-payload.js.map +1 -1
  4. package/dist/cli/commands/configure.d.ts +7 -0
  5. package/dist/cli/commands/configure.js +25 -5
  6. package/dist/cli/commands/configure.js.map +1 -1
  7. package/dist/cli/commands/ensure-isolated-config.js +3 -2
  8. package/dist/cli/commands/ensure-isolated-config.js.map +1 -1
  9. package/dist/cli/commands/experiment.js +38 -39
  10. package/dist/cli/commands/experiment.js.map +1 -1
  11. package/dist/cli/commands/memory.js +20 -15
  12. package/dist/cli/commands/memory.js.map +1 -1
  13. package/dist/cli/commands/pipeline-loop-agent.d.ts +1 -0
  14. package/dist/cli/commands/pipeline-loop-agent.js +2 -0
  15. package/dist/cli/commands/pipeline-loop-agent.js.map +1 -0
  16. package/dist/cli/commands/pipeline.js +42 -71
  17. package/dist/cli/commands/pipeline.js.map +1 -1
  18. package/dist/cli/commands/ralph.js +37 -20
  19. package/dist/cli/commands/ralph.js.map +1 -1
  20. package/dist/cli/commands/shared.d.ts +1 -0
  21. package/dist/cli/commands/shared.js +30 -1
  22. package/dist/cli/commands/shared.js.map +1 -1
  23. package/dist/cli/commands/test.js +5 -2
  24. package/dist/cli/commands/test.js.map +1 -1
  25. package/dist/cli/poe-code-command-runner.js +2 -7
  26. package/dist/cli/poe-code-command-runner.js.map +1 -1
  27. package/dist/cli/program.js +55 -46
  28. package/dist/cli/program.js.map +1 -1
  29. package/dist/cli/service-registry.d.ts +7 -1
  30. package/dist/cli/service-registry.js.map +1 -1
  31. package/dist/index.js +9189 -2108
  32. package/dist/index.js.map +4 -4
  33. package/dist/providers/claude-code.js +18 -1
  34. package/dist/providers/claude-code.js.map +3 -3
  35. package/dist/providers/codex.js +18 -1
  36. package/dist/providers/codex.js.map +3 -3
  37. package/dist/providers/create-provider.d.ts +1 -0
  38. package/dist/providers/create-provider.js +3 -0
  39. package/dist/providers/create-provider.js.map +1 -1
  40. package/dist/providers/goose.js +19 -2
  41. package/dist/providers/goose.js.map +3 -3
  42. package/dist/providers/kimi.js +18 -1
  43. package/dist/providers/kimi.js.map +3 -3
  44. package/dist/providers/opencode.js +18 -1
  45. package/dist/providers/opencode.js.map +3 -3
  46. package/dist/providers/poe-agent.js +723 -300
  47. package/dist/providers/poe-agent.js.map +4 -4
  48. package/dist/providers/tiny-http-mcp-server.d.ts +22 -0
  49. package/dist/providers/tiny-http-mcp-server.js +1471 -0
  50. package/dist/providers/tiny-http-mcp-server.js.map +7 -0
  51. package/dist/templates/pipeline/SKILL_plan.md +64 -52
  52. package/package.json +10 -2
  53. package/packages/memory/dist/explain.cli.d.ts +1 -2
  54. package/packages/memory/dist/explain.cli.js +1 -2
  55. package/packages/memory/dist/explain.d.ts +1 -2
  56. package/packages/memory/dist/explain.js +19 -12
  57. package/packages/memory/dist/handle.d.ts +37 -0
  58. package/packages/memory/dist/handle.js +41 -0
  59. package/packages/memory/dist/index.d.ts +3 -1
  60. package/packages/memory/dist/index.js +1474 -452
  61. package/packages/memory/dist/index.js.map +4 -4
  62. package/packages/memory/dist/ingest.d.ts +12 -1
  63. package/packages/memory/dist/ingest.js +23 -12
  64. package/packages/memory/dist/mcp.d.ts +4 -2
  65. package/packages/memory/dist/mcp.js +6 -10
  66. package/packages/memory/dist/query.js +2 -8
  67. package/packages/memory/dist/types.d.ts +0 -21
  68. package/packages/tiny-oauth-test-server/dist/cli.js +191 -0
  69. package/packages/tiny-stdio-mcp-server/dist/server.js +9 -1
  70. package/packages/tiny-stdio-mcp-server/dist/types.d.ts +1 -1
@@ -1,3 +1,14 @@
1
+ import { computeIngestKey, readCacheEntry, writeCacheEntry } from "./cache.js";
2
+ import { reconcile, snapshot } from "./reconcile.js";
3
+ import { computeTokenStats } from "./tokens.js";
1
4
  import type { IngestOptions, IngestResult, MemoryRoot } from "./types.js";
2
5
  export declare const INGEST_PROMPT_VERSION = "v1";
3
- export declare function ingest(root: MemoryRoot, opts: IngestOptions): Promise<IngestResult>;
6
+ export type IngestRunners = {
7
+ computeIngestKey?: typeof computeIngestKey;
8
+ readCacheEntry?: typeof readCacheEntry;
9
+ writeCacheEntry?: typeof writeCacheEntry;
10
+ computeTokenStats?: typeof computeTokenStats;
11
+ snapshot?: typeof snapshot;
12
+ reconcile?: typeof reconcile;
13
+ };
14
+ export declare function ingest(root: MemoryRoot, opts: IngestOptions, runners?: IngestRunners): Promise<IngestResult>;
@@ -1,12 +1,24 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { spawn } from "@poe-code/agent-spawn";
3
4
  import { cacheEnabled, configuredTimeout, resolveAgent } from "@poe-code/poe-code-config";
4
5
  import { computeIngestKey, readCacheEntry, writeCacheEntry } from "./cache.js";
5
6
  import { MEMORY_INDEX_RELPATH } from "./paths.js";
6
7
  import { reconcile, snapshot } from "./reconcile.js";
7
8
  import { computeTokenStats } from "./tokens.js";
8
9
  export const INGEST_PROMPT_VERSION = "v1";
9
- export async function ingest(root, opts) {
10
+ function resolveRunners(overrides) {
11
+ return {
12
+ computeIngestKey: overrides?.computeIngestKey ?? computeIngestKey,
13
+ readCacheEntry: overrides?.readCacheEntry ?? readCacheEntry,
14
+ writeCacheEntry: overrides?.writeCacheEntry ?? writeCacheEntry,
15
+ computeTokenStats: overrides?.computeTokenStats ?? computeTokenStats,
16
+ snapshot: overrides?.snapshot ?? snapshot,
17
+ reconcile: overrides?.reconcile ?? reconcile
18
+ };
19
+ }
20
+ export async function ingest(root, opts, runners) {
21
+ const resolved = resolveRunners(runners);
10
22
  const source = await materializeSource(opts.source);
11
23
  const indexMdBytes = await fs.readFile(path.join(root, MEMORY_INDEX_RELPATH));
12
24
  const configOptions = {
@@ -14,21 +26,21 @@ export async function ingest(root, opts) {
14
26
  filePath: path.join(inferRepoRoot(root), "poe-code.json")
15
27
  };
16
28
  const agentId = (await resolveAgent(configOptions, opts.agent ?? null)) ?? opts.agent ?? "claude-code";
17
- const key = computeIngestKey({
29
+ const key = resolved.computeIngestKey({
18
30
  sourceBytes: source.bytes,
19
31
  indexMdBytes,
20
32
  promptTemplateVersion: INGEST_PROMPT_VERSION,
21
33
  agentId
22
34
  });
23
35
  if (!opts.force && (await cacheEnabled(configOptions))) {
24
- const hit = await readCacheEntry(root, key);
36
+ const hit = await resolved.readCacheEntry(root, key);
25
37
  if (hit !== null) {
26
38
  return {
27
39
  diff: { created: [], updated: [], deleted: [] },
28
40
  exitCode: 0,
29
41
  durationMs: 0,
30
42
  cacheHit: true,
31
- tokens: await computeTokenStats(root)
43
+ tokens: await resolved.computeTokenStats(root)
32
44
  };
33
45
  }
34
46
  }
@@ -40,29 +52,28 @@ export async function ingest(root, opts) {
40
52
  exitCode: 0,
41
53
  durationMs: 0,
42
54
  cacheHit: false,
43
- tokens: await computeTokenStats(root)
55
+ tokens: await resolved.computeTokenStats(root)
44
56
  };
45
57
  }
46
- const before = await snapshot(root);
58
+ const before = await resolved.snapshot(root);
47
59
  let exitCode = 1;
48
60
  let durationMs = 0;
49
61
  let timeoutError;
50
62
  try {
51
- const spawnFn = opts.spawnFn;
52
- const result = await runWithTimeout(spawnFn?.(agentId, prompt) ?? Promise.resolve({ exitCode: 0, durationMs: 0 }), opts.timeoutMs ?? (await configuredTimeout(configOptions)));
63
+ const result = await runWithTimeout(spawn(agentId, { prompt }), opts.timeoutMs ?? (await configuredTimeout(configOptions)));
53
64
  exitCode = result.exitCode;
54
- durationMs = result.durationMs;
65
+ durationMs = result.durationMs ?? 0;
55
66
  }
56
67
  catch (error) {
57
68
  timeoutError = error instanceof Error ? error : new Error(String(error));
58
69
  }
59
- const diff = await reconcile(root, before, "ingest", opts.reason ?? `ingest ${source.label}`);
60
- const tokens = await computeTokenStats(root);
70
+ const diff = await resolved.reconcile(root, before, "ingest", opts.reason ?? `ingest ${source.label}`);
71
+ const tokens = await resolved.computeTokenStats(root);
61
72
  if (timeoutError !== undefined) {
62
73
  throw timeoutError;
63
74
  }
64
75
  if (!opts.noCacheWrite && (await cacheEnabled(configOptions)) && exitCode === 0) {
65
- await writeCacheEntry(root, {
76
+ await resolved.writeCacheEntry(root, {
66
77
  key,
67
78
  ingestedAt: new Date().toISOString(),
68
79
  sourceLabel: source.label,
@@ -1,6 +1,8 @@
1
1
  import type { Server } from "../../tiny-stdio-mcp-server/dist/index.js";
2
- import type { McpServerOptions } from "./types.js";
3
- export declare function startMemoryMcpServer(opts: McpServerOptions): Promise<{
2
+ import type { MemoryHandle } from "./handle.js";
3
+ export declare function startMemoryMcpServer(handle: MemoryHandle, opts: {
4
+ allowWrites: boolean;
5
+ }): Promise<{
4
6
  stop: () => Promise<void>;
5
7
  server: Server;
6
8
  }>;
@@ -1,21 +1,17 @@
1
1
  import { createServer, defineSchema } from "tiny-stdio-mcp-server";
2
- import { appendToPage } from "./write.js";
3
- import { listPages, readPage } from "./pages.js";
4
- import { searchMemory } from "./search.js";
5
- import { statusOf } from "./status.js";
6
- export async function startMemoryMcpServer(opts) {
2
+ export async function startMemoryMcpServer(handle, opts) {
7
3
  const server = createServer({
8
4
  name: "poe-code-memory",
9
5
  version: "0.0.1"
10
6
  });
11
7
  server.tool("list_pages", "List memory pages.", defineSchema({}), async () => ({
12
- pages: (await listPages(opts.root)).map((page) => ({
8
+ pages: (await handle.listPages()).map((page) => ({
13
9
  rel_path: page.relPath,
14
10
  description: page.frontmatter.description ?? ""
15
11
  }))
16
12
  }));
17
13
  server.tool("read_page", "Read a memory page.", defineSchema({ rel_path: { type: "string" } }), async ({ rel_path }) => {
18
- const page = await readPage(opts.root, rel_path);
14
+ const page = await handle.readPage(rel_path);
19
15
  return {
20
16
  rel_path: page.relPath,
21
17
  frontmatter: page.frontmatter,
@@ -27,17 +23,17 @@ export async function startMemoryMcpServer(opts) {
27
23
  query: { type: "string" },
28
24
  limit: { type: "number", optional: true }
29
25
  }), async ({ query, limit }) => {
30
- const hits = await searchMemory(opts.root, query);
26
+ const hits = await handle.searchMemory(query);
31
27
  return { hits: typeof limit === "number" ? hits.slice(0, limit) : hits };
32
28
  });
33
- server.tool("status", "Show memory status.", defineSchema({}), async () => statusOf(opts.root));
29
+ server.tool("status", "Show memory status.", defineSchema({}), async () => handle.statusOf());
34
30
  if (opts.allowWrites) {
35
31
  server.tool("append_to_page", "Append content to a memory page.", defineSchema({
36
32
  rel_path: { type: "string" },
37
33
  content: { type: "string" },
38
34
  reason: { type: "string" }
39
35
  }), async ({ rel_path, content, reason }) => ({
40
- diff: await appendToPage(opts.root, rel_path, content, { reason })
36
+ diff: await handle.appendToPage(rel_path, content, { reason })
41
37
  }));
42
38
  }
43
39
  return {
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { countTokens } from "tokenfill";
4
+ import { spawn } from "@poe-code/agent-spawn";
4
5
  import { resolveAgent } from "@poe-code/poe-code-config";
5
6
  import { listPages } from "./pages.js";
6
7
  import { MEMORY_INDEX_RELPATH } from "./paths.js";
@@ -21,14 +22,7 @@ export async function queryMemory(root, options) {
21
22
  };
22
23
  const agentId = (await resolveAgent(configOptions, options.agent ?? null)) ?? options.agent ?? "claude-code";
23
24
  const context = await selectQueryContext(root, options.question, options.budget);
24
- const spawnFn = options.spawnFn;
25
- const result = (await spawnFn?.(agentId, context.prompt)) ?? {
26
- answer: "",
27
- citations: [],
28
- tokensUsed: context.tokensUsed,
29
- budget: options.budget,
30
- exitCode: 0
31
- };
25
+ const result = (await spawn(agentId, { prompt: context.prompt }));
32
26
  return {
33
27
  answer: result.answer,
34
28
  citations: result.citations,
@@ -70,7 +70,6 @@ export type IngestSource = {
70
70
  kind: "url";
71
71
  url: string;
72
72
  };
73
- export type SpawnFn<TResult = unknown> = (agent: string, prompt: string) => Promise<TResult>;
74
73
  export type IngestOptions = {
75
74
  source: IngestSource;
76
75
  agent?: string;
@@ -79,14 +78,6 @@ export type IngestOptions = {
79
78
  dryRun?: boolean;
80
79
  force?: boolean;
81
80
  noCacheWrite?: boolean;
82
- spawnFn?: SpawnFn;
83
- };
84
- export type LintOptions = {
85
- fix?: boolean;
86
- agent?: string;
87
- timeoutMs?: number;
88
- dryRun?: boolean;
89
- spawnFn?: SpawnFn;
90
81
  };
91
82
  export type IngestResult = {
92
83
  diff: MemoryDiff;
@@ -95,13 +86,6 @@ export type IngestResult = {
95
86
  cacheHit: boolean;
96
87
  tokens: TokenStats;
97
88
  };
98
- export type LintResult = {
99
- diff: MemoryDiff;
100
- issues: string[];
101
- exitCode: number;
102
- durationMs: number;
103
- tokens: TokenStats;
104
- };
105
89
  export type IngestCacheKey = string;
106
90
  export type IngestCacheEntry = {
107
91
  key: IngestCacheKey;
@@ -121,10 +105,6 @@ export type TokenStats = {
121
105
  reductionRatio: number;
122
106
  missingSources: string[];
123
107
  };
124
- export type McpServerOptions = {
125
- root: MemoryRoot;
126
- allowWrites: boolean;
127
- };
128
108
  export type MemoryInstallResult = {
129
109
  skillInstalled: boolean;
130
110
  mcpConfigured: boolean;
@@ -135,7 +115,6 @@ export type QueryOptions = {
135
115
  question: string;
136
116
  budget: number;
137
117
  agent?: string;
138
- spawnFn?: SpawnFn;
139
118
  };
140
119
  export type QueryCitation = {
141
120
  relPath: string;
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import { parseArgs } from "node:util";
4
+ import { pathToFileURL } from "node:url";
5
+ import { createOAuthTestServer, } from "./index.js";
6
+ function readPackageInfo() {
7
+ try {
8
+ const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
9
+ if (typeof packageJson.name === "string" &&
10
+ typeof packageJson.version === "string") {
11
+ return {
12
+ name: packageJson.name,
13
+ version: packageJson.version,
14
+ };
15
+ }
16
+ }
17
+ catch {
18
+ // Fall through to stable defaults when package.json is unavailable.
19
+ }
20
+ return {
21
+ name: "tiny-oauth-test-server",
22
+ version: "0.0.0",
23
+ };
24
+ }
25
+ const packageInfo = readPackageInfo();
26
+ const HELP_TEXT = [
27
+ "Usage: tiny-oauth-test-server [options]",
28
+ "",
29
+ "Options:",
30
+ " --port <port> Port to listen on (default: 0, ephemeral)",
31
+ " --hostname <hostname> Hostname to bind to (default: 127.0.0.1)",
32
+ " --issuer <url> Issuer URL to publish in metadata and tokens",
33
+ " --ttl-seconds <seconds> Access token TTL in seconds (default: 60)",
34
+ " --auto-approve Auto-approve every authorization request",
35
+ " --static-client <client_id:redirect_uri[,redirect_uri...]>",
36
+ " Register a repeatable static client",
37
+ " -h, --help Show this help message",
38
+ ].join("\n");
39
+ function parsePort(value) {
40
+ if (value === undefined) {
41
+ return 0;
42
+ }
43
+ const port = Number(value);
44
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
45
+ throw new Error("--port must be an integer between 0 and 65535.");
46
+ }
47
+ return port;
48
+ }
49
+ function parsePositiveInteger(value, flagName) {
50
+ if (value === undefined) {
51
+ return 60;
52
+ }
53
+ const parsed = Number(value);
54
+ if (!Number.isInteger(parsed) || parsed <= 0) {
55
+ throw new Error(`${flagName} must be a positive integer.`);
56
+ }
57
+ return parsed;
58
+ }
59
+ function parseIssuer(value) {
60
+ if (value === undefined) {
61
+ return undefined;
62
+ }
63
+ try {
64
+ return new URL(value).toString();
65
+ }
66
+ catch {
67
+ throw new Error("--issuer must be an absolute URL.");
68
+ }
69
+ }
70
+ function parseStaticClientEntry(value) {
71
+ const separatorIndex = value.indexOf(":");
72
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
73
+ throw new Error("--static-client must use client_id:redirect_uri[,redirect_uri...] format.");
74
+ }
75
+ const clientId = value.slice(0, separatorIndex);
76
+ const redirectUris = value
77
+ .slice(separatorIndex + 1)
78
+ .split(",")
79
+ .map((item) => item.trim())
80
+ .filter((item) => item.length > 0);
81
+ if (redirectUris.length === 0) {
82
+ throw new Error("--static-client must include at least one redirect_uri.");
83
+ }
84
+ return {
85
+ clientId,
86
+ redirectUris,
87
+ };
88
+ }
89
+ function parseCliOptions(args) {
90
+ const { values } = parseArgs({
91
+ args,
92
+ strict: true,
93
+ allowPositionals: false,
94
+ options: {
95
+ port: { type: "string" },
96
+ hostname: { type: "string" },
97
+ issuer: { type: "string" },
98
+ "ttl-seconds": { type: "string" },
99
+ "auto-approve": { type: "boolean" },
100
+ "static-client": { type: "string", multiple: true },
101
+ help: { type: "boolean", short: "h" },
102
+ },
103
+ });
104
+ return {
105
+ help: values.help ?? false,
106
+ port: parsePort(values.port),
107
+ hostname: values.hostname ?? "127.0.0.1",
108
+ issuer: parseIssuer(values.issuer),
109
+ ttlSeconds: parsePositiveInteger(values["ttl-seconds"], "--ttl-seconds"),
110
+ autoApprove: values["auto-approve"] ?? false,
111
+ staticClients: (values["static-client"] ?? []).map(parseStaticClientEntry),
112
+ };
113
+ }
114
+ function formatCurlInvocation(baseUrl) {
115
+ return [
116
+ "curl -sS -X POST",
117
+ `${baseUrl}/testing/issue-token`,
118
+ "-H 'Content-Type: application/json'",
119
+ "-d '{\"client_id\":\"demo-client\",\"resource\":\"https://resource.example.com/mcp\",\"scopes\":[\"mcp.read\"]}'",
120
+ ].join(" ");
121
+ }
122
+ function waitForShutdown(shutdown) {
123
+ return new Promise((resolve, reject) => {
124
+ const onSignal = () => {
125
+ process.off("SIGINT", onSignal);
126
+ process.off("SIGTERM", onSignal);
127
+ void shutdown().then(resolve, reject);
128
+ };
129
+ process.once("SIGINT", onSignal);
130
+ process.once("SIGTERM", onSignal);
131
+ });
132
+ }
133
+ export function isCliInvocation(argv, moduleUrl, realpath = realpathSync) {
134
+ const entry = argv.at(1);
135
+ if (typeof entry !== "string") {
136
+ return false;
137
+ }
138
+ const candidates = [pathToFileURL(entry).href];
139
+ try {
140
+ candidates.push(pathToFileURL(realpath(entry)).href);
141
+ }
142
+ catch {
143
+ // Ignore resolution failures and keep the direct path candidate.
144
+ }
145
+ return candidates.includes(moduleUrl);
146
+ }
147
+ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
148
+ const stdout = dependencies.stdout ?? process.stdout;
149
+ const stderr = dependencies.stderr ?? process.stderr;
150
+ let parsed;
151
+ try {
152
+ parsed = parseCliOptions(args);
153
+ }
154
+ catch (error) {
155
+ stderr.write(`${error instanceof Error ? error.message : String(error)}\n\n${HELP_TEXT}\n`);
156
+ return 1;
157
+ }
158
+ if (parsed.help) {
159
+ stdout.write(`${HELP_TEXT}\n`);
160
+ return 0;
161
+ }
162
+ const listenOptions = {
163
+ port: parsed.port,
164
+ hostname: parsed.hostname,
165
+ };
166
+ const server = createOAuthTestServer({
167
+ issuer: parsed.issuer,
168
+ defaultTokenTtlSeconds: parsed.ttlSeconds,
169
+ staticClients: parsed.staticClients,
170
+ defaultAuthorization: {
171
+ autoApprove: parsed.autoApprove,
172
+ },
173
+ });
174
+ const handle = await server.listen(listenOptions);
175
+ const metadataUrl = `${server.issuer}/.well-known/oauth-authorization-server`;
176
+ stdout.write(`${packageInfo.name} ${packageInfo.version}\n`);
177
+ stdout.write(`Bound URL: ${handle.url}\n`);
178
+ stdout.write(`Issuer: ${server.issuer}\n`);
179
+ stdout.write(`PRM metadata URL: ${metadataUrl}\n`);
180
+ stdout.write(`Issue token curl: ${formatCurlInvocation(handle.url)}\n`);
181
+ await (dependencies.waitForShutdown ?? waitForShutdown)(handle.close);
182
+ return 0;
183
+ }
184
+ if (isCliInvocation(process.argv, import.meta.url)) {
185
+ void runCli().then((exitCode) => {
186
+ process.exitCode = exitCode;
187
+ }, (error) => {
188
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
189
+ process.exitCode = 1;
190
+ });
191
+ }
@@ -76,7 +76,9 @@ export function createServer(options) {
76
76
  }
77
77
  try {
78
78
  const handlerResult = await tool.handler(toolArgs);
79
- const result = { content: toContentBlocks(handlerResult) };
79
+ const result = isCallToolResult(handlerResult)
80
+ ? handlerResult
81
+ : { content: toContentBlocks(handlerResult) };
80
82
  return { result };
81
83
  }
82
84
  catch (err) {
@@ -224,3 +226,9 @@ export function createServer(options) {
224
226
  };
225
227
  return server;
226
228
  }
229
+ function isCallToolResult(value) {
230
+ if (typeof value !== "object" || value === null || !("content" in value)) {
231
+ return false;
232
+ }
233
+ return Array.isArray(value.content);
234
+ }
@@ -92,7 +92,7 @@ export interface ServerOptions {
92
92
  version: string;
93
93
  }
94
94
  import type { ToolReturn } from "./content/index.js";
95
- export type ToolHandler<T = Record<string, unknown>> = (args: T) => Promise<ToolReturn> | ToolReturn;
95
+ export type ToolHandler<T = Record<string, unknown>> = (args: T) => Promise<ToolReturn | CallToolResult> | ToolReturn | CallToolResult;
96
96
  export interface ToolDefinition<T = Record<string, unknown>> {
97
97
  name: string;
98
98
  description: string;