@mantyx/sdk 0.1.1 → 0.3.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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/tools.ts","../src/zod-to-json-schema.ts","../src/client.ts","../src/version.ts"],"sourcesContent":["/**\n * Error types raised by the MANTYX SDK.\n */\n\nexport class MantyxError extends Error {\n readonly code: string;\n readonly status: number | undefined;\n readonly hint: string | undefined;\n\n constructor(\n message: string,\n opts: { code?: string; status?: number; hint?: string } = {},\n ) {\n super(message);\n this.name = \"MantyxError\";\n this.code = opts.code ?? \"mantyx_error\";\n this.status = opts.status;\n this.hint = opts.hint;\n }\n}\n\nexport class MantyxNetworkError extends MantyxError {\n constructor(message: string, opts: { cause?: unknown } = {}) {\n super(message, { code: \"network\" });\n this.name = \"MantyxNetworkError\";\n if (opts.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = opts.cause;\n }\n }\n}\n\nexport class MantyxAuthError extends MantyxError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, { code: \"unauthorized\", status: 401 });\n this.name = \"MantyxAuthError\";\n }\n}\n\nexport class MantyxToolError extends MantyxError {\n readonly toolName: string;\n\n constructor(toolName: string, message: string) {\n super(`Local tool ${JSON.stringify(toolName)} failed: ${message}`, {\n code: \"local_tool_failed\",\n });\n this.name = \"MantyxToolError\";\n this.toolName = toolName;\n }\n}\n\nexport class MantyxRunError extends MantyxError {\n readonly runId: string;\n readonly subtype: string;\n\n constructor(runId: string, subtype: string, message: string) {\n super(message, { code: subtype });\n this.name = \"MantyxRunError\";\n this.runId = runId;\n this.subtype = subtype;\n }\n}\n","/**\n * Minimal Server-Sent Events parser.\n *\n * Reads a `ReadableStream<Uint8Array>` (the body of a `fetch()` response) and\n * yields parsed events with `id`, `event` and `data` fields. We deliberately\n * keep this dependency-free instead of pulling in `eventsource` / `eventsource-parser`\n * so the SDK has the smallest possible install footprint.\n *\n * Reconnect/replay is handled at a higher layer using `Last-Event-ID` (the\n * default for browsers' `EventSource`) plus a `?lastSeq=` query param so curl\n * users and SSE-via-fetch consumers both work.\n */\n\nexport interface SseEvent {\n id?: string;\n event?: string;\n data: string;\n}\n\nexport interface SseStreamOptions {\n /** AbortSignal for cancellation. */\n signal?: AbortSignal;\n}\n\n/**\n * Async generator yielding parsed SSE events from a fetch response body.\n * Comment frames (`:keep-alive`) are dropped.\n */\nexport async function* readSseStream(\n body: ReadableStream<Uint8Array> | null,\n opts: SseStreamOptions = {},\n): AsyncGenerator<SseEvent, void, void> {\n if (!body) return;\n const reader = body.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n\n let cancelled = false;\n const onAbort = (): void => {\n cancelled = true;\n try {\n void reader.cancel();\n } catch {\n // ignore\n }\n };\n if (opts.signal) {\n if (opts.signal.aborted) {\n onAbort();\n } else {\n opts.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n try {\n while (!cancelled) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n // SSE events are separated by a blank line (\\n\\n). Some servers emit \\r\\n.\n let sepIdx: number;\n while ((sepIdx = findSeparator(buffer)) !== -1) {\n const raw = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + (buffer.startsWith(\"\\r\", sepIdx) ? 4 : 2));\n const ev = parseEventBlock(raw);\n if (ev) yield ev;\n }\n }\n } finally {\n if (opts.signal) opts.signal.removeEventListener(\"abort\", onAbort);\n try {\n reader.releaseLock();\n } catch {\n // ignore\n }\n }\n}\n\nfunction findSeparator(s: string): number {\n const lf = s.indexOf(\"\\n\\n\");\n const crlf = s.indexOf(\"\\r\\n\\r\\n\");\n if (lf === -1) return crlf;\n if (crlf === -1) return lf;\n return Math.min(lf, crlf);\n}\n\nfunction parseEventBlock(block: string): SseEvent | null {\n const lines = block.split(/\\r?\\n/);\n let id: string | undefined;\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of lines) {\n if (line.length === 0) continue;\n if (line.startsWith(\":\")) continue; // comment / heartbeat\n const colonIdx = line.indexOf(\":\");\n const field = colonIdx === -1 ? line : line.slice(0, colonIdx);\n let value = colonIdx === -1 ? \"\" : line.slice(colonIdx + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (field === \"id\") id = value;\n else if (field === \"event\") event = value;\n else if (field === \"data\") dataLines.push(value);\n }\n if (dataLines.length === 0 && id === undefined && event === undefined) {\n return null;\n }\n return {\n ...(id !== undefined ? { id } : {}),\n ...(event !== undefined ? { event } : {}),\n data: dataLines.join(\"\\n\"),\n };\n}\n","/**\n * Public tool helpers for the MANTYX SDK.\n *\n * defineLocalTool({ name, description, parameters, execute })\n * → A tool that runs in the developer's process. The MANTYX server pauses\n * the agent loop, emits a `local_tool_call` event, and waits for the SDK\n * to POST the result back.\n *\n * mantyxTool(id) → Reference an existing workspace `Tool` row by id.\n * mantyxPluginTool(name) → Reference a built-in plugin tool by `@plugin/tool` name.\n */\nimport type { z } from \"zod\";\n\nexport type ZodLikeObject = z.ZodType<Record<string, unknown>> & {\n _def?: unknown;\n parse?: (value: unknown) => unknown;\n};\n\nexport interface LocalTool<TArgs = Record<string, unknown>> {\n readonly kind: \"local\";\n readonly name: string;\n readonly description: string;\n readonly parameters: ZodLikeObject | undefined;\n readonly execute: (args: TArgs) => Promise<string> | string;\n}\n\nexport interface MantyxToolRef {\n readonly kind: \"mantyx\";\n readonly id: string;\n}\n\nexport interface MantyxPluginToolRef {\n readonly kind: \"mantyx_plugin\";\n readonly name: string;\n}\n\nexport type ToolRef = MantyxToolRef | MantyxPluginToolRef | LocalTool;\n\nexport interface DefineLocalToolOptions<T extends ZodLikeObject | undefined> {\n /** Lowercase alphanumeric + underscore, max 64 chars. */\n name: string;\n description?: string;\n parameters?: T;\n execute: (args: T extends ZodLikeObject ? z.infer<T> : Record<string, unknown>) => Promise<string> | string;\n}\n\nexport function defineLocalTool<T extends ZodLikeObject | undefined>(\n opts: DefineLocalToolOptions<T>,\n): LocalTool {\n if (!/^[a-zA-Z0-9_]{1,64}$/.test(opts.name)) {\n throw new Error(\n `Invalid local tool name ${JSON.stringify(opts.name)}: must match /^[a-zA-Z0-9_]{1,64}$/`,\n );\n }\n return {\n kind: \"local\",\n name: opts.name,\n description: opts.description ?? \"\",\n parameters: opts.parameters,\n execute: opts.execute as LocalTool[\"execute\"],\n };\n}\n\nexport function mantyxTool(id: string): MantyxToolRef {\n if (typeof id !== \"string\" || id.length === 0) {\n throw new Error(\"mantyxTool(id): id must be a non-empty string\");\n }\n return { kind: \"mantyx\", id };\n}\n\nexport function mantyxPluginTool(name: string): MantyxPluginToolRef {\n if (typeof name !== \"string\" || !name.startsWith(\"@\") || !name.includes(\"/\")) {\n throw new Error(\n `mantyxPluginTool(name): expected \"@plugin-slug/tool-name\", got ${JSON.stringify(name)}`,\n );\n }\n return { kind: \"mantyx_plugin\", name };\n}\n\nexport function isLocalTool(t: ToolRef): t is LocalTool {\n return t.kind === \"local\";\n}\n","/**\n * Lightweight Zod → JSON Schema converter for tool parameter definitions.\n *\n * Tries `z.toJSONSchema` (Zod v4+) first; falls back to a hand-rolled walker\n * for v3 schemas so the SDK works on a wide range of zod versions.\n *\n * The output is a JSON-Schema-shaped object with `type: \"object\"`, `properties`,\n * and `required`. The MANTYX server feeds this to LLM providers verbatim, so\n * unsupported zod features (effects, transforms, intersections) degrade to a\n * permissive `\"object\"` description rather than failing.\n */\nimport { z } from \"zod\";\n\ntype JsonSchema = Record<string, unknown>;\n\ninterface ZodLikeWithToJsonSchema {\n toJSONSchema?: (schema: unknown) => JsonSchema;\n}\n\nexport function zodToJsonSchema(schema: z.ZodType<unknown>): JsonSchema {\n const builtIn = (z as unknown as ZodLikeWithToJsonSchema).toJSONSchema;\n if (typeof builtIn === \"function\") {\n try {\n const out = builtIn.call(z, schema) as JsonSchema;\n if (out && typeof out === \"object\") return out;\n } catch {\n // fall through to manual converter\n }\n }\n return convertNode(schema);\n}\n\nfunction convertNode(schema: z.ZodType<unknown>): JsonSchema {\n const def = (schema as unknown as { _def?: { typeName?: string } })._def;\n const typeName = def?.typeName;\n switch (typeName) {\n case \"ZodString\":\n return { type: \"string\" };\n case \"ZodNumber\":\n return { type: \"number\" };\n case \"ZodBoolean\":\n return { type: \"boolean\" };\n case \"ZodNull\":\n return { type: \"null\" };\n case \"ZodLiteral\": {\n const value = (def as { value?: unknown }).value;\n return { const: value, type: typeof value };\n }\n case \"ZodEnum\": {\n const values = (def as { values?: readonly string[] }).values ?? [];\n return { type: \"string\", enum: [...values] };\n }\n case \"ZodArray\": {\n const inner = (def as { type?: z.ZodType<unknown> }).type;\n return {\n type: \"array\",\n items: inner ? convertNode(inner) : {},\n };\n }\n case \"ZodOptional\":\n case \"ZodNullable\": {\n const inner = (def as { innerType?: z.ZodType<unknown> }).innerType;\n return inner ? convertNode(inner) : {};\n }\n case \"ZodDefault\": {\n const inner = (def as { innerType?: z.ZodType<unknown> }).innerType;\n return inner ? convertNode(inner) : {};\n }\n case \"ZodObject\": {\n const shape = (def as { shape?: () => Record<string, z.ZodType<unknown>> }).shape;\n const fields = typeof shape === \"function\" ? shape() : (shape as Record<string, z.ZodType<unknown>> | undefined);\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n if (fields) {\n for (const [key, value] of Object.entries(fields)) {\n properties[key] = convertNode(value);\n const innerDef = (value as unknown as { _def?: { typeName?: string } })._def;\n const innerTypeName = innerDef?.typeName;\n if (innerTypeName !== \"ZodOptional\" && innerTypeName !== \"ZodDefault\") {\n required.push(key);\n }\n }\n }\n const out: JsonSchema = { type: \"object\", properties };\n if (required.length > 0) out.required = required;\n return out;\n }\n default:\n return {};\n }\n}\n\n/**\n * Coerce a JSON-Schema-shaped value into a wire object suitable for the\n * MANTYX local-tool definition payload. Accepts either a Zod schema or an\n * already-shaped JSON Schema object.\n */\nexport function toToolParametersWire(\n parameters: z.ZodType<unknown> | JsonSchema | undefined,\n): JsonSchema {\n if (!parameters) return { type: \"object\", properties: {} };\n if (typeof (parameters as { _def?: unknown })._def !== \"undefined\") {\n return zodToJsonSchema(parameters as z.ZodType<unknown>);\n }\n return parameters as JsonSchema;\n}\n","/**\n * MANTYX SDK client: HTTP plumbing, model catalog, run + session drivers.\n */\nimport {\n MantyxAuthError,\n MantyxError,\n MantyxNetworkError,\n MantyxRunError,\n MantyxToolError,\n} from \"./errors.js\";\nimport { readSseStream } from \"./sse.js\";\nimport type { LocalTool, ToolRef } from \"./tools.js\";\nimport { isLocalTool } from \"./tools.js\";\nimport { toToolParametersWire } from \"./zod-to-json-schema.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.mantyx.com\";\n\nexport interface MantyxClientOptions {\n apiKey: string;\n workspaceSlug: string;\n /** Defaults to `https://api.mantyx.com`. Override for self-hosted instances. */\n baseUrl?: string;\n /** Optional `fetch` override (e.g. node-fetch wrapper, or a custom HTTP client). */\n fetch?: typeof fetch;\n /** Default per-request timeout in milliseconds. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport interface ModelInfo {\n id: string;\n label: string;\n provider: string;\n vendorModelId: string;\n source: \"workspace_provider\" | \"platform_offering\";\n contextWindowTokens: number | null;\n pricing: {\n inputPer1MUsd: number | null;\n outputPer1MUsd: number | null;\n cacheReadPer1MUsd: number | null;\n } | null;\n}\n\nexport interface ModelCatalog {\n models: ModelInfo[];\n defaultModelId: string | null;\n}\n\nexport interface AgentSpecBase {\n name?: string;\n /**\n * Reference to a persisted MANTYX agent in this workspace. When set, the\n * server hydrates `systemPrompt`, `modelId`, and the agent's own tools\n * (memory, skills, plugin tools, …) from the Agent row at run time, and any\n * `tools` you supply here are merged on top — typically `local` tools the\n * SDK wants the agent to be able to call back into.\n *\n * Either `agentId` or `systemPrompt` must be set.\n */\n agentId?: string;\n /** Required unless `agentId` is set. */\n systemPrompt?: string;\n modelId?: string;\n tools?: ToolRef[];\n budgets?: { maxToolTurns?: number };\n /**\n * Flat string→string KV carried alongside the run / session for\n * observability. Use it to tag runs with your own application identifiers\n * (customer id, environment, workflow name, …) — the values are visible in\n * the MANTYX dashboard and can be filtered there.\n *\n * Limits enforced server-side: max 16 entries; keys match\n * `[A-Za-z0-9._-]{1,64}`; values are strings ≤ 256 chars; serialized JSON\n * ≤ 4 KB. For session-scoped runs, the session's metadata is inherited and\n * any per-message override is merged on top.\n */\n metadata?: Record<string, string>;\n}\n\nexport interface RunSpec extends AgentSpecBase {\n prompt?: string;\n messages?: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>;\n /** Receives streaming assistant text deltas. */\n onAssistantDelta?: (delta: string) => void;\n /** Receives raw events (assistant_message, local_tool_call, tool_result, ...) for advanced consumers. */\n onEvent?: (event: RunEvent) => void;\n /** Aborts the run on the client and best-effort cancels server-side. */\n signal?: AbortSignal;\n}\n\nexport type SessionSpec = AgentSpecBase;\n\nexport interface RunResult {\n runId: string;\n text: string;\n events: RunEvent[];\n}\n\nexport interface RunEventBase {\n seq: number;\n type: string;\n}\n\nexport interface AssistantDeltaEvent extends RunEventBase {\n type: \"assistant_delta\";\n text: string;\n}\n\nexport interface ThinkingDeltaEvent extends RunEventBase {\n type: \"thinking_delta\";\n text: string;\n}\n\nexport interface AssistantMessageEvent extends RunEventBase {\n type: \"assistant_message\";\n text: string;\n}\n\nexport interface ServerToolResultEvent extends RunEventBase {\n type: \"tool_result\";\n name: string;\n args?: Record<string, unknown>;\n ok?: boolean;\n summary?: string;\n phase?: \"start\" | \"end\";\n}\n\nexport interface LocalToolCallEvent extends RunEventBase {\n type: \"local_tool_call\";\n toolUseId: string;\n name: string;\n args: Record<string, unknown>;\n}\n\nexport interface LocalToolResultInEvent extends RunEventBase {\n type: \"local_tool_result_in\";\n toolUseId: string;\n result?: string;\n error?: string;\n}\n\nexport interface ResultEvent extends RunEventBase {\n type: \"result\";\n subtype: string;\n text?: string;\n error?: string;\n}\n\nexport interface ErrorEvent extends RunEventBase {\n type: \"error\";\n error: string;\n code?: string;\n}\n\nexport interface CancelledEvent extends RunEventBase {\n type: \"cancelled\";\n reason?: string;\n}\n\nexport type RunEvent =\n | AssistantDeltaEvent\n | ThinkingDeltaEvent\n | AssistantMessageEvent\n | ServerToolResultEvent\n | LocalToolCallEvent\n | LocalToolResultInEvent\n | ResultEvent\n | ErrorEvent\n | CancelledEvent\n | (RunEventBase & { type: string; [key: string]: unknown });\n\nexport interface SessionInfo {\n id: string;\n name: string;\n status: \"active\" | \"ended\";\n createdAt: string;\n lastUsedAt: string;\n endedAt: string | null;\n agentSpec: AgentSpecBase;\n messages: Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>;\n /** Metadata that was attached to the session at create time, returned for observability. */\n metadata: Record<string, string>;\n}\n\nexport class MantyxClient {\n readonly options: Required<Pick<MantyxClientOptions, \"apiKey\" | \"workspaceSlug\" | \"baseUrl\">> & {\n fetch: typeof fetch;\n timeoutMs: number;\n };\n\n constructor(opts: MantyxClientOptions) {\n if (!opts.apiKey || typeof opts.apiKey !== \"string\") {\n throw new MantyxError(\"apiKey is required\");\n }\n if (!opts.workspaceSlug || typeof opts.workspaceSlug !== \"string\") {\n throw new MantyxError(\"workspaceSlug is required\");\n }\n const f = opts.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new MantyxError(\n \"Global fetch is not available; pass a custom `fetch` implementation in MantyxClientOptions.\",\n );\n }\n this.options = {\n apiKey: opts.apiKey,\n workspaceSlug: opts.workspaceSlug,\n baseUrl: (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\n fetch: f,\n timeoutMs: opts.timeoutMs ?? 60_000,\n };\n }\n\n // -------------------------------------------------------------- Models\n\n async listModels(): Promise<ModelCatalog> {\n return this.request<ModelCatalog>({\n method: \"GET\",\n path: \"/models\",\n });\n }\n\n // ------------------------------------------------------------- One-shot\n\n async runAgent(spec: RunSpec): Promise<RunResult> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: \"/agent-runs\",\n body: serializeAgentSpec(spec, {\n prompt: spec.prompt,\n messages: spec.messages,\n }),\n });\n return this.driveRun(created.runId, handlers, {\n ...(spec.onAssistantDelta ? { onAssistantDelta: spec.onAssistantDelta } : {}),\n ...(spec.onEvent ? { onEvent: spec.onEvent } : {}),\n ...(spec.signal ? { signal: spec.signal } : {}),\n });\n }\n\n async *streamAgent(spec: RunSpec): AsyncGenerator<RunEvent, void, void> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: \"/agent-runs\",\n body: serializeAgentSpec(spec, {\n prompt: spec.prompt,\n messages: spec.messages,\n }),\n });\n yield* this.streamRunEvents(created.runId, handlers, spec.signal);\n }\n\n // ------------------------------------------------------------- Sessions\n\n async createSession(spec: SessionSpec): Promise<AgentSession> {\n const handlers = collectLocalHandlers(spec.tools ?? []);\n const created = await this.request<{ sessionId: string; name: string; createdAt: string }>({\n method: \"POST\",\n path: \"/agent-sessions\",\n body: serializeAgentSpec(spec),\n });\n return new AgentSession(this, created.sessionId, handlers);\n }\n\n async resumeSession(\n sessionId: string,\n opts: { tools?: ToolRef[] } = {},\n ): Promise<AgentSession> {\n // Verify the session exists and is still active. Optionally refresh tool defs.\n await this.getSessionInfo(sessionId);\n const handlers = collectLocalHandlers(opts.tools ?? []);\n return new AgentSession(this, sessionId, handlers, opts.tools);\n }\n\n async endSession(sessionId: string): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"DELETE\",\n path: `/agent-sessions/${encodeURIComponent(sessionId)}`,\n });\n }\n\n async getSessionInfo(sessionId: string): Promise<SessionInfo> {\n return this.request<SessionInfo>({\n method: \"GET\",\n path: `/agent-sessions/${encodeURIComponent(sessionId)}`,\n });\n }\n\n // ----------------------------------------------------------- Internals\n\n /** Drive an existing run to completion (collect events, dispatch local tools). */\n async driveRun(\n runId: string,\n handlers: Map<string, LocalTool>,\n opts: {\n onAssistantDelta?: (delta: string) => void;\n onEvent?: (event: RunEvent) => void;\n signal?: AbortSignal;\n } = {},\n ): Promise<RunResult> {\n const collected: RunEvent[] = [];\n let finalText = \"\";\n for await (const ev of this.streamRunEvents(runId, handlers, opts.signal)) {\n collected.push(ev);\n if (opts.onEvent) opts.onEvent(ev);\n if (ev.type === \"assistant_delta\" && opts.onAssistantDelta) {\n opts.onAssistantDelta((ev as AssistantDeltaEvent).text);\n }\n if (ev.type === \"result\") {\n const r = ev as ResultEvent;\n if (r.subtype === \"success\") {\n finalText = typeof r.text === \"string\" ? r.text : \"\";\n } else {\n throw new MantyxRunError(runId, r.subtype, r.error ?? r.subtype);\n }\n } else if (ev.type === \"error\") {\n const e = ev as ErrorEvent;\n throw new MantyxRunError(runId, e.code ?? \"error\", e.error);\n } else if (ev.type === \"cancelled\") {\n throw new MantyxRunError(runId, \"cancelled\", \"Run was cancelled\");\n }\n }\n return { runId, text: finalText, events: collected };\n }\n\n async *streamRunEvents(\n runId: string,\n handlers: Map<string, LocalTool>,\n signal?: AbortSignal,\n ): AsyncGenerator<RunEvent, void, void> {\n const url = this.absoluteUrl(`/agent-runs/${encodeURIComponent(runId)}/stream`);\n let lastSeq = 0;\n while (true) {\n const reqUrl = lastSeq > 0 ? `${url}?lastSeq=${lastSeq}` : url;\n const res = await this.options.fetch(reqUrl, {\n method: \"GET\",\n headers: {\n ...this.authHeaders(),\n Accept: \"text/event-stream\",\n ...(lastSeq > 0 ? { \"Last-Event-ID\": String(lastSeq) } : {}),\n },\n ...(signal ? { signal } : {}),\n }).catch((err: unknown) => {\n throw new MantyxNetworkError(`Failed to open SSE stream: ${(err as Error).message}`, {\n cause: err,\n });\n });\n if (!res.ok) {\n throw await this.errorFromResponse(res);\n }\n let terminal = false;\n try {\n for await (const sseEvent of readSseStream(res.body, { ...(signal ? { signal } : {}) })) {\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(sseEvent.data || \"{}\") as Record<string, unknown>;\n } catch {\n data = {};\n }\n const evType = sseEvent.event ?? (data.type as string | undefined) ?? \"message\";\n const seq = typeof data.seq === \"number\" ? data.seq : lastSeq;\n if (typeof seq === \"number\" && seq > lastSeq) lastSeq = seq;\n const ev = { seq, type: evType, ...data } as RunEvent;\n yield ev;\n if (evType === \"local_tool_call\") {\n const localEv = ev as LocalToolCallEvent;\n void this.dispatchLocalTool(runId, localEv, handlers).catch((err) => {\n // best-effort logging; the run will surface a `result/error` if the\n // server eventually times out.\n console.error(\"[mantyx-sdk] local tool dispatch failed:\", err);\n });\n }\n if (evType === \"result\" || evType === \"error\" || evType === \"cancelled\") {\n terminal = true;\n return;\n }\n }\n } catch (err) {\n if (signal?.aborted) {\n throw new MantyxRunError(runId, \"cancelled\", \"Run was cancelled by the client\");\n }\n // Network blip — retry after a tiny backoff with `?lastSeq=`.\n await sleep(500);\n continue;\n }\n if (terminal) return;\n // Stream closed without a terminal event (server restart, etc.) — reconnect.\n }\n }\n\n async dispatchLocalTool(\n runId: string,\n ev: LocalToolCallEvent,\n handlers: Map<string, LocalTool>,\n ): Promise<void> {\n const handler = handlers.get(ev.name);\n if (!handler) {\n await this.postToolResult(runId, ev.toolUseId, {\n error: `No local handler registered for tool ${JSON.stringify(ev.name)}`,\n });\n return;\n }\n try {\n const args = handler.parameters ? handler.parameters.parse?.(ev.args) ?? ev.args : ev.args;\n const out = await handler.execute(args as Record<string, unknown>);\n const resultText = typeof out === \"string\" ? out : JSON.stringify(out);\n await this.postToolResult(runId, ev.toolUseId, { result: resultText });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await this.postToolResult(runId, ev.toolUseId, {\n error: new MantyxToolError(handler.name, message).message,\n });\n }\n }\n\n async postToolResult(\n runId: string,\n toolUseId: string,\n payload: { result?: string; error?: string },\n ): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"POST\",\n path: `/agent-runs/${encodeURIComponent(runId)}/tool-results`,\n body: { toolUseId, ...payload },\n });\n }\n\n async cancelRun(runId: string): Promise<void> {\n await this.request<{ ok: boolean }>({\n method: \"POST\",\n path: `/agent-runs/${encodeURIComponent(runId)}/cancel`,\n });\n }\n\n // -------------------------------------------------------------- HTTP\n\n private absoluteUrl(path: string): string {\n return `${this.options.baseUrl}/api/v1/workspaces/${encodeURIComponent(this.options.workspaceSlug)}${path}`;\n }\n\n private authHeaders(): Record<string, string> {\n return { Authorization: `Bearer ${this.options.apiKey}` };\n }\n\n async request<T>(args: {\n method: string;\n path: string;\n body?: unknown;\n timeoutMs?: number;\n }): Promise<T> {\n const url = this.absoluteUrl(args.path);\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), args.timeoutMs ?? this.options.timeoutMs);\n try {\n const res = await this.options.fetch(url, {\n method: args.method,\n headers: {\n ...this.authHeaders(),\n ...(args.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n Accept: \"application/json\",\n },\n ...(args.body !== undefined ? { body: JSON.stringify(args.body) } : {}),\n signal: ctrl.signal,\n }).catch((err: unknown) => {\n if (ctrl.signal.aborted) {\n throw new MantyxNetworkError(`Request timed out after ${args.timeoutMs ?? this.options.timeoutMs}ms`);\n }\n throw new MantyxNetworkError(`Network error: ${(err as Error).message}`, { cause: err });\n });\n if (!res.ok) {\n throw await this.errorFromResponse(res);\n }\n const text = await res.text();\n if (!text) return undefined as unknown as T;\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new MantyxError(`Failed to parse JSON response: ${(err as Error).message}`);\n }\n } finally {\n clearTimeout(t);\n }\n }\n\n private async errorFromResponse(res: Response): Promise<MantyxError> {\n let body: { error?: string; code?: string; hint?: string } = {};\n try {\n body = (await res.json()) as typeof body;\n } catch {\n // ignore\n }\n if (res.status === 401) {\n return new MantyxAuthError(body.error ?? \"Invalid API key\");\n }\n return new MantyxError(body.error ?? `HTTP ${res.status}`, {\n code: body.code ?? `http_${res.status}`,\n status: res.status,\n ...(body.hint ? { hint: body.hint } : {}),\n });\n }\n}\n\n// ---------------------------------------------------------------- Sessions\n\nexport class AgentSession {\n readonly id: string;\n readonly client: MantyxClient;\n private readonly handlers: Map<string, LocalTool>;\n private readonly toolsForResume: ToolRef[] | undefined;\n\n constructor(\n client: MantyxClient,\n id: string,\n handlers: Map<string, LocalTool>,\n toolsForResume?: ToolRef[],\n ) {\n this.client = client;\n this.id = id;\n this.handlers = handlers;\n this.toolsForResume = toolsForResume;\n }\n\n async send(\n prompt: string,\n opts: {\n onAssistantDelta?: (s: string) => void;\n signal?: AbortSignal;\n /**\n * Per-message metadata override. Server-side this is merged on top of\n * the session's metadata at run-creation time (run-level keys win).\n * Useful for tagging individual turns (e.g. `{ \"trace_id\": \"abc\" }`).\n */\n metadata?: Record<string, string>;\n } = {},\n ): Promise<RunResult> {\n const created = await this.client.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: `/agent-sessions/${encodeURIComponent(this.id)}/messages`,\n body: {\n prompt,\n ...(this.toolsForResume ? { tools: serializeToolRefs(this.toolsForResume) } : {}),\n ...(opts.metadata && Object.keys(opts.metadata).length > 0\n ? { metadata: opts.metadata }\n : {}),\n },\n });\n return this.client.driveRun(created.runId, this.handlers, {\n ...(opts.onAssistantDelta ? { onAssistantDelta: opts.onAssistantDelta } : {}),\n ...(opts.signal ? { signal: opts.signal } : {}),\n });\n }\n\n async *stream(\n prompt: string,\n opts: { signal?: AbortSignal; metadata?: Record<string, string> } = {},\n ): AsyncGenerator<RunEvent, void, void> {\n const created = await this.client.request<{ runId: string; streamUrl: string }>({\n method: \"POST\",\n path: `/agent-sessions/${encodeURIComponent(this.id)}/messages`,\n body: {\n prompt,\n ...(this.toolsForResume ? { tools: serializeToolRefs(this.toolsForResume) } : {}),\n ...(opts.metadata && Object.keys(opts.metadata).length > 0\n ? { metadata: opts.metadata }\n : {}),\n },\n });\n yield* this.client.streamRunEvents(created.runId, this.handlers, opts.signal);\n }\n\n async history(): Promise<Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>> {\n const info = await this.client.getSessionInfo(this.id);\n return info.messages;\n }\n\n async info(): Promise<SessionInfo> {\n return this.client.getSessionInfo(this.id);\n }\n\n async end(): Promise<void> {\n await this.client.endSession(this.id);\n }\n}\n\n// ---------------------------------------------------------------- Helpers\n\nfunction serializeAgentSpec(\n spec: AgentSpecBase,\n extra: { prompt?: string; messages?: Array<{ role: string; content: string }> } = {},\n): Record<string, unknown> {\n if (!spec.agentId && (typeof spec.systemPrompt !== \"string\" || spec.systemPrompt.length === 0)) {\n throw new MantyxError(\"Either `agentId` or `systemPrompt` is required\");\n }\n const body: Record<string, unknown> = {\n tools: serializeToolRefs(spec.tools ?? []),\n };\n if (typeof spec.systemPrompt === \"string\") body.systemPrompt = spec.systemPrompt;\n if (spec.agentId) body.agentId = spec.agentId;\n if (spec.name) body.name = spec.name;\n if (spec.modelId) body.modelId = spec.modelId;\n if (spec.budgets) body.budgets = spec.budgets;\n if (spec.metadata && Object.keys(spec.metadata).length > 0) body.metadata = spec.metadata;\n if (extra.prompt !== undefined) body.prompt = extra.prompt;\n if (extra.messages !== undefined) body.messages = extra.messages;\n return body;\n}\n\nfunction serializeToolRefs(tools: ToolRef[]): unknown[] {\n return tools.map((t) => {\n if (t.kind === \"mantyx\") return { kind: \"mantyx\", id: t.id };\n if (t.kind === \"mantyx_plugin\") return { kind: \"mantyx_plugin\", name: t.name };\n return {\n kind: \"local\",\n name: t.name,\n description: t.description,\n parameters: toToolParametersWire(t.parameters),\n };\n });\n}\n\nfunction collectLocalHandlers(tools: ToolRef[]): Map<string, LocalTool> {\n const map = new Map<string, LocalTool>();\n for (const t of tools) {\n if (isLocalTool(t)) map.set(t.name, t);\n }\n return map;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","/**\n * Release version — synced from repo root VERSION (`npm run sync-version`).\n */\nexport const SDK_VERSION = \"0.1.1\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["/**\n * Release version — synced from repo root VERSION (`npm run sync-version`).\n */\nexport const SDK_VERSION = \"0.3.0\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,cAAc;","names":[]}
@@ -5,8 +5,14 @@ speaks with SDKs. It is the source of truth for anyone implementing a new
5
5
  client (Python, Rust, Java…) and is shipped with each first-party SDK so the
6
6
  SDK repository can stand on its own when it is extracted from this monorepo.
7
7
 
8
- The companion document for this protocol — server-side overview, internals,
9
- deployment notes — is [`docs/agent-runs.md`](./agent-runs.md).
8
+ Companion documents:
9
+
10
+ - [`docs/wire-protocol.md`](./wire-protocol.md) — the messaging-layer
11
+ reference: every SSE event payload, the SDK-side dispatcher pattern, and
12
+ the resolved data structures (`a2a_local` Agent Card, `mcp_local`
13
+ `Tool[]`) the SDK is expected to ship.
14
+ - [`docs/agent-runs.md`](./agent-runs.md) — server-side overview, internals,
15
+ deployment notes.
10
16
 
11
17
  ## 1. Concepts
12
18
 
@@ -15,17 +21,34 @@ than persisted as a row in MANTYX's `Agent` table. The full spec (system
15
21
  prompt, model, tools) is stored as part of each session/run for observability
16
22
  but is not editable from the dashboard.
17
23
 
18
- **Tool refs.** Three flavours, all carried inside the agent spec:
24
+ **Tool refs.** Seven flavours, all carried inside the agent spec's `tools`
25
+ array:
19
26
 
20
27
  | `kind` | Resolved by | Notes |
21
28
  | ---------------- | ----------- | ----- |
22
29
  | `mantyx` | server | A workspace `Tool` row referenced by id (HTTP / Code / Plugin). |
23
30
  | `mantyx_plugin` | server | A platform plugin tool referenced by name. |
24
- | `local` | client | Defined and executed in the SDK's process. |
25
-
26
- When the model calls a `local` tool, MANTYX pauses the agent loop, emits a
27
- `local_tool_call` event over SSE and waits for the SDK to POST a tool-result
28
- back via HTTP.
31
+ | `local` | client | A custom tool defined and executed in the SDK's process. |
32
+ | `a2a` | server | A *remote* Agent2Agent peer MANTYX can reach; invoked via `message/send` and the reply is surfaced as the tool result. |
33
+ | `a2a_local` | client | An A2A peer MANTYX **cannot** reach. SDK resolves the [Agent Card](https://google.github.io/A2A/specification/#agent-card) locally and ships it inline; MANTYX uses it for the model description and routes calls back to the SDK over SSE. |
34
+ | `mcp` | server | A *remote* MCP server (Streamable HTTP). At run start MANTYX lists the catalog and exposes every tool as `<server>_<tool>` (subject to `toolFilter`). |
35
+ | `mcp_local` | client | An MCP server MANTYX **cannot** reach. SDK runs `Initialize` + `tools/list` locally and ships the resolved `Tool[]` (with `inputSchema`); MANTYX exposes them to the model with the SDK-declared names and routes calls back over SSE. |
36
+
37
+ The split is deliberate:
38
+
39
+ - **Server-resolved** (`mantyx`, `mantyx_plugin`, `a2a`, `mcp`) — MANTYX has
40
+ network access to the resource. The worker runs the tool itself and the
41
+ SDK only sees an informational `tool_result` event in the SSE stream. For
42
+ MCP/A2A this also means MANTYX does discovery (`listTools`, agent-card
43
+ fetch).
44
+ - **Client-resolved / "local"** (`local`, `a2a_local`, `mcp_local`) —
45
+ MANTYX has *no* access to the resource. The SDK does **all** of the
46
+ work: connection, discovery, listing, expansion, arg validation, auth,
47
+ execution, retries. MANTYX is a thin LLM-routing layer that emits a
48
+ `local_tool_call` event and blocks until the SDK POSTs back to
49
+ `.../tool-results`. The event payload carries a `kind` discriminator
50
+ (`"local"` implied when absent, `"a2a_local"` and `"mcp_local"` explicit)
51
+ so SDKs can dispatch to the right local handler.
29
52
 
30
53
  **One-shot run vs. session.** A run is an LLM execution. Runs may be:
31
54
 
@@ -114,6 +137,7 @@ The agent spec is the body shape used by `POST /agent-runs` and `POST
114
137
  "agentId": "agent_cm6abc123", // optional — see §4.1
115
138
  "systemPrompt": "You are helpful.", // required unless agentId is set
116
139
  "modelId": "platform:cm6abc123", // optional, see §3
140
+ "reasoningLevel": "medium", // optional, see §4.4
117
141
  "tools": [
118
142
  { "kind": "mantyx", "id": "tool_cm6..." },
119
143
  { "kind": "mantyx_plugin", "name": "web_search" },
@@ -127,10 +151,68 @@ The agent spec is the body shape used by `POST /agent-runs` and `POST
127
151
  "required": ["path"],
128
152
  "additionalProperties": false
129
153
  }
154
+ },
155
+ {
156
+ "kind": "a2a",
157
+ "name": "billing_agent",
158
+ "description": "Delegate billing questions to the Acme billing agent.",
159
+ "agentCardUrl": "https://billing.acme.com/.well-known/agent-card.json",
160
+ "headers": { "Authorization": "Bearer ${BILLING_TOKEN}" },
161
+ "contextId": "ctx_abc" // optional A2A context to thread turns
162
+ },
163
+ {
164
+ "kind": "a2a_local",
165
+ "name": "intranet_hr_agent",
166
+ "agentCard": { // SDK-resolved A2A Agent Card content
167
+ "protocolVersion": "0.3.0",
168
+ "name": "Acme HR",
169
+ "description": "Answers questions about HR policies and benefits.",
170
+ "url": "https://hr.intranet.acme/a2a",
171
+ "version": "1.4.0",
172
+ "capabilities": { "streaming": false },
173
+ "skills": [
174
+ {
175
+ "id": "pto_lookup",
176
+ "name": "PTO lookup",
177
+ "description": "Find a teammate's remaining PTO days for the year."
178
+ },
179
+ {
180
+ "id": "benefits_qa",
181
+ "name": "Benefits Q&A",
182
+ "description": "Answer questions about insurance, 401k, and parental leave."
183
+ }
184
+ ]
185
+ }
186
+ },
187
+ {
188
+ "kind": "mcp",
189
+ "name": "github", // → tools become github_<tool>
190
+ "url": "https://mcp.github.com/v1",
191
+ "headers": { "Authorization": "Bearer ${GH_PAT}" },
192
+ "toolFilter": ["search_repos", "read_file"] // optional allowlist
193
+ },
194
+ {
195
+ "kind": "mcp_local",
196
+ "name": "fs", // SDK-side server label only — NOT a prefix
197
+ "serverInfo": { // optional; from MCP Initialize
198
+ "name": "mcp-server-filesystem",
199
+ "version": "0.4.1"
200
+ },
201
+ "tools": [ // verbatim MCP tools/list response
202
+ {
203
+ "name": "fs_read_file", // model-facing name, exactly as declared
204
+ "description": "Read a file from the user's workstation",
205
+ "inputSchema": { // MCP's term — JSON Schema
206
+ "type": "object",
207
+ "properties": { "path": { "type": "string" } },
208
+ "required": ["path"]
209
+ }
210
+ }
211
+ ]
130
212
  }
131
213
  ],
132
214
  "budgets": { "maxToolTurns": 32 }, // optional safety cap
133
- "metadata": { // optional, see §4.2
215
+ "metadata": { // optional, see §4.5
134
216
  "customer": "acme",
135
217
  "env": "prod"
136
218
  }
@@ -165,7 +247,232 @@ defining an ephemeral one inline. When `agentId` is set:
165
247
  Both `agentId` and `systemPrompt` may be supplied. The agent's stored prompt
166
248
  wins; the inline `systemPrompt` is ignored.
167
249
 
168
- ### 4.2 `metadata` (developer-supplied KV for filtering)
250
+ ### 4.2 A2A tool refs
251
+
252
+ A2A delegation lets the agent hand a task to another
253
+ [Agent2Agent](https://google.github.io/A2A/) peer. The wire protocol exposes
254
+ two kinds depending on **who can reach the peer**:
255
+
256
+ - `kind: "a2a"` — *remote* (server-resolved). MANTYX dials `agentCardUrl`
257
+ directly. Pick this when the peer is on the public internet or in the
258
+ same VPC as MANTYX.
259
+ - `kind: "a2a_local"` — *local* (client-resolved). The SDK invokes the peer
260
+ on its side and posts back the reply. Pick this when the peer lives on an
261
+ intranet, behind a VPN, or on the user's device — anywhere MANTYX can't
262
+ reach but the SDK can.
263
+
264
+ Both kinds present the **same** `{ "message": string }` argument shape to
265
+ the model, so an agent prompt that uses one transparently works with the
266
+ other. (This also matches MANTYX's internal `delegate_to_<name>` tools, so
267
+ models trained on one pattern carry across.)
268
+
269
+ #### `kind: "a2a"` — remote A2A
270
+
271
+ MANTYX resolves the tool server-side: when the model calls it, the worker
272
+ POSTs the model's `message` argument to `agentCardUrl` over A2A's standard
273
+ `message/send` RPC (Google ADK JSON-RPC root, A2A `/rpc`, `/message:send`,
274
+ and `/message/send` endpoints are probed in order) and forwards the remote
275
+ agent's text reply back as the tool result.
276
+
277
+ | Field | Required | Notes |
278
+ | --------------- | -------- | ----- |
279
+ | `kind` | yes | Discriminator literal `"a2a"`. |
280
+ | `name` | yes | Tool name surfaced to the model — must match `/^[a-zA-Z0-9_]{1,64}$/`. |
281
+ | `description` | no | Model-facing description. Defaults to `"Delegate a task to the <name> agent over A2A. Pass the full task as a single message."`. Mention the remote agent's purpose so the model picks it for the right turn. |
282
+ | `agentCardUrl` | yes | URL of the remote Agent Card (`/.well-known/agent-card.json`) or the JSON-RPC root the peer accepts. |
283
+ | `headers` | no | Flat string→string HTTP headers sent on every A2A request — typically `Authorization`. Each value is capped at 8 KB. |
284
+ | `contextId` | no | A2A `contextId` to thread multiple delegations into the same remote conversation. Omit for fresh per-call context. |
285
+
286
+ > **Secret handling.** `headers` are forwarded **as-is** by the SDK API. If
287
+ > you need long-lived credentials (refresh tokens, rotating API keys),
288
+ > register the peer as a workspace `ExternalAgent` instead — those headers
289
+ > support `{{secret:NAME}}` resolution against the workspace secrets store
290
+ > (see `runtime/a2a-client.ts`). The wire-protocol `a2a` ref is best for
291
+ > short-lived per-run tokens minted by your application.
292
+
293
+ #### `kind: "a2a_local"` — local A2A
294
+
295
+ > **MANTYX does no A2A work for this kind.** It does not fetch the agent
296
+ > card, validate transport, manage credentials, or speak `message/send`.
297
+ > The SDK owns the entire A2A relationship; MANTYX merely translates the
298
+ > model's `delegate_to_<name>` call into a `local_tool_call` event and
299
+ > waits for the SDK to POST back the reply text.
300
+
301
+ Per-run lifecycle:
302
+
303
+ 1. **Resolution (SDK).** Before submitting the spec, the SDK obtains the
304
+ peer's [A2A Agent Card](https://google.github.io/A2A/specification/#agent-card)
305
+ — typically by fetching `/.well-known/agent-card.json` from the local
306
+ peer, or by reading it from a config file / registry / inline constant.
307
+ 2. **Submission (SDK → MANTYX).** SDK posts the spec with the resolved
308
+ card embedded as `agentCard`. MANTYX uses the card's `name`,
309
+ `description`, and `skills[]` to compose the model-facing tool
310
+ description so the LLM understands what the peer can do.
311
+ 3. **Tool call (MANTYX → SDK).** When the model calls the tool, MANTYX
312
+ emits a `local_tool_call` event with `kind: "a2a_local"`,
313
+ `args: { message: string }`, and the **full `agentCard`** echoed back
314
+ so the SDK can route to the right local A2A handler (matching by URL,
315
+ name, skill set, or any other field).
316
+ 4. **Execution (SDK).** SDK invokes the A2A peer (its own client, its own
317
+ credentials, its own retries) and POSTs the reply text to
318
+ `POST /agent-runs/:runId/tool-results`.
319
+ 5. **Continuation (MANTYX).** MANTYX feeds the reply back into the model
320
+ loop as the tool result.
321
+
322
+ | Field | Required | Notes |
323
+ | --------------- | -------- | ----- |
324
+ | `kind` | yes | Discriminator literal `"a2a_local"`. |
325
+ | `name` | yes | Tool name surfaced to the model — must match `/^[a-zA-Z0-9_]{1,64}$/`. |
326
+ | `description` | no | Model-facing description override. When omitted, MANTYX synthesizes one from `agentCard.name`, `agentCard.description`, and the first 12 skills. |
327
+ | `agentCard` | yes | The resolved A2A Agent Card (JSON content). Schema follows the [A2A Agent Card spec](https://google.github.io/A2A/specification/#agent-card) — passthrough for unknown fields, so any spec-compliant card works. See the *Agent Card shape* table below for the fields MANTYX actually reads. |
328
+
329
+ **Agent Card shape** (only the fields MANTYX inspects; everything else is
330
+ forwarded verbatim back to the SDK):
331
+
332
+ | Card field | Used by MANTYX | Notes |
333
+ | --------------------- | -------------- | ----- |
334
+ | `protocolVersion` | echo only | A2A protocol version (e.g. `"0.3.0"`). |
335
+ | `name` | description | Used when synthesizing the tool description (`"Delegate a task to the <name> agent ..."`). |
336
+ | `description` | description | One-paragraph summary of what the peer does — surfaced to the model. |
337
+ | `url` | echo only | Peer's A2A endpoint. Forwarded back to the SDK in the `local_tool_call` event so the SDK can dispatch by URL. Never fetched server-side. |
338
+ | `version` | echo only | Peer agent version. |
339
+ | `provider` | echo only | Vendor info. |
340
+ | `capabilities` | echo only | A2A capability flags (streaming, push notifications, …). |
341
+ | `defaultInputModes` | echo only | Modalities the peer accepts. |
342
+ | `defaultOutputModes` | echo only | Modalities the peer returns. |
343
+ | `skills[]` | description | First 12 skills (`name`, `description`) are bulleted into the tool description so the model knows what to ask for. |
344
+ | `securitySchemes`, `security` | echo only | Forwarded to the SDK; MANTYX does no auth. |
345
+ | *anything else* | echo only | Passthrough — survives round-trip unchanged. |
346
+
347
+ Local A2A respects the same `localToolTimeoutMs` budget (default 5 minutes)
348
+ as `kind: "local"`. Tool-result POSTs after timeout return `409 run_terminal`.
349
+
350
+ ### 4.3 MCP tool refs
351
+
352
+ [Model Context Protocol](https://modelcontextprotocol.io/) connectors
353
+ expose every tool published by an MCP server to the agent loop in one go.
354
+ Like A2A, the protocol distinguishes by **where the server lives**:
355
+
356
+ - `kind: "mcp"` — *remote* MCP (Streamable HTTP). MANTYX has network access
357
+ to the server, dials it, lists the catalog at run start, and proxies each
358
+ call server-side. **MANTYX prefixes every discovered tool name with the
359
+ ref's `name`** (e.g. `github_search_repos`) so multiple MCP servers
360
+ can coexist without colliding.
361
+ - `kind: "mcp_local"` — *local* MCP (stdio, on-device, intranet). MANTYX
362
+ has **no** access to the server; the SDK does discovery, validation, and
363
+ execution. The SDK declares the tool catalog with **the exact names it
364
+ wants the model to see** — MANTYX does not auto-prefix.
365
+
366
+ #### `kind: "mcp"` — remote MCP
367
+
368
+ | Field | Required | Notes |
369
+ | -------------- | -------- | ----- |
370
+ | `kind` | yes | Discriminator literal `"mcp"`. |
371
+ | `name` | yes | Server label — MANTYX prefixes every discovered tool name as `<name>_<tool>`. Must match `/^[a-zA-Z0-9_]{1,64}$/`. |
372
+ | `url` | yes | Streamable HTTP MCP endpoint. |
373
+ | `headers` | no | Flat string→string HTTP headers (e.g. `Authorization`). Each value capped at 8 KB. |
374
+ | `toolFilter` | no | Allowlist of MCP tool names (un-prefixed, as the server returns them). When set, tools not in the list are silently dropped. When omitted, every published tool is exposed. |
375
+
376
+ If the MCP server is unreachable when the run starts, MANTYX still exposes
377
+ a single stub tool named `<server>_unavailable` so the model can report the
378
+ failure to the user instead of silently going without the catalog.
379
+
380
+ #### `kind: "mcp_local"` — local MCP
381
+
382
+ > **MANTYX does no MCP work for this kind.** It does not speak
383
+ > `Initialize`, `tools/list`, or `tools/call`, does not validate args,
384
+ > and does not interpret result content blocks. The SDK owns the entire
385
+ > MCP relationship — including discovery — and gives MANTYX the resolved
386
+ > tool catalog so the model can be told what's available. MANTYX is
387
+ > purely a transport.
388
+
389
+ Per-run lifecycle:
390
+
391
+ 1. **Discovery (SDK).** Before submitting the spec, the SDK connects to
392
+ its local MCP server, speaks `Initialize` (capturing the `Implementation`
393
+ block as optional `serverInfo`), then calls `tools/list`. The
394
+ resulting `Tool[]` array is shipped **verbatim** as `tools[]`.
395
+ 2. **Submission (SDK → MANTYX).** SDK posts the spec with the resolved
396
+ catalog. Field names match the MCP spec exactly — `inputSchema`, not
397
+ `parameters` — so a TypeScript SDK can pass through what its MCP client
398
+ already decoded. The `tools[].name` values are exactly what the model
399
+ will see; MANTYX does **not** auto-prefix or rename anything. Sanitize
400
+ them to `[a-zA-Z0-9_]{1,64}` yourself (if you want `fs/read_file` to
401
+ surface as `fs_read_file`, declare it that way).
402
+ 3. **Tool call (MANTYX → SDK).** When the model calls a tool, MANTYX emits
403
+ a `local_tool_call` event with `kind: "mcp_local"` and these extra
404
+ hints so the SDK can dispatch to the right MCP client:
405
+
406
+ ```jsonc
407
+ {
408
+ "seq": 9,
409
+ "type": "local_tool_call",
410
+ "data": {
411
+ "toolUseId": "tu_x",
412
+ "name": "fs_read_file", // SDK-declared name; same string the model called
413
+ "args": { "path": "/etc/hosts" },
414
+ "kind": "mcp_local",
415
+ "mcpServer": "fs", // the SDK-side label from the ref's `name`
416
+ "mcpToolName": "fs_read_file", // duplicates `name` for the SDK's convenience
417
+ "mcpServerInfo": { // present iff the ref carried `serverInfo`
418
+ "name": "mcp-server-filesystem",
419
+ "version": "0.4.1"
420
+ }
421
+ }
422
+ }
423
+ ```
424
+
425
+ 4. **Execution (SDK).** SDK validates args against the locally-known
426
+ `inputSchema`, speaks MCP `tools/call`, flattens the response content
427
+ blocks (typically the joined `text` blocks), and POSTs the result back
428
+ to `.../tool-results`.
429
+ 5. **Refresh (optional).** To pick up new tools mid-session, send the
430
+ updated `mcp_local` ref inside `POST /agent-sessions/:id/messages`'s
431
+ `tools` field; the catalog snapshot lives on the run, not the session.
432
+
433
+ | Field | Required | Notes |
434
+ | -------------- | -------- | ----- |
435
+ | `kind` | yes | Discriminator literal `"mcp_local"`. |
436
+ | `name` | yes | SDK-side server label (e.g. `"fs"`, `"jira"`). Echoed back unchanged as `mcpServer` on every `local_tool_call`. **Not used to prefix tool names.** Match `/^[a-zA-Z0-9_]{1,64}$/`. |
437
+ | `serverInfo` | no | The MCP `Implementation` block the SDK got from `Initialize` (`{ name, version? }`, plus any extra fields the server returned). Forwarded to the SDK in `local_tool_call.mcpServerInfo` for observability; not used to drive behavior. |
438
+ | `tools` | yes | Verbatim MCP `tools/list` output (1–64 entries). Each item is the standard MCP `Tool` shape: `{ name, description?, inputSchema?, annotations?, … }`. `name` is the model-facing tool name (SDK owns naming). `inputSchema` is the MCP-spec JSON Schema for the tool's arguments — used to constrain the LLM's tool call. Empty `inputSchema` means a no-arg tool. |
439
+
440
+ Older SDKs that ignore the `kind` discriminator still see a normal
441
+ `local_tool_call` and can match on `name` alone.
442
+
443
+ ### 4.4 `reasoningLevel` (provider thinking strength)
444
+
445
+ `reasoningLevel` controls how much extended-thinking / reasoning effort the
446
+ model spends per turn. MANTYX maps the same value onto every supported
447
+ provider:
448
+
449
+ - **OpenAI Responses** — `reasoning.effort` on reasoning models (o-series,
450
+ GPT-5.x, …; ignored on non-reasoning models and on xAI Grok).
451
+ - **Gemini 3+** — `thinkingConfig.thinkingLevel`; pre-Gemini-3 models
452
+ consume the equivalent `thinkingBudget` token count.
453
+ - **Anthropic / Bedrock-Anthropic** — extended thinking with a budget that
454
+ scales with strength (≈512 tokens at `low` → ≈8000 at `high`).
455
+
456
+ Two equivalent input shapes are accepted:
457
+
458
+ | Form | Values | Notes |
459
+ | ----------- | ------------------------------------- | ----- |
460
+ | **String** | `"off"`, `"low"`, `"medium"`, `"high"` | Snaps to the same anchors the web composer uses (Fast=30, Moderate=50, Smart=80; off=0). |
461
+ | **Number** | integer `0`–`100` | Pass-through to `RunAgentOptions.reasoningLevel`. `0` explicitly disables provider thinking even on reasoning models. |
462
+
463
+ When omitted, MANTYX falls back to the agent's default — for ephemeral
464
+ specs, that means thinking is off; for `agentId`-backed specs, it follows
465
+ the persisted `Agent` configuration.
466
+
467
+ For session-scoped runs the inheritance rules are:
468
+
469
+ - `POST /agent-sessions { reasoningLevel }` — sets the session-default
470
+ applied to every subsequent message run.
471
+ - `POST /agent-sessions/:id/messages { reasoningLevel }` — optional
472
+ per-message override; applies to that one run only and does not mutate
473
+ the session's stored value.
474
+
475
+ ### 4.5 `metadata` (developer-supplied KV for filtering)
169
476
 
170
477
  `metadata` is a flat string→string KV that is **persisted alongside the run /
171
478
  session** and surfaced in the MANTYX dashboard. Use it to tag runs with your
@@ -282,6 +589,11 @@ data: <utf-8 JSON>
282
589
  // streamed assistant tokens (zero or more per turn)
283
590
  { "seq": 2, "type": "assistant_delta", "data": { "text": "Hello" } }
284
591
 
592
+ // streamed reasoning / extended-thinking tokens (only when reasoningLevel > 0
593
+ // AND the active provider exposes thought parts: Anthropic extended thinking,
594
+ // Gemini `includeThoughts`, OpenAI `reasoning_content` on reasoning models).
595
+ { "seq": 2, "type": "thinking_delta", "data": { "text": "First, I should…" } }
596
+
285
597
  // completed assistant message (text + any tool calls about to execute)
286
598
  { "seq": 3, "type": "assistant_message", "data": { "text": "...", "toolCalls": [...] } }
287
599
 
@@ -289,8 +601,13 @@ data: <utf-8 JSON>
289
601
  { "seq": 4, "type": "tool_call", "data": { "toolUseId": "...", "name": "...", "input": {...} } }
290
602
  { "seq": 5, "type": "tool_result", "data": { "toolUseId": "...", "name": "...", "ok": true, "summary": "..." } }
291
603
 
292
- // LOCAL tool call — SDK MUST POST a tool-result for the same toolUseId
293
- { "seq": 6, "type": "local_tool_call", "data": { "toolUseId": "tu_x", "name": "read_file", "input": { "path": "/etc/hosts" } } }
604
+ // LOCAL tool call — SDK MUST POST a tool-result for the same toolUseId.
605
+ // `kind` carries the discriminator so the SDK can dispatch to the right
606
+ // local handler (generic registry, A2A client, or MCP client). Older SDKs
607
+ // that ignore `kind` still match on `name`.
608
+ { "seq": 6, "type": "local_tool_call", "data": { "toolUseId": "tu_x", "name": "read_file", "args": { "path": "/etc/hosts" } } }
609
+ { "seq": 6, "type": "local_tool_call", "data": { "toolUseId": "tu_y", "name": "intranet_hr_agent", "args": { "message": "When does PTO reset?" }, "kind": "a2a_local", "agentCard": { "name": "Acme HR", "url": "https://hr.intranet.acme/a2a", "skills": [ { "id": "pto_lookup", "name": "PTO lookup" } ] } } }
610
+ { "seq": 6, "type": "local_tool_call", "data": { "toolUseId": "tu_z", "name": "fs_read_file", "args": { "path": "/etc/hosts" }, "kind": "mcp_local", "mcpServer": "fs", "mcpToolName": "fs_read_file", "mcpServerInfo": { "name": "mcp-server-filesystem", "version": "0.4.1" } } }
294
611
 
295
612
  // echo of the SDK's POSTed tool-result, persisted for replay
296
613
  { "seq": 7, "type": "local_tool_result_in", "data": { "toolUseId": "tu_x", "output": "127.0.0.1 ..." } }
@@ -368,17 +685,52 @@ A reference SDK should:
368
685
 
369
686
  1. Hold the API key + workspace slug and a small `fetch` (or stdlib HTTP)
370
687
  client.
371
- 2. Maintain a registry of local tool handlers, keyed by `name`.
688
+ 2. Maintain three local-callback registries (or one tagged-union registry),
689
+ keyed by tool `name`:
690
+ - **Generic local tools** (`kind: "local"`) — caller-supplied handler
691
+ functions, dispatched by `name`.
692
+ - **Local A2A peers** (`kind: "a2a_local"`) — caller-supplied A2A
693
+ clients. Resolve the peer's Agent Card *first* (e.g. `fetch
694
+ "<peer>/.well-known/agent-card.json"` or read from a local registry),
695
+ attach it to the spec as `agentCard`, and in the dispatcher look the
696
+ client up by `agentCard.url` (or any other field you indexed on)
697
+ when the `local_tool_call` arrives.
698
+ - **Local MCP servers** (`kind: "mcp_local"`) — caller-supplied MCP
699
+ client connections. Speak `Initialize` and `tools/list` once at
700
+ setup, ship the verbatim `tools[]` (with `inputSchema`) plus
701
+ optional `serverInfo`, and dispatch incoming calls by the `mcpServer`
702
+ field in the event payload.
703
+
704
+ `mantyx`, `mantyx_plugin`, `a2a`, and `mcp` refs are server-resolved —
705
+ no SDK-side registry needed.
372
706
  3. On `runAgent` / `session.send`:
707
+ - Accept `reasoningLevel` from the caller and pass it through unchanged
708
+ (string `"off" | "low" | "medium" | "high"` *or* number `0–100`); do
709
+ **not** translate to a vendor-specific knob — the server owns that
710
+ mapping so all SDKs stay aligned with the web composer.
373
711
  - POST the run/message, get `{ runId, streamUrl }`.
374
712
  - Open the SSE stream with `Last-Event-ID` if reconnecting.
375
- - On `local_tool_call`, look up the handler, validate args against the
376
- tool's schema, run it, POST the result back to `.../tool-results`.
713
+ - On `local_tool_call`, dispatch by the event's `kind` discriminator
714
+ (defaulting to `"local"` when omitted): generic registry / local A2A
715
+ client / local MCP client. Validate args against the tool's schema,
716
+ run it, POST the result back to `.../tool-results`.
717
+ - Treat `thinking_delta` events as opt-in callback fodder; many UIs hide
718
+ them by default. Their presence depends on `reasoningLevel > 0` and
719
+ on the active model exposing thought parts.
377
720
  - On terminal `result`, resolve the call. On `error` subtype, throw.
378
721
  4. Re-emit assistant deltas/events as a stream/iterator for callers who care
379
722
  about live output.
380
723
  5. Treat the protocol as the contract. Implementation details such as Valkey
381
724
  pub/sub or pgvector are server-side only.
382
-
383
- The TypeScript SDK in [`packages/mantyx-sdk/ts/`](../packages/mantyx-sdk/ts/) and the Go SDK in
384
- [`packages/mantyx-sdk/go/`](../packages/mantyx-sdk/go/) are reference implementations of this protocol.
725
+ 6. **Execution model (server-side, informational).** Runs are executed
726
+ out-of-process by the inbound worker off a dedicated
727
+ `mantyx:agent-runs` RabbitMQ queue; the API only persists the run row and
728
+ enqueues. Nothing about this is observable on the wire — clients still see
729
+ `202 { runId, streamUrl }` followed by the same SSE vocabulary — but it
730
+ means the `local_tool_call` ↔ `tool-results` round-trip is valid across
731
+ any API or worker replica, and transient broker failures surface as a
732
+ terminal `error` event on the stream, not as a 5xx on the initial POST.
733
+
734
+ The npm package [`@mantyx/sdk`](https://www.npmjs.com/package/@mantyx/sdk) and the Go module
735
+ [`github.com/mantyx/mantyx-go-sdk`](https://github.com/mantyx/mantyx-go-sdk) are reference implementations of this protocol
736
+ (maintained in the official **mantyx-sdk** repositories).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantyx/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "MANTYX as a hosted agent runtime: define ephemeral agents, mix server-side MANTYX tools with locally-executed tools, run them remotely.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -11,6 +11,11 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
+ },
15
+ "./a2a-server": {
16
+ "types": "./dist/a2a-server.d.ts",
17
+ "import": "./dist/a2a-server.js",
18
+ "require": "./dist/a2a-server.cjs"
14
19
  }
15
20
  },
16
21
  "files": [
@@ -23,8 +28,8 @@
23
28
  ],
24
29
  "scripts": {
25
30
  "sync-version": "node ../scripts/sync-version.mjs",
26
- "build": "tsup src/index.ts --format esm,cjs --dts --clean --sourcemap",
27
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
31
+ "build": "tsup --config tsup.config.ts",
32
+ "dev": "tsup --config tsup.config.ts --watch",
28
33
  "typecheck": "tsc --noEmit",
29
34
  "test": "vitest run",
30
35
  "test:watch": "vitest",
@@ -34,10 +39,26 @@
34
39
  "node": ">=18.17.0"
35
40
  },
36
41
  "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.29.0",
37
43
  "zod": "^3.23.8"
38
44
  },
45
+ "peerDependencies": {
46
+ "@a2a-js/sdk": "^0.3.0",
47
+ "express": "^4.0.0 || ^5.0.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "@a2a-js/sdk": {
51
+ "optional": true
52
+ },
53
+ "express": {
54
+ "optional": true
55
+ }
56
+ },
39
57
  "devDependencies": {
58
+ "@a2a-js/sdk": "^0.3.0",
59
+ "@types/express": "^5.0.0",
40
60
  "@types/node": "^22.0.0",
61
+ "express": "^5.0.0",
41
62
  "tsup": "^8.3.0",
42
63
  "typescript": "^5.6.0",
43
64
  "vitest": "^2.1.0"