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 +21 -0
- package/README.md +61 -71
- package/agent_adapter.js +192 -20
- package/index.js +6 -2
- package/mcp_server.js +0 -0
- package/package.json +11 -3
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
|
|
2
|
-
|
|
3
|
-
Base
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
+
[](https://www.npmjs.com/package/m2m-sentinel-sdk)
|
|
6
|
+
[](https://pypi.org/project/m2m-sentinel/)
|
|
7
|
+
[](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
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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 = '
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
error:
|
|
21
|
-
message:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
18
|
+
"url": "https://m2msentinel.com/docs.html"
|
|
11
19
|
},
|
|
12
20
|
"keywords": [
|
|
13
21
|
"web3",
|