@stll/folio-cli 0.3.4 → 0.4.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.
- package/README.md +37 -9
- package/dist/batch-operations.d.ts +29 -0
- package/dist/batch-operations.js +226 -0
- package/dist/document.d.ts +16 -2
- package/dist/document.js +32 -8
- package/dist/execute-read.d.ts +14 -10
- package/dist/execute-read.js +27 -15
- package/dist/execute-write.d.ts +1 -1
- package/dist/execute-write.js +36 -16
- package/dist/mcp-kit/envelope.d.ts +27 -0
- package/dist/mcp-kit/envelope.js +80 -0
- package/dist/mcp-kit/index.d.ts +6 -0
- package/dist/mcp-kit/index.js +5 -0
- package/dist/mcp-kit/input.d.ts +23 -0
- package/dist/mcp-kit/input.js +76 -0
- package/dist/mcp-kit/schema.d.ts +20 -0
- package/dist/mcp-kit/schema.js +97 -0
- package/dist/mcp-kit/surface.d.ts +19 -0
- package/dist/mcp-kit/surface.js +312 -0
- package/dist/mcp-kit/types.d.ts +98 -0
- package/dist/mcp-kit/types.js +1 -0
- package/dist/mcp.d.ts +8 -2
- package/dist/mcp.js +486 -72
- package/dist/package.js +1 -1
- package/dist/registry.js +4 -2
- package/dist/save.d.ts +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { KIT_ERROR_CODES, closestNames, didYouMean, failure, success, toCallResult, validationError } from "./envelope.js";
|
|
2
|
+
import { readToolInput } from "./input.js";
|
|
3
|
+
import { compactSchema } from "./schema.js";
|
|
4
|
+
//#region src/mcp-kit/surface.ts
|
|
5
|
+
/**
|
|
6
|
+
* A tool surface with lazy discovery. The frequent tools are listed directly
|
|
7
|
+
* with compact schemas; the rest stay out of `tools/list` and are reached
|
|
8
|
+
* through three capability tools, so a turn pays only for what is listed:
|
|
9
|
+
*
|
|
10
|
+
* - `list_capabilities` pages the unlisted tools: id, summary, access.
|
|
11
|
+
* - `describe_capability` returns one tool's parameter outline, short
|
|
12
|
+
* guidance and an example; with `detail: "full"`, its full input schema and
|
|
13
|
+
* guidance (listed tools included, whose listed schema is the compact one).
|
|
14
|
+
* - `invoke_capability` runs one by id with `input` checked against that
|
|
15
|
+
* full schema; `validate_only` checks without running.
|
|
16
|
+
*
|
|
17
|
+
* Every tool is also callable by its own name. Calls are read through
|
|
18
|
+
* `readToolInput` and answered with `toCallResult`'s one envelope.
|
|
19
|
+
*/
|
|
20
|
+
const CAPABILITY_TOOL_NAMES = {
|
|
21
|
+
list: "list_capabilities",
|
|
22
|
+
describe: "describe_capability",
|
|
23
|
+
invoke: "invoke_capability"
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_LIST_LIMIT = 50;
|
|
26
|
+
const MAX_LIST_LIMIT = 100;
|
|
27
|
+
const LIST_SCHEMA = {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
domain: { type: "string" },
|
|
31
|
+
access: {
|
|
32
|
+
type: "string",
|
|
33
|
+
enum: [
|
|
34
|
+
"all",
|
|
35
|
+
"read",
|
|
36
|
+
"write"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
cursor: { type: "string" },
|
|
40
|
+
limit: {
|
|
41
|
+
type: "integer",
|
|
42
|
+
minimum: 1,
|
|
43
|
+
maximum: MAX_LIST_LIMIT
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const DESCRIBE_SCHEMA = {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
capability: { type: "string" },
|
|
51
|
+
detail: {
|
|
52
|
+
type: "string",
|
|
53
|
+
enum: ["compact", "full"]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
required: ["capability"]
|
|
57
|
+
};
|
|
58
|
+
const INVOKE_SCHEMA = {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
capability: { type: "string" },
|
|
62
|
+
input: { type: "object" },
|
|
63
|
+
validate_only: { type: "boolean" }
|
|
64
|
+
},
|
|
65
|
+
required: ["capability"]
|
|
66
|
+
};
|
|
67
|
+
const READ_ONLY = {
|
|
68
|
+
readOnlyHint: true,
|
|
69
|
+
destructiveHint: false,
|
|
70
|
+
openWorldHint: false
|
|
71
|
+
};
|
|
72
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
73
|
+
/** Takes only the fields it reads, so a tool of any context type fits. */
|
|
74
|
+
const destructiveOf = (tool) => tool.destructive ?? tool.access === "write";
|
|
75
|
+
const listedSchema = (schema) => ({
|
|
76
|
+
...compactSchema(schema),
|
|
77
|
+
type: "object"
|
|
78
|
+
});
|
|
79
|
+
/** One property as a short type: `string`, `object[]`, `"a" | "b"`. */
|
|
80
|
+
const typeOutline = (schema) => {
|
|
81
|
+
if (!isRecord(schema)) return "any";
|
|
82
|
+
if (Array.isArray(schema["enum"])) return schema["enum"].map((value) => JSON.stringify(value)).join(" | ");
|
|
83
|
+
const type = schema["type"];
|
|
84
|
+
if (type === "array") return `${typeOutline(schema["items"])}[]`;
|
|
85
|
+
if (typeof type === "string") return type;
|
|
86
|
+
if (Array.isArray(type)) return type.join(" | ");
|
|
87
|
+
const variants = schema["oneOf"] ?? schema["anyOf"];
|
|
88
|
+
if (Array.isArray(variants)) return [...new Set(variants.map(typeOutline))].join(" | ");
|
|
89
|
+
return "any";
|
|
90
|
+
};
|
|
91
|
+
/** Each parameter's type, required ones marked: what a compact description shows. */
|
|
92
|
+
const parameterOutline = (schema) => {
|
|
93
|
+
const properties = isRecord(schema["properties"]) ? schema["properties"] : {};
|
|
94
|
+
const required = new Set(Array.isArray(schema["required"]) ? schema["required"] : []);
|
|
95
|
+
return Object.fromEntries(Object.entries(properties).map(([key, property]) => [key, `${typeOutline(property)}${required.has(key) ? " (required)" : ""}`]));
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Ordinal (code unit) order: the one order both the listing and its cursor
|
|
99
|
+
* use, so a page never skips or repeats an id whatever its case or symbols.
|
|
100
|
+
*/
|
|
101
|
+
const compareIds = (a, b) => {
|
|
102
|
+
if (a === b) return 0;
|
|
103
|
+
return a < b ? -1 : 1;
|
|
104
|
+
};
|
|
105
|
+
const encodeCursor = (id) => Buffer.from(id, "utf8").toString("base64url");
|
|
106
|
+
const decodeCursor = (cursor) => {
|
|
107
|
+
const decoded = Buffer.from(cursor, "base64url").toString("utf8");
|
|
108
|
+
return decoded.length > 0 && encodeCursor(decoded) === cursor ? decoded : null;
|
|
109
|
+
};
|
|
110
|
+
const hasJsonType = (value, type) => {
|
|
111
|
+
if (type === "object") return isRecord(value);
|
|
112
|
+
if (type === "integer") return Number.isInteger(value);
|
|
113
|
+
return typeof value === type;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* The capability tools' own arguments, read strictly: a misspelt flag or a
|
|
117
|
+
* string where a boolean belongs would otherwise change what runs.
|
|
118
|
+
*/
|
|
119
|
+
const readMetaArgs = (tool, schema, value) => {
|
|
120
|
+
if (value !== void 0 && !isRecord(value)) return {
|
|
121
|
+
ok: false,
|
|
122
|
+
outcome: validationError(`${tool} takes a JSON object.`, [])
|
|
123
|
+
};
|
|
124
|
+
const args = {};
|
|
125
|
+
const issues = [];
|
|
126
|
+
for (const [key, entry] of Object.entries(value ?? {})) {
|
|
127
|
+
const property = Object.hasOwn(schema.properties, key) ? schema.properties[key] : void 0;
|
|
128
|
+
if (property === void 0) {
|
|
129
|
+
issues.push({
|
|
130
|
+
path: key,
|
|
131
|
+
message: `Unknown parameter: ${key}`
|
|
132
|
+
});
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (entry === null) continue;
|
|
136
|
+
if (!hasJsonType(entry, property.type) || property.enum !== void 0 && !property.enum.includes(String(entry))) {
|
|
137
|
+
issues.push({
|
|
138
|
+
path: key,
|
|
139
|
+
message: `Expected ${property.enum === void 0 ? `a JSON ${property.type}` : property.enum.join(" | ")}.`
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
args[key] = entry;
|
|
144
|
+
}
|
|
145
|
+
for (const key of schema.required ?? []) if (args[key] === void 0 && !issues.some(({ path }) => path === key)) issues.push({
|
|
146
|
+
path: key,
|
|
147
|
+
message: `Missing parameter: ${key}`
|
|
148
|
+
});
|
|
149
|
+
return issues.length > 0 ? {
|
|
150
|
+
ok: false,
|
|
151
|
+
outcome: validationError(`${tool} arguments need clarification.`, issues, `Accepted parameters: ${Object.keys(schema.properties).join(", ")}.`)
|
|
152
|
+
} : {
|
|
153
|
+
ok: true,
|
|
154
|
+
args
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
/** Build a surface over `tools`. Names must be unique and not a capability tool's. */
|
|
158
|
+
const createToolSurface = ({ tools }) => {
|
|
159
|
+
const byName = /* @__PURE__ */ new Map();
|
|
160
|
+
const reserved = new Set(Object.values(CAPABILITY_TOOL_NAMES));
|
|
161
|
+
for (const tool of tools) {
|
|
162
|
+
if (byName.has(tool.name) || reserved.has(tool.name)) throw new Error(`Tool name ${tool.name} is taken.`);
|
|
163
|
+
byName.set(tool.name, tool);
|
|
164
|
+
}
|
|
165
|
+
const lazy = tools.filter((tool) => tool.direct === void 0).toSorted((a, b) => compareIds(a.name, b.name));
|
|
166
|
+
const names = [...byName.keys()];
|
|
167
|
+
const unknownCapability = (id) => failure({
|
|
168
|
+
code: KIT_ERROR_CODES.notFound,
|
|
169
|
+
message: `No capability with id "${id}".`,
|
|
170
|
+
hint: `${didYouMean(closestNames(id, names))}Call ${CAPABILITY_TOOL_NAMES.list} to browse the ids.`
|
|
171
|
+
});
|
|
172
|
+
const run = async (tool, value, context, validateOnly = false) => {
|
|
173
|
+
const read = readToolInput({
|
|
174
|
+
schema: tool.inputSchema,
|
|
175
|
+
value,
|
|
176
|
+
access: tool.access,
|
|
177
|
+
exactProperties: tool.exactProperties
|
|
178
|
+
});
|
|
179
|
+
if (!read.ok) return toCallResult(validationError(read.message, read.issues, read.hint));
|
|
180
|
+
if (validateOnly) return toCallResult(success({
|
|
181
|
+
valid: true,
|
|
182
|
+
input: read.value
|
|
183
|
+
}), read.notes);
|
|
184
|
+
let outcome;
|
|
185
|
+
try {
|
|
186
|
+
outcome = await tool.run(read.value, context);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
outcome = failure({
|
|
189
|
+
code: KIT_ERROR_CODES.internal,
|
|
190
|
+
message: error instanceof Error ? error.message : String(error)
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return toCallResult(outcome, read.notes);
|
|
194
|
+
};
|
|
195
|
+
const listCapabilities = (value) => {
|
|
196
|
+
const read = readMetaArgs(CAPABILITY_TOOL_NAMES.list, LIST_SCHEMA, value);
|
|
197
|
+
if (!read.ok) return read.outcome;
|
|
198
|
+
const { domain, access = "all", cursor, limit = DEFAULT_LIST_LIMIT } = read.args;
|
|
199
|
+
if (typeof limit !== "number" || limit < 1 || limit > MAX_LIST_LIMIT) return validationError("limit is out of range.", [{
|
|
200
|
+
path: "limit",
|
|
201
|
+
message: `Expected 1 to ${MAX_LIST_LIMIT}.`
|
|
202
|
+
}]);
|
|
203
|
+
const after = typeof cursor === "string" ? decodeCursor(cursor) : void 0;
|
|
204
|
+
if (after === null) return validationError("cursor is not a nextCursor from list_capabilities.", [{
|
|
205
|
+
path: "cursor",
|
|
206
|
+
message: "Unrecognised cursor."
|
|
207
|
+
}]);
|
|
208
|
+
const matching = lazy.filter((tool) => (domain === void 0 || tool.domain === domain) && (access === "all" || tool.access === access) && (after === void 0 || compareIds(tool.name, after) > 0));
|
|
209
|
+
const page = matching.slice(0, limit);
|
|
210
|
+
const last = page.at(-1);
|
|
211
|
+
return success({
|
|
212
|
+
items: page.map((tool) => ({
|
|
213
|
+
id: tool.name,
|
|
214
|
+
summary: tool.summary,
|
|
215
|
+
access: tool.access,
|
|
216
|
+
...destructiveOf(tool) && { destructive: true }
|
|
217
|
+
})),
|
|
218
|
+
nextCursor: last !== void 0 && matching.length > page.length ? encodeCursor(last.name) : null
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
const describeCapability = (value) => {
|
|
222
|
+
const read = readMetaArgs(CAPABILITY_TOOL_NAMES.describe, DESCRIBE_SCHEMA, value);
|
|
223
|
+
if (!read.ok) return read.outcome;
|
|
224
|
+
const id = String(read.args["capability"]);
|
|
225
|
+
const tool = byName.get(id);
|
|
226
|
+
if (tool === void 0) return unknownCapability(id);
|
|
227
|
+
if (read.args["detail"] === "full") return success({
|
|
228
|
+
id: tool.name,
|
|
229
|
+
description: tool.guide === void 0 ? tool.summary : `${tool.summary}\n${tool.guide}`,
|
|
230
|
+
access: tool.access,
|
|
231
|
+
destructive: destructiveOf(tool),
|
|
232
|
+
inputSchema: tool.describedSchema ?? tool.inputSchema
|
|
233
|
+
});
|
|
234
|
+
return success({
|
|
235
|
+
id: tool.name,
|
|
236
|
+
description: tool.brief === void 0 ? tool.summary : `${tool.summary}\n${tool.brief}`,
|
|
237
|
+
access: tool.access,
|
|
238
|
+
destructive: destructiveOf(tool),
|
|
239
|
+
parameters: parameterOutline(tool.inputSchema),
|
|
240
|
+
...tool.example !== void 0 && { example: tool.example },
|
|
241
|
+
more: "detail: \"full\" returns the full input schema."
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
const invokeCapability = async (value, context) => {
|
|
245
|
+
const read = readMetaArgs(CAPABILITY_TOOL_NAMES.invoke, INVOKE_SCHEMA, value);
|
|
246
|
+
if (!read.ok) return toCallResult(read.outcome);
|
|
247
|
+
const id = String(read.args["capability"]);
|
|
248
|
+
const tool = byName.get(id);
|
|
249
|
+
if (tool === void 0) return toCallResult(unknownCapability(id));
|
|
250
|
+
return await run(tool, read.args["input"] ?? {}, context, read.args["validate_only"] === true);
|
|
251
|
+
};
|
|
252
|
+
const listTools = () => {
|
|
253
|
+
const listed = tools.flatMap((tool) => tool.direct === void 0 ? [] : [{
|
|
254
|
+
name: tool.name,
|
|
255
|
+
description: tool.summary,
|
|
256
|
+
inputSchema: listedSchema(tool.direct.inputSchema),
|
|
257
|
+
annotations: {
|
|
258
|
+
readOnlyHint: tool.access === "read",
|
|
259
|
+
destructiveHint: destructiveOf(tool),
|
|
260
|
+
openWorldHint: false
|
|
261
|
+
}
|
|
262
|
+
}]);
|
|
263
|
+
if (lazy.length === 0) return listed;
|
|
264
|
+
return [
|
|
265
|
+
...listed,
|
|
266
|
+
{
|
|
267
|
+
name: CAPABILITY_TOOL_NAMES.list,
|
|
268
|
+
description: `List tools not shown here (${lazy.map(({ name }) => name).join(", ")}).`,
|
|
269
|
+
inputSchema: listedSchema(LIST_SCHEMA),
|
|
270
|
+
annotations: READ_ONLY
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: CAPABILITY_TOOL_NAMES.describe,
|
|
274
|
+
description: "Parameters, guidance and an example for any tool, by id; detail: \"full\" for the whole schema.",
|
|
275
|
+
inputSchema: listedSchema(DESCRIBE_SCHEMA),
|
|
276
|
+
annotations: READ_ONLY
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: CAPABILITY_TOOL_NAMES.invoke,
|
|
280
|
+
description: "Run a tool by id with its arguments as `input`.",
|
|
281
|
+
inputSchema: listedSchema(INVOKE_SCHEMA),
|
|
282
|
+
annotations: {
|
|
283
|
+
readOnlyHint: false,
|
|
284
|
+
destructiveHint: lazy.some(destructiveOf),
|
|
285
|
+
openWorldHint: false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
];
|
|
289
|
+
};
|
|
290
|
+
const callTool = async (name, args, context) => {
|
|
291
|
+
switch (name) {
|
|
292
|
+
case CAPABILITY_TOOL_NAMES.list: return toCallResult(listCapabilities(args));
|
|
293
|
+
case CAPABILITY_TOOL_NAMES.describe: return toCallResult(describeCapability(args));
|
|
294
|
+
case CAPABILITY_TOOL_NAMES.invoke: return await invokeCapability(args, context);
|
|
295
|
+
default: {
|
|
296
|
+
const tool = byName.get(name);
|
|
297
|
+
if (tool !== void 0) return await run(tool, args, context);
|
|
298
|
+
return toCallResult(failure({
|
|
299
|
+
code: KIT_ERROR_CODES.unknownTool,
|
|
300
|
+
message: `Unknown tool "${name}".`,
|
|
301
|
+
hint: `${didYouMean(closestNames(name, names))}Call ${CAPABILITY_TOOL_NAMES.list} to browse the rest.`
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
return {
|
|
307
|
+
listTools,
|
|
308
|
+
callTool
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
//#endregion
|
|
312
|
+
export { CAPABILITY_TOOL_NAMES, createToolSurface };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//#region src/mcp-kit/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The shapes a tool surface is built from. Nothing here knows about a
|
|
4
|
+
* transport, a schema library, or the product serving the tools: a tool is a
|
|
5
|
+
* name, a JSON Schema, and an async function, and every answer is either a
|
|
6
|
+
* JSON payload or one error shape.
|
|
7
|
+
*/
|
|
8
|
+
/** A JSON Schema object, as plain JSON. */
|
|
9
|
+
type JsonSchema = {
|
|
10
|
+
readonly [keyword: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
/** Whether a call only reads or may change state. */
|
|
13
|
+
type ToolAccess = "read" | "write";
|
|
14
|
+
/** One problem with one input value. */
|
|
15
|
+
type ToolInputIssue = {
|
|
16
|
+
readonly path: string;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The one error shape every tool answers with. `code` is machine-readable,
|
|
21
|
+
* `hint` says what to do next, and `retryable` says whether the same call
|
|
22
|
+
* can succeed once the caller has done it (re-read, waited).
|
|
23
|
+
*/
|
|
24
|
+
type ToolError = {
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
readonly hint?: string;
|
|
28
|
+
readonly retryable: boolean;
|
|
29
|
+
readonly issues?: readonly ToolInputIssue[];
|
|
30
|
+
readonly details?: unknown;
|
|
31
|
+
};
|
|
32
|
+
type ToolOutcome = {
|
|
33
|
+
readonly ok: true;
|
|
34
|
+
readonly value: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
readonly ok: false;
|
|
37
|
+
readonly error: ToolError;
|
|
38
|
+
};
|
|
39
|
+
type ToolDefinition<Context> = {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
/** One short sentence. A direct tool sends it on every turn, so every word costs. */
|
|
42
|
+
readonly summary: string;
|
|
43
|
+
/** Longer guidance, sent only by `describe_capability` with `detail: "full"`. */
|
|
44
|
+
readonly guide?: string;
|
|
45
|
+
/** Short guidance for the default, compact `describe_capability`. */
|
|
46
|
+
readonly brief?: string;
|
|
47
|
+
/** One example call's arguments, shown by the compact `describe_capability`. */
|
|
48
|
+
readonly example?: unknown;
|
|
49
|
+
readonly access: ToolAccess;
|
|
50
|
+
/** Whether a call can destroy or replace state. Defaults to `access === "write"`. */
|
|
51
|
+
readonly destructive?: boolean;
|
|
52
|
+
/** Groups lazy tools for `list_capabilities`' `domain` filter. */
|
|
53
|
+
readonly domain?: string;
|
|
54
|
+
/** Every argument the tool accepts: what calls are checked against. */
|
|
55
|
+
readonly inputSchema: JsonSchema;
|
|
56
|
+
/**
|
|
57
|
+
* What `describe_capability` returns as the input schema when a leaner
|
|
58
|
+
* rendering of `inputSchema` serves better (see `compactSchema`).
|
|
59
|
+
*/
|
|
60
|
+
readonly describedSchema?: JsonSchema;
|
|
61
|
+
/**
|
|
62
|
+
* Present for a tool listed on every turn, with the compact schema it is
|
|
63
|
+
* listed with. A tool without it is reached through the capability tools.
|
|
64
|
+
*/
|
|
65
|
+
readonly direct?: {
|
|
66
|
+
readonly inputSchema: JsonSchema;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Properties read exactly as sent, never leniently: switches that widen
|
|
70
|
+
* what a call may do (overwrite, repack) must be JSON `true` to count.
|
|
71
|
+
*/
|
|
72
|
+
readonly exactProperties?: readonly string[];
|
|
73
|
+
readonly run: (args: Record<string, unknown>, context: Context) => Promise<ToolOutcome>;
|
|
74
|
+
};
|
|
75
|
+
/** A tool as `tools/list` advertises it. */
|
|
76
|
+
type ListedTool = {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object";
|
|
81
|
+
[keyword: string]: unknown;
|
|
82
|
+
};
|
|
83
|
+
annotations: {
|
|
84
|
+
readOnlyHint: boolean;
|
|
85
|
+
destructiveHint: boolean;
|
|
86
|
+
openWorldHint: boolean;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** A `tools/call` answer: a JSON payload or `{ error }`, as text. */
|
|
90
|
+
type ToolCallResult = {
|
|
91
|
+
content: {
|
|
92
|
+
type: "text";
|
|
93
|
+
text: string;
|
|
94
|
+
}[];
|
|
95
|
+
isError: boolean;
|
|
96
|
+
};
|
|
97
|
+
//#endregion
|
|
98
|
+
export { JsonSchema, ListedTool, ToolAccess, ToolCallResult, ToolDefinition, ToolError, ToolInputIssue, ToolOutcome };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/mcp.d.ts
CHANGED
|
@@ -13,8 +13,14 @@ type FolioMcpServerOptions = {
|
|
|
13
13
|
bounds?: FolioReadBounds;
|
|
14
14
|
now?: () => Date;
|
|
15
15
|
};
|
|
16
|
-
/** The tool's MCP input schema: its own arguments plus the file envelope. */
|
|
16
|
+
/** The tool's full MCP input schema: its own arguments plus the file envelope. */
|
|
17
17
|
declare const mcpInputSchema: (tool: FolioFileToolSpec) => JsonObjectSchema;
|
|
18
|
+
/**
|
|
19
|
+
* Blocks as lines, a table row on one line: `| [id] cell | [id] cell |`,
|
|
20
|
+
* a cell's paragraphs side by side. Empty cells hold no block and show none.
|
|
21
|
+
*/
|
|
22
|
+
declare const blockLines: (blocks: readonly unknown[]) => string;
|
|
23
|
+
/** What `tools/list` returns. */
|
|
18
24
|
declare const listMcpTools: () => Tool[];
|
|
19
25
|
/** A low-level MCP server exposing the file tools; connect it to a transport. */
|
|
20
26
|
declare const createFolioMcpServer: (options: FolioMcpServerOptions) => Server;
|
|
@@ -25,4 +31,4 @@ type ServeOptions = FolioMcpServerOptions & {
|
|
|
25
31
|
/** Serve the file tools over stdio until the client disconnects. */
|
|
26
32
|
declare const serveFolioMcp: ({ input, output, ...options }: ServeOptions) => Promise<void>;
|
|
27
33
|
//#endregion
|
|
28
|
-
export { FolioMcpServerOptions, MCP_READ_BOUNDS, createFolioMcpServer, listMcpTools, mcpInputSchema, serveFolioMcp };
|
|
34
|
+
export { FolioMcpServerOptions, MCP_READ_BOUNDS, blockLines, createFolioMcpServer, listMcpTools, mcpInputSchema, serveFolioMcp };
|