interline 0.1.0__tar.gz
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.
- interline-0.1.0/LICENSE +21 -0
- interline-0.1.0/PKG-INFO +82 -0
- interline-0.1.0/README.md +120 -0
- interline-0.1.0/interline.egg-info/PKG-INFO +82 -0
- interline-0.1.0/interline.egg-info/SOURCES.txt +29 -0
- interline-0.1.0/interline.egg-info/dependency_links.txt +1 -0
- interline-0.1.0/interline.egg-info/entry_points.txt +2 -0
- interline-0.1.0/interline.egg-info/requires.txt +9 -0
- interline-0.1.0/interline.egg-info/top_level.txt +2 -0
- interline-0.1.0/mcp_router/README.md +63 -0
- interline-0.1.0/mcp_router/__init__.py +1 -0
- interline-0.1.0/mcp_router/discovery.py +152 -0
- interline-0.1.0/mcp_router/selftest.py +144 -0
- interline-0.1.0/mcp_router/server.py +85 -0
- interline-0.1.0/pyproject.toml +37 -0
- interline-0.1.0/router/__init__.py +0 -0
- interline-0.1.0/router/buyer.py +70 -0
- interline-0.1.0/router/config.py +113 -0
- interline-0.1.0/router/facilitator_mock.py +119 -0
- interline-0.1.0/router/facilitator_real.py +67 -0
- interline-0.1.0/router/inbound/__init__.py +0 -0
- interline-0.1.0/router/inbound/ap2_adapter.py +234 -0
- interline-0.1.0/router/ledger.py +36 -0
- interline-0.1.0/router/paywall.py +153 -0
- interline-0.1.0/router/rails/__init__.py +61 -0
- interline-0.1.0/router/rails/base.py +45 -0
- interline-0.1.0/router/rails/mpp_rail.py +332 -0
- interline-0.1.0/router/rails/x402_rail.py +73 -0
- interline-0.1.0/router/seller.py +95 -0
- interline-0.1.0/router/wallet.py +62 -0
- interline-0.1.0/setup.cfg +4 -0
interline-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Choppaaahh
|
|
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.
|
interline-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: interline
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Neutral, non-custodial MCP server that lets any AI agent pay across payment rails (x402 today, more landing) through one integration — rail-discovery + routing + a unified receipt ledger.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: mcp,model-context-protocol,x402,agent-payments,payment-router,agentic-payments,non-custodial,usdc,ai-agent
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: mcp>=1.0
|
|
11
|
+
Requires-Dist: httpx>=0.28
|
|
12
|
+
Requires-Dist: x402>=2.13
|
|
13
|
+
Requires-Dist: eth-account>=0.13
|
|
14
|
+
Requires-Dist: web3>=7.0
|
|
15
|
+
Requires-Dist: fastapi>=0.110
|
|
16
|
+
Provides-Extra: server
|
|
17
|
+
Requires-Dist: uvicorn>=0.27; extra == "server"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Interline (MCP server)
|
|
21
|
+
|
|
22
|
+
**Rail-discovery + future-proof payments for AI agents — as an MCP server.**
|
|
23
|
+
|
|
24
|
+
Two things your agent gets, **today**:
|
|
25
|
+
1. **Discover** which payment rail(s) any paid endpoint accepts — *before* paying. (The discovery layer single-rail clients don't have.)
|
|
26
|
+
2. **Pay** through it — non-custodial, your own key, capped.
|
|
27
|
+
|
|
28
|
+
And the part that pays off across **every rail**: **integrate once.** Three rails are live today — [x402](https://x402.org) (HTTP 402 + USDC) on EVM (Base Sepolia) and on Solana (devnet), plus **MPP** on Tempo (Moderato testnet) — all behind the *same tool*. Real on-chain agent-to-agent settles are confirmed on all three. Every future rail drops in with **zero code change**. **Never re-integrate agent payments again** — when the next rail ships, your agent already speaks it.
|
|
29
|
+
|
|
30
|
+
This is **not "another x402 MCP."** Single-rail clients make you wire up one rail (and re-wire for the next). This is the **discovery + routing layer above them** — same shape OpenRouter has for models. Non-custodial by design: payments use **your own wallet key**; this server never holds, sees, or routes funds through itself.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `discover_payment_rails(url)` | Probe a paid endpoint; report **which rails + prices** it accepts. **No payment.** The discovery layer. |
|
|
37
|
+
| `pay_for_resource(url, task, max_price_usdc)` | Pay + fetch through the accepted rail; return content **+ a settlement receipt**. Never exceeds `max_price_usdc`. |
|
|
38
|
+
| `payment_history(limit)` | The **unified cross-rail receipt ledger** — every settlement, every rail, one view. |
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install interline # (or: uvx interline)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Add to Claude Desktop / Cursor / any MCP client
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"interline": {
|
|
52
|
+
"command": "uvx",
|
|
53
|
+
"args": ["interline"],
|
|
54
|
+
"env": {
|
|
55
|
+
"APV0_BUYER_PRIVATE_KEY": "0x...", // YOUR wallet key — stays in your env, never leaves your machine
|
|
56
|
+
"APV0_NETWORK": "base-sepolia" // or "base" for mainnet
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Local dev (from a clone):
|
|
64
|
+
|
|
65
|
+
```jsonc
|
|
66
|
+
{ "mcpServers": { "interline": {
|
|
67
|
+
"command": "python", "args": ["-m", "mcp_router.server"],
|
|
68
|
+
"cwd": "/path/to/interline-routes",
|
|
69
|
+
"env": { "APV0_BUYER_PRIVATE_KEY": "0x...", "APV0_NETWORK": "base-sepolia" }
|
|
70
|
+
}}}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`discover_payment_rails` needs no key (it only reads the 402 challenge). `pay_for_resource` needs `APV0_BUYER_PRIVATE_KEY` (your funded wallet).
|
|
74
|
+
|
|
75
|
+
## Non-custodial guarantee
|
|
76
|
+
|
|
77
|
+
- The router **never holds funds.** `discover` only reads a public 402 challenge; `pay` signs with **your** key, locally, bounded by `max_price_usdc`.
|
|
78
|
+
- The fee model (when one exists) is a **software/routing fee billed to you, the developer — never a cut of the funds flow.** That's the line between software (this) and money transmission (not this).
|
|
79
|
+
|
|
80
|
+
## Status
|
|
81
|
+
|
|
82
|
+
Three rails live — x402 USDC on EVM (Base Sepolia) and on Solana (devnet), plus MPP on Tempo (Moderato testnet). Real on-chain agent-to-agent settles confirmed on all three. Neutral by construction: `discover_payment_rails` surfaces *any* rail an endpoint offers, and each additional rail is a drop-in adapter with no caller change. Built on the [interline-routes](https://github.com/Choppaaahh/interline-routes) rail-agnostic core.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Interline — agent-to-agent payment router
|
|
2
|
+
|
|
3
|
+
An **agent-to-agent payment router**: one agent does work, another agent pays for
|
|
4
|
+
it, no human keys a card. **Interline** is a neutral, non-custodial router over the
|
|
5
|
+
fragmented agent-payment-rail stack — *"OpenRouter for agent payments."* Built on
|
|
6
|
+
[x402](https://x402.org) (HTTP-402 micropayments, USDC), aggregator-shaped so adding
|
|
7
|
+
a rail is one adapter, not a rewrite.
|
|
8
|
+
|
|
9
|
+
Today it routes payments across **three live rails** — **x402** USDC on EVM
|
|
10
|
+
(Base Sepolia) and on Solana (devnet), plus **MPP** on Tempo (Moderato testnet) —
|
|
11
|
+
all behind one `Paywall.gate()` call. Real on-chain agent-to-agent settles are
|
|
12
|
+
confirmed on all three. It ships an **MCP router** so agents can discover + pay
|
|
13
|
+
endpoints, and includes a **Google-AP2 inbound adapter** — a signed AP2 mandate
|
|
14
|
+
(verified, constraint-checked + freshness-gated) settles non-custodially on the rails.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Run the demo (no wallet, no faucet, no risk)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
21
|
+
pip install -r requirements.txt
|
|
22
|
+
python3 run_demo.py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Expected: `DEMO PASS — agent paid agent, settlement receipt issued`.
|
|
26
|
+
|
|
27
|
+
What just happened (the x402 "exact-evm" loop):
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
buyer GET /work
|
|
31
|
+
-> seller 402 + PaymentRequirements {amount, asset=USDC, payTo, network}
|
|
32
|
+
-> buyer signs an EIP-3009 transferWithAuthorization (gasless USDC auth)
|
|
33
|
+
-> buyer retries with X-PAYMENT header (base64 signed payload)
|
|
34
|
+
-> facilitator VERIFIES the signature (real signer-recovery) + checks policy
|
|
35
|
+
-> facilitator SETTLES (mock: fake tx hash; live: on-chain USDC transfer)
|
|
36
|
+
-> seller 200 + work product + X-PAYMENT-RESPONSE receipt (tx hash)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The facilitator's verify step is **real cryptography** even in mock mode — it
|
|
40
|
+
recovers the EIP-712 signer and matches it to the authorization. Only the
|
|
41
|
+
on-chain broadcast is mocked. So a passing demo proves the buyer's signing is
|
|
42
|
+
protocol-correct against real USDC.
|
|
43
|
+
|
|
44
|
+
## Files
|
|
45
|
+
|
|
46
|
+
| file | role |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `router/paywall.py` | the reusable `Paywall` primitive — gate any endpoint behind x402 in one `.gate()` call |
|
|
49
|
+
| `router/ledger.py` | settlement receipt-ledger → `logs/agent_payment_settlements.jsonl` (audit trail) |
|
|
50
|
+
| `router/seller.py` | FastAPI agent that sells work — defines the requirements + work, wires one `Paywall.gate()` |
|
|
51
|
+
| `router/buyer.py` | agent that auto-pays 402s (`pay_and_get`) with a client-side spend limit |
|
|
52
|
+
| `router/eip3009.py` | the one crypto-subtle file: EIP-3009 typed-data, shared by buyer+facilitator so they can't drift |
|
|
53
|
+
| `router/facilitator_mock.py` | in-process verify+settle (real sig check, mock chain) — the dry-run rail |
|
|
54
|
+
| `router/facilitator_real.py` | live x402 facilitator over HTTP via the x402 SDK's own client; `get_facilitator()` picks mock↔real by config |
|
|
55
|
+
| `router/config.py` | network/asset facts + `.env` loader; mock↔testnet↔mainnet is a one-line env change |
|
|
56
|
+
| `run_demo.py` | end-to-end smoke (ephemeral keys, mock facilitator) |
|
|
57
|
+
| `run_live.py` | live runner — settles a real x402 payment on Base Sepolia |
|
|
58
|
+
|
|
59
|
+
## Go live (Base Sepolia testnet)
|
|
60
|
+
|
|
61
|
+
1. **Two testnet wallets** (buyer signs, seller receives):
|
|
62
|
+
```bash
|
|
63
|
+
python3 -c "from eth_account import Account; a=Account.create(); print('addr', a.address); print('key', a.key.hex())"
|
|
64
|
+
```
|
|
65
|
+
Do this twice. Keep the keys out of git (use `.env`).
|
|
66
|
+
2. **Fund the buyer** with Base Sepolia testnet USDC (Circle faucet) + a little
|
|
67
|
+
testnet ETH for any gas the facilitator relays.
|
|
68
|
+
3. **`.env`** (copy `.env.example` → `.env`, it's gitignored + auto-loaded):
|
|
69
|
+
```
|
|
70
|
+
APV0_NETWORK=base-sepolia
|
|
71
|
+
APV0_BUYER_PRIVATE_KEY=0x... # buyer testnet key (signs)
|
|
72
|
+
APV0_SELLER_ADDRESS=0x... # seller address (receives)
|
|
73
|
+
APV0_FACILITATOR_URL=https://x402.org/facilitator
|
|
74
|
+
```
|
|
75
|
+
4. **Run it live:**
|
|
76
|
+
```bash
|
|
77
|
+
python3 run_live.py
|
|
78
|
+
```
|
|
79
|
+
The seller now uses `RealFacilitator` (the x402 SDK's own facilitator client),
|
|
80
|
+
the buyer auto-pays the 402, and x402.org broadcasts the EIP-3009 USDC transfer
|
|
81
|
+
on-chain. You get a real tx hash → `https://sepolia.basescan.org/tx/<hash>`.
|
|
82
|
+
|
|
83
|
+
> Gasless: with EIP-3009 the **facilitator relays gas**, so the buyer only needs
|
|
84
|
+
> testnet **USDC**, not ETH.
|
|
85
|
+
|
|
86
|
+
## Gate your own endpoint (the product primitive)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from router.paywall import Paywall
|
|
90
|
+
from router.facilitator_real import get_facilitator
|
|
91
|
+
|
|
92
|
+
paywall = Paywall(get_facilitator(), my_requirements_fn) # mock ↔ real by env
|
|
93
|
+
|
|
94
|
+
@app.get("/my-endpoint")
|
|
95
|
+
def my_endpoint(request: Request):
|
|
96
|
+
return paywall.gate(request, lambda: do_expensive_work())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
That one `.gate()` call handles the whole x402 V2 dance — 402 challenge → verify →
|
|
100
|
+
settle → record receipt → deliver — and appends every settlement to the
|
|
101
|
+
receipt-ledger. The deployer never touches the protocol or holds a key.
|
|
102
|
+
|
|
103
|
+
## v1 dogfood — pay an agent, get REAL work back
|
|
104
|
+
|
|
105
|
+
v0 proved the *payment*. v1 proves the *loop*: a buyer agent pays → a seller agent
|
|
106
|
+
does **real inference** → returns the result + a settlement receipt. The seller's
|
|
107
|
+
`_do_the_work(task)` is the product's integration point (plug in your own
|
|
108
|
+
model/compute/service).
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
APV0_OPENROUTER_KEY=sk-or-... python3 run_v1_dogfood.py "your task here"
|
|
112
|
+
```
|
|
113
|
+
→ `V1 DOGFOOD PASS — agent paid agent for real work ✅` (mock payment + real model
|
|
114
|
+
call by default; set `APV0_NETWORK=base-sepolia` for a real on-chain settle under
|
|
115
|
+
the same loop). Without a key it returns a stub so the product still runs.
|
|
116
|
+
|
|
117
|
+
This is the authentic loop: an agent doing real work, metered + paid-for over
|
|
118
|
+
x402 — the same primitive whether the work is a model call, a compute job, or
|
|
119
|
+
any other billable agent task.
|
|
120
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: interline
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Neutral, non-custodial MCP server that lets any AI agent pay across payment rails (x402 today, more landing) through one integration — rail-discovery + routing + a unified receipt ledger.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: mcp,model-context-protocol,x402,agent-payments,payment-router,agentic-payments,non-custodial,usdc,ai-agent
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: mcp>=1.0
|
|
11
|
+
Requires-Dist: httpx>=0.28
|
|
12
|
+
Requires-Dist: x402>=2.13
|
|
13
|
+
Requires-Dist: eth-account>=0.13
|
|
14
|
+
Requires-Dist: web3>=7.0
|
|
15
|
+
Requires-Dist: fastapi>=0.110
|
|
16
|
+
Provides-Extra: server
|
|
17
|
+
Requires-Dist: uvicorn>=0.27; extra == "server"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Interline (MCP server)
|
|
21
|
+
|
|
22
|
+
**Rail-discovery + future-proof payments for AI agents — as an MCP server.**
|
|
23
|
+
|
|
24
|
+
Two things your agent gets, **today**:
|
|
25
|
+
1. **Discover** which payment rail(s) any paid endpoint accepts — *before* paying. (The discovery layer single-rail clients don't have.)
|
|
26
|
+
2. **Pay** through it — non-custodial, your own key, capped.
|
|
27
|
+
|
|
28
|
+
And the part that pays off across **every rail**: **integrate once.** Three rails are live today — [x402](https://x402.org) (HTTP 402 + USDC) on EVM (Base Sepolia) and on Solana (devnet), plus **MPP** on Tempo (Moderato testnet) — all behind the *same tool*. Real on-chain agent-to-agent settles are confirmed on all three. Every future rail drops in with **zero code change**. **Never re-integrate agent payments again** — when the next rail ships, your agent already speaks it.
|
|
29
|
+
|
|
30
|
+
This is **not "another x402 MCP."** Single-rail clients make you wire up one rail (and re-wire for the next). This is the **discovery + routing layer above them** — same shape OpenRouter has for models. Non-custodial by design: payments use **your own wallet key**; this server never holds, sees, or routes funds through itself.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `discover_payment_rails(url)` | Probe a paid endpoint; report **which rails + prices** it accepts. **No payment.** The discovery layer. |
|
|
37
|
+
| `pay_for_resource(url, task, max_price_usdc)` | Pay + fetch through the accepted rail; return content **+ a settlement receipt**. Never exceeds `max_price_usdc`. |
|
|
38
|
+
| `payment_history(limit)` | The **unified cross-rail receipt ledger** — every settlement, every rail, one view. |
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install interline # (or: uvx interline)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Add to Claude Desktop / Cursor / any MCP client
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"interline": {
|
|
52
|
+
"command": "uvx",
|
|
53
|
+
"args": ["interline"],
|
|
54
|
+
"env": {
|
|
55
|
+
"APV0_BUYER_PRIVATE_KEY": "0x...", // YOUR wallet key — stays in your env, never leaves your machine
|
|
56
|
+
"APV0_NETWORK": "base-sepolia" // or "base" for mainnet
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Local dev (from a clone):
|
|
64
|
+
|
|
65
|
+
```jsonc
|
|
66
|
+
{ "mcpServers": { "interline": {
|
|
67
|
+
"command": "python", "args": ["-m", "mcp_router.server"],
|
|
68
|
+
"cwd": "/path/to/interline-routes",
|
|
69
|
+
"env": { "APV0_BUYER_PRIVATE_KEY": "0x...", "APV0_NETWORK": "base-sepolia" }
|
|
70
|
+
}}}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`discover_payment_rails` needs no key (it only reads the 402 challenge). `pay_for_resource` needs `APV0_BUYER_PRIVATE_KEY` (your funded wallet).
|
|
74
|
+
|
|
75
|
+
## Non-custodial guarantee
|
|
76
|
+
|
|
77
|
+
- The router **never holds funds.** `discover` only reads a public 402 challenge; `pay` signs with **your** key, locally, bounded by `max_price_usdc`.
|
|
78
|
+
- The fee model (when one exists) is a **software/routing fee billed to you, the developer — never a cut of the funds flow.** That's the line between software (this) and money transmission (not this).
|
|
79
|
+
|
|
80
|
+
## Status
|
|
81
|
+
|
|
82
|
+
Three rails live — x402 USDC on EVM (Base Sepolia) and on Solana (devnet), plus MPP on Tempo (Moderato testnet). Real on-chain agent-to-agent settles confirmed on all three. Neutral by construction: `discover_payment_rails` surfaces *any* rail an endpoint offers, and each additional rail is a drop-in adapter with no caller change. Built on the [interline-routes](https://github.com/Choppaaahh/interline-routes) rail-agnostic core.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
interline.egg-info/PKG-INFO
|
|
5
|
+
interline.egg-info/SOURCES.txt
|
|
6
|
+
interline.egg-info/dependency_links.txt
|
|
7
|
+
interline.egg-info/entry_points.txt
|
|
8
|
+
interline.egg-info/requires.txt
|
|
9
|
+
interline.egg-info/top_level.txt
|
|
10
|
+
mcp_router/README.md
|
|
11
|
+
mcp_router/__init__.py
|
|
12
|
+
mcp_router/discovery.py
|
|
13
|
+
mcp_router/selftest.py
|
|
14
|
+
mcp_router/server.py
|
|
15
|
+
router/__init__.py
|
|
16
|
+
router/buyer.py
|
|
17
|
+
router/config.py
|
|
18
|
+
router/facilitator_mock.py
|
|
19
|
+
router/facilitator_real.py
|
|
20
|
+
router/ledger.py
|
|
21
|
+
router/paywall.py
|
|
22
|
+
router/seller.py
|
|
23
|
+
router/wallet.py
|
|
24
|
+
router/inbound/__init__.py
|
|
25
|
+
router/inbound/ap2_adapter.py
|
|
26
|
+
router/rails/__init__.py
|
|
27
|
+
router/rails/base.py
|
|
28
|
+
router/rails/mpp_rail.py
|
|
29
|
+
router/rails/x402_rail.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Interline (MCP server)
|
|
2
|
+
|
|
3
|
+
**Rail-discovery + future-proof payments for AI agents — as an MCP server.**
|
|
4
|
+
|
|
5
|
+
Two things your agent gets, **today**:
|
|
6
|
+
1. **Discover** which payment rail(s) any paid endpoint accepts — *before* paying. (The discovery layer single-rail clients don't have.)
|
|
7
|
+
2. **Pay** through it — non-custodial, your own key, capped.
|
|
8
|
+
|
|
9
|
+
And the part that pays off across **every rail**: **integrate once.** Three rails are live today — [x402](https://x402.org) (HTTP 402 + USDC) on EVM (Base Sepolia) and on Solana (devnet), plus **MPP** on Tempo (Moderato testnet) — all behind the *same tool*. Real on-chain agent-to-agent settles are confirmed on all three. Every future rail drops in with **zero code change**. **Never re-integrate agent payments again** — when the next rail ships, your agent already speaks it.
|
|
10
|
+
|
|
11
|
+
This is **not "another x402 MCP."** Single-rail clients make you wire up one rail (and re-wire for the next). This is the **discovery + routing layer above them** — same shape OpenRouter has for models. Non-custodial by design: payments use **your own wallet key**; this server never holds, sees, or routes funds through itself.
|
|
12
|
+
|
|
13
|
+
## Tools
|
|
14
|
+
|
|
15
|
+
| Tool | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `discover_payment_rails(url)` | Probe a paid endpoint; report **which rails + prices** it accepts. **No payment.** The discovery layer. |
|
|
18
|
+
| `pay_for_resource(url, task, max_price_usdc)` | Pay + fetch through the accepted rail; return content **+ a settlement receipt**. Never exceeds `max_price_usdc`. |
|
|
19
|
+
| `payment_history(limit)` | The **unified cross-rail receipt ledger** — every settlement, every rail, one view. |
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install interline # (or: uvx interline)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Add to Claude Desktop / Cursor / any MCP client
|
|
28
|
+
|
|
29
|
+
```jsonc
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"interline": {
|
|
33
|
+
"command": "uvx",
|
|
34
|
+
"args": ["interline"],
|
|
35
|
+
"env": {
|
|
36
|
+
"APV0_BUYER_PRIVATE_KEY": "0x...", // YOUR wallet key — stays in your env, never leaves your machine
|
|
37
|
+
"APV0_NETWORK": "base-sepolia" // or "base" for mainnet
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Local dev (from a clone):
|
|
45
|
+
|
|
46
|
+
```jsonc
|
|
47
|
+
{ "mcpServers": { "interline": {
|
|
48
|
+
"command": "python", "args": ["-m", "mcp_router.server"],
|
|
49
|
+
"cwd": "/path/to/interline-routes",
|
|
50
|
+
"env": { "APV0_BUYER_PRIVATE_KEY": "0x...", "APV0_NETWORK": "base-sepolia" }
|
|
51
|
+
}}}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`discover_payment_rails` needs no key (it only reads the 402 challenge). `pay_for_resource` needs `APV0_BUYER_PRIVATE_KEY` (your funded wallet).
|
|
55
|
+
|
|
56
|
+
## Non-custodial guarantee
|
|
57
|
+
|
|
58
|
+
- The router **never holds funds.** `discover` only reads a public 402 challenge; `pay` signs with **your** key, locally, bounded by `max_price_usdc`.
|
|
59
|
+
- The fee model (when one exists) is a **software/routing fee billed to you, the developer — never a cut of the funds flow.** That's the line between software (this) and money transmission (not this).
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
|
|
63
|
+
Three rails live — x402 USDC on EVM (Base Sepolia) and on Solana (devnet), plus MPP on Tempo (Moderato testnet). Real on-chain agent-to-agent settles confirmed on all three. Neutral by construction: `discover_payment_rails` surfaces *any* rail an endpoint offers, and each additional rail is a drop-in adapter with no caller change. Built on the [interline-routes](https://github.com/Choppaaahh/interline-routes) rail-agnostic core.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Interline — MCP server: let any MCP agent pay across payment rails (neutral, non-custodial)."""
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rail discovery — given a paid endpoint, report which payment rails it accepts.
|
|
3
|
+
|
|
4
|
+
THIS is the differentiator vs "another x402 payment MCP": one call tells an agent
|
|
5
|
+
*every* way it could pay an endpoint, across whatever rails the endpoint offers —
|
|
6
|
+
the neutral router's rail-discovery layer. Today x402 is live; as rails land (MPP, …)
|
|
7
|
+
they surface in the same shape with zero caller change.
|
|
8
|
+
|
|
9
|
+
The parse step (`parse_accepts`) is split from the network fetch (`discover_rails`) so
|
|
10
|
+
the normalization is golden-fixture testable with no network.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
# x402/MPP payment `scheme` → human rail name. Grows as rails land.
|
|
17
|
+
# Unknown schemes fall through to themselves, so discovery still SURFACES a rail we don't
|
|
18
|
+
# yet have a pretty name for (neutrality: report what's offered, don't hide it).
|
|
19
|
+
SCHEME_TO_RAIL = {"exact": "x402", "mpp": "mpp"}
|
|
20
|
+
|
|
21
|
+
# Default token decimals when not derivable from the challenge (USDC = 6).
|
|
22
|
+
_DEFAULT_DECIMALS = 6
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_accepts(body: dict) -> list[dict]:
|
|
26
|
+
"""Pure: normalize an x402 402-challenge body's `accepts` array into a rail list."""
|
|
27
|
+
rails: list[dict] = []
|
|
28
|
+
for a in (body.get("accepts") or []):
|
|
29
|
+
scheme = a.get("scheme", "?")
|
|
30
|
+
extra = a.get("extra") or {}
|
|
31
|
+
amt = a.get("amount")
|
|
32
|
+
try:
|
|
33
|
+
human = f"{int(amt) / 10 ** _DEFAULT_DECIMALS:.6f} {extra.get('name', 'token')}"
|
|
34
|
+
except (TypeError, ValueError):
|
|
35
|
+
human = str(amt)
|
|
36
|
+
rails.append({
|
|
37
|
+
"rail": SCHEME_TO_RAIL.get(scheme, scheme),
|
|
38
|
+
"scheme": scheme,
|
|
39
|
+
"network": a.get("network"),
|
|
40
|
+
"asset": a.get("asset"),
|
|
41
|
+
"amount_atomic": amt,
|
|
42
|
+
"price": human,
|
|
43
|
+
"pay_to": a.get("payTo"),
|
|
44
|
+
})
|
|
45
|
+
return rails
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def discover_rails(url: str, timeout: float = 15.0) -> dict:
|
|
49
|
+
"""GET `url`; if it returns a 402 challenge, report the rails it accepts. NO payment is made."""
|
|
50
|
+
try:
|
|
51
|
+
r = httpx.get(url, timeout=timeout, follow_redirects=True)
|
|
52
|
+
except Exception as e: # noqa: BLE001 — surface the failure as data, don't raise into the agent
|
|
53
|
+
return {"url": url, "error": f"fetch failed: {e}", "rails": []}
|
|
54
|
+
if r.status_code != 402:
|
|
55
|
+
return {
|
|
56
|
+
"url": url, "paid": False, "status_code": r.status_code,
|
|
57
|
+
"note": "no 402 payment challenge (endpoint is free, or not an x402 resource)",
|
|
58
|
+
"rails": [],
|
|
59
|
+
}
|
|
60
|
+
try:
|
|
61
|
+
body = r.json()
|
|
62
|
+
except Exception: # noqa: BLE001
|
|
63
|
+
return {"url": url, "paid": True, "error": "402 body was not JSON", "rails": []}
|
|
64
|
+
rails = parse_accepts(body)
|
|
65
|
+
return {
|
|
66
|
+
"url": url, "paid": True, "x402_version": body.get("x402Version"),
|
|
67
|
+
"rail_count": len(rails), "rails": rails,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ── known-rails capability registry ──────────────────────────────────────────
|
|
72
|
+
# The neutral router is legible about the WHOLE landscape — including rails it does
|
|
73
|
+
# NOT settle itself. `route_mode` is the honesty knob:
|
|
74
|
+
# native-settle = Interline settles this directly (x402) or via an inbound adapter
|
|
75
|
+
# that lands on x402 (AP2). We move the funds.
|
|
76
|
+
# handoff = Interline recognizes the protocol + routes an agent TO it, but does
|
|
77
|
+
# NOT settle it — the protocol settles in its own world (Virtuals' own
|
|
78
|
+
# x402 escrow; OpenAI/Stripe ACP's card-only delegated payment).
|
|
79
|
+
KNOWN_RAILS = [
|
|
80
|
+
{
|
|
81
|
+
"name": "x402",
|
|
82
|
+
"kind": "settlement-rail",
|
|
83
|
+
"route_mode": "native-settle",
|
|
84
|
+
"networks": ["eip155 (EVM)", "solana (SVM)"],
|
|
85
|
+
"settle_asset": "USDC",
|
|
86
|
+
"what": "HTTP-402 micropayments. Interline settles natively across EVM + Solana behind one Paywall.",
|
|
87
|
+
"docs": "https://x402.org",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "mpp",
|
|
91
|
+
"kind": "settlement-rail",
|
|
92
|
+
"route_mode": "native-settle",
|
|
93
|
+
"networks": ["tempo (stablecoin)"],
|
|
94
|
+
"settle_asset": "stablecoin",
|
|
95
|
+
"what": "Machine Payments Protocol (Stripe + Tempo, IETF draft-ryan-httpauth-payment). HTTP-402 "
|
|
96
|
+
"challenge/credential/receipt — convergent with x402, RFC-7235 framed. Interline settles it as "
|
|
97
|
+
"a second PROTOCOL behind the same Paywall (the cross-protocol wedge: pay an endpoint via x402 OR "
|
|
98
|
+
"mpp through one integration). Phase-1 runs the mock facilitator; live Tempo settle (official "
|
|
99
|
+
"pympp SDK) is gated on a funded Tempo wallet — no Stripe account required.",
|
|
100
|
+
"docs": "https://mpp.dev",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "ap2",
|
|
104
|
+
"kind": "authorization-layer",
|
|
105
|
+
"route_mode": "native-settle",
|
|
106
|
+
"networks": ["eip155 (EVM)", "solana (SVM)"],
|
|
107
|
+
"settle_asset": "USDC",
|
|
108
|
+
"what": "Google Agent Payments Protocol — signed SD-JWT mandates. Interline's AP2 inbound adapter "
|
|
109
|
+
"verifies the mandate + constraints + freshness, then settles on x402. One seam for the "
|
|
110
|
+
"card/agent-commerce tier (UCP / Mastercard / Amex / PayPal all delegate to AP2).",
|
|
111
|
+
"docs": "https://github.com/google-agentic-commerce/AP2",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "virtuals-acp",
|
|
115
|
+
"kind": "commerce-ecosystem",
|
|
116
|
+
"route_mode": "handoff",
|
|
117
|
+
"networks": ["eip155:8453 (Base)"],
|
|
118
|
+
"settle_asset": "USDC",
|
|
119
|
+
"what": "Virtuals Protocol Agent Commerce Protocol — a crypto-native agent marketplace running its OWN "
|
|
120
|
+
"x402 rail + on-chain escrow on Base. Interline routes an agent to it (handoff); it settles in "
|
|
121
|
+
"its own ecosystem, not ours. Python SDK: virtuals-acp.",
|
|
122
|
+
"docs": "https://whitepaper.virtuals.io/about-virtuals/agent-commerce-protocol-acp",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "openai-stripe-acp",
|
|
126
|
+
"kind": "commerce-layer",
|
|
127
|
+
"route_mode": "handoff",
|
|
128
|
+
"networks": ["card / PSP networks"],
|
|
129
|
+
"settle_asset": "fiat (card)",
|
|
130
|
+
"what": "OpenAI/Stripe Agentic Commerce Protocol (ChatGPT Instant Checkout). Its delegated payment is "
|
|
131
|
+
"CARD-ONLY (payment_method_type=card, settled through PSP/card networks), so Interline routes an "
|
|
132
|
+
"agent to it (handoff) — our crypto rail can't be the settlement target.",
|
|
133
|
+
"docs": "https://github.com/agentic-commerce-protocol/agentic-commerce-protocol",
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def known_rails_catalog() -> dict:
|
|
139
|
+
"""The neutral router's full rail catalog — what Interline knows about + how it relates to each.
|
|
140
|
+
|
|
141
|
+
Separates rails Interline SETTLES natively (route_mode=native-settle: x402, AP2-via-adapter) from
|
|
142
|
+
protocols it ROUTES an agent to but does not settle (route_mode=handoff: Virtuals' own x402 escrow,
|
|
143
|
+
OpenAI/Stripe ACP's card-only delegated payment). Legibility over the whole landscape — reporting
|
|
144
|
+
every rail, including ones we don't move funds on — IS the neutral-router thesis."""
|
|
145
|
+
settles = [r["name"] for r in KNOWN_RAILS if r["route_mode"] == "native-settle"]
|
|
146
|
+
handoffs = [r["name"] for r in KNOWN_RAILS if r["route_mode"] == "handoff"]
|
|
147
|
+
return {
|
|
148
|
+
"rail_count": len(KNOWN_RAILS),
|
|
149
|
+
"native_settle": settles,
|
|
150
|
+
"handoff": handoffs,
|
|
151
|
+
"rails": KNOWN_RAILS,
|
|
152
|
+
}
|