n8n-nodes-tendem 0.1.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 +268 -0
- package/dist/credentials/TendemApi.credentials.d.ts +10 -0
- package/dist/credentials/TendemApi.credentials.js +61 -0
- package/dist/credentials/TendemApi.credentials.js.map +1 -0
- package/dist/nodes/Tendem/Tendem.node.d.ts +5 -0
- package/dist/nodes/Tendem/Tendem.node.js +211 -0
- package/dist/nodes/Tendem/Tendem.node.js.map +1 -0
- package/dist/nodes/Tendem/Tendem.node.json +18 -0
- package/dist/nodes/Tendem/descriptions.d.ts +9 -0
- package/dist/nodes/Tendem/descriptions.js +320 -0
- package/dist/nodes/Tendem/descriptions.js.map +1 -0
- package/dist/nodes/Tendem/retry.d.ts +18 -0
- package/dist/nodes/Tendem/retry.js +47 -0
- package/dist/nodes/Tendem/retry.js.map +1 -0
- package/dist/nodes/Tendem/tendem.svg +12 -0
- package/dist/nodes/Tendem/tools.d.ts +34 -0
- package/dist/nodes/Tendem/tools.js +80 -0
- package/dist/nodes/Tendem/tools.js.map +1 -0
- package/dist/nodes/Tendem/transport.d.ts +56 -0
- package/dist/nodes/Tendem/transport.js +265 -0
- package/dist/nodes/Tendem/transport.js.map +1 -0
- package/dist/nodes/Tendem/waitForTask.d.ts +33 -0
- package/dist/nodes/Tendem/waitForTask.js +76 -0
- package/dist/nodes/Tendem/waitForTask.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Toloka AI BV
|
|
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,268 @@
|
|
|
1
|
+
# n8n-nodes-tendem
|
|
2
|
+
|
|
3
|
+
An [n8n](https://n8n.io) community node for [Tendem](https://tendem.ai) — a hybrid
|
|
4
|
+
AI + human task service. You submit a task in plain English, Tendem's orchestrator
|
|
5
|
+
scopes it in a chat and quotes a transparent price, and once a **human** approves
|
|
6
|
+
the spend, a vetted expert executes it and returns verified results as markdown
|
|
7
|
+
plus files.
|
|
8
|
+
|
|
9
|
+
Use it when a workflow hits work an automation can't reliably nail: research and
|
|
10
|
+
competitive analysis, copywriting and editing, design review, data cleaning, list
|
|
11
|
+
building, anything needing real judgement. Don't use it for quick
|
|
12
|
+
general-knowledge lookups, and note that Tendem declines data-scraping work by
|
|
13
|
+
policy.
|
|
14
|
+
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Credentials](#credentials)
|
|
17
|
+
- [Operations](#operations)
|
|
18
|
+
- [Approving a task spends real money](#approving-a-task-spends-real-money)
|
|
19
|
+
- [Worked example](#worked-example-research-brief-end-to-end)
|
|
20
|
+
- [Polling without a busy loop](#polling-without-a-busy-loop)
|
|
21
|
+
- [Attaching input files](#attaching-input-files)
|
|
22
|
+
- [Compatibility](#compatibility)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Go to **Settings → Community nodes → Install**, enter `n8n-nodes-tendem`, accept
|
|
27
|
+
the risk prompt, and install. A **Tendem** node appears in the node panel.
|
|
28
|
+
|
|
29
|
+
Other setups are covered in n8n's own guide: [Installing community
|
|
30
|
+
nodes](https://docs.n8n.io/integrations/community-nodes/installation/).
|
|
31
|
+
|
|
32
|
+
## Credentials
|
|
33
|
+
|
|
34
|
+
The node talks to Tendem's hosted MCP server over streamable HTTP. Authentication
|
|
35
|
+
is an **API key**, sent as `Authorization: ApiKey <token>`.
|
|
36
|
+
|
|
37
|
+
1. Sign in at [agent.tendem.ai](https://agent.tendem.ai).
|
|
38
|
+
2. Go to [agent.tendem.ai/mcp](https://agent.tendem.ai/mcp), open the **Agent
|
|
39
|
+
builders** tab, and click **Generate API key**.
|
|
40
|
+
3. In n8n: **Credentials → New → Tendem API**, paste the token, save.
|
|
41
|
+
|
|
42
|
+
Use n8n's **Test** button on the credential — it performs a real MCP `initialize`
|
|
43
|
+
handshake against the endpoint, so a bad key fails immediately rather than at the
|
|
44
|
+
first workflow run.
|
|
45
|
+
|
|
46
|
+
| Field | Default | Notes |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| API Key | — | Required. Stored encrypted by n8n; never put it in a node parameter or an HTTP Request node. |
|
|
49
|
+
| MCP Endpoint | `https://mcp.tendem.ai/mcp?utm_hash=83dad40a52` | Overridable, but only change it to target a non-production Tendem deployment. The `utm_hash` is n8n channel attribution. |
|
|
50
|
+
|
|
51
|
+
Tendem also supports interactive OAuth, which is what the Claude Code / Cursor /
|
|
52
|
+
Gemini plugins use. n8n workflows run unattended, so this node deliberately does
|
|
53
|
+
API keys only.
|
|
54
|
+
|
|
55
|
+
## Operations
|
|
56
|
+
|
|
57
|
+
| Resource | Operation | Tendem tool | Spends money |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| Task | Create | `create_task` | no |
|
|
60
|
+
| Task | Get | `get_task` | no |
|
|
61
|
+
| Task | Get Contract | `get_contract` | no |
|
|
62
|
+
| Task | **Approve (Spends Money)** | `approve_task` | **yes** |
|
|
63
|
+
| Task | Get Cancel URL | `cancel_task` | no |
|
|
64
|
+
| Task | Get Result | `get_task_result` | no |
|
|
65
|
+
| Task | List | `list_tasks` | no |
|
|
66
|
+
| Task | Wait for Change | `get_task` (long-poll) | no |
|
|
67
|
+
| Chat | Read | `read_chat` | no |
|
|
68
|
+
| Chat | Send | `send_message` | no |
|
|
69
|
+
| Account | Get | `get_account` | no |
|
|
70
|
+
| File | Get Upload URL | `get_file_upload_url` | no |
|
|
71
|
+
|
|
72
|
+
Every response is passed through as JSON on the item, including Tendem's guidance
|
|
73
|
+
envelope — `next_action`, `poll_after_seconds`, `poll_timeout_seconds`,
|
|
74
|
+
`guidance`. **Branch on `next_action`, not on the raw `status`**; Tendem treats it
|
|
75
|
+
as authoritative. The statuses you'll see are `ACTING`, `LISTENING`,
|
|
76
|
+
`NEEDS_REPAIR`, `CLOSED` (terminal, but the result is still fetchable) and
|
|
77
|
+
`DELETED`.
|
|
78
|
+
|
|
79
|
+
### Note on Get Cancel URL
|
|
80
|
+
|
|
81
|
+
`cancel_task` does **not** cancel anything. It returns a Tendem UI URL where a
|
|
82
|
+
person can cancel. Send them there.
|
|
83
|
+
|
|
84
|
+
## Approving a task spends real money
|
|
85
|
+
|
|
86
|
+
Approval charges the Tendem account, so the node treats it as a distinct,
|
|
87
|
+
deliberate act rather than a step that can happen on the way to something else.
|
|
88
|
+
Four things stand in the way, and they are structural, not documentation:
|
|
89
|
+
|
|
90
|
+
1. **Approval is its own operation.** `approve_task` is reachable only from
|
|
91
|
+
Task → Approve. A workflow author has to add that node on purpose.
|
|
92
|
+
2. **Per-operation capability guard.** Each operation executes against an
|
|
93
|
+
allowlist naming the single Tendem tool it may call. Approve is the only row
|
|
94
|
+
containing `approve_task`; every other operation is refused *before* any HTTP
|
|
95
|
+
request is made. Creating a task, polling one, or reading its chat cannot
|
|
96
|
+
approve it even if the code above them were wrong.
|
|
97
|
+
3. **Confirm Spend must be on.** The Approve operation refuses while the
|
|
98
|
+
**Confirm Spend** toggle is off. Drive it from an expression if a human
|
|
99
|
+
decision upstream — an n8n Wait-for-form, a Slack approval, an email gate —
|
|
100
|
+
should decide. It is evaluated per item, so confirming one item never
|
|
101
|
+
approves another.
|
|
102
|
+
4. **A price must be supplied.** Approve requires the quoted price, which means
|
|
103
|
+
the amount being committed had to be read before it could be committed.
|
|
104
|
+
|
|
105
|
+
The node also never tops up and never retries. Insufficient balance comes back as
|
|
106
|
+
**data**, not an exception, so you can route on it:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
{ "approved": false, "spendBlocked": true, "topupUrl": "https://…", "reason": "insufficient_balance" }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Send `topupUrl` to the user. That link is task-bound: paying it tops up *and*
|
|
113
|
+
auto-approves this task, so don't call Approve again afterwards.
|
|
114
|
+
|
|
115
|
+
On success you get `approved: true`, `spendBlocked: false`, `topupUrl: null`.
|
|
116
|
+
|
|
117
|
+
### Why the node isn't available as an AI Agent tool
|
|
118
|
+
|
|
119
|
+
n8n's `usableAsTool` is all-or-nothing per node — there is no form of it that
|
|
120
|
+
exposes some operations and withholds others. Turning it on would hand a model
|
|
121
|
+
the Approve operation. Spend is a workflow author's decision, so this node stays
|
|
122
|
+
off the AI tool surface. An agent that needs the read-only parts can talk to the
|
|
123
|
+
[Tendem MCP server](https://github.com/Toloka/tendem-mcp) directly, where the
|
|
124
|
+
host application owns the approval gate.
|
|
125
|
+
|
|
126
|
+
## Worked example: research brief, end to end
|
|
127
|
+
|
|
128
|
+
A workflow that submits a brief, surfaces the quote to a human, and only then
|
|
129
|
+
spends. Nine nodes:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Manual Trigger
|
|
133
|
+
→ Tendem: Task → Create
|
|
134
|
+
→ Tendem: Task → Wait for Change (until the quote is ready)
|
|
135
|
+
→ Tendem: Task → Get Contract (scope + price)
|
|
136
|
+
→ Slack / Email: send the scope and price to a human
|
|
137
|
+
→ Wait for approval (n8n Wait node with a form / webhook resume)
|
|
138
|
+
→ IF: did the human approve?
|
|
139
|
+
true → Tendem: Task → Approve (Confirm Spend = the human's answer)
|
|
140
|
+
→ Tendem: Task → Wait for Change (until the work is done)
|
|
141
|
+
→ Tendem: Task → Get Result
|
|
142
|
+
false → Tendem: Task → Get Cancel URL (and send it to the human)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**1. Task → Create.** Pass the requester's own words through verbatim. Don't
|
|
146
|
+
pre-interrogate them and don't rewrite the brief — Tendem's orchestrator does the
|
|
147
|
+
scoping and asks follow-up questions over chat.
|
|
148
|
+
|
|
149
|
+
| Parameter | Value |
|
|
150
|
+
|---|---|
|
|
151
|
+
| Name | `Competitor research for EU freight brokerage` |
|
|
152
|
+
| Description | `Research the top 5 competitors in EU freight brokerage and summarise their pricing models in a one-page brief.` |
|
|
153
|
+
| Conversation ID | *(optional)* `{{ $execution.id }}` — lets Tendem correlate several tasks from one run |
|
|
154
|
+
|
|
155
|
+
Output carries `task_id` and `last_seen_offset`. Nothing is charged yet.
|
|
156
|
+
|
|
157
|
+
**2. Task → Wait for Change.** Task ID `{{ $json.task_id }}`. This blocks until
|
|
158
|
+
Tendem needs you. When `next_action` comes back `await_input`, Tendem asked a
|
|
159
|
+
scoping question: read it with **Chat → Read** and answer with **Chat → Send**,
|
|
160
|
+
then wait again. Send figures out the conversation position on its own (that's
|
|
161
|
+
the "Resolve Offset Automatically" toggle, on by default), so there is no offset
|
|
162
|
+
to thread through the workflow. When `next_action` is `await_user_approval`, the
|
|
163
|
+
quote is ready.
|
|
164
|
+
|
|
165
|
+
**3. Task → Get Contract.** Returns the scope Tendem committed to
|
|
166
|
+
(`task_description`, `criteria`) and the price. Scope is ready before the price
|
|
167
|
+
is, so a contract can legitimately come back with `price: null` — that means keep
|
|
168
|
+
waiting, not approve at zero.
|
|
169
|
+
|
|
170
|
+
**4–6. Show a human the scope and the price, and wait for an answer.** This is
|
|
171
|
+
the part the node cannot do for you and deliberately will not fake. Any n8n
|
|
172
|
+
approval mechanism works: a Wait node with a form, a Slack interactive message, a
|
|
173
|
+
webhook.
|
|
174
|
+
|
|
175
|
+
**7. Task → Approve.**
|
|
176
|
+
|
|
177
|
+
| Parameter | Value |
|
|
178
|
+
|---|---|
|
|
179
|
+
| Task ID | `{{ $json.task_id }}` |
|
|
180
|
+
| Name | `{{ $json.name }}` |
|
|
181
|
+
| Price | `{{ $json.price }}` — the number the human was shown |
|
|
182
|
+
| Confirm Spend | `{{ $json.humanApproved === true }}` |
|
|
183
|
+
|
|
184
|
+
Route `spendBlocked === true` to a branch that sends the human `topupUrl`.
|
|
185
|
+
|
|
186
|
+
**8. Task → Wait for Change** again, until `next_action` is `fetch_result` or
|
|
187
|
+
`done`. Expert work takes minutes to hours, so set **Max Rounds** to what you're
|
|
188
|
+
willing to hold an execution open for (each round is at most 30 seconds) and
|
|
189
|
+
handle `tendemWait.timedOut === true` by re-entering the wait on a schedule
|
|
190
|
+
rather than by raising the cap.
|
|
191
|
+
|
|
192
|
+
**9. Task → Get Result.** Returns the result markdown plus pre-signed download
|
|
193
|
+
URLs for any files. Fetch those with an HTTP Request node if you need the bytes.
|
|
194
|
+
|
|
195
|
+
## Polling without a busy loop
|
|
196
|
+
|
|
197
|
+
**Wait for Change** does not spin. Each round is a single
|
|
198
|
+
`get_task(task_id, wait_for_change_seconds=N)` call, and the *server* holds the
|
|
199
|
+
request open until the task actually changes — no client-side interval, no
|
|
200
|
+
repeated requests while nothing is happening.
|
|
201
|
+
|
|
202
|
+
| Parameter | Default | Range |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| Wait for Change (Seconds) | 30 | 5–30 (30 is the Tendem API's ceiling) |
|
|
205
|
+
| Max Rounds | 20 | 1–240 |
|
|
206
|
+
|
|
207
|
+
The round count is a hard cap. When the budget runs out the node emits the latest
|
|
208
|
+
snapshot with:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{ "tendemWait": { "settled": false, "timedOut": true, "rounds": 20 } }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Branch on `tendemWait.timedOut` and re-enter the wait later. At the defaults, one
|
|
215
|
+
node holds an execution open for at most ten minutes.
|
|
216
|
+
|
|
217
|
+
There's a backstop underneath: if a round returns faster than the server could
|
|
218
|
+
plausibly have blocked — a proxy that doesn't support long-polling, say — the node
|
|
219
|
+
sleeps before the next one, honouring Tendem's `poll_after_seconds` hint. A
|
|
220
|
+
misbehaving endpoint degrades to a paced poll, never to a hot loop.
|
|
221
|
+
|
|
222
|
+
Transient failures are absorbed too: read operations retry a
|
|
223
|
+
`TEMPORARILY_UNAVAILABLE`, a 5xx, or a dropped connection with backoff (up to 5
|
|
224
|
+
attempts) instead of failing the workflow, so an hours-long expert wait survives
|
|
225
|
+
the blips it will inevitably see. Operations that write — Create, Send, Approve —
|
|
226
|
+
are never retried, because repeating them could duplicate a task, a message, or
|
|
227
|
+
a charge.
|
|
228
|
+
|
|
229
|
+
## Attaching input files
|
|
230
|
+
|
|
231
|
+
Uploads need a `task_id`, so they can only happen *after* Create.
|
|
232
|
+
|
|
233
|
+
1. **File → Get Upload URL** with the task ID. You get a short-lived, folder-level
|
|
234
|
+
pre-signed URL.
|
|
235
|
+
2. For each file, build the PUT URL from the folder URL and upload with an HTTP
|
|
236
|
+
Request node:
|
|
237
|
+
- swap the host from `.dfs.core.windows.net` to `.blob.core.windows.net`,
|
|
238
|
+
- append the file name to the path **before** the query string —
|
|
239
|
+
`<base>/<filename>?<query>`,
|
|
240
|
+
- `PUT` the raw bytes with the header `x-ms-blob-type: BlockBlob`.
|
|
241
|
+
|
|
242
|
+
All three matter: a `PUT` to the URL exactly as returned fails with HTTP 400.
|
|
243
|
+
Keep names simple; avoid spaces. Subpaths like `data/input.csv` are fine.
|
|
244
|
+
3. **Chat → Send** naming the files you uploaded, e.g.
|
|
245
|
+
`I've uploaded brief.pdf and data/input.csv for this task.` Tendem waits for
|
|
246
|
+
this message; without it the expert never sees the files.
|
|
247
|
+
|
|
248
|
+
If the brief already references the files, mention them in the Create description
|
|
249
|
+
and promise them. Tendem may ask for them before your upload finishes — that race
|
|
250
|
+
is expected, and the confirming chat message settles it.
|
|
251
|
+
|
|
252
|
+
## Compatibility
|
|
253
|
+
|
|
254
|
+
- n8n API version 1, node type version 1
|
|
255
|
+
- Node.js >= 20.19
|
|
256
|
+
- No runtime dependencies. The MCP client is implemented in this package, in
|
|
257
|
+
~350 lines, against the [Streamable HTTP
|
|
258
|
+
transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
|
|
259
|
+
spec — so there is nothing to audit but this repository.
|
|
260
|
+
|
|
261
|
+
## Support
|
|
262
|
+
|
|
263
|
+
- Issues: <https://github.com/Toloka/tendem-mcp/issues>
|
|
264
|
+
- Email: <support@tendem.ai>
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
[MIT](./LICENSE) © Toloka AI BV
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, Icon, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class TendemApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: Icon;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TendemApi = void 0;
|
|
4
|
+
const transport_1 = require("../nodes/Tendem/transport");
|
|
5
|
+
class TendemApi {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'tendemApi';
|
|
8
|
+
this.displayName = 'Tendem API';
|
|
9
|
+
this.icon = 'file:../nodes/Tendem/tendem.svg';
|
|
10
|
+
this.documentationUrl = 'https://github.com/Toloka/tendem-mcp';
|
|
11
|
+
this.properties = [
|
|
12
|
+
{
|
|
13
|
+
displayName: 'API Key',
|
|
14
|
+
name: 'apiKey',
|
|
15
|
+
type: 'string',
|
|
16
|
+
typeOptions: { password: true },
|
|
17
|
+
default: '',
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'Tendem API key. Create one at https://agent.tendem.ai/mcp, on the "Agent builders" tab. Sent as the "Authorization: ApiKey <token>" header.',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
displayName: 'MCP Endpoint',
|
|
23
|
+
name: 'endpoint',
|
|
24
|
+
type: 'string',
|
|
25
|
+
default: transport_1.TENDEM_DEFAULT_ENDPOINT,
|
|
26
|
+
required: true,
|
|
27
|
+
description: 'Tendem MCP endpoint (Streamable HTTP). Change this only to target a non-production Tendem deployment.',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
this.authenticate = {
|
|
31
|
+
type: 'generic',
|
|
32
|
+
properties: {
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: '=ApiKey {{$credentials.apiKey}}',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
this.test = {
|
|
39
|
+
request: {
|
|
40
|
+
url: '={{$credentials.endpoint}}',
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
Accept: 'application/json, text/event-stream',
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
},
|
|
46
|
+
body: {
|
|
47
|
+
jsonrpc: '2.0',
|
|
48
|
+
id: 1,
|
|
49
|
+
method: 'initialize',
|
|
50
|
+
params: {
|
|
51
|
+
protocolVersion: transport_1.MCP_PROTOCOL_VERSION,
|
|
52
|
+
capabilities: {},
|
|
53
|
+
clientInfo: { name: transport_1.MCP_CLIENT_NAME, version: transport_1.MCP_CLIENT_VERSION },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.TendemApi = TendemApi;
|
|
61
|
+
//# sourceMappingURL=TendemApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TendemApi.credentials.js","sourceRoot":"","sources":["../../credentials/TendemApi.credentials.ts"],"names":[],"mappings":";;;AAQA,yDAKmC;AAEnC,MAAa,SAAS;IAAtB;QACC,SAAI,GAAG,WAAW,CAAC;QAEnB,gBAAW,GAAG,YAAY,CAAC;QAO3B,SAAI,GAAS,iCAAiC,CAAC;QAE/C,qBAAgB,GAAG,sCAAsC,CAAC;QAE1D,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,6IAA6I;aAC9I;YACD;gBACC,WAAW,EAAE,cAAc;gBAC3B,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,mCAAuB;gBAChC,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,uGAAuG;aACxG;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,aAAa,EAAE,iCAAiC;iBAChD;aACD;SACD,CAAC;QAGF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,GAAG,EAAE,4BAA4B;gBACjC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,MAAM,EAAE,qCAAqC;oBAC7C,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE;oBACL,OAAO,EAAE,KAAK;oBACd,EAAE,EAAE,CAAC;oBACL,MAAM,EAAE,YAAY;oBACpB,MAAM,EAAE;wBACP,eAAe,EAAE,gCAAoB;wBACrC,YAAY,EAAE,EAAE;wBAChB,UAAU,EAAE,EAAE,IAAI,EAAE,2BAAe,EAAE,OAAO,EAAE,8BAAkB,EAAE;qBAClE;iBACD;aACD;SACD,CAAC;IACH,CAAC;CAAA;AAlED,8BAkEC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type IExecuteFunctions, type INodeExecutionData, type INodeType, type INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class Tendem implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Tendem = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const descriptions_1 = require("./descriptions");
|
|
6
|
+
const transport_1 = require("./transport");
|
|
7
|
+
const retry_1 = require("./retry");
|
|
8
|
+
const tools_1 = require("./tools");
|
|
9
|
+
const waitForTask_1 = require("./waitForTask");
|
|
10
|
+
class Tendem {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.description = {
|
|
13
|
+
displayName: 'Tendem',
|
|
14
|
+
name: 'tendem',
|
|
15
|
+
icon: 'file:tendem.svg',
|
|
16
|
+
group: ['transform'],
|
|
17
|
+
version: 1,
|
|
18
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
19
|
+
description: 'Delegate work to vetted human experts through Tendem',
|
|
20
|
+
defaults: { name: 'Tendem' },
|
|
21
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
22
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
23
|
+
credentials: [{ name: 'tendemApi', required: true }],
|
|
24
|
+
properties: [
|
|
25
|
+
descriptions_1.resourceField,
|
|
26
|
+
descriptions_1.taskOperations,
|
|
27
|
+
descriptions_1.chatOperations,
|
|
28
|
+
descriptions_1.accountOperations,
|
|
29
|
+
descriptions_1.fileOperations,
|
|
30
|
+
...descriptions_1.taskFields,
|
|
31
|
+
...descriptions_1.chatFields,
|
|
32
|
+
...descriptions_1.fileFields,
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async execute() {
|
|
37
|
+
const items = this.getInputData();
|
|
38
|
+
const returnData = [];
|
|
39
|
+
const credentials = await this.getCredentials('tendemApi');
|
|
40
|
+
const endpoint = typeof credentials.endpoint === 'string' && credentials.endpoint.trim() !== ''
|
|
41
|
+
? credentials.endpoint.trim()
|
|
42
|
+
: transport_1.TENDEM_DEFAULT_ENDPOINT;
|
|
43
|
+
const requester = async (options) => (await this.helpers.httpRequestWithAuthentication.call(this, 'tendemApi', options));
|
|
44
|
+
const session = new transport_1.McpSession(requester, { endpoint });
|
|
45
|
+
const retrying = new retry_1.RetryingToolCaller(session, { sleep: n8n_workflow_1.sleep });
|
|
46
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
47
|
+
try {
|
|
48
|
+
const resource = this.getNodeParameter('resource', i);
|
|
49
|
+
const operation = this.getNodeParameter('operation', i);
|
|
50
|
+
const key = (0, tools_1.operationKey)(resource, operation);
|
|
51
|
+
const guarded = (0, tools_1.guardFor)(retrying, key);
|
|
52
|
+
const payload = await runOperation.call(this, guarded, key, i);
|
|
53
|
+
returnData.push(...this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(payload), {
|
|
54
|
+
itemData: { item: i },
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (this.continueOnFail()) {
|
|
59
|
+
returnData.push({
|
|
60
|
+
json: { error: error instanceof Error ? error.message : String(error) },
|
|
61
|
+
pairedItem: { item: i },
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
throw asNodeError.call(this, error, i);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return [returnData];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.Tendem = Tendem;
|
|
72
|
+
function asNodeError(error, itemIndex) {
|
|
73
|
+
if (error instanceof n8n_workflow_1.NodeOperationError || error instanceof n8n_workflow_1.NodeApiError)
|
|
74
|
+
return error;
|
|
75
|
+
return new n8n_workflow_1.NodeApiError(this.getNode(), error, {
|
|
76
|
+
itemIndex,
|
|
77
|
+
message: error instanceof Error ? error.message : String(error),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function runOperation(guarded, key, i) {
|
|
81
|
+
switch (key) {
|
|
82
|
+
case 'task:create': {
|
|
83
|
+
const args = {
|
|
84
|
+
name: this.getNodeParameter('taskName', i),
|
|
85
|
+
description: this.getNodeParameter('description', i),
|
|
86
|
+
};
|
|
87
|
+
const conversationId = this.getNodeParameter('conversationId', i, '');
|
|
88
|
+
if (conversationId !== '')
|
|
89
|
+
args.conversation_id = conversationId;
|
|
90
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.CREATE_TASK, args);
|
|
91
|
+
}
|
|
92
|
+
case 'task:get':
|
|
93
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.GET_TASK, {
|
|
94
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
95
|
+
});
|
|
96
|
+
case 'task:getContract':
|
|
97
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.GET_CONTRACT, {
|
|
98
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
99
|
+
});
|
|
100
|
+
case 'task:approve':
|
|
101
|
+
return await approveTask.call(this, guarded, i);
|
|
102
|
+
case 'task:cancel':
|
|
103
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.CANCEL_TASK, {
|
|
104
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
105
|
+
name: this.getNodeParameter('taskName', i),
|
|
106
|
+
});
|
|
107
|
+
case 'task:getResult':
|
|
108
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.GET_TASK_RESULT, {
|
|
109
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
110
|
+
});
|
|
111
|
+
case 'task:list':
|
|
112
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.LIST_TASKS, {
|
|
113
|
+
limit: this.getNodeParameter('limit', i, 50),
|
|
114
|
+
offset: this.getNodeParameter('offset', i, 0),
|
|
115
|
+
});
|
|
116
|
+
case 'task:wait': {
|
|
117
|
+
const outcome = await (0, waitForTask_1.waitForTaskChange)({ caller: guarded, sleep: n8n_workflow_1.sleep, now: () => Date.now() }, {
|
|
118
|
+
taskId: this.getNodeParameter('taskId', i),
|
|
119
|
+
waitForChangeSeconds: this.getNodeParameter('waitForChangeSeconds', i, 30),
|
|
120
|
+
maxRounds: this.getNodeParameter('maxRounds', i, 20),
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
...outcome.task,
|
|
124
|
+
tendemWait: {
|
|
125
|
+
settled: outcome.settled,
|
|
126
|
+
timedOut: outcome.timedOut,
|
|
127
|
+
rounds: outcome.rounds,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
case 'chat:read':
|
|
132
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.READ_CHAT, {
|
|
133
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
134
|
+
from_offset: this.getNodeParameter('fromOffset', i, 0),
|
|
135
|
+
});
|
|
136
|
+
case 'chat:send':
|
|
137
|
+
return await sendChatMessage.call(this, guarded, i);
|
|
138
|
+
case 'account:get':
|
|
139
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.GET_ACCOUNT, {});
|
|
140
|
+
case 'file:getUploadUrl':
|
|
141
|
+
return await guarded.callTool(tools_1.TENDEM_TOOLS.GET_FILE_UPLOAD_URL, {
|
|
142
|
+
task_id: this.getNodeParameter('taskId', i),
|
|
143
|
+
});
|
|
144
|
+
default:
|
|
145
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported Tendem operation "${key}"`, {
|
|
146
|
+
itemIndex: i,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function sendChatMessage(guarded, i) {
|
|
151
|
+
const taskId = this.getNodeParameter('taskId', i);
|
|
152
|
+
const text = this.getNodeParameter('text', i);
|
|
153
|
+
const autoOffset = this.getNodeParameter('autoOffset', i, true);
|
|
154
|
+
const latestOffset = async () => {
|
|
155
|
+
const chat = await guarded.callTool(tools_1.TENDEM_TOOLS.READ_CHAT, {
|
|
156
|
+
task_id: taskId,
|
|
157
|
+
from_offset: 0,
|
|
158
|
+
});
|
|
159
|
+
return typeof chat.last_seen_offset === 'number' ? chat.last_seen_offset : 0;
|
|
160
|
+
};
|
|
161
|
+
let offset = autoOffset
|
|
162
|
+
? await latestOffset()
|
|
163
|
+
: this.getNodeParameter('lastSeenOffset', i, 0);
|
|
164
|
+
let response = await guarded.callTool(tools_1.TENDEM_TOOLS.SEND_MESSAGE, {
|
|
165
|
+
task_id: taskId,
|
|
166
|
+
text,
|
|
167
|
+
last_seen_offset: offset,
|
|
168
|
+
});
|
|
169
|
+
if (autoOffset && response.response_type === 'race') {
|
|
170
|
+
offset =
|
|
171
|
+
typeof response.last_seen_offset === 'number' ? response.last_seen_offset : await latestOffset();
|
|
172
|
+
response = await guarded.callTool(tools_1.TENDEM_TOOLS.SEND_MESSAGE, {
|
|
173
|
+
task_id: taskId,
|
|
174
|
+
text,
|
|
175
|
+
last_seen_offset: offset,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return response;
|
|
179
|
+
}
|
|
180
|
+
async function approveTask(guarded, i) {
|
|
181
|
+
const taskId = this.getNodeParameter('taskId', i);
|
|
182
|
+
const taskName = this.getNodeParameter('taskName', i);
|
|
183
|
+
const price = this.getNodeParameter('price', i);
|
|
184
|
+
const confirmed = this.getNodeParameter('confirmSpend', i, false);
|
|
185
|
+
if (confirmed !== true) {
|
|
186
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Refusing to approve a Tendem task: the spend was not confirmed', {
|
|
187
|
+
itemIndex: i,
|
|
188
|
+
description: 'Approving charges the Tendem account. Read the scope and price with the "Get Contract" operation, surface it to a human, then enable "Confirm Spend" on this node.',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (typeof price !== 'string' || price.trim() === '') {
|
|
192
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Refusing to approve a Tendem task: no price was supplied', {
|
|
193
|
+
itemIndex: i,
|
|
194
|
+
description: 'Pass the quoted price through from the Get Task or Get Contract operation, so the approval names the amount being committed.',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const response = await guarded.callTool(tools_1.TENDEM_TOOLS.APPROVE_TASK, {
|
|
198
|
+
task_id: taskId,
|
|
199
|
+
name: taskName,
|
|
200
|
+
price,
|
|
201
|
+
});
|
|
202
|
+
const approvedRaw = response.approved;
|
|
203
|
+
const approved = typeof approvedRaw === 'boolean' ? approvedRaw : undefined;
|
|
204
|
+
return {
|
|
205
|
+
...response,
|
|
206
|
+
approved: approved === undefined ? null : approved,
|
|
207
|
+
spendBlocked: approved === false,
|
|
208
|
+
topupUrl: typeof response.topup_url === 'string' ? response.topup_url : null,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=Tendem.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tendem.node.js","sourceRoot":"","sources":["../../../nodes/Tendem/Tendem.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "n8n-nodes-tendem.tendem",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Utility", "Productivity"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/Toloka/tendem-mcp/tree/main/integrations/n8n-nodes-tendem#credentials"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/Toloka/tendem-mcp/tree/main/integrations/n8n-nodes-tendem"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare const resourceField: INodeProperties;
|
|
3
|
+
export declare const taskOperations: INodeProperties;
|
|
4
|
+
export declare const chatOperations: INodeProperties;
|
|
5
|
+
export declare const accountOperations: INodeProperties;
|
|
6
|
+
export declare const fileOperations: INodeProperties;
|
|
7
|
+
export declare const taskFields: INodeProperties[];
|
|
8
|
+
export declare const chatFields: INodeProperties[];
|
|
9
|
+
export declare const fileFields: INodeProperties[];
|