@dadaki/mcp 1.0.0-beta.1 → 1.0.0-beta.2
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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.js +41 -23
- package/package.json +5 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dadaki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -248,10 +248,25 @@ survive the feature being broken.
|
|
|
248
248
|
```bash
|
|
249
249
|
pnpm test # agent API unit tests
|
|
250
250
|
pnpm --filter @dadaki/mcp smoke # full MCP round-trip, every tool
|
|
251
|
+
pnpm --filter @dadaki/mcp smoke:bundle # the same, against the published bundle
|
|
251
252
|
pnpm --filter @dadaki/mcp smoke:bridge # bridge mode, incl. its security rules
|
|
252
253
|
pnpm --filter @dadaki/mcp smoke:modes <dir> # every mode; <dir> holds cert.pem/key.pem
|
|
254
|
+
pnpm --filter @dadaki/mcp smoke:offline # relay economics, against a stand-in backend
|
|
255
|
+
pnpm --filter @dadaki/mcp smoke:relay # relay against a real deployment
|
|
253
256
|
```
|
|
254
257
|
|
|
258
|
+
**`smoke:bundle` matters more than it looks.** What npm ships is not these
|
|
259
|
+
sources — it is one esbuild output with every dependency inlined and a
|
|
260
|
+
`createRequire` shim. A test that only runs `src/` cannot see a bundling
|
|
261
|
+
failure, and a bundling failure reaches users as a broken install. Every smoke
|
|
262
|
+
test takes `DADAKI_MCP_TEST_BUNDLE=1` and runs against `dist/index.js` instead.
|
|
263
|
+
|
|
264
|
+
**`smoke:offline` counts requests.** Against a real deployment you cannot tell
|
|
265
|
+
whether a tool call cost one HTTP round-trip or two, and that difference is the
|
|
266
|
+
whole latency of a drawing loop. A stand-in backend can also refuse a pairing
|
|
267
|
+
code or vanish entirely on demand, which is how the failure messages get tested
|
|
268
|
+
at all.
|
|
269
|
+
|
|
255
270
|
The unit tests cover the agent API against the real WASM engine. The smoke tests
|
|
256
271
|
cover what only exists assembled: the MCP handshake, each transport, CanvasKit
|
|
257
272
|
booting, and rendering.
|
package/dist/index.js
CHANGED
|
@@ -14501,7 +14501,7 @@ function cleanParams(params, data) {
|
|
|
14501
14501
|
const p2 = typeof p === "string" ? { message: p } : p;
|
|
14502
14502
|
return p2;
|
|
14503
14503
|
}
|
|
14504
|
-
function custom(check2, _params = {},
|
|
14504
|
+
function custom(check2, _params = {}, fatal2) {
|
|
14505
14505
|
if (check2)
|
|
14506
14506
|
return ZodAny.create().superRefine((data, ctx) => {
|
|
14507
14507
|
const r = check2(data);
|
|
@@ -14509,14 +14509,14 @@ function custom(check2, _params = {}, fatal) {
|
|
|
14509
14509
|
return r.then((r2) => {
|
|
14510
14510
|
if (!r2) {
|
|
14511
14511
|
const params = cleanParams(_params, data);
|
|
14512
|
-
const _fatal = params.fatal ??
|
|
14512
|
+
const _fatal = params.fatal ?? fatal2 ?? true;
|
|
14513
14513
|
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
14514
14514
|
}
|
|
14515
14515
|
});
|
|
14516
14516
|
}
|
|
14517
14517
|
if (!r) {
|
|
14518
14518
|
const params = cleanParams(_params, data);
|
|
14519
|
-
const _fatal = params.fatal ??
|
|
14519
|
+
const _fatal = params.fatal ?? fatal2 ?? true;
|
|
14520
14520
|
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
14521
14521
|
}
|
|
14522
14522
|
return;
|
|
@@ -15146,10 +15146,10 @@ function prefixIssues(path, issues) {
|
|
|
15146
15146
|
function unwrapMessage(message) {
|
|
15147
15147
|
return typeof message === "string" ? message : message?.message;
|
|
15148
15148
|
}
|
|
15149
|
-
function finalizeIssue(iss, ctx,
|
|
15149
|
+
function finalizeIssue(iss, ctx, config2) {
|
|
15150
15150
|
const full = { ...iss, path: iss.path ?? [] };
|
|
15151
15151
|
if (!iss.message) {
|
|
15152
|
-
const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(
|
|
15152
|
+
const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
|
|
15153
15153
|
full.message = message;
|
|
15154
15154
|
}
|
|
15155
15155
|
delete full.inst;
|
|
@@ -23894,13 +23894,13 @@ var ExperimentalMcpServerTasks = class {
|
|
|
23894
23894
|
constructor(_mcpServer) {
|
|
23895
23895
|
this._mcpServer = _mcpServer;
|
|
23896
23896
|
}
|
|
23897
|
-
registerToolTask(name,
|
|
23898
|
-
const execution = { taskSupport: "required", ...
|
|
23897
|
+
registerToolTask(name, config2, handler) {
|
|
23898
|
+
const execution = { taskSupport: "required", ...config2.execution };
|
|
23899
23899
|
if (execution.taskSupport === "forbidden") {
|
|
23900
23900
|
throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`);
|
|
23901
23901
|
}
|
|
23902
23902
|
const mcpServerInternal = this._mcpServer;
|
|
23903
|
-
return mcpServerInternal._createRegisteredTool(name,
|
|
23903
|
+
return mcpServerInternal._createRegisteredTool(name, config2.title, config2.description, config2.inputSchema, config2.outputSchema, config2.annotations, execution, config2._meta, handler);
|
|
23904
23904
|
}
|
|
23905
23905
|
};
|
|
23906
23906
|
|
|
@@ -24329,12 +24329,12 @@ var McpServer = class {
|
|
|
24329
24329
|
return registeredResourceTemplate;
|
|
24330
24330
|
}
|
|
24331
24331
|
}
|
|
24332
|
-
registerResource(name, uriOrTemplate,
|
|
24332
|
+
registerResource(name, uriOrTemplate, config2, readCallback) {
|
|
24333
24333
|
if (typeof uriOrTemplate === "string") {
|
|
24334
24334
|
if (this._registeredResources[uriOrTemplate]) {
|
|
24335
24335
|
throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
24336
24336
|
}
|
|
24337
|
-
const registeredResource = this._createRegisteredResource(name,
|
|
24337
|
+
const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, config2, readCallback);
|
|
24338
24338
|
this.setResourceRequestHandlers();
|
|
24339
24339
|
this.sendResourceListChanged();
|
|
24340
24340
|
return registeredResource;
|
|
@@ -24342,7 +24342,7 @@ var McpServer = class {
|
|
|
24342
24342
|
if (this._registeredResourceTemplates[name]) {
|
|
24343
24343
|
throw new Error(`Resource template ${name} is already registered`);
|
|
24344
24344
|
}
|
|
24345
|
-
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name,
|
|
24345
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, config2, readCallback);
|
|
24346
24346
|
this.setResourceRequestHandlers();
|
|
24347
24347
|
this.sendResourceListChanged();
|
|
24348
24348
|
return registeredResourceTemplate;
|
|
@@ -24540,11 +24540,11 @@ var McpServer = class {
|
|
|
24540
24540
|
/**
|
|
24541
24541
|
* Registers a tool with a config object and callback.
|
|
24542
24542
|
*/
|
|
24543
|
-
registerTool(name,
|
|
24543
|
+
registerTool(name, config2, cb) {
|
|
24544
24544
|
if (this._registeredTools[name]) {
|
|
24545
24545
|
throw new Error(`Tool ${name} is already registered`);
|
|
24546
24546
|
}
|
|
24547
|
-
const { title, description, inputSchema, outputSchema, annotations, _meta } =
|
|
24547
|
+
const { title, description, inputSchema, outputSchema, annotations, _meta } = config2;
|
|
24548
24548
|
return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
|
|
24549
24549
|
}
|
|
24550
24550
|
prompt(name, ...rest) {
|
|
@@ -24568,11 +24568,11 @@ var McpServer = class {
|
|
|
24568
24568
|
/**
|
|
24569
24569
|
* Registers a prompt with a config object and callback.
|
|
24570
24570
|
*/
|
|
24571
|
-
registerPrompt(name,
|
|
24571
|
+
registerPrompt(name, config2, cb) {
|
|
24572
24572
|
if (this._registeredPrompts[name]) {
|
|
24573
24573
|
throw new Error(`Prompt ${name} is already registered`);
|
|
24574
24574
|
}
|
|
24575
|
-
const { title, description, argsSchema } =
|
|
24575
|
+
const { title, description, argsSchema } = config2;
|
|
24576
24576
|
const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb);
|
|
24577
24577
|
this.setPromptRequestHandlers();
|
|
24578
24578
|
this.sendPromptListChanged();
|
|
@@ -24832,6 +24832,8 @@ var BridgeTransport = class {
|
|
|
24832
24832
|
opts;
|
|
24833
24833
|
/** Resolves when an editor attaches; replaced after each disconnect. */
|
|
24834
24834
|
attached;
|
|
24835
|
+
/** Has an editor ever held this bridge? Decides how patient `call` is. */
|
|
24836
|
+
seenEditor = false;
|
|
24835
24837
|
markAttached;
|
|
24836
24838
|
constructor(opts) {
|
|
24837
24839
|
this.opts = opts;
|
|
@@ -24890,6 +24892,7 @@ var BridgeTransport = class {
|
|
|
24890
24892
|
return;
|
|
24891
24893
|
}
|
|
24892
24894
|
this.socket = ws;
|
|
24895
|
+
this.seenEditor = true;
|
|
24893
24896
|
this.markAttached();
|
|
24894
24897
|
console.error("[dadaki-mcp] editor attached");
|
|
24895
24898
|
ws.on("message", (raw) => {
|
|
@@ -24924,7 +24927,7 @@ var BridgeTransport = class {
|
|
|
24924
24927
|
async call(method, args = []) {
|
|
24925
24928
|
await this.listen();
|
|
24926
24929
|
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
24927
|
-
const wait = this.opts.attachTimeoutMs ??
|
|
24930
|
+
const wait = this.opts.attachTimeoutMs ?? (this.seenEditor ? 45e3 : 4e3);
|
|
24928
24931
|
const timedOut = /* @__PURE__ */ Symbol("timeout");
|
|
24929
24932
|
let timer;
|
|
24930
24933
|
const race = await Promise.race([
|
|
@@ -25049,11 +25052,10 @@ var RelayTransport = class {
|
|
|
25049
25052
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
25050
25053
|
}
|
|
25051
25054
|
}
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
let res;
|
|
25055
|
+
/** One call attempt. Network failures are the transport's own error. */
|
|
25056
|
+
async post(method, args) {
|
|
25055
25057
|
try {
|
|
25056
|
-
|
|
25058
|
+
return await fetch(`${this.base}/call`, {
|
|
25057
25059
|
method: "POST",
|
|
25058
25060
|
headers: { "Content-Type": "application/json" },
|
|
25059
25061
|
body: JSON.stringify({ token: this.opts.token, method, args }),
|
|
@@ -25064,12 +25066,20 @@ var RelayTransport = class {
|
|
|
25064
25066
|
`could not reach the relay at ${this.base} (${err.message})`
|
|
25065
25067
|
);
|
|
25066
25068
|
}
|
|
25069
|
+
}
|
|
25070
|
+
async call(method, args = []) {
|
|
25071
|
+
let res = await this.post(method, args);
|
|
25072
|
+
if (res.status === 409) {
|
|
25073
|
+
await this.waitForEditor();
|
|
25074
|
+
res = await this.post(method, args);
|
|
25075
|
+
}
|
|
25067
25076
|
if (res.status === 409) {
|
|
25068
25077
|
throw new AgentCallError("no editor is attached to this session");
|
|
25069
25078
|
}
|
|
25070
25079
|
if (!res.ok) {
|
|
25071
25080
|
throw new AgentCallError(`relay returned ${res.status} for ${method}`);
|
|
25072
25081
|
}
|
|
25082
|
+
this.seenEditor = true;
|
|
25073
25083
|
return unwrap(await res.json());
|
|
25074
25084
|
}
|
|
25075
25085
|
async close() {
|
|
@@ -25201,8 +25211,13 @@ async function createTransport(cfg) {
|
|
|
25201
25211
|
}
|
|
25202
25212
|
|
|
25203
25213
|
// src/index.ts
|
|
25204
|
-
|
|
25205
|
-
|
|
25214
|
+
function fatal(err) {
|
|
25215
|
+
console.error(err?.message ?? String(err));
|
|
25216
|
+
process.exit(1);
|
|
25217
|
+
}
|
|
25218
|
+
var { transport: session, notice } = await (async () => createTransport(readConfig()))().catch(
|
|
25219
|
+
fatal
|
|
25220
|
+
);
|
|
25206
25221
|
console.error(notice);
|
|
25207
25222
|
var INSTRUCTIONS = `Dadaki is a real vector editor. You are drawing actual
|
|
25208
25223
|
vector geometry \u2014 paths, gradients, live text \u2014 not describing an image for
|
|
@@ -25660,4 +25675,7 @@ var shutdown = async () => {
|
|
|
25660
25675
|
};
|
|
25661
25676
|
process.on("SIGINT", shutdown);
|
|
25662
25677
|
process.on("SIGTERM", shutdown);
|
|
25663
|
-
|
|
25678
|
+
var stdio = new StdioServerTransport();
|
|
25679
|
+
stdio.onclose = shutdown;
|
|
25680
|
+
process.stdin.on("end", shutdown);
|
|
25681
|
+
await server.connect(stdio);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dadaki/mcp",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "MCP server that lets an agent author vector artwork in Dadaki — intent-level drawing verbs plus render-back so the agent can see its own work.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
|
+
"LICENSE",
|
|
30
31
|
"README.md"
|
|
31
32
|
],
|
|
32
33
|
"engines": {
|
|
@@ -43,7 +44,9 @@
|
|
|
43
44
|
"smoke": "node --experimental-strip-types smoke.ts",
|
|
44
45
|
"smoke:bridge": "node --experimental-strip-types smoke_bridge.ts",
|
|
45
46
|
"smoke:modes": "node --experimental-strip-types smoke_modes.ts",
|
|
46
|
-
"smoke:relay": "node smoke_relay.mjs"
|
|
47
|
+
"smoke:relay": "node smoke_relay.mjs",
|
|
48
|
+
"smoke:offline": "node --experimental-strip-types smoke_relay_offline.ts",
|
|
49
|
+
"smoke:bundle": "node build.mjs && DADAKI_MCP_TEST_BUNDLE=1 node --experimental-strip-types smoke.ts"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
52
|
"@modelcontextprotocol/sdk": "^1.22.0",
|