Tools, execute and send
The session object and the methods that list, run and talk to tools: tools, findTools, execute, proxyExecute, send, sendStream.
Session
What create returns. A session is one user's connection to a set of servers; every method below sends that session's id, so the backend can bill, audit and scope each call.
const = await .("user_0000", { : "brazilian" });
.; // "ses_..."
.; // "active" | "closed" | "error"
.; // { url, headers }: the session as a tool endpoint, what @codespar/mcp emits config forThe object create builds also carries userId, servers (the ids the preset expanded to) and createdAt (a Date), but the Session interface in @codespar/types declares only id, status and mcp; reading the other three needs a cast, and the compiled example above stops at what the type knows.
Two of the methods below, tools and findTools, are on the object but not on the Session interface in @codespar/types, so session.tools() does not type-check without a cast. The free functions of the same name reach them by duck typing, take any SessionBase, and are the typed way to call them; the compiled examples below use them.
tools
tools(): Promise<Tool[]>Every tool the session can call: the meta-tools plus each connected server's tools. Cached after the first call; connections refreshes the cache.
No parameters.
const = await ();
for (const of ) .(., .);interface Tool {
/** e.g. "codespar_pay" */
name: string;
/** Human-readable, shown to LLMs */
description: string;
/** JSON Schema for the tool's input */
input_schema: Record<string, unknown>;
/** The server that provides it; "codespar" for meta-tools */
server: string;
}Throws nothing on a failed refresh: connections swallows transport errors and this returns the cached list, or [] when there is none.
Related REST GET /v1/sessions/{id}/connections, which carries the tool list.
findTools
findTools(intent: string): Promise<Tool[]>The subset of tools() whose name or description contains intent, case-insensitively. A substring match, not a search: for a ranked, catalog-wide search use discover.
| Parameter | Type | Required | Description |
|---|---|---|---|
intent | string | yes | The substring to look for in tool names and descriptions |
const = await (, "pix");// The same Tool shape as tools(), filtered.
Tool[]Throws nothing beyond what tools() throws.
execute
execute(toolName: string, params: Record<string, unknown>): Promise<ToolResult>Runs whatever tool you name; a payment tool moves money.
Runs one tool by name, meta-tool or catalog tool, through POST /v1/sessions/{id}/execute. The result is a ToolResult envelope either way: a tool failure and a non-2xx HTTP answer both come back as success: false with the reason in error, so a caller branches on the envelope rather than catching.
| Parameter | Type | Required | Description |
|---|---|---|---|
toolName | string | yes | codespar_pay, or a catalog tool as the server's tool name |
params | Record<string, unknown> | yes | The tool's input, matching its input_schema |
const = await .("codespar_wallet", { : "balance" });
if (.) {
.(.);
} else {
.(.);
}interface ToolResult {
success: boolean;
data: unknown;
error: string | null;
/** ms */
duration: number;
server: string;
tool: string;
tool_call_id?: string;
called_at?: string;
}Throws a CodesparApiError with status: 0 when the request never reaches the backend, and a TimeoutError on timeout. An HTTP error status does not throw: it is { success: false, error: "<status>: <body>" }. When the tool refuses for a governed reason, data carries a coded output; the tool-result guards narrow it.
Related Meta-tools for the names and inputs, REST POST /v1/sessions/{id}/execute.
proxyExecute
proxyExecute(request: ProxyRequest): Promise<ProxyResult>Calls the provider's own API with the tenant's stored credentials; what it does is whatever that endpoint does.
A raw HTTP call to a connected server's upstream API, with the backend injecting the stored credential. For the endpoints a provider has that the catalog does not wrap. Never send provider keys in headers; the backend adds them.
| Parameter | Type | Required | Description |
|---|---|---|---|
request | ProxyRequest | yes | Server id, endpoint path on the provider, method, and optional body, query and headers |
const = await .({
: "asaas",
: "/v3/customers",
: "GET",
: { : 10 },
});
.(., .);interface ProxyRequest {
server: string;
endpoint: string;
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
body?: unknown;
params?: Record<string, string | number | boolean>;
headers?: Record<string, string>;
}
interface ProxyResult {
status: number;
data: unknown;
headers: Record<string, string>;
/** ms */
duration: number;
proxy_call_id?: string;
}Throws a CodesparApiError when the CodeSpar backend answers non-2xx or cannot be reached; the upstream provider's own status comes back as a value in status. A TimeoutError on timeout.
Related REST POST /v1/sessions/{id}/proxy_execute.
send
send(message: string): Promise<SendResult>Drives a tool-use loop that may call any tool of the session, payment tools included.
One natural-language turn. The backend runs a model tool-use loop over the session's tools and resolves when the loop finishes, with the final text and every tool call it made.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The user's message |
const = await .("Charge R$150 via Pix and issue the NF-e");
.(.);
for (const of .) .(., .);interface SendResult {
message: string;
tool_calls: ToolCallRecord[];
iterations: number;
}
interface ToolCallRecord {
id: string;
tool_name: string;
server_id: string;
status: "success" | "error";
duration_ms: number;
input: unknown;
output: unknown;
error_code: string | null;
}Throws a CodesparApiError on a non-2xx answer or a transport failure, a TimeoutError when the whole turn exceeds the timeout.
Related sendStream for the same turn as events, REST POST /v1/sessions/{id}/send.
sendStream
sendStream(message: string): AsyncIterable<StreamEvent>The same loop as send(), streamed.
The same turn as send, as it happens. Sends Accept: text/event-stream to the same route and yields one typed event per SSE frame. The timeout is an idle timeout that resets on every complete frame, so a long turn that keeps producing frames does not time out.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The user's message |
for await (const of .("Charge R$150 via Pix")) {
if (. === "assistant_text") ..(.);
if (. === "tool_result") .(..);
if (. === "done") .(..);
}type StreamEvent =
| { type: "user_message"; content: string }
| { type: "assistant_text"; content: string; iteration: number }
| { type: "tool_use"; id: string; name: string; input: Record<string, unknown> }
| { type: "tool_result"; toolCall: ToolCallRecord }
| { type: "done"; result: SendResult }
| { type: "error"; error: string; message?: string };Throws a CodesparApiError when the response is not 2xx or the transport fails, a TimeoutError when no complete frame arrives within the timeout. An error event is a value, not a throw.
Related send, REST POST /v1/sessions/{id}/send.