csoai-gspc-mcp 0.2.2 → 0.2.3-rc.2
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 +33 -12
- package/axis-aliases.json +101 -0
- package/gspc-tools.json +665 -4
- package/index.mjs +167 -8
- package/package.json +4 -3
- package/paid-tools.json +12 -0
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# csoai-gspc-mcp
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/csoai-gspc-mcp)
|
|
4
|
+
[](https://www.npmjs.com/package/csoai-gspc-mcp)
|
|
5
|
+
[](https://github.com/CSOAI-ORG/councilof-ai/blob/master/LICENSE)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
[](https://councilof.ai/gspc-scoreboard)
|
|
9
|
+
|
|
10
|
+
<!-- No board totals or tool counts are typed in this README: they go stale between releases. The live board is GET https://councilof.ai/api/gspc (totals.public_count); the live tool list is tools/list. tools-match-door.test.ts fails if a count is typed here again. -->
|
|
4
11
|
|
|
5
12
|
Stdio MCP server for the live GSPC board and the signed measurement cards at
|
|
6
13
|
[councilof.ai](https://councilof.ai). Zero dependencies. Node >= 20.
|
|
@@ -24,16 +31,19 @@ are reported as two labelled numbers and never reconciled.
|
|
|
24
31
|
| `get_card` | GET one card-v0 leaf by sha256. VALID / INVALID (not a leaf) / UNCHECKABLE (fetch failed). A 404 leaf is INVALID, not UNCHECKABLE. |
|
|
25
32
|
| `verify_inclusion` | GET `/api/proof?sha=`. VALID (included) / INVALID (not a leaf) / UNCHECKABLE (proof endpoint unreachable). |
|
|
26
33
|
| `x402_trust` | Latest x402 catalog trust snapshot: counts of correct challenges and phantom resources. A 402 is a challenge, not delivery. |
|
|
34
|
+
| `mcp_trust` | Latest MCP handshake census snapshot: counts only. `partial: true` whenever the enumeration did not complete — a cap-limited read is a slice, never the population. |
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
`wired-tools.test.mjs` fails if a listed
|
|
36
|
+
The free tools above and the metered ones below are exactly what `tools/list` returns — ask it for
|
|
37
|
+
the current set rather than trusting a number in a README. `wired-tools.test.mjs` fails if a listed
|
|
38
|
+
tool does not run or a running tool is not listed. Axis names are resolved case-insensitively through
|
|
39
|
+
one alias table (`axis-aliases.json`): `governance`, `gov` and `gspc-governance` are the same axis.
|
|
30
40
|
|
|
31
|
-
The same
|
|
41
|
+
The same free tools, from the same definitions file
|
|
32
42
|
(`functions/mcp/gspc-tools.json`), are served over HTTP at
|
|
33
43
|
`https://councilof.ai/mcp` (streamable HTTP, JSON-RPC 2.0 POST). Use whichever
|
|
34
44
|
transport your client speaks; the contracts are identical.
|
|
35
45
|
|
|
36
|
-
### The
|
|
46
|
+
### The x402-metered tools
|
|
37
47
|
|
|
38
48
|
| tool | route | free path |
|
|
39
49
|
|---|---|---|
|
|
@@ -51,10 +61,15 @@ Settlement is the route's job, fail-closed.
|
|
|
51
61
|
|
|
52
62
|
Top-level statuses describe delivery, not settlement:
|
|
53
63
|
|
|
54
|
-
- **`PAYMENT_REQUIRED`** — the route answered 402.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
- **`PAYMENT_REQUIRED`** — the route answered 402. Following the x402 MCP transport
|
|
65
|
+
(`specs/transports-v2/mcp.md`), the tool result has `isError: true`, `structuredContent` carries the
|
|
66
|
+
route's `PaymentRequired` object at the top level (`x402Version`, `resource`, `accepts[]`, `extensions`)
|
|
67
|
+
alongside this wrapper's fields (`status`, `settlement_state`, …), and `content[0].text` is that same
|
|
68
|
+
object as JSON; the human summary is `content[1].text`. With no `x_payment`, nothing was charged by that
|
|
69
|
+
request. If an authorization was presented, settlement remains `UNCONFIRMED`; inspect before signing or
|
|
70
|
+
retrying. `isError` marks "not delivered yet" — it is a payment challenge, not a fault. Payment is still
|
|
71
|
+
read from the `x_payment` argument; `_meta["x402/payment"]` is not read yet, so a client that sends only
|
|
72
|
+
that gets the same challenge back and is charged nothing.
|
|
58
73
|
- **`DELIVERED`** — the route answered 2xx and returned a deliverable. Inspect `delivery_kind`:
|
|
59
74
|
`PREVIEW_OR_FREE`, `DELIVERED_SETTLEMENT_UNCONFIRMED`, `DELIVERED_RECEIPT_GAP`, or
|
|
60
75
|
`DELIVERED_WITH_ROUTE_RECEIPT`. `receipt_state: PRESENT_UNVERIFIED` means a JWS-shaped receipt was
|
|
@@ -90,6 +105,9 @@ claude mcp add gspc -- npx -y csoai-gspc-mcp
|
|
|
90
105
|
|
|
91
106
|
From a checkout of the repo the server is `mcp/gspc-server/index.mjs` (no extra install).
|
|
92
107
|
|
|
108
|
+
MCP Registry name: `ai.councilof/gspc` (canonical, domain-verified). `io.github.CSOAI-ORG/gspc` is its
|
|
109
|
+
deprecated alias for the same door.
|
|
110
|
+
|
|
93
111
|
### Claude Desktop
|
|
94
112
|
|
|
95
113
|
Add to `claude_desktop_config.json` (macOS:
|
|
@@ -140,9 +158,10 @@ newline-delimited JSON-RPC 2.0 on its stdin/stdout (stderr is logs only):
|
|
|
140
158
|
3. send `{"jsonrpc":"2.0","id":2,"method":"tools/list"}`
|
|
141
159
|
4. call tools: `{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"board_totals","arguments":{}}}`
|
|
142
160
|
|
|
143
|
-
Every `tools/call` result carries
|
|
144
|
-
|
|
145
|
-
2025-03-26, 2025-06-18
|
|
161
|
+
Every `tools/call` result carries a human text summary and a machine
|
|
162
|
+
`structuredContent` object (a payment challenge puts the challenge JSON first, see
|
|
163
|
+
above). Protocol versions accepted: 2024-11-05, 2025-03-26, 2025-06-18,
|
|
164
|
+
2025-11-25; an unknown requested version is answered with 2025-11-25.
|
|
146
165
|
|
|
147
166
|
If you cannot spawn processes, POST the same JSON-RPC bodies to
|
|
148
167
|
`https://councilof.ai/mcp` instead.
|
|
@@ -167,6 +186,8 @@ card-attestation key) even though it is perfectly self-consistent.
|
|
|
167
186
|
|
|
168
187
|
## One source of truth
|
|
169
188
|
|
|
189
|
+
- Axis aliases: `functions/mcp/axis-aliases.json` — one table for this server, the
|
|
190
|
+
HTTP endpoint and the `csoai-gspc` Python client.
|
|
170
191
|
- Tool definitions: `functions/mcp/gspc-tools.json` — shared byte-for-byte with
|
|
171
192
|
the HTTP endpoint (`functions/mcp/[[path]].ts`). Neither surface defines
|
|
172
193
|
these tools anywhere else.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "csoai.axis-aliases/1",
|
|
3
|
+
"note": "THE ONE axis alias table. Keys are the canonical axis ids exactly as GET https://councilof.ai/api/gspc spells them (axes[].axis). Values are the other spellings a caller meets: the board row's own dataset code (csoai/gspc-<code> -> <code> and gspc-<code>) and the gspc-<axis> spelling the signed card index uses. Nothing else is an alias: an index name with no grounded link to a board row (arc-30, care-refusal-help, jail-escape-detection, swarm-candidates, ...) stays its own name. Resolution is case-insensitive: trim, lowercase, canonical match first, then alias. Byte-identical copies ship in mcp/gspc-server/axis-aliases.json (npm) and scripts/spray/pypi/csoai-gspc/csoai_gspc/axis_aliases.json (PyPI); functions/mcp/axis-aliases.test.ts fails if a copy drifts or a key stops being a board row.",
|
|
4
|
+
"axes": {
|
|
5
|
+
"governance": [
|
|
6
|
+
"gov",
|
|
7
|
+
"gspc-gov",
|
|
8
|
+
"gspc-governance"
|
|
9
|
+
],
|
|
10
|
+
"safety": [
|
|
11
|
+
"agi",
|
|
12
|
+
"gspc-agi",
|
|
13
|
+
"gspc-safety"
|
|
14
|
+
],
|
|
15
|
+
"provenance": [
|
|
16
|
+
"prv",
|
|
17
|
+
"gspc-prv",
|
|
18
|
+
"gspc-provenance"
|
|
19
|
+
],
|
|
20
|
+
"continuity": [
|
|
21
|
+
"asi",
|
|
22
|
+
"gspc-asi",
|
|
23
|
+
"gspc-continuity"
|
|
24
|
+
],
|
|
25
|
+
"conformance": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"gspc-mcp",
|
|
28
|
+
"gspc-conformance"
|
|
29
|
+
],
|
|
30
|
+
"openness": [
|
|
31
|
+
"oss",
|
|
32
|
+
"gspc-oss",
|
|
33
|
+
"gspc-openness"
|
|
34
|
+
],
|
|
35
|
+
"machinery-conformity": [
|
|
36
|
+
"mach",
|
|
37
|
+
"gspc-mach",
|
|
38
|
+
"gspc-machinery-conformity"
|
|
39
|
+
],
|
|
40
|
+
"care": [
|
|
41
|
+
"gspc-care"
|
|
42
|
+
],
|
|
43
|
+
"cross-reality": [
|
|
44
|
+
"xr",
|
|
45
|
+
"gspc-xr",
|
|
46
|
+
"gspc-cross-reality"
|
|
47
|
+
],
|
|
48
|
+
"detector-interop": [
|
|
49
|
+
"det",
|
|
50
|
+
"gspc-det",
|
|
51
|
+
"gspc-detector-interop"
|
|
52
|
+
],
|
|
53
|
+
"art5-safeguard": [
|
|
54
|
+
"art5",
|
|
55
|
+
"gspc-art5",
|
|
56
|
+
"gspc-art5-safeguard"
|
|
57
|
+
],
|
|
58
|
+
"swarm": [
|
|
59
|
+
"gspc-swarm"
|
|
60
|
+
],
|
|
61
|
+
"affect": [
|
|
62
|
+
"gspc-affect"
|
|
63
|
+
],
|
|
64
|
+
"jail": [
|
|
65
|
+
"jail-goldbank",
|
|
66
|
+
"gspc-jail-goldbank",
|
|
67
|
+
"gspc-jail"
|
|
68
|
+
],
|
|
69
|
+
"effect-binding": [
|
|
70
|
+
"gspc-effect-binding"
|
|
71
|
+
],
|
|
72
|
+
"provenance-controls": [
|
|
73
|
+
"gspc-provenance-controls"
|
|
74
|
+
],
|
|
75
|
+
"reserve-attestation": [
|
|
76
|
+
"gspc-reserve-attestation"
|
|
77
|
+
],
|
|
78
|
+
"regulatory-framework": [
|
|
79
|
+
"gspc-regulatory-framework"
|
|
80
|
+
],
|
|
81
|
+
"distribution-integrity": [
|
|
82
|
+
"gspc-distribution-integrity"
|
|
83
|
+
],
|
|
84
|
+
"custody-disclosure": [
|
|
85
|
+
"gspc-custody-disclosure"
|
|
86
|
+
],
|
|
87
|
+
"ai-adoption-components": [
|
|
88
|
+
"ai-economy-index",
|
|
89
|
+
"gspc-ai-economy-index",
|
|
90
|
+
"gspc-ai-adoption-components"
|
|
91
|
+
],
|
|
92
|
+
"labour-components": [
|
|
93
|
+
"human-labour-index",
|
|
94
|
+
"gspc-human-labour-index",
|
|
95
|
+
"gspc-labour-components"
|
|
96
|
+
],
|
|
97
|
+
"humanoid-labour-index": [
|
|
98
|
+
"gspc-humanoid-labour-index"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
package/gspc-tools.json
CHANGED
|
@@ -4,16 +4,66 @@
|
|
|
4
4
|
"tools": [
|
|
5
5
|
{
|
|
6
6
|
"name": "board_totals",
|
|
7
|
+
"title": "Live GSPC board totals",
|
|
7
8
|
"description": "Live GSPC board totals from https://councilof.ai/api/gspc. Returns the slot count and the measured count as two labelled numbers WITH their kind \u2014 a slot is a declared position on the board, a measurement is a real run behind it; the two are never summed and never swapped \u2014 plus as_of dates for the board and for this fetch. We measure, never certify. If the board cannot be fetched the answer is a distinct UNREACHABLE state: no cached number is ever presented as live.",
|
|
8
9
|
"inputSchema": {
|
|
9
10
|
"type": "object",
|
|
10
11
|
"properties": {},
|
|
11
12
|
"additionalProperties": false
|
|
13
|
+
},
|
|
14
|
+
"outputSchema": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"state": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "LIVE or UNREACHABLE"
|
|
20
|
+
},
|
|
21
|
+
"counts": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"name": {
|
|
27
|
+
"type": "string"
|
|
28
|
+
},
|
|
29
|
+
"kind": {
|
|
30
|
+
"type": "string"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"public_count": {
|
|
36
|
+
"type": [
|
|
37
|
+
"string",
|
|
38
|
+
"null"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"count_grammar": {},
|
|
42
|
+
"by_family": {},
|
|
43
|
+
"as_of": {},
|
|
44
|
+
"source": {
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"not_a_certification": {
|
|
48
|
+
"type": "boolean"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": [
|
|
52
|
+
"state"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"annotations": {
|
|
56
|
+
"title": "Live GSPC board totals",
|
|
57
|
+
"readOnlyHint": true,
|
|
58
|
+
"destructiveHint": false,
|
|
59
|
+
"idempotentHint": true,
|
|
60
|
+
"openWorldHint": true
|
|
12
61
|
}
|
|
13
62
|
},
|
|
14
63
|
{
|
|
15
64
|
"name": "get_axis",
|
|
16
|
-
"
|
|
65
|
+
"title": "One GSPC board axis",
|
|
66
|
+
"description": "One axis row from the live GSPC board at https://councilof.ai/api/gspc \u2014 every axis the board carries, behavioural and financial families alike, addressed by the axis id exactly as the board spells it: n, accuracy, interval, MEASURED or UNMEASURED status, family, kind, the bank or run-artifact URL behind the row, and dates. An unmeasured axis is a first-class answer \u2014 a declared slot with no run behind it, published so the gap is visible \u2014 never an error and never a zero. Never a certification.",
|
|
17
67
|
"inputSchema": {
|
|
18
68
|
"type": "object",
|
|
19
69
|
"properties": {
|
|
@@ -26,10 +76,64 @@
|
|
|
26
76
|
"axis"
|
|
27
77
|
],
|
|
28
78
|
"additionalProperties": false
|
|
79
|
+
},
|
|
80
|
+
"outputSchema": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"properties": {
|
|
83
|
+
"state": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "LIVE, NOT_ON_BOARD, BAD_INPUT or UNREACHABLE"
|
|
86
|
+
},
|
|
87
|
+
"axis": {
|
|
88
|
+
"type": [
|
|
89
|
+
"string",
|
|
90
|
+
"null"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"resolved_from": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"description": "present when the caller named an alias (e.g. gov) that resolved to this board axis"
|
|
96
|
+
},
|
|
97
|
+
"status": {
|
|
98
|
+
"type": [
|
|
99
|
+
"string",
|
|
100
|
+
"null"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"measured": {
|
|
104
|
+
"type": "boolean"
|
|
105
|
+
},
|
|
106
|
+
"family": {},
|
|
107
|
+
"n": {},
|
|
108
|
+
"accuracy": {},
|
|
109
|
+
"interval": {},
|
|
110
|
+
"leader": {},
|
|
111
|
+
"dataset": {},
|
|
112
|
+
"board_carries": {
|
|
113
|
+
"type": "array"
|
|
114
|
+
},
|
|
115
|
+
"source": {
|
|
116
|
+
"type": "string"
|
|
117
|
+
},
|
|
118
|
+
"not_a_certification": {
|
|
119
|
+
"type": "boolean"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": [
|
|
123
|
+
"state"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"annotations": {
|
|
127
|
+
"title": "One GSPC board axis",
|
|
128
|
+
"readOnlyHint": true,
|
|
129
|
+
"destructiveHint": false,
|
|
130
|
+
"idempotentHint": true,
|
|
131
|
+
"openWorldHint": true
|
|
29
132
|
}
|
|
30
133
|
},
|
|
31
134
|
{
|
|
32
135
|
"name": "verify_card",
|
|
136
|
+
"title": "Verify a signed measurement card (free)",
|
|
33
137
|
"description": "Verify a signed gspc.measurement-card under the published rule (https://councilof.ai/signed/HOW-TO-VERIFY.md): recompute the id from the canonical body bytes, then check the Ed25519 signature under the PINNED key published at did:web:csoai.org#card-attestation-1. A card that carries its own key proves only that the file is self-consistent \u2014 anyone can alter a body and sign it with a key they just generated \u2014 so a signer other than the published key is reported INVALID. Three verdicts, never two: VALID, INVALID (with the reason), or UNCHECKABLE when the check could not be completed \u2014 'could not check' is a different claim from 'forged'. Accepts the card as a JSON object, a JSON string, or a councilof.ai / csoai.org URL. Never a certification.",
|
|
34
138
|
"inputSchema": {
|
|
35
139
|
"type": "object",
|
|
@@ -50,10 +154,97 @@
|
|
|
50
154
|
"card"
|
|
51
155
|
],
|
|
52
156
|
"additionalProperties": false
|
|
157
|
+
},
|
|
158
|
+
"outputSchema": {
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"state": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"description": "VALID, INVALID or UNCHECKABLE"
|
|
164
|
+
},
|
|
165
|
+
"id": {
|
|
166
|
+
"type": [
|
|
167
|
+
"string",
|
|
168
|
+
"null"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"family": {
|
|
172
|
+
"type": [
|
|
173
|
+
"string",
|
|
174
|
+
"null"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"reason": {
|
|
178
|
+
"type": [
|
|
179
|
+
"string",
|
|
180
|
+
"null"
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
"reasons": {
|
|
184
|
+
"type": "array",
|
|
185
|
+
"items": {
|
|
186
|
+
"type": "string"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"checks": {
|
|
190
|
+
"type": "array",
|
|
191
|
+
"items": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"properties": {
|
|
194
|
+
"check": {
|
|
195
|
+
"type": "string"
|
|
196
|
+
},
|
|
197
|
+
"ok": {
|
|
198
|
+
"type": [
|
|
199
|
+
"boolean",
|
|
200
|
+
"null"
|
|
201
|
+
],
|
|
202
|
+
"description": "true = passed, false = failed, null = not applicable or not run (detail says UNCHECKED)"
|
|
203
|
+
},
|
|
204
|
+
"code": {
|
|
205
|
+
"type": "string"
|
|
206
|
+
},
|
|
207
|
+
"detail": {
|
|
208
|
+
"type": "string"
|
|
209
|
+
},
|
|
210
|
+
"advisory": {
|
|
211
|
+
"type": "boolean",
|
|
212
|
+
"description": "true = reported but never decides the verdict (the live did.json cross-check)"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"pinned_key": {
|
|
218
|
+
"type": [
|
|
219
|
+
"string",
|
|
220
|
+
"null"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"rule": {
|
|
224
|
+
"type": "string"
|
|
225
|
+
},
|
|
226
|
+
"note": {
|
|
227
|
+
"type": "string"
|
|
228
|
+
},
|
|
229
|
+
"not_a_certification": {
|
|
230
|
+
"type": "boolean"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"required": [
|
|
234
|
+
"state"
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
"annotations": {
|
|
238
|
+
"title": "Verify a signed measurement card (free)",
|
|
239
|
+
"readOnlyHint": true,
|
|
240
|
+
"destructiveHint": false,
|
|
241
|
+
"idempotentHint": true,
|
|
242
|
+
"openWorldHint": true
|
|
53
243
|
}
|
|
54
244
|
},
|
|
55
245
|
{
|
|
56
246
|
"name": "list_cards",
|
|
247
|
+
"title": "List signed card index rows",
|
|
57
248
|
"description": "The published signed-card index (https://councilof.ai/signed/card_index.json): what the index declares (n_cards) and how many rows it actually carries, reported next to \u2014 never reconciled with \u2014 the count the card store endpoint (https://councilof.ai/api/cards) reports for itself. Two labelled numbers from two surfaces; if they disagree, this tool shows the disagreement rather than picking one. Optional filters return recent rows: axis, limit.",
|
|
58
249
|
"inputSchema": {
|
|
59
250
|
"type": "object",
|
|
@@ -70,19 +261,127 @@
|
|
|
70
261
|
}
|
|
71
262
|
},
|
|
72
263
|
"additionalProperties": false
|
|
264
|
+
},
|
|
265
|
+
"outputSchema": {
|
|
266
|
+
"type": "object",
|
|
267
|
+
"properties": {
|
|
268
|
+
"index": {
|
|
269
|
+
"type": [
|
|
270
|
+
"object",
|
|
271
|
+
"null"
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
"card_store_count_endpoint": {
|
|
275
|
+
"type": [
|
|
276
|
+
"object",
|
|
277
|
+
"null"
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
"rows": {
|
|
281
|
+
"type": [
|
|
282
|
+
"array",
|
|
283
|
+
"null"
|
|
284
|
+
],
|
|
285
|
+
"items": {
|
|
286
|
+
"type": "object",
|
|
287
|
+
"properties": {
|
|
288
|
+
"card": {
|
|
289
|
+
"type": "string"
|
|
290
|
+
},
|
|
291
|
+
"axis": {
|
|
292
|
+
"type": "string"
|
|
293
|
+
},
|
|
294
|
+
"ts": {},
|
|
295
|
+
"signed": {}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"axis_query": {
|
|
300
|
+
"type": "object",
|
|
301
|
+
"properties": {
|
|
302
|
+
"asked": {
|
|
303
|
+
"type": "string"
|
|
304
|
+
},
|
|
305
|
+
"canonical": {
|
|
306
|
+
"type": "string"
|
|
307
|
+
},
|
|
308
|
+
"spellings": {
|
|
309
|
+
"type": "array",
|
|
310
|
+
"items": {
|
|
311
|
+
"type": "string"
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
"index_names_matched": {
|
|
315
|
+
"type": "array",
|
|
316
|
+
"items": {
|
|
317
|
+
"type": "string"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
"doctrine": {
|
|
323
|
+
"type": "string"
|
|
324
|
+
},
|
|
325
|
+
"not_a_certification": {
|
|
326
|
+
"type": "boolean"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
"annotations": {
|
|
331
|
+
"title": "List signed card index rows",
|
|
332
|
+
"readOnlyHint": true,
|
|
333
|
+
"destructiveHint": false,
|
|
334
|
+
"idempotentHint": true,
|
|
335
|
+
"openWorldHint": true
|
|
73
336
|
}
|
|
74
337
|
},
|
|
75
338
|
{
|
|
76
339
|
"name": "get_root",
|
|
340
|
+
"title": "Public Merkle root",
|
|
77
341
|
"description": "GET the permissionless public-root at https://councilof.ai/root.json. Returns merkle_root, card_count, as_of, and UNMEASURED notes. Separate from GSPC. Three states: VALID (fetched), UNREACHABLE (could not fetch), UNCHECKABLE (body unreadable). Never a certificate.",
|
|
78
342
|
"inputSchema": {
|
|
79
343
|
"type": "object",
|
|
80
344
|
"properties": {},
|
|
81
345
|
"additionalProperties": false
|
|
346
|
+
},
|
|
347
|
+
"outputSchema": {
|
|
348
|
+
"type": "object",
|
|
349
|
+
"properties": {
|
|
350
|
+
"state": {
|
|
351
|
+
"type": "string",
|
|
352
|
+
"description": "VALID, UNREACHABLE or UNCHECKABLE"
|
|
353
|
+
},
|
|
354
|
+
"merkle_root": {
|
|
355
|
+
"type": [
|
|
356
|
+
"string",
|
|
357
|
+
"null"
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
"card_count": {},
|
|
361
|
+
"as_of": {},
|
|
362
|
+
"kind": {},
|
|
363
|
+
"source": {
|
|
364
|
+
"type": "string"
|
|
365
|
+
},
|
|
366
|
+
"not_gspc": {
|
|
367
|
+
"type": "boolean"
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
"required": [
|
|
371
|
+
"state"
|
|
372
|
+
]
|
|
373
|
+
},
|
|
374
|
+
"annotations": {
|
|
375
|
+
"title": "Public Merkle root",
|
|
376
|
+
"readOnlyHint": true,
|
|
377
|
+
"destructiveHint": false,
|
|
378
|
+
"idempotentHint": true,
|
|
379
|
+
"openWorldHint": true
|
|
82
380
|
}
|
|
83
381
|
},
|
|
84
382
|
{
|
|
85
383
|
"name": "get_card",
|
|
384
|
+
"title": "One public-root card-v0 leaf",
|
|
86
385
|
"description": "GET one public-root card-v0 leaf by sha256 (64 hex) from https://councilof.ai/cards/{sha16}.json. UNMEASURED cells stay visible. Three states: VALID (fetched), INVALID (not a leaf of the live root), UNCHECKABLE (fetch failed). Not a GSPC measurement-card.",
|
|
87
386
|
"inputSchema": {
|
|
88
387
|
"type": "object",
|
|
@@ -96,10 +395,45 @@
|
|
|
96
395
|
"sha256"
|
|
97
396
|
],
|
|
98
397
|
"additionalProperties": false
|
|
398
|
+
},
|
|
399
|
+
"outputSchema": {
|
|
400
|
+
"type": "object",
|
|
401
|
+
"properties": {
|
|
402
|
+
"state": {
|
|
403
|
+
"type": "string",
|
|
404
|
+
"description": "VALID, INVALID or UNCHECKABLE"
|
|
405
|
+
},
|
|
406
|
+
"sha256": {
|
|
407
|
+
"type": "string"
|
|
408
|
+
},
|
|
409
|
+
"reason": {
|
|
410
|
+
"type": "string"
|
|
411
|
+
},
|
|
412
|
+
"source": {
|
|
413
|
+
"type": "string"
|
|
414
|
+
},
|
|
415
|
+
"surface": {},
|
|
416
|
+
"unmeasured": {},
|
|
417
|
+
"sig_ed25519": {},
|
|
418
|
+
"not_gspc": {
|
|
419
|
+
"type": "boolean"
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
"required": [
|
|
423
|
+
"state"
|
|
424
|
+
]
|
|
425
|
+
},
|
|
426
|
+
"annotations": {
|
|
427
|
+
"title": "One public-root card-v0 leaf",
|
|
428
|
+
"readOnlyHint": true,
|
|
429
|
+
"destructiveHint": false,
|
|
430
|
+
"idempotentHint": true,
|
|
431
|
+
"openWorldHint": true
|
|
99
432
|
}
|
|
100
433
|
},
|
|
101
434
|
{
|
|
102
435
|
"name": "verify_inclusion",
|
|
436
|
+
"title": "Merkle inclusion check",
|
|
103
437
|
"description": "Check a sha256 against the live public-root merkle via GET /api/proof?sha=. Three states only: VALID (included), INVALID (not a leaf), UNCHECKABLE (proof endpoint unreachable). Does not claim Ed25519 unless sig_ed25519 is present and checked separately. Never a grade.",
|
|
104
438
|
"inputSchema": {
|
|
105
439
|
"type": "object",
|
|
@@ -113,11 +447,41 @@
|
|
|
113
447
|
"sha256"
|
|
114
448
|
],
|
|
115
449
|
"additionalProperties": false
|
|
450
|
+
},
|
|
451
|
+
"outputSchema": {
|
|
452
|
+
"type": "object",
|
|
453
|
+
"properties": {
|
|
454
|
+
"state": {
|
|
455
|
+
"type": "string",
|
|
456
|
+
"description": "VALID, INVALID or UNCHECKABLE"
|
|
457
|
+
},
|
|
458
|
+
"sha256": {
|
|
459
|
+
"type": "string"
|
|
460
|
+
},
|
|
461
|
+
"merkle_root": {
|
|
462
|
+
"type": [
|
|
463
|
+
"string",
|
|
464
|
+
"null"
|
|
465
|
+
]
|
|
466
|
+
},
|
|
467
|
+
"reason": {}
|
|
468
|
+
},
|
|
469
|
+
"required": [
|
|
470
|
+
"state"
|
|
471
|
+
]
|
|
472
|
+
},
|
|
473
|
+
"annotations": {
|
|
474
|
+
"title": "Merkle inclusion check",
|
|
475
|
+
"readOnlyHint": true,
|
|
476
|
+
"destructiveHint": false,
|
|
477
|
+
"idempotentHint": true,
|
|
478
|
+
"openWorldHint": true
|
|
116
479
|
}
|
|
117
480
|
},
|
|
118
481
|
{
|
|
119
482
|
"name": "x402_trust",
|
|
120
|
-
"
|
|
483
|
+
"title": "x402 catalog trust snapshot",
|
|
484
|
+
"description": "GET the latest x402 catalog trust snapshot: counts of how many catalogued x402 resources open a correct 402 challenge vs how many are phantom on the wire. Counts only by doctrine \u2014 host details withheld; a 402 is NOT delivery; measurement, never certification.",
|
|
121
485
|
"inputSchema": {
|
|
122
486
|
"type": "object",
|
|
123
487
|
"properties": {},
|
|
@@ -126,16 +490,313 @@
|
|
|
126
490
|
"outputSchema": {
|
|
127
491
|
"type": "object",
|
|
128
492
|
"properties": {
|
|
493
|
+
"state": {
|
|
494
|
+
"type": "string",
|
|
495
|
+
"description": "VALID or UNREACHABLE"
|
|
496
|
+
},
|
|
129
497
|
"counts": {
|
|
130
|
-
"type":
|
|
498
|
+
"type": [
|
|
499
|
+
"object",
|
|
500
|
+
"null"
|
|
501
|
+
]
|
|
131
502
|
},
|
|
132
503
|
"headline": {
|
|
504
|
+
"type": [
|
|
505
|
+
"string",
|
|
506
|
+
"null"
|
|
507
|
+
]
|
|
508
|
+
},
|
|
509
|
+
"as_of": {
|
|
510
|
+
"type": [
|
|
511
|
+
"string",
|
|
512
|
+
"null"
|
|
513
|
+
]
|
|
514
|
+
},
|
|
515
|
+
"kind": {},
|
|
516
|
+
"source": {
|
|
133
517
|
"type": "string"
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
"required": [
|
|
521
|
+
"state"
|
|
522
|
+
]
|
|
523
|
+
},
|
|
524
|
+
"annotations": {
|
|
525
|
+
"title": "x402 catalog trust snapshot",
|
|
526
|
+
"readOnlyHint": true,
|
|
527
|
+
"destructiveHint": false,
|
|
528
|
+
"idempotentHint": true,
|
|
529
|
+
"openWorldHint": true
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
"name": "mcp_trust",
|
|
534
|
+
"title": "MCP handshake census snapshot",
|
|
535
|
+
"description": "GET the latest MCP handshake trust snapshot (https://councilof.ai/interop/mcp-trust/latest.json): counts of how many internet-facing MCP servers answer a correct initialize handshake, how many respond with an auth challenge, and how many are unreachable. Counts only by doctrine \u2014 host details withheld by design. A partial round or a cap change is disclosed in the snapshot. Measurement, never certification. Three states: VALID (fetched), UNREACHABLE (could not fetch), UNCHECKABLE (body unreadable).",
|
|
536
|
+
"inputSchema": {
|
|
537
|
+
"type": "object",
|
|
538
|
+
"properties": {},
|
|
539
|
+
"additionalProperties": false
|
|
540
|
+
},
|
|
541
|
+
"outputSchema": {
|
|
542
|
+
"type": "object",
|
|
543
|
+
"properties": {
|
|
544
|
+
"state": {
|
|
545
|
+
"type": "string",
|
|
546
|
+
"description": "VALID or UNREACHABLE"
|
|
547
|
+
},
|
|
548
|
+
"counts": {
|
|
549
|
+
"type": [
|
|
550
|
+
"object",
|
|
551
|
+
"null"
|
|
552
|
+
]
|
|
134
553
|
},
|
|
135
554
|
"as_of": {
|
|
555
|
+
"type": [
|
|
556
|
+
"string",
|
|
557
|
+
"null"
|
|
558
|
+
]
|
|
559
|
+
},
|
|
560
|
+
"partial": {
|
|
561
|
+
"type": "boolean",
|
|
562
|
+
"description": "true whenever the enumeration did not complete \u2014 a cap-limited read is partial"
|
|
563
|
+
},
|
|
564
|
+
"partial_reason": {
|
|
565
|
+
"type": [
|
|
566
|
+
"string",
|
|
567
|
+
"null"
|
|
568
|
+
]
|
|
569
|
+
},
|
|
570
|
+
"enumeration": {
|
|
571
|
+
"type": [
|
|
572
|
+
"object",
|
|
573
|
+
"null"
|
|
574
|
+
]
|
|
575
|
+
},
|
|
576
|
+
"diff": {},
|
|
577
|
+
"kind": {},
|
|
578
|
+
"source": {
|
|
136
579
|
"type": "string"
|
|
580
|
+
},
|
|
581
|
+
"headline": {
|
|
582
|
+
"type": [
|
|
583
|
+
"string",
|
|
584
|
+
"null"
|
|
585
|
+
]
|
|
137
586
|
}
|
|
138
|
-
}
|
|
587
|
+
},
|
|
588
|
+
"required": [
|
|
589
|
+
"state"
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
"annotations": {
|
|
593
|
+
"title": "MCP handshake census snapshot",
|
|
594
|
+
"readOnlyHint": true,
|
|
595
|
+
"destructiveHint": false,
|
|
596
|
+
"idempotentHint": true,
|
|
597
|
+
"openWorldHint": true
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"name": "measurement_index",
|
|
602
|
+
"title": "Measurement-capsule index",
|
|
603
|
+
"description": "Read the latest signed measurement-capsule index published at https://councilof.ai/measurement-capsules/latest.json: the index root over every capsule, each batch (adapter, kind, capsule count, measurement states, batch Merkle root, record sha256, record signature and OpenTimestamps state), the index's own board signature re-verified here against the pinned did:web:csoai.org#board-attestation-1 key, and the anchor states published beside it (OpenTimestamps, Rekor, XRPL) \u2014 PENDING is never called attested. States only: measurement, not endorsement; no verdict, score or ranking. NOT_PUBLISHED when no index is served; UNREACHABLE when the source could not be fetched.",
|
|
604
|
+
"annotations": {
|
|
605
|
+
"title": "Measurement-capsule index",
|
|
606
|
+
"readOnlyHint": true,
|
|
607
|
+
"destructiveHint": false,
|
|
608
|
+
"idempotentHint": true,
|
|
609
|
+
"openWorldHint": true
|
|
610
|
+
},
|
|
611
|
+
"inputSchema": {
|
|
612
|
+
"type": "object",
|
|
613
|
+
"properties": {},
|
|
614
|
+
"additionalProperties": false
|
|
615
|
+
},
|
|
616
|
+
"outputSchema": {
|
|
617
|
+
"type": "object",
|
|
618
|
+
"properties": {
|
|
619
|
+
"state": {
|
|
620
|
+
"type": "string",
|
|
621
|
+
"enum": [
|
|
622
|
+
"PUBLISHED",
|
|
623
|
+
"NOT_PUBLISHED",
|
|
624
|
+
"UNREACHABLE",
|
|
625
|
+
"UNCHECKABLE"
|
|
626
|
+
]
|
|
627
|
+
},
|
|
628
|
+
"doctrine": {
|
|
629
|
+
"type": "string",
|
|
630
|
+
"const": "measurement, not endorsement"
|
|
631
|
+
},
|
|
632
|
+
"version": {
|
|
633
|
+
"type": "string"
|
|
634
|
+
},
|
|
635
|
+
"index_root": {
|
|
636
|
+
"type": [
|
|
637
|
+
"string",
|
|
638
|
+
"null"
|
|
639
|
+
]
|
|
640
|
+
},
|
|
641
|
+
"n_capsules_total": {
|
|
642
|
+
"type": [
|
|
643
|
+
"integer",
|
|
644
|
+
"null"
|
|
645
|
+
]
|
|
646
|
+
},
|
|
647
|
+
"n_batches": {
|
|
648
|
+
"type": "integer"
|
|
649
|
+
},
|
|
650
|
+
"batches": {
|
|
651
|
+
"type": "array",
|
|
652
|
+
"items": {
|
|
653
|
+
"type": "object"
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
"signature": {
|
|
657
|
+
"type": "object"
|
|
658
|
+
},
|
|
659
|
+
"anchors": {
|
|
660
|
+
"type": "object"
|
|
661
|
+
},
|
|
662
|
+
"reason": {
|
|
663
|
+
"type": "string"
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
"required": [
|
|
667
|
+
"state"
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
"name": "verify_capsule",
|
|
673
|
+
"title": "Verify one measurement capsule",
|
|
674
|
+
"description": "Verify one measurement capsule. Pass capsule_json as the capsule's JSON TEXT (exact: number lexemes are kept) or as an object. Recomputes capsule_id (sha256 of the canonical JSON without capsule_id), picks the rule by the capsule's schema (csoai.measurement-capsule/0.2: RFC 6962 Merkle with 0x00/0x01 domain separation; the superseded v0.1 capsule schema: the v0.1 rule), finds the published batch of the same kind, recomputes that batch's root from its published leaves, and returns the audit path of this capsule against the root named by the signed index. States: INCLUDED, NOT_INCLUDED, ID_MISMATCH, UNCHECKABLE, NOT_PUBLISHED. Verifying is free forever. It proves the bytes were published and bound; what they measured is the capsule's own measurement_state and limitations \u2014 measurement, not endorsement.",
|
|
675
|
+
"annotations": {
|
|
676
|
+
"title": "Verify one measurement capsule",
|
|
677
|
+
"readOnlyHint": true,
|
|
678
|
+
"destructiveHint": false,
|
|
679
|
+
"idempotentHint": true,
|
|
680
|
+
"openWorldHint": true
|
|
681
|
+
},
|
|
682
|
+
"inputSchema": {
|
|
683
|
+
"type": "object",
|
|
684
|
+
"properties": {
|
|
685
|
+
"capsule_json": {
|
|
686
|
+
"description": "The capsule: its JSON text (preferred, byte-exact) or the parsed object.",
|
|
687
|
+
"type": [
|
|
688
|
+
"string",
|
|
689
|
+
"object"
|
|
690
|
+
]
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
"required": [
|
|
694
|
+
"capsule_json"
|
|
695
|
+
],
|
|
696
|
+
"additionalProperties": false
|
|
697
|
+
},
|
|
698
|
+
"outputSchema": {
|
|
699
|
+
"type": "object",
|
|
700
|
+
"properties": {
|
|
701
|
+
"state": {
|
|
702
|
+
"type": "string",
|
|
703
|
+
"enum": [
|
|
704
|
+
"INCLUDED",
|
|
705
|
+
"NOT_INCLUDED",
|
|
706
|
+
"ID_MISMATCH",
|
|
707
|
+
"UNCHECKABLE",
|
|
708
|
+
"NOT_PUBLISHED",
|
|
709
|
+
"UNREACHABLE"
|
|
710
|
+
]
|
|
711
|
+
},
|
|
712
|
+
"doctrine": {
|
|
713
|
+
"type": "string",
|
|
714
|
+
"const": "measurement, not endorsement"
|
|
715
|
+
},
|
|
716
|
+
"capsule_id": {
|
|
717
|
+
"type": "object"
|
|
718
|
+
},
|
|
719
|
+
"batch": {
|
|
720
|
+
"type": "object"
|
|
721
|
+
},
|
|
722
|
+
"inclusion": {
|
|
723
|
+
"type": "object"
|
|
724
|
+
},
|
|
725
|
+
"index_signature": {
|
|
726
|
+
"type": "object"
|
|
727
|
+
},
|
|
728
|
+
"reason": {
|
|
729
|
+
"type": "string"
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
"required": [
|
|
733
|
+
"state"
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
"name": "server_evidence",
|
|
739
|
+
"title": "Every published capsule about one endpoint",
|
|
740
|
+
"description": "Trust per server, not totals: every published measurement capsule about ONE endpoint URL across all batches \u2014 MCP contract-parity dimensions (AUTH, PAYMENT, PROTOCOL, TOOLS, VERSION), A2A card-signature state, self-parity cells for CSOAI's own doors, and any later adapter (e.g. tool drift) \u2014 each with its measurement_state, observed_at, correction_pointer, limitations, batch Merkle root and an inclusion pointer (verify_capsule re-derives inclusion). Read from a static per-endpoint shard keyed by sha256 of the normalised URL. An endpoint with no capsule answers NOT_MEASURED with an empty list \u2014 never an error and never a clean bill. No verdict, score or ranking: measurement, not endorsement.",
|
|
741
|
+
"annotations": {
|
|
742
|
+
"title": "Every published capsule about one endpoint",
|
|
743
|
+
"readOnlyHint": true,
|
|
744
|
+
"destructiveHint": false,
|
|
745
|
+
"idempotentHint": true,
|
|
746
|
+
"openWorldHint": true
|
|
747
|
+
},
|
|
748
|
+
"inputSchema": {
|
|
749
|
+
"type": "object",
|
|
750
|
+
"properties": {
|
|
751
|
+
"endpoint_url": {
|
|
752
|
+
"type": "string",
|
|
753
|
+
"description": "The endpoint URL, e.g. https://example.com/mcp or an agent-card URL."
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
"required": [
|
|
757
|
+
"endpoint_url"
|
|
758
|
+
],
|
|
759
|
+
"additionalProperties": false
|
|
760
|
+
},
|
|
761
|
+
"outputSchema": {
|
|
762
|
+
"type": "object",
|
|
763
|
+
"properties": {
|
|
764
|
+
"state": {
|
|
765
|
+
"type": "string",
|
|
766
|
+
"enum": [
|
|
767
|
+
"MEASURED",
|
|
768
|
+
"NOT_MEASURED",
|
|
769
|
+
"NOT_PUBLISHED",
|
|
770
|
+
"UNREACHABLE",
|
|
771
|
+
"UNCHECKABLE"
|
|
772
|
+
]
|
|
773
|
+
},
|
|
774
|
+
"doctrine": {
|
|
775
|
+
"type": "string",
|
|
776
|
+
"const": "measurement, not endorsement"
|
|
777
|
+
},
|
|
778
|
+
"endpoint": {
|
|
779
|
+
"type": [
|
|
780
|
+
"string",
|
|
781
|
+
"null"
|
|
782
|
+
]
|
|
783
|
+
},
|
|
784
|
+
"n_capsules": {
|
|
785
|
+
"type": "integer"
|
|
786
|
+
},
|
|
787
|
+
"capsules": {
|
|
788
|
+
"type": "array",
|
|
789
|
+
"items": {
|
|
790
|
+
"type": "object"
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
"reason": {
|
|
794
|
+
"type": "string"
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
"required": [
|
|
798
|
+
"state"
|
|
799
|
+
]
|
|
139
800
|
}
|
|
140
801
|
}
|
|
141
802
|
]
|
package/index.mjs
CHANGED
|
@@ -79,6 +79,26 @@ const PAID_TOOLS = PAID_TOOLS_PATH ? JSON.parse(readFileSync(PAID_TOOLS_PATH, "u
|
|
|
79
79
|
const PAID_BY_NAME = new Map(PAID_TOOLS.map((t) => [t.name, t]));
|
|
80
80
|
const TOOLS = [...FREE_TOOLS, ...PAID_TOOLS];
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Axis names: ONE alias table shared with the HTTP door (functions/mcp/axis-aliases.json) and the
|
|
84
|
+
* Python client. `governance`, `GOV` and `gspc-governance` name the same axis on every surface.
|
|
85
|
+
*/
|
|
86
|
+
const ALIASES_PATH = firstExisting([
|
|
87
|
+
"../../functions/mcp/axis-aliases.json", // repo checkout: the canonical file
|
|
88
|
+
"./axis-aliases.json", // npm package: the byte-identical pack-time copy
|
|
89
|
+
]);
|
|
90
|
+
const AXIS_TABLE = ALIASES_PATH ? JSON.parse(readFileSync(ALIASES_PATH, "utf8")).axes : {};
|
|
91
|
+
const AXIS_CANON = new Map();
|
|
92
|
+
for (const [canonical, aliases] of Object.entries(AXIS_TABLE)) {
|
|
93
|
+
AXIS_CANON.set(canonical.toLowerCase(), canonical);
|
|
94
|
+
for (const a of aliases) AXIS_CANON.set(String(a).toLowerCase(), canonical);
|
|
95
|
+
}
|
|
96
|
+
function canonicalAxis(name) {
|
|
97
|
+
const k = String(name ?? "").trim().toLowerCase();
|
|
98
|
+
return AXIS_CANON.get(k) ?? k;
|
|
99
|
+
}
|
|
100
|
+
const sameAxis = (a, b) => canonicalAxis(a) === canonicalAxis(b);
|
|
101
|
+
|
|
82
102
|
const VERIFIER_PATH = firstExisting([
|
|
83
103
|
"../../public/signed/verify-card.mjs", // repo checkout: the canonical file
|
|
84
104
|
"./verify-card.mjs", // npm package: the byte-identical pack-time copy
|
|
@@ -163,8 +183,9 @@ async function boardTotals() {
|
|
|
163
183
|
}
|
|
164
184
|
|
|
165
185
|
async function getAxis(args) {
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
186
|
+
const asked = String(args.axis ?? "").trim();
|
|
187
|
+
if (!asked) return { state: "BAD_INPUT", error: "pass an axis name, e.g. governance" };
|
|
188
|
+
const wanted = canonicalAxis(asked);
|
|
168
189
|
let d;
|
|
169
190
|
try {
|
|
170
191
|
d = await fetchJson("/api/gspc");
|
|
@@ -172,11 +193,11 @@ async function getAxis(args) {
|
|
|
172
193
|
return unreachable("/api/gspc", e);
|
|
173
194
|
}
|
|
174
195
|
const rows = d.axes ?? [];
|
|
175
|
-
const row = rows.find((r) =>
|
|
196
|
+
const row = rows.find((r) => sameAxis(r.axis, wanted));
|
|
176
197
|
if (!row) {
|
|
177
198
|
return {
|
|
178
199
|
state: "NOT_ON_BOARD",
|
|
179
|
-
axis:
|
|
200
|
+
axis: asked,
|
|
180
201
|
note: "This name is not a row on the live board. That is a fact about the board, not a verdict about the subject.",
|
|
181
202
|
board_carries: rows.map((r) => r.axis),
|
|
182
203
|
as_of: { board_measured_on: d.measured_on ?? null, fetched_at: new Date().toISOString() },
|
|
@@ -186,6 +207,7 @@ async function getAxis(args) {
|
|
|
186
207
|
return {
|
|
187
208
|
state: "LIVE",
|
|
188
209
|
axis: row.axis,
|
|
210
|
+
...(asked.toLowerCase() !== String(row.axis).toLowerCase() ? { resolved_from: asked } : {}),
|
|
189
211
|
family: row.family ?? null,
|
|
190
212
|
status: row.status ?? null,
|
|
191
213
|
measured,
|
|
@@ -197,6 +219,13 @@ async function getAxis(args) {
|
|
|
197
219
|
interval: row.interval ?? null,
|
|
198
220
|
leader: row.leader ?? null,
|
|
199
221
|
dataset: row.dataset ?? null,
|
|
222
|
+
// Same three door fields the HTTP door returns (functions/mcp/_board.ts): copied from the row,
|
|
223
|
+
// null when the row carries none, never invented.
|
|
224
|
+
kind: row.kind ?? null,
|
|
225
|
+
dataset_url: row.dataset_url ?? null,
|
|
226
|
+
evidence_url: typeof row.evidence_url === "string" && row.evidence_url.startsWith("/")
|
|
227
|
+
? `${ORIGIN}${row.evidence_url}` : (row.evidence_url ?? null),
|
|
228
|
+
row_url: `${ORIGIN}/api/gspc?axis=${encodeURIComponent(String(row.axis))}`,
|
|
200
229
|
note: row.note ?? null,
|
|
201
230
|
as_of: { board_measured_on: d.measured_on ?? null, fetched_at: new Date().toISOString() },
|
|
202
231
|
source: `${ORIGIN}/api/gspc`,
|
|
@@ -272,10 +301,20 @@ async function listCards(args) {
|
|
|
272
301
|
packaged_at: idx.packaged_at ?? null,
|
|
273
302
|
pubkey: idx.pubkey ?? null,
|
|
274
303
|
};
|
|
275
|
-
const wanted = args.axis ?
|
|
304
|
+
const wanted = args.axis ? canonicalAxis(args.axis) : null;
|
|
276
305
|
const limit = Number.isInteger(args.limit) ? args.limit : 10;
|
|
306
|
+
if (wanted) {
|
|
307
|
+
const matched = [...new Set(rows.map((r) => String(r.axis ?? "")).filter((a) => sameAxis(a, wanted)))].sort();
|
|
308
|
+
out.axis_query = {
|
|
309
|
+
asked: String(args.axis),
|
|
310
|
+
canonical: wanted,
|
|
311
|
+
spellings: AXIS_TABLE[wanted] ? [wanted, ...AXIS_TABLE[wanted]] : [wanted],
|
|
312
|
+
index_names_matched: matched,
|
|
313
|
+
note: "rows whose index axis name resolves to the same axis under functions/mcp/axis-aliases.json; each row keeps the index's own spelling",
|
|
314
|
+
};
|
|
315
|
+
}
|
|
277
316
|
out.rows = rows
|
|
278
|
-
.filter((r) => !wanted ||
|
|
317
|
+
.filter((r) => !wanted || sameAxis(r.axis, wanted))
|
|
279
318
|
.slice()
|
|
280
319
|
.sort((a, b) => String(b.ts ?? "").localeCompare(String(a.ts ?? "")))
|
|
281
320
|
.slice(0, limit)
|
|
@@ -376,6 +415,44 @@ async function verifyInclusion(args) {
|
|
|
376
415
|
* canonical measured artefact; this package delegates to it instead of
|
|
377
416
|
* copying counts or manufacturing a trust verdict locally.
|
|
378
417
|
*/
|
|
418
|
+
/** partial = the snapshot says so OR its enumeration did not complete (a cap is not completion). Mirrors functions/mcp/_board.ts partialOf. */
|
|
419
|
+
function partialOf(d) {
|
|
420
|
+
const e = d.enumeration ?? {};
|
|
421
|
+
if (d.partial === true) return { partial: true, partial_reason: String(e.stop_reason ?? "the snapshot marks itself partial") };
|
|
422
|
+
if (e.complete === false) {
|
|
423
|
+
const seen = e.rows_with_remote ?? e.registry_rows_seen;
|
|
424
|
+
return {
|
|
425
|
+
partial: true,
|
|
426
|
+
partial_reason: `enumeration incomplete: ${String(e.stop_reason ?? "stopped early")}${
|
|
427
|
+
e.unique_hosts != null && seen != null ? ` (${e.unique_hosts} hosts probed of ${seen} registry rows with a remote)` : ""
|
|
428
|
+
}`,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return { partial: false, partial_reason: null };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async function mcpTrust() {
|
|
435
|
+
const path = "/interop/mcp-trust/latest.json";
|
|
436
|
+
try {
|
|
437
|
+
const d = await fetchJson(path);
|
|
438
|
+
return {
|
|
439
|
+
state: "VALID",
|
|
440
|
+
source: `${ORIGIN}${path}`,
|
|
441
|
+
kind: d.kind ?? null,
|
|
442
|
+
as_of: d.as_of ?? null,
|
|
443
|
+
// A cap-limited read is PARTIAL — read from the enumeration, not only the flag (2026-09-26).
|
|
444
|
+
...partialOf(d),
|
|
445
|
+
enumeration: d.enumeration ?? null,
|
|
446
|
+
counts: d.counts ?? null,
|
|
447
|
+
headline: d.headline ?? null,
|
|
448
|
+
diff: d.diff ?? null,
|
|
449
|
+
not_a_certification: true,
|
|
450
|
+
};
|
|
451
|
+
} catch (e) {
|
|
452
|
+
return { ...unreachable(path, e), state: "UNREACHABLE" };
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
379
456
|
async function x402Trust() {
|
|
380
457
|
const path = "/interop/x402-trust/latest.json";
|
|
381
458
|
try {
|
|
@@ -394,6 +471,56 @@ async function x402Trust() {
|
|
|
394
471
|
}
|
|
395
472
|
}
|
|
396
473
|
|
|
474
|
+
/* ------------------------------------------------- measurement-capsule readers */
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* measurement_index, verify_capsule and server_evidence are answered by the door's own /mcp
|
|
478
|
+
* (functions/_lib/measurementCapsule.ts): this package forwards the call and returns the door's
|
|
479
|
+
* structuredContent unchanged, so the two implementations cannot disagree about a capsule. The door
|
|
480
|
+
* re-derives everything it returns (capsule ids, Merkle inclusion, the index signature against the
|
|
481
|
+
* pinned key). If the door cannot be reached the answer is UNREACHABLE — never a guess, never
|
|
482
|
+
* NOT_MEASURED (which is a statement about the index, not about the connection).
|
|
483
|
+
*/
|
|
484
|
+
const MEASUREMENT_DOCTRINE = "measurement, not endorsement";
|
|
485
|
+
|
|
486
|
+
async function doorTool(name, args) {
|
|
487
|
+
const url = `${ORIGIN}/mcp`;
|
|
488
|
+
try {
|
|
489
|
+
const r = await fetch(url, {
|
|
490
|
+
method: "POST",
|
|
491
|
+
headers: {
|
|
492
|
+
"content-type": "application/json",
|
|
493
|
+
accept: "application/json, text/event-stream",
|
|
494
|
+
"MCP-Protocol-Version": "2025-03-26",
|
|
495
|
+
},
|
|
496
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } }),
|
|
497
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
498
|
+
});
|
|
499
|
+
if (!r.ok) throw new Error(`POST ${url} returned HTTP ${r.status}`);
|
|
500
|
+
const text = await r.text();
|
|
501
|
+
const ct = r.headers.get("content-type") || "";
|
|
502
|
+
let msg;
|
|
503
|
+
if (ct.includes("text/event-stream")) {
|
|
504
|
+
const frames = text
|
|
505
|
+
.replace(/\r\n/g, "\n")
|
|
506
|
+
.split(/\n\n/)
|
|
507
|
+
.map((e) => e.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n"))
|
|
508
|
+
.filter(Boolean);
|
|
509
|
+
msg = JSON.parse(frames[frames.length - 1]);
|
|
510
|
+
} else msg = JSON.parse(text);
|
|
511
|
+
const sc = msg?.result?.structuredContent;
|
|
512
|
+
if (!sc || typeof sc !== "object") throw new Error(msg?.error?.message || "the door returned no structuredContent");
|
|
513
|
+
return { ...sc, answered_by: url };
|
|
514
|
+
} catch (e) {
|
|
515
|
+
return {
|
|
516
|
+
state: "UNREACHABLE",
|
|
517
|
+
doctrine: MEASUREMENT_DOCTRINE,
|
|
518
|
+
reason: `the door that answers ${name} could not be reached: ${e instanceof Error ? e.message : String(e)}`,
|
|
519
|
+
source: url,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
397
524
|
/* ---------------------------------------------------------------- paid tools */
|
|
398
525
|
|
|
399
526
|
const PAID_DOCTRINE =
|
|
@@ -632,11 +759,19 @@ const HANDLERS = {
|
|
|
632
759
|
get_card: getCard,
|
|
633
760
|
verify_inclusion: verifyInclusion,
|
|
634
761
|
x402_trust: x402Trust,
|
|
762
|
+
mcp_trust: mcpTrust,
|
|
763
|
+
measurement_index: (a) => doorTool("measurement_index", a),
|
|
764
|
+
verify_capsule: (a) => doorTool("verify_capsule", a),
|
|
765
|
+
server_evidence: (a) => doorTool("server_evidence", a),
|
|
635
766
|
};
|
|
636
767
|
|
|
637
768
|
/* ----------------------------------------------------------------- transport */
|
|
638
769
|
|
|
639
|
-
|
|
770
|
+
// Oldest first; the LAST entry is the latest this server speaks. An unknown requested version is
|
|
771
|
+
// answered with the latest (MCP lifecycle: "the server MUST respond with another protocol version
|
|
772
|
+
// it supports. This SHOULD be the latest version supported"), not the oldest as it was until
|
|
773
|
+
// 2026-09-26. README.md "stdio" lists exactly this array (tools-match-door.test.ts checks it).
|
|
774
|
+
const SUPPORTED_PROTOCOLS = ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"];
|
|
640
775
|
|
|
641
776
|
function send(msg) {
|
|
642
777
|
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
@@ -675,6 +810,12 @@ function summaryLine(name, payload) {
|
|
|
675
810
|
return `${payload.state ?? "?"} — inclusion against live merkle.`;
|
|
676
811
|
case "x402_trust":
|
|
677
812
|
return `${payload.state ?? "?"} — ${payload.headline || "catalog trust counts"}.`;
|
|
813
|
+
case "mcp_trust":
|
|
814
|
+
return `${payload.state ?? "?"} — MCP handshake census${payload.partial ? " (partial round)" : ""}.`;
|
|
815
|
+
case "measurement_index":
|
|
816
|
+
case "verify_capsule":
|
|
817
|
+
case "server_evidence":
|
|
818
|
+
return `${payload.state ?? "?"}${payload.reason ? " — " + payload.reason : ""} (${MEASUREMENT_DOCTRINE}).`;
|
|
678
819
|
case "commission_card":
|
|
679
820
|
case "art50_marking_evidence":
|
|
680
821
|
case "rwa_evidence":
|
|
@@ -694,7 +835,7 @@ async function handle(msg) {
|
|
|
694
835
|
if (method === "initialize") {
|
|
695
836
|
const asked = params?.protocolVersion;
|
|
696
837
|
return reply(id, {
|
|
697
|
-
protocolVersion: SUPPORTED_PROTOCOLS.includes(asked) ? asked :
|
|
838
|
+
protocolVersion: SUPPORTED_PROTOCOLS.includes(asked) ? asked : SUPPORTED_PROTOCOLS[SUPPORTED_PROTOCOLS.length - 1],
|
|
698
839
|
capabilities: { tools: {} },
|
|
699
840
|
serverInfo: { name: "csoai-gspc-mcp", version: VERSION },
|
|
700
841
|
});
|
|
@@ -709,6 +850,24 @@ async function handle(msg) {
|
|
|
709
850
|
if (!fn) return replyError(id, -32602, `unknown tool: ${name}`);
|
|
710
851
|
try {
|
|
711
852
|
const payload = await fn(params?.arguments ?? {});
|
|
853
|
+
// x402 MCP transport (x402-foundation/x402 specs/transports-v2/mcp.md): a payment challenge is
|
|
854
|
+
// a tool result with isError:true whose structuredContent IS the PaymentRequired object and
|
|
855
|
+
// whose content[0].text is that object as JSON. Same shape as the HTTP door
|
|
856
|
+
// (functions/mcp/_paid.ts). Payment is still read only from the x_payment argument.
|
|
857
|
+
if (payload?.status === "PAYMENT_REQUIRED") {
|
|
858
|
+
const pr = payload.payment_required && typeof payload.payment_required === "object" && !Array.isArray(payload.payment_required)
|
|
859
|
+
? payload.payment_required
|
|
860
|
+
: {};
|
|
861
|
+
const sc = { ...pr, ...payload };
|
|
862
|
+
return reply(id, {
|
|
863
|
+
content: [
|
|
864
|
+
{ type: "text", text: JSON.stringify(sc) },
|
|
865
|
+
{ type: "text", text: summaryLine(name, payload) },
|
|
866
|
+
],
|
|
867
|
+
structuredContent: sc,
|
|
868
|
+
isError: true,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
712
871
|
return reply(id, {
|
|
713
872
|
content: [{ type: "text", text: `${summaryLine(name, payload)}\n\n${JSON.stringify(payload, null, 2)}` }],
|
|
714
873
|
structuredContent: payload,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "csoai-gspc-mcp",
|
|
3
|
-
"version": "0.2.2",
|
|
4
|
-
"description": "Stdio MCP server for Council of AI's GSPC board and signed measurement evidence.
|
|
3
|
+
"version": "0.2.3-rc.2",
|
|
4
|
+
"description": "Stdio MCP server for Council of AI's GSPC board and signed measurement evidence. Sixteen tools: twelve free readers and four x402-metered evidence tools. Measurement only, never certification. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"csoai-gspc-mcp": "index.mjs"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"main": "index.mjs",
|
|
10
10
|
"files": [
|
|
11
11
|
"README.md",
|
|
12
|
+
"axis-aliases.json",
|
|
12
13
|
"gspc-tools.json",
|
|
13
14
|
"index.mjs",
|
|
14
15
|
"paid-tools.json",
|
|
@@ -51,5 +52,5 @@
|
|
|
51
52
|
"engines": {
|
|
52
53
|
"node": ">=20"
|
|
53
54
|
},
|
|
54
|
-
"mcpName": "
|
|
55
|
+
"mcpName": "ai.councilof/gspc"
|
|
55
56
|
}
|
package/paid-tools.json
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
"tools": [
|
|
6
6
|
{
|
|
7
7
|
"name": "commission_card",
|
|
8
|
+
"title": "Commission a measurement card (paid, x402)",
|
|
9
|
+
"annotations": {"title": "Commission a measurement card (paid, x402)", "readOnlyHint": false, "destructiveHint": false, "idempotentHint": false, "openWorldHint": true},
|
|
10
|
+
"outputSchema": {"type": "object", "description": "The wrapper's status fields. On PAYMENT_REQUIRED (isError:true) the route's x402 PaymentRequired object (x402Version, resource, accepts, extensions) is spread at the top level too, per the x402 MCP transport.", "properties": {"status": {"type": "string", "description": "PAYMENT_REQUIRED, DELIVERED, NOT_DEPLOYED, UNREACHABLE, UNREADABLE_RESPONSE, BAD_ARGUMENTS or HTTP_<code>"}, "tool": {"type": "string"}, "route": {"type": "string"}, "http_status": {"type": "integer"}, "payment_presented": {"type": "boolean"}, "delivery_state": {"type": "string"}, "settlement_state": {"type": "string"}, "x402Version": {"type": "integer"}, "accepts": {"type": "array"}, "resource": {"type": "object"}, "deliverable": {}, "not_a_certification": {"type": "boolean"}}, "required": ["status"]},
|
|
8
11
|
"description": "PAID (x402). Commission one signed card-v0 receipt (surface ras.commission) for a named subject on the frozen bank via https://councilof.ai/api/request-attestation. Re-serves every signed measurement card already on file for the subject; a payment never mints a MEASURED cell (fresh_run stays UNMEASURED until a published run exists). Without x_payment the tool returns the 402 challenge (accepts[] with asset, amount, payTo) plus a free preview of the cards already on file. Measurement, not certification — never a rank, a grade or a certificate. Verification stays free.",
|
|
9
12
|
"inputSchema": {
|
|
10
13
|
"type": "object",
|
|
@@ -20,6 +23,9 @@
|
|
|
20
23
|
},
|
|
21
24
|
{
|
|
22
25
|
"name": "art50_marking_evidence",
|
|
26
|
+
"title": "Article 50 marking evidence (paid, x402; preview free)",
|
|
27
|
+
"annotations": {"title": "Article 50 marking evidence (paid, x402; preview free)", "readOnlyHint": false, "destructiveHint": false, "idempotentHint": false, "openWorldHint": true},
|
|
28
|
+
"outputSchema": {"type": "object", "description": "The wrapper's status fields. On PAYMENT_REQUIRED (isError:true) the route's x402 PaymentRequired object (x402Version, resource, accepts, extensions) is spread at the top level too, per the x402 MCP transport.", "properties": {"status": {"type": "string", "description": "PAYMENT_REQUIRED, DELIVERED, NOT_DEPLOYED, UNREACHABLE, UNREADABLE_RESPONSE, BAD_ARGUMENTS or HTTP_<code>"}, "tool": {"type": "string"}, "route": {"type": "string"}, "http_status": {"type": "integer"}, "payment_presented": {"type": "boolean"}, "delivery_state": {"type": "string"}, "settlement_state": {"type": "string"}, "x402Version": {"type": "integer"}, "accepts": {"type": "array"}, "resource": {"type": "object"}, "deliverable": {}, "not_a_certification": {"type": "boolean"}}, "required": ["status"]},
|
|
23
29
|
"description": "PAID (x402 or CSOAI LTD invoice). Article 50 marking-evidence pack via https://councilof.ai/api/art50/marking-evidence: is a machine-readable mark DETECTABLE in these bytes right now (C2PA manifest store, assertion hashes, hard binding, claim signature; IPTC digitalSourceType), beside the verbatim Art 50(2) excerpt hash and the Art 99(4) ceiling. Watermarks are UNCHECKABLE where no public detector exists and the pack says so. Point-in-time detection — never a conformity opinion, never a compliance word of any kind. preview=true is free and returns the same measurement unsigned. Without x_payment the tool returns the 402 challenge. If the route is not deployed on this origin the tool says NOT_DEPLOYED rather than inventing a result.",
|
|
24
30
|
"inputSchema": {
|
|
25
31
|
"type": "object",
|
|
@@ -36,6 +42,9 @@
|
|
|
36
42
|
},
|
|
37
43
|
{
|
|
38
44
|
"name": "rwa_evidence",
|
|
45
|
+
"title": "RWA asset evidence (paid, x402; preview free)",
|
|
46
|
+
"annotations": {"title": "RWA asset evidence (paid, x402; preview free)", "readOnlyHint": false, "destructiveHint": false, "idempotentHint": false, "openWorldHint": true},
|
|
47
|
+
"outputSchema": {"type": "object", "description": "The wrapper's status fields. On PAYMENT_REQUIRED (isError:true) the route's x402 PaymentRequired object (x402Version, resource, accepts, extensions) is spread at the top level too, per the x402 MCP transport.", "properties": {"status": {"type": "string", "description": "PAYMENT_REQUIRED, DELIVERED, NOT_DEPLOYED, UNREACHABLE, UNREADABLE_RESPONSE, BAD_ARGUMENTS or HTTP_<code>"}, "tool": {"type": "string"}, "route": {"type": "string"}, "http_status": {"type": "integer"}, "payment_presented": {"type": "boolean"}, "delivery_state": {"type": "string"}, "settlement_state": {"type": "string"}, "x402Version": {"type": "integer"}, "accepts": {"type": "array"}, "resource": {"type": "object"}, "deliverable": {}, "not_a_certification": {"type": "boolean"}}, "required": ["status"]},
|
|
39
48
|
"description": "PAID (x402). Per-request signed evidence card of ONE XRPL issued asset's deterministic on-ledger state via https://councilof.ai/api/rwa/evidence: AccountRoot lsf* flags, Domain, the two-way xrp-ledger.toml check (PASS / FAIL / UNCHECKABLE — unreachable is never FAIL), gateway_balances obligation, holders as the free reader has them, every raw fetch sha256'd. preview=true is free (unsigned state). Historical state at fetched_at — not a rating, not a guarantee, not a conformity mark; /api/xrpl and /root.json stay free. Without x_payment the tool returns the 402 challenge. If the route is not deployed on this origin the tool says NOT_DEPLOYED.",
|
|
40
49
|
"inputSchema": {
|
|
41
50
|
"type": "object",
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
},
|
|
52
61
|
{
|
|
53
62
|
"name": "receipts_batch",
|
|
63
|
+
"title": "Receipts batch (paid, x402; preview free)",
|
|
64
|
+
"annotations": {"title": "Receipts batch (paid, x402; preview free)", "readOnlyHint": false, "destructiveHint": false, "idempotentHint": false, "openWorldHint": true},
|
|
65
|
+
"outputSchema": {"type": "object", "description": "The wrapper's status fields. On PAYMENT_REQUIRED (isError:true) the route's x402 PaymentRequired object (x402Version, resource, accepts, extensions) is spread at the top level too, per the x402 MCP transport.", "properties": {"status": {"type": "string", "description": "PAYMENT_REQUIRED, DELIVERED, NOT_DEPLOYED, UNREACHABLE, UNREADABLE_RESPONSE, BAD_ARGUMENTS or HTTP_<code>"}, "tool": {"type": "string"}, "route": {"type": "string"}, "http_status": {"type": "integer"}, "payment_presented": {"type": "boolean"}, "delivery_state": {"type": "string"}, "settlement_state": {"type": "string"}, "x402Version": {"type": "integer"}, "accepts": {"type": "array"}, "resource": {"type": "object"}, "deliverable": {}, "not_a_certification": {"type": "boolean"}}, "required": ["status"]},
|
|
54
66
|
"description": "PAID (x402 or CSOAI LTD invoice). A historical batch of the estate's measurement receipts via https://councilof.ai/api/receipts/batch: every signed card-v0 leaf whose as_of falls in [from,to] (≤200), each with its Merkle inclusion path and the public root(s) that carried it, plus the root index for the window and one signed manifest card citing the batch sha256. preview=true is free and returns count, span, root count and the sha256 of the exact bytes the paid call returns. Recent leaves are free at /root.json, /cards/ and /api/proof?sha= — the batch sells assembly across history, never a conclusion. No settlement-receipt stream exists (/api/receipts/latest is UNPUBLISHED) and this tool never claims one. Without x_payment the tool returns the 402 challenge.",
|
|
55
67
|
"inputSchema": {
|
|
56
68
|
"type": "object",
|