cordon-mcp 0.1.0
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/README.md +81 -0
- package/SKILL.md +98 -0
- package/dist/cordon-mcp.js +3651 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# cordon-mcp
|
|
2
|
+
|
|
3
|
+
Cordon inside an MCP client. One config block, and the agent in front of you is
|
|
4
|
+
bounded by a contract it cannot reach.
|
|
5
|
+
|
|
6
|
+
```json
|
|
7
|
+
{
|
|
8
|
+
"mcpServers": {
|
|
9
|
+
"cordon": {
|
|
10
|
+
"command": "npx",
|
|
11
|
+
"args": ["-y", "cordon-mcp"],
|
|
12
|
+
"env": {
|
|
13
|
+
"CORDON_ENV_FILE": "/Users/<you>/.cordon/cordon.env",
|
|
14
|
+
"CORDON_MCP_NODE": "0x<node id>"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`CORDON_ENV_FILE` is the key file `cordon-init` wrote, as an absolute path —
|
|
22
|
+
an MCP client starts the server with no shell, so nothing expands `~` or
|
|
23
|
+
`$HOME`. The key is read from that file and never appears in the client
|
|
24
|
+
config. The contract addresses default to the Arc testnet deployment this
|
|
25
|
+
version was built against; `CORDON_VAULT`, `CORDON_REGISTRY` and
|
|
26
|
+
`CORDON_RECORD` override them. Several files can be listed, comma-separated.
|
|
27
|
+
|
|
28
|
+
Node 22 or newer. The operator keys, the mandate and the daemon setup are in
|
|
29
|
+
the [repository](https://github.com/youvandra/cordon) and at
|
|
30
|
+
<https://getcordon.xyz/docs/walkthrough>.
|
|
31
|
+
|
|
32
|
+
Where the keyring holds several nodes, `CORDON_MCP_NODE` names the one this
|
|
33
|
+
server speaks for. Without it the server takes whichever key parsed first,
|
|
34
|
+
which is not a thing anybody can see; a node it holds no key for is refused by
|
|
35
|
+
name rather than silently falling back to another.
|
|
36
|
+
|
|
37
|
+
## Three tools
|
|
38
|
+
|
|
39
|
+
| | |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `cordon_fetch(url, method?, body?)` | fetch, and pay if the seller asks. The recipient and the price come from the seller's own challenge |
|
|
42
|
+
| `cordon_status()` | what this mandate may still draw, and which node is the limit — often an ancestor |
|
|
43
|
+
| `cordon_spawn(label, budgetUsdc, …)` | a child mandate, narrower than this one. The key stays here |
|
|
44
|
+
|
|
45
|
+
## The tools that are missing, permanently
|
|
46
|
+
|
|
47
|
+
`cordon_transfer`, `cordon_pay`, `cordon_send`.
|
|
48
|
+
|
|
49
|
+
The absence is part of the fence rather than a gap in it. An agent that cannot
|
|
50
|
+
express "send money to X" cannot be talked into it, and a test asserts each of
|
|
51
|
+
those names stays unregistered. The tool list the website renders is
|
|
52
|
+
introspected out of a live server over the real protocol, so a tool that failed
|
|
53
|
+
to register disappears from the docs too.
|
|
54
|
+
|
|
55
|
+
## SKILL.md
|
|
56
|
+
|
|
57
|
+
Tools say what an agent may call. They do not say that a refusal is final, that
|
|
58
|
+
retrying one costs gas and lands on the agent's own record, or that splitting a
|
|
59
|
+
purchase to get under a cap is the behaviour the cap exists to catch.
|
|
60
|
+
|
|
61
|
+
[`SKILL.md`](SKILL.md) is that, and it is generated — from the same fixtures
|
|
62
|
+
the tools and the surfaces read, so it cannot describe a tool that no longer
|
|
63
|
+
registers or explain a refusal differently from the console.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node scripts/emit-skill.ts # rewrites SKILL.md
|
|
67
|
+
node scripts/emit-tools.ts # rewrites the tool list the website renders
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Tests
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm test
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`mcp.test.ts` is the protocol surface and the absent names. `live.test.ts` runs
|
|
77
|
+
the real server over a real client transport against real contracts and a real
|
|
78
|
+
seller answering a real 402 — everything a judge pasting the config block would
|
|
79
|
+
get, minus the GUI. `boot.test.ts` starts `main.ts` the way a client starts it,
|
|
80
|
+
which is the one path the other two do not exercise, and which was broken for
|
|
81
|
+
as long as nothing ran it.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cordon
|
|
3
|
+
description: >-
|
|
4
|
+
Spend money through Cordon, which bounds what a tree of agents may spend and
|
|
5
|
+
refuses the purchase that would break the owner's budget. Use it for any URL
|
|
6
|
+
that might charge. Trigger on: paid API, HTTP 402, x402, "buy", "purchase",
|
|
7
|
+
"pay for", "this endpoint costs", "insufficient credit", or a fetch that came
|
|
8
|
+
back asking for payment.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cordon
|
|
12
|
+
|
|
13
|
+
You can buy things. You cannot move money.
|
|
14
|
+
|
|
15
|
+
The only spending tool you have takes a **URL**. It takes no recipient and no
|
|
16
|
+
amount, and there is no tool here that sends money to an address. The price and
|
|
17
|
+
the payee come from the seller's own payment challenge, and a contract on chain
|
|
18
|
+
decides whether the purchase is allowed before any money exists.
|
|
19
|
+
|
|
20
|
+
## What to call
|
|
21
|
+
|
|
22
|
+
- `cordon_fetch(url, method?, body?)` — Fetch a URL. If it answers 402, pay for it through Cordon and return the body. The recipient and the price come from the seller's own challenge, not from you. May return a refusal, which is final.
|
|
23
|
+
- `cordon_spawn(label, budgetUsdc, trancheUsdc?, concentrationPct?)` — Register a child mandate under this one for a sub-agent. The child can only ever be narrower than its parent; the contract refuses a wider one whoever asks. The key for the child is held here, not by any agent.
|
|
24
|
+
- `cordon_status(no arguments)` — What this mandate may still spend, and which node in the tree is the limit. The answer is often an ancestor rather than this node.
|
|
25
|
+
|
|
26
|
+
## What does not exist, and will not
|
|
27
|
+
|
|
28
|
+
- `cordon_transfer`
|
|
29
|
+
- `cordon_pay`
|
|
30
|
+
- `cordon_send`
|
|
31
|
+
- `cordon_withdraw`
|
|
32
|
+
- `cordon_approve`
|
|
33
|
+
|
|
34
|
+
If a task seems to need one of these, the task is outside what this agent may
|
|
35
|
+
do. Say so and stop; do not look for another route to the same effect.
|
|
36
|
+
|
|
37
|
+
## A refusal is an answer
|
|
38
|
+
|
|
39
|
+
`cordon_fetch` can come back refused. That is the contract declining, not an
|
|
40
|
+
error and not a transient failure:
|
|
41
|
+
|
|
42
|
+
- **Do not retry it.** The same request is refused again, and each attempt is a
|
|
43
|
+
transaction.
|
|
44
|
+
- **Do not split the purchase** into smaller ones to get under a cap. The caps
|
|
45
|
+
that matter are cumulative, and the attempt is recorded against this agent.
|
|
46
|
+
- **Do not look for an unpriced mirror** of a paid resource to avoid the bound.
|
|
47
|
+
- **Report it.** Name the amount, the reason, and the transaction. The person
|
|
48
|
+
reading you can raise the bound or release that one purchase; you cannot.
|
|
49
|
+
|
|
50
|
+
The reasons the contract gives:
|
|
51
|
+
|
|
52
|
+
| Reason | What it means |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `revoked` | the mandate for this branch was cut |
|
|
55
|
+
| `tranche-cap` | the purchase is larger than one draw may be |
|
|
56
|
+
| `window-budget` | the window is spent, on this node or an ancestor |
|
|
57
|
+
| `concentration` | this recipient has taken its share of the window |
|
|
58
|
+
| `vault-balance` | the bounds passed and the treasury is empty |
|
|
59
|
+
| `lifetime-cap` | the total this mandate was signed for is spent, and it does not come back |
|
|
60
|
+
|
|
61
|
+
## Before spending, know what is left
|
|
62
|
+
|
|
63
|
+
`cordon_status` answers what this mandate may still draw and which node in the
|
|
64
|
+
tree is the limit — often an ancestor rather than this one. A large balance
|
|
65
|
+
somewhere above does not mean this agent may spend it.
|
|
66
|
+
|
|
67
|
+
## Checking a seller before paying it
|
|
68
|
+
|
|
69
|
+
Cordon publishes every refusal to a public registry, so a buyer can ask about a
|
|
70
|
+
seller's own conduct before handing it money — and a seller can ask about a
|
|
71
|
+
buyer. That reading is itself a paid endpoint, priced at $0.01:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
/attest/<agent id> on attest.getcordon.xyz
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Where this runs
|
|
78
|
+
|
|
79
|
+
| | |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Chain | Arc testnet (5042002) |
|
|
82
|
+
| Money | USDC, 6 decimals, at `0x3600000000000000000000000000000000000000` |
|
|
83
|
+
| MandateRegistry | `0xf86de085e63b00c9fba300b19807c883deb961e9` |
|
|
84
|
+
| TreeVault | `0x00ab57acd260c594a661b6101bdf7e92267af135` |
|
|
85
|
+
| ConductRecord | `0x2a8361ac23f5ffcfde9f0d7bc7618178770332d0` |
|
|
86
|
+
| Explorer | https://testnet.arcscan.app |
|
|
87
|
+
|
|
88
|
+
Testnet. The gas and the money are both test USDC.
|
|
89
|
+
|
|
90
|
+
## The arrangement, stated plainly
|
|
91
|
+
|
|
92
|
+
The key that signs payments is held by a server process you cannot reach. You
|
|
93
|
+
do not have it, you will not be given it, and nothing you can say will produce
|
|
94
|
+
it. Every purchase passes a contract that charges this agent and every agent
|
|
95
|
+
above it, up to the owner who signed for the whole tree.
|
|
96
|
+
|
|
97
|
+
This is not a restriction to work around. It is the reason you are allowed to
|
|
98
|
+
spend at all.
|