Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const composioPlugin = {
return;
}

if (!config.consumerKey) {
if (!config.consumerKey && !config.apiKey) {
api.logger.warn(
"[composio] No consumer key configured. Set COMPOSIO_CONSUMER_KEY env var or plugins.composio.consumerKey in config. Get your key (ck_...) from dashboard.composio.dev"
"[composio] No credentials configured. Set COMPOSIO_CONSUMER_KEY (ck_...) or COMPOSIO_API_KEY (ak_...) env var, or set consumerKey/apiKey in plugin config. Get your key from dashboard.composio.dev"
);
return;
}
Expand Down Expand Up @@ -75,8 +75,22 @@ Do NOT use any pretrained knowledge about Composio APIs or SDKs.
</composio>`,
}));

// Build MCP URL — append user_id if using apiKey mode with a userId
let mcpUrl = config.mcpUrl;
if (config.apiKey && config.userId && !mcpUrl.includes("user_id=")) {
const separator = mcpUrl.includes("?") ? "&" : "?";
mcpUrl = `${mcpUrl}${separator}user_id=${encodeURIComponent(config.userId)}`;
}

// Choose auth header based on which credential is provided
// apiKey (ak_...) uses x-api-key for backend API / tool router endpoints
// consumerKey (ck_...) uses x-consumer-api-key for connect.composio.dev
const authHeaders: Record<string, string> = config.apiKey
? { "x-api-key": config.apiKey }
: { "x-consumer-api-key": config.consumerKey };

// Fire MCP connection in the background (not awaited)
api.logger.info(`[composio] Connecting to ${config.mcpUrl}`);
api.logger.info(`[composio] Connecting to ${mcpUrl}`);

void (async () => {
try {
Expand All @@ -87,9 +101,9 @@ Do NOT use any pretrained knowledge about Composio APIs or SDKs.

const mcpClient = new Client({ name: "openclaw", version: "1.0" });
await mcpClient.connect(
new StreamableHTTPClientTransport(new URL(config.mcpUrl), {
new StreamableHTTPClientTransport(new URL(mcpUrl), {
requestInit: {
headers: { "x-consumer-api-key": config.consumerKey },
headers: authHeaders,
},
})
);
Expand Down
20 changes: 19 additions & 1 deletion openclaw.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@
"type": "string",
"description": "Your Composio consumer key (ck_...) from dashboard.composio.dev/settings"
},
"apiKey": {
"type": "string",
"description": "Your Composio API key (ak_...) for backend API / tool router endpoints. Use instead of consumerKey for per-user support."
},
"mcpUrl": {
"type": "string",
"description": "Composio MCP server URL (default: https://connect.composio.dev/mcp)"
"description": "Composio MCP server URL (default: https://connect.composio.dev/mcp). For per-user: use a tool router URL."
},
"userId": {
"type": "string",
"description": "Per-user identifier for scoping connected accounts. Appended as ?user_id= when using apiKey."
}
}
},
Expand All @@ -33,10 +41,20 @@
"help": "Your Composio consumer key (ck_...) from dashboard.composio.dev/settings",
"sensitive": true
},
"apiKey": {
"label": "API Key",
"help": "Your Composio API key (ak_...) for per-user tool router sessions",
"sensitive": true
},
"mcpUrl": {
"label": "MCP Server URL",
"help": "Composio MCP server URL (default: https://connect.composio.dev/mcp)",
"advanced": true
},
"userId": {
"label": "User ID",
"help": "Per-user identifier for scoping connected accounts",
"advanced": true
}
}
}
31 changes: 28 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import type { ComposioConfig } from "./types.js";
export const ComposioConfigSchema = z.object({
enabled: z.boolean().default(true),
consumerKey: z.string().default(""),
apiKey: z.string().default(""),
mcpUrl: z.string().default("https://connect.composio.dev/mcp"),
userId: z.string().default(""),
});

export function parseComposioConfig(value: unknown): ComposioConfig {
Expand All @@ -21,12 +23,25 @@ export function parseComposioConfig(value: unknown): ComposioConfig {
process.env.COMPOSIO_CONSUMER_KEY ||
"";

const apiKey =
(typeof configObj?.apiKey === "string" && configObj.apiKey.trim()) ||
(typeof raw.apiKey === "string" && raw.apiKey.trim()) ||
process.env.COMPOSIO_API_KEY ||
"";

const mcpUrl =
(typeof configObj?.mcpUrl === "string" && configObj.mcpUrl.trim()) ||
(typeof raw.mcpUrl === "string" && raw.mcpUrl.trim()) ||
process.env.COMPOSIO_MCP_URL ||
"https://connect.composio.dev/mcp";

return ComposioConfigSchema.parse({ ...raw, consumerKey, mcpUrl });
const userId =
(typeof configObj?.userId === "string" && configObj.userId.trim()) ||
(typeof raw.userId === "string" && raw.userId.trim()) ||
process.env.COMPOSIO_USER_ID ||
"";

return ComposioConfigSchema.parse({ ...raw, consumerKey, apiKey, mcpUrl, userId });
}

export const composioPluginConfigSchema = {
Expand All @@ -38,12 +53,22 @@ export const composioPluginConfigSchema = {
},
consumerKey: {
label: "Consumer Key",
help: "Your Composio consumer key (ck_...) from dashboard.composio.dev/settings",
help: "Your Composio consumer key (ck_...) from dashboard.composio.dev/settings. Used with the default connect.composio.dev endpoint.",
sensitive: true,
},
apiKey: {
label: "API Key",
help: "Your Composio API key (ak_...) for the backend API. Use this instead of consumerKey for per-user tool router sessions.",
sensitive: true,
},
mcpUrl: {
label: "MCP Server URL",
help: "Composio MCP server URL (default: https://connect.composio.dev/mcp)",
help: "Composio MCP server URL. For per-user: use a tool router URL like https://backend.composio.dev/tool_router/trs_XXX/mcp",
advanced: true,
},
userId: {
label: "User ID",
help: "Per-user identifier for scoping connected accounts. Appended as ?user_id= when using the backend API with apiKey.",
advanced: true,
},
},
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export interface ComposioConfig {
enabled: boolean;
consumerKey: string;
apiKey: string;
mcpUrl: string;
userId: string;
}