@ardensh/mcp 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/README.md +79 -0
- package/dist/auth.d.ts +3 -0
- package/dist/auth.js +14 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +57 -0
- package/dist/tools/fund.d.ts +2 -0
- package/dist/tools/fund.js +35 -0
- package/dist/tools/list.d.ts +2 -0
- package/dist/tools/list.js +38 -0
- package/dist/tools/provision.d.ts +38 -0
- package/dist/tools/provision.js +51 -0
- package/dist/tools/status.d.ts +2 -0
- package/dist/tools/status.js +43 -0
- package/dist/tools/update.d.ts +12 -0
- package/dist/tools/update.js +46 -0
- package/package.json +25 -0
- package/src/auth.ts +14 -0
- package/src/index.ts +88 -0
- package/src/tools/fund.ts +42 -0
- package/src/tools/list.ts +53 -0
- package/src/tools/provision.ts +63 -0
- package/src/tools/status.ts +63 -0
- package/src/tools/update.ts +51 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# @ardensh/mcp
|
|
2
|
+
|
|
3
|
+
MCP server for managing [Arden](https://arden.sh) AI agent spend controls. Lets Claude Desktop, Cursor, Claude Code, and other MCP-compatible AI clients provision and manage Arden agents via natural language.
|
|
4
|
+
|
|
5
|
+
> npm package: `@ardensh/mcp`
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `arden_provision_agent` | Create a new agent with budget limits and vendor restrictions |
|
|
12
|
+
| `arden_list_agents` | List all agents with budget usage |
|
|
13
|
+
| `arden_agent_status` | Detailed status and budget breakdown for a specific agent |
|
|
14
|
+
| `arden_update_agent` | Update an agent's budgets, allowed vendors, or status |
|
|
15
|
+
| `arden_fund_agent` | Get wallet address and USDC funding instructions |
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
Install the Arden CLI and log in to generate your API key:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g @arden/cli
|
|
23
|
+
arden login
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This writes your API key to `~/.arden/config.json`, which the MCP server reads automatically. You can skip the `ARDEN_API_KEY` env var if you've already done this.
|
|
27
|
+
|
|
28
|
+
## Usage with Claude Desktop
|
|
29
|
+
|
|
30
|
+
Add this to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"arden": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["-y", "@ardensh/mcp"],
|
|
38
|
+
"env": {
|
|
39
|
+
"ARDEN_API_KEY": "arden_live_..."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`ARDEN_API_KEY` is optional if you have already run `arden login` on the same machine.
|
|
47
|
+
|
|
48
|
+
## Usage with Claude Code
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude mcp add arden npx -y @ardensh/mcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you haven't run `arden login`, pass your key via env:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add arden -e ARDEN_API_KEY=arden_live_... npx -y @ardensh/mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage with Cursor
|
|
61
|
+
|
|
62
|
+
Add the same block as the Claude Desktop config above to your Cursor MCP settings under **Settings → MCP**.
|
|
63
|
+
|
|
64
|
+
## Authentication
|
|
65
|
+
|
|
66
|
+
The server looks for credentials in this order:
|
|
67
|
+
|
|
68
|
+
1. `ARDEN_API_KEY` environment variable
|
|
69
|
+
2. `~/.arden/config.json` (written by `arden login`)
|
|
70
|
+
|
|
71
|
+
If neither is found, tool calls return a clear error message rather than crashing.
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm install
|
|
77
|
+
npm run dev # run with tsx (no build step)
|
|
78
|
+
npm run build # compile to dist/
|
|
79
|
+
```
|
package/dist/auth.d.ts
ADDED
package/dist/auth.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
const store = new Conf({ projectName: 'arden', cwd: os.homedir() + '/.arden' });
|
|
4
|
+
export function getApiKey() {
|
|
5
|
+
const fromEnv = process.env.ARDEN_API_KEY;
|
|
6
|
+
if (fromEnv)
|
|
7
|
+
return fromEnv;
|
|
8
|
+
const fromConfig = store.get('apiKey');
|
|
9
|
+
if (fromConfig)
|
|
10
|
+
return fromConfig;
|
|
11
|
+
throw new Error('Not authenticated. Set ARDEN_API_KEY or run `arden login`.');
|
|
12
|
+
}
|
|
13
|
+
export const API_BASE = 'https://ir7hdqp3ei.execute-api.us-east-1.amazonaws.com/prod';
|
|
14
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { provisionAgent } from './tools/provision.js';
|
|
6
|
+
import { listAgents } from './tools/list.js';
|
|
7
|
+
import { agentStatus } from './tools/status.js';
|
|
8
|
+
import { fundAgent } from './tools/fund.js';
|
|
9
|
+
import { updateAgent } from './tools/update.js';
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: 'arden',
|
|
12
|
+
version: '0.1.0',
|
|
13
|
+
});
|
|
14
|
+
server.tool('arden_provision_agent', 'Provision a new Arden AI agent with spend controls and vendor restrictions', {
|
|
15
|
+
name: z.string().describe('Agent name: 3-50 chars, alphanumeric + hyphens only'),
|
|
16
|
+
monthly_budget: z.number().optional().describe('Monthly spend budget in USD'),
|
|
17
|
+
daily_budget: z.number().optional().describe('Daily spend budget in USD'),
|
|
18
|
+
weekly_budget: z.number().optional().describe('Weekly spend budget in USD'),
|
|
19
|
+
per_transaction_limit: z.number().optional().describe('Per-transaction spend limit in USD'),
|
|
20
|
+
allowed_vendors: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe('Comma-separated vendor domains, or "*" for all vendors'),
|
|
23
|
+
}, async (input) => {
|
|
24
|
+
const text = await provisionAgent(input);
|
|
25
|
+
return { content: [{ type: 'text', text }] };
|
|
26
|
+
});
|
|
27
|
+
server.tool('arden_list_agents', 'List all Arden agents with their status and budget usage', {}, async () => {
|
|
28
|
+
const text = await listAgents();
|
|
29
|
+
return { content: [{ type: 'text', text }] };
|
|
30
|
+
});
|
|
31
|
+
server.tool('arden_agent_status', 'Get detailed status and budget breakdown for a specific Arden agent', {
|
|
32
|
+
name: z.string().describe('Name of the agent to inspect'),
|
|
33
|
+
}, async ({ name }) => {
|
|
34
|
+
const text = await agentStatus(name);
|
|
35
|
+
return { content: [{ type: 'text', text }] };
|
|
36
|
+
});
|
|
37
|
+
server.tool('arden_fund_agent', 'Get wallet address and funding instructions for an Arden agent', {
|
|
38
|
+
name: z.string().describe('Name of the agent to fund'),
|
|
39
|
+
}, async ({ name }) => {
|
|
40
|
+
const text = await fundAgent(name);
|
|
41
|
+
return { content: [{ type: 'text', text }] };
|
|
42
|
+
});
|
|
43
|
+
server.tool('arden_update_agent', "Update an existing agent's budget limits, allowed vendors, or status", {
|
|
44
|
+
name: z.string().describe('Name of the agent to update'),
|
|
45
|
+
monthly_budget: z.number().optional().describe('New monthly budget in USD'),
|
|
46
|
+
daily_budget: z.number().optional().describe('New daily budget in USD'),
|
|
47
|
+
weekly_budget: z.number().optional().describe('New weekly budget in USD'),
|
|
48
|
+
per_transaction_limit: z.number().optional().describe('New per-transaction limit in USD'),
|
|
49
|
+
allowed_vendors: z.string().optional().describe('Comma-separated vendor domains, or "*" for all'),
|
|
50
|
+
status: z.enum(['active', 'paused']).optional().describe('Set agent status to active or paused'),
|
|
51
|
+
}, async (input) => {
|
|
52
|
+
const text = await updateAgent(input);
|
|
53
|
+
return { content: [{ type: 'text', text }] };
|
|
54
|
+
});
|
|
55
|
+
const transport = new StdioServerTransport();
|
|
56
|
+
await server.connect(transport);
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js';
|
|
3
|
+
export async function fundAgent(name) {
|
|
4
|
+
const apiKey = getApiKey();
|
|
5
|
+
try {
|
|
6
|
+
const { data } = await axios.get(`${API_BASE}/agents/${encodeURIComponent(name)}`, {
|
|
7
|
+
headers: { 'X-API-Key': apiKey },
|
|
8
|
+
});
|
|
9
|
+
const wallet = data.wallet_address;
|
|
10
|
+
if (!wallet) {
|
|
11
|
+
return `Agent "${name}" does not have a wallet address yet.`;
|
|
12
|
+
}
|
|
13
|
+
return [
|
|
14
|
+
`Funding instructions for agent "${name}":`,
|
|
15
|
+
'',
|
|
16
|
+
`Wallet address: ${wallet}`,
|
|
17
|
+
'',
|
|
18
|
+
'To fund this agent:',
|
|
19
|
+
' 1. Send USDC to the wallet address above.',
|
|
20
|
+
' 2. Network: Base mainnet (chain ID 8453).',
|
|
21
|
+
' 3. Token: USDC (native USDC on Base, not bridged).',
|
|
22
|
+
'',
|
|
23
|
+
'The agent will use this wallet for x402 payments when making API calls on your behalf.',
|
|
24
|
+
'Funds are available immediately after the transaction is confirmed on-chain.',
|
|
25
|
+
].join('\n');
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (axios.isAxiosError(err)) {
|
|
29
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message;
|
|
30
|
+
return `Failed to retrieve agent wallet: ${body}`;
|
|
31
|
+
}
|
|
32
|
+
return `Failed to retrieve agent wallet: ${String(err)}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=fund.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js';
|
|
3
|
+
export async function listAgents() {
|
|
4
|
+
const apiKey = getApiKey();
|
|
5
|
+
try {
|
|
6
|
+
const { data } = await axios.get(`${API_BASE}/agents`, {
|
|
7
|
+
headers: { 'X-API-Key': apiKey },
|
|
8
|
+
});
|
|
9
|
+
const agents = Array.isArray(data) ? data : data.agents ?? [];
|
|
10
|
+
if (agents.length === 0) {
|
|
11
|
+
return 'No agents found. Use arden_provision_agent to create one.';
|
|
12
|
+
}
|
|
13
|
+
const lines = agents.map((agent) => {
|
|
14
|
+
const spent = agent.spent_this_month ?? 0;
|
|
15
|
+
const budget = agent.monthly_budget;
|
|
16
|
+
const remaining = budget !== undefined ? budget - spent : undefined;
|
|
17
|
+
const pct = budget !== undefined && budget > 0 ? ((spent / budget) * 100).toFixed(1) + '%' : 'N/A';
|
|
18
|
+
return [
|
|
19
|
+
`Name: ${agent.name}`,
|
|
20
|
+
`Status: ${agent.status}`,
|
|
21
|
+
`Monthly budget: ${budget !== undefined ? '$' + budget : 'N/A'}`,
|
|
22
|
+
`Spent this month: $${spent}`,
|
|
23
|
+
`Remaining: ${remaining !== undefined ? '$' + remaining : 'N/A'}`,
|
|
24
|
+
`% Used: ${pct}`,
|
|
25
|
+
`Wallet: ${agent.wallet_address ?? 'N/A'}`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
});
|
|
28
|
+
return lines.join('\n\n---\n\n');
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (axios.isAxiosError(err)) {
|
|
32
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message;
|
|
33
|
+
return `Failed to list agents: ${body}`;
|
|
34
|
+
}
|
|
35
|
+
return `Failed to list agents: ${String(err)}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const provisionAgentSchema: {
|
|
2
|
+
name: {
|
|
3
|
+
type: "string";
|
|
4
|
+
description: string;
|
|
5
|
+
};
|
|
6
|
+
monthly_budget: {
|
|
7
|
+
type: "number";
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
daily_budget: {
|
|
11
|
+
type: "number";
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
weekly_budget: {
|
|
15
|
+
type: "number";
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
per_transaction_limit: {
|
|
19
|
+
type: "number";
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
allowed_vendors: {
|
|
23
|
+
type: "string";
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export declare const provisionAgentRequired: readonly ["name", "allowed_vendors"];
|
|
28
|
+
interface ProvisionInput {
|
|
29
|
+
name: string;
|
|
30
|
+
monthly_budget?: number;
|
|
31
|
+
daily_budget?: number;
|
|
32
|
+
weekly_budget?: number;
|
|
33
|
+
per_transaction_limit?: number;
|
|
34
|
+
allowed_vendors: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function provisionAgent(input: ProvisionInput): Promise<string>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=provision.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js';
|
|
3
|
+
export const provisionAgentSchema = {
|
|
4
|
+
name: { type: 'string', description: 'Agent name: 3-50 chars, alphanumeric + hyphens only' },
|
|
5
|
+
monthly_budget: { type: 'number', description: 'Monthly spend budget in USD' },
|
|
6
|
+
daily_budget: { type: 'number', description: 'Daily spend budget in USD' },
|
|
7
|
+
weekly_budget: { type: 'number', description: 'Weekly spend budget in USD' },
|
|
8
|
+
per_transaction_limit: { type: 'number', description: 'Per-transaction spend limit in USD' },
|
|
9
|
+
allowed_vendors: { type: 'string', description: 'Comma-separated vendor domains, or "*" for all vendors' },
|
|
10
|
+
};
|
|
11
|
+
export const provisionAgentRequired = ['name', 'allowed_vendors'];
|
|
12
|
+
export async function provisionAgent(input) {
|
|
13
|
+
const { name, monthly_budget, daily_budget, weekly_budget, per_transaction_limit, allowed_vendors } = input;
|
|
14
|
+
if (!/^[a-zA-Z0-9-]{3,50}$/.test(name)) {
|
|
15
|
+
return 'Invalid agent name. Must be 3-50 characters, alphanumeric and hyphens only.';
|
|
16
|
+
}
|
|
17
|
+
if (!monthly_budget && !daily_budget && !weekly_budget) {
|
|
18
|
+
return 'At least one budget field is required: monthly_budget, daily_budget, or weekly_budget.';
|
|
19
|
+
}
|
|
20
|
+
const apiKey = getApiKey();
|
|
21
|
+
const body = { name, allowed_vendors };
|
|
22
|
+
if (monthly_budget !== undefined)
|
|
23
|
+
body.monthly_budget = monthly_budget;
|
|
24
|
+
if (daily_budget !== undefined)
|
|
25
|
+
body.daily_budget = daily_budget;
|
|
26
|
+
if (weekly_budget !== undefined)
|
|
27
|
+
body.weekly_budget = weekly_budget;
|
|
28
|
+
if (per_transaction_limit !== undefined)
|
|
29
|
+
body.per_transaction_limit = per_transaction_limit;
|
|
30
|
+
try {
|
|
31
|
+
const { data } = await axios.post(`${API_BASE}/agents`, body, {
|
|
32
|
+
headers: { 'X-API-Key': apiKey },
|
|
33
|
+
});
|
|
34
|
+
const { wallet_address, agent_key } = data;
|
|
35
|
+
return [
|
|
36
|
+
`Agent "${name}" provisioned successfully.`,
|
|
37
|
+
`Wallet address: ${wallet_address}`,
|
|
38
|
+
`Agent API key: ${agent_key}`,
|
|
39
|
+
'',
|
|
40
|
+
'IMPORTANT: Save the agent API key — it will not be shown again.',
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (axios.isAxiosError(err)) {
|
|
45
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message;
|
|
46
|
+
return `Failed to provision agent: ${body}`;
|
|
47
|
+
}
|
|
48
|
+
return `Failed to provision agent: ${String(err)}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=provision.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js';
|
|
3
|
+
function formatBudgetWindow(label, window) {
|
|
4
|
+
if (!window)
|
|
5
|
+
return `${label}: not set`;
|
|
6
|
+
const limit = window.limit !== undefined ? '$' + window.limit : 'N/A';
|
|
7
|
+
const spent = window.spent !== undefined ? '$' + window.spent : '$0';
|
|
8
|
+
const remaining = window.limit !== undefined && window.spent !== undefined
|
|
9
|
+
? '$' + (window.limit - window.spent)
|
|
10
|
+
: 'N/A';
|
|
11
|
+
const pct = window.limit !== undefined && window.limit > 0 && window.spent !== undefined
|
|
12
|
+
? ((window.spent / window.limit) * 100).toFixed(1) + '%'
|
|
13
|
+
: 'N/A';
|
|
14
|
+
return `${label}: limit=${limit}, spent=${spent}, remaining=${remaining}, used=${pct}`;
|
|
15
|
+
}
|
|
16
|
+
export async function agentStatus(name) {
|
|
17
|
+
const apiKey = getApiKey();
|
|
18
|
+
try {
|
|
19
|
+
const { data } = await axios.get(`${API_BASE}/agents/${encodeURIComponent(name)}`, {
|
|
20
|
+
headers: { 'X-API-Key': apiKey },
|
|
21
|
+
});
|
|
22
|
+
return [
|
|
23
|
+
`Agent: ${data.name}`,
|
|
24
|
+
`Status: ${data.status}`,
|
|
25
|
+
`Wallet: ${data.wallet_address ?? 'N/A'}`,
|
|
26
|
+
`Allowed vendors: ${data.allowed_vendors ?? 'N/A'}`,
|
|
27
|
+
'',
|
|
28
|
+
'Budget windows:',
|
|
29
|
+
' ' + formatBudgetWindow('Monthly', data.monthly),
|
|
30
|
+
' ' + formatBudgetWindow('Daily', data.daily),
|
|
31
|
+
' ' + formatBudgetWindow('Weekly', data.weekly),
|
|
32
|
+
` Per-transaction limit: ${data.per_transaction_limit !== undefined ? '$' + data.per_transaction_limit : 'not set'}`,
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (axios.isAxiosError(err)) {
|
|
37
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message;
|
|
38
|
+
return `Failed to get agent status: ${body}`;
|
|
39
|
+
}
|
|
40
|
+
return `Failed to get agent status: ${String(err)}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface UpdateInput {
|
|
2
|
+
name: string;
|
|
3
|
+
monthly_budget?: number;
|
|
4
|
+
daily_budget?: number;
|
|
5
|
+
weekly_budget?: number;
|
|
6
|
+
per_transaction_limit?: number;
|
|
7
|
+
allowed_vendors?: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function updateAgent(input: UpdateInput): Promise<string>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js';
|
|
3
|
+
export async function updateAgent(input) {
|
|
4
|
+
const { name, monthly_budget, daily_budget, weekly_budget, per_transaction_limit, allowed_vendors, status } = input;
|
|
5
|
+
const body = {};
|
|
6
|
+
if (monthly_budget !== undefined)
|
|
7
|
+
body.monthly_budget = monthly_budget;
|
|
8
|
+
if (daily_budget !== undefined)
|
|
9
|
+
body.daily_budget = daily_budget;
|
|
10
|
+
if (weekly_budget !== undefined)
|
|
11
|
+
body.weekly_budget = weekly_budget;
|
|
12
|
+
if (per_transaction_limit !== undefined)
|
|
13
|
+
body.per_transaction_limit = per_transaction_limit;
|
|
14
|
+
if (allowed_vendors !== undefined)
|
|
15
|
+
body.allowed_vendors = allowed_vendors;
|
|
16
|
+
if (status !== undefined)
|
|
17
|
+
body.status = status;
|
|
18
|
+
if (Object.keys(body).length === 0) {
|
|
19
|
+
return 'Provide at least one field to update.';
|
|
20
|
+
}
|
|
21
|
+
const apiKey = getApiKey();
|
|
22
|
+
try {
|
|
23
|
+
await axios.patch(`${API_BASE}/agents/${encodeURIComponent(name)}`, body, {
|
|
24
|
+
headers: { 'X-API-Key': apiKey },
|
|
25
|
+
});
|
|
26
|
+
const changed = Object.entries(body)
|
|
27
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
28
|
+
.join('\n');
|
|
29
|
+
return `Agent "${name}" updated successfully:\n${changed}`;
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (axios.isAxiosError(err)) {
|
|
33
|
+
const status = err.response?.status;
|
|
34
|
+
const data = err.response?.data;
|
|
35
|
+
if (status === 404)
|
|
36
|
+
return `Agent '${name}' not found.`;
|
|
37
|
+
if (status === 400)
|
|
38
|
+
return `Bad request: ${typeof data === 'object' ? JSON.stringify(data) : data ?? err.message}`;
|
|
39
|
+
if (status === 401 || status === 403)
|
|
40
|
+
return 'Not authenticated. Set ARDEN_API_KEY or run `arden login`.';
|
|
41
|
+
return `Failed to update agent: ${typeof data === 'object' ? JSON.stringify(data) : data ?? err.message}`;
|
|
42
|
+
}
|
|
43
|
+
return `Failed to update agent: ${String(err)}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=update.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ardensh/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for managing Arden AI agent spend controls",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"arden-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
16
|
+
"axios": "^1.6.0",
|
|
17
|
+
"conf": "^13.0.0",
|
|
18
|
+
"zod": "^3.22.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.0.0",
|
|
22
|
+
"tsx": "^4.0.0",
|
|
23
|
+
"@types/node": "^20.0.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Conf from 'conf'
|
|
2
|
+
import os from 'os'
|
|
3
|
+
|
|
4
|
+
const store = new Conf({ projectName: 'arden', cwd: os.homedir() + '/.arden' })
|
|
5
|
+
|
|
6
|
+
export function getApiKey(): string {
|
|
7
|
+
const fromEnv = process.env.ARDEN_API_KEY
|
|
8
|
+
if (fromEnv) return fromEnv
|
|
9
|
+
const fromConfig = store.get('apiKey') as string | undefined
|
|
10
|
+
if (fromConfig) return fromConfig
|
|
11
|
+
throw new Error('Not authenticated. Set ARDEN_API_KEY or run `arden login`.')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const API_BASE = 'https://ir7hdqp3ei.execute-api.us-east-1.amazonaws.com/prod'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import { provisionAgent } from './tools/provision.js'
|
|
6
|
+
import { listAgents } from './tools/list.js'
|
|
7
|
+
import { agentStatus } from './tools/status.js'
|
|
8
|
+
import { fundAgent } from './tools/fund.js'
|
|
9
|
+
import { updateAgent } from './tools/update.js'
|
|
10
|
+
|
|
11
|
+
const server = new McpServer({
|
|
12
|
+
name: 'arden',
|
|
13
|
+
version: '0.1.0',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
server.tool(
|
|
17
|
+
'arden_provision_agent',
|
|
18
|
+
'Provision a new Arden AI agent with spend controls and vendor restrictions',
|
|
19
|
+
{
|
|
20
|
+
name: z.string().describe('Agent name: 3-50 chars, alphanumeric + hyphens only'),
|
|
21
|
+
monthly_budget: z.number().optional().describe('Monthly spend budget in USD'),
|
|
22
|
+
daily_budget: z.number().optional().describe('Daily spend budget in USD'),
|
|
23
|
+
weekly_budget: z.number().optional().describe('Weekly spend budget in USD'),
|
|
24
|
+
per_transaction_limit: z.number().optional().describe('Per-transaction spend limit in USD'),
|
|
25
|
+
allowed_vendors: z
|
|
26
|
+
.string()
|
|
27
|
+
.describe('Comma-separated vendor domains, or "*" for all vendors'),
|
|
28
|
+
},
|
|
29
|
+
async (input) => {
|
|
30
|
+
const text = await provisionAgent(input)
|
|
31
|
+
return { content: [{ type: 'text', text }] }
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
server.tool(
|
|
36
|
+
'arden_list_agents',
|
|
37
|
+
'List all Arden agents with their status and budget usage',
|
|
38
|
+
{},
|
|
39
|
+
async () => {
|
|
40
|
+
const text = await listAgents()
|
|
41
|
+
return { content: [{ type: 'text', text }] }
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
server.tool(
|
|
46
|
+
'arden_agent_status',
|
|
47
|
+
'Get detailed status and budget breakdown for a specific Arden agent',
|
|
48
|
+
{
|
|
49
|
+
name: z.string().describe('Name of the agent to inspect'),
|
|
50
|
+
},
|
|
51
|
+
async ({ name }) => {
|
|
52
|
+
const text = await agentStatus(name)
|
|
53
|
+
return { content: [{ type: 'text', text }] }
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
server.tool(
|
|
58
|
+
'arden_fund_agent',
|
|
59
|
+
'Get wallet address and funding instructions for an Arden agent',
|
|
60
|
+
{
|
|
61
|
+
name: z.string().describe('Name of the agent to fund'),
|
|
62
|
+
},
|
|
63
|
+
async ({ name }) => {
|
|
64
|
+
const text = await fundAgent(name)
|
|
65
|
+
return { content: [{ type: 'text', text }] }
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
server.tool(
|
|
70
|
+
'arden_update_agent',
|
|
71
|
+
"Update an existing agent's budget limits, allowed vendors, or status",
|
|
72
|
+
{
|
|
73
|
+
name: z.string().describe('Name of the agent to update'),
|
|
74
|
+
monthly_budget: z.number().optional().describe('New monthly budget in USD'),
|
|
75
|
+
daily_budget: z.number().optional().describe('New daily budget in USD'),
|
|
76
|
+
weekly_budget: z.number().optional().describe('New weekly budget in USD'),
|
|
77
|
+
per_transaction_limit: z.number().optional().describe('New per-transaction limit in USD'),
|
|
78
|
+
allowed_vendors: z.string().optional().describe('Comma-separated vendor domains, or "*" for all'),
|
|
79
|
+
status: z.enum(['active', 'paused']).optional().describe('Set agent status to active or paused'),
|
|
80
|
+
},
|
|
81
|
+
async (input) => {
|
|
82
|
+
const text = await updateAgent(input)
|
|
83
|
+
return { content: [{ type: 'text', text }] }
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const transport = new StdioServerTransport()
|
|
88
|
+
await server.connect(transport)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js'
|
|
3
|
+
|
|
4
|
+
interface AgentDetail {
|
|
5
|
+
wallet_address?: string
|
|
6
|
+
[key: string]: unknown
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function fundAgent(name: string): Promise<string> {
|
|
10
|
+
const apiKey = getApiKey()
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await axios.get<AgentDetail>(`${API_BASE}/agents/${encodeURIComponent(name)}`, {
|
|
14
|
+
headers: { 'X-API-Key': apiKey },
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const wallet = data.wallet_address
|
|
18
|
+
if (!wallet) {
|
|
19
|
+
return `Agent "${name}" does not have a wallet address yet.`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
`Funding instructions for agent "${name}":`,
|
|
24
|
+
'',
|
|
25
|
+
`Wallet address: ${wallet}`,
|
|
26
|
+
'',
|
|
27
|
+
'To fund this agent:',
|
|
28
|
+
' 1. Send USDC to the wallet address above.',
|
|
29
|
+
' 2. Network: Base mainnet (chain ID 8453).',
|
|
30
|
+
' 3. Token: USDC (native USDC on Base, not bridged).',
|
|
31
|
+
'',
|
|
32
|
+
'The agent will use this wallet for x402 payments when making API calls on your behalf.',
|
|
33
|
+
'Funds are available immediately after the transaction is confirmed on-chain.',
|
|
34
|
+
].join('\n')
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (axios.isAxiosError(err)) {
|
|
37
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message
|
|
38
|
+
return `Failed to retrieve agent wallet: ${body}`
|
|
39
|
+
}
|
|
40
|
+
return `Failed to retrieve agent wallet: ${String(err)}`
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js'
|
|
3
|
+
|
|
4
|
+
interface Agent {
|
|
5
|
+
name: string
|
|
6
|
+
status: string
|
|
7
|
+
monthly_budget?: number
|
|
8
|
+
spent_this_month?: number
|
|
9
|
+
wallet_address?: string
|
|
10
|
+
[key: string]: unknown
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function listAgents(): Promise<string> {
|
|
14
|
+
const apiKey = getApiKey()
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const { data } = await axios.get<Agent[]>(`${API_BASE}/agents`, {
|
|
18
|
+
headers: { 'X-API-Key': apiKey },
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const agents: Agent[] = Array.isArray(data) ? data : (data as { agents?: Agent[] }).agents ?? []
|
|
22
|
+
|
|
23
|
+
if (agents.length === 0) {
|
|
24
|
+
return 'No agents found. Use arden_provision_agent to create one.'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const lines = agents.map((agent) => {
|
|
28
|
+
const spent = agent.spent_this_month ?? 0
|
|
29
|
+
const budget = agent.monthly_budget
|
|
30
|
+
const remaining = budget !== undefined ? budget - spent : undefined
|
|
31
|
+
const pct =
|
|
32
|
+
budget !== undefined && budget > 0 ? ((spent / budget) * 100).toFixed(1) + '%' : 'N/A'
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
`Name: ${agent.name}`,
|
|
36
|
+
`Status: ${agent.status}`,
|
|
37
|
+
`Monthly budget: ${budget !== undefined ? '$' + budget : 'N/A'}`,
|
|
38
|
+
`Spent this month: $${spent}`,
|
|
39
|
+
`Remaining: ${remaining !== undefined ? '$' + remaining : 'N/A'}`,
|
|
40
|
+
`% Used: ${pct}`,
|
|
41
|
+
`Wallet: ${agent.wallet_address ?? 'N/A'}`,
|
|
42
|
+
].join('\n')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return lines.join('\n\n---\n\n')
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (axios.isAxiosError(err)) {
|
|
48
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message
|
|
49
|
+
return `Failed to list agents: ${body}`
|
|
50
|
+
}
|
|
51
|
+
return `Failed to list agents: ${String(err)}`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js'
|
|
3
|
+
|
|
4
|
+
export const provisionAgentSchema = {
|
|
5
|
+
name: { type: 'string' as const, description: 'Agent name: 3-50 chars, alphanumeric + hyphens only' },
|
|
6
|
+
monthly_budget: { type: 'number' as const, description: 'Monthly spend budget in USD' },
|
|
7
|
+
daily_budget: { type: 'number' as const, description: 'Daily spend budget in USD' },
|
|
8
|
+
weekly_budget: { type: 'number' as const, description: 'Weekly spend budget in USD' },
|
|
9
|
+
per_transaction_limit: { type: 'number' as const, description: 'Per-transaction spend limit in USD' },
|
|
10
|
+
allowed_vendors: { type: 'string' as const, description: 'Comma-separated vendor domains, or "*" for all vendors' },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const provisionAgentRequired = ['name', 'allowed_vendors'] as const
|
|
14
|
+
|
|
15
|
+
interface ProvisionInput {
|
|
16
|
+
name: string
|
|
17
|
+
monthly_budget?: number
|
|
18
|
+
daily_budget?: number
|
|
19
|
+
weekly_budget?: number
|
|
20
|
+
per_transaction_limit?: number
|
|
21
|
+
allowed_vendors: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function provisionAgent(input: ProvisionInput): Promise<string> {
|
|
25
|
+
const { name, monthly_budget, daily_budget, weekly_budget, per_transaction_limit, allowed_vendors } = input
|
|
26
|
+
|
|
27
|
+
if (!/^[a-zA-Z0-9-]{3,50}$/.test(name)) {
|
|
28
|
+
return 'Invalid agent name. Must be 3-50 characters, alphanumeric and hyphens only.'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!monthly_budget && !daily_budget && !weekly_budget) {
|
|
32
|
+
return 'At least one budget field is required: monthly_budget, daily_budget, or weekly_budget.'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const apiKey = getApiKey()
|
|
36
|
+
|
|
37
|
+
const body: Record<string, unknown> = { name, allowed_vendors }
|
|
38
|
+
if (monthly_budget !== undefined) body.monthly_budget = monthly_budget
|
|
39
|
+
if (daily_budget !== undefined) body.daily_budget = daily_budget
|
|
40
|
+
if (weekly_budget !== undefined) body.weekly_budget = weekly_budget
|
|
41
|
+
if (per_transaction_limit !== undefined) body.per_transaction_limit = per_transaction_limit
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const { data } = await axios.post(`${API_BASE}/agents`, body, {
|
|
45
|
+
headers: { 'X-API-Key': apiKey },
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const { wallet_address, agent_key } = data
|
|
49
|
+
return [
|
|
50
|
+
`Agent "${name}" provisioned successfully.`,
|
|
51
|
+
`Wallet address: ${wallet_address}`,
|
|
52
|
+
`Agent API key: ${agent_key}`,
|
|
53
|
+
'',
|
|
54
|
+
'IMPORTANT: Save the agent API key — it will not be shown again.',
|
|
55
|
+
].join('\n')
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (axios.isAxiosError(err)) {
|
|
58
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message
|
|
59
|
+
return `Failed to provision agent: ${body}`
|
|
60
|
+
}
|
|
61
|
+
return `Failed to provision agent: ${String(err)}`
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js'
|
|
3
|
+
|
|
4
|
+
interface Budget {
|
|
5
|
+
limit?: number
|
|
6
|
+
spent?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AgentDetail {
|
|
10
|
+
name: string
|
|
11
|
+
status: string
|
|
12
|
+
wallet_address?: string
|
|
13
|
+
allowed_vendors?: string
|
|
14
|
+
monthly?: Budget
|
|
15
|
+
daily?: Budget
|
|
16
|
+
weekly?: Budget
|
|
17
|
+
per_transaction_limit?: number
|
|
18
|
+
[key: string]: unknown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatBudgetWindow(label: string, window?: Budget): string {
|
|
22
|
+
if (!window) return `${label}: not set`
|
|
23
|
+
const limit = window.limit !== undefined ? '$' + window.limit : 'N/A'
|
|
24
|
+
const spent = window.spent !== undefined ? '$' + window.spent : '$0'
|
|
25
|
+
const remaining =
|
|
26
|
+
window.limit !== undefined && window.spent !== undefined
|
|
27
|
+
? '$' + (window.limit - window.spent)
|
|
28
|
+
: 'N/A'
|
|
29
|
+
const pct =
|
|
30
|
+
window.limit !== undefined && window.limit > 0 && window.spent !== undefined
|
|
31
|
+
? ((window.spent / window.limit) * 100).toFixed(1) + '%'
|
|
32
|
+
: 'N/A'
|
|
33
|
+
return `${label}: limit=${limit}, spent=${spent}, remaining=${remaining}, used=${pct}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function agentStatus(name: string): Promise<string> {
|
|
37
|
+
const apiKey = getApiKey()
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const { data } = await axios.get<AgentDetail>(`${API_BASE}/agents/${encodeURIComponent(name)}`, {
|
|
41
|
+
headers: { 'X-API-Key': apiKey },
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
`Agent: ${data.name}`,
|
|
46
|
+
`Status: ${data.status}`,
|
|
47
|
+
`Wallet: ${data.wallet_address ?? 'N/A'}`,
|
|
48
|
+
`Allowed vendors: ${data.allowed_vendors ?? 'N/A'}`,
|
|
49
|
+
'',
|
|
50
|
+
'Budget windows:',
|
|
51
|
+
' ' + formatBudgetWindow('Monthly', data.monthly),
|
|
52
|
+
' ' + formatBudgetWindow('Daily', data.daily),
|
|
53
|
+
' ' + formatBudgetWindow('Weekly', data.weekly),
|
|
54
|
+
` Per-transaction limit: ${data.per_transaction_limit !== undefined ? '$' + data.per_transaction_limit : 'not set'}`,
|
|
55
|
+
].join('\n')
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (axios.isAxiosError(err)) {
|
|
58
|
+
const body = err.response?.data ? JSON.stringify(err.response.data) : err.message
|
|
59
|
+
return `Failed to get agent status: ${body}`
|
|
60
|
+
}
|
|
61
|
+
return `Failed to get agent status: ${String(err)}`
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { getApiKey, API_BASE } from '../auth.js'
|
|
3
|
+
|
|
4
|
+
interface UpdateInput {
|
|
5
|
+
name: string
|
|
6
|
+
monthly_budget?: number
|
|
7
|
+
daily_budget?: number
|
|
8
|
+
weekly_budget?: number
|
|
9
|
+
per_transaction_limit?: number
|
|
10
|
+
allowed_vendors?: string
|
|
11
|
+
status?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function updateAgent(input: UpdateInput): Promise<string> {
|
|
15
|
+
const { name, monthly_budget, daily_budget, weekly_budget, per_transaction_limit, allowed_vendors, status } = input
|
|
16
|
+
|
|
17
|
+
const body: Record<string, unknown> = {}
|
|
18
|
+
if (monthly_budget !== undefined) body.monthly_budget = monthly_budget
|
|
19
|
+
if (daily_budget !== undefined) body.daily_budget = daily_budget
|
|
20
|
+
if (weekly_budget !== undefined) body.weekly_budget = weekly_budget
|
|
21
|
+
if (per_transaction_limit !== undefined) body.per_transaction_limit = per_transaction_limit
|
|
22
|
+
if (allowed_vendors !== undefined) body.allowed_vendors = allowed_vendors
|
|
23
|
+
if (status !== undefined) body.status = status
|
|
24
|
+
|
|
25
|
+
if (Object.keys(body).length === 0) {
|
|
26
|
+
return 'Provide at least one field to update.'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const apiKey = getApiKey()
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await axios.patch(`${API_BASE}/agents/${encodeURIComponent(name)}`, body, {
|
|
33
|
+
headers: { 'X-API-Key': apiKey },
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const changed = Object.entries(body)
|
|
37
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
38
|
+
.join('\n')
|
|
39
|
+
return `Agent "${name}" updated successfully:\n${changed}`
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (axios.isAxiosError(err)) {
|
|
42
|
+
const status = err.response?.status
|
|
43
|
+
const data = err.response?.data
|
|
44
|
+
if (status === 404) return `Agent '${name}' not found.`
|
|
45
|
+
if (status === 400) return `Bad request: ${typeof data === 'object' ? JSON.stringify(data) : data ?? err.message}`
|
|
46
|
+
if (status === 401 || status === 403) return 'Not authenticated. Set ARDEN_API_KEY or run `arden login`.'
|
|
47
|
+
return `Failed to update agent: ${typeof data === 'object' ? JSON.stringify(data) : data ?? err.message}`
|
|
48
|
+
}
|
|
49
|
+
return `Failed to update agent: ${String(err)}`
|
|
50
|
+
}
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|