@pixerate/editor 0.261.0 → 0.262.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/dist/agent/index.cjs +19 -3
- package/dist/agent/index.js +21 -5
- package/dist/bridge/cli.cjs +4 -3
- package/dist/bridge/cli.js +3 -2
- package/dist/bridge/index.cjs +4 -3
- package/dist/bridge/index.js +3 -2
- package/dist/{chunk-KBNCEZZB.cjs → chunk-5NRD76T7.cjs} +9 -7
- package/dist/{chunk-SPPDMY7T.js → chunk-6LWEL5DO.js} +4 -2
- package/dist/{chunk-RTPBM46F.cjs → chunk-7TD63DZP.cjs} +1 -294
- package/dist/{chunk-ABHOYBZD.js → chunk-K4BGRCSH.js} +1 -294
- package/dist/chunk-SYEOA5MS.js +301 -0
- package/dist/{chunk-5DLCOQOQ.cjs → chunk-YBC3RKT2.cjs} +177 -16
- package/dist/chunk-ZEKI6EPI.cjs +301 -0
- package/dist/{chunk-UCYW4M66.js → chunk-ZQI3WUPP.js} +163 -2
- package/dist/paint-editor/contract.d.cts +11 -0
- package/dist/paint-editor/contract.d.ts +11 -0
- package/dist/paint-editor/index.cjs +23 -1
- package/dist/paint-editor/index.d.cts +1 -1
- package/dist/paint-editor/index.d.ts +1 -1
- package/dist/paint-editor/index.js +23 -1
- package/dist/tools/conformance.d.cts +138 -0
- package/dist/tools/conformance.d.ts +138 -0
- package/dist/tools/index.cjs +19 -3
- package/dist/tools/index.d.cts +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +21 -5
- package/package.json +1 -1
|
@@ -921,292 +921,6 @@ function searchTools(tools, toolsets, query, options = {}) {
|
|
|
921
921
|
return results.slice(0, options.limit ?? 10);
|
|
922
922
|
}
|
|
923
923
|
|
|
924
|
-
// src/tools/mcp-server.ts
|
|
925
|
-
var MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
926
|
-
var JSON_RPC_ERRORS = {
|
|
927
|
-
parseError: -32700,
|
|
928
|
-
invalidRequest: -32600,
|
|
929
|
-
methodNotFound: -32601,
|
|
930
|
-
invalidParams: -32602,
|
|
931
|
-
internalError: -32603
|
|
932
|
-
};
|
|
933
|
-
var errorResponse = (id, code, message, data) => ({
|
|
934
|
-
jsonrpc: "2.0",
|
|
935
|
-
id,
|
|
936
|
-
error: { code, message, ...data !== void 0 ? { data } : {} }
|
|
937
|
-
});
|
|
938
|
-
function toolsetInstructions(source) {
|
|
939
|
-
const sets = source.toolsets();
|
|
940
|
-
const discovery = source.hasTool("list_toolsets") && source.hasTool("search_tools");
|
|
941
|
-
const lines = [
|
|
942
|
-
"Tools are grouped into toolsets, one per open editor or feature, and named `<toolset>__<tool>`." + (discovery ? " Only some are listed at first: call list_toolsets to see them all, search_tools to find a tool for a task, and enable_tools to add what you need." : "")
|
|
943
|
-
];
|
|
944
|
-
if (sets.length > 0) {
|
|
945
|
-
lines.push("", "Toolsets:");
|
|
946
|
-
for (const set of sets) {
|
|
947
|
-
const about = [set.title, summarize(set.description)].filter(Boolean).join(": ");
|
|
948
|
-
const skill = set.skill ? ` (skill: ${set.skill})` : "";
|
|
949
|
-
lines.push(`- ${set.name}${about ? ` - ${about}` : ""}${skill}, ${set.tools.length} tools`);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
if (sets.some((s) => s.skill)) {
|
|
953
|
-
lines.push(
|
|
954
|
-
"",
|
|
955
|
-
"Read the skill a toolset names before using it" + (source.hasTool("get_skill") ? " (get_skill)." : " (an MCP resource or prompt when the server offers skills).")
|
|
956
|
-
);
|
|
957
|
-
}
|
|
958
|
-
return lines.join("\n");
|
|
959
|
-
}
|
|
960
|
-
var skillUri = (path) => `skill://${path}`;
|
|
961
|
-
function createMcpServer(registry, options) {
|
|
962
|
-
const skillFiles = options.skills ?? null;
|
|
963
|
-
const skills = skillFiles ? listSkills(skillFiles) : [];
|
|
964
|
-
const capabilities = {
|
|
965
|
-
...registry.capabilities,
|
|
966
|
-
...skills.length > 0 ? { resources: {}, prompts: {} } : {}
|
|
967
|
-
};
|
|
968
|
-
const readSkillPath = (path) => skillFiles?.[path] ?? skillFiles?.[`./${path}`];
|
|
969
|
-
let initialized = false;
|
|
970
|
-
let actor;
|
|
971
|
-
let unsubscribe = null;
|
|
972
|
-
const running = /* @__PURE__ */ new Map();
|
|
973
|
-
const startNotifications = () => {
|
|
974
|
-
if (unsubscribe || !options.send) return;
|
|
975
|
-
const send = options.send;
|
|
976
|
-
unsubscribe = registry.onListChanged(() => send({ jsonrpc: "2.0", method: "notifications/tools/list_changed" }));
|
|
977
|
-
};
|
|
978
|
-
async function dispatch(request) {
|
|
979
|
-
const id = request.id ?? null;
|
|
980
|
-
const isNotification = request.id === void 0 || request.id === null;
|
|
981
|
-
const params = request.params ?? {};
|
|
982
|
-
switch (request.method) {
|
|
983
|
-
case "initialize": {
|
|
984
|
-
const client = params.clientInfo;
|
|
985
|
-
if (client && typeof client.name === "string" && client.name) {
|
|
986
|
-
actor = { id: `mcp:${client.name}`, name: typeof client.title === "string" && client.title ? client.title : client.name };
|
|
987
|
-
}
|
|
988
|
-
const instructions = typeof options.instructions === "function" ? options.instructions(registry) : options.instructions;
|
|
989
|
-
const requested = String(params.protocolVersion ?? "");
|
|
990
|
-
const protocolVersion = MCP_PROTOCOL_VERSIONS.includes(requested) ? requested : MCP_PROTOCOL_VERSIONS[0];
|
|
991
|
-
return {
|
|
992
|
-
jsonrpc: "2.0",
|
|
993
|
-
id,
|
|
994
|
-
result: {
|
|
995
|
-
protocolVersion,
|
|
996
|
-
capabilities,
|
|
997
|
-
serverInfo: { name: options.name, version: options.version, ...options.title ? { title: options.title } : {} },
|
|
998
|
-
...instructions ? { instructions } : {}
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
case "notifications/initialized":
|
|
1003
|
-
initialized = true;
|
|
1004
|
-
startNotifications();
|
|
1005
|
-
return null;
|
|
1006
|
-
case "notifications/cancelled": {
|
|
1007
|
-
const requestId = params.requestId;
|
|
1008
|
-
if (requestId !== void 0) running.get(requestId)?.abort(params.reason);
|
|
1009
|
-
return null;
|
|
1010
|
-
}
|
|
1011
|
-
case "ping":
|
|
1012
|
-
return { jsonrpc: "2.0", id, result: {} };
|
|
1013
|
-
case "tools/list":
|
|
1014
|
-
return { jsonrpc: "2.0", id, result: registry.listTools() };
|
|
1015
|
-
case "tools/call": {
|
|
1016
|
-
const name = params.name;
|
|
1017
|
-
if (typeof name !== "string") {
|
|
1018
|
-
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, 'tools/call needs a string "name".');
|
|
1019
|
-
}
|
|
1020
|
-
if (!registry.hasTool(name)) {
|
|
1021
|
-
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown tool: ${name}`);
|
|
1022
|
-
}
|
|
1023
|
-
const args = params.arguments;
|
|
1024
|
-
if (args !== void 0 && (typeof args !== "object" || args === null || Array.isArray(args))) {
|
|
1025
|
-
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, '"arguments" must be an object.');
|
|
1026
|
-
}
|
|
1027
|
-
const controller = new AbortController();
|
|
1028
|
-
if (!isNotification) running.set(id, controller);
|
|
1029
|
-
try {
|
|
1030
|
-
const result = await registry.callTool(name, args ?? {}, { signal: controller.signal, ...actor ? { actor } : {} });
|
|
1031
|
-
if (controller.signal.aborted) return null;
|
|
1032
|
-
return { jsonrpc: "2.0", id, result };
|
|
1033
|
-
} finally {
|
|
1034
|
-
if (!isNotification) running.delete(id);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
case "resources/list":
|
|
1038
|
-
case "prompts/list":
|
|
1039
|
-
case "resources/read":
|
|
1040
|
-
case "prompts/get": {
|
|
1041
|
-
if (skills.length === 0) break;
|
|
1042
|
-
if (request.method === "resources/list") {
|
|
1043
|
-
const resources = skills.flatMap(
|
|
1044
|
-
(skill2) => ["SKILL.md", ...skill2.references].map((file) => ({
|
|
1045
|
-
uri: skillUri(`${skill2.name}/${file}`),
|
|
1046
|
-
name: file === "SKILL.md" ? skill2.name : `${skill2.name}/${file}`,
|
|
1047
|
-
...file === "SKILL.md" ? { description: skill2.description } : {},
|
|
1048
|
-
mimeType: "text/markdown"
|
|
1049
|
-
}))
|
|
1050
|
-
);
|
|
1051
|
-
return { jsonrpc: "2.0", id, result: { resources } };
|
|
1052
|
-
}
|
|
1053
|
-
if (request.method === "resources/read") {
|
|
1054
|
-
const uri = String(params.uri ?? "");
|
|
1055
|
-
const text2 = uri.startsWith("skill://") ? readSkillPath(uri.slice("skill://".length)) : void 0;
|
|
1056
|
-
if (text2 === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown resource: ${uri}`, { uri });
|
|
1057
|
-
return { jsonrpc: "2.0", id, result: { contents: [{ uri, mimeType: "text/markdown", text: text2 }] } };
|
|
1058
|
-
}
|
|
1059
|
-
if (request.method === "prompts/list") {
|
|
1060
|
-
return { jsonrpc: "2.0", id, result: { prompts: skills.map((s) => ({ name: s.name, description: s.description })) } };
|
|
1061
|
-
}
|
|
1062
|
-
const skill = skills.find((s) => s.name === params.name);
|
|
1063
|
-
const text = skill ? readSkillPath(`${skill.name}/SKILL.md`) : void 0;
|
|
1064
|
-
if (!skill || text === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown prompt: ${String(params.name)}`);
|
|
1065
|
-
return {
|
|
1066
|
-
jsonrpc: "2.0",
|
|
1067
|
-
id,
|
|
1068
|
-
result: {
|
|
1069
|
-
description: skill.description,
|
|
1070
|
-
messages: [{ role: "user", content: { type: "text", text: parseSkillFrontmatter(text).body.trim() } }]
|
|
1071
|
-
}
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
if (isNotification) return null;
|
|
1076
|
-
return errorResponse(id, JSON_RPC_ERRORS.methodNotFound, `Method not found: ${request.method}`);
|
|
1077
|
-
}
|
|
1078
|
-
return {
|
|
1079
|
-
get initialized() {
|
|
1080
|
-
return initialized;
|
|
1081
|
-
},
|
|
1082
|
-
async handle(message) {
|
|
1083
|
-
let request;
|
|
1084
|
-
if (typeof message === "string") {
|
|
1085
|
-
try {
|
|
1086
|
-
request = JSON.parse(message);
|
|
1087
|
-
} catch {
|
|
1088
|
-
return errorResponse(null, JSON_RPC_ERRORS.parseError, "Parse error.");
|
|
1089
|
-
}
|
|
1090
|
-
} else {
|
|
1091
|
-
request = message;
|
|
1092
|
-
}
|
|
1093
|
-
if (!request || typeof request !== "object" || Array.isArray(request) || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
|
|
1094
|
-
const id = request && typeof request === "object" && !Array.isArray(request) ? request.id ?? null : null;
|
|
1095
|
-
return errorResponse(id, JSON_RPC_ERRORS.invalidRequest, "Invalid JSON-RPC 2.0 request.");
|
|
1096
|
-
}
|
|
1097
|
-
try {
|
|
1098
|
-
return await dispatch(request);
|
|
1099
|
-
} catch (error) {
|
|
1100
|
-
if (request.id === void 0 || request.id === null) return null;
|
|
1101
|
-
return errorResponse(
|
|
1102
|
-
request.id,
|
|
1103
|
-
JSON_RPC_ERRORS.internalError,
|
|
1104
|
-
error instanceof Error ? error.message : String(error)
|
|
1105
|
-
);
|
|
1106
|
-
}
|
|
1107
|
-
},
|
|
1108
|
-
close() {
|
|
1109
|
-
unsubscribe?.();
|
|
1110
|
-
unsubscribe = null;
|
|
1111
|
-
for (const controller of running.values()) controller.abort("Server closed.");
|
|
1112
|
-
running.clear();
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
// src/tools/bridge-client.ts
|
|
1118
|
-
var TOOL_BRIDGE_PROTOCOL = 1;
|
|
1119
|
-
var TOOL_BRIDGE_DEFAULT_PORT = 4477;
|
|
1120
|
-
function connectToolBridge(registry, options) {
|
|
1121
|
-
const Ctor = options.WebSocket ?? globalThis.WebSocket;
|
|
1122
|
-
if (!Ctor) throw new Error("No WebSocket implementation: pass options.WebSocket.");
|
|
1123
|
-
const base = options.url ?? `ws://127.0.0.1:${TOOL_BRIDGE_DEFAULT_PORT}`;
|
|
1124
|
-
const url = options.token ? `${base}${base.includes("?") ? "&" : "?"}token=${encodeURIComponent(options.token)}` : base;
|
|
1125
|
-
let status = "connecting";
|
|
1126
|
-
let editorId = null;
|
|
1127
|
-
let socket = null;
|
|
1128
|
-
let closed = false;
|
|
1129
|
-
let delay = 500;
|
|
1130
|
-
let retry = null;
|
|
1131
|
-
const running = /* @__PURE__ */ new Map();
|
|
1132
|
-
const setStatus = (next, detail) => {
|
|
1133
|
-
if (next === status && !detail) return;
|
|
1134
|
-
status = next;
|
|
1135
|
-
options.onStatus?.(next, detail);
|
|
1136
|
-
};
|
|
1137
|
-
const send = (message) => {
|
|
1138
|
-
if (socket && socket.readyState === 1) socket.send(JSON.stringify(message));
|
|
1139
|
-
};
|
|
1140
|
-
const unsubscribe = registry.onListChanged((tools) => send({ t: "tools", tools }));
|
|
1141
|
-
const open = () => {
|
|
1142
|
-
if (closed) return;
|
|
1143
|
-
setStatus("connecting");
|
|
1144
|
-
const ws = new Ctor(url);
|
|
1145
|
-
socket = ws;
|
|
1146
|
-
ws.addEventListener("open", () => {
|
|
1147
|
-
delay = 500;
|
|
1148
|
-
send({ t: "hello", v: TOOL_BRIDGE_PROTOCOL, name: options.name, ...options.kind ? { kind: options.kind } : {}, tools: registry.listTools().tools });
|
|
1149
|
-
});
|
|
1150
|
-
ws.addEventListener("message", (event) => {
|
|
1151
|
-
let message;
|
|
1152
|
-
try {
|
|
1153
|
-
message = JSON.parse(String(event.data));
|
|
1154
|
-
} catch {
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
if (message.t === "welcome") {
|
|
1158
|
-
editorId = message.id;
|
|
1159
|
-
setStatus("connected");
|
|
1160
|
-
} else if (message.t === "call") {
|
|
1161
|
-
const controller = new AbortController();
|
|
1162
|
-
running.set(message.id, controller);
|
|
1163
|
-
void registry.callTool(message.name, message.arguments ?? {}, { signal: controller.signal }).then((result) => {
|
|
1164
|
-
running.delete(message.id);
|
|
1165
|
-
if (!controller.signal.aborted) send({ t: "result", id: message.id, result });
|
|
1166
|
-
});
|
|
1167
|
-
} else if (message.t === "cancel") {
|
|
1168
|
-
running.get(message.id)?.abort(message.reason ?? "Cancelled by the agent.");
|
|
1169
|
-
running.delete(message.id);
|
|
1170
|
-
}
|
|
1171
|
-
});
|
|
1172
|
-
ws.addEventListener("close", (event) => {
|
|
1173
|
-
if (socket !== ws) return;
|
|
1174
|
-
socket = null;
|
|
1175
|
-
editorId = null;
|
|
1176
|
-
for (const controller of running.values()) controller.abort("The bridge disconnected.");
|
|
1177
|
-
running.clear();
|
|
1178
|
-
if (closed) return setStatus("closed");
|
|
1179
|
-
setStatus("disconnected", event.reason || void 0);
|
|
1180
|
-
if (options.reconnect === false) return;
|
|
1181
|
-
retry = setTimeout(open, delay);
|
|
1182
|
-
delay = Math.min(1e4, delay * 2);
|
|
1183
|
-
});
|
|
1184
|
-
ws.addEventListener("error", () => {
|
|
1185
|
-
});
|
|
1186
|
-
};
|
|
1187
|
-
open();
|
|
1188
|
-
return {
|
|
1189
|
-
get status() {
|
|
1190
|
-
return status;
|
|
1191
|
-
},
|
|
1192
|
-
get editorId() {
|
|
1193
|
-
return editorId;
|
|
1194
|
-
},
|
|
1195
|
-
close() {
|
|
1196
|
-
if (closed) return;
|
|
1197
|
-
closed = true;
|
|
1198
|
-
if (retry) clearTimeout(retry);
|
|
1199
|
-
unsubscribe();
|
|
1200
|
-
for (const controller of running.values()) controller.abort("The editor disconnected.");
|
|
1201
|
-
running.clear();
|
|
1202
|
-
const ws = socket;
|
|
1203
|
-
socket = null;
|
|
1204
|
-
ws?.close(1e3, "Editor closed.");
|
|
1205
|
-
setStatus("closed");
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
924
|
export {
|
|
1211
925
|
searchTokens,
|
|
1212
926
|
summarize,
|
|
@@ -1224,12 +938,5 @@ export {
|
|
|
1224
938
|
errorResult,
|
|
1225
939
|
toToolResult,
|
|
1226
940
|
ToolRegistry,
|
|
1227
|
-
createToolRegistry
|
|
1228
|
-
MCP_PROTOCOL_VERSIONS,
|
|
1229
|
-
JSON_RPC_ERRORS,
|
|
1230
|
-
toolsetInstructions,
|
|
1231
|
-
createMcpServer,
|
|
1232
|
-
TOOL_BRIDGE_PROTOCOL,
|
|
1233
|
-
TOOL_BRIDGE_DEFAULT_PORT,
|
|
1234
|
-
connectToolBridge
|
|
941
|
+
createToolRegistry
|
|
1235
942
|
};
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listSkills,
|
|
3
|
+
parseSkillFrontmatter,
|
|
4
|
+
summarize
|
|
5
|
+
} from "./chunk-K4BGRCSH.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/mcp-server.ts
|
|
8
|
+
var MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
9
|
+
var JSON_RPC_ERRORS = {
|
|
10
|
+
parseError: -32700,
|
|
11
|
+
invalidRequest: -32600,
|
|
12
|
+
methodNotFound: -32601,
|
|
13
|
+
invalidParams: -32602,
|
|
14
|
+
internalError: -32603
|
|
15
|
+
};
|
|
16
|
+
var errorResponse = (id, code, message, data) => ({
|
|
17
|
+
jsonrpc: "2.0",
|
|
18
|
+
id,
|
|
19
|
+
error: { code, message, ...data !== void 0 ? { data } : {} }
|
|
20
|
+
});
|
|
21
|
+
function toolsetInstructions(source) {
|
|
22
|
+
const sets = source.toolsets();
|
|
23
|
+
const discovery = source.hasTool("list_toolsets") && source.hasTool("search_tools");
|
|
24
|
+
const lines = [
|
|
25
|
+
"Tools are grouped into toolsets, one per open editor or feature, and named `<toolset>__<tool>`." + (discovery ? " Only some are listed at first: call list_toolsets to see them all, search_tools to find a tool for a task, and enable_tools to add what you need." : "")
|
|
26
|
+
];
|
|
27
|
+
if (sets.length > 0) {
|
|
28
|
+
lines.push("", "Toolsets:");
|
|
29
|
+
for (const set of sets) {
|
|
30
|
+
const about = [set.title, summarize(set.description)].filter(Boolean).join(": ");
|
|
31
|
+
const skill = set.skill ? ` (skill: ${set.skill})` : "";
|
|
32
|
+
lines.push(`- ${set.name}${about ? ` - ${about}` : ""}${skill}, ${set.tools.length} tools`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (sets.some((s) => s.skill)) {
|
|
36
|
+
lines.push(
|
|
37
|
+
"",
|
|
38
|
+
"Read the skill a toolset names before using it" + (source.hasTool("get_skill") ? " (get_skill)." : " (an MCP resource or prompt when the server offers skills).")
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
var skillUri = (path) => `skill://${path}`;
|
|
44
|
+
function createMcpServer(registry, options) {
|
|
45
|
+
const skillFiles = options.skills ?? null;
|
|
46
|
+
const skills = skillFiles ? listSkills(skillFiles) : [];
|
|
47
|
+
const capabilities = {
|
|
48
|
+
...registry.capabilities,
|
|
49
|
+
...skills.length > 0 ? { resources: {}, prompts: {} } : {}
|
|
50
|
+
};
|
|
51
|
+
const readSkillPath = (path) => skillFiles?.[path] ?? skillFiles?.[`./${path}`];
|
|
52
|
+
let initialized = false;
|
|
53
|
+
let actor;
|
|
54
|
+
let unsubscribe = null;
|
|
55
|
+
const running = /* @__PURE__ */ new Map();
|
|
56
|
+
const startNotifications = () => {
|
|
57
|
+
if (unsubscribe || !options.send) return;
|
|
58
|
+
const send = options.send;
|
|
59
|
+
unsubscribe = registry.onListChanged(() => send({ jsonrpc: "2.0", method: "notifications/tools/list_changed" }));
|
|
60
|
+
};
|
|
61
|
+
async function dispatch(request) {
|
|
62
|
+
const id = request.id ?? null;
|
|
63
|
+
const isNotification = request.id === void 0 || request.id === null;
|
|
64
|
+
const params = request.params ?? {};
|
|
65
|
+
switch (request.method) {
|
|
66
|
+
case "initialize": {
|
|
67
|
+
const client = params.clientInfo;
|
|
68
|
+
if (client && typeof client.name === "string" && client.name) {
|
|
69
|
+
actor = { id: `mcp:${client.name}`, name: typeof client.title === "string" && client.title ? client.title : client.name };
|
|
70
|
+
}
|
|
71
|
+
const instructions = typeof options.instructions === "function" ? options.instructions(registry) : options.instructions;
|
|
72
|
+
const requested = String(params.protocolVersion ?? "");
|
|
73
|
+
const protocolVersion = MCP_PROTOCOL_VERSIONS.includes(requested) ? requested : MCP_PROTOCOL_VERSIONS[0];
|
|
74
|
+
return {
|
|
75
|
+
jsonrpc: "2.0",
|
|
76
|
+
id,
|
|
77
|
+
result: {
|
|
78
|
+
protocolVersion,
|
|
79
|
+
capabilities,
|
|
80
|
+
serverInfo: { name: options.name, version: options.version, ...options.title ? { title: options.title } : {} },
|
|
81
|
+
...instructions ? { instructions } : {}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
case "notifications/initialized":
|
|
86
|
+
initialized = true;
|
|
87
|
+
startNotifications();
|
|
88
|
+
return null;
|
|
89
|
+
case "notifications/cancelled": {
|
|
90
|
+
const requestId = params.requestId;
|
|
91
|
+
if (requestId !== void 0) running.get(requestId)?.abort(params.reason);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
case "ping":
|
|
95
|
+
return { jsonrpc: "2.0", id, result: {} };
|
|
96
|
+
case "tools/list":
|
|
97
|
+
return { jsonrpc: "2.0", id, result: registry.listTools() };
|
|
98
|
+
case "tools/call": {
|
|
99
|
+
const name = params.name;
|
|
100
|
+
if (typeof name !== "string") {
|
|
101
|
+
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, 'tools/call needs a string "name".');
|
|
102
|
+
}
|
|
103
|
+
if (!registry.hasTool(name)) {
|
|
104
|
+
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown tool: ${name}`);
|
|
105
|
+
}
|
|
106
|
+
const args = params.arguments;
|
|
107
|
+
if (args !== void 0 && (typeof args !== "object" || args === null || Array.isArray(args))) {
|
|
108
|
+
return errorResponse(id, JSON_RPC_ERRORS.invalidParams, '"arguments" must be an object.');
|
|
109
|
+
}
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
if (!isNotification) running.set(id, controller);
|
|
112
|
+
try {
|
|
113
|
+
const result = await registry.callTool(name, args ?? {}, { signal: controller.signal, ...actor ? { actor } : {} });
|
|
114
|
+
if (controller.signal.aborted) return null;
|
|
115
|
+
return { jsonrpc: "2.0", id, result };
|
|
116
|
+
} finally {
|
|
117
|
+
if (!isNotification) running.delete(id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
case "resources/list":
|
|
121
|
+
case "prompts/list":
|
|
122
|
+
case "resources/read":
|
|
123
|
+
case "prompts/get": {
|
|
124
|
+
if (skills.length === 0) break;
|
|
125
|
+
if (request.method === "resources/list") {
|
|
126
|
+
const resources = skills.flatMap(
|
|
127
|
+
(skill2) => ["SKILL.md", ...skill2.references].map((file) => ({
|
|
128
|
+
uri: skillUri(`${skill2.name}/${file}`),
|
|
129
|
+
name: file === "SKILL.md" ? skill2.name : `${skill2.name}/${file}`,
|
|
130
|
+
...file === "SKILL.md" ? { description: skill2.description } : {},
|
|
131
|
+
mimeType: "text/markdown"
|
|
132
|
+
}))
|
|
133
|
+
);
|
|
134
|
+
return { jsonrpc: "2.0", id, result: { resources } };
|
|
135
|
+
}
|
|
136
|
+
if (request.method === "resources/read") {
|
|
137
|
+
const uri = String(params.uri ?? "");
|
|
138
|
+
const text2 = uri.startsWith("skill://") ? readSkillPath(uri.slice("skill://".length)) : void 0;
|
|
139
|
+
if (text2 === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown resource: ${uri}`, { uri });
|
|
140
|
+
return { jsonrpc: "2.0", id, result: { contents: [{ uri, mimeType: "text/markdown", text: text2 }] } };
|
|
141
|
+
}
|
|
142
|
+
if (request.method === "prompts/list") {
|
|
143
|
+
return { jsonrpc: "2.0", id, result: { prompts: skills.map((s) => ({ name: s.name, description: s.description })) } };
|
|
144
|
+
}
|
|
145
|
+
const skill = skills.find((s) => s.name === params.name);
|
|
146
|
+
const text = skill ? readSkillPath(`${skill.name}/SKILL.md`) : void 0;
|
|
147
|
+
if (!skill || text === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown prompt: ${String(params.name)}`);
|
|
148
|
+
return {
|
|
149
|
+
jsonrpc: "2.0",
|
|
150
|
+
id,
|
|
151
|
+
result: {
|
|
152
|
+
description: skill.description,
|
|
153
|
+
messages: [{ role: "user", content: { type: "text", text: parseSkillFrontmatter(text).body.trim() } }]
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (isNotification) return null;
|
|
159
|
+
return errorResponse(id, JSON_RPC_ERRORS.methodNotFound, `Method not found: ${request.method}`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
get initialized() {
|
|
163
|
+
return initialized;
|
|
164
|
+
},
|
|
165
|
+
async handle(message) {
|
|
166
|
+
let request;
|
|
167
|
+
if (typeof message === "string") {
|
|
168
|
+
try {
|
|
169
|
+
request = JSON.parse(message);
|
|
170
|
+
} catch {
|
|
171
|
+
return errorResponse(null, JSON_RPC_ERRORS.parseError, "Parse error.");
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
request = message;
|
|
175
|
+
}
|
|
176
|
+
if (!request || typeof request !== "object" || Array.isArray(request) || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
|
|
177
|
+
const id = request && typeof request === "object" && !Array.isArray(request) ? request.id ?? null : null;
|
|
178
|
+
return errorResponse(id, JSON_RPC_ERRORS.invalidRequest, "Invalid JSON-RPC 2.0 request.");
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
return await dispatch(request);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (request.id === void 0 || request.id === null) return null;
|
|
184
|
+
return errorResponse(
|
|
185
|
+
request.id,
|
|
186
|
+
JSON_RPC_ERRORS.internalError,
|
|
187
|
+
error instanceof Error ? error.message : String(error)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
close() {
|
|
192
|
+
unsubscribe?.();
|
|
193
|
+
unsubscribe = null;
|
|
194
|
+
for (const controller of running.values()) controller.abort("Server closed.");
|
|
195
|
+
running.clear();
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/tools/bridge-client.ts
|
|
201
|
+
var TOOL_BRIDGE_PROTOCOL = 1;
|
|
202
|
+
var TOOL_BRIDGE_DEFAULT_PORT = 4477;
|
|
203
|
+
function connectToolBridge(registry, options) {
|
|
204
|
+
const Ctor = options.WebSocket ?? globalThis.WebSocket;
|
|
205
|
+
if (!Ctor) throw new Error("No WebSocket implementation: pass options.WebSocket.");
|
|
206
|
+
const base = options.url ?? `ws://127.0.0.1:${TOOL_BRIDGE_DEFAULT_PORT}`;
|
|
207
|
+
const url = options.token ? `${base}${base.includes("?") ? "&" : "?"}token=${encodeURIComponent(options.token)}` : base;
|
|
208
|
+
let status = "connecting";
|
|
209
|
+
let editorId = null;
|
|
210
|
+
let socket = null;
|
|
211
|
+
let closed = false;
|
|
212
|
+
let delay = 500;
|
|
213
|
+
let retry = null;
|
|
214
|
+
const running = /* @__PURE__ */ new Map();
|
|
215
|
+
const setStatus = (next, detail) => {
|
|
216
|
+
if (next === status && !detail) return;
|
|
217
|
+
status = next;
|
|
218
|
+
options.onStatus?.(next, detail);
|
|
219
|
+
};
|
|
220
|
+
const send = (message) => {
|
|
221
|
+
if (socket && socket.readyState === 1) socket.send(JSON.stringify(message));
|
|
222
|
+
};
|
|
223
|
+
const unsubscribe = registry.onListChanged((tools) => send({ t: "tools", tools }));
|
|
224
|
+
const open = () => {
|
|
225
|
+
if (closed) return;
|
|
226
|
+
setStatus("connecting");
|
|
227
|
+
const ws = new Ctor(url);
|
|
228
|
+
socket = ws;
|
|
229
|
+
ws.addEventListener("open", () => {
|
|
230
|
+
delay = 500;
|
|
231
|
+
send({ t: "hello", v: TOOL_BRIDGE_PROTOCOL, name: options.name, ...options.kind ? { kind: options.kind } : {}, tools: registry.listTools().tools });
|
|
232
|
+
});
|
|
233
|
+
ws.addEventListener("message", (event) => {
|
|
234
|
+
let message;
|
|
235
|
+
try {
|
|
236
|
+
message = JSON.parse(String(event.data));
|
|
237
|
+
} catch {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (message.t === "welcome") {
|
|
241
|
+
editorId = message.id;
|
|
242
|
+
setStatus("connected");
|
|
243
|
+
} else if (message.t === "call") {
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
running.set(message.id, controller);
|
|
246
|
+
void registry.callTool(message.name, message.arguments ?? {}, { signal: controller.signal }).then((result) => {
|
|
247
|
+
running.delete(message.id);
|
|
248
|
+
if (!controller.signal.aborted) send({ t: "result", id: message.id, result });
|
|
249
|
+
});
|
|
250
|
+
} else if (message.t === "cancel") {
|
|
251
|
+
running.get(message.id)?.abort(message.reason ?? "Cancelled by the agent.");
|
|
252
|
+
running.delete(message.id);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
ws.addEventListener("close", (event) => {
|
|
256
|
+
if (socket !== ws) return;
|
|
257
|
+
socket = null;
|
|
258
|
+
editorId = null;
|
|
259
|
+
for (const controller of running.values()) controller.abort("The bridge disconnected.");
|
|
260
|
+
running.clear();
|
|
261
|
+
if (closed) return setStatus("closed");
|
|
262
|
+
setStatus("disconnected", event.reason || void 0);
|
|
263
|
+
if (options.reconnect === false) return;
|
|
264
|
+
retry = setTimeout(open, delay);
|
|
265
|
+
delay = Math.min(1e4, delay * 2);
|
|
266
|
+
});
|
|
267
|
+
ws.addEventListener("error", () => {
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
open();
|
|
271
|
+
return {
|
|
272
|
+
get status() {
|
|
273
|
+
return status;
|
|
274
|
+
},
|
|
275
|
+
get editorId() {
|
|
276
|
+
return editorId;
|
|
277
|
+
},
|
|
278
|
+
close() {
|
|
279
|
+
if (closed) return;
|
|
280
|
+
closed = true;
|
|
281
|
+
if (retry) clearTimeout(retry);
|
|
282
|
+
unsubscribe();
|
|
283
|
+
for (const controller of running.values()) controller.abort("The editor disconnected.");
|
|
284
|
+
running.clear();
|
|
285
|
+
const ws = socket;
|
|
286
|
+
socket = null;
|
|
287
|
+
ws?.close(1e3, "Editor closed.");
|
|
288
|
+
setStatus("closed");
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
MCP_PROTOCOL_VERSIONS,
|
|
295
|
+
JSON_RPC_ERRORS,
|
|
296
|
+
toolsetInstructions,
|
|
297
|
+
createMcpServer,
|
|
298
|
+
TOOL_BRIDGE_PROTOCOL,
|
|
299
|
+
TOOL_BRIDGE_DEFAULT_PORT,
|
|
300
|
+
connectToolBridge
|
|
301
|
+
};
|