@oneshot-agent/mcp-server 0.15.0 → 0.17.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 +10 -0
- package/dist/budget-env.d.ts +18 -0
- package/dist/budget-env.js +47 -0
- package/dist/index.js +15 -3
- package/dist/tools/domains.js +1 -1
- package/dist/tools/email.js +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/wallet.d.ts +2 -0
- package/dist/tools/wallet.js +14 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,6 +20,11 @@ The server supports two auth methods: **CDP Wallet** (recommended, no private ke
|
|
|
20
20
|
| `CDP_API_KEY_SECRET` | Option A | Coinbase CDP API key secret |
|
|
21
21
|
| `CDP_WALLET_SECRET` | Option A | Coinbase CDP wallet secret |
|
|
22
22
|
| `ONESHOT_WALLET_PRIVATE_KEY` | Option B | Raw private key for signing payments |
|
|
23
|
+
| `ONESHOT_BUDGET_DAILY` | No | Max USDC the agent may spend per UTC day (spend budget, enforced server-side) |
|
|
24
|
+
| `ONESHOT_BUDGET_PER_TRANSACTION` | No | Max USDC for any single call |
|
|
25
|
+
| `ONESHOT_BUDGET_ALERT_AT` | No | Fraction of the daily budget that triggers a warning (default 0.8) |
|
|
26
|
+
| `ONESHOT_BUDGET_PAUSE_AT` | No | Fraction at which paid calls stop (default 1.0) |
|
|
27
|
+
| `ONESHOT_BUDGET_ALERT_EMAIL` | No | Email for budget alerts |
|
|
23
28
|
|
|
24
29
|
### Claude Desktop
|
|
25
30
|
|
|
@@ -83,6 +88,10 @@ Add to `~/.claude/settings.json`:
|
|
|
83
88
|
|
|
84
89
|
Get CDP credentials at [Coinbase Agentic Wallet](https://docs.cdp.coinbase.com/agentic-wallet/welcome). Or use `ONESHOT_WALLET_PRIVATE_KEY` instead of CDP env vars for raw key auth.
|
|
85
90
|
|
|
91
|
+
### Read authentication
|
|
92
|
+
|
|
93
|
+
Read tools (inbox, SMS inbox, notifications, balance, browser profiles) return private, per-agent data. The server wraps the OneShot TypeScript SDK, which automatically signs a short-lived **EIP-712 read proof** (`x-agent-proof`) on each read so the API can verify you control the `X-Agent-ID` wallet. No configuration needed — this ships via `@oneshot-agent/sdk >= 0.25.0` (the bundled dependency). Read-proof verification is rolling out in log-only mode server-side, so older callers keep working until enforcement is enabled.
|
|
94
|
+
|
|
86
95
|
## Available Tools
|
|
87
96
|
|
|
88
97
|
### Communication
|
|
@@ -181,6 +190,7 @@ Get CDP credentials at [Coinbase Agentic Wallet](https://docs.cdp.coinbase.com/a
|
|
|
181
190
|
| `oneshot_notifications` | List agent notifications |
|
|
182
191
|
| `oneshot_mark_notification_read` | Mark notification as read |
|
|
183
192
|
| `oneshot_get_balance` | Get USDC wallet balance |
|
|
193
|
+
| `oneshot_budget_status` | Today's spend vs the configured budget (read-only) |
|
|
184
194
|
|
|
185
195
|
All paid tools are priced in USDC via the x402 protocol. See [Pricing](https://docs.oneshotagent.com/pricing) for current rates.
|
|
186
196
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend budget from ONESHOT_BUDGET_* env vars. Set at the process level by the
|
|
3
|
+
* developer, never by the model: the only budget tool exposed to the agent
|
|
4
|
+
* (oneshot_budget_status) is read-only — a guardrail the agent can raise
|
|
5
|
+
* itself isn't a guardrail. Unset → undefined → the SDK leaves whatever is
|
|
6
|
+
* stored server-side untouched.
|
|
7
|
+
*/
|
|
8
|
+
export declare function budgetConfigFromEnv(env?: NodeJS.ProcessEnv): {
|
|
9
|
+
budgets?: {
|
|
10
|
+
daily?: number;
|
|
11
|
+
perTransaction?: number;
|
|
12
|
+
alertAt?: number;
|
|
13
|
+
pauseAt?: number;
|
|
14
|
+
};
|
|
15
|
+
alerts?: {
|
|
16
|
+
email?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend budget from ONESHOT_BUDGET_* env vars. Set at the process level by the
|
|
3
|
+
* developer, never by the model: the only budget tool exposed to the agent
|
|
4
|
+
* (oneshot_budget_status) is read-only — a guardrail the agent can raise
|
|
5
|
+
* itself isn't a guardrail. Unset → undefined → the SDK leaves whatever is
|
|
6
|
+
* stored server-side untouched.
|
|
7
|
+
*/
|
|
8
|
+
export function budgetConfigFromEnv(env = process.env) {
|
|
9
|
+
const problems = [];
|
|
10
|
+
// A set-but-invalid value must fail startup, not silently become "no cap":
|
|
11
|
+
// the SDK would reject it server-side and the agent would run unguarded.
|
|
12
|
+
const read = (name, check, rule) => {
|
|
13
|
+
const raw = env[name];
|
|
14
|
+
if (raw === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
// Blank = unset, deliberately: `${VAR}` templating in compose/CI yields ""
|
|
17
|
+
// for an unset variable, and failing startup there would break every
|
|
18
|
+
// deployment that templates optional vars. Warn so it's never silent.
|
|
19
|
+
if (raw.trim() === "") {
|
|
20
|
+
console.error(`Warning: ${name} is set but blank — treating as unset (no cap from this variable)`);
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const n = Number(raw);
|
|
24
|
+
if (!Number.isFinite(n) || !check(n)) {
|
|
25
|
+
problems.push(`${name}=${JSON.stringify(raw)} (${rule})`);
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return n;
|
|
29
|
+
};
|
|
30
|
+
const budgets = {
|
|
31
|
+
daily: read("ONESHOT_BUDGET_DAILY", (n) => n > 0, "must be a positive number"),
|
|
32
|
+
perTransaction: read("ONESHOT_BUDGET_PER_TRANSACTION", (n) => n > 0, "must be a positive number"),
|
|
33
|
+
alertAt: read("ONESHOT_BUDGET_ALERT_AT", (n) => n > 0 && n <= 1, "must be a fraction in (0, 1]"),
|
|
34
|
+
pauseAt: read("ONESHOT_BUDGET_PAUSE_AT", (n) => n > 0 && n <= 1, "must be a fraction in (0, 1]"),
|
|
35
|
+
};
|
|
36
|
+
const email = env.ONESHOT_BUDGET_ALERT_EMAIL?.trim();
|
|
37
|
+
if (email && !email.includes("@"))
|
|
38
|
+
problems.push(`ONESHOT_BUDGET_ALERT_EMAIL=${JSON.stringify(email)} (must be an email address)`);
|
|
39
|
+
if (problems.length) {
|
|
40
|
+
throw new Error(`Invalid spend budget configuration:\n ${problems.join("\n ")}`);
|
|
41
|
+
}
|
|
42
|
+
const hasBudget = Object.values(budgets).some((v) => v !== undefined);
|
|
43
|
+
return {
|
|
44
|
+
...(hasBudget ? { budgets } : {}),
|
|
45
|
+
...(email ? { alerts: { email } } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,17 +4,29 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { OneShot } from "@oneshot-agent/sdk";
|
|
6
6
|
import { tools, handleToolCall } from "./tools/index.js";
|
|
7
|
+
import { budgetConfigFromEnv } from "./budget-env.js";
|
|
7
8
|
const CDP_KEY_ID = process.env.CDP_API_KEY_ID;
|
|
8
9
|
const PRIVATE_KEY = process.env.ONESHOT_WALLET_PRIVATE_KEY;
|
|
9
10
|
async function initAgent() {
|
|
11
|
+
let budget;
|
|
12
|
+
try {
|
|
13
|
+
budget = budgetConfigFromEnv();
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
// Fail visibly: a mis-set ONESHOT_BUDGET_* must not start an unguarded agent.
|
|
17
|
+
console.error(`Error: ${err.message}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (budget.budgets)
|
|
21
|
+
console.error(`Spend budget: ${JSON.stringify(budget.budgets)}`);
|
|
10
22
|
if (CDP_KEY_ID) {
|
|
11
23
|
// Preferred: Coinbase CDP Server Wallet (no private key exposure)
|
|
12
24
|
console.error("Using Coinbase CDP wallet");
|
|
13
|
-
return OneShot.create({ cdp: true });
|
|
25
|
+
return OneShot.create({ cdp: true, ...budget });
|
|
14
26
|
}
|
|
15
27
|
if (PRIVATE_KEY) {
|
|
16
28
|
// Fallback: raw private key
|
|
17
|
-
return new OneShot({ privateKey: PRIVATE_KEY });
|
|
29
|
+
return new OneShot({ privateKey: PRIVATE_KEY, ...budget });
|
|
18
30
|
}
|
|
19
31
|
console.error("Error: Set CDP_API_KEY_ID + CDP_API_KEY_SECRET + CDP_WALLET_SECRET (recommended)\n" +
|
|
20
32
|
" or ONESHOT_WALLET_PRIVATE_KEY");
|
|
@@ -26,7 +38,7 @@ async function main() {
|
|
|
26
38
|
// Create MCP server
|
|
27
39
|
const server = new Server({
|
|
28
40
|
name: "oneshot-mcp",
|
|
29
|
-
version: "0.
|
|
41
|
+
version: "0.17.0", // keep in sync with package.json; guarded by version.test.ts
|
|
30
42
|
}, {
|
|
31
43
|
capabilities: {
|
|
32
44
|
tools: {},
|
package/dist/tools/domains.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const listDomainsTool = {
|
|
2
2
|
name: "oneshot_list_domains",
|
|
3
|
-
description: "List the agent's sending domains. Each has default_from (canonical default sender, e.g. agent@domain — billed like any address on its first send), addresses[] (mailboxes already provisioned — {address, status, warmup_state}; empty means none yet), pool_status (rotation eligibility: active|paused|removed), warmup_state (reputation health: warming|warmed|degraded), pause_reason (warming|low_reputation|manual|null), warmup_score, and daily send limits. A domain can be paused AND warming — reputation pauses auto-recover once warmed; manual pauses need a resume. Free.",
|
|
3
|
+
description: "List the agent's sending domains. Each has default_from (canonical default sender, e.g. agent@domain — billed like any address on its first send), mailbox_mode (relay|mailbox), addresses[] (mailboxes already provisioned — {address, status, warmup_state}; empty means none yet), pool_status (rotation eligibility: active|paused|removed), warmup_state (reputation health: warming|warmed|degraded), pause_reason (warming|low_reputation|manual|null), warmup_score, and daily send limits. mailbox_mode is a property of the domain, fixed when it was set up: on a 'mailbox' domain EVERY address not already active in addresses[] bills the one-time mailbox_provisioning_fee on its first send, the canonical default_from included — omitting mailbox_mode when you quote does not make the send relay. 'relay' domains never charge it. A domain can be paused AND warming — reputation pauses auto-recover once warmed; manual pauses need a resume. Free.",
|
|
4
4
|
inputSchema: { type: "object", properties: {} },
|
|
5
5
|
};
|
|
6
6
|
export const pauseDomainTool = {
|
package/dist/tools/email.js
CHANGED
|
@@ -35,7 +35,7 @@ export const emailTool = {
|
|
|
35
35
|
mailbox_mode: {
|
|
36
36
|
type: "string",
|
|
37
37
|
enum: ["relay", "mailbox"],
|
|
38
|
-
description: "How this domain sends (optional
|
|
38
|
+
description: "How this domain sends (optional). 'relay' = header send, no per-address mailbox, no mailbox fee. 'mailbox' = a real dedicated mailbox per address (better deliverability + per-address warmup); each new address adds a one-time mailbox_provisioning_fee to the quote. Only honored while a domain is unprovisioned (brand new, or owned with provisioning_status 'unprovisioned') — an already-provisioned domain keeps the mode it was created with, which means OMITTING this does not make a send relay and does not avoid the fee. On a domain already in mailbox mode every address that isn't active yet bills the fee on its first send, the default agent@ included; from_mailbox does not change that. Use oneshot_list_domains to see each domain's mailbox_mode before quoting.",
|
|
39
39
|
},
|
|
40
40
|
attachments: {
|
|
41
41
|
type: "array",
|
package/dist/tools/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { enrichProfileTool, findEmailTool, verifyEmailTool, handleEnrichProfile,
|
|
|
8
8
|
import { inboxListTool, inboxGetTool, handleInboxList, handleInboxGet } from "./inbox.js";
|
|
9
9
|
import { smsInboxListTool, smsInboxGetTool, handleSmsInboxList, handleSmsInboxGet } from "./sms-inbox.js";
|
|
10
10
|
import { notificationsTool, markReadTool, handleNotifications, handleMarkRead } from "./notifications.js";
|
|
11
|
-
import { getBalanceTool, handleGetBalance } from "./wallet.js";
|
|
11
|
+
import { getBalanceTool, budgetStatusTool, handleGetBalance, handleBudgetStatus } from "./wallet.js";
|
|
12
12
|
import { buildTool, updateBuildTool, handleBuild, handleUpdateBuild } from "./build.js";
|
|
13
13
|
import { browserTool, browserCreateProfileTool, browserListProfilesTool, browserDeleteProfileTool, handleBrowser, handleBrowserCreateProfile, handleBrowserListProfiles, handleBrowserDeleteProfile } from "./browser.js";
|
|
14
14
|
import { webSearchTool, handleWebSearch } from "./search.js";
|
|
@@ -77,6 +77,7 @@ export const tools = [
|
|
|
77
77
|
notificationsTool,
|
|
78
78
|
markReadTool,
|
|
79
79
|
getBalanceTool,
|
|
80
|
+
budgetStatusTool,
|
|
80
81
|
];
|
|
81
82
|
// Tool handlers map
|
|
82
83
|
const handlers = {
|
|
@@ -137,6 +138,7 @@ const handlers = {
|
|
|
137
138
|
"oneshot_notifications": handleNotifications,
|
|
138
139
|
"oneshot_mark_notification_read": handleMarkRead,
|
|
139
140
|
"oneshot_get_balance": handleGetBalance,
|
|
141
|
+
"oneshot_budget_status": handleBudgetStatus,
|
|
140
142
|
};
|
|
141
143
|
export async function handleToolCall(agent, toolName, args) {
|
|
142
144
|
const handler = handlers[toolName];
|
package/dist/tools/wallet.d.ts
CHANGED
|
@@ -2,3 +2,5 @@ import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
|
2
2
|
import { OneShot } from "@oneshot-agent/sdk";
|
|
3
3
|
export declare const getBalanceTool: Tool;
|
|
4
4
|
export declare function handleGetBalance(agent: OneShot, _args: Record<string, unknown>): Promise<import("@oneshot-agent/sdk").UnifiedBalance>;
|
|
5
|
+
export declare const budgetStatusTool: Tool;
|
|
6
|
+
export declare function handleBudgetStatus(agent: OneShot, _args: Record<string, unknown>): Promise<import("@oneshot-agent/sdk").AgentBudgetStatus>;
|
package/dist/tools/wallet.js
CHANGED
|
@@ -10,3 +10,17 @@ export async function handleGetBalance(agent, _args) {
|
|
|
10
10
|
const balance = await agent.getUnifiedBalance();
|
|
11
11
|
return balance;
|
|
12
12
|
}
|
|
13
|
+
export const budgetStatusTool = {
|
|
14
|
+
name: "oneshot_budget_status",
|
|
15
|
+
description: "Read-only: today's spend versus the agent's configured spend budget — daily cap, per-call cap, " +
|
|
16
|
+
"spent_today_usdc, remaining_usdc, pct_used and resets_at (next UTC midnight). Use it to plan " +
|
|
17
|
+
"remaining work; paid calls are rejected with budget_exceeded once the cap is reached. The budget " +
|
|
18
|
+
"itself is set by the operator via ONESHOT_BUDGET_* env vars and cannot be changed from here. Free.",
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export async function handleBudgetStatus(agent, _args) {
|
|
25
|
+
return agent.budgets();
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneshot-agent/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "MCP server for OneShot - commercial actions for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@coinbase/cdp-sdk": "1.45.0",
|
|
27
27
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
28
|
-
"@oneshot-agent/sdk": "^0.
|
|
28
|
+
"@oneshot-agent/sdk": "^0.27.0",
|
|
29
29
|
"zod": "3.25.76"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|