@mekareteriker/opencode-mcp 1.10.2-mekareteriker.0
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/CHANGELOG.md +204 -0
- package/LICENSE +22 -0
- package/README.md +174 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.js +282 -0
- package/dist/client.js.map +1 -0
- package/dist/helpers.d.ts +150 -0
- package/dist/helpers.js +575 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +198 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.js +210 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +10 -0
- package/dist/resources.js +197 -0
- package/dist/resources.js.map +1 -0
- package/dist/server-manager.d.ts +72 -0
- package/dist/server-manager.js +264 -0
- package/dist/server-manager.js.map +1 -0
- package/dist/tools/config.d.ts +3 -0
- package/dist/tools/config.js +105 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/events.d.ts +6 -0
- package/dist/tools/events.js +63 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/file.d.ts +3 -0
- package/dist/tools/file.js +153 -0
- package/dist/tools/file.js.map +1 -0
- package/dist/tools/global.d.ts +3 -0
- package/dist/tools/global.js +17 -0
- package/dist/tools/global.js.map +1 -0
- package/dist/tools/message.d.ts +3 -0
- package/dist/tools/message.js +169 -0
- package/dist/tools/message.js.map +1 -0
- package/dist/tools/misc.d.ts +3 -0
- package/dist/tools/misc.js +298 -0
- package/dist/tools/misc.js.map +1 -0
- package/dist/tools/project.d.ts +3 -0
- package/dist/tools/project.js +62 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/provider.d.ts +3 -0
- package/dist/tools/provider.js +175 -0
- package/dist/tools/provider.js.map +1 -0
- package/dist/tools/session.d.ts +3 -0
- package/dist/tools/session.js +392 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/tui.d.ts +7 -0
- package/dist/tools/tui.js +121 -0
- package/dist/tools/tui.js.map +1 -0
- package/dist/tools/workflow.d.ts +7 -0
- package/dist/tools/workflow.js +775 -0
- package/dist/tools/workflow.js.map +1 -0
- package/package.json +68 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client wrapper for the OpenCode server API.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Basic auth support
|
|
6
|
+
* - Automatic retry with exponential backoff for transient errors
|
|
7
|
+
* - Proper 204 No Content handling on all methods
|
|
8
|
+
* - SSE streaming support
|
|
9
|
+
* - Error categorization (transient vs permanent)
|
|
10
|
+
* - Directory path normalization and validation
|
|
11
|
+
* - Lazy server reconnection on connection failure
|
|
12
|
+
*/
|
|
13
|
+
import { normalizeDirectory } from "./helpers.js";
|
|
14
|
+
import { ensureServer, isServerRunning } from "./server-manager.js";
|
|
15
|
+
export class OpenCodeError extends Error {
|
|
16
|
+
status;
|
|
17
|
+
method;
|
|
18
|
+
path;
|
|
19
|
+
body;
|
|
20
|
+
constructor(message, status, method, path, body) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.method = method;
|
|
24
|
+
this.path = path;
|
|
25
|
+
this.body = body;
|
|
26
|
+
this.name = "OpenCodeError";
|
|
27
|
+
}
|
|
28
|
+
get isTransient() {
|
|
29
|
+
return (this.status === 429 ||
|
|
30
|
+
this.status === 502 ||
|
|
31
|
+
this.status === 503 ||
|
|
32
|
+
this.status === 504);
|
|
33
|
+
}
|
|
34
|
+
get isNotFound() {
|
|
35
|
+
return this.status === 404;
|
|
36
|
+
}
|
|
37
|
+
get isAuth() {
|
|
38
|
+
return this.status === 401 || this.status === 403;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const MAX_RETRIES = 2;
|
|
42
|
+
const BASE_DELAY_MS = 500;
|
|
43
|
+
/** Max reconnection attempts per MCP session lifetime. */
|
|
44
|
+
const MAX_RECONNECT_ATTEMPTS = 3;
|
|
45
|
+
/** Check if an error looks like a connection failure (server unreachable). */
|
|
46
|
+
function isConnectionError(err) {
|
|
47
|
+
const msg = err.message.toLowerCase();
|
|
48
|
+
return (msg.includes("econnrefused") ||
|
|
49
|
+
msg.includes("enotfound") ||
|
|
50
|
+
msg.includes("ehostunreach") ||
|
|
51
|
+
msg.includes("fetch failed") ||
|
|
52
|
+
msg.includes("network error") ||
|
|
53
|
+
msg.includes("socket hang up"));
|
|
54
|
+
}
|
|
55
|
+
export class OpenCodeClient {
|
|
56
|
+
baseUrl;
|
|
57
|
+
authHeader;
|
|
58
|
+
autoServe;
|
|
59
|
+
reconnectAttempts = 0;
|
|
60
|
+
username;
|
|
61
|
+
password;
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
64
|
+
this.autoServe = options.autoServe ?? false;
|
|
65
|
+
this.username = options.username;
|
|
66
|
+
this.password = options.password;
|
|
67
|
+
if (options.password) {
|
|
68
|
+
const username = options.username ?? "opencode";
|
|
69
|
+
this.authHeader =
|
|
70
|
+
"Basic " +
|
|
71
|
+
Buffer.from(`${username}:${options.password}`).toString("base64");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
getBaseUrl() {
|
|
75
|
+
return this.baseUrl;
|
|
76
|
+
}
|
|
77
|
+
buildUrl(path, query) {
|
|
78
|
+
const url = new URL(path, this.baseUrl);
|
|
79
|
+
if (query) {
|
|
80
|
+
for (const [key, value] of Object.entries(query)) {
|
|
81
|
+
if (value !== undefined && value !== "") {
|
|
82
|
+
url.searchParams.set(key, value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return url.toString();
|
|
87
|
+
}
|
|
88
|
+
headers(accept, directory) {
|
|
89
|
+
const h = {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
Accept: accept ?? "application/json",
|
|
92
|
+
};
|
|
93
|
+
if (this.authHeader) {
|
|
94
|
+
h["Authorization"] = this.authHeader;
|
|
95
|
+
}
|
|
96
|
+
// Normalize and validate the directory path before sending as header
|
|
97
|
+
const normalized = normalizeDirectory(directory);
|
|
98
|
+
if (normalized) {
|
|
99
|
+
h["x-opencode-directory"] = normalized;
|
|
100
|
+
}
|
|
101
|
+
return h;
|
|
102
|
+
}
|
|
103
|
+
async request(method, path, opts) {
|
|
104
|
+
const url = this.buildUrl(path, opts?.query);
|
|
105
|
+
let lastError;
|
|
106
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
107
|
+
if (attempt > 0) {
|
|
108
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
109
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
const timeoutId = opts?.timeout
|
|
114
|
+
? setTimeout(() => controller.abort(), opts.timeout)
|
|
115
|
+
: undefined;
|
|
116
|
+
const res = await fetch(url, {
|
|
117
|
+
method,
|
|
118
|
+
headers: this.headers(undefined, opts?.directory),
|
|
119
|
+
body: opts?.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
120
|
+
signal: controller.signal,
|
|
121
|
+
});
|
|
122
|
+
if (timeoutId)
|
|
123
|
+
clearTimeout(timeoutId);
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
const text = await res.text();
|
|
126
|
+
const err = new OpenCodeError(`${method} ${path} failed (${res.status}): ${text}`, res.status, method, path, text);
|
|
127
|
+
if (err.isTransient && attempt < MAX_RETRIES) {
|
|
128
|
+
lastError = err;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
// Handle 204 No Content
|
|
134
|
+
if (res.status === 204) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
138
|
+
if (contentType.includes("application/json")) {
|
|
139
|
+
return (await res.json());
|
|
140
|
+
}
|
|
141
|
+
// Return text for non-JSON responses
|
|
142
|
+
return (await res.text());
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
if (e instanceof OpenCodeError)
|
|
146
|
+
throw e;
|
|
147
|
+
lastError = e;
|
|
148
|
+
if (attempt >= MAX_RETRIES)
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Lazy reconnection: if all retries exhausted and error looks like a
|
|
153
|
+
// connection failure, try restarting the server and retry once.
|
|
154
|
+
if (this.autoServe &&
|
|
155
|
+
this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS &&
|
|
156
|
+
lastError &&
|
|
157
|
+
isConnectionError(lastError)) {
|
|
158
|
+
this.reconnectAttempts++;
|
|
159
|
+
console.error(`Connection failed (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}), attempting server reconnection...`);
|
|
160
|
+
try {
|
|
161
|
+
const status = await isServerRunning(this.baseUrl, this.username, this.password);
|
|
162
|
+
if (!status.healthy) {
|
|
163
|
+
await ensureServer({
|
|
164
|
+
baseUrl: this.baseUrl,
|
|
165
|
+
autoServe: true,
|
|
166
|
+
username: this.username,
|
|
167
|
+
password: this.password
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Retry the original request once after reconnection
|
|
171
|
+
return this.request(method, path, opts);
|
|
172
|
+
}
|
|
173
|
+
catch (reconnectErr) {
|
|
174
|
+
console.error(`Server reconnection failed: ${reconnectErr instanceof Error ? reconnectErr.message : String(reconnectErr)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
throw lastError ?? new Error(`${method} ${path} failed after retries`);
|
|
178
|
+
}
|
|
179
|
+
async get(path, query, directory) {
|
|
180
|
+
return this.request("GET", path, { query, directory });
|
|
181
|
+
}
|
|
182
|
+
async post(path, body, opts) {
|
|
183
|
+
return this.request("POST", path, {
|
|
184
|
+
body,
|
|
185
|
+
timeout: opts?.timeout,
|
|
186
|
+
directory: opts?.directory,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async patch(path, body, directory) {
|
|
190
|
+
return this.request("PATCH", path, { body, directory });
|
|
191
|
+
}
|
|
192
|
+
async put(path, body, directory) {
|
|
193
|
+
return this.request("PUT", path, { body, directory });
|
|
194
|
+
}
|
|
195
|
+
async delete(path, query, directory) {
|
|
196
|
+
return this.request("DELETE", path, { query, directory });
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Subscribe to SSE events. Returns an async iterable of parsed events.
|
|
200
|
+
* The caller should break out of the loop when done.
|
|
201
|
+
*/
|
|
202
|
+
async *subscribeSSE(path, opts) {
|
|
203
|
+
const url = this.buildUrl(path);
|
|
204
|
+
const res = await fetch(url, {
|
|
205
|
+
method: "GET",
|
|
206
|
+
headers: {
|
|
207
|
+
...this.headers("text/event-stream"),
|
|
208
|
+
Accept: "text/event-stream",
|
|
209
|
+
"Cache-Control": "no-cache",
|
|
210
|
+
},
|
|
211
|
+
signal: opts?.signal,
|
|
212
|
+
});
|
|
213
|
+
if (!res.ok) {
|
|
214
|
+
const text = await res.text();
|
|
215
|
+
throw new OpenCodeError(`SSE ${path} failed (${res.status}): ${text}`, res.status, "GET", path, text);
|
|
216
|
+
}
|
|
217
|
+
if (!res.body) {
|
|
218
|
+
throw new Error("No response body for SSE stream");
|
|
219
|
+
}
|
|
220
|
+
const reader = res.body.getReader();
|
|
221
|
+
const decoder = new TextDecoder();
|
|
222
|
+
let buffer = "";
|
|
223
|
+
let currentEvent = "";
|
|
224
|
+
let currentData = "";
|
|
225
|
+
const abortHandler = () => {
|
|
226
|
+
try {
|
|
227
|
+
// Cancels any pending reader.read() and causes the generator to unwind.
|
|
228
|
+
void reader.cancel().catch(() => {
|
|
229
|
+
// ignore
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// ignore
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
if (opts?.signal) {
|
|
237
|
+
if (opts.signal.aborted)
|
|
238
|
+
abortHandler();
|
|
239
|
+
else
|
|
240
|
+
opts.signal.addEventListener("abort", abortHandler, { once: true });
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
while (true) {
|
|
244
|
+
if (opts?.signal?.aborted)
|
|
245
|
+
break;
|
|
246
|
+
const { done, value } = await reader.read();
|
|
247
|
+
if (done)
|
|
248
|
+
break;
|
|
249
|
+
buffer += decoder.decode(value, { stream: true });
|
|
250
|
+
const lines = buffer.split("\n");
|
|
251
|
+
buffer = lines.pop() ?? "";
|
|
252
|
+
for (const line of lines) {
|
|
253
|
+
if (line.startsWith("event:")) {
|
|
254
|
+
currentEvent = line.slice(6).trim();
|
|
255
|
+
}
|
|
256
|
+
else if (line.startsWith("data:")) {
|
|
257
|
+
currentData = line.slice(5).trim();
|
|
258
|
+
}
|
|
259
|
+
else if (line === "") {
|
|
260
|
+
if (currentData) {
|
|
261
|
+
yield { event: currentEvent || "message", data: currentData };
|
|
262
|
+
currentEvent = "";
|
|
263
|
+
currentData = "";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
if (opts?.signal) {
|
|
271
|
+
try {
|
|
272
|
+
opts.signal.removeEventListener("abort", abortHandler);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// ignore
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
reader.releaseLock();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart response formatting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Instead of dumping raw JSON to the LLM, these helpers extract the
|
|
5
|
+
* meaningful content from OpenCode API responses so the LLM can reason
|
|
6
|
+
* about them efficiently.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
/** Read-only tool: does not modify state. */
|
|
10
|
+
export declare const readOnly: {
|
|
11
|
+
readonly readOnlyHint: true;
|
|
12
|
+
readonly destructiveHint: false;
|
|
13
|
+
};
|
|
14
|
+
/** Destructive tool: permanently deletes data or shuts down services. */
|
|
15
|
+
export declare const destructive: {
|
|
16
|
+
readonly readOnlyHint: false;
|
|
17
|
+
readonly destructiveHint: true;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Shared Zod parameter for project directory targeting.
|
|
21
|
+
* When provided, sent as the x-opencode-directory header so the
|
|
22
|
+
* OpenCode server scopes the request to that project.
|
|
23
|
+
*/
|
|
24
|
+
export declare const directoryParam: z.ZodOptional<z.ZodString>;
|
|
25
|
+
/**
|
|
26
|
+
* Set the global default provider and model.
|
|
27
|
+
* Called once from index.ts during startup.
|
|
28
|
+
*/
|
|
29
|
+
export declare function setModelDefaults(providerID?: string, modelID?: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Apply model defaults: use explicit params if both are provided,
|
|
32
|
+
* otherwise fall back to env-var defaults if both are set,
|
|
33
|
+
* otherwise return undefined (let the server decide).
|
|
34
|
+
*
|
|
35
|
+
* Returns `{ providerID, modelID }` or `undefined`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyModelDefaults(providerID?: string, modelID?: string, variant?: string): {
|
|
38
|
+
providerID: string;
|
|
39
|
+
modelID: string;
|
|
40
|
+
variant?: string;
|
|
41
|
+
} | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Normalize and validate a directory path:
|
|
44
|
+
* - Resolves to absolute (handles "..", ".", trailing slashes, and
|
|
45
|
+
* converts relative inputs against `process.cwd()`)
|
|
46
|
+
* - Confirms the resolved path is absolute for the current platform
|
|
47
|
+
* - Validates that the path exists on disk
|
|
48
|
+
*
|
|
49
|
+
* Accepts both POSIX ("/home/user/my-project") and Windows
|
|
50
|
+
* ("C:\\Users\\me\\my-project", "\\\\server\\share") absolute paths via
|
|
51
|
+
* the platform-aware `resolve` + `isAbsolute` from `node:path`.
|
|
52
|
+
*
|
|
53
|
+
* Returns the normalized path, or undefined if input was undefined.
|
|
54
|
+
* Throws a descriptive Error on validation failure.
|
|
55
|
+
*/
|
|
56
|
+
export declare function normalizeDirectory(directory?: string): string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Extract a human-readable summary from a message response.
|
|
59
|
+
* Pulls text content from parts, summarizes tool calls, etc.
|
|
60
|
+
* Accepts any shape — casts internally for safety.
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatMessageResponse(response: unknown): string;
|
|
63
|
+
/**
|
|
64
|
+
* Format a list of messages, extracting text content from each.
|
|
65
|
+
*
|
|
66
|
+
* When the assistant message has no text content (common with some providers
|
|
67
|
+
* that only emit tool calls), we show a concise summary of tool actions
|
|
68
|
+
* instead of blank output. Cost/token metadata from step-finish parts is
|
|
69
|
+
* appended when available.
|
|
70
|
+
*/
|
|
71
|
+
export declare function formatMessageList(messages: unknown[]): string;
|
|
72
|
+
/**
|
|
73
|
+
* Format a diff response into a readable summary.
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatDiffResponse(diffs: unknown[]): string;
|
|
76
|
+
/**
|
|
77
|
+
* Format session objects for LLM-friendly display.
|
|
78
|
+
*/
|
|
79
|
+
export declare function formatSessionList(sessions: unknown[]): string;
|
|
80
|
+
/**
|
|
81
|
+
* Generic safe JSON stringify with truncation for very large responses.
|
|
82
|
+
*/
|
|
83
|
+
export declare function safeStringify(value: unknown, maxLength?: number): string;
|
|
84
|
+
/**
|
|
85
|
+
* Analyze an AI message response for signs of failure:
|
|
86
|
+
* - Completely empty (null/undefined)
|
|
87
|
+
* - Has parts but no text content (provider returned nothing)
|
|
88
|
+
* - Contains error indicators in parts
|
|
89
|
+
*
|
|
90
|
+
* Returns a diagnostic object with `isEmpty`, `hasError`, and `warning` text.
|
|
91
|
+
*/
|
|
92
|
+
export declare function analyzeMessageResponse(response: unknown): {
|
|
93
|
+
isEmpty: boolean;
|
|
94
|
+
hasError: boolean;
|
|
95
|
+
warning: string | null;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Redact values that look like API keys, tokens, or secrets.
|
|
99
|
+
* Replaces the value with the first 4 characters + "***REDACTED***".
|
|
100
|
+
* Works recursively on objects and arrays.
|
|
101
|
+
*
|
|
102
|
+
* Three layers of detection:
|
|
103
|
+
* 1. Key-name based: key names matching sensitive patterns (KEY, TOKEN, SECRET, etc.)
|
|
104
|
+
* 2. Value-based: string values matching known API key prefixes or long hex/base64 tokens
|
|
105
|
+
* 3. URL-based: query parameters in URL strings that contain secrets
|
|
106
|
+
*/
|
|
107
|
+
export declare function redactSecrets(value: unknown): unknown;
|
|
108
|
+
/**
|
|
109
|
+
* Determine whether a provider object from the OpenCode API is truly configured
|
|
110
|
+
* (i.e. has usable credentials), as opposed to being a built-in default.
|
|
111
|
+
*
|
|
112
|
+
* Detection layers:
|
|
113
|
+
* - source "env" / "config" / "api" → always configured
|
|
114
|
+
* - source "custom" with a non-empty apiKey → configured
|
|
115
|
+
* - source "custom" for "anthropic" with extra option keys (OAuth sets headers) → configured
|
|
116
|
+
* - Everything else → not configured
|
|
117
|
+
*/
|
|
118
|
+
export declare function isProviderConfigured(p: Record<string, unknown>): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a session status value from the OpenCode API.
|
|
121
|
+
*
|
|
122
|
+
* The API may return status as a plain string ("idle", "running") or as an
|
|
123
|
+
* object like `{ state: "running", ... }`. This helper normalises both forms
|
|
124
|
+
* into a human-readable string.
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveSessionStatus(raw: unknown): string;
|
|
127
|
+
/**
|
|
128
|
+
* Standard tool response builder.
|
|
129
|
+
*/
|
|
130
|
+
export declare function toolResult(text: string, isError?: boolean): {
|
|
131
|
+
isError?: boolean | undefined;
|
|
132
|
+
content: {
|
|
133
|
+
type: "text";
|
|
134
|
+
text: string;
|
|
135
|
+
}[];
|
|
136
|
+
};
|
|
137
|
+
export declare function toolError(e: unknown): {
|
|
138
|
+
isError?: boolean | undefined;
|
|
139
|
+
content: {
|
|
140
|
+
type: "text";
|
|
141
|
+
text: string;
|
|
142
|
+
}[];
|
|
143
|
+
};
|
|
144
|
+
export declare function toolJson(value: unknown): {
|
|
145
|
+
isError?: boolean | undefined;
|
|
146
|
+
content: {
|
|
147
|
+
type: "text";
|
|
148
|
+
text: string;
|
|
149
|
+
}[];
|
|
150
|
+
};
|