@melra/sdk 0.3.0-alpha.11 → 0.3.0-alpha.12
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 +41 -2
- package/dist/index.d.ts +32 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -8
- package/dist/index.js.map +1 -1
- package/dist/remote.d.ts +16 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +110 -0
- package/dist/remote.js.map +1 -0
- package/package.json +7 -4
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -174
- package/dist/index.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
TypeScript client for [MELRA](https://github.com/XAGI-Lab/melra), a local-only
|
|
4
4
|
MCP server that turns one tool call into a governed, verified, receipted task.
|
|
5
5
|
|
|
6
|
-
The client spawns a MELRA server over stdio
|
|
7
|
-
|
|
6
|
+
The client spawns a MELRA server over stdio, or connects to one already running
|
|
7
|
+
over HTTP or a Unix socket, and speaks the task and workflow tools for you, so
|
|
8
|
+
you work with typed requests instead of raw MCP plumbing.
|
|
8
9
|
|
|
9
10
|
```bash
|
|
10
11
|
npm install @melra/sdk
|
|
@@ -63,6 +64,44 @@ const melra = await MelraClient.connect({
|
|
|
63
64
|
});
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
## Connecting to a running server
|
|
68
|
+
|
|
69
|
+
`melra serve --http` and `melra serve --socket <path>` serve the same MCP tools
|
|
70
|
+
to any number of clients. Pass `url` or `socketPath` instead of a command, plus
|
|
71
|
+
the bearer token the server printed (or was given as `MELRA_HTTP_TOKEN`). Every
|
|
72
|
+
method below works the same on all three transports.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { MelraClient } from "@melra/sdk";
|
|
76
|
+
|
|
77
|
+
const token = process.env.MELRA_HTTP_TOKEN;
|
|
78
|
+
if (token === undefined) throw new Error("set MELRA_HTTP_TOKEN");
|
|
79
|
+
|
|
80
|
+
// The MCP URL `serve --http` prints; the bare origin works too.
|
|
81
|
+
const overHttp = await MelraClient.connect({
|
|
82
|
+
url: "http://127.0.0.1:7457/mcp",
|
|
83
|
+
token,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Not on Windows, where the server refuses sockets as well.
|
|
87
|
+
const overSocket = await MelraClient.connect({
|
|
88
|
+
socketPath: `${process.env.XDG_RUNTIME_DIR}/melra.sock`,
|
|
89
|
+
token,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await overHttp.close();
|
|
93
|
+
await overSocket.close();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The token is never read from the environment for you: a library that picks up
|
|
97
|
+
a credential on its own and sends it to whatever URL it was handed is how
|
|
98
|
+
tokens leak. A plain `http:` URL is refused unless its host is loopback
|
|
99
|
+
(`localhost`, `127.0.0.0/8`, `[::1]`) — the token drives the machine, and over
|
|
100
|
+
cleartext it is readable by anything on the path. Put TLS in front and use
|
|
101
|
+
`https:`, or, on a network you trust end to end such as a private container
|
|
102
|
+
network, pass `allowInsecureHttp: true`. A wrong token fails `connect()` with
|
|
103
|
+
`melra_unauthorized`.
|
|
104
|
+
|
|
66
105
|
## Workflows
|
|
67
106
|
|
|
68
107
|
`planWorkflow`, `advanceWorkflow`, `workflowStatus`, `cancelWorkflow`, and
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
import { type WorkflowAdvanceResult, type WorkflowDefinitionInput, type WorkflowInput, type WorkflowRun, type ApprovalResponse, type TaskRequestInput } from "@melra/protocol";
|
|
2
|
-
|
|
2
|
+
interface ClientIdentity {
|
|
3
|
+
clientName?: string;
|
|
4
|
+
clientVersion?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Spawn `melra serve` (or `command`) as a child and speak MCP over stdio. */
|
|
7
|
+
export interface MelraStdioOptions extends ClientIdentity {
|
|
3
8
|
command?: string;
|
|
4
9
|
args?: string[];
|
|
5
10
|
cwd?: string;
|
|
6
11
|
env?: Record<string, string>;
|
|
7
|
-
clientName?: string;
|
|
8
|
-
clientVersion?: string;
|
|
9
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Connect to a running `melra serve --http`. `url` is the MCP endpoint it
|
|
15
|
+
* prints (`http://127.0.0.1:7457/mcp`); plain `http:` is accepted only for a
|
|
16
|
+
* loopback host, since the token would otherwise cross the network in clear.
|
|
17
|
+
*/
|
|
18
|
+
export interface MelraHttpOptions extends ClientIdentity {
|
|
19
|
+
url: string | URL;
|
|
20
|
+
token: string;
|
|
21
|
+
/**
|
|
22
|
+
* Send the token over plain `http:` to a host that is not loopback. Off by
|
|
23
|
+
* default; turn it on only for a network you trust end to end, such as a
|
|
24
|
+
* private container network, and prefer `https:` everywhere else.
|
|
25
|
+
*/
|
|
26
|
+
allowInsecureHttp?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Connect to a running `melra serve --socket <path>`. Not on Windows. */
|
|
29
|
+
export interface MelraSocketOptions extends ClientIdentity {
|
|
30
|
+
socketPath: string;
|
|
31
|
+
token: string;
|
|
32
|
+
}
|
|
33
|
+
type Without<K extends string> = {
|
|
34
|
+
[P in K]?: never;
|
|
35
|
+
};
|
|
36
|
+
type StdioKeys = "command" | "args" | "cwd" | "env";
|
|
37
|
+
export type MelraClientOptions = (MelraStdioOptions & Without<"url" | "socketPath" | "token" | "allowInsecureHttp">) | (MelraHttpOptions & Without<StdioKeys | "socketPath">) | (MelraSocketOptions & Without<StdioKeys | "url" | "allowInsecureHttp">);
|
|
10
38
|
export declare function parseMelraToolResult(result: unknown): Record<string, unknown>;
|
|
11
39
|
export declare class MelraClient {
|
|
12
40
|
private readonly client;
|
|
@@ -29,4 +57,5 @@ export declare class MelraClient {
|
|
|
29
57
|
controlWorkflow(workflowId: string, action: "pause" | "resume" | "suspend"): Promise<WorkflowRun>;
|
|
30
58
|
close(): Promise<void>;
|
|
31
59
|
}
|
|
60
|
+
export {};
|
|
32
61
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
+
import { StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
import { httpTransport, socketTransport } from "./remote.js";
|
|
5
7
|
import { WorkflowAdvanceInputSchema, WorkflowAdvanceResultSchema, WorkflowControlInputSchema, WorkflowDefinitionSchema, WorkflowIdInputSchema, WorkflowRunSchema, TaskRequestSchema, } from "@melra/protocol";
|
|
6
8
|
export function parseMelraToolResult(result) {
|
|
7
9
|
const response = result;
|
|
@@ -18,18 +20,33 @@ export class MelraClient {
|
|
|
18
20
|
this.client = client;
|
|
19
21
|
}
|
|
20
22
|
static async connect(options = {}) {
|
|
21
|
-
const transport =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const transport = options.url !== undefined
|
|
24
|
+
? httpTransport(options.url, options.token, options.allowInsecureHttp === true)
|
|
25
|
+
: options.socketPath !== undefined
|
|
26
|
+
? socketTransport(options.socketPath, options.token)
|
|
27
|
+
: new StdioClientTransport({
|
|
28
|
+
command: options.command ?? "melra",
|
|
29
|
+
args: options.args ?? ["serve"],
|
|
30
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
31
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
32
|
+
stderr: "pipe",
|
|
33
|
+
});
|
|
28
34
|
const client = new Client({
|
|
29
35
|
name: options.clientName ?? "melra-sdk",
|
|
30
36
|
version: options.clientVersion ?? "0.1.0",
|
|
31
37
|
});
|
|
32
|
-
|
|
38
|
+
try {
|
|
39
|
+
await client.connect(transport);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
// `Client.connect` has already closed the transport. Without an OAuth
|
|
43
|
+
// provider the MCP client reports a refused token as a generic POST
|
|
44
|
+
// failure; name it, since it is the one failure a caller can fix.
|
|
45
|
+
if (error instanceof StreamableHTTPError && error.code === 401) {
|
|
46
|
+
throw new Error("melra_unauthorized: the server refused the token; use the one `melra serve` printed or MELRA_HTTP_TOKEN", { cause: error });
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
33
50
|
return new MelraClient(client);
|
|
34
51
|
}
|
|
35
52
|
async call(name, args) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GAOlB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/remote.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* The token is the key to a machine-control API. Over `https:` it is
|
|
4
|
+
* encrypted wherever it goes; over `http:` it is readable by anything on the
|
|
5
|
+
* path, which is nothing only when the path is loopback. A remote `http:`
|
|
6
|
+
* URL means a proxy without TLS, and the SDK refuses to hand it the token
|
|
7
|
+
* rather than leave that to every caller to notice. `allowInsecureHttp` is the
|
|
8
|
+
* explicit opt-out for a network the caller trusts, matching the Python SDK.
|
|
9
|
+
*/
|
|
10
|
+
export declare function httpTransport(url: string | URL, token: string, allowInsecureHttp?: boolean): Transport;
|
|
11
|
+
/**
|
|
12
|
+
* Streamable HTTP over a Unix socket. The server names its socket requests
|
|
13
|
+
* `http://localhost`, so that is the URL; the bytes go to `socketPath`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function socketTransport(socketPath: string, token: string): Transport;
|
|
16
|
+
//# sourceMappingURL=remote.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AA8B/E;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,KAAK,EAAE,MAAM,EACb,iBAAiB,UAAQ,GACxB,SAAS,CAoBX;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAc5E"}
|
package/dist/remote.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Copyright 2026 XAGI Labs Private Limited
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { request as httpRequest } from "node:http";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
/** Loopback is the only place a cleartext bearer token never crosses a wire. */
|
|
7
|
+
function isLoopbackHost(hostname) {
|
|
8
|
+
// `URL` has already normalised `127.1` to `127.0.0.1` and brackets IPv6.
|
|
9
|
+
return (hostname === "localhost" ||
|
|
10
|
+
hostname === "[::1]" ||
|
|
11
|
+
/^127\.\d+\.\d+\.\d+$/.test(hostname));
|
|
12
|
+
}
|
|
13
|
+
function requireToken(token) {
|
|
14
|
+
// `token: process.env.MELRA_HTTP_TOKEN ?? ""` would otherwise connect with
|
|
15
|
+
// `Bearer ` and fail later as a confusing 401.
|
|
16
|
+
if (token.trim() === "")
|
|
17
|
+
throw new Error("melra_token_required");
|
|
18
|
+
return token.trim();
|
|
19
|
+
}
|
|
20
|
+
function bearer(token) {
|
|
21
|
+
return { headers: { authorization: `Bearer ${requireToken(token)}` } };
|
|
22
|
+
}
|
|
23
|
+
// ponytail: the SDK declares `sessionId?: string` on this transport, which
|
|
24
|
+
// `exactOptionalPropertyTypes` reads as incompatible with the `Transport` it
|
|
25
|
+
// implements. Cast once here, as `apps/cli/src/conformance.ts` does.
|
|
26
|
+
function asTransport(transport) {
|
|
27
|
+
return transport;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The token is the key to a machine-control API. Over `https:` it is
|
|
31
|
+
* encrypted wherever it goes; over `http:` it is readable by anything on the
|
|
32
|
+
* path, which is nothing only when the path is loopback. A remote `http:`
|
|
33
|
+
* URL means a proxy without TLS, and the SDK refuses to hand it the token
|
|
34
|
+
* rather than leave that to every caller to notice. `allowInsecureHttp` is the
|
|
35
|
+
* explicit opt-out for a network the caller trusts, matching the Python SDK.
|
|
36
|
+
*/
|
|
37
|
+
export function httpTransport(url, token, allowInsecureHttp = false) {
|
|
38
|
+
const parsed = new URL(url);
|
|
39
|
+
// `serve --http` prints both the console URL and the MCP URL; accept either,
|
|
40
|
+
// as `melra conformance --url` does. The console URL carries `?token=`,
|
|
41
|
+
// which has no business riding along on every MCP request.
|
|
42
|
+
if (!parsed.pathname.endsWith("/mcp")) {
|
|
43
|
+
parsed.pathname = `${parsed.pathname.replace(/\/$/, "")}/mcp`;
|
|
44
|
+
parsed.search = "";
|
|
45
|
+
}
|
|
46
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
47
|
+
throw new Error(`melra_unsupported_url: ${parsed.protocol} is not http(s)`);
|
|
48
|
+
}
|
|
49
|
+
if (parsed.protocol === "http:" && !allowInsecureHttp && !isLoopbackHost(parsed.hostname)) {
|
|
50
|
+
throw new Error(`melra_insecure_url: refusing to send the token over plain http to ${parsed.hostname}; use https, a loopback address, or allowInsecureHttp`);
|
|
51
|
+
}
|
|
52
|
+
return asTransport(new StreamableHTTPClientTransport(parsed, { requestInit: bearer(token) }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Streamable HTTP over a Unix socket. The server names its socket requests
|
|
56
|
+
* `http://localhost`, so that is the URL; the bytes go to `socketPath`.
|
|
57
|
+
*/
|
|
58
|
+
export function socketTransport(socketPath, token) {
|
|
59
|
+
// Mirrors the server, which refuses `--socket` on Windows: a named pipe's
|
|
60
|
+
// ACL is not a mode it can check, so there is nothing to connect to.
|
|
61
|
+
if (process.platform === "win32") {
|
|
62
|
+
throw new Error("socket_transport_unsupported_on_windows: use stdio or loopback HTTP");
|
|
63
|
+
}
|
|
64
|
+
return asTransport(new StreamableHTTPClientTransport(new URL("http://localhost/mcp"), {
|
|
65
|
+
fetch: socketFetch(socketPath),
|
|
66
|
+
requestInit: bearer(token),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `fetch` over a Unix socket, built on `http.request`, which is the one client
|
|
71
|
+
* in Node that takes a `socketPath` without a dependency. The response body is
|
|
72
|
+
* streamed rather than buffered because Streamable HTTP answers with SSE, and
|
|
73
|
+
* a buffered stream would deliver nothing until the server closed it.
|
|
74
|
+
*/
|
|
75
|
+
function socketFetch(socketPath) {
|
|
76
|
+
return async (input, init) => {
|
|
77
|
+
const outgoing = new Request(input, init);
|
|
78
|
+
outgoing.signal.throwIfAborted();
|
|
79
|
+
const url = new URL(outgoing.url);
|
|
80
|
+
// Request bodies are small JSON-RPC messages, so buffering them is fine.
|
|
81
|
+
const body = outgoing.body === null
|
|
82
|
+
? undefined
|
|
83
|
+
: Buffer.from(await outgoing.arrayBuffer());
|
|
84
|
+
return await new Promise((resolveResponse, rejectResponse) => {
|
|
85
|
+
const request = httpRequest({
|
|
86
|
+
socketPath,
|
|
87
|
+
path: `${url.pathname}${url.search}`,
|
|
88
|
+
method: outgoing.method,
|
|
89
|
+
headers: Object.fromEntries(outgoing.headers),
|
|
90
|
+
}, (incoming) => {
|
|
91
|
+
const headers = new Headers();
|
|
92
|
+
for (const [name, value] of Object.entries(incoming.headers)) {
|
|
93
|
+
if (value !== undefined) {
|
|
94
|
+
headers.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const status = incoming.statusCode ?? 500;
|
|
98
|
+
resolveResponse(new Response(status === 204 || status === 304
|
|
99
|
+
? null
|
|
100
|
+
: Readable.toWeb(incoming), { status, headers }));
|
|
101
|
+
});
|
|
102
|
+
// `close()` aborts the transport's long-lived SSE GET; destroying the
|
|
103
|
+
// request is what actually releases the socket.
|
|
104
|
+
outgoing.signal.addEventListener("abort", () => request.destroy(new Error("aborted")));
|
|
105
|
+
request.on("error", rejectResponse);
|
|
106
|
+
request.end(body);
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=remote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melra/sdk",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.12",
|
|
4
4
|
"description": "TypeScript task and workflow client SDK for MELRA.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -21,13 +21,16 @@
|
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@modelcontextprotocol/sdk": "1.
|
|
25
|
-
"@melra/protocol": "0.3.0-alpha.
|
|
24
|
+
"@modelcontextprotocol/sdk": "1.32.1",
|
|
25
|
+
"@melra/protocol": "0.3.0-alpha.12"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@melra/server": "0.3.0-alpha.12"
|
|
26
29
|
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"build": "tsc -p tsconfig.json",
|
|
29
32
|
"clean": "rm -rf dist",
|
|
30
33
|
"test": "vitest run",
|
|
31
|
-
"typecheck": "tsc -p tsconfig.json
|
|
34
|
+
"typecheck": "tsc -p tsconfig.test.json"
|
|
32
35
|
}
|
|
33
36
|
}
|
package/dist/index.test.d.ts
DELETED
package/dist/index.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
|
package/dist/index.test.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
// Copyright 2026 XAGI Labs Private Limited
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
import { WorkflowDefinitionSchema } from "@melra/protocol";
|
|
5
|
-
import { MelraClient, parseMelraToolResult } from "./index.js";
|
|
6
|
-
describe("TypeScript SDK result parsing", () => {
|
|
7
|
-
it("parses the server's JSON text contract", () => {
|
|
8
|
-
expect(parseMelraToolResult({
|
|
9
|
-
content: [{ type: "text", text: '{"verified":true}' }],
|
|
10
|
-
})).toEqual({ verified: true });
|
|
11
|
-
});
|
|
12
|
-
it("does not hide MCP tool errors", () => {
|
|
13
|
-
expect(() => parseMelraToolResult({
|
|
14
|
-
isError: true,
|
|
15
|
-
content: [{ type: "text", text: "approval_required" }],
|
|
16
|
-
})).toThrow("approval_required");
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
describe("TypeScript workflow SDK", () => {
|
|
20
|
-
it("calls the exact workflow tools with typed arguments", async () => {
|
|
21
|
-
const workflowId = "11111111-1111-4111-8111-111111111111";
|
|
22
|
-
const traceId = "22222222-2222-4222-8222-222222222222";
|
|
23
|
-
const definition = WorkflowDefinitionSchema.parse({
|
|
24
|
-
schemaVersion: "1.0.0",
|
|
25
|
-
id: "33333333-3333-4333-8333-333333333333",
|
|
26
|
-
version: 1,
|
|
27
|
-
name: "SDK workflow",
|
|
28
|
-
nodes: [
|
|
29
|
-
{
|
|
30
|
-
id: "inspect",
|
|
31
|
-
type: "operation",
|
|
32
|
-
request: {
|
|
33
|
-
goal: "Inspect",
|
|
34
|
-
operation: { kind: "system", action: "info" },
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
});
|
|
39
|
-
const run = {
|
|
40
|
-
schemaVersion: "1.0.0",
|
|
41
|
-
id: workflowId,
|
|
42
|
-
definitionId: definition.id,
|
|
43
|
-
definitionVersion: 1,
|
|
44
|
-
status: "planned",
|
|
45
|
-
stateVersion: 2,
|
|
46
|
-
nodes: { inspect: { status: "pending", taskIds: [] } },
|
|
47
|
-
traceId,
|
|
48
|
-
createdAt: "2026-07-30T12:00:00.000Z",
|
|
49
|
-
updatedAt: "2026-07-30T12:00:00.000Z",
|
|
50
|
-
};
|
|
51
|
-
const callTool = vi.fn(async ({ name }) => ({
|
|
52
|
-
content: [
|
|
53
|
-
{
|
|
54
|
-
type: "text",
|
|
55
|
-
text: JSON.stringify(name === "melra_workflow_advance"
|
|
56
|
-
? { run, tasks: [], events: [] }
|
|
57
|
-
: run),
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
}));
|
|
61
|
-
const client = new MelraClient({
|
|
62
|
-
callTool,
|
|
63
|
-
async close() { },
|
|
64
|
-
});
|
|
65
|
-
const approval = {
|
|
66
|
-
approvalId: "44444444-4444-4444-8444-444444444444",
|
|
67
|
-
phrase: "APPROVE exact",
|
|
68
|
-
};
|
|
69
|
-
const answer = {
|
|
70
|
-
nodeId: "inspect",
|
|
71
|
-
value: "ship it",
|
|
72
|
-
};
|
|
73
|
-
await client.planWorkflow(definition);
|
|
74
|
-
await client.advanceWorkflow(workflowId, [approval], [answer]);
|
|
75
|
-
await client.workflowStatus(workflowId);
|
|
76
|
-
await client.controlWorkflow(workflowId, "pause");
|
|
77
|
-
await client.cancelWorkflow(workflowId);
|
|
78
|
-
expect(callTool.mock.calls.map(([input]) => input)).toEqual([
|
|
79
|
-
{
|
|
80
|
-
name: "melra_workflow_plan",
|
|
81
|
-
arguments: { definition },
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: "melra_workflow_advance",
|
|
85
|
-
arguments: { workflowId, approvals: [approval], inputs: [answer] },
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "melra_workflow_status",
|
|
89
|
-
arguments: { workflowId },
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "melra_workflow_control",
|
|
93
|
-
arguments: { workflowId, action: "pause" },
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "melra_workflow_cancel",
|
|
97
|
-
arguments: { workflowId },
|
|
98
|
-
},
|
|
99
|
-
]);
|
|
100
|
-
});
|
|
101
|
-
it("plans a definition written by hand, defaults and all", async () => {
|
|
102
|
-
const run = {
|
|
103
|
-
schemaVersion: "1.0.0",
|
|
104
|
-
id: "55555555-5555-4555-8555-555555555555",
|
|
105
|
-
definitionId: "66666666-6666-4666-8666-666666666666",
|
|
106
|
-
definitionVersion: 1,
|
|
107
|
-
status: "planned",
|
|
108
|
-
stateVersion: 2,
|
|
109
|
-
nodes: { inspect: { status: "pending", taskIds: [] } },
|
|
110
|
-
traceId: "77777777-7777-4777-8777-777777777777",
|
|
111
|
-
createdAt: "2026-08-08T12:00:00.000Z",
|
|
112
|
-
updatedAt: "2026-08-08T12:00:00.000Z",
|
|
113
|
-
};
|
|
114
|
-
const callTool = vi.fn(async (_input) => ({
|
|
115
|
-
content: [{ type: "text", text: JSON.stringify(run) }],
|
|
116
|
-
}));
|
|
117
|
-
const client = new MelraClient({ callTool, async close() { } });
|
|
118
|
-
// No `dependsOn`, `constraints`, `forbiddenEffects`, `requiredEvidence`, or
|
|
119
|
-
// `budget`. Requiring those would mean writing out `constraints: []` — where
|
|
120
|
-
// any other value is a policy deny — just to satisfy the compiler.
|
|
121
|
-
await client.planWorkflow({
|
|
122
|
-
schemaVersion: "1.0.0",
|
|
123
|
-
id: run.definitionId,
|
|
124
|
-
version: 1,
|
|
125
|
-
name: "Handwritten workflow",
|
|
126
|
-
nodes: [
|
|
127
|
-
{
|
|
128
|
-
id: "inspect",
|
|
129
|
-
type: "operation",
|
|
130
|
-
request: {
|
|
131
|
-
goal: "Inspect",
|
|
132
|
-
operation: { kind: "system", action: "info" },
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
});
|
|
137
|
-
// The server still receives the complete definition, so the defaults are
|
|
138
|
-
// applied once here rather than differing per caller.
|
|
139
|
-
expect(callTool.mock.calls[0]?.[0]).toEqual({
|
|
140
|
-
name: "melra_workflow_plan",
|
|
141
|
-
arguments: {
|
|
142
|
-
definition: {
|
|
143
|
-
schemaVersion: "1.0.0",
|
|
144
|
-
id: run.definitionId,
|
|
145
|
-
version: 1,
|
|
146
|
-
name: "Handwritten workflow",
|
|
147
|
-
nodes: [
|
|
148
|
-
{
|
|
149
|
-
id: "inspect",
|
|
150
|
-
type: "operation",
|
|
151
|
-
dependsOn: [],
|
|
152
|
-
request: {
|
|
153
|
-
goal: "Inspect",
|
|
154
|
-
operation: { kind: "system", action: "info" },
|
|
155
|
-
constraints: [],
|
|
156
|
-
forbiddenEffects: [],
|
|
157
|
-
requiredEvidence: [],
|
|
158
|
-
reconciliation: [],
|
|
159
|
-
budget: { maxSteps: 10, maxDurationMs: 120_000, maxRetries: 2 },
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
it("names the missing receipt selector without a round trip", async () => {
|
|
168
|
-
const callTool = vi.fn();
|
|
169
|
-
const client = new MelraClient({ callTool, async close() { } });
|
|
170
|
-
await expect(client.receipt({})).rejects.toThrow("taskId or receiptId is required");
|
|
171
|
-
expect(callTool).not.toHaveBeenCalled();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
//# sourceMappingURL=index.test.js.map
|
package/dist/index.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
|