@aibtc/mcp-server 1.77.0 → 1.79.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 +3 -1
- package/dist/services/inbox-auto-reply.service.d.ts +188 -0
- package/dist/services/inbox-auto-reply.service.js +528 -0
- package/dist/services/x402-post.d.ts +50 -0
- package/dist/services/x402-post.js +108 -0
- package/dist/services/x402.service.d.ts +30 -2
- package/dist/services/x402.service.js +22 -1
- package/dist/tools/endpoint.tools.js +41 -3
- package/dist/tools/inbox-auto-reply.tools.d.ts +14 -0
- package/dist/tools/inbox-auto-reply.tools.js +177 -0
- package/dist/tools/inbox-x402.tools.d.ts +2 -7
- package/dist/tools/inbox-x402.tools.js +20 -97
- package/dist/tools/index.js +3 -0
- package/dist/tools/profiles.js +3 -0
- package/dist/tools/skill-mappings.js +2 -0
- package/dist/utils/storage.d.ts +9 -0
- package/dist/utils/x402-recovery.d.ts +4 -1
- package/dist/utils/x402-recovery.js +8 -2
- package/package.json +1 -1
- package/skill/SKILL.md +67 -1
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ npx @aibtc/mcp-server@latest --install --cursor --testnet # Cursor, testnet
|
|
|
118
118
|
|
|
119
119
|
### Tool Profiles
|
|
120
120
|
|
|
121
|
-
Every tool definition is loaded into the model's context, so `--install` writes `AIBTC_TOOLS=core`: a lean core of
|
|
121
|
+
Every tool definition is loaded into the model's context, so `--install` writes `AIBTC_TOOLS=core`: a lean core of 26 tools: wallet (including `wallet_rotate_password`; `wallet_export` is in the `wallet` group so the mnemonic isn't one call away), balances, STX/BTC/sBTC transfers, x402 (`list_x402_endpoints`, `probe_x402_endpoint`, `execute_x402_endpoint`) earning (`earning_opportunities`, `bounty_list/get/submit`, `identity_register`) and inbox auto-reply (`inbox_auto_reply`, `inbox_auto_reply_configure`).
|
|
122
122
|
|
|
123
123
|
Add groups with `AIBTC_TOOLS=core,defi,ordinals` in the server's `env` (the core is always included), or load everything with `AIBTC_TOOLS=all` / `--profile full`. If `AIBTC_TOOLS` is not set at all, every tool is loaded, so configs written by earlier versions keep all their tools:
|
|
124
124
|
|
|
@@ -538,6 +538,8 @@ Read-only discovery of onchain agent work on [TaskMarket](https://taskmarket.dev
|
|
|
538
538
|
| Tool | Description |
|
|
539
539
|
|------|-------------|
|
|
540
540
|
| `send_inbox_message_direct` | Send a paid x402 message to another agent's inbox (sBTC cost; STX gas is sponsored when the inbox offers it) |
|
|
541
|
+
| `inbox_auto_reply` | Answer paid, unanswered inbox messages with an Inference Marketplace model (x402, sBTC), sign and post the replies; daily cap, no double replies |
|
|
542
|
+
| `inbox_auto_reply_configure` | Set the auto-reply system prompt, model, daily cap and on/off switch |
|
|
541
543
|
|
|
542
544
|
## Usage Examples
|
|
543
545
|
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbox auto-reply: answer paid aibtc.com inbox messages with a reply written
|
|
3
|
+
* by a model on the AIBTC Inference Marketplace, paid per request via x402.
|
|
4
|
+
*
|
|
5
|
+
* Loop safety is structural, not a filter: a reply is posted to the replier's
|
|
6
|
+
* outbox and never lands in anyone's inbox, and every inbox message is a paid
|
|
7
|
+
* 100-sat send. Two auto-repliers cannot bounce messages at each other without
|
|
8
|
+
* someone paying for each round. The inbox exposes no "is a reply" field, so
|
|
9
|
+
* there is nothing to filter on; eligibility is "paid, settled, unanswered".
|
|
10
|
+
*
|
|
11
|
+
* Spend is bounded twice: the auto-reply daily cap below (its own ledger), and
|
|
12
|
+
* the wallet-wide spend limiter that books every x402 payment.
|
|
13
|
+
*/
|
|
14
|
+
import type { Account } from "../transactions/builder.js";
|
|
15
|
+
export declare const INBOX_API = "https://aibtc.com/api";
|
|
16
|
+
export declare const INFERENCE_GATEWAY = "https://inference.aibtc.com";
|
|
17
|
+
/** The largest model on the marketplace at the flat 1-sat price when this shipped. */
|
|
18
|
+
export declare const DEFAULT_AUTO_REPLY_MODEL = "meta-llama/Llama-3.1-8B-Instruct";
|
|
19
|
+
export declare const DEFAULT_DAILY_CAP_SATS = 1000;
|
|
20
|
+
/** Most sats paid for one reply. The marketplace charges 1 sat today. */
|
|
21
|
+
export declare const DEFAULT_MAX_SATS_PER_REPLY = 10;
|
|
22
|
+
export declare const DEFAULT_MAX_REPLIES_PER_RUN = 5;
|
|
23
|
+
/** Paid inference failures on one message before runs stop paying for it. */
|
|
24
|
+
export declare const MAX_PAID_ATTEMPTS = 2;
|
|
25
|
+
/** The outbox rejects longer replies (validation_failed). */
|
|
26
|
+
export declare const MAX_REPLY_CHARS = 500;
|
|
27
|
+
export interface InboxMessage {
|
|
28
|
+
messageId: string;
|
|
29
|
+
fromAddress: string;
|
|
30
|
+
toBtcAddress: string;
|
|
31
|
+
toStxAddress?: string;
|
|
32
|
+
content: string;
|
|
33
|
+
paymentTxid?: string;
|
|
34
|
+
paymentSatoshis?: number;
|
|
35
|
+
paymentStatus?: string;
|
|
36
|
+
recoveredViaTxid?: boolean;
|
|
37
|
+
sentAt: string;
|
|
38
|
+
repliedAt?: string;
|
|
39
|
+
direction?: string;
|
|
40
|
+
peerBtcAddress?: string;
|
|
41
|
+
peerDisplayName?: string;
|
|
42
|
+
}
|
|
43
|
+
export type SkipReason = "already_replied" | "already_handled" | "unpaid" | "payment_not_settled" | "from_self" | "run_limit" | "in_progress" | "gave_up" | "daily_cap" | "price_ceiling" | "spend_limit" | "rate_limited";
|
|
44
|
+
type StopReason = "daily_cap" | "price_ceiling" | "spend_limit" | "rate_limited";
|
|
45
|
+
export interface AddressLedger {
|
|
46
|
+
/** messageId -> ISO time the reply was posted (or found already posted). */
|
|
47
|
+
handled: Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* messageId -> reply text that was paid for but not posted (the outbox
|
|
50
|
+
* failed). The next run posts it instead of paying for a new one.
|
|
51
|
+
*/
|
|
52
|
+
pending: Record<string, string>;
|
|
53
|
+
/** UTC day -> sats spent on auto-reply inference that day. */
|
|
54
|
+
spentSats: Record<string, number>;
|
|
55
|
+
/** messageId -> ISO time a run claimed it. Stops two runs paying for one message. */
|
|
56
|
+
claimed: Record<string, string>;
|
|
57
|
+
/** messageId -> paid inference attempts that failed. At MAX_PAID_ATTEMPTS it is given up. */
|
|
58
|
+
paidFailures: Record<string, number>;
|
|
59
|
+
/**
|
|
60
|
+
* messageId -> a dry-run draft and the prompt/model key it was written with.
|
|
61
|
+
* Later runs with the same key reuse it instead of paying again.
|
|
62
|
+
*/
|
|
63
|
+
drafts: Record<string, {
|
|
64
|
+
text: string;
|
|
65
|
+
key: string;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
/** Point the ledger at another file (tests). */
|
|
69
|
+
export declare function setAutoReplyLedgerFile(file: string): void;
|
|
70
|
+
export declare function readAddressLedger(btcAddress: string): Promise<AddressLedger>;
|
|
71
|
+
export declare class DailyCapError extends Error {
|
|
72
|
+
readonly spent: number;
|
|
73
|
+
readonly cap: number;
|
|
74
|
+
readonly price: number;
|
|
75
|
+
constructor(spent: number, cap: number, price: number);
|
|
76
|
+
}
|
|
77
|
+
export declare class PriceCeilingError extends Error {
|
|
78
|
+
readonly price: number;
|
|
79
|
+
readonly max: number;
|
|
80
|
+
constructor(price: number, max: number);
|
|
81
|
+
}
|
|
82
|
+
export declare function fetchInbox(btcAddress: string): Promise<InboxMessage[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Why a message must not be auto-answered, or null when it may be. A message
|
|
85
|
+
* is paid when it carries a payment txid and sats. It is settled when the
|
|
86
|
+
* relay confirmed it, it was recovered from a confirmed txid, or it has no
|
|
87
|
+
* `paymentStatus` at all: messages from before the field existed were only
|
|
88
|
+
* delivered once their payment settled. Any other status (`pending`, ...) is
|
|
89
|
+
* a staged payment that may still fail.
|
|
90
|
+
*/
|
|
91
|
+
export declare function skipReason(message: InboxMessage, self: {
|
|
92
|
+
btcAddress: string;
|
|
93
|
+
stxAddress: string;
|
|
94
|
+
}, ledger: AddressLedger): SkipReason | null;
|
|
95
|
+
export interface GeneratedReply {
|
|
96
|
+
text: string;
|
|
97
|
+
/** Sats paid for the generation (0 when the source is free). */
|
|
98
|
+
inferenceCostSats: number;
|
|
99
|
+
txid?: string;
|
|
100
|
+
provider?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Where reply text comes from. The marketplace is the only source today (every
|
|
104
|
+
* auto-reply is metered marketplace usage); another source, such as the
|
|
105
|
+
* agent's own model, plugs in here without touching the inbox side.
|
|
106
|
+
*/
|
|
107
|
+
export interface ReplyGenerator {
|
|
108
|
+
/** The model behind it, so a dry-run draft is reused only by the same source. */
|
|
109
|
+
readonly id?: string;
|
|
110
|
+
generate(input: {
|
|
111
|
+
systemPrompt: string;
|
|
112
|
+
message: InboxMessage;
|
|
113
|
+
/** Throw from here to refuse the price before anything is signed. */
|
|
114
|
+
beforePay: (priceSats: number) => Promise<void>;
|
|
115
|
+
}): Promise<GeneratedReply>;
|
|
116
|
+
}
|
|
117
|
+
/** Strip reasoning blocks some models emit, collapse whitespace, fit the outbox limit. */
|
|
118
|
+
export declare function cleanReply(raw: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* The inference was paid for but its response is not a usable reply. The run
|
|
121
|
+
* keeps the cap booking and counts it as a paid failure.
|
|
122
|
+
*/
|
|
123
|
+
export declare class PaidInferenceError extends Error {
|
|
124
|
+
constructor(message: string);
|
|
125
|
+
}
|
|
126
|
+
export declare function marketplaceReplyGenerator(model: string): ReplyGenerator;
|
|
127
|
+
/** Live model ids on the marketplace (status "live"). */
|
|
128
|
+
export declare function listLiveModels(): Promise<string[]>;
|
|
129
|
+
export declare function replySigningMessage(messageId: string, reply: string): string;
|
|
130
|
+
/** BIP-322 over the outbox message with the wallet's native SegWit key. */
|
|
131
|
+
export declare function signReply(account: Account, messageId: string, reply: string): string;
|
|
132
|
+
export type PostReplyResult = {
|
|
133
|
+
status: "posted";
|
|
134
|
+
repliedAt?: string;
|
|
135
|
+
} | {
|
|
136
|
+
status: "already_replied";
|
|
137
|
+
} | {
|
|
138
|
+
status: "rate_limited";
|
|
139
|
+
retryAfter?: number;
|
|
140
|
+
};
|
|
141
|
+
export declare function postReply(account: Account, btcAddress: string, messageId: string, reply: string): Promise<PostReplyResult>;
|
|
142
|
+
export interface AutoReplyRunOptions {
|
|
143
|
+
account: Account;
|
|
144
|
+
systemPrompt: string;
|
|
145
|
+
dailyCapSats: number;
|
|
146
|
+
/** Most sats paid for one reply. A higher quote stops the run before anything is signed. */
|
|
147
|
+
maxSatsPerReply: number;
|
|
148
|
+
maxRepliesPerRun: number;
|
|
149
|
+
dryRun: boolean;
|
|
150
|
+
generator: ReplyGenerator;
|
|
151
|
+
}
|
|
152
|
+
interface ReplyRecord {
|
|
153
|
+
messageId: string;
|
|
154
|
+
from: string;
|
|
155
|
+
reply: string;
|
|
156
|
+
inferenceCostSats: number;
|
|
157
|
+
inferenceTxid?: string;
|
|
158
|
+
provider?: string;
|
|
159
|
+
/** The reply was paid for in an earlier run whose post failed. */
|
|
160
|
+
reusedPending?: boolean;
|
|
161
|
+
/** The reply is a dry-run draft written with the same prompt and model. */
|
|
162
|
+
reusedDraft?: boolean;
|
|
163
|
+
}
|
|
164
|
+
export interface AutoReplyRunSummary {
|
|
165
|
+
checked: number;
|
|
166
|
+
eligible: number;
|
|
167
|
+
replied: ReplyRecord[];
|
|
168
|
+
drafted: ReplyRecord[];
|
|
169
|
+
skipped: Array<{
|
|
170
|
+
messageId: string;
|
|
171
|
+
reason: SkipReason;
|
|
172
|
+
}>;
|
|
173
|
+
/** `gaveUp`: the message failed after payment MAX_PAID_ATTEMPTS times and is no longer retried. */
|
|
174
|
+
failed: Array<{
|
|
175
|
+
messageId: string;
|
|
176
|
+
error: string;
|
|
177
|
+
replySaved: boolean;
|
|
178
|
+
gaveUp?: boolean;
|
|
179
|
+
}>;
|
|
180
|
+
/** Why the run stopped before working through every eligible message. */
|
|
181
|
+
stoppedEarly?: StopReason;
|
|
182
|
+
inferenceSatsSpent: number;
|
|
183
|
+
inferenceSatsSpentToday: number;
|
|
184
|
+
inferenceCapRemainingSats: number;
|
|
185
|
+
}
|
|
186
|
+
export declare function runAutoReply(opts: AutoReplyRunOptions): Promise<AutoReplyRunSummary>;
|
|
187
|
+
export {};
|
|
188
|
+
//# sourceMappingURL=inbox-auto-reply.service.d.ts.map
|