m2m-sentinel-sdk 1.1.0 → 1.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 M2M Sentinel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,71 +1,61 @@
1
- # M2M Sentinel SDKs
2
-
3
- Base URL: `https://m2msentinel.vercel.app`.
4
-
5
- M2M Sentinel reports selected static bytecode capabilities, common proxy
6
- structures, evidence quality, and sourced Base market observations. It does not
7
- classify a contract as safe, malicious, or exploitable. Transaction middleware
8
- therefore requires a caller-defined policy and has no built-in allow threshold.
9
-
10
- ## Support status
11
-
12
- Supported here: JavaScript/Node (`index.js`), TypeScript source, Python source,
13
- and the MCP stdio server. Other language folders are community previews and
14
- must be checked against `/openapi.json` before production use.
15
-
16
- ## Install this source build
17
-
18
- ```bash
19
- node -e "const { M2MSentinelClient } = require('./public/sdk')"
20
- python -m pip install ./public/sdk/python
21
- ```
22
-
23
- Registry publishing is intentionally separate from the production API.
24
- The repository does not claim that these source-installable packages are available from every language registry.
25
-
26
- ## Authentication and x402
27
-
28
- Send API keys only in `x-api-key` (or `Authorization: Bearer`). Query-string
29
- credentials are rejected. Payable routes also support x402 v2. An unpaid call
30
- returns a base64 `PAYMENT-REQUIRED` challenge when the facilitator is ready; a
31
- successful settlement returns `PAYMENT-RESPONSE`.
32
-
33
- ## Public routes
34
-
35
- - `GET /v1/status`
36
- - `GET /v1/stats` (public counter-free privacy envelope; exact aggregate
37
- commercial counters are operator-only)
38
- - `GET /v1/plans`
39
- - `GET /v1/demo/audit/:address` for the published sample allowlist
40
- - free-key and subscription onboarding routes
41
-
42
- The JavaScript, TypeScript, and Python clients expose multi-period
43
- purchase/renewal and wallet recovery without requiring callers to hand-build
44
- requests:
45
-
46
- ```js
47
- await client.createSubscriptionIntent('GROWTH', wallet, {
48
- durationDays: 90,
49
- renewExistingKey: true,
50
- apiKey: existingPaidKey
51
- });
52
- const challenge = await client.createRecoveryChallenge(wallet, { txHash });
53
- await client.claimRecoveredKey(challenge.intent.id, walletSignature);
54
- ```
55
-
56
- `getPublicStats()` returns the counter-free privacy envelope. Operator-only
57
- detail has a deliberately separate `getOperatorAggregateStats(days, token)`
58
- method and sends the operator token only as `Authorization: Bearer`. This is
59
- for private server/operator tooling only: never embed or bundle the operator
60
- token in browser, mobile, SDK-distribution, or customer code.
61
-
62
- ## Protected routes
63
-
64
- - `GET /v1/audit/:address`
65
- - `GET /v1/security/score/:address` (legacy URL; returns a capability coverage index)
66
- - Base gas, DEX, token-price, and large-transfer observation routes
67
- - key self-service routes
68
-
69
- All live-data responses preserve provenance. Capability responses include
70
- `notASafetyGuarantee: true`, a limitations array, `capabilityRating`, and an
71
- evidence-grade flag. Do not replace `UNVERIFIED` or `null` with a default.
1
+ # M2M Sentinel SDK
2
+
3
+ Official multi-language client libraries for M2M Sentinel — the pre-transaction capability evidence layer for Base agents.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/m2m-sentinel-sdk.svg)](https://www.npmjs.com/package/m2m-sentinel-sdk)
6
+ [![PyPI version](https://img.shields.io/pypi/v/m2m-sentinel.svg)](https://pypi.org/project/m2m-sentinel/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
+
9
+ M2M Sentinel provides proxy-aware contract capability observations, implementation slot resolution, gas metrics, DEX liquidity telemetry, and whale signals for autonomous agents operating on Base.
10
+
11
+ ## Installation
12
+
13
+ ### JavaScript / TypeScript (Node.js)
14
+ ```bash
15
+ npm install m2m-sentinel-sdk
16
+ ```
17
+
18
+ ### Python
19
+ ```bash
20
+ pip install m2m-sentinel
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### Node.js / TypeScript
26
+ ```javascript
27
+ const { M2MSentinelClient } = require('m2m-sentinel-sdk');
28
+
29
+ const client = new M2MSentinelClient({
30
+ apiKey: process.env.M2M_SENTINEL_API_KEY
31
+ });
32
+
33
+ async function main() {
34
+ const audit = await client.auditContract('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913');
35
+ console.log('Contract Type:', audit.data.bytecodeAnalysis.contractType);
36
+ console.log('Proxy Detected:', audit.data.proxyDetection.isProxy);
37
+ console.log('Capabilities:', audit.data.bytecodeAnalysis.detectedCapabilities);
38
+ }
39
+
40
+ main().catch(console.error);
41
+ ```
42
+
43
+ ### Python
44
+ ```python
45
+ from m2m_sentinel import M2MSentinelClient
46
+
47
+ client = M2MSentinelClient(api_key="your_api_key")
48
+ audit = client.audit_contract("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
49
+ print("Contract Type:", audit["data"]["bytecodeAnalysis"]["contractType"])
50
+ ```
51
+
52
+ ## Model Context Protocol (MCP) Server
53
+
54
+ M2M Sentinel includes a full Model Context Protocol (MCP) server supporting stdio and HTTP/SSE streams.
55
+
56
+ ```bash
57
+ node mcp_server.js
58
+ ```
59
+
60
+ ## License
61
+ MIT License. Copyright (c) 2026 M2M Sentinel.
package/agent_adapter.js CHANGED
@@ -1,30 +1,202 @@
1
- // UNSUPPORTED / COMMUNITY PREVIEW — not covered by the supported-SDK guarantee; verify against /openapi.json before production use.
1
+ 'use strict';
2
2
 
3
- const { M2MSentinelClient } = require('./index.js');
3
+ /**
4
+ * Coinbase AgentKit Action Provider for M2M Sentinel.
5
+ *
6
+ * Exposes pre-transaction contract bytecode capability inspection, proxy resolution,
7
+ * and market observations to autonomous Base agents using @coinbase/agentkit.
8
+ */
4
9
 
5
- class M2MSentinelAgentTool {
10
+ const https = require('https');
11
+ const http = require('http');
12
+
13
+ const DEFAULT_BASE_URL = process.env.M2M_SENTINEL_BASE_URL || 'https://api.m2msentinel.com';
14
+ const DEFAULT_TIMEOUT_MS = Number(process.env.M2M_SENTINEL_TIMEOUT_MS || 30000);
15
+
16
+ class M2MSentinelActionProvider {
6
17
  constructor(options = {}) {
7
- this.name = 'm2m_sentinel_capability_analysis';
8
- this.description = 'Reports selected static bytecode capabilities, common proxy structures, provenance, and limitations for Base contracts.';
9
- this.client = new M2MSentinelClient({
10
- apiKey: options.apiKey || process.env.M2M_SENTINEL_API_KEY,
11
- baseUrl: options.baseUrl
18
+ this.name = 'm2m_sentinel';
19
+ this.actionProviderName = 'm2m_sentinel';
20
+ this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;
21
+ this.apiKey = options.apiKey || process.env.M2M_SENTINEL_API_KEY || '';
22
+ this.timeoutMs = Number(options.timeoutMs || DEFAULT_TIMEOUT_MS);
23
+ }
24
+
25
+ supportsNetwork(network) {
26
+ if (!network) return true;
27
+ const chainId = String(network.chainId || network.networkId || '');
28
+ const protocolFamily = String(network.protocolFamily || 'evm').toLowerCase();
29
+ return protocolFamily === 'evm' && (
30
+ chainId === '8453' ||
31
+ chainId === 'base' ||
32
+ chainId === 'base-mainnet' ||
33
+ chainId === 'base-sepolia' ||
34
+ chainId === '84532'
35
+ );
36
+ }
37
+
38
+ async _queryApi(endpointPath, options = {}) {
39
+ const url = new URL(endpointPath, this.baseUrl);
40
+ const isHttps = url.protocol === 'https:';
41
+ const transport = isHttps ? https : http;
42
+
43
+ const headers = {
44
+ Accept: 'application/json',
45
+ 'User-Agent': 'M2MSentinel-AgentKit/1.1.1',
46
+ ...options.headers
47
+ };
48
+ if (this.apiKey && !headers['x-api-key']) {
49
+ headers['x-api-key'] = this.apiKey;
50
+ }
51
+
52
+ return new Promise((resolve, reject) => {
53
+ const req = transport.request(url, {
54
+ method: 'GET',
55
+ headers,
56
+ timeout: this.timeoutMs
57
+ }, (res) => {
58
+ let data = '';
59
+ res.setEncoding('utf8');
60
+ res.on('data', (chunk) => { data += chunk; });
61
+ res.on('end', () => {
62
+ let body = null;
63
+ if (data) {
64
+ try { body = JSON.parse(data); } catch (_) { body = { raw: data }; }
65
+ }
66
+ resolve({
67
+ statusCode: res.statusCode,
68
+ ok: res.statusCode >= 200 && res.statusCode < 300,
69
+ body
70
+ });
71
+ });
72
+ });
73
+
74
+ req.on('timeout', () => req.destroy(new Error('M2M Sentinel request timed out')));
75
+ req.on('error', reject);
76
+ req.end();
12
77
  });
13
78
  }
14
79
 
15
- async _call(address) {
16
- try {
17
- return await this.client.auditContract(address);
18
- } catch (err) {
19
- return {
20
- error: err.name || 'M2MSentinelError',
21
- message: err.message,
22
- status: err.status,
23
- paymentRequired: err.paymentRequired || null,
24
- retryAfter: err.retryAfter || null
25
- };
80
+ async auditContract(args) {
81
+ const address = String(args.address || args.contractAddress || '').trim();
82
+ if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
83
+ return JSON.stringify({
84
+ status: 'ERROR',
85
+ error: 'INVALID_ADDRESS',
86
+ message: 'A valid 40-hex 0x-prefixed Base contract address is required.'
87
+ });
88
+ }
89
+
90
+ const res = await this._queryApi(`/v1/audit/${encodeURIComponent(address)}`);
91
+ if (!res.ok) {
92
+ return JSON.stringify({
93
+ status: 'ERROR',
94
+ statusCode: res.statusCode,
95
+ error: res.body && res.body.error ? res.body.error : 'API_ERROR',
96
+ message: res.body && res.body.message ? res.body.message : 'Contract audit query failed',
97
+ notASafetyGuarantee: true
98
+ });
26
99
  }
100
+
101
+ return JSON.stringify({
102
+ status: 'SUCCESS',
103
+ data: res.body,
104
+ notASafetyGuarantee: true,
105
+ observationSummary: `Contract ${address}: Type=${res.body.bytecodeAnalysis?.contractType || 'UNKNOWN'}, isProxy=${Boolean(res.body.proxyDetection?.isProxy)}`
106
+ });
107
+ }
108
+
109
+ async getGasMetrics() {
110
+ const res = await this._queryApi('/v1/gas/fees');
111
+ if (!res.ok) {
112
+ return JSON.stringify({ status: 'ERROR', statusCode: res.statusCode, message: 'Failed to retrieve gas fees' });
113
+ }
114
+ return JSON.stringify(res.body);
115
+ }
116
+
117
+ async getTokenPrice(args) {
118
+ const symbol = String(args.symbol || args.tokenSymbol || '').trim().toUpperCase();
119
+ if (!symbol) {
120
+ return JSON.stringify({ status: 'ERROR', message: 'Token symbol is required (e.g. USDC, WETH)' });
121
+ }
122
+ const res = await this._queryApi(`/v1/token/price/${encodeURIComponent(symbol)}`);
123
+ if (!res.ok) {
124
+ return JSON.stringify({ status: 'ERROR', statusCode: res.statusCode, message: `Failed to retrieve price for ${symbol}` });
125
+ }
126
+ return JSON.stringify(res.body);
127
+ }
128
+
129
+ async getServiceStatus() {
130
+ const res = await this._queryApi('/v1/status');
131
+ if (!res.ok) {
132
+ return JSON.stringify({ status: 'UNAVAILABLE', statusCode: res.statusCode });
133
+ }
134
+ return JSON.stringify(res.body);
135
+ }
136
+
137
+ getActions(_walletProvider) {
138
+ return [
139
+ {
140
+ name: 'm2m_audit_contract',
141
+ description: 'Inspect Base target contract bytecode capability observations, proxy implementation slots, and limitations before executing transactions. Returns factual evidence, not a safety guarantee.',
142
+ schema: {
143
+ type: 'object',
144
+ properties: {
145
+ address: {
146
+ type: 'string',
147
+ description: 'Target Base contract address (0x-prefixed 40-hex)'
148
+ }
149
+ },
150
+ required: ['address']
151
+ },
152
+ invoke: (args) => this.auditContract(args)
153
+ },
154
+ {
155
+ name: 'm2m_get_gas_metrics',
156
+ description: 'Get real-time Base network gas execution metrics and recommendations before submitting on-chain transactions.',
157
+ schema: {
158
+ type: 'object',
159
+ properties: {}
160
+ },
161
+ invoke: () => this.getGasMetrics()
162
+ },
163
+ {
164
+ name: 'm2m_get_token_price',
165
+ description: 'Observe real-time Base DEX token price for slippage check and valuation.',
166
+ schema: {
167
+ type: 'object',
168
+ properties: {
169
+ symbol: {
170
+ type: 'string',
171
+ description: 'Token symbol on Base (e.g. USDC, WETH)'
172
+ }
173
+ },
174
+ required: ['symbol']
175
+ },
176
+ invoke: (args) => this.getTokenPrice(args)
177
+ },
178
+ {
179
+ name: 'm2m_get_service_status',
180
+ description: 'Check operational status of M2M Sentinel upstream verification rails.',
181
+ schema: {
182
+ type: 'object',
183
+ properties: {}
184
+ },
185
+ invoke: () => this.getServiceStatus()
186
+ }
187
+ ];
27
188
  }
28
189
  }
29
190
 
30
- module.exports = { M2MSentinelAgentTool };
191
+ function m2mSentinelActionProvider(options = {}) {
192
+ return new M2MSentinelActionProvider(options);
193
+ }
194
+
195
+ // Backward compatibility alias
196
+ class M2MSentinelAgentTool extends M2MSentinelActionProvider {}
197
+
198
+ module.exports = {
199
+ M2MSentinelActionProvider,
200
+ m2mSentinelActionProvider,
201
+ M2MSentinelAgentTool
202
+ };
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const DEFAULT_BASE_URL = 'https://m2msentinel.vercel.app';
1
+ const DEFAULT_BASE_URL = 'https://api.m2msentinel.com';
2
2
  const DEFAULT_TIMEOUT_MS = 30000;
3
3
 
4
4
  class M2MSentinelError extends Error {
@@ -171,6 +171,8 @@ function createViemSentinelInterceptor(apiKey, baseUrl, policy) {
171
171
  };
172
172
  }
173
173
 
174
+ const { M2MSentinelActionProvider, m2mSentinelActionProvider } = require('./agent_adapter.js');
175
+
174
176
  module.exports = {
175
177
  M2MSentinelClient,
176
178
  M2MSentinelError,
@@ -179,5 +181,7 @@ module.exports = {
179
181
  DataSourceUnavailableError,
180
182
  enforceCallerPolicy,
181
183
  createEthersSentinelMiddleware,
182
- createViemSentinelInterceptor
184
+ createViemSentinelInterceptor,
185
+ M2MSentinelActionProvider,
186
+ m2mSentinelActionProvider
183
187
  };
package/mcp_server.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "m2m-sentinel-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "JavaScript client for M2M Sentinel Base bytecode capability, proxy and market observations",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
+ "bin": {
8
+ "m2m-sentinel-mcp": "./mcp_server.js",
9
+ "m2m-sentinel": "./mcp_server.js"
10
+ },
7
11
  "license": "MIT",
8
- "homepage": "https://m2msentinel.vercel.app",
12
+ "homepage": "https://m2msentinel.com",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/M2M-Sentinel/m2m-sentinel-sdk.git"
16
+ },
9
17
  "bugs": {
10
- "url": "https://m2msentinel.vercel.app/docs.html"
18
+ "url": "https://m2msentinel.com/docs.html"
11
19
  },
12
20
  "keywords": [
13
21
  "web3",