@customclaw/composio 0.0.5 → 0.0.6

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.
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from "vitest";
2
2
  import { ComposioClient } from "./client.js";
3
3
  import { parseComposioConfig } from "./config.js";
4
4
  import { createComposioExecuteTool } from "./tools/execute.js";
5
+ import { createComposioConnectionsTool } from "./tools/connections.js";
5
6
  // Mock the Composio SDK
6
7
  vi.mock("@composio/core", () => ({
7
8
  Composio: vi.fn().mockImplementation(() => ({
@@ -15,6 +16,7 @@ vi.mock("@composio/core", () => ({
15
16
  { slug: "gmail", name: "Gmail", connection: { isActive: true } },
16
17
  { slug: "sentry", name: "Sentry", connection: { isActive: false } },
17
18
  { slug: "github", name: "GitHub", connection: { isActive: true } },
19
+ { slug: "affinity", name: "Affinity", connection: { isActive: false } },
18
20
  ],
19
21
  }),
20
22
  experimental: { assistivePrompt: "" },
@@ -182,3 +184,43 @@ describe("execute tool string arguments (GLM-5 workaround)", () => {
182
184
  expect(result.details).toHaveProperty("success", true);
183
185
  });
184
186
  });
187
+ describe("connections tool", () => {
188
+ function makeConnectionsTool() {
189
+ const client = makeClient();
190
+ const config = parseComposioConfig({ config: { apiKey: "test-key" } });
191
+ return createComposioConnectionsTool(client, config);
192
+ }
193
+ it("list action passes user_id to client", async () => {
194
+ const tool = makeConnectionsTool();
195
+ const result = await tool.execute("test", { action: "list", user_id: "custom-user" });
196
+ const details = result.details;
197
+ expect(details).toHaveProperty("action", "list");
198
+ expect(details.toolkits).toBeInstanceOf(Array);
199
+ });
200
+ it("status probes API-key toolkit and flips to connected on success", async () => {
201
+ const tool = makeConnectionsTool();
202
+ const result = await tool.execute("test", { action: "status", toolkit: "affinity" });
203
+ const details = result.details;
204
+ const conn = details.connections.find((c) => c.toolkit === "affinity");
205
+ expect(conn.connected).toBe(true);
206
+ });
207
+ it("status does not probe toolkits without a defined probe", async () => {
208
+ const tool = makeConnectionsTool();
209
+ const result = await tool.execute("test", { action: "status", toolkit: "sentry" });
210
+ const details = result.details;
211
+ const conn = details.connections.find((c) => c.toolkit === "sentry");
212
+ expect(conn.connected).toBe(false);
213
+ });
214
+ it("status keeps disconnected when probe fails", async () => {
215
+ const tool = makeConnectionsTool();
216
+ // Get the latest Composio instance (the one this tool's client is using)
217
+ const { Composio } = await import("@composio/core");
218
+ const mockResults = Composio.mock.results;
219
+ const instance = mockResults[mockResults.length - 1].value;
220
+ instance.client.tools.execute.mockRejectedValueOnce(new Error("probe failed"));
221
+ const result = await tool.execute("test", { action: "status", toolkit: "affinity" });
222
+ const details = result.details;
223
+ const conn = details.connections.find((c) => c.toolkit === "affinity");
224
+ expect(conn.connected).toBe(false);
225
+ });
226
+ });
package/dist/index.js CHANGED
@@ -73,22 +73,6 @@ const composioPlugin = {
73
73
  config,
74
74
  logger: api.logger,
75
75
  }), { commands: ["composio"] });
76
- // Inject agent instructions via before_agent_start hook
77
- api.on("before_agent_start", () => {
78
- return {
79
- prependContext: `<composio-tools>
80
- You have access to Composio tools for third-party integrations (Gmail, Sentry, etc.).
81
-
82
- ## Usage
83
- 1. Use \`composio_search_tools\` to find tools and their parameter schemas.
84
- 2. Use \`composio_manage_connections\` with action="status" to check if a toolkit is connected. Use action="create" to generate an auth URL if needed.
85
- 3. Use \`composio_execute_tool\` with the tool_slug and arguments from search results.
86
-
87
- Always search first to get the correct parameter schema before executing a tool.
88
- Tool slugs are uppercase. If a tool fails with auth errors, prompt the user to connect the toolkit.
89
- </composio-tools>`,
90
- };
91
- });
92
76
  api.logger.info("[composio] Plugin registered with 3 tools and CLI commands");
93
77
  },
94
78
  };
@@ -1,4 +1,10 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ const CONNECTION_PROBES = {
3
+ affinity: {
4
+ toolSlug: "AFFINITY_GET_METADATA_ON_ALL_LISTS",
5
+ args: { limit: 1 },
6
+ },
7
+ };
2
8
  /**
3
9
  * Tool parameters for composio_manage_connections
4
10
  */
@@ -23,8 +29,9 @@ export function createComposioConnectionsTool(client, _config) {
23
29
  return {
24
30
  name: "composio_manage_connections",
25
31
  label: "Composio Manage Connections",
26
- description: "Manage Composio toolkit connections. Check connection status, create new auth links, " +
27
- "or list available toolkits. Use this before executing tools to ensure authentication.",
32
+ description: "Manage Composio toolkit connections. Use action='status' to check if a toolkit is connected, " +
33
+ "action='create' to generate an auth URL when disconnected, or action='list' to see available toolkits. " +
34
+ "Check connection status before executing tools with composio_execute_tool.",
28
35
  parameters: ComposioManageConnectionsToolSchema,
29
36
  async execute(_toolCallId, params) {
30
37
  const action = String(params.action || "status");
@@ -32,7 +39,7 @@ export function createComposioConnectionsTool(client, _config) {
32
39
  try {
33
40
  switch (action) {
34
41
  case "list": {
35
- const toolkits = await client.listToolkits();
42
+ const toolkits = await client.listToolkits(userId);
36
43
  const response = {
37
44
  action: "list",
38
45
  count: toolkits.length,
@@ -82,6 +89,25 @@ export function createComposioConnectionsTool(client, _config) {
82
89
  toolkitsToCheck = params.toolkits.filter((t) => typeof t === "string" && t.trim() !== "");
83
90
  }
84
91
  const statuses = await client.getConnectionStatus(toolkitsToCheck, userId);
92
+ // Fallback probe for API-key style integrations where
93
+ // connection.isActive can be false despite successful tool execution
94
+ if (toolkitsToCheck && toolkitsToCheck.length > 0) {
95
+ for (const status of statuses) {
96
+ if (status.connected)
97
+ continue;
98
+ const probe = CONNECTION_PROBES[String(status.toolkit || "").toLowerCase()];
99
+ if (!probe)
100
+ continue;
101
+ try {
102
+ const probeResult = await client.executeTool(probe.toolSlug, probe.args, userId);
103
+ if (probeResult?.success)
104
+ status.connected = true;
105
+ }
106
+ catch {
107
+ // keep false if probe fails
108
+ }
109
+ }
110
+ }
85
111
  const response = {
86
112
  action: "status",
87
113
  count: statuses.length,
@@ -20,8 +20,9 @@ export function createComposioExecuteTool(client, _config) {
20
20
  return {
21
21
  name: "composio_execute_tool",
22
22
  label: "Composio Execute Tool",
23
- description: "Execute a single Composio tool. Use composio_search_tools first to find the tool slug " +
24
- "and parameter schema. The tool must be connected (use composio_manage_connections to check).",
23
+ description: "Execute a single Composio tool. Use composio_search_tools first to find the UPPERCASE tool slug " +
24
+ "and parameter schema. The toolkit must be connected use composio_manage_connections to check " +
25
+ "status or create an auth link. If execution fails with auth errors, prompt the user to reconnect.",
25
26
  parameters: ComposioExecuteToolSchema,
26
27
  async execute(_toolCallId, params) {
27
28
  const toolSlug = String(params.tool_slug || "").trim();
@@ -23,8 +23,9 @@ export function createComposioSearchTool(client, _config) {
23
23
  return {
24
24
  name: "composio_search_tools",
25
25
  label: "Composio Search Tools",
26
- description: "Search for tools across 1000+ integrations (Gmail, Slack, GitHub, Notion, etc.) " +
27
- "by describing what you want to accomplish. Returns matching tools with their schemas.",
26
+ description: "Search for Composio tools across 1000+ integrations (Gmail, Slack, GitHub, Notion, etc.) " +
27
+ "by describing what you want to accomplish. Returns matching tools with their UPPERCASE slugs and parameter schemas. " +
28
+ "Always search first before executing a tool with composio_execute_tool.",
28
29
  parameters: ComposioSearchToolSchema,
29
30
  async execute(_toolCallId, params) {
30
31
  const query = String(params.query || "").trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customclaw/composio",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "description": "Composio Tool Router plugin for OpenClaw — access 1000+ third-party integrations",
6
6
  "main": "dist/index.js",