baconservices-mcp 0.1.2 → 0.2.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/src/server.js CHANGED
@@ -14,7 +14,7 @@ import { formatUsdc } from './money.js';
14
14
  import { Purchaser, Refusal } from './payment.js';
15
15
  import { balanceReader, loadWallet, redact } from './wallet.js';
16
16
 
17
- export const VERSION = '0.1.2';
17
+ export const VERSION = '0.2.0';
18
18
  const CATALOG_TTL_MS = 5 * 60 * 1000;
19
19
 
20
20
  const FREE_TOOLS = [
package/src/wallet.js CHANGED
@@ -1,60 +1,60 @@
1
- // The buyer's signer, from their own machine only.
2
- //
3
- // The private key comes from BACON_WALLET_PRIVATE_KEY or from a file named by
4
- // BACON_WALLET_KEY_FILE (so it need not sit in an MCP host's config file). It is never a tool
5
- // argument, never sent anywhere (only signatures leave this process), and never printed: every
6
- // message this package produces passes through redact(), which removes it in any spelling.
7
-
8
- import { readFileSync } from 'node:fs';
9
- import { createPublicClient, http, parseAbi } from 'viem';
10
- import { privateKeyToAccount } from 'viem/accounts';
11
-
12
- const ERC20 = parseAbi(['function balanceOf(address owner) view returns (uint256)']);
13
-
14
- /** @returns {{ account: object|null, problem: string|null, secrets: string[] }} */
15
- export function loadWallet(env) {
16
- const inline = env.BACON_WALLET_PRIVATE_KEY ?? '';
17
- const file = (env.BACON_WALLET_KEY_FILE ?? '').trim();
18
- if (inline.trim() && file) {
19
- return { account: null, problem: 'Set only one of BACON_WALLET_PRIVATE_KEY or BACON_WALLET_KEY_FILE.', secrets: secretsOf(inline) };
20
- }
21
- let raw = inline;
22
- if (file) {
23
- try {
24
- raw = readFileSync(file, 'utf8');
25
- } catch {
26
- return { account: null, problem: `BACON_WALLET_KEY_FILE could not be read (${file}).`, secrets: [] };
27
- }
28
- }
29
- const cleaned = raw.replace(/[\s"']/g, '');
30
- if (!cleaned) {
31
- return { account: null, problem: 'No wallet is configured: set BACON_WALLET_PRIVATE_KEY or BACON_WALLET_KEY_FILE to pay for services.', secrets: [] };
32
- }
33
- const key = cleaned.startsWith('0x') ? cleaned : `0x${cleaned}`;
34
- const secrets = secretsOf(cleaned);
35
- if (!/^0x[0-9a-fA-F]{64}$/.test(key)) {
36
- // Says what is wrong with it without repeating any of it.
37
- return { account: null, problem: `The wallet key is not a private key: it has ${key.length - 2} characters after 0x, a private key has exactly 64 hex characters.`, secrets };
38
- }
39
- return { account: privateKeyToAccount(key), problem: null, secrets };
40
- }
41
-
42
- function secretsOf(raw) {
43
- const bare = raw.replace(/[\s"']/g, '').replace(/^0x/i, '');
44
- return bare.length >= 8 ? [bare] : [];
45
- }
46
-
47
- /** Removes every secret from a message, whatever its case and with or without 0x. */
48
- export function redact(text, secrets) {
49
- let out = String(text);
50
- for (const secret of secrets) {
51
- out = out.replace(new RegExp(secret.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '[redacted]');
52
- }
53
- return out;
54
- }
55
-
56
- /** The wallet's USDC balance in atomic units, read from the network's RPC. Throws when it cannot be read. */
57
- export function balanceReader(rpcUrl) {
58
- const client = createPublicClient({ transport: http(rpcUrl, { timeout: 15_000, retryCount: 1 }) });
59
- return (usdcAddress, owner) => client.readContract({ address: usdcAddress, abi: ERC20, functionName: 'balanceOf', args: [owner] });
60
- }
1
+ // The buyer's signer, from their own machine only.
2
+ //
3
+ // The private key comes from BACON_WALLET_PRIVATE_KEY or from a file named by
4
+ // BACON_WALLET_KEY_FILE (so it need not sit in an MCP host's config file). It is never a tool
5
+ // argument, never sent anywhere (only signatures leave this process), and never printed: every
6
+ // message this package produces passes through redact(), which removes it in any spelling.
7
+
8
+ import { readFileSync } from 'node:fs';
9
+ import { createPublicClient, http, parseAbi } from 'viem';
10
+ import { privateKeyToAccount } from 'viem/accounts';
11
+
12
+ const ERC20 = parseAbi(['function balanceOf(address owner) view returns (uint256)']);
13
+
14
+ /** @returns {{ account: object|null, problem: string|null, secrets: string[] }} */
15
+ export function loadWallet(env) {
16
+ const inline = env.BACON_WALLET_PRIVATE_KEY ?? '';
17
+ const file = (env.BACON_WALLET_KEY_FILE ?? '').trim();
18
+ if (inline.trim() && file) {
19
+ return { account: null, problem: 'Set only one of BACON_WALLET_PRIVATE_KEY or BACON_WALLET_KEY_FILE.', secrets: secretsOf(inline) };
20
+ }
21
+ let raw = inline;
22
+ if (file) {
23
+ try {
24
+ raw = readFileSync(file, 'utf8');
25
+ } catch {
26
+ return { account: null, problem: `BACON_WALLET_KEY_FILE could not be read (${file}).`, secrets: [] };
27
+ }
28
+ }
29
+ const cleaned = raw.replace(/[\s"']/g, '');
30
+ if (!cleaned) {
31
+ return { account: null, problem: 'No wallet is configured: set BACON_WALLET_PRIVATE_KEY or BACON_WALLET_KEY_FILE to pay for services.', secrets: [] };
32
+ }
33
+ const key = cleaned.startsWith('0x') ? cleaned : `0x${cleaned}`;
34
+ const secrets = secretsOf(cleaned);
35
+ if (!/^0x[0-9a-fA-F]{64}$/.test(key)) {
36
+ // Says what is wrong with it without repeating any of it.
37
+ return { account: null, problem: `The wallet key is not a private key: it has ${key.length - 2} characters after 0x, a private key has exactly 64 hex characters.`, secrets };
38
+ }
39
+ return { account: privateKeyToAccount(key), problem: null, secrets };
40
+ }
41
+
42
+ function secretsOf(raw) {
43
+ const bare = raw.replace(/[\s"']/g, '').replace(/^0x/i, '');
44
+ return bare.length >= 8 ? [bare] : [];
45
+ }
46
+
47
+ /** Removes every secret from a message, whatever its case and with or without 0x. */
48
+ export function redact(text, secrets) {
49
+ let out = String(text);
50
+ for (const secret of secrets) {
51
+ out = out.replace(new RegExp(secret.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '[redacted]');
52
+ }
53
+ return out;
54
+ }
55
+
56
+ /** The wallet's USDC balance in atomic units, read from the network's RPC. Throws when it cannot be read. */
57
+ export function balanceReader(rpcUrl) {
58
+ const client = createPublicClient({ transport: http(rpcUrl, { timeout: 15_000, retryCount: 1 }) });
59
+ return (usdcAddress, owner) => client.readContract({ address: usdcAddress, abi: ERC20, functionName: 'balanceOf', args: [owner] });
60
+ }