m2m-sentinel-sdk 1.2.3 → 1.2.7

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/mcp_server.js CHANGED
@@ -5,75 +5,131 @@ const https = require('https');
5
5
  const http = require('http');
6
6
  const readline = require('readline');
7
7
 
8
+ const pkg = require('./package.json');
9
+
8
10
  let BASE_URL = process.env.M2M_SENTINEL_BASE_URL || 'https://api.m2msentinel.com';
9
11
  let API_KEY = process.env.M2M_SENTINEL_API_KEY || '';
10
12
  const TIMEOUT_MS = Number(process.env.M2M_SENTINEL_TIMEOUT_MS || 30000);
11
- const VERSION = '1.2.3';
13
+ const VERSION = pkg.version;
14
+
15
+ const SERVER_INSTRUCTIONS =
16
+ 'M2M Sentinel MCP server provides factual, read-only observations on Base Mainnet (chainId 8453).\n\n' +
17
+ '• Canonical Tools: The six m2m_* tool names (m2m_audit_contract, m2m_get_gas_metrics, m2m_get_token_price, m2m_get_dex_liquidity, m2m_get_whale_signals, m2m_get_service_status) are canonical.\n' +
18
+ '• Backwards Compatibility: Legacy tool names (audit_contract, get_capability_score, get_gas_fees, get_dex_metrics, get_token_price, get_whale_signals) are not advertised in tools/list but remain compatibility-callable.\n' +
19
+ '• Upstream Behavior: m2m_get_dex_liquidity returns aggregate tracked pool liquidity metrics without pair query filtering; m2m_get_whale_signals returns recent high-value transfer events (up to 50) without query limit filtering.\n' +
20
+ '• Access and Errors: The stdio wrapper reads M2M_SENTINEL_API_KEY when set. Protected upstream calls may return structured 401, 402, 429, or 503 responses; x402 payment metadata is surfaced, but this wrapper does not sign payments.\n' +
21
+ '• Observational Scope: All operations are factual, read-only observations. The server does not custody private keys, sign transactions, or execute on-chain state changes.\n' +
22
+ '• Limitation: Outputs reflect observed bytecode patterns and telemetry and are not safety, economic, or exploitability guarantees.';
12
23
 
13
24
  const TOOLS = [
14
25
  {
15
26
  name: 'm2m_audit_contract',
16
- description: 'Return selected static bytecode capability observations, common proxy resolution, limitations, and provenance for a Base contract. This is factual capability observation, not a safety or exploitability guarantee.',
17
- inputSchema: { type: 'object', properties: { address: { type: 'string', description: 'Base contract address (0x...)' } }, required: ['address'] }
27
+ description: 'Inspect static bytecode capabilities (e.g. mint, pause, freeze, upgradeability slots), common proxy target resolution, and coverage index for a single Base contract address. Factual capability observation only, not a safety or exploitability guarantee. Distinguishable from m2m_get_service_status (infrastructure status) and legacy score-only endpoints.',
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ address: {
32
+ type: 'string',
33
+ description: 'Base contract address (0x-prefixed 40-hex string, chainId 8453) to inspect.'
34
+ }
35
+ },
36
+ required: ['address']
37
+ },
38
+ annotations: {
39
+ readOnlyHint: true,
40
+ destructiveHint: false,
41
+ idempotentHint: true,
42
+ openWorldHint: true
43
+ }
18
44
  },
19
45
  {
20
46
  name: 'm2m_get_gas_metrics',
21
- description: 'Return sourced Base gas fee metrics and execution recommendations.',
22
- inputSchema: { type: 'object', properties: {} }
47
+ description: 'Fetch current Base gas price in wei/gwei with RPC provenance for the upstream observation. Read-only telemetry; does not inspect contract code, return token/DEX pricing, or authorize or submit transactions.',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {}
51
+ },
52
+ annotations: {
53
+ readOnlyHint: true,
54
+ destructiveHint: false,
55
+ idempotentHint: true,
56
+ openWorldHint: true
57
+ }
23
58
  },
24
59
  {
25
60
  name: 'm2m_get_token_price',
26
- description: 'Return sourced Base DEX token price observation for allowlisted assets (e.g. USDC, WETH).',
27
- inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Token symbol (USDC, WETH)' } }, required: ['symbol'] }
61
+ description: 'Fetch the median Base DEX spot price in USD across up to five deepest indexed pools, with contract address, decimals, and pool provenance for one allowlisted token symbol (e.g. USDC, WETH, AERO). Does not return historical price series; contrast with m2m_get_dex_liquidity, which returns aggregate pool reserve depth rather than an asset price.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ symbol: {
66
+ type: 'string',
67
+ description: 'Allowlisted token symbol on Base (e.g. USDC, WETH, AERO). Lookups are case-insensitive single symbols.'
68
+ }
69
+ },
70
+ required: ['symbol']
71
+ },
72
+ annotations: {
73
+ readOnlyHint: true,
74
+ destructiveHint: false,
75
+ idempotentHint: true,
76
+ openWorldHint: true
77
+ }
28
78
  },
29
79
  {
30
80
  name: 'm2m_get_dex_liquidity',
31
- description: 'Return tracked Base DEX pool reserve and liquidity metrics.',
32
- inputSchema: { type: 'object', properties: { pair: { type: 'string' } } }
81
+ description: 'Fetch aggregate pool reserve, depth, and volume metrics across tracked Base DEX liquidity pools. Reports tracked-pool metrics only and does not filter by individual trading pair; contrast with m2m_get_token_price, which observes token spot prices.',
82
+ inputSchema: {
83
+ type: 'object',
84
+ properties: {}
85
+ },
86
+ annotations: {
87
+ readOnlyHint: true,
88
+ destructiveHint: false,
89
+ idempotentHint: true,
90
+ openWorldHint: true
91
+ }
33
92
  },
34
93
  {
35
94
  name: 'm2m_get_whale_signals',
36
- description: 'Return tracked Base whale transfer signals.',
37
- inputSchema: { type: 'object', properties: { limit: { type: 'number' } } }
95
+ description: 'Fetch up to 50 tracked recent high-value ERC-20 transfer signals on Base with transaction hashes, token/sender/receiver addresses, amounts, and valuation provenance. Observes large on-chain transfer events without query parameter limits; does not inspect contract bytecode or query DEX pricing.',
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {}
99
+ },
100
+ annotations: {
101
+ readOnlyHint: true,
102
+ destructiveHint: false,
103
+ idempotentHint: true,
104
+ openWorldHint: true
105
+ }
38
106
  },
39
107
  {
40
108
  name: 'm2m_get_service_status',
41
- description: 'Return real-time operational status of M2M Sentinel upstream RPC and persistence rails.',
42
- inputSchema: { type: 'object', properties: {} }
43
- },
44
- // Backwards compatibility aliases
45
- {
46
- name: 'audit_contract',
47
- description: 'Alias for m2m_audit_contract.',
48
- inputSchema: { type: 'object', properties: { address: { type: 'string' } }, required: ['address'] }
49
- },
50
- {
51
- name: 'get_capability_score',
52
- description: 'Return the static capability coverage index and provenance for a Base contract. This is not a safety score.',
53
- inputSchema: { type: 'object', properties: { address: { type: 'string' } }, required: ['address'] }
54
- },
55
- {
56
- name: 'get_gas_fees',
57
- description: 'Alias for m2m_get_gas_metrics.',
58
- inputSchema: { type: 'object', properties: {} }
59
- },
60
- {
61
- name: 'get_dex_metrics',
62
- description: 'Alias for m2m_get_dex_liquidity.',
63
- inputSchema: { type: 'object', properties: {} }
64
- },
65
- {
66
- name: 'get_token_price',
67
- description: 'Alias for m2m_get_token_price.',
68
- inputSchema: { type: 'object', properties: { symbol: { type: 'string' } }, required: ['symbol'] }
69
- },
70
- {
71
- name: 'get_whale_signals',
72
- description: 'Alias for m2m_get_whale_signals.',
73
- inputSchema: { type: 'object', properties: {} }
109
+ description: 'Fetch operational status, upstream Base RPC quorum status, and persistence availability for M2M Sentinel infrastructure. Does not return blockchain or market telemetry.',
110
+ inputSchema: {
111
+ type: 'object',
112
+ properties: {}
113
+ },
114
+ annotations: {
115
+ readOnlyHint: true,
116
+ destructiveHint: false,
117
+ idempotentHint: true,
118
+ openWorldHint: true
119
+ }
74
120
  }
75
121
  ];
76
122
 
123
+ // Backwards compatibility alias lookup
124
+ const TOOL_ALIASES = Object.freeze({
125
+ audit_contract: 'm2m_audit_contract',
126
+ get_capability_score: 'm2m_audit_contract',
127
+ get_gas_fees: 'm2m_get_gas_metrics',
128
+ get_dex_metrics: 'm2m_get_dex_liquidity',
129
+ get_token_price: 'm2m_get_token_price',
130
+ get_whale_signals: 'm2m_get_whale_signals'
131
+ });
132
+
77
133
  function parseX402Header(value) {
78
134
  if (!value) return null;
79
135
  try { return JSON.parse(value); } catch (_) {}
@@ -87,8 +143,8 @@ function parseX402Header(value) {
87
143
  }
88
144
 
89
145
  function queryApi(path, customApiKey = null, customBaseUrl = null) {
90
- const activeBaseUrl = customBaseUrl || BASE_URL;
91
- const activeApiKey = customApiKey !== null ? customApiKey : API_KEY;
146
+ const activeBaseUrl = customBaseUrl || process.env.M2M_SENTINEL_BASE_URL || BASE_URL;
147
+ const activeApiKey = customApiKey !== null ? customApiKey : (process.env.M2M_SENTINEL_API_KEY || API_KEY);
92
148
 
93
149
  return new Promise((resolve, reject) => {
94
150
  const url = new URL(path, activeBaseUrl);
@@ -129,12 +185,22 @@ function pathForTool(name, args) {
129
185
  const input = args || {};
130
186
  switch (name) {
131
187
  case 'm2m_audit_contract':
132
- case 'audit_contract':
133
- if (!input.address) throw new Error('address is required');
134
- return '/v1/audit/' + encodeURIComponent(input.address);
135
- case 'get_capability_score':
136
- if (!input.address) throw new Error('address is required');
137
- return '/v1/security/score/' + encodeURIComponent(input.address);
188
+ case 'audit_contract': {
189
+ const addr = input.address ? String(input.address).trim() : '';
190
+ if (!addr) throw new Error('address is required');
191
+ if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) {
192
+ throw new Error('A valid 40-hex 0x-prefixed Base contract address is required');
193
+ }
194
+ return '/v1/audit/' + encodeURIComponent(addr);
195
+ }
196
+ case 'get_capability_score': {
197
+ const addr = input.address ? String(input.address).trim() : '';
198
+ if (!addr) throw new Error('address is required');
199
+ if (!/^0x[0-9a-fA-F]{40}$/.test(addr)) {
200
+ throw new Error('A valid 40-hex 0x-prefixed Base contract address is required');
201
+ }
202
+ return '/v1/security/score/' + encodeURIComponent(addr);
203
+ }
138
204
  case 'm2m_get_gas_metrics':
139
205
  case 'get_gas_fees':
140
206
  return '/v1/gas/fees';
@@ -142,9 +208,11 @@ function pathForTool(name, args) {
142
208
  case 'get_dex_metrics':
143
209
  return '/v1/dex/metrics';
144
210
  case 'm2m_get_token_price':
145
- case 'get_token_price':
146
- if (!input.symbol) throw new Error('symbol is required');
147
- return '/v1/token/price/' + encodeURIComponent(input.symbol);
211
+ case 'get_token_price': {
212
+ const sym = input.symbol ? String(input.symbol).trim().toUpperCase() : '';
213
+ if (!sym) throw new Error('symbol is required');
214
+ return '/v1/token/price/' + encodeURIComponent(sym);
215
+ }
148
216
  case 'm2m_get_whale_signals':
149
217
  case 'get_whale_signals':
150
218
  return '/v1/whales/signals';
@@ -173,24 +241,32 @@ function failure(id, code, message, data) {
173
241
  send({ jsonrpc: '2.0', id, error });
174
242
  }
175
243
 
176
- async function handleMcpMessage(request) {
244
+ async function handleMcpMessage(request, responder = null) {
245
+ const replySuccess = responder ? (id, res) => responder({ jsonrpc: '2.0', id, result: res }) : success;
246
+ const replyFailure = responder ? (id, code, msg, data) => {
247
+ const error = { code, message: msg };
248
+ if (data !== undefined) error.data = data;
249
+ responder({ jsonrpc: '2.0', id, error });
250
+ } : failure;
251
+
177
252
  if (!request || request.jsonrpc !== '2.0' || typeof request.method !== 'string') {
178
- failure(request && request.id !== undefined ? request.id : null, -32600, 'Invalid Request');
253
+ replyFailure(request && request.id !== undefined ? request.id : null, -32600, 'Invalid Request');
179
254
  return;
180
255
  }
181
256
  if (request.id === undefined || request.id === null) return;
182
257
 
183
258
  try {
184
259
  if (request.method === 'initialize') {
185
- success(request.id, {
260
+ replySuccess(request.id, {
186
261
  protocolVersion: request.params && request.params.protocolVersion ? request.params.protocolVersion : '2024-11-05',
187
262
  capabilities: { tools: {} },
188
- serverInfo: { name: 'm2m-sentinel-mcp', version: VERSION }
263
+ serverInfo: { name: 'm2m-sentinel-mcp', version: VERSION },
264
+ instructions: SERVER_INSTRUCTIONS
189
265
  });
190
266
  return;
191
267
  }
192
268
  if (request.method === 'tools/list') {
193
- success(request.id, { tools: TOOLS });
269
+ replySuccess(request.id, { tools: TOOLS });
194
270
  return;
195
271
  }
196
272
  if (request.method === 'tools/call') {
@@ -198,7 +274,7 @@ async function handleMcpMessage(request) {
198
274
  const path = pathForTool(params.name, params.arguments || {});
199
275
  const apiResult = await queryApi(path);
200
276
  const isError = !apiResult.ok;
201
- success(request.id, {
277
+ replySuccess(request.id, {
202
278
  content: [{ type: 'text', text: typeof apiResult.body === 'object' ? JSON.stringify(apiResult.body, null, 2) : String(apiResult.body) }],
203
279
  isError,
204
280
  structuredContent: apiResult.body,
@@ -212,9 +288,9 @@ async function handleMcpMessage(request) {
212
288
  });
213
289
  return;
214
290
  }
215
- failure(request.id, -32601, 'Method not found');
291
+ replyFailure(request.id, -32601, 'Method not found');
216
292
  } catch (err) {
217
- failure(request.id, -32603, err && err.message ? err.message : 'Internal error');
293
+ replyFailure(request.id, -32603, err && err.message ? err.message : 'Internal error');
218
294
  }
219
295
  }
220
296
 
@@ -250,10 +326,10 @@ USAGE:
250
326
 
251
327
  COMMANDS:
252
328
  audit <address> Disassemble bytecode, detect capabilities & resolve proxies
253
- gas Fetch real-time Base Mainnet gas metrics & advice
254
- price <symbol> Fetch DEX liquidity-weighted token price (USDC, WETH, AERO)
255
- dex [pair] Fetch tracked Base liquidity pool metrics
256
- whales [limit] Fetch large ERC-20 transfer signals on Base
329
+ gas Fetch current Base Mainnet gas-price telemetry
330
+ price <symbol> Fetch median Base DEX token price from deepest pools (USDC, WETH, AERO)
331
+ dex Fetch aggregate tracked Base liquidity pool metrics
332
+ whales Fetch large ERC-20 transfer signals on Base (up to 50)
257
333
  status Check live API, RPC quorum, and persistence health
258
334
  mcp Launch Model Context Protocol stdio JSON-RPC server
259
335
 
@@ -419,13 +495,16 @@ async function runCli(args) {
419
495
 
420
496
  } else if (commandName === 'gas' || commandName === 'fees') {
421
497
  console.log('================================================================');
422
- console.log('⛽ M2M SENTINEL — BASE GAS METRICS');
498
+ console.log('⛽ M2M SENTINEL — BASE GAS PRICE TELEMETRY');
423
499
  console.log(`Network: Base Mainnet (8453) | Sourced Latency: ${res.latencyMs}ms`);
424
500
  console.log('================================================================');
425
- console.log(`• Standard Gas Price: ${data.standard || data.gasPriceGwei || '0.005'} gwei`);
426
- if (data.fast) console.log(`• Fast Gas Price: ${data.fast} gwei`);
427
- if (data.instant) console.log(`• Instant Gas Price: ${data.instant} gwei`);
428
- if (data.recommendation) console.log(`• Execution Advice: ${data.recommendation}`);
501
+ const gasPrice = data && data.gasPrice && typeof data.gasPrice === 'object' ? data.gasPrice : data;
502
+ const gwei = gasPrice && gasPrice.gwei !== undefined
503
+ ? gasPrice.gwei
504
+ : (data && data.gasPriceGwei !== undefined ? data.gasPriceGwei : (data && data.standard));
505
+ if (gwei !== undefined) console.log(`• Gas Price: ${gwei} gwei`);
506
+ if (gasPrice && gasPrice.wei !== undefined) console.log(`• Gas Price: ${gasPrice.wei} wei`);
507
+ if (data && data.provenance) console.log(`• RPC Provenance: ${JSON.stringify(data.provenance)}`);
429
508
  console.log('================================================================\n');
430
509
 
431
510
  } else if (commandName === 'price') {
@@ -473,20 +552,33 @@ async function runCli(args) {
473
552
  // Entrypoint: Dual Mode Selection
474
553
  // ---------------------------------------------------------------------------
475
554
 
476
- const cliArgs = process.argv.slice(2);
555
+ if (require.main === module) {
556
+ const cliArgs = process.argv.slice(2);
477
557
 
478
- // If arguments are passed or run in an interactive terminal, invoke CLI
479
- if (cliArgs.length > 0 || (process.stdin.isTTY && !process.env.M2M_MCP_FORCE)) {
480
- if (cliArgs.length === 0) {
481
- printHelp();
558
+ // If arguments are passed or run in an interactive terminal, invoke CLI
559
+ if (cliArgs.length > 0 || (process.stdin.isTTY && !process.env.M2M_MCP_FORCE)) {
560
+ if (cliArgs.length === 0) {
561
+ printHelp();
562
+ } else {
563
+ runCli(cliArgs).then((code) => {
564
+ if (code !== 0 && typeof code === 'number') process.exit(code);
565
+ });
566
+ }
482
567
  } else {
483
- runCli(cliArgs).then((code) => {
484
- if (code !== 0 && typeof code === 'number') process.exit(code);
485
- });
568
+ // Piped execution without args -> start MCP stdio server
569
+ startMcpServer();
486
570
  }
487
- } else {
488
- // Piped execution without args -> start MCP stdio server
489
- startMcpServer();
490
571
  }
491
572
 
492
- module.exports = { TOOLS, pathForTool, parseX402Header, runCli, queryApi };
573
+ module.exports = {
574
+ TOOLS,
575
+ TOOL_ALIASES,
576
+ SERVER_INSTRUCTIONS,
577
+ pathForTool,
578
+ parseX402Header,
579
+ runCli,
580
+ queryApi,
581
+ handleMcpMessage,
582
+ startMcpServer,
583
+ VERSION
584
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "m2m-sentinel-sdk",
3
- "version": "1.2.3",
3
+ "version": "1.2.7",
4
4
  "mcpName": "io.github.M2M-Sentinel/m2m-sentinel-sdk",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,11 +8,17 @@
8
8
  "description": "JavaScript client for M2M Sentinel Base bytecode capability, proxy and market observations",
9
9
  "main": "index.js",
10
10
  "types": "index.d.ts",
11
+ "engines": {
12
+ "node": ">=18"
13
+ },
11
14
  "bin": {
12
15
  "m2m-sentinel-mcp": "./mcp_server.js",
13
16
  "m2m-sentinel": "./mcp_server.js",
14
17
  "m2msentinel": "./mcp_server.js"
15
18
  },
19
+ "scripts": {
20
+ "test": "node --test"
21
+ },
16
22
  "license": "MIT",
17
23
  "homepage": "https://m2msentinel.com",
18
24
  "repository": {
@@ -37,19 +43,29 @@
37
43
  "glama",
38
44
  "agentkit",
39
45
  "coinbase",
40
- "smart-contract-audit"
46
+ "smart-contract-audit",
47
+ "paymaster",
48
+ "wallet-send-calls",
49
+ "sponsored-transaction"
41
50
  ],
42
51
  "author": "M2M Sentinel",
43
52
  "files": [
44
53
  "index.js",
45
54
  "index.d.ts",
55
+ "base_account_paymaster_guard.js",
46
56
  "m2m_sentinel_sdk.js",
47
57
  "agent_adapter.js",
48
58
  "x402_signer.js",
49
59
  "eliza_plugin.js",
60
+ "langchain_tool.js",
50
61
  "mcp_server.js",
51
62
  "smithery.yaml",
52
63
  "README.md",
53
- "typescript/"
64
+ "typescript/index.ts",
65
+ "typescript/x402.ts",
66
+ "typescript/base_account_paymaster_guard.ts",
67
+ "typescript/package.json",
68
+ "typescript/package-lock.json",
69
+ "typescript/tsconfig.json"
54
70
  ]
55
71
  }
package/smithery.yaml CHANGED
@@ -13,7 +13,7 @@ startCommand:
13
13
  M2M_SENTINEL_API_KEY:
14
14
  type: string
15
15
  title: M2M Sentinel API Key
16
- description: Optional API key for higher rate limits. Public free tier works without key.
16
+ description: Optional API key for prepaid access. Protected calls may return structured 401, 402, 429, or 503 responses without authorized access.
17
17
  default: ""
18
18
 
19
19
  configSchema:
@@ -22,6 +22,6 @@ configSchema:
22
22
  M2M_SENTINEL_API_KEY:
23
23
  type: string
24
24
  title: M2M Sentinel API Key
25
- description: Optional API key for higher rate limits. Public free tier works without key.
25
+ description: Optional API key for prepaid access. Protected calls may return structured 401, 402, 429, or 503 responses without authorized access.
26
26
  default: ""
27
27