@scriptmasterlabs/mcp-x402 2.0.1 → 2.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.
Files changed (93) hide show
  1. package/.well-known/agentcard.json +34 -34
  2. package/.well-known/ai.txt +32 -0
  3. package/CONTRIBUTING.md +76 -76
  4. package/LICENSE +21 -21
  5. package/README.md +304 -304
  6. package/agents.json +81 -67
  7. package/ai/faq.json +74 -0
  8. package/ai/summary.json +157 -0
  9. package/dist/lib/chains/base.d.ts.map +1 -1
  10. package/dist/lib/chains/base.js +2 -0
  11. package/dist/lib/chains/base.js.map +1 -1
  12. package/dist/lib/credit/bureau.d.ts +7 -1
  13. package/dist/lib/credit/bureau.d.ts.map +1 -1
  14. package/dist/lib/credit/bureau.js +40 -10
  15. package/dist/lib/credit/bureau.js.map +1 -1
  16. package/dist/server/index.js +128 -5
  17. package/dist/server/index.js.map +1 -1
  18. package/llms.txt +170 -70
  19. package/package.json +78 -78
  20. package/server.json +52 -48
  21. package/.env.example +0 -35
  22. package/.github/workflows/ci.yml +0 -59
  23. package/.github/workflows/keepalive.yml +0 -31
  24. package/Dockerfile +0 -19
  25. package/docker-compose.yml +0 -50
  26. package/mcp-publisher.exe +0 -0
  27. package/render.yaml +0 -39
  28. package/sdk/mcp-x402-sdk/package.json +0 -18
  29. package/sdk/mcp-x402-sdk/src/index.ts +0 -118
  30. package/sdk/mcp-x402-sdk/tsconfig.json +0 -14
  31. package/services/backtest_service.py +0 -176
  32. package/src/lib/chains/base.ts +0 -77
  33. package/src/lib/chains/solana.ts +0 -59
  34. package/src/lib/chains/xrpl.ts +0 -63
  35. package/src/lib/credit/bureau.ts +0 -65
  36. package/src/lib/sml-api/agentcard.ts +0 -40
  37. package/src/lib/sml-api/backtest.ts +0 -47
  38. package/src/lib/sml-api/brokers.ts +0 -160
  39. package/src/lib/sml-api/copytrader.ts +0 -33
  40. package/src/lib/sml-api/crawl.ts +0 -44
  41. package/src/lib/sml-api/echo.ts +0 -28
  42. package/src/lib/sml-api/forge.ts +0 -33
  43. package/src/lib/sml-api/ftd.ts +0 -53
  44. package/src/lib/sml-api/ghost.ts +0 -35
  45. package/src/lib/sml-api/launchpad.ts +0 -43
  46. package/src/lib/sml-api/leviathan.ts +0 -49
  47. package/src/lib/sml-api/nexus.ts +0 -50
  48. package/src/lib/sml-api/proof402.ts +0 -27
  49. package/src/lib/sml-api/rails.ts +0 -34
  50. package/src/lib/sml-api/shadow.ts +0 -35
  51. package/src/lib/sml-api/squeezeos.ts +0 -95
  52. package/src/lib/sml-api/xdeo.ts +0 -40
  53. package/src/lib/sml-api/xmit.ts +0 -40
  54. package/src/server/health.ts +0 -52
  55. package/src/server/index.ts +0 -213
  56. package/src/server/payments/ap2.ts +0 -101
  57. package/src/server/payments/receipt.ts +0 -85
  58. package/src/server/payments/router.ts +0 -110
  59. package/src/server/payments/wallet.ts +0 -123
  60. package/src/server/payments/x402.ts +0 -177
  61. package/src/server/registry/catalog.ts +0 -61
  62. package/src/server/registry/discovery.ts +0 -39
  63. package/src/server/registry/pricing.ts +0 -133
  64. package/src/server/security/acl.ts +0 -42
  65. package/src/server/security/audit.ts +0 -94
  66. package/src/server/security/rate-limit.ts +0 -84
  67. package/src/server/security/sandbox.ts +0 -40
  68. package/src/server/tools/agentcard.ts +0 -134
  69. package/src/server/tools/backtest.ts +0 -119
  70. package/src/server/tools/brokers.ts +0 -250
  71. package/src/server/tools/copytrader.ts +0 -104
  72. package/src/server/tools/crawl.ts +0 -70
  73. package/src/server/tools/discovery.ts +0 -202
  74. package/src/server/tools/echo.ts +0 -58
  75. package/src/server/tools/forge.ts +0 -87
  76. package/src/server/tools/ftd.ts +0 -88
  77. package/src/server/tools/ghost.ts +0 -93
  78. package/src/server/tools/index.ts +0 -42
  79. package/src/server/tools/launchpad.ts +0 -173
  80. package/src/server/tools/leviathan.ts +0 -81
  81. package/src/server/tools/nexus.ts +0 -76
  82. package/src/server/tools/proof402.ts +0 -87
  83. package/src/server/tools/rails.ts +0 -92
  84. package/src/server/tools/shadow.ts +0 -128
  85. package/src/server/tools/squeezeos.ts +0 -312
  86. package/src/server/tools/xdeo.ts +0 -67
  87. package/src/server/tools/xmit.ts +0 -68
  88. package/tests/integration/e2e.test.ts +0 -51
  89. package/tests/unit/payments.test.ts +0 -49
  90. package/tests/unit/security.test.ts +0 -92
  91. package/tests/unit/tools.test.ts +0 -42
  92. package/tsconfig.json +0 -21
  93. package/vitest.config.ts +0 -20
@@ -1,134 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { executeX402Payment } from '../payments/x402.js';
4
- import { RateLimiter } from '../security/rate-limit.js';
5
- import { Sandbox } from '../security/sandbox.js';
6
- import { AuditLogger } from '../security/audit.js';
7
- import { PriceRegistry } from '../registry/pricing.js';
8
- import { AgentCardAPI } from '../../lib/sml-api/agentcard.js';
9
-
10
- const LookupSchema = z.object({
11
- identifier: z.string().min(1),
12
- });
13
-
14
- const VerifySchema = z.object({
15
- wallet_address: z.string().min(10),
16
- message: z.string().min(1),
17
- signature: z.string().min(1),
18
- });
19
-
20
- const MintSchema = z.object({
21
- wallet_address: z.string().min(10),
22
- name: z.string().min(1).max(64),
23
- did: z.string().optional(),
24
- metadata: z.record(z.unknown()).optional(),
25
- payment_wallet: z.string().optional(),
26
- });
27
-
28
- export function registerAgentCard(server: McpServer): void {
29
- const audit = AuditLogger.getInstance();
30
-
31
- // ── FREE: agentcard_lookup ─────────────────────────────────────────────────
32
- server.tool(
33
- 'agentcard_lookup',
34
- {
35
- identifier: z.string().describe('Agent wallet address or DID to look up.'),
36
- },
37
- async (rawArgs) => {
38
- const { identifier } = Sandbox.validate(LookupSchema, rawArgs);
39
- if (!RateLimiter.getInstance().checkTool('agentcard_lookup')) {
40
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded', retry_after: 60 }) }], isError: true };
41
- }
42
- try {
43
- const data = await AgentCardAPI.lookup(identifier);
44
- audit.info('agentcard_lookup', { identifier });
45
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
46
- } catch (err) {
47
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
48
- }
49
- },
50
- );
51
-
52
- // ── FREE: agentcard_verify ─────────────────────────────────────────────────
53
- server.tool(
54
- 'agentcard_verify',
55
- {
56
- wallet_address: z.string().describe('Agent wallet address that signed the message.'),
57
- message: z.string().describe('Original message that was signed.'),
58
- signature: z.string().describe('Ed25519 signature (hex or base64).'),
59
- },
60
- async (rawArgs) => {
61
- const args = Sandbox.validate(VerifySchema, rawArgs);
62
- if (!RateLimiter.getInstance().checkTool('agentcard_verify')) {
63
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded', retry_after: 60 }) }], isError: true };
64
- }
65
- try {
66
- const data = await AgentCardAPI.verify({
67
- walletAddress: args.wallet_address,
68
- message: args.message,
69
- signature: args.signature,
70
- });
71
- audit.info('agentcard_verify', { wallet_address: args.wallet_address });
72
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
73
- } catch (err) {
74
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
75
- }
76
- },
77
- );
78
-
79
- // ── PAID: agentcard_mint (0.01 USDC) ──────────────────────────────────────
80
- server.tool(
81
- 'agentcard_mint',
82
- {
83
- wallet_address: z.string().describe('XRPL wallet address for the new agent identity.'),
84
- name: z.string().describe('Human-readable agent name (max 64 chars).'),
85
- did: z.string().describe('Optional DID (decentralized identifier) for the agent.'),
86
- metadata: z.record(z.unknown()).describe('Optional metadata object (capabilities, version, etc.).'),
87
- payment_wallet: z.string().describe('Wallet to pay x402 fee from (defaults to wallet_address).'),
88
- },
89
- async (rawArgs) => {
90
- const args = Sandbox.validate(MintSchema, rawArgs);
91
- const paymentWallet = args.payment_wallet ?? args.wallet_address;
92
-
93
- if (!RateLimiter.getInstance().checkTool('agentcard_mint')) {
94
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded', retry_after: 60 }) }], isError: true };
95
- }
96
-
97
- await PriceRegistry.getInstance().seedDefaults();
98
- const price = await PriceRegistry.getInstance().getPrice('agentcard_mint');
99
- if (!price) {
100
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
101
- }
102
-
103
- let payment;
104
- try {
105
- payment = await executeX402Payment({ price, currency: 'USDC', toolName: 'agentcard_mint', walletAddress: paymentWallet });
106
- } catch (err) {
107
- audit.warn('agentcard_mint_payment_fail', { error: String(err) });
108
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
109
- }
110
-
111
- try {
112
- const data = await AgentCardAPI.mint({
113
- walletAddress: args.wallet_address,
114
- name: args.name,
115
- did: args.did,
116
- metadata: args.metadata,
117
- });
118
- audit.info('agentcard_mint_success', { name: args.name, receiptId: payment.receiptId });
119
- return {
120
- content: [{
121
- type: 'text',
122
- text: JSON.stringify({
123
- data,
124
- _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}`, timestamp: payment.timestamp },
125
- }),
126
- }],
127
- };
128
- } catch (err) {
129
- audit.error('agentcard_mint_api_fail', { error: String(err) });
130
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
131
- }
132
- },
133
- );
134
- }
@@ -1,119 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { executeX402Payment } from '../payments/x402.js';
4
- import { RateLimiter } from '../security/rate-limit.js';
5
- import { Sandbox } from '../security/sandbox.js';
6
- import { AuditLogger } from '../security/audit.js';
7
- import { PriceRegistry } from '../registry/pricing.js';
8
- import { BacktestAPI } from '../../lib/sml-api/backtest.js';
9
-
10
- const BacktestSchema = z.object({
11
- ticker: z.string().min(1).max(10).toUpperCase(),
12
- lookback_days: z.number().int().min(30).max(1260).default(252),
13
- fees: z.number().min(0).max(0.05).default(0.001),
14
- slippage: z.number().min(0).max(0.05).default(0.0005),
15
- momentum_window: z.number().int().min(2).max(50).default(10),
16
- momentum_threshold: z.number().min(0).max(0.1).default(0.001),
17
- wallet_address: z.string().optional(),
18
- });
19
-
20
- const ValidateSchema = z.object({
21
- ticker: z.string().min(1).max(10).toUpperCase(),
22
- lookback_days: z.number().int().min(60).max(1260).default(504),
23
- train_ratio: z.number().min(0.5).max(0.9).default(0.7),
24
- fees: z.number().min(0).max(0.05).default(0.001),
25
- slippage: z.number().min(0).max(0.05).default(0.0005),
26
- wallet_address: z.string().optional(),
27
- });
28
-
29
- export function registerBacktest(server: McpServer): void {
30
- const audit = AuditLogger.getInstance();
31
-
32
- // ── backtest_run — full backtest on live price data (FREE) ─────────────────
33
- server.tool(
34
- 'backtest_run',
35
- {
36
- ticker: z.string().describe('Ticker symbol (e.g. NVDA, SPY, GME)'),
37
- lookback_days: z.number().describe('Days of history to backtest (30–1260, default 252)'),
38
- fees: z.number().describe('Round-trip commission rate (default 0.001 = 0.1%)'),
39
- slippage: z.number().describe('Slippage per side (default 0.0005)'),
40
- momentum_window: z.number().describe('Momentum rolling window in days (default 10)'),
41
- momentum_threshold: z.number().describe('Minimum momentum to enter long (default 0.001)'),
42
- wallet_address: z.string().describe('Agent wallet address (optional)'),
43
- },
44
- async (rawArgs) => {
45
- const args = Sandbox.validate(BacktestSchema, rawArgs);
46
- if (!RateLimiter.getInstance().checkTool('backtest_run')) {
47
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
48
- }
49
- try {
50
- const result = await BacktestAPI.backtest({
51
- ticker: args.ticker,
52
- lookback_days: args.lookback_days,
53
- fees: args.fees,
54
- slippage: args.slippage,
55
- momentum_window: args.momentum_window,
56
- momentum_threshold: args.momentum_threshold,
57
- });
58
- audit.info('backtest_run_success', { ticker: args.ticker });
59
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
60
- } catch (err) {
61
- audit.warn('backtest_run_fail', { error: String(err) });
62
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
63
- }
64
- },
65
- );
66
-
67
- // ── backtest_validate — walk-forward OOS validation ($0.02) ───────────────
68
- server.tool(
69
- 'backtest_validate',
70
- {
71
- ticker: z.string().describe('Ticker symbol to validate'),
72
- lookback_days: z.number().describe('Total history window (60–1260, default 504 = 2 years)'),
73
- train_ratio: z.number().describe('Train/test split ratio (default 0.7 = 70% in-sample)'),
74
- fees: z.number().describe('Round-trip commission rate (default 0.001)'),
75
- slippage: z.number().describe('Slippage per side (default 0.0005)'),
76
- wallet_address: z.string().describe('Agent wallet for x402 payment (AP2 required)'),
77
- },
78
- async (rawArgs) => {
79
- const args = Sandbox.validate(ValidateSchema, rawArgs);
80
- if (!RateLimiter.getInstance().checkTool('backtest_validate')) {
81
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
82
- }
83
- await PriceRegistry.getInstance().seedDefaults();
84
- const price = await PriceRegistry.getInstance().getPrice('backtest_validate');
85
- if (!price) {
86
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
87
- }
88
- let payment;
89
- try {
90
- payment = await executeX402Payment({ price, currency: 'USDC', toolName: 'backtest_validate', walletAddress: args.wallet_address });
91
- } catch (err) {
92
- audit.warn('backtest_validate_payment_fail', { error: String(err) });
93
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
94
- }
95
- try {
96
- const result = await BacktestAPI.walkForward({
97
- ticker: args.ticker,
98
- lookback_days: args.lookback_days,
99
- train_ratio: args.train_ratio,
100
- fees: args.fees,
101
- slippage: args.slippage,
102
- });
103
- audit.info('backtest_validate_success', { ticker: args.ticker });
104
- return {
105
- content: [{
106
- type: 'text',
107
- text: JSON.stringify({
108
- ...(result as object),
109
- _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}` },
110
- }),
111
- }],
112
- };
113
- } catch (err) {
114
- audit.warn('backtest_validate_fail', { error: String(err) });
115
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
116
- }
117
- },
118
- );
119
- }
@@ -1,250 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { executeX402Payment } from '../payments/x402.js';
4
- import { RateLimiter } from '../security/rate-limit.js';
5
- import { Sandbox } from '../security/sandbox.js';
6
- import { AuditLogger } from '../security/audit.js';
7
- import { PriceRegistry } from '../registry/pricing.js';
8
- import { TradierAPI, RobinhoodAPI } from '../../lib/sml-api/brokers.js';
9
-
10
- // ── Schemas ────────────────────────────────────────────────────────────────
11
-
12
- const TradierQuoteSchema = z.object({
13
- symbols: z.string().min(1).max(200),
14
- });
15
-
16
- const TradierOrderSchema = z.object({
17
- account_id: z.string().min(1),
18
- symbol: z.string().min(1).max(10).toUpperCase(),
19
- side: z.enum(['buy', 'sell']),
20
- quantity: z.number().int().min(1).max(10000),
21
- type: z.enum(['market', 'limit', 'stop', 'stop_limit']),
22
- duration: z.enum(['day', 'gtc', 'pre', 'post']),
23
- price: z.number().positive().optional(),
24
- stop: z.number().positive().optional(),
25
- wallet_address: z.string().optional(),
26
- });
27
-
28
- const RobinhoodQuoteSchema = z.object({
29
- symbol: z.string().min(1).max(10).toUpperCase(),
30
- });
31
-
32
- const RobinhoodOrderSchema = z.object({
33
- symbol: z.string().min(1).max(10).toUpperCase(),
34
- side: z.enum(['buy', 'sell']),
35
- quantity: z.number().int().min(1).max(10000),
36
- type: z.enum(['market', 'limit']),
37
- time_in_force: z.enum(['gfd', 'gtc', 'ioc', 'opg']),
38
- price: z.number().positive().optional(),
39
- wallet_address: z.string().optional(),
40
- });
41
-
42
- const AccountSchema = z.object({
43
- account_id: z.string().min(1),
44
- });
45
-
46
- // ── Registration ───────────────────────────────────────────────────────────
47
-
48
- export function registerBrokers(server: McpServer): void {
49
- const audit = AuditLogger.getInstance();
50
-
51
- // ── tradier_quote — FREE ─────────────────────────────────────────────────
52
- server.tool(
53
- 'tradier_quote',
54
- {
55
- symbols: z.string().describe('Comma-separated ticker symbols (e.g. "NVDA,SPY,AAPL")'),
56
- },
57
- async (rawArgs) => {
58
- const args = Sandbox.validate(TradierQuoteSchema, rawArgs);
59
- if (!RateLimiter.getInstance().checkTool('tradier_quote')) {
60
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
61
- }
62
- try {
63
- const data = await TradierAPI.quote({ symbols: args.symbols });
64
- audit.info('tradier_quote_success', { symbols: args.symbols });
65
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
66
- } catch (err) {
67
- audit.warn('tradier_quote_fail', { error: String(err) });
68
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
69
- }
70
- },
71
- );
72
-
73
- // ── tradier_order — $0.01 x402 ──────────────────────────────────────────
74
- server.tool(
75
- 'tradier_order',
76
- {
77
- account_id: z.string().describe('Tradier brokerage account ID'),
78
- symbol: z.string().describe('Ticker symbol (e.g. NVDA)'),
79
- side: z.string().describe('"buy" or "sell"'),
80
- quantity: z.number().describe('Number of shares (integer)'),
81
- type: z.string().describe('"market" | "limit" | "stop" | "stop_limit"'),
82
- duration: z.string().describe('"day" | "gtc" | "pre" | "post"'),
83
- price: z.number().describe('Limit price (required for limit/stop_limit orders)'),
84
- stop: z.number().describe('Stop price (required for stop/stop_limit orders)'),
85
- wallet_address: z.string().describe('Agent wallet for x402 payment (AP2 required)'),
86
- },
87
- async (rawArgs) => {
88
- const args = Sandbox.validate(TradierOrderSchema, rawArgs);
89
- if (!RateLimiter.getInstance().checkTool('tradier_order')) {
90
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
91
- }
92
- await PriceRegistry.getInstance().seedDefaults();
93
- const price = await PriceRegistry.getInstance().getPrice('tradier_order');
94
- if (!price) {
95
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
96
- }
97
- let payment;
98
- try {
99
- payment = await executeX402Payment({ price, currency: 'USDC', toolName: 'tradier_order', walletAddress: args.wallet_address });
100
- } catch (err) {
101
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
102
- }
103
- try {
104
- const result = await TradierAPI.order({
105
- account_id: args.account_id,
106
- symbol: args.symbol,
107
- side: args.side,
108
- quantity: args.quantity,
109
- type: args.type,
110
- duration: args.duration,
111
- price: args.price,
112
- stop: args.stop,
113
- });
114
- audit.info('tradier_order_success', { symbol: args.symbol, side: args.side, quantity: args.quantity });
115
- return {
116
- content: [{
117
- type: 'text',
118
- text: JSON.stringify({
119
- ...(result as object),
120
- _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}` },
121
- }),
122
- }],
123
- };
124
- } catch (err) {
125
- audit.warn('tradier_order_fail', { error: String(err) });
126
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
127
- }
128
- },
129
- );
130
-
131
- // ── tradier_positions — FREE ─────────────────────────────────────────────
132
- server.tool(
133
- 'tradier_positions',
134
- {
135
- account_id: z.string().describe('Tradier brokerage account ID'),
136
- },
137
- async (rawArgs) => {
138
- const args = Sandbox.validate(AccountSchema, rawArgs);
139
- if (!RateLimiter.getInstance().checkTool('tradier_positions')) {
140
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
141
- }
142
- try {
143
- const [positions, balances] = await Promise.all([
144
- TradierAPI.positions(args.account_id),
145
- TradierAPI.balances(args.account_id),
146
- ]);
147
- return { content: [{ type: 'text', text: JSON.stringify({ positions, balances }) }] };
148
- } catch (err) {
149
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
150
- }
151
- },
152
- );
153
-
154
- // ── robinhood_quote — FREE ───────────────────────────────────────────────
155
- server.tool(
156
- 'robinhood_quote',
157
- {
158
- symbol: z.string().describe('Ticker symbol (e.g. NVDA)'),
159
- },
160
- async (rawArgs) => {
161
- const args = Sandbox.validate(RobinhoodQuoteSchema, rawArgs);
162
- if (!RateLimiter.getInstance().checkTool('robinhood_quote')) {
163
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
164
- }
165
- try {
166
- const data = await RobinhoodAPI.quote(args.symbol);
167
- audit.info('robinhood_quote_success', { symbol: args.symbol });
168
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
169
- } catch (err) {
170
- audit.warn('robinhood_quote_fail', { error: String(err) });
171
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
172
- }
173
- },
174
- );
175
-
176
- // ── robinhood_order — $0.01 x402 ────────────────────────────────────────
177
- server.tool(
178
- 'robinhood_order',
179
- {
180
- symbol: z.string().describe('Ticker symbol (e.g. NVDA)'),
181
- side: z.string().describe('"buy" or "sell"'),
182
- quantity: z.number().describe('Number of shares (integer)'),
183
- type: z.string().describe('"market" or "limit"'),
184
- time_in_force: z.string().describe('"gfd" (good for day) | "gtc" | "ioc" | "opg"'),
185
- price: z.number().describe('Limit price (required for limit orders)'),
186
- wallet_address: z.string().describe('Agent wallet for x402 payment (AP2 required)'),
187
- },
188
- async (rawArgs) => {
189
- const args = Sandbox.validate(RobinhoodOrderSchema, rawArgs);
190
- if (!RateLimiter.getInstance().checkTool('robinhood_order')) {
191
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
192
- }
193
- await PriceRegistry.getInstance().seedDefaults();
194
- const price = await PriceRegistry.getInstance().getPrice('robinhood_order');
195
- if (!price) {
196
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
197
- }
198
- let payment;
199
- try {
200
- payment = await executeX402Payment({ price, currency: 'USDC', toolName: 'robinhood_order', walletAddress: args.wallet_address });
201
- } catch (err) {
202
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
203
- }
204
- try {
205
- const result = await RobinhoodAPI.order({
206
- symbol: args.symbol,
207
- side: args.side,
208
- quantity: args.quantity,
209
- type: args.type,
210
- time_in_force: args.time_in_force,
211
- price: args.price,
212
- });
213
- audit.info('robinhood_order_success', { symbol: args.symbol, side: args.side, quantity: args.quantity });
214
- return {
215
- content: [{
216
- type: 'text',
217
- text: JSON.stringify({
218
- ...(result as object),
219
- _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}` },
220
- }),
221
- }],
222
- };
223
- } catch (err) {
224
- audit.warn('robinhood_order_fail', { error: String(err) });
225
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
226
- }
227
- },
228
- );
229
-
230
- // ── robinhood_portfolio — FREE ───────────────────────────────────────────
231
- server.tool(
232
- 'robinhood_portfolio',
233
- {},
234
- async () => {
235
- if (!RateLimiter.getInstance().checkTool('robinhood_portfolio')) {
236
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
237
- }
238
- try {
239
- const [portfolio, positions, orders] = await Promise.all([
240
- RobinhoodAPI.portfolio(),
241
- RobinhoodAPI.positions(),
242
- RobinhoodAPI.orderHistory(),
243
- ]);
244
- return { content: [{ type: 'text', text: JSON.stringify({ portfolio, positions, recent_orders: orders }) }] };
245
- } catch (err) {
246
- return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
247
- }
248
- },
249
- );
250
- }
@@ -1,104 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { executeX402Payment } from '../payments/x402.js';
4
- import { RateLimiter } from '../security/rate-limit.js';
5
- import { Sandbox } from '../security/sandbox.js';
6
- import { AuditLogger } from '../security/audit.js';
7
- import { PriceRegistry } from '../registry/pricing.js';
8
- import { CopyTraderAPI } from '../../lib/sml-api/copytrader.js';
9
-
10
- const SubscribeSchema = z.object({
11
- whale_address: z.string().min(10),
12
- subscriber_address: z.string().min(10),
13
- max_copy_amount_xrp: z.number().positive(),
14
- wallet_address: z.string().optional(),
15
- });
16
-
17
- export function registerCopyTrader(server: McpServer): void {
18
- const audit = AuditLogger.getInstance();
19
-
20
- // ── FREE: copytrader_status ────────────────────────────────────────────────
21
- server.tool(
22
- 'copytrader_status',
23
- {},
24
- async () => {
25
- try {
26
- const data = await CopyTraderAPI.status();
27
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
28
- } catch (err) {
29
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
30
- }
31
- },
32
- );
33
-
34
- // ── FREE: copytrader_whales ────────────────────────────────────────────────
35
- server.tool(
36
- 'copytrader_whales',
37
- {},
38
- async () => {
39
- if (!RateLimiter.getInstance().checkTool('copytrader_whales')) {
40
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded', retry_after: 60 }) }], isError: true };
41
- }
42
- try {
43
- const data = await CopyTraderAPI.whales();
44
- audit.info('copytrader_whales', {});
45
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
46
- } catch (err) {
47
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
48
- }
49
- },
50
- );
51
-
52
- // ── PAID: copytrader_subscribe (0.05 USDC) ────────────────────────────────
53
- server.tool(
54
- 'copytrader_subscribe',
55
- {
56
- whale_address: z.string().describe('XRPL address of the whale to copy.'),
57
- subscriber_address: z.string().describe('Your XRPL address that will mirror trades.'),
58
- max_copy_amount_xrp: z.number().describe('Maximum XRP to allocate per copied trade.'),
59
- wallet_address: z.string().describe('Agent wallet for x402 payment.'),
60
- },
61
- async (rawArgs) => {
62
- const args = Sandbox.validate(SubscribeSchema, rawArgs);
63
-
64
- if (!RateLimiter.getInstance().checkTool('copytrader_subscribe')) {
65
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded', retry_after: 60 }) }], isError: true };
66
- }
67
-
68
- await PriceRegistry.getInstance().seedDefaults();
69
- const price = await PriceRegistry.getInstance().getPrice('copytrader_subscribe');
70
- if (!price) {
71
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
72
- }
73
-
74
- let payment;
75
- try {
76
- payment = await executeX402Payment({ price, currency: 'USDC', toolName: 'copytrader_subscribe', walletAddress: args.wallet_address });
77
- } catch (err) {
78
- audit.warn('copytrader_subscribe_payment_fail', { error: String(err) });
79
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
80
- }
81
-
82
- try {
83
- const data = await CopyTraderAPI.subscribe({
84
- whaleAddress: args.whale_address,
85
- subscriberAddress: args.subscriber_address,
86
- maxCopyAmountXrp: args.max_copy_amount_xrp,
87
- });
88
- audit.info('copytrader_subscribe_success', { receiptId: payment.receiptId });
89
- return {
90
- content: [{
91
- type: 'text',
92
- text: JSON.stringify({
93
- data,
94
- _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}`, timestamp: payment.timestamp },
95
- }),
96
- }],
97
- };
98
- } catch (err) {
99
- audit.error('copytrader_subscribe_api_fail', { error: String(err) });
100
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'api_error', message: String(err) }) }], isError: true };
101
- }
102
- },
103
- );
104
- }
@@ -1,70 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { executeX402Payment } from '../payments/x402.js';
4
- import { RateLimiter } from '../security/rate-limit.js';
5
- import { Sandbox } from '../security/sandbox.js';
6
- import { AuditLogger } from '../security/audit.js';
7
- import { CrawlClient } from '../../lib/sml-api/crawl.js';
8
- import { PriceRegistry } from '../registry/pricing.js';
9
-
10
- const InputSchema = z.object({
11
- url: z.string().url(),
12
- extract: z.enum(['text', 'links', 'tables', 'structured', 'all']).default('text'),
13
- wallet_address: z.string().optional(),
14
- user_agent: z.string().optional(),
15
- });
16
-
17
- export function registerCrawl(server: McpServer): void {
18
- server.tool(
19
- 'crawl_paid_fetch',
20
- {
21
- url: z.string().describe('URL to fetch and parse. Must be http or https.'),
22
- extract: z.enum(['text', 'links', 'tables', 'structured', 'all']).describe('What to extract. Default: text.'),
23
- wallet_address: z.string().describe('Agent wallet for payment. Humans bypass automatically.'),
24
- user_agent: z.string().describe('Custom user-agent string.'),
25
- },
26
- async (rawArgs) => {
27
- const args = Sandbox.validate(InputSchema, rawArgs);
28
- const audit = AuditLogger.getInstance();
29
-
30
- // Validate URL safety
31
- Sandbox.validateUrl(args.url);
32
-
33
- if (!RateLimiter.getInstance().checkTool('crawl_paid_fetch')) {
34
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'rate_limit_exceeded' }) }], isError: true };
35
- }
36
-
37
- await PriceRegistry.getInstance().seedDefaults();
38
- const price = await PriceRegistry.getInstance().getPrice('crawl_paid_fetch');
39
- if (!price) {
40
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'price_unavailable' }) }], isError: true };
41
- }
42
-
43
- let payment;
44
- try {
45
- payment = await executeX402Payment({
46
- price,
47
- currency: 'USDC',
48
- toolName: 'crawl_paid_fetch',
49
- walletAddress: args.wallet_address,
50
- });
51
- } catch (err) {
52
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'payment_failed', message: String(err) }) }], isError: true };
53
- }
54
-
55
- const client = CrawlClient.getInstance();
56
- const data = await client.fetch({ url: args.url, extract: args.extract ?? 'text', userAgent: args.user_agent });
57
-
58
- // Sanitize response to prevent prompt injection
59
- const safeContent = typeof data.content === 'string'
60
- ? Sandbox.sanitizeApiResponse(data.content)
61
- : data.content;
62
-
63
- audit.info('crawl_success', { receiptId: payment.receiptId });
64
-
65
- return {
66
- content: [{ type: 'text', text: JSON.stringify({ data: { ...data, content: safeContent }, _meta: { receipt_id: payment.receiptId, tx_hash: payment.txHash, chain: payment.chain, amount_paid: `${payment.amountPaid} ${payment.currency}` } }) }],
67
- };
68
- },
69
- );
70
- }