Fix Composio API endpoints to match verified v3 contract

Updated getTools to use the validated toolkit_slug parameter and
per-toolkit fetching. Updated executeTool to use the verified path
POST /api/v3/tools/execute/{TOOL_NAME} with entityId and arguments
fields. Updated normalizeToolSchema to use slug and input_parameters
from the actual API response format.
This commit is contained in:
ZacharyZhang-NY committed 2026-06-30 21:50:35 -04:00
1 parent 13a235a42d
commit 651e7a69ec
1 file changed
+18 -14
@@ -21,27 +21,31 @@ export class ComposioClient {
constructor(private readonly apiKey: string) {} constructor(private readonly apiKey: string) {}
async getTools(toolkits: string[]): Promise<ComposioToolSchema[]> { async getTools(toolkits: string[]): Promise<ComposioToolSchema[]> {
const params = new URLSearchParams(); const all: ComposioToolSchema[] = [];
for (const toolkit of toolkits) params.append("toolkit", toolkit); for (const toolkit of toolkits) {
params.set("limit", "50"); const params = new URLSearchParams({ toolkit_slug: toolkit, limit: "20" });
const url = `${BASE_URL}/tools?${params.toString()}`; const url = `${BASE_URL}/tools?${params.toString()}`;
const response = await this.request("GET", url); const response = await this.request("GET", url);
if (!Array.isArray(response.items)) return []; if (Array.isArray(response.items)) {
return (response.items as Array<Record<string, unknown>>).map(normalizeToolSchema); all.push(...(response.items as Array<Record<string, unknown>>).map(normalizeToolSchema));
}
}
return all;
} }
async executeTool(input: { async executeTool(input: {
toolName: string; toolName: string;
arguments: Record<string, unknown>; arguments: Record<string, unknown>;
entityId: string; entityId: string;
connectedAccountId?: string; connectedAccountId?: string;
}): Promise<ComposioToolExecutionResult> { }): Promise<ComposioToolExecutionResult> {
const url = `${BASE_URL}/tools/${encodeURIComponent(input.toolName)}/execute`; const url = `${BASE_URL}/tools/execute/${encodeURIComponent(input.toolName)}`;
const body: Record<string, unknown> = { const body: Record<string, unknown> = {
entity_id: input.entityId, entityId: input.entityId,
input: input.arguments, arguments: input.arguments,
}; };
if (input.connectedAccountId) body.connected_account_id = input.connectedAccountId; if (input.connectedAccountId) body.connectedAccountId = input.connectedAccountId;
const response = await this.request("POST", url, body); const response = await this.request("POST", url, body);
return { return {
data: typeof response === "object" && response !== null ? response as Record<string, unknown> : {}, data: typeof response === "object" && response !== null ? response as Record<string, unknown> : {},
@@ -110,11 +114,11 @@ export class ComposioClient {
} }
function normalizeToolSchema(raw: Record<string, unknown>): ComposioToolSchema { function normalizeToolSchema(raw: Record<string, unknown>): ComposioToolSchema {
const params = raw.parameters ?? raw.inputSchema ?? raw.input_schema ?? {}; const params = raw.input_parameters ?? raw.parameters ?? raw.inputSchema ?? {};
const schema = typeof params === "object" && params !== null ? params as Record<string, unknown> : {}; const schema = typeof params === "object" && params !== null ? params as Record<string, unknown> : {};
return { return {
name: String(raw.name ?? raw.slug ?? ""), name: String(raw.slug ?? raw.name ?? ""),
description: String(raw.description ?? raw.display_name ?? ""), description: String(raw.description ?? "").slice(0, 300),
parameters: { parameters: {
type: "object", type: "object",
properties: (schema.properties ?? {}) as Record<string, unknown>, properties: (schema.properties ?? {}) as Record<string, unknown>,