toolcraft 0.0.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 +91 -0
- package/dist/cli.compile-check.d.ts +1 -0
- package/dist/cli.compile-check.js +26 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +1312 -0
- package/dist/index.compile-check.d.ts +1 -0
- package/dist/index.compile-check.js +50 -0
- package/dist/index.d.ts +164 -0
- package/dist/index.js +366 -0
- package/dist/mcp.compile-check.d.ts +1 -0
- package/dist/mcp.compile-check.js +26 -0
- package/dist/mcp.d.ts +31 -0
- package/dist/mcp.js +354 -0
- package/dist/number-schema.d.ts +3 -0
- package/dist/number-schema.js +8 -0
- package/dist/renderer.d.ts +5 -0
- package/dist/renderer.js +148 -0
- package/dist/schema-scope.d.ts +4 -0
- package/dist/schema-scope.js +34 -0
- package/dist/sdk.compile-check.d.ts +1 -0
- package/dist/sdk.compile-check.js +79 -0
- package/dist/sdk.d.ts +63 -0
- package/dist/sdk.js +218 -0
- package/node_modules/@poe-code/design-system/dist/acp/components.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/acp/components.js +121 -0
- package/node_modules/@poe-code/design-system/dist/acp/index.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/acp/index.js +2 -0
- package/node_modules/@poe-code/design-system/dist/acp/writer.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/acp/writer.js +21 -0
- package/node_modules/@poe-code/design-system/dist/components/command-errors.d.ts +16 -0
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +22 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +27 -0
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/components/index.js +7 -0
- package/node_modules/@poe-code/design-system/dist/components/logger.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/components/logger.js +60 -0
- package/node_modules/@poe-code/design-system/dist/components/symbols.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/components/symbols.js +71 -0
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +74 -0
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +14 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +104 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +18 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +298 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.d.ts +25 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +189 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/border.d.ts +9 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/border.js +123 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.js +57 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +254 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.js +121 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.js +167 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/demo.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/demo.js +145 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/index.js +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +99 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/layout.d.ts +25 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/layout.js +79 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.js +7 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.js +68 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/store.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/store.js +51 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.d.ts +37 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +233 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +36 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.js +1 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +33 -0
- package/node_modules/@poe-code/design-system/dist/index.js +31 -0
- package/node_modules/@poe-code/design-system/dist/internal/output-format.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/internal/output-format.js +22 -0
- package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.js +3 -0
- package/node_modules/@poe-code/design-system/dist/internal/theme-detect.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/internal/theme-detect.js +49 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.d.ts +66 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.js +132 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +9 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +15 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.d.ts +18 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +101 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +39 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +16 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +74 -0
- package/node_modules/@poe-code/design-system/dist/prompts/theme.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/prompts/theme.js +12 -0
- package/node_modules/@poe-code/design-system/dist/static/index.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/static/index.js +2 -0
- package/node_modules/@poe-code/design-system/dist/static/menu.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/static/menu.js +36 -0
- package/node_modules/@poe-code/design-system/dist/static/spinner.d.ts +14 -0
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +46 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.d.ts +92 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.js +1 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.js +139 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.js +8 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.js +1495 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +412 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.js +1166 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.js +42 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +572 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.js +27 -0
- package/node_modules/@poe-code/design-system/dist/tokens/colors.d.ts +35 -0
- package/node_modules/@poe-code/design-system/dist/tokens/colors.js +34 -0
- package/node_modules/@poe-code/design-system/dist/tokens/index.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/tokens/index.js +4 -0
- package/node_modules/@poe-code/design-system/dist/tokens/spacing.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/tokens/spacing.js +6 -0
- package/node_modules/@poe-code/design-system/dist/tokens/typography.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/tokens/typography.js +8 -0
- package/node_modules/@poe-code/design-system/dist/tokens/widths.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/tokens/widths.js +5 -0
- package/node_modules/@poe-code/design-system/package.json +25 -0
- package/package.json +57 -0
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createServer, JSON_RPC_ERROR_CODES, ToolError, } from "tiny-stdio-mcp-server";
|
|
3
|
+
import { toJsonSchema } from "agent-kit-schema";
|
|
4
|
+
import { UserError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
|
|
5
|
+
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
6
|
+
import { filterSchemaForScope } from "./schema-scope.js";
|
|
7
|
+
const RESERVED_SERVICE_NAMES = new Set(["params", "secrets", "fetch", "fs", "env", "progress"]);
|
|
8
|
+
function normalizeRoots(roots) {
|
|
9
|
+
if (!Array.isArray(roots)) {
|
|
10
|
+
return roots;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
kind: "group",
|
|
14
|
+
name: "",
|
|
15
|
+
aliases: [],
|
|
16
|
+
secrets: {},
|
|
17
|
+
children: roots,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function splitWords(value) {
|
|
21
|
+
const words = [];
|
|
22
|
+
let current = "";
|
|
23
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
24
|
+
const char = value[index] ?? "";
|
|
25
|
+
const lower = char.toLowerCase();
|
|
26
|
+
const upper = char.toUpperCase();
|
|
27
|
+
const isSeparator = char === "-" || char === "_" || char === " " || char === ".";
|
|
28
|
+
if (isSeparator) {
|
|
29
|
+
if (current.length > 0) {
|
|
30
|
+
words.push(current.toLowerCase());
|
|
31
|
+
current = "";
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const isUppercase = char !== lower && char === upper;
|
|
36
|
+
const previous = value[index - 1];
|
|
37
|
+
const next = value[index + 1];
|
|
38
|
+
const previousIsLowercase = previous !== undefined && previous === previous.toLowerCase() && previous !== previous.toUpperCase();
|
|
39
|
+
const nextIsLowercase = next !== undefined && next === next.toLowerCase() && next !== next.toUpperCase();
|
|
40
|
+
if (isUppercase && current.length > 0 && (previousIsLowercase || nextIsLowercase)) {
|
|
41
|
+
words.push(current.toLowerCase());
|
|
42
|
+
current = char;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
current += char;
|
|
46
|
+
}
|
|
47
|
+
if (current.length > 0) {
|
|
48
|
+
words.push(current.toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
return words;
|
|
51
|
+
}
|
|
52
|
+
function formatSegment(segment, casing) {
|
|
53
|
+
const words = splitWords(segment);
|
|
54
|
+
if (casing === "snake") {
|
|
55
|
+
return words.join("_");
|
|
56
|
+
}
|
|
57
|
+
return words
|
|
58
|
+
.map((word, index) => index === 0 ? word : `${word[0]?.toUpperCase() ?? ""}${word.slice(1)}`)
|
|
59
|
+
.join("");
|
|
60
|
+
}
|
|
61
|
+
function unwrapOptional(schema) {
|
|
62
|
+
if (schema.kind === "optional") {
|
|
63
|
+
return unwrapOptional(schema.inner);
|
|
64
|
+
}
|
|
65
|
+
return schema;
|
|
66
|
+
}
|
|
67
|
+
function isOptional(schema) {
|
|
68
|
+
return schema.kind === "optional";
|
|
69
|
+
}
|
|
70
|
+
function isPlainObject(value) {
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
72
|
+
}
|
|
73
|
+
function createFs() {
|
|
74
|
+
return {
|
|
75
|
+
readFile: async (path, encoding = "utf8") => readFile(path, { encoding }),
|
|
76
|
+
writeFile: async (path, contents) => {
|
|
77
|
+
await writeFile(path, contents);
|
|
78
|
+
},
|
|
79
|
+
exists: async (path) => {
|
|
80
|
+
try {
|
|
81
|
+
await access(path);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function createEnv(values = process.env) {
|
|
91
|
+
return {
|
|
92
|
+
get(key) {
|
|
93
|
+
return values[key];
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function validateServices(services) {
|
|
98
|
+
for (const name of Object.keys(services)) {
|
|
99
|
+
if (RESERVED_SERVICE_NAMES.has(name)) {
|
|
100
|
+
throw new Error(`Service name "${name}" is reserved. Choose a different name.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function applySchemaCasing(schema, casing) {
|
|
105
|
+
if (schema.type !== "object" || schema.properties === undefined) {
|
|
106
|
+
if (schema.type === "array" && schema.items !== undefined) {
|
|
107
|
+
return {
|
|
108
|
+
...schema,
|
|
109
|
+
items: applySchemaCasing(schema.items, casing),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return schema;
|
|
113
|
+
}
|
|
114
|
+
const properties = Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => [
|
|
115
|
+
formatSegment(key, casing),
|
|
116
|
+
applySchemaCasing(value, casing),
|
|
117
|
+
]));
|
|
118
|
+
const required = schema.required?.map((key) => formatSegment(key, casing));
|
|
119
|
+
return {
|
|
120
|
+
...schema,
|
|
121
|
+
properties,
|
|
122
|
+
...(required === undefined ? {} : { required }),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function collectParamSummaries(schema, casing, path = [], inheritedOptional = false) {
|
|
126
|
+
const summaries = [];
|
|
127
|
+
for (const [key, rawChildSchema] of Object.entries(schema.shape)) {
|
|
128
|
+
const childSchema = unwrapOptional(rawChildSchema);
|
|
129
|
+
const nextPath = [...path, formatSegment(key, casing)];
|
|
130
|
+
const optional = inheritedOptional || isOptional(rawChildSchema);
|
|
131
|
+
if (childSchema.kind === "object") {
|
|
132
|
+
summaries.push(...collectParamSummaries(childSchema, casing, nextPath, optional));
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
summaries.push(`${nextPath.join(".")}${optional ? "" : " (required)"}`);
|
|
136
|
+
}
|
|
137
|
+
return summaries;
|
|
138
|
+
}
|
|
139
|
+
function buildToolDescription(description, params, casing) {
|
|
140
|
+
const summary = collectParamSummaries(params, casing);
|
|
141
|
+
const parameterSummary = summary.length === 0 ? "" : `Parameters: ${summary.join(", ")}.`;
|
|
142
|
+
if (description === undefined) {
|
|
143
|
+
return parameterSummary;
|
|
144
|
+
}
|
|
145
|
+
if (parameterSummary.length === 0) {
|
|
146
|
+
return description;
|
|
147
|
+
}
|
|
148
|
+
return `${description} ${parameterSummary}`;
|
|
149
|
+
}
|
|
150
|
+
function matchesAllowlist(toolName, allowlist) {
|
|
151
|
+
if (allowlist === undefined) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
const segments = toolName.split("__");
|
|
155
|
+
const candidates = segments.map((_segment, index) => segments.slice(0, index + 1).join("__"));
|
|
156
|
+
return candidates.some((candidate) => allowlist.includes(candidate));
|
|
157
|
+
}
|
|
158
|
+
function formatToolName(path) {
|
|
159
|
+
return path.map((segment) => formatSegment(segment, "snake")).join("__");
|
|
160
|
+
}
|
|
161
|
+
function enumerateTools(root, casing, allowlist) {
|
|
162
|
+
const tools = [];
|
|
163
|
+
function visit(node, path) {
|
|
164
|
+
if (node.kind === "command") {
|
|
165
|
+
if (!node.scope.includes("mcp")) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const name = formatToolName([...path, node.name]);
|
|
169
|
+
const params = filterSchemaForScope(node.params, "mcp");
|
|
170
|
+
if (!matchesAllowlist(name, allowlist)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (params === undefined || params.kind !== "object") {
|
|
174
|
+
throw new Error(`Bug: command "${name}" must define an object params schema for MCP.`);
|
|
175
|
+
}
|
|
176
|
+
tools.push({
|
|
177
|
+
command: node,
|
|
178
|
+
name,
|
|
179
|
+
description: buildToolDescription(node.description, params, casing),
|
|
180
|
+
inputSchema: applySchemaCasing(toJsonSchema(params), casing),
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const nextPath = [...path, node.name];
|
|
185
|
+
for (const child of node.children) {
|
|
186
|
+
visit(child, nextPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const rootPath = root.name.length === 0 ? [] : [root.name];
|
|
190
|
+
for (const child of root.children) {
|
|
191
|
+
visit(child, rootPath);
|
|
192
|
+
}
|
|
193
|
+
return tools;
|
|
194
|
+
}
|
|
195
|
+
function validateEnum(value, schema, label) {
|
|
196
|
+
if (!schema.values.includes(value)) {
|
|
197
|
+
throw new UserError(`Invalid value for "${label}". Expected one of: ${schema.values.map((candidate) => String(candidate)).join(", ")}.`);
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
function validateSchemaValue(schema, value, casing, label) {
|
|
202
|
+
const unwrappedSchema = unwrapOptional(schema);
|
|
203
|
+
if (value === null && unwrappedSchema.nullable === true) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
switch (unwrappedSchema.kind) {
|
|
207
|
+
case "string":
|
|
208
|
+
if (typeof value !== "string") {
|
|
209
|
+
throw new UserError(`Invalid value for "${label}". Expected a string.`);
|
|
210
|
+
}
|
|
211
|
+
return value;
|
|
212
|
+
case "number":
|
|
213
|
+
if (!isValidNumberSchemaValue(value, unwrappedSchema)) {
|
|
214
|
+
throw new UserError(`Invalid value for "${label}". Expected ${getExpectedNumberDescription(unwrappedSchema)}.`);
|
|
215
|
+
}
|
|
216
|
+
return value;
|
|
217
|
+
case "boolean":
|
|
218
|
+
if (typeof value !== "boolean") {
|
|
219
|
+
throw new UserError(`Invalid value for "${label}". Expected a boolean.`);
|
|
220
|
+
}
|
|
221
|
+
return value;
|
|
222
|
+
case "enum":
|
|
223
|
+
return validateEnum(value, unwrappedSchema, label);
|
|
224
|
+
case "array":
|
|
225
|
+
if (!Array.isArray(value)) {
|
|
226
|
+
throw new UserError(`Invalid value for "${label}". Expected an array.`);
|
|
227
|
+
}
|
|
228
|
+
return value.map((item, index) => validateSchemaValue(unwrappedSchema.item, item, casing, `${label}[${index}]`));
|
|
229
|
+
case "object":
|
|
230
|
+
return validateObjectSchema(unwrappedSchema, value, casing, label);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function validateObjectSchema(schema, value, casing, label) {
|
|
234
|
+
if (!isPlainObject(value)) {
|
|
235
|
+
throw new UserError(`Invalid value for "${label}". Expected an object.`);
|
|
236
|
+
}
|
|
237
|
+
const result = {};
|
|
238
|
+
const expectedKeys = new Map();
|
|
239
|
+
for (const [key, childSchema] of Object.entries(schema.shape)) {
|
|
240
|
+
expectedKeys.set(formatSegment(key, casing), [key, childSchema]);
|
|
241
|
+
}
|
|
242
|
+
for (const key of Object.keys(value)) {
|
|
243
|
+
if (!expectedKeys.has(key)) {
|
|
244
|
+
const fieldLabel = label.length === 0 ? key : `${label}.${key}`;
|
|
245
|
+
throw new UserError(`Unexpected parameter "${fieldLabel}".`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (const [inputKey, [outputKey, rawChildSchema]] of expectedKeys.entries()) {
|
|
249
|
+
const childSchema = unwrapOptional(rawChildSchema);
|
|
250
|
+
const hasValue = Object.prototype.hasOwnProperty.call(value, inputKey);
|
|
251
|
+
const fieldLabel = label.length === 0 ? inputKey : `${label}.${inputKey}`;
|
|
252
|
+
if (!hasValue) {
|
|
253
|
+
if (childSchema.default !== undefined) {
|
|
254
|
+
result[outputKey] = childSchema.default;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (isOptional(rawChildSchema)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
throw new UserError(`Missing required parameter "${fieldLabel}".`);
|
|
261
|
+
}
|
|
262
|
+
result[outputKey] = validateSchemaValue(rawChildSchema, value[inputKey], casing, fieldLabel);
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
function validateToolArguments(schema, argumentsValue, casing) {
|
|
267
|
+
return validateObjectSchema(schema, argumentsValue ?? {}, casing, "");
|
|
268
|
+
}
|
|
269
|
+
function isContentBlock(value) {
|
|
270
|
+
if (!isPlainObject(value) || typeof value.type !== "string") {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
return (value.type === "text" ||
|
|
274
|
+
value.type === "image" ||
|
|
275
|
+
value.type === "audio" ||
|
|
276
|
+
value.type === "resource");
|
|
277
|
+
}
|
|
278
|
+
function toToolContent(result) {
|
|
279
|
+
if (result === undefined) {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
if (Array.isArray(result)) {
|
|
283
|
+
return result.flatMap((item) => toToolContent(item));
|
|
284
|
+
}
|
|
285
|
+
if (typeof result === "string" ||
|
|
286
|
+
typeof result === "number" ||
|
|
287
|
+
typeof result === "boolean") {
|
|
288
|
+
return [{ type: "text", text: String(result) }];
|
|
289
|
+
}
|
|
290
|
+
if (result === null) {
|
|
291
|
+
return [{ type: "text", text: "null" }];
|
|
292
|
+
}
|
|
293
|
+
if (isContentBlock(result)) {
|
|
294
|
+
return [result];
|
|
295
|
+
}
|
|
296
|
+
return [{ type: "text", text: JSON.stringify(result) }];
|
|
297
|
+
}
|
|
298
|
+
function toToolError(error) {
|
|
299
|
+
if (error instanceof ToolError) {
|
|
300
|
+
return error;
|
|
301
|
+
}
|
|
302
|
+
if (error instanceof UserError) {
|
|
303
|
+
return new ToolError(JSON_RPC_ERROR_CODES.INVALID_PARAMS, error.message);
|
|
304
|
+
}
|
|
305
|
+
if (error instanceof Error) {
|
|
306
|
+
return new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, error.message);
|
|
307
|
+
}
|
|
308
|
+
return new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, String(error));
|
|
309
|
+
}
|
|
310
|
+
export function createMCPServer(roots, options) {
|
|
311
|
+
const root = normalizeRoots(roots);
|
|
312
|
+
const casing = options.casing ?? "snake";
|
|
313
|
+
const services = (options.services ?? {});
|
|
314
|
+
validateServices(services);
|
|
315
|
+
const tools = enumerateTools(root, casing, options.tools);
|
|
316
|
+
const server = createServer({ name: options.name, version: options.version });
|
|
317
|
+
for (const tool of tools) {
|
|
318
|
+
server.tool(tool.name, tool.description, tool.inputSchema, async (argumentsValue) => {
|
|
319
|
+
try {
|
|
320
|
+
const secrets = resolveCommandSecrets(tool.command);
|
|
321
|
+
const baseContext = {
|
|
322
|
+
...services,
|
|
323
|
+
secrets,
|
|
324
|
+
fetch: globalThis.fetch,
|
|
325
|
+
fs: createFs(),
|
|
326
|
+
env: createEnv(),
|
|
327
|
+
progress() {
|
|
328
|
+
return undefined;
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
await assertCommandRequirements(tool.command, { ...baseContext, params: undefined });
|
|
332
|
+
const params = validateToolArguments(tool.command.params, argumentsValue, casing);
|
|
333
|
+
const result = await tool.command.handler({
|
|
334
|
+
...baseContext,
|
|
335
|
+
params,
|
|
336
|
+
});
|
|
337
|
+
return toToolContent(result);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
throw toToolError(error);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
...server,
|
|
346
|
+
connect(transport) {
|
|
347
|
+
return server.connectSDK(transport);
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
export async function runMCP(roots, options) {
|
|
352
|
+
const server = createMCPServer(roots, options);
|
|
353
|
+
await server.listen();
|
|
354
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function isValidNumberSchemaValue(value, schema) {
|
|
2
|
+
return (typeof value === "number" &&
|
|
3
|
+
Number.isFinite(value) &&
|
|
4
|
+
(schema.jsonType !== "integer" || Number.isInteger(value)));
|
|
5
|
+
}
|
|
6
|
+
export function getExpectedNumberDescription(schema) {
|
|
7
|
+
return schema.jsonType === "integer" ? "an integer" : "a number";
|
|
8
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Command, RenderPrimitives } from "./index.js";
|
|
2
|
+
export type OutputMode = "rich" | "md" | "json";
|
|
3
|
+
type WriteFn = (chunk: string) => void;
|
|
4
|
+
export declare function renderResult(command: Command<any, any, any, any>, result: unknown, output: OutputMode, primitives: RenderPrimitives, write?: WriteFn): void;
|
|
5
|
+
export {};
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
function isObject(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function isArrayOfObjects(value) {
|
|
5
|
+
return Array.isArray(value) && value.every((entry) => isObject(entry));
|
|
6
|
+
}
|
|
7
|
+
function stringifyValue(value) {
|
|
8
|
+
if (value === undefined) {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === "string") {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
return stringifyJson(value);
|
|
15
|
+
}
|
|
16
|
+
function stringifyJson(value, spaces) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(value, (_key, currentValue) => (typeof currentValue === "bigint" ? currentValue.toString() : currentValue), spaces) ?? String(value);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return String(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function renderObjectTable(result, primitives) {
|
|
25
|
+
const rows = Object.entries(result).map(([key, value]) => ({
|
|
26
|
+
key,
|
|
27
|
+
value: stringifyValue(value),
|
|
28
|
+
}));
|
|
29
|
+
return primitives.renderTable({
|
|
30
|
+
theme: primitives.getTheme(),
|
|
31
|
+
columns: [
|
|
32
|
+
{
|
|
33
|
+
name: "key",
|
|
34
|
+
title: "Key",
|
|
35
|
+
alignment: "left",
|
|
36
|
+
maxLen: Math.max("Key".length, ...rows.map((row) => row.key.length)),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "value",
|
|
40
|
+
title: "Value",
|
|
41
|
+
alignment: "left",
|
|
42
|
+
maxLen: Math.max("Value".length, ...rows.map((row) => row.value.length)),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
rows,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function renderObjectMarkdown(result) {
|
|
49
|
+
return Object.entries(result)
|
|
50
|
+
.map(([key, value]) => `- ${key}: ${stringifyValue(value)}`)
|
|
51
|
+
.join("\n");
|
|
52
|
+
}
|
|
53
|
+
function getColumnNames(rows) {
|
|
54
|
+
const names = new Set();
|
|
55
|
+
for (const row of rows) {
|
|
56
|
+
for (const name of Object.keys(row)) {
|
|
57
|
+
names.add(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [...names];
|
|
61
|
+
}
|
|
62
|
+
function renderArrayTable(result, primitives) {
|
|
63
|
+
if (result.length === 0) {
|
|
64
|
+
return "[]";
|
|
65
|
+
}
|
|
66
|
+
const columnNames = getColumnNames(result);
|
|
67
|
+
return primitives.renderTable({
|
|
68
|
+
theme: primitives.getTheme(),
|
|
69
|
+
columns: columnNames.map((name) => ({
|
|
70
|
+
name,
|
|
71
|
+
title: name,
|
|
72
|
+
alignment: "left",
|
|
73
|
+
maxLen: Math.max(name.length, ...result.map((row) => (name in row ? stringifyValue(row[name]).length : 0))),
|
|
74
|
+
})),
|
|
75
|
+
rows: result.map((row) => Object.fromEntries(columnNames.map((name) => [name, name in row ? stringifyValue(row[name]) : ""]))),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function renderArrayMarkdown(result) {
|
|
79
|
+
if (result.length === 0) {
|
|
80
|
+
return "[]";
|
|
81
|
+
}
|
|
82
|
+
const columnNames = getColumnNames(result);
|
|
83
|
+
const header = `| ${columnNames.join(" | ")} |`;
|
|
84
|
+
const separator = `| ${columnNames.map(() => ":---").join(" | ")} |`;
|
|
85
|
+
const rows = result.map((row) => `| ${columnNames
|
|
86
|
+
.map((name) => (name in row ? stringifyValue(row[name]).replaceAll("|", "\\|") : ""))
|
|
87
|
+
.join(" | ")} |`);
|
|
88
|
+
return [header, separator, ...rows].join("\n");
|
|
89
|
+
}
|
|
90
|
+
function autoRender(result, output, primitives) {
|
|
91
|
+
if (result === null || result === undefined) {
|
|
92
|
+
if (output === "json") {
|
|
93
|
+
return stringifyJson({ ok: true }, 2);
|
|
94
|
+
}
|
|
95
|
+
return "Done.";
|
|
96
|
+
}
|
|
97
|
+
if (typeof result === "string") {
|
|
98
|
+
if (output === "json") {
|
|
99
|
+
return stringifyJson({ result }, 2);
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
if (isObject(result)) {
|
|
104
|
+
if (output === "rich") {
|
|
105
|
+
return renderObjectTable(result, primitives);
|
|
106
|
+
}
|
|
107
|
+
if (output === "md") {
|
|
108
|
+
return renderObjectMarkdown(result);
|
|
109
|
+
}
|
|
110
|
+
return stringifyJson(result, 2);
|
|
111
|
+
}
|
|
112
|
+
if (isArrayOfObjects(result)) {
|
|
113
|
+
if (output === "md") {
|
|
114
|
+
return renderArrayMarkdown(result);
|
|
115
|
+
}
|
|
116
|
+
if (output === "json") {
|
|
117
|
+
return stringifyJson(result, 2);
|
|
118
|
+
}
|
|
119
|
+
return renderArrayTable(result, primitives);
|
|
120
|
+
}
|
|
121
|
+
return stringifyJson(result, 2);
|
|
122
|
+
}
|
|
123
|
+
export function renderResult(command, result, output, primitives, write = (chunk) => {
|
|
124
|
+
process.stdout.write(chunk);
|
|
125
|
+
}) {
|
|
126
|
+
if (output === "json" && command.render?.json) {
|
|
127
|
+
const payload = command.render.json(result, primitives);
|
|
128
|
+
if (payload !== undefined) {
|
|
129
|
+
write(`${stringifyJson(payload, 2)}\n`);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (output === "md" && command.render?.markdown) {
|
|
134
|
+
const payload = command.render.markdown(result, primitives);
|
|
135
|
+
if (typeof payload === "string" && payload.length > 0) {
|
|
136
|
+
write(`${payload}\n`);
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (output === "rich" && command.render?.rich) {
|
|
141
|
+
command.render.rich(result, primitives);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const payload = autoRender(result, output, primitives);
|
|
145
|
+
if (payload.length > 0) {
|
|
146
|
+
write(`${payload}\n`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function filterSchemaForScope(schema, scope) {
|
|
2
|
+
if (schema.scope !== undefined && !schema.scope.includes(scope)) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
switch (schema.kind) {
|
|
6
|
+
case "optional": {
|
|
7
|
+
const inner = filterSchemaForScope(schema.inner, scope);
|
|
8
|
+
if (inner === undefined) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
if (inner.requiredScopes?.includes(scope)) {
|
|
12
|
+
return inner;
|
|
13
|
+
}
|
|
14
|
+
return { ...schema, inner };
|
|
15
|
+
}
|
|
16
|
+
case "array": {
|
|
17
|
+
const item = filterSchemaForScope(schema.item, scope);
|
|
18
|
+
return item === undefined ? undefined : { ...schema, item };
|
|
19
|
+
}
|
|
20
|
+
case "string":
|
|
21
|
+
case "number":
|
|
22
|
+
case "boolean":
|
|
23
|
+
case "enum":
|
|
24
|
+
return schema;
|
|
25
|
+
case "object":
|
|
26
|
+
return {
|
|
27
|
+
...schema,
|
|
28
|
+
shape: Object.fromEntries(Object.entries(schema.shape).flatMap(([key, childSchema]) => {
|
|
29
|
+
const filtered = filterSchemaForScope(childSchema, scope);
|
|
30
|
+
return filtered === undefined ? [] : [[key, filtered]];
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { S } from "agent-kit-schema";
|
|
2
|
+
import { defineCommand, defineGroup } from "./index.js";
|
|
3
|
+
import { createSDK } from "./sdk.js";
|
|
4
|
+
const ignoredRoot = defineGroup({
|
|
5
|
+
name: "root",
|
|
6
|
+
children: [
|
|
7
|
+
defineGroup({
|
|
8
|
+
name: "poe-code",
|
|
9
|
+
children: [
|
|
10
|
+
defineGroup({
|
|
11
|
+
name: "generate",
|
|
12
|
+
children: [
|
|
13
|
+
defineCommand({
|
|
14
|
+
name: "text",
|
|
15
|
+
scope: ["sdk"],
|
|
16
|
+
params: S.Object({
|
|
17
|
+
prompt_text: S.String(),
|
|
18
|
+
max_tokens: S.Optional(S.Number()),
|
|
19
|
+
}),
|
|
20
|
+
handler: async ({ params }) => ({
|
|
21
|
+
model: "demo",
|
|
22
|
+
content: params.prompt_text,
|
|
23
|
+
maxTokens: params.max_tokens ?? 0,
|
|
24
|
+
}),
|
|
25
|
+
}),
|
|
26
|
+
defineCommand({
|
|
27
|
+
name: "HTTPServer",
|
|
28
|
+
scope: ["sdk"],
|
|
29
|
+
params: S.Object({
|
|
30
|
+
APIKey: S.String(),
|
|
31
|
+
}),
|
|
32
|
+
handler: async ({ params }) => ({
|
|
33
|
+
apiKey: params.APIKey,
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
defineCommand({
|
|
37
|
+
name: "cli-only",
|
|
38
|
+
scope: ["cli"],
|
|
39
|
+
params: S.Object({}),
|
|
40
|
+
handler: async () => "hidden",
|
|
41
|
+
}),
|
|
42
|
+
],
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
const ignoredOptions = {
|
|
49
|
+
casing: "camel",
|
|
50
|
+
services: {
|
|
51
|
+
logger: console,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const ignoredSdk = createSDK(ignoredRoot, ignoredOptions);
|
|
55
|
+
const ignoredResult = ignoredSdk.poeCode.generate.text({
|
|
56
|
+
promptText: "hello",
|
|
57
|
+
maxTokens: 128,
|
|
58
|
+
});
|
|
59
|
+
void ignoredResult.then((value) => {
|
|
60
|
+
void value.model;
|
|
61
|
+
void value.content;
|
|
62
|
+
void value.maxTokens;
|
|
63
|
+
});
|
|
64
|
+
const ignoredHttpServerResult = ignoredSdk.poeCode.generate.httpServer({
|
|
65
|
+
apiKey: "secret",
|
|
66
|
+
});
|
|
67
|
+
void ignoredHttpServerResult.then((value) => {
|
|
68
|
+
void value.apiKey;
|
|
69
|
+
});
|
|
70
|
+
// @ts-expect-error cli-only commands are not exposed in the SDK surface
|
|
71
|
+
void ignoredSdk.poeCode.generate.cliOnly;
|
|
72
|
+
// @ts-expect-error wrong parameter name
|
|
73
|
+
ignoredSdk.poeCode.generate.text({ prompt_text: "hello" });
|
|
74
|
+
// @ts-expect-error wrong parameter type
|
|
75
|
+
ignoredSdk.poeCode.generate.text({ promptText: 123 });
|
|
76
|
+
// @ts-expect-error acronym command names should still camel-case cleanly
|
|
77
|
+
void ignoredSdk.poeCode.generate.hTTPServer;
|
|
78
|
+
// @ts-expect-error acronym parameter names should camel-case cleanly
|
|
79
|
+
ignoredSdk.poeCode.generate.httpServer({ aPIKey: "secret" });
|