@withone/mcp 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/build/client.d.ts +102 -0
- package/build/client.js +362 -0
- package/build/helpers.d.ts +53 -0
- package/build/helpers.js +150 -0
- package/build/index.d.ts +10 -0
- package/build/index.js +281 -0
- package/build/schemas.d.ts +285 -0
- package/build/schemas.js +110 -0
- package/build/server.d.ts +27 -0
- package/build/server.js +215 -0
- package/build/types.d.ts +182 -0
- package/build/types.js +9 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 One Systems, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<img src="https://assets.withone.ai/banners/mcp.png?v=1" alt="One MCP — Connect your agents to every app through a single MCP." style="border-radius: 5px;">
|
|
2
|
+
|
|
3
|
+
<h3 align="center">One MCP Server</h3>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://withone.ai"><strong>Website</strong></a>
|
|
7
|
+
·
|
|
8
|
+
<a href="https://withone.ai/docs"><strong>Docs</strong></a>
|
|
9
|
+
·
|
|
10
|
+
<a href="https://app.withone.ai"><strong>Dashboard</strong></a>
|
|
11
|
+
·
|
|
12
|
+
<a href="https://withone.ai/changelog"><strong>Changelog</strong></a>
|
|
13
|
+
·
|
|
14
|
+
<a href="https://x.com/withoneai"><strong>X</strong></a>
|
|
15
|
+
·
|
|
16
|
+
<a href="https://linkedin.com/company/withoneai"><strong>LinkedIn</strong></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://npmjs.com/package/@withone/mcp"><img src="https://img.shields.io/npm/v/%40withone%2Fmcp" alt="npm version"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
Connect your AI agents to 250+ apps through a single [MCP](https://modelcontextprotocol.io) server. Search for actions, read documentation, and execute API calls across platforms, without having to manage OAuth tokens or API keys.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g @withone/cli
|
|
27
|
+
one init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. The [One CLI](https://www.npmjs.com/package/@withone/cli) will prompt you for your API key (get one from the [One dashboard](https://app.withone.ai/settings/api-keys)) and configure the MCP server for your environment: Claude Desktop, Cursor, Claude Code, Windsurf, or any MCP-compatible agent.
|
|
31
|
+
|
|
32
|
+
## Capabilities
|
|
33
|
+
|
|
34
|
+
- **250+ platforms.** Gmail, Slack, Shopify, HubSpot, Stripe, Linear, QuickBooks, and [more](https://app.withone.ai/tools).
|
|
35
|
+
- **Natural language execution.** "read my last gmail email", "send a message to #general on Slack"
|
|
36
|
+
- **Code generation.** "build a form to send emails using Gmail", "create a dashboard that lists my Linear projects"
|
|
37
|
+
- **No tool bloat.** Only 4 tools exposed regardless of how many platforms or actions you connect. Actions are search-based, so your agent's context window stays clean.
|
|
38
|
+
- **Fine-grained access control.** Restrict which actions, connections, and permission levels (read, write, admin) your agent has access to.
|
|
39
|
+
- **Secure by default.** All requests proxied through One, secrets automatically redacted, no platform API keys to manage.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
**Execute actions directly:**
|
|
44
|
+
> "Get my last 5 emails from Gmail"
|
|
45
|
+
|
|
46
|
+
> "Send a Slack message to #general: 'Meeting in 10 minutes'"
|
|
47
|
+
|
|
48
|
+
> "Get all products from my Shopify store"
|
|
49
|
+
|
|
50
|
+
**Generate integration code:**
|
|
51
|
+
> "Create a React form component that sends emails using Gmail"
|
|
52
|
+
|
|
53
|
+
> "Build a dashboard that displays Linear users and their assigned projects with filtering"
|
|
54
|
+
|
|
55
|
+
> "Create a paginatable table that fetches and displays QuickBooks invoices with search and sort"
|
|
56
|
+
|
|
57
|
+
## Tools
|
|
58
|
+
|
|
59
|
+
The server exposes four MCP tools:
|
|
60
|
+
|
|
61
|
+
| Tool | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `list_one_integrations` | List available platforms and active connections |
|
|
64
|
+
| `search_one_platform_actions` | Search for actions on a specific platform |
|
|
65
|
+
| `get_one_action_knowledge` | Get detailed documentation for an action |
|
|
66
|
+
| `execute_one_action` | Execute an API action on a connected platform |
|
|
67
|
+
|
|
68
|
+
## Manual Installation
|
|
69
|
+
|
|
70
|
+
If you prefer to configure the server manually instead of using `one init`, install the package directly:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm install @withone/mcp
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then set the required environment variable:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ONE_SECRET=your-one-secret-key
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Identity Scoping
|
|
83
|
+
|
|
84
|
+
Scope connections to a specific identity (e.g., a user, team, or organization):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
ONE_IDENTITY=user_123
|
|
88
|
+
ONE_IDENTITY_TYPE=user
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Variable | Description | Values |
|
|
92
|
+
|----------|-------------|--------|
|
|
93
|
+
| `ONE_IDENTITY` | The identifier for the entity (e.g., user ID, team ID) | Any string |
|
|
94
|
+
| `ONE_IDENTITY_TYPE` | The type of identity | `user`, `team`, `organization`, `project` |
|
|
95
|
+
|
|
96
|
+
When set, the MCP server will only return connections associated with the specified identity. This is useful for multi-tenant applications where you want to scope integrations to specific users or entities.
|
|
97
|
+
|
|
98
|
+
### Access Control
|
|
99
|
+
|
|
100
|
+
Fine-tune what the MCP server can see and do:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ONE_PERMISSIONS=read
|
|
104
|
+
ONE_CONNECTION_KEYS=conn_key_1,conn_key_2
|
|
105
|
+
ONE_ACTION_IDS=action_id_1,action_id_2
|
|
106
|
+
ONE_KNOWLEDGE_AGENT=true
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Variable | Type | Default | Description |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `ONE_PERMISSIONS` | `read` \| `write` \| `admin` | `admin` | Filter actions by HTTP method. `read` = GET only, `write` = GET/POST/PUT/PATCH, `admin` = all methods |
|
|
112
|
+
| `ONE_CONNECTION_KEYS` | `*` or comma-separated keys | `*` | Restrict visible connections and platforms to specific connection keys |
|
|
113
|
+
| `ONE_ACTION_IDS` | `*` or comma-separated IDs | `*` | Restrict visible and executable actions to specific action IDs |
|
|
114
|
+
| `ONE_KNOWLEDGE_AGENT` | `true` \| `false` | `false` | Remove the `execute_one_action` tool entirely, forcing knowledge-only mode |
|
|
115
|
+
|
|
116
|
+
All defaults preserve current behavior. If no access control env vars are set, the server starts with full access and all tools available.
|
|
117
|
+
|
|
118
|
+
## Manual Configuration
|
|
119
|
+
|
|
120
|
+
If you used `one init`, the configuration below is already done for you. These examples are for reference or manual setups.
|
|
121
|
+
|
|
122
|
+
### Standalone
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx @withone/mcp
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Claude Desktop / Cursor
|
|
129
|
+
|
|
130
|
+
Add the following to your MCP config:
|
|
131
|
+
|
|
132
|
+
- **Claude Desktop:** MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json` · Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
133
|
+
- **Cursor:** Open the Cursor menu and select "MCP Settings"
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"mcpServers": {
|
|
138
|
+
"one": {
|
|
139
|
+
"command": "npx",
|
|
140
|
+
"args": ["@withone/mcp"],
|
|
141
|
+
"env": {
|
|
142
|
+
"ONE_SECRET": "your-one-secret-key"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Remote MCP Server
|
|
150
|
+
|
|
151
|
+
The remote MCP server is available at [https://mcp.withone.ai](https://mcp.withone.ai).
|
|
152
|
+
|
|
153
|
+
### Docker
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
docker build -t one-mcp-server .
|
|
157
|
+
docker run -e ONE_SECRET=your_one_secret_key one-mcp-server
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
All environment variables listed above can be passed as `-e` flags.
|
|
161
|
+
|
|
162
|
+
## Security
|
|
163
|
+
|
|
164
|
+
All requests to third-party platforms are authenticated and proxied through One's API. The MCP server never handles OAuth tokens or platform API keys directly. The `ONE_SECRET` key is the sole credential required, and it is automatically redacted from all response payloads returned to clients. Sensitive headers are stripped from logged and returned request configurations.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
170
|
+
## Support
|
|
171
|
+
|
|
172
|
+
For support, please contact support@withone.ai or visit https://withone.ai
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pica API Client
|
|
3
|
+
*
|
|
4
|
+
* This module provides a TypeScript client for interacting with the Pica API.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Pica API client with authentication and data management
|
|
7
|
+
* @author Pica
|
|
8
|
+
*/
|
|
9
|
+
import { Connection, ConnectionDefinition, AvailableAction, ActionDetails, GetPicaActionKnowledgeResponse, ExecutePicaActionArgs, ExecutePassthroughResponse } from './types.js';
|
|
10
|
+
export type IdentityType = 'user' | 'team' | 'organization' | 'project';
|
|
11
|
+
export interface PicaClientOptions {
|
|
12
|
+
secret: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
identity?: string;
|
|
15
|
+
identityType?: IdentityType;
|
|
16
|
+
connectionKeys?: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Client for interacting with the Pica API
|
|
20
|
+
*/
|
|
21
|
+
export declare class PicaClient {
|
|
22
|
+
private readonly secret;
|
|
23
|
+
private readonly baseUrl;
|
|
24
|
+
private readonly identity?;
|
|
25
|
+
private readonly identityType?;
|
|
26
|
+
private readonly connectionKeys?;
|
|
27
|
+
private connections;
|
|
28
|
+
private connectors;
|
|
29
|
+
private isInitialized;
|
|
30
|
+
constructor(options: PicaClientOptions);
|
|
31
|
+
constructor(secret: string, baseUrl?: string);
|
|
32
|
+
/**
|
|
33
|
+
* Initializes the client by fetching connections and available connectors
|
|
34
|
+
* @throws {Error} If initialization fails completely
|
|
35
|
+
*/
|
|
36
|
+
initialize(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Generates standard headers for API requests
|
|
39
|
+
*/
|
|
40
|
+
generateHeaders(): Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* Fetches user connections from the API
|
|
43
|
+
*/
|
|
44
|
+
private fetchConnections;
|
|
45
|
+
/**
|
|
46
|
+
* Fetches available connection definitions from the API
|
|
47
|
+
*/
|
|
48
|
+
private fetchConnectionDefinitions;
|
|
49
|
+
/**
|
|
50
|
+
* Searches for actions on a specific platform using a query
|
|
51
|
+
* @param platform - The platform name to search actions for
|
|
52
|
+
* @param query - The search query to find relevant actions
|
|
53
|
+
* @param agentType - The type of agent context (execute or knowledge)
|
|
54
|
+
* @returns Array of top 5 most relevant actions for the platform
|
|
55
|
+
* @throws {Error} If platform or query is not provided or API request fails
|
|
56
|
+
*/
|
|
57
|
+
searchAvailableActions(platform: string, query: string, agentType?: "execute" | "knowledge"): Promise<AvailableAction[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Gets full action details by ID
|
|
60
|
+
* @param actionId - The action ID to get details for
|
|
61
|
+
* @returns The full action object
|
|
62
|
+
* @throws {Error} If action ID is not provided or API request fails
|
|
63
|
+
*/
|
|
64
|
+
getActionDetails(actionId: string): Promise<ActionDetails>;
|
|
65
|
+
/**
|
|
66
|
+
* Gets knowledge for a specific action by ID
|
|
67
|
+
* @param actionId - The action ID to get knowledge for
|
|
68
|
+
* @returns The knowledge string for the action
|
|
69
|
+
* @throws {Error} If action ID is not provided or API request fails
|
|
70
|
+
*/
|
|
71
|
+
getActionKnowledge(actionId: string): Promise<GetPicaActionKnowledgeResponse>;
|
|
72
|
+
/**
|
|
73
|
+
* Executes a passthrough request to a third-party API through Pica
|
|
74
|
+
* @param args - The execution arguments containing all request details
|
|
75
|
+
* @returns Object containing sanitized request config and response data
|
|
76
|
+
* @throws {Error} If the request fails
|
|
77
|
+
*/
|
|
78
|
+
executePassthroughRequest(args: ExecutePicaActionArgs, preloadedAction?: ActionDetails): Promise<ExecutePassthroughResponse>;
|
|
79
|
+
/**
|
|
80
|
+
* Gets the base URL for the Pica API
|
|
81
|
+
* @returns The base URL for the Pica API
|
|
82
|
+
*/
|
|
83
|
+
getBaseUrl(): string;
|
|
84
|
+
/**
|
|
85
|
+
* Gets the current user's connections
|
|
86
|
+
* @returns Array of user connections
|
|
87
|
+
*/
|
|
88
|
+
getUserConnections(): Connection[];
|
|
89
|
+
/**
|
|
90
|
+
* Gets available connectors/platforms
|
|
91
|
+
* @returns Array of available connection definitions
|
|
92
|
+
*/
|
|
93
|
+
getAvailableConnectors(): ConnectionDefinition[];
|
|
94
|
+
/**
|
|
95
|
+
* Checks if the client has been initialized
|
|
96
|
+
*/
|
|
97
|
+
isClientInitialized(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Refreshes connections and connectors data
|
|
100
|
+
*/
|
|
101
|
+
refresh(): Promise<void>;
|
|
102
|
+
}
|
package/build/client.js
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One API Client
|
|
3
|
+
*
|
|
4
|
+
* This module provides a TypeScript client for interacting with the One API.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview One API client with authentication and data management
|
|
7
|
+
* @author One
|
|
8
|
+
*/
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import FormData from 'form-data';
|
|
11
|
+
import { fetchPaginatedData, replacePathVariables } from './helpers.js';
|
|
12
|
+
/**
|
|
13
|
+
* Client for interacting with the One API
|
|
14
|
+
*/
|
|
15
|
+
export class OneClient {
|
|
16
|
+
secret;
|
|
17
|
+
baseUrl;
|
|
18
|
+
identity;
|
|
19
|
+
identityType;
|
|
20
|
+
connectionKeys;
|
|
21
|
+
connections = [];
|
|
22
|
+
connectors = [];
|
|
23
|
+
isInitialized = false;
|
|
24
|
+
constructor(optionsOrSecret, baseUrl = "https://api.withone.ai") {
|
|
25
|
+
if (typeof optionsOrSecret === 'string') {
|
|
26
|
+
// Legacy constructor: (secret, baseUrl)
|
|
27
|
+
if (!optionsOrSecret?.trim()) {
|
|
28
|
+
throw new Error("One secret is required and cannot be empty");
|
|
29
|
+
}
|
|
30
|
+
this.secret = optionsOrSecret;
|
|
31
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// New constructor: (options)
|
|
35
|
+
if (!optionsOrSecret.secret?.trim()) {
|
|
36
|
+
throw new Error("One secret is required and cannot be empty");
|
|
37
|
+
}
|
|
38
|
+
this.secret = optionsOrSecret.secret;
|
|
39
|
+
this.baseUrl = (optionsOrSecret.baseUrl || "https://api.withone.ai").replace(/\/$/, '');
|
|
40
|
+
this.identity = optionsOrSecret.identity;
|
|
41
|
+
this.identityType = optionsOrSecret.identityType;
|
|
42
|
+
this.connectionKeys = optionsOrSecret.connectionKeys;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Initializes the client by fetching connections and available connectors
|
|
47
|
+
* @throws {Error} If initialization fails completely
|
|
48
|
+
*/
|
|
49
|
+
async initialize() {
|
|
50
|
+
if (this.isInitialized) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const results = await Promise.allSettled([
|
|
54
|
+
this.fetchConnections(),
|
|
55
|
+
this.fetchConnectionDefinitions(),
|
|
56
|
+
]);
|
|
57
|
+
const [connectionsResult, connectorsResult] = results;
|
|
58
|
+
if (connectionsResult.status === 'rejected') {
|
|
59
|
+
console.error("Failed to fetch connections:", connectionsResult.reason);
|
|
60
|
+
this.connections = [];
|
|
61
|
+
}
|
|
62
|
+
if (connectorsResult.status === 'rejected') {
|
|
63
|
+
console.error("Failed to fetch connectors:", connectorsResult.reason);
|
|
64
|
+
this.connectors = [];
|
|
65
|
+
}
|
|
66
|
+
this.isInitialized = true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generates standard headers for API requests
|
|
70
|
+
*/
|
|
71
|
+
generateHeaders() {
|
|
72
|
+
return {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
"x-one-secret": this.secret,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fetches user connections from the API
|
|
79
|
+
*/
|
|
80
|
+
async fetchConnections() {
|
|
81
|
+
try {
|
|
82
|
+
// If connectionKeys is set, not ["*"], and empty → no connections
|
|
83
|
+
if (this.connectionKeys && !this.connectionKeys.includes("*") && this.connectionKeys.length === 0) {
|
|
84
|
+
this.connections = [];
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const headers = this.generateHeaders();
|
|
88
|
+
const url = `${this.baseUrl}/v1/vault/connections`;
|
|
89
|
+
const additionalParams = {};
|
|
90
|
+
if (this.identity) {
|
|
91
|
+
additionalParams.identity = this.identity;
|
|
92
|
+
}
|
|
93
|
+
if (this.identityType) {
|
|
94
|
+
additionalParams.identityType = this.identityType;
|
|
95
|
+
}
|
|
96
|
+
if (this.connectionKeys && !this.connectionKeys.includes("*")) {
|
|
97
|
+
additionalParams.key = this.connectionKeys.join(",");
|
|
98
|
+
}
|
|
99
|
+
this.connections = await fetchPaginatedData(url, headers, Object.keys(additionalParams).length > 0 ? additionalParams : undefined);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error("Failed to fetch connections:", error);
|
|
103
|
+
this.connections = [];
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Fetches available connection definitions from the API
|
|
109
|
+
*/
|
|
110
|
+
async fetchConnectionDefinitions() {
|
|
111
|
+
try {
|
|
112
|
+
const headers = this.generateHeaders();
|
|
113
|
+
const url = `${this.baseUrl}/v1/available-connectors`;
|
|
114
|
+
this.connectors = await fetchPaginatedData(url, headers);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error("Failed to fetch connection definitions:", error);
|
|
118
|
+
this.connectors = [];
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Searches for actions on a specific platform using a query
|
|
124
|
+
* @param platform - The platform name to search actions for
|
|
125
|
+
* @param query - The search query to find relevant actions
|
|
126
|
+
* @param agentType - The type of agent context (execute or knowledge)
|
|
127
|
+
* @returns Array of top 5 most relevant actions for the platform
|
|
128
|
+
* @throws {Error} If platform or query is not provided or API request fails
|
|
129
|
+
*/
|
|
130
|
+
async searchAvailableActions(platform, query, agentType) {
|
|
131
|
+
if (!platform?.trim()) {
|
|
132
|
+
throw new Error("Platform name is required");
|
|
133
|
+
}
|
|
134
|
+
if (!query?.trim()) {
|
|
135
|
+
throw new Error("Search query is required");
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const headers = this.generateHeaders();
|
|
139
|
+
const url = `${this.baseUrl}/v1/available-actions/search/${platform}`;
|
|
140
|
+
// Default to knowledgeAgent if not specified
|
|
141
|
+
const isKnowledgeAgent = !agentType || agentType === "knowledge";
|
|
142
|
+
const params = {
|
|
143
|
+
query,
|
|
144
|
+
limit: '5'
|
|
145
|
+
};
|
|
146
|
+
if (isKnowledgeAgent) {
|
|
147
|
+
params.knowledgeAgent = 'true';
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
params.executeAgent = 'true';
|
|
151
|
+
}
|
|
152
|
+
const response = await axios.get(url, {
|
|
153
|
+
headers,
|
|
154
|
+
params
|
|
155
|
+
});
|
|
156
|
+
return response.data || [];
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("Error searching available actions:", error);
|
|
160
|
+
if (axios.isAxiosError(error)) {
|
|
161
|
+
throw new Error(`Failed to search available actions: ${error.response?.status} ${error.response?.statusText}`);
|
|
162
|
+
}
|
|
163
|
+
throw new Error("Failed to search available actions");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Gets full action details by ID
|
|
168
|
+
* @param actionId - The action ID to get details for
|
|
169
|
+
* @returns The full action object
|
|
170
|
+
* @throws {Error} If action ID is not provided or API request fails
|
|
171
|
+
*/
|
|
172
|
+
async getActionDetails(actionId) {
|
|
173
|
+
if (!actionId?.trim()) {
|
|
174
|
+
throw new Error("Action ID is required");
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const headers = this.generateHeaders();
|
|
178
|
+
const url = `${this.baseUrl}/v1/knowledge`;
|
|
179
|
+
const params = {
|
|
180
|
+
_id: actionId
|
|
181
|
+
};
|
|
182
|
+
const response = await axios.get(url, {
|
|
183
|
+
headers,
|
|
184
|
+
params
|
|
185
|
+
});
|
|
186
|
+
const actions = response.data?.rows || [];
|
|
187
|
+
if (actions.length === 0) {
|
|
188
|
+
throw new Error(`Action with ID ${actionId} not found`);
|
|
189
|
+
}
|
|
190
|
+
return actions[0];
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error("Error fetching action details:", error);
|
|
194
|
+
if (axios.isAxiosError(error)) {
|
|
195
|
+
throw new Error(`Failed to fetch action details: ${error.response?.status} ${error.response?.statusText}`);
|
|
196
|
+
}
|
|
197
|
+
throw new Error("Failed to fetch action details");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Gets knowledge for a specific action by ID
|
|
202
|
+
* @param actionId - The action ID to get knowledge for
|
|
203
|
+
* @returns The knowledge string for the action
|
|
204
|
+
* @throws {Error} If action ID is not provided or API request fails
|
|
205
|
+
*/
|
|
206
|
+
async getActionKnowledge(actionId) {
|
|
207
|
+
try {
|
|
208
|
+
const action = await this.getActionDetails(actionId);
|
|
209
|
+
if (!action.knowledge || !action.method) {
|
|
210
|
+
return {
|
|
211
|
+
knowledge: "No knowledge was found",
|
|
212
|
+
method: "No method was found"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
knowledge: action.knowledge,
|
|
217
|
+
method: action.method
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error("Error fetching action knowledge:", error);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Executes a passthrough request to a third-party API through One
|
|
227
|
+
* @param args - The execution arguments containing all request details
|
|
228
|
+
* @returns Object containing sanitized request config and response data
|
|
229
|
+
* @throws {Error} If the request fails
|
|
230
|
+
*/
|
|
231
|
+
async executePassthroughRequest(args, preloadedAction) {
|
|
232
|
+
const { actionId, connectionKey, data, pathVariables, queryParams, headers, isFormData, isFormUrlEncoded, } = args;
|
|
233
|
+
// Use preloaded action or fetch action details
|
|
234
|
+
const action = preloadedAction ?? await this.getActionDetails(actionId);
|
|
235
|
+
const method = action.method;
|
|
236
|
+
const contentType = isFormData ? 'multipart/form-data' : isFormUrlEncoded ? 'application/x-www-form-urlencoded' : 'application/json';
|
|
237
|
+
const requestHeaders = {
|
|
238
|
+
...this.generateHeaders(),
|
|
239
|
+
'x-one-connection-key': connectionKey,
|
|
240
|
+
'x-one-action-id': action._id,
|
|
241
|
+
'Content-Type': contentType,
|
|
242
|
+
...headers
|
|
243
|
+
};
|
|
244
|
+
const finalActionPath = pathVariables
|
|
245
|
+
? replacePathVariables(action.path, pathVariables)
|
|
246
|
+
: action.path;
|
|
247
|
+
const normalizedPath = finalActionPath.startsWith('/') ? finalActionPath : `/${finalActionPath}`;
|
|
248
|
+
const url = `${this.baseUrl}/v1/passthrough${normalizedPath}`;
|
|
249
|
+
let requestData = data;
|
|
250
|
+
if (typeof requestData === 'string') {
|
|
251
|
+
try {
|
|
252
|
+
requestData = JSON.parse(requestData);
|
|
253
|
+
}
|
|
254
|
+
catch { }
|
|
255
|
+
}
|
|
256
|
+
// Check if action has "custom" tag and add connectionKey to body if needed
|
|
257
|
+
const isCustomAction = action.tags?.includes('custom');
|
|
258
|
+
if (isCustomAction && method?.toLowerCase() !== 'get') {
|
|
259
|
+
requestData = {
|
|
260
|
+
...requestData,
|
|
261
|
+
connectionKey
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const requestConfig = {
|
|
265
|
+
url,
|
|
266
|
+
method,
|
|
267
|
+
headers: requestHeaders,
|
|
268
|
+
params: queryParams
|
|
269
|
+
};
|
|
270
|
+
if (method?.toLowerCase() !== 'get') {
|
|
271
|
+
if (isFormData) {
|
|
272
|
+
const formData = new FormData();
|
|
273
|
+
if (requestData && typeof requestData === 'object' && !Array.isArray(requestData)) {
|
|
274
|
+
Object.entries(requestData).forEach(([key, value]) => {
|
|
275
|
+
if (typeof value === 'object') {
|
|
276
|
+
formData.append(key, JSON.stringify(value));
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
formData.append(key, String(value));
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
requestConfig.data = formData;
|
|
284
|
+
Object.assign(requestConfig.headers, formData.getHeaders());
|
|
285
|
+
}
|
|
286
|
+
else if (isFormUrlEncoded) {
|
|
287
|
+
const params = new URLSearchParams();
|
|
288
|
+
if (requestData && typeof requestData === 'object' && !Array.isArray(requestData)) {
|
|
289
|
+
Object.entries(requestData).forEach(([key, value]) => {
|
|
290
|
+
if (typeof value === 'object') {
|
|
291
|
+
params.append(key, JSON.stringify(value));
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
params.append(key, String(value));
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
requestConfig.data = params;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
requestConfig.data = requestData;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const sanitizedConfig = {
|
|
305
|
+
...requestConfig,
|
|
306
|
+
headers: {
|
|
307
|
+
...requestConfig.headers,
|
|
308
|
+
'x-one-secret': '***REDACTED***'
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
try {
|
|
312
|
+
const response = await axios(requestConfig);
|
|
313
|
+
return {
|
|
314
|
+
requestConfig: sanitizedConfig,
|
|
315
|
+
responseData: response.data
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
console.error("Error executing passthrough request:", error);
|
|
320
|
+
if (axios.isAxiosError(error)) {
|
|
321
|
+
throw new Error(`Failed to execute passthrough request: ${error.response?.status} ${error.response?.statusText}`);
|
|
322
|
+
}
|
|
323
|
+
throw new Error("Failed to execute passthrough request");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Gets the base URL for the One API
|
|
328
|
+
* @returns The base URL for the One API
|
|
329
|
+
*/
|
|
330
|
+
getBaseUrl() {
|
|
331
|
+
return this.baseUrl;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Gets the current user's connections
|
|
335
|
+
* @returns Array of user connections
|
|
336
|
+
*/
|
|
337
|
+
getUserConnections() {
|
|
338
|
+
return [...this.connections];
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Gets available connectors/platforms
|
|
342
|
+
* @returns Array of available connection definitions
|
|
343
|
+
*/
|
|
344
|
+
getAvailableConnectors() {
|
|
345
|
+
return [...this.connectors];
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Checks if the client has been initialized
|
|
349
|
+
*/
|
|
350
|
+
isClientInitialized() {
|
|
351
|
+
return this.isInitialized;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Refreshes connections and connectors data
|
|
355
|
+
*/
|
|
356
|
+
async refresh() {
|
|
357
|
+
await Promise.allSettled([
|
|
358
|
+
this.fetchConnections(),
|
|
359
|
+
this.fetchConnectionDefinitions(),
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
}
|