@mantyx/sdk 0.2.0 → 0.4.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/CHANGELOG.md +13 -1
- package/README.md +256 -12
- package/dist/a2a-server.cjs +407 -0
- package/dist/a2a-server.cjs.map +1 -0
- package/dist/a2a-server.d.cts +170 -0
- package/dist/a2a-server.d.ts +170 -0
- package/dist/a2a-server.js +344 -0
- package/dist/a2a-server.js.map +1 -0
- package/dist/chunk-UVIVQD4O.js +1162 -0
- package/dist/chunk-UVIVQD4O.js.map +1 -0
- package/dist/client-ChxfhYBJ.d.cts +658 -0
- package/dist/client-ChxfhYBJ.d.ts +658 -0
- package/dist/index.cjs +674 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -261
- package/dist/index.d.ts +17 -261
- package/dist/index.js +34 -587
- package/dist/index.js.map +1 -1
- package/docs/agent-runs-protocol.md +441 -18
- package/package.json +24 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Add output schema
|
|
11
|
+
- Add support for a2a, mcp and local versions
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Changelo
|
|
16
|
+
|
|
17
|
+
## [0.2.0] — 2026-05-04
|
|
18
|
+
|
|
8
19
|
### Fixed
|
|
9
20
|
|
|
10
21
|
- Use correct default base url
|
|
@@ -21,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
32
|
|
|
22
33
|
## [0.1.0] — 2026-05-02
|
|
23
34
|
|
|
24
|
-
[unreleased]: https://github.com/mantyx-io/mantyx-sdk/compare/v0.
|
|
35
|
+
[unreleased]: https://github.com/mantyx-io/mantyx-sdk/compare/v0.2.0..HEAD
|
|
36
|
+
[0.2.0]: https://github.com/mantyx-io/mantyx-sdk/compare/v0.1.1..v0.2.0
|
|
25
37
|
[0.1.1]: https://github.com/mantyx-io/mantyx-sdk/compare/v0.1.0..v0.1.1
|
|
26
38
|
[0.1.0]: https://github.com/mantyx-io/mantyx-sdk/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -6,13 +6,20 @@ locally-executed tools, run them remotely, and stream events back into your
|
|
|
6
6
|
process.
|
|
7
7
|
|
|
8
8
|
- LLM loop runs on MANTYX (BYOK or platform-hosted models).
|
|
9
|
-
- Server-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- Server-resolved tools (`mantyx`, `mantyx_plugin`, `a2a`, `mcp`) execute
|
|
10
|
+
inside MANTYX — including remote Agent2Agent peers and remote MCP servers.
|
|
11
|
+
- Client-resolved tools (`local`, `a2a_local`, `mcp_local`) execute inside
|
|
12
|
+
*your* process; the SDK shuttles inputs and outputs over an SSE stream +
|
|
13
|
+
a tool-result POST.
|
|
14
|
+
- Tunable provider thinking via `reasoningLevel` (string anchors or 0–100).
|
|
12
15
|
- One-shot runs and multi-turn sessions, both with persisted observability.
|
|
13
16
|
- Authenticated with a single workspace API key.
|
|
14
17
|
|
|
15
|
-
For background, see the [agent-runs protocol spec](./docs/agent-runs-protocol.md)
|
|
18
|
+
For background, see the [agent-runs protocol spec](./docs/agent-runs-protocol.md)
|
|
19
|
+
and the messaging-layer reference in [`docs/wire-protocol.md`](./docs/wire-protocol.md)
|
|
20
|
+
— the latter pins down the exact `local_tool_call` event shape and the
|
|
21
|
+
resolved data structures (`a2a_local` Agent Card, `mcp_local` `Tool[]`)
|
|
22
|
+
that this SDK ships.
|
|
16
23
|
|
|
17
24
|
## Install
|
|
18
25
|
|
|
@@ -22,9 +29,11 @@ npm install @mantyx/sdk zod
|
|
|
22
29
|
# or: yarn add @mantyx/sdk zod
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
Requires Node.js 18.17+ (for `fetch` and `ReadableStream`).
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
Requires Node.js 18.17+ (for `fetch` and `ReadableStream`). The SDK depends
|
|
33
|
+
on `zod` (parameter schemas) and `@modelcontextprotocol/sdk` (the official
|
|
34
|
+
MCP TypeScript SDK that powers `defineLocalMcp`'s stdio + Streamable HTTP
|
|
35
|
+
transports). The MCP SDK is loaded lazily — apps that never use
|
|
36
|
+
`defineLocalMcp` don't pay its startup cost.
|
|
28
37
|
|
|
29
38
|
## Quickstart
|
|
30
39
|
|
|
@@ -103,6 +112,235 @@ Notes:
|
|
|
103
112
|
The same `agentId` field works on `client.createSession({ ... })` for
|
|
104
113
|
multi-turn conversations against a persisted agent.
|
|
105
114
|
|
|
115
|
+
## Agent2Agent delegation
|
|
116
|
+
|
|
117
|
+
Hand a turn off to another agent — either a remote peer MANTYX dials directly
|
|
118
|
+
(`mantyxA2A`) or a peer that only the SDK can reach (`defineLocalA2A`). The
|
|
119
|
+
model addresses both with the same `{ message: string }` argument shape, so
|
|
120
|
+
an agent prompt that uses one works unchanged with the other.
|
|
121
|
+
|
|
122
|
+
`defineLocalA2A` is fully URL-driven: pass the Agent Card URL and the SDK
|
|
123
|
+
takes care of the rest — fetching the card on the first run, shipping it
|
|
124
|
+
inline as part of the spec, and POSTing JSON-RPC `message/send` to the
|
|
125
|
+
card's `url` whenever MANTYX emits a `local_tool_call`. You don't write any
|
|
126
|
+
A2A code yourself.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { MantyxClient, defineLocalA2A, mantyxA2A } from "@mantyx/sdk";
|
|
130
|
+
|
|
131
|
+
const client = new MantyxClient({ apiKey: "...", workspaceSlug: "acme" });
|
|
132
|
+
|
|
133
|
+
await client.runAgent({
|
|
134
|
+
systemPrompt: "You are a helpful router. Delegate billing to billing_agent.",
|
|
135
|
+
prompt: "Why was I charged twice last month?",
|
|
136
|
+
tools: [
|
|
137
|
+
// Public peer MANTYX dials directly.
|
|
138
|
+
mantyxA2A({
|
|
139
|
+
name: "billing_agent",
|
|
140
|
+
description: "Delegate billing questions to the Acme billing agent.",
|
|
141
|
+
agentCardUrl: "https://billing.acme.com/.well-known/agent-card.json",
|
|
142
|
+
headers: { Authorization: `Bearer ${process.env.BILLING_TOKEN}` },
|
|
143
|
+
}),
|
|
144
|
+
// Intranet peer the SDK reaches on MANTYX's behalf — URL only.
|
|
145
|
+
defineLocalA2A({
|
|
146
|
+
name: "intranet_hr",
|
|
147
|
+
agentCardUrl: "https://hr.intranet.acme/.well-known/agent-card.json",
|
|
148
|
+
headers: { Authorization: `Bearer ${process.env.HR_TOKEN}` },
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The same `headers` are sent on both the card fetch *and* every subsequent
|
|
155
|
+
`message/send` POST, which is typically what intranet peers want. The SDK
|
|
156
|
+
caches the resolved card on the tool ref for the duration of the run /
|
|
157
|
+
session — re-construct the ref to force a refetch.
|
|
158
|
+
|
|
159
|
+
> **Headers and secrets.** The `headers` you pass to `mantyxA2A` are forwarded
|
|
160
|
+
> as-is. For long-lived credentials, register the peer as a workspace
|
|
161
|
+
> `ExternalAgent` instead — those headers support `{{secret:NAME}}`
|
|
162
|
+
> placeholders. Use `mantyxA2A` for short-lived, per-run tokens minted by
|
|
163
|
+
> your application.
|
|
164
|
+
|
|
165
|
+
### Exposing an agent over A2A
|
|
166
|
+
|
|
167
|
+
The inverse direction also works: wrap a MANTYX agent (ephemeral spec or a
|
|
168
|
+
persisted `agentId`) and serve it as an Agent2Agent peer using the official
|
|
169
|
+
[`@a2a-js/sdk`](https://www.npmjs.com/package/@a2a-js/sdk) library. Other
|
|
170
|
+
agents can then discover it at `/.well-known/agent-card.json` and call
|
|
171
|
+
`message/send` over JSON-RPC — including MANTYX agents elsewhere in your
|
|
172
|
+
estate consuming this one via `mantyxA2A` or `defineLocalA2A`.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { MantyxClient } from "@mantyx/sdk";
|
|
176
|
+
import { serveAgentOverA2A } from "@mantyx/sdk/a2a-server";
|
|
177
|
+
|
|
178
|
+
const client = new MantyxClient({ apiKey: "...", workspaceSlug: "acme" });
|
|
179
|
+
|
|
180
|
+
const handle = await serveAgentOverA2A({
|
|
181
|
+
client,
|
|
182
|
+
agent: { agentId: "agent_cm6abc123" }, // or { systemPrompt, modelId, tools }
|
|
183
|
+
port: 4000,
|
|
184
|
+
agentCard: {
|
|
185
|
+
name: "Acme Support",
|
|
186
|
+
description: "Customer support questions.",
|
|
187
|
+
protocolVersion: "0.3.0",
|
|
188
|
+
version: "1.0.0",
|
|
189
|
+
url: "http://localhost:4000",
|
|
190
|
+
skills: [{ id: "support", name: "Support", tags: ["support"] }],
|
|
191
|
+
capabilities: { streaming: true, pushNotifications: false },
|
|
192
|
+
defaultInputModes: ["text"],
|
|
193
|
+
defaultOutputModes: ["text"],
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
console.log(`A2A peer up on ${handle.url}`);
|
|
198
|
+
// later: await handle.close();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`@a2a-js/sdk` and `express` are declared as **optional peer dependencies**,
|
|
202
|
+
so apps that don't expose an A2A server pay zero bundle cost. Install them
|
|
203
|
+
on demand:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npm install @a2a-js/sdk express
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Each unique A2A `contextId` opens a long-lived MANTYX session by default, so
|
|
210
|
+
multi-turn `message/send` calls share conversational history. Pass
|
|
211
|
+
`conversation: "stateless"` to reduce every A2A request to a one-shot
|
|
212
|
+
`runAgent` call.
|
|
213
|
+
|
|
214
|
+
For lower-level integration (mounting the executor in your own Express /
|
|
215
|
+
Fastify / Connect app), `@mantyx/sdk/a2a-server` also exports a
|
|
216
|
+
`MantyxAgentExecutor` class implementing `@a2a-js/sdk/server`'s
|
|
217
|
+
`AgentExecutor` interface.
|
|
218
|
+
|
|
219
|
+
## MCP connectors
|
|
220
|
+
|
|
221
|
+
Expose every tool published by an MCP server to the agent loop in one go,
|
|
222
|
+
without listing them individually.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { MantyxClient, mantyxMcp, defineLocalMcp } from "@mantyx/sdk";
|
|
226
|
+
|
|
227
|
+
const client = new MantyxClient({ apiKey: "...", workspaceSlug: "acme" });
|
|
228
|
+
|
|
229
|
+
await client.runAgent({
|
|
230
|
+
systemPrompt: "You are a developer assistant with GitHub + filesystem access.",
|
|
231
|
+
prompt: "Summarize the latest 5 issues on octocat/hello-world.",
|
|
232
|
+
tools: [
|
|
233
|
+
// Remote MCP server (Streamable HTTP) — MANTYX lists the catalog at run
|
|
234
|
+
// start and proxies every call. Tools surface as `github_<tool>`.
|
|
235
|
+
mantyxMcp({
|
|
236
|
+
name: "github",
|
|
237
|
+
url: "https://mcp.github.com/v1",
|
|
238
|
+
headers: { Authorization: `Bearer ${process.env.GH_PAT}` },
|
|
239
|
+
toolFilter: ["search_issues", "get_repo"],
|
|
240
|
+
}),
|
|
241
|
+
// Local MCP server — fully managed by the SDK. Pass either a
|
|
242
|
+
// Streamable HTTP `url` *or* an stdio `command`; the SDK opens the
|
|
243
|
+
// transport, runs `Initialize` + `tools/list`, ships the resolved
|
|
244
|
+
// catalog inline, and forwards every invocation to `tools/call`. The
|
|
245
|
+
// model sees `<server>_<tool>` (`fs_read_file`, `fs_list_dir`, …) —
|
|
246
|
+
// same shape as `mantyxMcp` above.
|
|
247
|
+
|
|
248
|
+
// (a) Streamable HTTP MCP server.
|
|
249
|
+
defineLocalMcp({
|
|
250
|
+
name: "fs",
|
|
251
|
+
url: "http://localhost:8080/mcp",
|
|
252
|
+
headers: { Authorization: `Bearer ${process.env.FS_TOKEN}` },
|
|
253
|
+
}),
|
|
254
|
+
|
|
255
|
+
// (b) stdio MCP server — the SDK spawns the process for you.
|
|
256
|
+
// defineLocalMcp({
|
|
257
|
+
// name: "fs",
|
|
258
|
+
// command: "mcp-server-filesystem",
|
|
259
|
+
// args: ["/workspace"],
|
|
260
|
+
// env: { LOG_LEVEL: "info" },
|
|
261
|
+
// }),
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The MCP transport is opened lazily on the first `runAgent` / first
|
|
267
|
+
`session.send`, kept warm for subsequent calls within the same run /
|
|
268
|
+
session, and closed when the run completes or `session.end()` is called.
|
|
269
|
+
If the MCP server can't be reached, the SDK throws before submitting the
|
|
270
|
+
spec — you get the failure synchronously rather than mid-conversation.
|
|
271
|
+
|
|
272
|
+
If a remote (`kind: "mcp"`) MCP server is unreachable when the run starts,
|
|
273
|
+
MANTYX still exposes a single `<server>_unavailable` stub so the model can
|
|
274
|
+
tell the user why the connector is missing.
|
|
275
|
+
|
|
276
|
+
## Reasoning effort (`reasoningLevel`)
|
|
277
|
+
|
|
278
|
+
Crank up provider thinking on reasoning models without writing
|
|
279
|
+
provider-specific code:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
await client.runAgent({
|
|
283
|
+
systemPrompt: "...",
|
|
284
|
+
prompt: "Plan a multi-week migration.",
|
|
285
|
+
reasoningLevel: "high", // or 80, etc.
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
| Form | Values | Notes |
|
|
290
|
+
| ------------ | -------------------------------------------- | ----- |
|
|
291
|
+
| String | `"off"`, `"low"`, `"medium"`, `"high"` | Snaps to the same anchors the web composer uses (Fast=30, Moderate=50, Smart=80; off=0). |
|
|
292
|
+
| Number | integer `0`–`100` | `0` explicitly disables provider thinking on reasoning models. |
|
|
293
|
+
|
|
294
|
+
The server maps this onto each LLM's native dial — `reasoning.effort` for
|
|
295
|
+
OpenAI, `thinkingConfig` for Gemini, extended-thinking budget for Anthropic.
|
|
296
|
+
Non-reasoning models silently ignore it. On sessions, `reasoningLevel`
|
|
297
|
+
inherits from the session and can be overridden per `session.send`.
|
|
298
|
+
|
|
299
|
+
## Structured output (`outputSchema`)
|
|
300
|
+
|
|
301
|
+
Constrain the assistant's **final reply** to a JSON document matching a
|
|
302
|
+
JSON Schema. The wire still ships the reply as `text: string`, but that
|
|
303
|
+
string is guaranteed-parseable JSON. Pair with `parseRunOutput` for a
|
|
304
|
+
typed value with a clean error path:
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
import { z } from "zod";
|
|
308
|
+
import { MantyxClient, parseRunOutput } from "@mantyx/sdk";
|
|
309
|
+
|
|
310
|
+
const Weather = z.object({ city: z.string(), temperature_c: z.number() });
|
|
311
|
+
const WeatherJsonSchema = {
|
|
312
|
+
type: "object",
|
|
313
|
+
properties: {
|
|
314
|
+
city: { type: "string" },
|
|
315
|
+
temperature_c: { type: "number" },
|
|
316
|
+
},
|
|
317
|
+
required: ["city", "temperature_c"],
|
|
318
|
+
additionalProperties: false,
|
|
319
|
+
} as const;
|
|
320
|
+
|
|
321
|
+
const result = await client.runAgent({
|
|
322
|
+
systemPrompt: "Return the weather as JSON.",
|
|
323
|
+
prompt: "What's the weather in San Francisco right now?",
|
|
324
|
+
outputSchema: { name: "weather_report", schema: WeatherJsonSchema },
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const report = parseRunOutput(result, (v) => Weather.parse(v));
|
|
328
|
+
// ^? { city: string; temperature_c: number }
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The SDK validates `name` (regex `/^[a-zA-Z0-9_-]{1,64}$/`), schema shape
|
|
332
|
+
(non-array JSON object), and total size (≤ 32 KB) locally so you get a
|
|
333
|
+
typed `MantyxError` up front instead of a server round-trip rejection.
|
|
334
|
+
On parse failure (rare; bad model output), `parseRunOutput` throws
|
|
335
|
+
`MantyxParseError` with the original `text` preserved.
|
|
336
|
+
|
|
337
|
+
`outputSchema` is independent of `reasoningLevel` — combine them for
|
|
338
|
+
deep-reasoning JSON outputs. On sessions it inherits from
|
|
339
|
+
`createSession({ outputSchema })` and can be overridden per
|
|
340
|
+
`session.send(prompt, { outputSchema })`. See
|
|
341
|
+
[`docs/wire-protocol.md` §7](./docs/wire-protocol.md) for the full
|
|
342
|
+
per-provider mapping.
|
|
343
|
+
|
|
106
344
|
## Picking a model
|
|
107
345
|
|
|
108
346
|
```ts
|
|
@@ -248,11 +486,15 @@ interface MantyxClientOptions {
|
|
|
248
486
|
|
|
249
487
|
### Tools
|
|
250
488
|
|
|
251
|
-
| Helper | Use case
|
|
252
|
-
| -------------------------- |
|
|
253
|
-
| `defineLocalTool(opts)` | Define a local tool with a Zod parameter schema and handler.
|
|
254
|
-
| `
|
|
255
|
-
| `
|
|
489
|
+
| Helper | Use case |
|
|
490
|
+
| -------------------------- | ----------------------------------------------------------------------- |
|
|
491
|
+
| `defineLocalTool(opts)` | Define a local tool with a Zod parameter schema and handler. |
|
|
492
|
+
| `defineLocalA2A(opts)` | Local Agent2Agent peer — pass an `agentCardUrl`; the SDK fetches the card and speaks `message/send` for you. |
|
|
493
|
+
| `defineLocalMcp(opts)` | Local MCP server — pass either a Streamable HTTP `url` or an stdio `command`; the SDK runs `Initialize` + `tools/list` + `tools/call` for you. |
|
|
494
|
+
| `mantyxTool(id)` | Reference an existing MANTYX tool by id. |
|
|
495
|
+
| `mantyxPluginTool(name)` | Reference an installed platform plugin tool by name. |
|
|
496
|
+
| `mantyxA2A(opts)` | Remote Agent2Agent peer reachable from MANTYX (server-resolved). |
|
|
497
|
+
| `mantyxMcp(opts)` | Remote MCP server (Streamable HTTP) MANTYX dials and proxies for you. |
|
|
256
498
|
|
|
257
499
|
### Errors
|
|
258
500
|
|
|
@@ -272,6 +514,8 @@ Self-contained example projects live under [`examples/`](./examples/):
|
|
|
272
514
|
- `examples/mixed-tools` — combines local, MANTYX, and plugin tools.
|
|
273
515
|
- `examples/streaming` — token streaming to stdout.
|
|
274
516
|
- `examples/list-models` — model catalog + pick-and-run.
|
|
517
|
+
- `examples/a2a-tools` — remote (`mantyxA2A`) + local (`defineLocalA2A`) Agent2Agent peers.
|
|
518
|
+
- `examples/mcp-tools` — remote (`mantyxMcp`) + local (`defineLocalMcp`) MCP servers.
|
|
275
519
|
|
|
276
520
|
Each example is its own project (`package.json`, `tsconfig.json`, `README.md`)
|
|
277
521
|
so you can copy any one of them out of the repo and run it standalone.
|