Pharos Sentinel
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Pharos SentinelCheck risk for transferring 50 PHRS to 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Sentinel โ a pre-action on-chain risk gate for Pharos agents
๐ Live demo: pharos-sentinel-production.up.railway.app โ run the risk gate against the live Atlantic fixtures (each /check executes real Foundry cast reads on Pharos Atlantic testnet).
This repository ships the Pharos Skill Engine with Sentinel added as its
Step 0 โ risk pre-check. The engine
(PharosNetwork/pharos-skill-engine)
gives an AI agent the full Pharos on-chain toolkit โ balance/transaction queries, transfers,
contract deploy & verify, and batch airdrops โ driven through Foundry (cast / forge).
Sentinel is the reusable Skill this repo adds on top: an agent calls it before it moves
value and gets a risk verdict (safe / caution / dangerous), the reasons behind
it, and a risk-bounded execution plan. It is read-only โ it advises and blocks; it never
signs or sends a transaction.
A pre-action risk check is the most-called primitive in any on-chain agent stack: every transfer, swap, or approval is a place an agent can lose funds. Sentinel makes that check a single, composable call, and wires it in as the engine's first write-operation pre-check.
What's in this package
This is the Pharos Skill Engine layout, with Sentinel slotted in as a skill:
SKILL.mdโ the engine's agent entry point. Sentinel is registered in the Capability Index and as Step 0 of the Write-Operation Pre-checks.references/โ the engine's command references (query.md,transaction.md,contract.md,script-gen.md) plussentinel.md, the risk-gate reference.assets/โ the engine'snetworks.json(Atlantic testnet + mainnet),tokens.json, ERC-20 / airdrop Solidity templates, and script-generation templates.Sentinel runtime โ
sentinel_skill.py(MCP server),sentinel_cli.py(CLI),pharos_atlantic.py(RPC reads), plus the demos, live risk gallery, x402 gate, and tests.
Sentinel runs on Atlantic testnet โ matching the engine's default network and its Piggy Bank
reference skill โ while mainnet stays available in networks.json for the engine's other
capabilities.
Related MCP server: aegis-defi
What makes it different
Read-only by design. Sentinel never holds keys and never sends a transaction โ the safest possible posture for a Skill that agents trust before moving money.
Foundry-native execution. Every on-chain read runs through the Foundry
castCLI โ the same toolchain the rest of the Skill Engine uses โ so Sentinel composes into the engine rather than bolting a separate client onto it. No indexer, no third-party API, no keys, no database.Not just a verdict โ a plan.
execution_planreturns approve/block plus bounded sizing within the caller's risk tolerance, so the agent gets a decision, not just a score.Real EVM depth. Bytecode opcode analysis and proxy/ownership introspection, not surface heuristics (details below).
How it works
Sentinel sits between an agent and the chain. The agent declares an intent; Sentinel reads
Pharos Atlantic by executing read-only Foundry cast commands (no keys, no transaction), scores
what it finds, and returns a verdict, the reasons, and a risk-gated plan. The agent acts on the plan.
flowchart LR
A["Pharos agent<br/>transfer ยท swap ยท approve ยท call"] -->|"address + action + amount"| S{{"Sentinel Skill<br/>risk_check / execution_plan"}}
S -->|"cast reads ยท no keys ยท no tx"| C[("Pharos Atlantic<br/>chainId 688689")]
C -->|"cast code ยท call ยท storage"| S
S -->|"verdict + reasons + plan"| D{verdict}
D -->|safe| P["Proceed"]
D -->|caution| R["Reduce size / confirm"]
D -->|dangerous| B["Block"]A blocked approve under a strict safe-only tolerance, end to end (mirrors demo_agent.py):
sequenceDiagram
autonumber
participant Ag as Pharos agent
participant Se as Sentinel
participant Ch as Pharos Atlantic
Ag->>Se: risk_check(addr, "approve", 100)
Se->>Ch: cast code ยท cast call ยท cast storage
Ch-->>Se: bytecode ยท owner ยท impl slot ยท paused
Se-->>Ag: verdict "caution" + reasons
Ag->>Se: execution_plan(addr, "approve", 100, max_risk "safe")
Se-->>Ag: approved=false ยท BLOCK
Note over Ag: agent halts โ no allowance is signedTwo tools
Tool | Purpose |
| Returns |
| Risk-gated: approve/block + bounded sizing within tolerance. |
action is one of transfer | swap | approve | call.
Risk signals (v2 โ read via Foundry cast)
Contract vs EOA, and action/target mismatches (e.g.
approveto a non-token, or to a wallet).Proxy detection: EIP-1167 minimal proxies and EIP-1967/1822 upgradeable proxies (the owner can swap the logic after you interact โ a real rug vector).
Bytecode opcode analysis: a proper opcode walk (stepping over PUSH immediates) that flags SELFDESTRUCT and DELEGATECALL used outside a known proxy pattern.
Ownership & upgrade-admin concentration:
owner()+ the EIP-1967 admin slot, distinguishing an EOA owner (higher centralization risk) from a contract owner (likely multisig/timelock).ERC-20 introspection:
symbol/decimals/totalSupply, with a zero-supply-trap flag.Pausable state (
paused()), tiny-bytecode stubs, and brand-new / zero-history counterparties (a typo & address-poisoning guard).
The score is additive, so signals stack; the verdict is a band over it
(>=70 dangerous, >=35 caution, else safe). Every signal feeds one additive score, which a
band turns into the verdict and execution_plan turns into a decision:
flowchart TD
addr["address + action"] --> isC{"has bytecode?"}
isC -->|"no โ EOA"| eoa["EOA signals<br/>fresh / no history ยท action/target mismatch"]
isC -->|"yes โ contract"| con["Contract signals"]
con --> px["proxy<br/>EIP-1167 ยท 1967 ยท 1822"]
con --> bc["bytecode<br/>SELFDESTRUCT ยท DELEGATECALL"]
con --> own["ownership / upgrade-admin<br/>EOA vs contract"]
con --> tok["ERC-20<br/>supply ยท zero-supply trap"]
con --> st["pausable ยท tiny stub"]
eoa --> sc(["additive score"])
px --> sc
bc --> sc
own --> sc
tok --> sc
st --> sc
sc --> band{"score band"}
band -->|">= 70"| dg["dangerous"]
band -->|">= 35"| ca["caution"]
band -->|"else"| sf["safe"]
dg --> plan["execution_plan<br/>approve / block + bounded size"]
ca --> plan
sf --> planUse it two ways
As an MCP server โ for MCP-capable agents:
pip install -r requirements.txt
python sentinel_skill.py # stdio MCP server exposing risk_check + execution_planAs a framework Skill (SKILL.md) / CLI โ for Claude Code / OpenClaw / Codex style agents:
python sentinel_cli.py <address> <action> # verdict (JSON)
python sentinel_cli.py <address> approve --plan --max-risk safe # risk-gated planThe CLI exits 0 for safe/caution (or an approved plan) and 2 for dangerous/blocked, so a
shell or agent can branch on the exit status alone. See SKILL.md for the skill definition.
Quickstart
Live reads require Foundry (cast) on your PATH (curl -L https://foundry.paradigm.xyz | bash && foundryup).
The offline tests and the --synthetic tour need neither Foundry nor network.
python -m unittest test_sentinel # 34 deterministic offline tests (no network, no Foundry)
python feature_tour.py --synthetic # walk every signal instantly (no network)
python demo_agent.py # an agent drives the Skill over MCP against live Atlantic
python -c "import pharos_atlantic as p; print('chain_ok:', p.chain_ok())"Sample output
$ python sentinel_cli.py 0x24f3cd306c85903ca2ccd0ee8dc1c74111151b23 call
{
"verdict": "caution",
"score": 35,
"reasons": ["tiny bytecode (1 bytes) โ likely a stub/trap rather than a working contract"],
"data": { "is_contract": true, "code_size": 1 }
}Live demo
Drive five Sentinel features as five real Atlantic transactions, one command each:
python demo.py deploy # deploy a malicious contract -> Sentinel flags it DANGEROUS
python demo.py upgrade # swap a proxy's logic in one tx -> verdict escalates
python demo.py pause # pause a contract -> verdict escalates
python demo.py transfer # execution_plan sends real PHRS only when safe
python demo.py x402 # pay-per-query risk check over x402
python demo.py all # all five, with a transaction summaryEvery run deploys fresh contracts and sends fresh transactions โ it's live, not a recording. Full
runbook (commands, suggested patter, expected output) in DEMO.md.
Live on Pharos Atlantic
Sentinel integrates with Pharos Atlantic Testnet via live Foundry cast reads:
RPC
https://atlantic.dplabs-internal.comยท chainId 688689 ยท explorerhttps://atlantic.pharosscan.xyzยท gas token PHRS
Live risk gallery
To prove the engine against real bytecode (not mocks), a spectrum of decoy contracts was
deployed on Atlantic, each engineered to trip a different signal. Sentinel reads them live and
returns a monotonic safe โ caution โ dangerous ladder โ reproduce it yourself with python gallery.py:
Exhibit | Action | Verdict | Score | Signal demonstrated |
transfer | ๐ข safe | 0 | clean ERC-20, no privileged owner โ baseline, no false alarm | |
call | ๐ข safe | 10 | EIP-1167 minimal proxy detected | |
call | ๐ก caution | 35 | tiny-bytecode stub / trap | |
transfer | ๐ก caution | 40 | zero-supply token trap + EOA owner | |
call | ๐ก caution | 50 | EIP-1967 upgradeable + EOA owner + paused | |
call | ๐ด dangerous | 70 | SELFDESTRUCT + unguarded DELEGATECALL + paused |
Each verdict above is produced live, on-chain. The address/verdict map lives in
fixtures.json; gallery.py re-checks every exhibit and fails on any drift. The
Solidity-backed exhibits (CleanToken, ZeroSupplyToken, UpgradeableProxy, Backdoor) have verified
source on Pharos Scan โ open any address and check the Contract tab; sources are in
fixtures/.
Live upgrade attack โ Sentinel catches a rug as it happens
The gallery above is static. To prove Sentinel reads live, mutable state โ and to demonstrate the exact threat it warns about โ a mutable EIP-1967 proxy was deployed pointing at benign logic, then upgraded on-chain to hostile logic in a single transaction. Sentinel read the same proxy address before and after:
Implementation | Verdict | What Sentinel sees | |
Before | benign logic | ๐ข safe (20) | upgradeable โ owner can swap the logic after you interact |
After (upgrade tx) | hostile logic | ๐ก caution (50) | + an EOA owner now holds privileged control + the contract is now PAUSED |
The proxy address never changed โ
0x22Aaโฆd27A โ
but its implementation, ownership, and pause state did. That is the upgrade-rug vector demonstrated
end to end: because Sentinel reads on-chain state at call time, its verdict reflects the swap the
moment it lands. The warning it prints before an attack is the risk that becomes the attack.
Live pause flip โ Sentinel tracks operational state
A second live mutation, on a plain (non-proxy) contract. It carries a latent SELFDESTRUCT
(25 โ below the caution line on its own); the operator then pauses it in a single transaction,
and Sentinel, reading state at call time, tips to caution:
| Verdict | What Sentinel sees | |
Before | โ | ๐ข safe (25) | latent SELFDESTRUCT |
After (pause tx) | true | ๐ก caution (45) | + the contract is now PAUSED |
Same contract โ 0xE84fโฆ410B โ
different live state, different verdict.
The gate moves real value
execution_plan is not advisory theatre โ it decides whether value actually moves.
guarded_transfer.py asks Sentinel, then sends a real PHRS transfer only
when the plan is approved:
โ vetted counterparty โ
safeโ approved โ 0.0005 PHRS sentโ the live
Backdoorfixture โdangerousโ blocked โ no transaction is signed
The Skill stays read-only; only the agent signs. One approval moved value on-chain; one block stopped it before a transaction existed.
Paid calls via x402
Pharos lists "pay-per-query supplier / supply-chain risk assessment" as a flagship x402
use case โ which is exactly what Sentinel is. So risk_check is also exposed behind an
x402 paywall: an unpaid request gets 402 Payment Required; the agent pays a micro-transfer
on Atlantic; the retry returns the verdict plus the settlement tx_hash.
python sentinel_x402.py # read-only x402 gate on 127.0.0.1:4021
python x402_demo.py # 402 -> pay on Atlantic -> 200 verdict -> replay rejectedThe gate verifies payment with the same Foundry cast reads Sentinel uses for risk, so the
server stays read-only and keyless (only the client sends value) and adds no new dependencies
beyond Foundry + the Python stdlib. Full design and the official @x402 SDK path: X402.md.
Verify it yourself
Nothing here is a recording. Sentinel only reads public Pharos Atlantic state, so you can independently confirm both the verdicts and that it never writes.
1. Offline tests โ no network, no Foundry:
python -m unittest test_sentinel # 34 deterministic tests pin the verdict logic2. Live risk gallery โ read-only (needs Foundry cast):
python gallery.py # re-checks the 6 deployed fixtures; fails on any driftExpect a monotonic safe โ caution โ dangerous ladder, all six matching fixtures.json.
3. Check any address yourself:
python sentinel_cli.py 0x75fb8b091A7A88bAF14F23Eac2F33962A4Cdd35D call # the Backdoor fixture โ dangerous (exit 2)4. Reproduce Sentinel's reads by hand โ proof it just reads public chain data via Foundry, nothing hidden:
RPC=https://atlantic.dplabs-internal.com
# the bytecode Sentinel scans (contains SELFDESTRUCT / unguarded DELEGATECALL):
cast code 0x75fb8b091A7A88bAF14F23Eac2F33962A4Cdd35D --rpc-url $RPC
# the pause flag it reads:
cast call 0x75fb8b091A7A88bAF14F23Eac2F33962A4Cdd35D "paused()(bool)" --rpc-url $RPC
# the EIP-1967 implementation slot of the UpgradeableProxy fixture:
cast storage 0xE7797e15DEb86931d7F7b940684Ed1edc5cC7513 \
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $RPCThese are exactly the reads documented in references/sentinel.md โ Sentinel just
folds them into a single verdict. (All are cast call / code / storage; never cast send.)
Security posture
The Skill is intentionally minimal and auditable. It executes only read-only Foundry cast
commands (cast code / call / storage / balance / nonce / chain-id, plus cast rpc
for tx/receipt lookups) against a single Pharos RPC endpoint โ the same execution model the rest
of the Skill Engine uses. It holds no private key, never signs or sends a transaction, makes no
filesystem writes, and reads no secrets or environment beyond the RPC URL.
Files
File | Role |
| MCP server โ tools |
| Pharos Atlantic config + read-only Foundry |
| Thin CLI wrapper for SKILL.md / framework agents |
| Skill definition for Claude Code / OpenClaw / Codex |
| Live demo driver โ one subcommand per on-chain feature (deploy/upgrade/pause/transfer/x402) |
| Live demo runbook โ commands, suggested patter, expected output |
| Demo agent driving the Skill over a real MCP connection against live Atlantic |
| Agent that sends a real PHRS transfer only when |
| Guided walkthrough of every risk signal |
| Re-checks the live Atlantic risk-gallery fixtures and flags drift |
| Deployed gallery addresses + expected verdicts |
| x402 paid-call gate โ |
| Drives the full x402 pay-per-query loop on live Atlantic |
| x402 design: native verify-by-RPC + the official |
| Sentinel's engine reference file โ risk gate + Foundry ( |
| Pharos Skill Engine command references (queries, transactions, contracts, script-gen) |
| Pharos network config (Atlantic testnet + mainnet) โ engine schema |
| Known token registry (both networks) โ engine schema |
| Engine assets โ ERC-20 + airdrop Solidity, script-gen templates |
| 34 offline, deterministic tests |
| Sentinel MCP manifest |
License
MIT-0 (MIT No Attribution) โ free to use, modify, and redistribute. See LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mattcrypted/pharos-sentinel'
If you have feedback or need assistance with the MCP directory API, please join our Discord server