@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 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
@@ -0,0 +1,3 @@
1
+ export declare function getApiKey(): string;
2
+ export declare const API_BASE = "https://ir7hdqp3ei.execute-api.us-east-1.amazonaws.com/prod";
3
+ //# sourceMappingURL=auth.d.ts.map
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
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
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,2 @@
1
+ export declare function fundAgent(name: string): Promise<string>;
2
+ //# sourceMappingURL=fund.d.ts.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,2 @@
1
+ export declare function listAgents(): Promise<string>;
2
+ //# sourceMappingURL=list.d.ts.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,2 @@
1
+ export declare function agentStatus(name: string): Promise<string>;
2
+ //# sourceMappingURL=status.d.ts.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
+ }