Skip to content
Merged
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
6 changes: 0 additions & 6 deletions .gitkeep

This file was deleted.

6 changes: 3 additions & 3 deletions plugins/composio-direct/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ Use `composio-direct` when a user asks Teleton to work with an external app supp

## Required Setup

The plugin requires the Teleton secret `composio_api_key`, using a Composio project API key, user API key, or organization API key. It can also be supplied through `COMPOSIO_DIRECT_COMPOSIO_API_KEY`, with `COMPOSIO_API_KEY` kept as a legacy fallback. If the key is missing, stop and ask the operator to configure it before attempting Composio calls.
The plugin requires the Teleton secret `composio_api_key`, using a Composio project API key or user API key. It can also be supplied through `COMPOSIO_DIRECT_COMPOSIO_API_KEY`, with `COMPOSIO_API_KEY` kept as a legacy fallback. If the key is missing, stop and ask the operator to configure it before attempting Composio calls.

Default runtime settings:

| Setting | Default | Use |
|---|---:|---|
| `base_url` | `https://backend.composio.dev/api/v3.1` | Composio API endpoint |
| `api_key_auth_scheme` | `auto` | API key header mode: `auto`, `project`, `user`, or `org` |
| `api_key_auth_scheme` | `auto` | API key header mode: `auto`, `project`, or `user` |
| `timeout_ms` | `30000` | Default request timeout |
| `max_parallel_executions` | `10` | Batch execution concurrency |
| `tool_version` | `latest` | Tool execution/schema version |
Expand Down Expand Up @@ -198,7 +198,7 @@ The plugin returns structured results:

For `auth_required`, do not retry blindly. Generate or surface a connection link, wait for user confirmation, then retry. For validation errors, fetch the schema again and correct the parameters. For transient network or 5xx failures, the plugin already retries three times with exponential backoff.

HTTP 401/403 from Composio indicates API key authentication or permission failure, not a missing external app connection. Do not call `composio_auth_link` for those errors; verify the `composio_api_key` key type, `api_key_auth_scheme`, endpoint permissions, and any Composio IP allowlist. In `auto` mode, the plugin tries `x-api-key`, then `x-user-api-key`, then `x-org-api-key` on endpoints that are not project-only.
HTTP 401/403 from Composio indicates API key authentication or permission failure, not a missing external app connection. Do not call `composio_auth_link` for those errors; verify the `composio_api_key` key type, `api_key_auth_scheme`, endpoint permissions, and any Composio IP allowlist. In `auto` mode, the plugin sends known `ak_` keys as `x-api-key` and known `uak_` keys as `x-user-api-key`; for older unknown key shapes it tries `x-api-key` then `x-user-api-key` on endpoints that support user keys. Composio organization API keys (`oak_`) are only for organization-management endpoints and cannot execute regular `composio-direct` tools.

## Security Rules

Expand Down
10 changes: 6 additions & 4 deletions plugins/composio-direct/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Composio sessions are still the preferred SDK path for new agent integrations. T

## Setup

1. Get your Composio project, user, or organization API key at <https://app.composio.dev/settings>
1. Get your Composio project or user API key at <https://app.composio.dev/settings>
2. Set the `composio_api_key` secret in Teleton:

```text
Expand All @@ -37,14 +37,16 @@ Composio sessions are still the preferred SDK path for new agent integrations. T

For container and CI deployments, Teleton also resolves the secret from `COMPOSIO_DIRECT_COMPOSIO_API_KEY`. The plugin keeps `COMPOSIO_API_KEY` as a direct fallback for older deployments.

By default `api_key_auth_scheme` is `auto`: the plugin sends the key as a project key (`x-api-key`) first and, for endpoints that accept non-project API keys, retries as `x-user-api-key` and then `x-org-api-key` on Composio 401/403 responses. Set it to `project`, `user`, or `org` only when you want to force a specific header. Project-only endpoints, such as Files and Webhooks, still use `x-api-key`.
By default `api_key_auth_scheme` is `auto`: the plugin infers known Composio key prefixes (`ak_` for project keys, `uak_` for user keys) and sends the matching header first. For older or unknown key shapes, it keeps the legacy fallback from `x-api-key` to `x-user-api-key` on endpoints that support user keys. Project-only endpoints, such as Files and Webhooks, still use `x-api-key`.

Organization API keys (`oak_`) are for Composio organization-management endpoints. They cannot execute, discover, authorize, or manage regular tools through `composio-direct`; configure a project or user API key for this plugin.

```yaml
# config.yaml example
plugins:
composio_direct:
base_url: "https://backend.composio.dev/api/v3.1" # optional
api_key_auth_scheme: "auto" # optional (auto/project/user/org)
api_key_auth_scheme: "auto" # optional (auto/project/user)
timeout_ms: 30000 # optional (default: 30s)
max_parallel_executions: 10 # optional (default: 10)
tool_version: "latest" # optional
Expand Down Expand Up @@ -485,4 +487,4 @@ node --test plugins/composio-direct/test/unit/composio-direct.test.js \
- Meta-tool alignment: `composio_search_tools`, `composio_get_tool_schemas`, `composio_multi_execute`, connection/auth tools, `composio_manage_connections`, `composio_remote_bash`, and `composio_remote_workbench` cover the practical `search_tools`, `get_tool_schemas`, `multi_execute_tool`, `manage_connections`, `remote_bash_tool`, and `remote_workbench` flows for Teleton.
- Custom provider alignment: Teleton consumes the static plugin tools exported here, so the plugin implements the Composio provider transform/execute/helper contract over REST instead of importing `@composio/core` provider classes.
- Sessions note: Composio recommends sessions for SDK agents, while this direct plugin keeps the explicit `/tools`, `/tools/execute`, auth, connection, trigger, webhook, and file routes available to Teleton.
- HTTP 401/403 responses are reported as Composio API key access failures, not as `auth_required` service authorization. Check the project/user/org key type, `api_key_auth_scheme`, endpoint permissions, and any Composio IP allowlist before retrying.
- HTTP 401/403 responses are reported as Composio API key access failures, not as `auth_required` service authorization. Check the project/user key type, `api_key_auth_scheme`, endpoint permissions, and any Composio IP allowlist before retrying.
137 changes: 116 additions & 21 deletions plugins/composio-direct/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
*
* Authentication:
* - Requires a Composio API key stored in sdk.secrets as "composio_api_key"
* - Supports project API keys (x-api-key), user API keys (x-user-api-key),
* and organization API keys (x-org-api-key)
* - Supports project API keys (x-api-key) and user API keys (x-user-api-key)
* for tool/auth/connection endpoints. Organization API keys are valid only
* for Composio organization-management endpoints and cannot execute tools.
* - Set COMPOSIO_DIRECT_COMPOSIO_API_KEY, COMPOSIO_API_KEY, or use the secrets store
*
* Transport:
Expand Down Expand Up @@ -67,7 +68,7 @@

export const manifest = {
name: "composio-direct",
version: "1.9.4",
version: "1.9.5",
sdkVersion: ">=1.0.0",
description:
"Direct access to 1000+ Composio automation tools plus v3.1 toolkits, files, triggers, webhooks, connection reuse, and meta-tools without MCP transport",
Expand All @@ -76,7 +77,7 @@
required: true,
env: "COMPOSIO_DIRECT_COMPOSIO_API_KEY",
description:
"Composio project, user, or organization API key (create at https://app.composio.dev/settings)",
"Composio project or user API key (create at https://app.composio.dev/settings)",
},
},
defaultConfig: {
Expand Down Expand Up @@ -150,20 +151,94 @@

/**
* Composio v3.1 accepts project keys on x-api-key and user keys on
* x-user-api-key for most user-facing endpoints. Composio also exposes
* organization keys on x-org-api-key for org-scoped access. In auto mode, try
* the project header first to preserve existing behavior, then retry with
* user and organization headers only when the endpoint is not project-only and
* Composio rejected access.
* x-user-api-key for most user-facing endpoints. Organization keys on
* x-org-api-key are reserved for /org/* management endpoints in the OpenAPI
* schema and are not valid credentials for tool execution or discovery.
* In auto mode, infer known key prefixes first; for unknown/legacy key shapes,
* try project first to preserve existing behavior, then user when supported.
* @param {"auto" | "project" | "user" | "org"} apiKeyAuthScheme
* @param {boolean} supportsNonProjectApiKeys
* @param {boolean} supportsUserApiKeys
* @param {boolean} supportsOrgApiKeys
* @param {string} [apiKey]
* @returns {Array<"project" | "user" | "org">}
*/
function getApiKeyAuthAttempts(apiKeyAuthScheme, supportsNonProjectApiKeys) {
function getApiKeyAuthAttempts(
apiKeyAuthScheme,
supportsUserApiKeys,
supportsOrgApiKeys,
apiKey
) {
if (apiKeyAuthScheme === "project") return ["project"];
if (apiKeyAuthScheme === "user") return ["user"];
if (apiKeyAuthScheme === "org") return ["org"];
return supportsNonProjectApiKeys ? ["project", "user", "org"] : ["project"];
if (apiKeyAuthScheme === "user") return supportsUserApiKeys ? ["user"] : [];
if (apiKeyAuthScheme === "org") return supportsOrgApiKeys ? ["org"] : [];

const detectedScheme = detectApiKeyAuthScheme(apiKey);
if (detectedScheme === "project") return ["project"];
if (detectedScheme === "user") return supportsUserApiKeys ? ["user"] : [];
if (detectedScheme === "org") return supportsOrgApiKeys ? ["org"] : [];

return supportsUserApiKeys ? ["project", "user"] : ["project"];
}

/**
* Infer Composio API-key kind from stable key prefixes described by Composio.
* Unknown shapes keep legacy project->user auto fallback behavior.
* @param {string | null | undefined} apiKey
* @returns {"project" | "user" | "org" | null}
*/
function detectApiKeyAuthScheme(apiKey) {
const key = String(apiKey ?? "").trim().toLowerCase();
if (key.startsWith("uak_")) return "user";
if (key.startsWith("oak_")) return "org";
if (key.startsWith("ak_")) return "project";
return null;
}

/**
* Build a local response when a configured key/header scheme is unsupported
* for the current Composio endpoint. This avoids a misleading remote 403.
* @param {"auto" | "project" | "user" | "org"} apiKeyAuthScheme
* @param {string} apiKey
* @param {boolean} supportsUserApiKeys
* @param {boolean} supportsOrgApiKeys
* @returns {{ status: number; data: unknown }}
*/
function unsupportedApiKeyAuthResponse(
apiKeyAuthScheme,
apiKey,
supportsUserApiKeys,
supportsOrgApiKeys

Check warning on line 210 in plugins/composio-direct/index.js

View workflow job for this annotation

GitHub Actions / Lint

'supportsOrgApiKeys' is defined but never used. Allowed unused args must match /^_/u
) {
const detectedScheme = detectApiKeyAuthScheme(apiKey);
const scheme = apiKeyAuthScheme === "auto" ? detectedScheme : apiKeyAuthScheme;
let message =
"Configured Composio API key auth scheme is not supported by this endpoint.";
let suggestedFix =
"Use a Composio project API key (ak_...) for project-only endpoints, or a user API key (uak_...) for regular tool/auth/connection endpoints.";

if (scheme === "org") {
message =
"Composio organization API keys (oak_...) are only valid for organization-management endpoints and cannot execute or discover tools through composio-direct.";
suggestedFix =
"Set composio_api_key to a Composio project or user API key (ak_... or uak_...) for composio-direct tools.";
} else if (scheme === "user" && !supportsUserApiKeys) {
message =
"This Composio endpoint accepts only project API keys; the configured user API key cannot be used here.";
suggestedFix =
"Set composio_api_key to a Composio project API key (ak_...) for this endpoint.";
}

return {
status: 400,
data: {
error: {
message,
slug: "UNSUPPORTED_COMPOSIO_API_KEY_AUTH_SCHEME",
status: 400,
suggested_fix: suggestedFix,
},
},
};
}

/**
Expand Down Expand Up @@ -298,15 +373,15 @@
if (response.status === 403) {
return (
"Composio API key permission denied. Check that composio_api_key is a " +
"project, user, or organization API key with permissions for this endpoint, that " +
"project or user API key with permissions for this endpoint, that " +
"api_key_auth_scheme matches the key type when set explicitly, and that " +
`any Composio IP allowlist includes this runtime. Composio response: ${message}`
);
}

return (
"Composio API key was rejected. Check that composio_api_key is a valid " +
`project, user, or organization API key. Composio response: ${message}`
`project or user API key. Composio response: ${message}`
);
}

Expand Down Expand Up @@ -912,7 +987,7 @@
return {
success: false,
error:
"Composio API key is not configured. Please set the composio_api_key secret or COMPOSIO_DIRECT_COMPOSIO_API_KEY with your project, user, or organization key from https://app.composio.dev/settings",
"Composio API key is not configured. Please set the composio_api_key secret or COMPOSIO_DIRECT_COMPOSIO_API_KEY with your project or user key from https://app.composio.dev/settings",
};
}

Expand All @@ -925,6 +1000,8 @@
* @param {unknown} [opts.body]
* @param {number} opts.timeoutMs
* @param {boolean} [opts.supportsNonProjectApiKeys]
* @param {boolean} [opts.supportsUserApiKeys]
* @param {boolean} [opts.supportsOrgApiKeys]
* @returns {Promise<{ status: number; data: unknown }>}
*/
async function requestComposio({
Expand All @@ -933,14 +1010,27 @@
method,
body,
timeoutMs,
supportsNonProjectApiKeys = true,
supportsNonProjectApiKeys,
supportsUserApiKeys = supportsNonProjectApiKeys ?? true,
supportsOrgApiKeys = false,
}) {
const { apiKeyAuthScheme } = getConfig();
const authAttempts = getApiKeyAuthAttempts(
apiKeyAuthScheme,
supportsNonProjectApiKeys
supportsUserApiKeys,
supportsOrgApiKeys,
apiKey
);

if (authAttempts.length === 0) {
return unsupportedApiKeyAuthResponse(
apiKeyAuthScheme,
apiKey,
supportsUserApiKeys,
supportsOrgApiKeys
);
}

let response = null;
let apiAccessErrorResponse = null;
for (let index = 0; index < authAttempts.length; index++) {
Expand Down Expand Up @@ -986,6 +1076,8 @@
* @param {unknown} [opts.body]
* @param {number} [opts.timeoutMs]
* @param {boolean} [opts.supportsNonProjectApiKeys]
* @param {boolean} [opts.supportsUserApiKeys]
* @param {boolean} [opts.supportsOrgApiKeys]
* @returns {Promise<{ status: number; data: unknown }>}
*/
function callComposio({
Expand All @@ -995,7 +1087,9 @@
query,
body,
timeoutMs,
supportsNonProjectApiKeys = true,
supportsNonProjectApiKeys,
supportsUserApiKeys = supportsNonProjectApiKeys ?? true,
supportsOrgApiKeys = false,
}) {
const cfg = getConfig();
return requestComposio({
Expand All @@ -1004,7 +1098,8 @@
method,
body,
timeoutMs: timeoutMs ?? cfg.timeoutMs,
supportsNonProjectApiKeys,
supportsUserApiKeys,
supportsOrgApiKeys,
});
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/composio-direct/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "composio-direct",
"name": "Composio Direct",
"version": "1.9.4",
"version": "1.9.5",
"description": "Direct access to 1000+ Composio automation tools plus v3.1 toolkits, files, triggers, webhooks, connection reuse, and meta-tools without MCP transport",
"author": {
"name": "xlabtg",
Expand All @@ -15,7 +15,7 @@
"composio_api_key": {
"required": true,
"env": "COMPOSIO_DIRECT_COMPOSIO_API_KEY",
"description": "Composio project, user, or organization API key (create at https://app.composio.dev/settings)"
"description": "Composio project or user API key (create at https://app.composio.dev/settings)"
}
},
"defaultConfig": {
Expand Down
2 changes: 1 addition & 1 deletion plugins/composio-direct/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "teleton-plugin-composio-direct",
"type": "module",
"version": "1.9.4",
"version": "1.9.5",
"private": true,
"description": "Teleton plugin for direct Composio API access"
}
2 changes: 1 addition & 1 deletion plugins/composio-direct/test/unit/composio-direct.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ describe("manifest", () => {
assert.ok(manifest.name, "manifest.name is set");
assert.ok(manifest.version, "manifest.version is set");
assert.ok(manifest.secrets?.composio_api_key, "secret composio_api_key declared");
assert.equal(manifest.version, "1.9.4");
assert.equal(manifest.version, "1.9.5");
assert.equal(manifest.defaultConfig?.base_url, "https://backend.composio.dev/api/v3.1");
assert.equal(manifest.defaultConfig?.api_key_auth_scheme, "auto");
});
Expand Down
Loading
Loading