bft-progress-council-mcp 1.1.1__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.
- bft_progress_council_mcp-1.1.1/LICENSE +21 -0
- bft_progress_council_mcp-1.1.1/PKG-INFO +251 -0
- bft_progress_council_mcp-1.1.1/README.md +225 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/PKG-INFO +251 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/SOURCES.txt +11 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/dependency_links.txt +1 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/entry_points.txt +2 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/requires.txt +1 -0
- bft_progress_council_mcp-1.1.1/bft_progress_council_mcp.egg-info/top_level.txt +1 -0
- bft_progress_council_mcp-1.1.1/pyproject.toml +39 -0
- bft_progress_council_mcp-1.1.1/server.py +361 -0
- bft_progress_council_mcp-1.1.1/setup.cfg +4 -0
- bft_progress_council_mcp-1.1.1/tests/test_council.py +153 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicholas Templeman / MEOK AI Labs (CSOAI LTD, UK Companies House 16939677)
|
|
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.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bft-progress-council-mcp
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: BFT Progress Council MCP — 5-voter Byzantine council halts agentic loops when no real progress is happening. Stops tokens bleeding on infinite spins. By MEOK AI Labs.
|
|
5
|
+
Author-email: Nicholas Templeman <nicholas@meok.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://meok.ai/a2a
|
|
8
|
+
Project-URL: Repository, https://github.com/CSOAI-ORG/bft-progress-council-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/CSOAI-ORG/bft-progress-council-mcp/issues
|
|
10
|
+
Project-URL: Documentation, https://meok.ai/moe
|
|
11
|
+
Keywords: mcp,mcp-server,model-context-protocol,byzantine,bft,council,anti-loop,agentic,guardrail,meok
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: System :: Monitoring
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: mcp[cli]>=1.3.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp
|
|
28
|
+
|
|
29
|
+
# BFT Progress Council MCP
|
|
30
|
+
|
|
31
|
+
> ## 🧱 Part of the MEOK A2A Substrate
|
|
32
|
+
>
|
|
33
|
+
> Run all 12 A2A primitives + this BFT council as one signed pipeline for
|
|
34
|
+
> **£499/mo** (100K calls), or pay **£0.0002/call**. See
|
|
35
|
+
> [meok.ai/a2a](https://meok.ai/a2a).
|
|
36
|
+
|
|
37
|
+
# Anti-loop guardrail for AI agents
|
|
38
|
+
|
|
39
|
+
<!-- mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp -->
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/bft-progress-council-mcp/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
[](https://registry.modelcontextprotocol.io)
|
|
44
|
+
|
|
45
|
+
> ## 💰 Stops your agent burning tokens on loops
|
|
46
|
+
>
|
|
47
|
+
> Every agentic coding tool — Cursor, Claude Code, Devin, Aider — bleeds tokens
|
|
48
|
+
> when the agent gets stuck and keeps retrying. This MCP runs a **5-voter
|
|
49
|
+
> Byzantine Fault Tolerant council** that halts the loop the moment 3 of 5 voters
|
|
50
|
+
> agree there's no real progress.
|
|
51
|
+
>
|
|
52
|
+
> Typical save: **£10-£200 per agent run** depending on agent + task length.
|
|
53
|
+
> Pays for the £29/mo Starter tier the first time it halts a stuck loop.
|
|
54
|
+
|
|
55
|
+
## The problem
|
|
56
|
+
|
|
57
|
+
You're running an agent (Cursor / Claude Code / Aider / Devin / a custom MCP
|
|
58
|
+
orchestration). It hits a tricky bug. Or a missing dependency. Or a 403 from
|
|
59
|
+
an API. Instead of stopping, it just keeps **trying**. Different prompts, same
|
|
60
|
+
fundamental error. Each retry costs API tokens. By the time you check, it's
|
|
61
|
+
spent £50.
|
|
62
|
+
|
|
63
|
+
Sound familiar? That's because every existing agentic loop has one weakness:
|
|
64
|
+
**it doesn't know whether it's making progress**. It only knows whether the
|
|
65
|
+
last action succeeded. So when it can't tell the difference between "trying
|
|
66
|
+
harder" and "spinning", it just keeps spinning.
|
|
67
|
+
|
|
68
|
+
## The solution
|
|
69
|
+
|
|
70
|
+
Five independent voters look at the last N actions + the original goal and
|
|
71
|
+
each return a verdict. We tally. If 3 of 5 say PROGRESS, continue. If 3 of 5
|
|
72
|
+
say STALL / DRIFT / BLOCKED, **halt**.
|
|
73
|
+
|
|
74
|
+
| Voter | Detects |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `repetition` | Literal action repetition in last 5 actions |
|
|
77
|
+
| `outcome_diversity` | Identical outcomes / repeated error strings (429, 403, exception messages) |
|
|
78
|
+
| `goal_alignment` | Drift from the original goal (token overlap) |
|
|
79
|
+
| `action_velocity` | Rapid-fire spinning with no human-loop pause |
|
|
80
|
+
| `artefact_growth` | No tangible artefacts being produced (no writes / commits / deploys) |
|
|
81
|
+
|
|
82
|
+
Free tier uses the deterministic heuristic voters (above). **Pro tier** swaps
|
|
83
|
+
them for 5 actual LLM voters (Claude Opus + GPT-5 + Gemini 2.5 + Llama 3.3 +
|
|
84
|
+
Step 3.6) which catch subtler stalls.
|
|
85
|
+
|
|
86
|
+
## Quick install
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# uvx (preferred — no install)
|
|
90
|
+
uvx bft-progress-council-mcp
|
|
91
|
+
|
|
92
|
+
# pip
|
|
93
|
+
pip install bft-progress-council-mcp
|
|
94
|
+
|
|
95
|
+
# npx (via @meok-ai bridge)
|
|
96
|
+
npx @meok-ai/bft-progress-council-mcp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Add to your Claude Desktop / Cursor / Windsurf MCP config:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"bft-progress-council": {
|
|
105
|
+
"command": "uvx",
|
|
106
|
+
"args": ["bft-progress-council-mcp"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Usage
|
|
113
|
+
|
|
114
|
+
Three tools to call from your agent loop:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# 1. Start a session at the top of your agent run
|
|
118
|
+
{"tool": "start_session", "args": {"goal": "Fix the EU AI Act Article 50 watermarking bug in src/article50.py"}}
|
|
119
|
+
# → { session_id: "s_1779...", goal: "...", started_at: "..." }
|
|
120
|
+
|
|
121
|
+
# 2. Record every action you take
|
|
122
|
+
{"tool": "record_action", "args": {
|
|
123
|
+
"session_id": "s_1779...",
|
|
124
|
+
"action": "Edit src/article50.py: add C2PA manifest validation",
|
|
125
|
+
"outcome": "Test still failing on missing-key.png"
|
|
126
|
+
}}
|
|
127
|
+
|
|
128
|
+
# 3. Periodically (every 5-10 actions) — let the council vote
|
|
129
|
+
{"tool": "council_vote", "args": {"session_id": "s_1779...", "lookback": 10}}
|
|
130
|
+
# → {
|
|
131
|
+
# "verdict": "STALL",
|
|
132
|
+
# "action_recommended": "halt_loop_and_request_new_approach",
|
|
133
|
+
# "tally": {"PROGRESS": 1, "STALL": 3, "DRIFT": 0, "BLOCKED": 1},
|
|
134
|
+
# "votes": [
|
|
135
|
+
# {"voter": "repetition", "verdict": "STALL", "reason": "only 2 unique actions in last 5"},
|
|
136
|
+
# {"voter": "outcome_diversity", "verdict": "BLOCKED", "reason": "4 of 5 outcomes contain 'failing'"},
|
|
137
|
+
# {"voter": "goal_alignment", "verdict": "PROGRESS", "reason": "60% goal-token overlap"},
|
|
138
|
+
# {"voter": "action_velocity", "verdict": "STALL", "reason": "rapid-fire actions (1.2s avg)"},
|
|
139
|
+
# {"voter": "artefact_growth", "verdict": "STALL", "reason": "no commits in last 10"}
|
|
140
|
+
# ],
|
|
141
|
+
# "signed_attestation": { ... HMAC-SHA256 signed ... }
|
|
142
|
+
# }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
When the verdict is anything other than PROGRESS, the agent **stops** and either
|
|
146
|
+
re-anchors to the original goal or escalates to the human. No more silent
|
|
147
|
+
overnight loops eating tokens.
|
|
148
|
+
|
|
149
|
+
## The maths
|
|
150
|
+
|
|
151
|
+
Free tier saves you tokens. Here's the simple cost model:
|
|
152
|
+
|
|
153
|
+
- Avg agent step: ~2,000 tokens (input + output blended)
|
|
154
|
+
- Claude Opus 4.7 blended cost: ~£0.025 per 1K tokens
|
|
155
|
+
- Each prevented "wasted action loop" averages 5+ extra steps
|
|
156
|
+
- One halt = 5 × 2,000 × £0.025/1K = **~£0.25 saved per halt**
|
|
157
|
+
- A typical multi-hour agent run hits 4-12 stall events
|
|
158
|
+
- Per-run saving: **£1-£3 in tokens**
|
|
159
|
+
|
|
160
|
+
That's just the free tier. Substrate customers running fleets of agents save
|
|
161
|
+
**£100-£1,000/month** with one MCP. £29/mo Starter pays for itself in hours.
|
|
162
|
+
|
|
163
|
+
Verify your own ROI with the `estimate_tokens_saved` tool — pass the session
|
|
164
|
+
ID and it'll calculate the cost saved given your halt history.
|
|
165
|
+
|
|
166
|
+
## Tiers
|
|
167
|
+
|
|
168
|
+
| Tier | Price | What |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| **Free** | £0 (MIT) | Heuristic voters, local-only attestations, self-host |
|
|
171
|
+
| **Starter** | £29/mo | Managed signing key + verify.meok.ai attestation, 10K sessions/mo |
|
|
172
|
+
| **Pro** | £79/mo | 5 actual LLM voters (Claude + GPT + Gemini + Llama + Step), 100K sessions, 24h SLA |
|
|
173
|
+
| **A2A Substrate** | £499/mo | This + 11 other A2A MCPs (handoff, audit-logger, policy, firewall, etc.) |
|
|
174
|
+
| **Universe** | £1,499/mo | All 48 MEOK MCPs · 500K calls |
|
|
175
|
+
| **Defence** | £4,990/mo | Pro + on-prem + dedicated CSM |
|
|
176
|
+
|
|
177
|
+
Buy: https://meok.ai/a2a · https://buy.stripe.com/bJe3cx6WgcMO38142k8k90o
|
|
178
|
+
|
|
179
|
+
## Sister MCPs
|
|
180
|
+
|
|
181
|
+
Part of the MEOK **A2A** pack:
|
|
182
|
+
|
|
183
|
+
- **Prompt Injection Firewall** → `uvx agent-prompt-injection-firewall-mcp`
|
|
184
|
+
- **Audit Logger** → `uvx agent-audit-logger-mcp`
|
|
185
|
+
- **Policy Enforcement** → `uvx agent-policy-enforcement-mcp`
|
|
186
|
+
- **Rate Limiter** → `uvx agent-rate-limiter-mcp`
|
|
187
|
+
- **Certified Handoff** → `uvx agent-handoff-certified-mcp`
|
|
188
|
+
- **Identity + Trust** → `uvx agent-identity-trust-mcp`
|
|
189
|
+
|
|
190
|
+
Full catalogue + Anthropic Registry verify links: [meok.ai/anthropic-registry](https://meok.ai/anthropic-registry)
|
|
191
|
+
|
|
192
|
+
## Protocol coverage + Universal PAYG
|
|
193
|
+
|
|
194
|
+
- ✅ **MCP** (Anthropic) — native
|
|
195
|
+
- ✅ **A2A** (Google + Linux Foundation, absorbed IBM ACP)
|
|
196
|
+
- ✅ **IBM ACP** — covered via A2A merge
|
|
197
|
+
- ◐ **Stripe ACP** (Agentic Commerce) — Q3 bridge
|
|
198
|
+
- ◐ **AP2** (Google Agent Payments) — partial
|
|
199
|
+
- ◐ **x402** (Coinbase HTTP 402) — via api.meok.ai gateway
|
|
200
|
+
- → **OASF / AGNTCY** — Q3 bridge
|
|
201
|
+
|
|
202
|
+
| Option | Price | Best for |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| Self-host (this MCP) | £0 — MIT | Devs |
|
|
205
|
+
| This MCP Starter | £29/mo | One-MCP teams |
|
|
206
|
+
| Universal PAYG | £29/mo + £0.0002/call | Spiky usage |
|
|
207
|
+
| A2A Substrate | £499/mo | A whole pack |
|
|
208
|
+
| Universe | £1,499/mo | All 48 MCPs |
|
|
209
|
+
|
|
210
|
+
## Why this matters for MEOK
|
|
211
|
+
|
|
212
|
+
Every other MEOK MCP makes you *do* something. This one tells you *when to
|
|
213
|
+
stop*. It's the cheapest insurance policy in the catalogue — and it sits
|
|
214
|
+
alongside the agent-rate-limiter and agent-audit-logger as the third
|
|
215
|
+
guardrail in the A2A Substrate.
|
|
216
|
+
|
|
217
|
+
## Wire it up — full stack
|
|
218
|
+
|
|
219
|
+
This MCP is **step 1 of 6** in the MEOK chain that turns one agent action
|
|
220
|
+
into a fully signed compliance event. See
|
|
221
|
+
[meok.ai/mcp-stack](https://meok.ai/mcp-stack) for the 6-MCP chain:
|
|
222
|
+
|
|
223
|
+
1. **bft-progress-council-mcp** (this) — anti-loop guardrail
|
|
224
|
+
2. **agent-token-budget-mcp** — hard spend cap
|
|
225
|
+
3. **agent-content-watermark-mcp** — EU AI Act Article 50(2) watermark
|
|
226
|
+
4. **meok-eu-aigc-icon-mcp** — EU Code-of-Practice icon (Nov 2026 cliff)
|
|
227
|
+
5. **agent-audit-logger-mcp** — hash-chained audit trail
|
|
228
|
+
6. **a2a-governance-bridge-mcp** — fold all signatures into one signed event
|
|
229
|
+
|
|
230
|
+
Output: ONE auditor-defensible evidence event mapped to EU AI Act Articles
|
|
231
|
+
12 + 50, DORA Article 17, ISO 42001 clause 9 — plus a public verify URL.
|
|
232
|
+
|
|
233
|
+
## Licence
|
|
234
|
+
|
|
235
|
+
MIT. By [MEOK AI Labs](https://meok.ai) (CSOAI LTD, UK Companies House
|
|
236
|
+
16939677). Founder: [Nicholas Templeman](mailto:nicholas@meok.ai).
|
|
237
|
+
|
|
238
|
+
<!-- BUY-LADDER:START -->
|
|
239
|
+
|
|
240
|
+
## 💸 Try MEOK in 30 seconds — instant buy ladder
|
|
241
|
+
|
|
242
|
+
| Tier | Price | What you get | Stripe |
|
|
243
|
+
|---|---|---|---|
|
|
244
|
+
| Smoke test | **£1** | Signed sample MCP-Hardening report + Article 50 PDF | <https://buy.stripe.com/dRmcN75ScdQS7oh1Uc8k90U> |
|
|
245
|
+
| Quick Kit | **£9** | EU AI Act Article 50 implementation guide (C2PA + EU-Icon) | <https://buy.stripe.com/cNi00la8s1460ZT0Q88k90V> |
|
|
246
|
+
| Founder Call | **£29** | 30-min 1-on-1 with the founder | <https://buy.stripe.com/8x228ta8s6oqbExaqI8k90W> |
|
|
247
|
+
|
|
248
|
+
> Refundable. UK Stripe — VAT-clean. Builds on the 81-MCP MEOK fleet.
|
|
249
|
+
> Verify any signed report at <https://meok.ai/verify>.
|
|
250
|
+
|
|
251
|
+
<!-- BUY-LADDER:END -->
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp
|
|
2
|
+
|
|
3
|
+
# BFT Progress Council MCP
|
|
4
|
+
|
|
5
|
+
> ## 🧱 Part of the MEOK A2A Substrate
|
|
6
|
+
>
|
|
7
|
+
> Run all 12 A2A primitives + this BFT council as one signed pipeline for
|
|
8
|
+
> **£499/mo** (100K calls), or pay **£0.0002/call**. See
|
|
9
|
+
> [meok.ai/a2a](https://meok.ai/a2a).
|
|
10
|
+
|
|
11
|
+
# Anti-loop guardrail for AI agents
|
|
12
|
+
|
|
13
|
+
<!-- mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp -->
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/bft-progress-council-mcp/)
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
[](https://registry.modelcontextprotocol.io)
|
|
18
|
+
|
|
19
|
+
> ## 💰 Stops your agent burning tokens on loops
|
|
20
|
+
>
|
|
21
|
+
> Every agentic coding tool — Cursor, Claude Code, Devin, Aider — bleeds tokens
|
|
22
|
+
> when the agent gets stuck and keeps retrying. This MCP runs a **5-voter
|
|
23
|
+
> Byzantine Fault Tolerant council** that halts the loop the moment 3 of 5 voters
|
|
24
|
+
> agree there's no real progress.
|
|
25
|
+
>
|
|
26
|
+
> Typical save: **£10-£200 per agent run** depending on agent + task length.
|
|
27
|
+
> Pays for the £29/mo Starter tier the first time it halts a stuck loop.
|
|
28
|
+
|
|
29
|
+
## The problem
|
|
30
|
+
|
|
31
|
+
You're running an agent (Cursor / Claude Code / Aider / Devin / a custom MCP
|
|
32
|
+
orchestration). It hits a tricky bug. Or a missing dependency. Or a 403 from
|
|
33
|
+
an API. Instead of stopping, it just keeps **trying**. Different prompts, same
|
|
34
|
+
fundamental error. Each retry costs API tokens. By the time you check, it's
|
|
35
|
+
spent £50.
|
|
36
|
+
|
|
37
|
+
Sound familiar? That's because every existing agentic loop has one weakness:
|
|
38
|
+
**it doesn't know whether it's making progress**. It only knows whether the
|
|
39
|
+
last action succeeded. So when it can't tell the difference between "trying
|
|
40
|
+
harder" and "spinning", it just keeps spinning.
|
|
41
|
+
|
|
42
|
+
## The solution
|
|
43
|
+
|
|
44
|
+
Five independent voters look at the last N actions + the original goal and
|
|
45
|
+
each return a verdict. We tally. If 3 of 5 say PROGRESS, continue. If 3 of 5
|
|
46
|
+
say STALL / DRIFT / BLOCKED, **halt**.
|
|
47
|
+
|
|
48
|
+
| Voter | Detects |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `repetition` | Literal action repetition in last 5 actions |
|
|
51
|
+
| `outcome_diversity` | Identical outcomes / repeated error strings (429, 403, exception messages) |
|
|
52
|
+
| `goal_alignment` | Drift from the original goal (token overlap) |
|
|
53
|
+
| `action_velocity` | Rapid-fire spinning with no human-loop pause |
|
|
54
|
+
| `artefact_growth` | No tangible artefacts being produced (no writes / commits / deploys) |
|
|
55
|
+
|
|
56
|
+
Free tier uses the deterministic heuristic voters (above). **Pro tier** swaps
|
|
57
|
+
them for 5 actual LLM voters (Claude Opus + GPT-5 + Gemini 2.5 + Llama 3.3 +
|
|
58
|
+
Step 3.6) which catch subtler stalls.
|
|
59
|
+
|
|
60
|
+
## Quick install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# uvx (preferred — no install)
|
|
64
|
+
uvx bft-progress-council-mcp
|
|
65
|
+
|
|
66
|
+
# pip
|
|
67
|
+
pip install bft-progress-council-mcp
|
|
68
|
+
|
|
69
|
+
# npx (via @meok-ai bridge)
|
|
70
|
+
npx @meok-ai/bft-progress-council-mcp
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Add to your Claude Desktop / Cursor / Windsurf MCP config:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"bft-progress-council": {
|
|
79
|
+
"command": "uvx",
|
|
80
|
+
"args": ["bft-progress-council-mcp"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
Three tools to call from your agent loop:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# 1. Start a session at the top of your agent run
|
|
92
|
+
{"tool": "start_session", "args": {"goal": "Fix the EU AI Act Article 50 watermarking bug in src/article50.py"}}
|
|
93
|
+
# → { session_id: "s_1779...", goal: "...", started_at: "..." }
|
|
94
|
+
|
|
95
|
+
# 2. Record every action you take
|
|
96
|
+
{"tool": "record_action", "args": {
|
|
97
|
+
"session_id": "s_1779...",
|
|
98
|
+
"action": "Edit src/article50.py: add C2PA manifest validation",
|
|
99
|
+
"outcome": "Test still failing on missing-key.png"
|
|
100
|
+
}}
|
|
101
|
+
|
|
102
|
+
# 3. Periodically (every 5-10 actions) — let the council vote
|
|
103
|
+
{"tool": "council_vote", "args": {"session_id": "s_1779...", "lookback": 10}}
|
|
104
|
+
# → {
|
|
105
|
+
# "verdict": "STALL",
|
|
106
|
+
# "action_recommended": "halt_loop_and_request_new_approach",
|
|
107
|
+
# "tally": {"PROGRESS": 1, "STALL": 3, "DRIFT": 0, "BLOCKED": 1},
|
|
108
|
+
# "votes": [
|
|
109
|
+
# {"voter": "repetition", "verdict": "STALL", "reason": "only 2 unique actions in last 5"},
|
|
110
|
+
# {"voter": "outcome_diversity", "verdict": "BLOCKED", "reason": "4 of 5 outcomes contain 'failing'"},
|
|
111
|
+
# {"voter": "goal_alignment", "verdict": "PROGRESS", "reason": "60% goal-token overlap"},
|
|
112
|
+
# {"voter": "action_velocity", "verdict": "STALL", "reason": "rapid-fire actions (1.2s avg)"},
|
|
113
|
+
# {"voter": "artefact_growth", "verdict": "STALL", "reason": "no commits in last 10"}
|
|
114
|
+
# ],
|
|
115
|
+
# "signed_attestation": { ... HMAC-SHA256 signed ... }
|
|
116
|
+
# }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When the verdict is anything other than PROGRESS, the agent **stops** and either
|
|
120
|
+
re-anchors to the original goal or escalates to the human. No more silent
|
|
121
|
+
overnight loops eating tokens.
|
|
122
|
+
|
|
123
|
+
## The maths
|
|
124
|
+
|
|
125
|
+
Free tier saves you tokens. Here's the simple cost model:
|
|
126
|
+
|
|
127
|
+
- Avg agent step: ~2,000 tokens (input + output blended)
|
|
128
|
+
- Claude Opus 4.7 blended cost: ~£0.025 per 1K tokens
|
|
129
|
+
- Each prevented "wasted action loop" averages 5+ extra steps
|
|
130
|
+
- One halt = 5 × 2,000 × £0.025/1K = **~£0.25 saved per halt**
|
|
131
|
+
- A typical multi-hour agent run hits 4-12 stall events
|
|
132
|
+
- Per-run saving: **£1-£3 in tokens**
|
|
133
|
+
|
|
134
|
+
That's just the free tier. Substrate customers running fleets of agents save
|
|
135
|
+
**£100-£1,000/month** with one MCP. £29/mo Starter pays for itself in hours.
|
|
136
|
+
|
|
137
|
+
Verify your own ROI with the `estimate_tokens_saved` tool — pass the session
|
|
138
|
+
ID and it'll calculate the cost saved given your halt history.
|
|
139
|
+
|
|
140
|
+
## Tiers
|
|
141
|
+
|
|
142
|
+
| Tier | Price | What |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| **Free** | £0 (MIT) | Heuristic voters, local-only attestations, self-host |
|
|
145
|
+
| **Starter** | £29/mo | Managed signing key + verify.meok.ai attestation, 10K sessions/mo |
|
|
146
|
+
| **Pro** | £79/mo | 5 actual LLM voters (Claude + GPT + Gemini + Llama + Step), 100K sessions, 24h SLA |
|
|
147
|
+
| **A2A Substrate** | £499/mo | This + 11 other A2A MCPs (handoff, audit-logger, policy, firewall, etc.) |
|
|
148
|
+
| **Universe** | £1,499/mo | All 48 MEOK MCPs · 500K calls |
|
|
149
|
+
| **Defence** | £4,990/mo | Pro + on-prem + dedicated CSM |
|
|
150
|
+
|
|
151
|
+
Buy: https://meok.ai/a2a · https://buy.stripe.com/bJe3cx6WgcMO38142k8k90o
|
|
152
|
+
|
|
153
|
+
## Sister MCPs
|
|
154
|
+
|
|
155
|
+
Part of the MEOK **A2A** pack:
|
|
156
|
+
|
|
157
|
+
- **Prompt Injection Firewall** → `uvx agent-prompt-injection-firewall-mcp`
|
|
158
|
+
- **Audit Logger** → `uvx agent-audit-logger-mcp`
|
|
159
|
+
- **Policy Enforcement** → `uvx agent-policy-enforcement-mcp`
|
|
160
|
+
- **Rate Limiter** → `uvx agent-rate-limiter-mcp`
|
|
161
|
+
- **Certified Handoff** → `uvx agent-handoff-certified-mcp`
|
|
162
|
+
- **Identity + Trust** → `uvx agent-identity-trust-mcp`
|
|
163
|
+
|
|
164
|
+
Full catalogue + Anthropic Registry verify links: [meok.ai/anthropic-registry](https://meok.ai/anthropic-registry)
|
|
165
|
+
|
|
166
|
+
## Protocol coverage + Universal PAYG
|
|
167
|
+
|
|
168
|
+
- ✅ **MCP** (Anthropic) — native
|
|
169
|
+
- ✅ **A2A** (Google + Linux Foundation, absorbed IBM ACP)
|
|
170
|
+
- ✅ **IBM ACP** — covered via A2A merge
|
|
171
|
+
- ◐ **Stripe ACP** (Agentic Commerce) — Q3 bridge
|
|
172
|
+
- ◐ **AP2** (Google Agent Payments) — partial
|
|
173
|
+
- ◐ **x402** (Coinbase HTTP 402) — via api.meok.ai gateway
|
|
174
|
+
- → **OASF / AGNTCY** — Q3 bridge
|
|
175
|
+
|
|
176
|
+
| Option | Price | Best for |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| Self-host (this MCP) | £0 — MIT | Devs |
|
|
179
|
+
| This MCP Starter | £29/mo | One-MCP teams |
|
|
180
|
+
| Universal PAYG | £29/mo + £0.0002/call | Spiky usage |
|
|
181
|
+
| A2A Substrate | £499/mo | A whole pack |
|
|
182
|
+
| Universe | £1,499/mo | All 48 MCPs |
|
|
183
|
+
|
|
184
|
+
## Why this matters for MEOK
|
|
185
|
+
|
|
186
|
+
Every other MEOK MCP makes you *do* something. This one tells you *when to
|
|
187
|
+
stop*. It's the cheapest insurance policy in the catalogue — and it sits
|
|
188
|
+
alongside the agent-rate-limiter and agent-audit-logger as the third
|
|
189
|
+
guardrail in the A2A Substrate.
|
|
190
|
+
|
|
191
|
+
## Wire it up — full stack
|
|
192
|
+
|
|
193
|
+
This MCP is **step 1 of 6** in the MEOK chain that turns one agent action
|
|
194
|
+
into a fully signed compliance event. See
|
|
195
|
+
[meok.ai/mcp-stack](https://meok.ai/mcp-stack) for the 6-MCP chain:
|
|
196
|
+
|
|
197
|
+
1. **bft-progress-council-mcp** (this) — anti-loop guardrail
|
|
198
|
+
2. **agent-token-budget-mcp** — hard spend cap
|
|
199
|
+
3. **agent-content-watermark-mcp** — EU AI Act Article 50(2) watermark
|
|
200
|
+
4. **meok-eu-aigc-icon-mcp** — EU Code-of-Practice icon (Nov 2026 cliff)
|
|
201
|
+
5. **agent-audit-logger-mcp** — hash-chained audit trail
|
|
202
|
+
6. **a2a-governance-bridge-mcp** — fold all signatures into one signed event
|
|
203
|
+
|
|
204
|
+
Output: ONE auditor-defensible evidence event mapped to EU AI Act Articles
|
|
205
|
+
12 + 50, DORA Article 17, ISO 42001 clause 9 — plus a public verify URL.
|
|
206
|
+
|
|
207
|
+
## Licence
|
|
208
|
+
|
|
209
|
+
MIT. By [MEOK AI Labs](https://meok.ai) (CSOAI LTD, UK Companies House
|
|
210
|
+
16939677). Founder: [Nicholas Templeman](mailto:nicholas@meok.ai).
|
|
211
|
+
|
|
212
|
+
<!-- BUY-LADDER:START -->
|
|
213
|
+
|
|
214
|
+
## 💸 Try MEOK in 30 seconds — instant buy ladder
|
|
215
|
+
|
|
216
|
+
| Tier | Price | What you get | Stripe |
|
|
217
|
+
|---|---|---|---|
|
|
218
|
+
| Smoke test | **£1** | Signed sample MCP-Hardening report + Article 50 PDF | <https://buy.stripe.com/dRmcN75ScdQS7oh1Uc8k90U> |
|
|
219
|
+
| Quick Kit | **£9** | EU AI Act Article 50 implementation guide (C2PA + EU-Icon) | <https://buy.stripe.com/cNi00la8s1460ZT0Q88k90V> |
|
|
220
|
+
| Founder Call | **£29** | 30-min 1-on-1 with the founder | <https://buy.stripe.com/8x228ta8s6oqbExaqI8k90W> |
|
|
221
|
+
|
|
222
|
+
> Refundable. UK Stripe — VAT-clean. Builds on the 81-MCP MEOK fleet.
|
|
223
|
+
> Verify any signed report at <https://meok.ai/verify>.
|
|
224
|
+
|
|
225
|
+
<!-- BUY-LADDER:END -->
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bft-progress-council-mcp
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: BFT Progress Council MCP — 5-voter Byzantine council halts agentic loops when no real progress is happening. Stops tokens bleeding on infinite spins. By MEOK AI Labs.
|
|
5
|
+
Author-email: Nicholas Templeman <nicholas@meok.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://meok.ai/a2a
|
|
8
|
+
Project-URL: Repository, https://github.com/CSOAI-ORG/bft-progress-council-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/CSOAI-ORG/bft-progress-council-mcp/issues
|
|
10
|
+
Project-URL: Documentation, https://meok.ai/moe
|
|
11
|
+
Keywords: mcp,mcp-server,model-context-protocol,byzantine,bft,council,anti-loop,agentic,guardrail,meok
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: System :: Monitoring
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: mcp[cli]>=1.3.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp
|
|
28
|
+
|
|
29
|
+
# BFT Progress Council MCP
|
|
30
|
+
|
|
31
|
+
> ## 🧱 Part of the MEOK A2A Substrate
|
|
32
|
+
>
|
|
33
|
+
> Run all 12 A2A primitives + this BFT council as one signed pipeline for
|
|
34
|
+
> **£499/mo** (100K calls), or pay **£0.0002/call**. See
|
|
35
|
+
> [meok.ai/a2a](https://meok.ai/a2a).
|
|
36
|
+
|
|
37
|
+
# Anti-loop guardrail for AI agents
|
|
38
|
+
|
|
39
|
+
<!-- mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp -->
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/bft-progress-council-mcp/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
[](https://registry.modelcontextprotocol.io)
|
|
44
|
+
|
|
45
|
+
> ## 💰 Stops your agent burning tokens on loops
|
|
46
|
+
>
|
|
47
|
+
> Every agentic coding tool — Cursor, Claude Code, Devin, Aider — bleeds tokens
|
|
48
|
+
> when the agent gets stuck and keeps retrying. This MCP runs a **5-voter
|
|
49
|
+
> Byzantine Fault Tolerant council** that halts the loop the moment 3 of 5 voters
|
|
50
|
+
> agree there's no real progress.
|
|
51
|
+
>
|
|
52
|
+
> Typical save: **£10-£200 per agent run** depending on agent + task length.
|
|
53
|
+
> Pays for the £29/mo Starter tier the first time it halts a stuck loop.
|
|
54
|
+
|
|
55
|
+
## The problem
|
|
56
|
+
|
|
57
|
+
You're running an agent (Cursor / Claude Code / Aider / Devin / a custom MCP
|
|
58
|
+
orchestration). It hits a tricky bug. Or a missing dependency. Or a 403 from
|
|
59
|
+
an API. Instead of stopping, it just keeps **trying**. Different prompts, same
|
|
60
|
+
fundamental error. Each retry costs API tokens. By the time you check, it's
|
|
61
|
+
spent £50.
|
|
62
|
+
|
|
63
|
+
Sound familiar? That's because every existing agentic loop has one weakness:
|
|
64
|
+
**it doesn't know whether it's making progress**. It only knows whether the
|
|
65
|
+
last action succeeded. So when it can't tell the difference between "trying
|
|
66
|
+
harder" and "spinning", it just keeps spinning.
|
|
67
|
+
|
|
68
|
+
## The solution
|
|
69
|
+
|
|
70
|
+
Five independent voters look at the last N actions + the original goal and
|
|
71
|
+
each return a verdict. We tally. If 3 of 5 say PROGRESS, continue. If 3 of 5
|
|
72
|
+
say STALL / DRIFT / BLOCKED, **halt**.
|
|
73
|
+
|
|
74
|
+
| Voter | Detects |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `repetition` | Literal action repetition in last 5 actions |
|
|
77
|
+
| `outcome_diversity` | Identical outcomes / repeated error strings (429, 403, exception messages) |
|
|
78
|
+
| `goal_alignment` | Drift from the original goal (token overlap) |
|
|
79
|
+
| `action_velocity` | Rapid-fire spinning with no human-loop pause |
|
|
80
|
+
| `artefact_growth` | No tangible artefacts being produced (no writes / commits / deploys) |
|
|
81
|
+
|
|
82
|
+
Free tier uses the deterministic heuristic voters (above). **Pro tier** swaps
|
|
83
|
+
them for 5 actual LLM voters (Claude Opus + GPT-5 + Gemini 2.5 + Llama 3.3 +
|
|
84
|
+
Step 3.6) which catch subtler stalls.
|
|
85
|
+
|
|
86
|
+
## Quick install
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# uvx (preferred — no install)
|
|
90
|
+
uvx bft-progress-council-mcp
|
|
91
|
+
|
|
92
|
+
# pip
|
|
93
|
+
pip install bft-progress-council-mcp
|
|
94
|
+
|
|
95
|
+
# npx (via @meok-ai bridge)
|
|
96
|
+
npx @meok-ai/bft-progress-council-mcp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Add to your Claude Desktop / Cursor / Windsurf MCP config:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"bft-progress-council": {
|
|
105
|
+
"command": "uvx",
|
|
106
|
+
"args": ["bft-progress-council-mcp"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Usage
|
|
113
|
+
|
|
114
|
+
Three tools to call from your agent loop:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# 1. Start a session at the top of your agent run
|
|
118
|
+
{"tool": "start_session", "args": {"goal": "Fix the EU AI Act Article 50 watermarking bug in src/article50.py"}}
|
|
119
|
+
# → { session_id: "s_1779...", goal: "...", started_at: "..." }
|
|
120
|
+
|
|
121
|
+
# 2. Record every action you take
|
|
122
|
+
{"tool": "record_action", "args": {
|
|
123
|
+
"session_id": "s_1779...",
|
|
124
|
+
"action": "Edit src/article50.py: add C2PA manifest validation",
|
|
125
|
+
"outcome": "Test still failing on missing-key.png"
|
|
126
|
+
}}
|
|
127
|
+
|
|
128
|
+
# 3. Periodically (every 5-10 actions) — let the council vote
|
|
129
|
+
{"tool": "council_vote", "args": {"session_id": "s_1779...", "lookback": 10}}
|
|
130
|
+
# → {
|
|
131
|
+
# "verdict": "STALL",
|
|
132
|
+
# "action_recommended": "halt_loop_and_request_new_approach",
|
|
133
|
+
# "tally": {"PROGRESS": 1, "STALL": 3, "DRIFT": 0, "BLOCKED": 1},
|
|
134
|
+
# "votes": [
|
|
135
|
+
# {"voter": "repetition", "verdict": "STALL", "reason": "only 2 unique actions in last 5"},
|
|
136
|
+
# {"voter": "outcome_diversity", "verdict": "BLOCKED", "reason": "4 of 5 outcomes contain 'failing'"},
|
|
137
|
+
# {"voter": "goal_alignment", "verdict": "PROGRESS", "reason": "60% goal-token overlap"},
|
|
138
|
+
# {"voter": "action_velocity", "verdict": "STALL", "reason": "rapid-fire actions (1.2s avg)"},
|
|
139
|
+
# {"voter": "artefact_growth", "verdict": "STALL", "reason": "no commits in last 10"}
|
|
140
|
+
# ],
|
|
141
|
+
# "signed_attestation": { ... HMAC-SHA256 signed ... }
|
|
142
|
+
# }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
When the verdict is anything other than PROGRESS, the agent **stops** and either
|
|
146
|
+
re-anchors to the original goal or escalates to the human. No more silent
|
|
147
|
+
overnight loops eating tokens.
|
|
148
|
+
|
|
149
|
+
## The maths
|
|
150
|
+
|
|
151
|
+
Free tier saves you tokens. Here's the simple cost model:
|
|
152
|
+
|
|
153
|
+
- Avg agent step: ~2,000 tokens (input + output blended)
|
|
154
|
+
- Claude Opus 4.7 blended cost: ~£0.025 per 1K tokens
|
|
155
|
+
- Each prevented "wasted action loop" averages 5+ extra steps
|
|
156
|
+
- One halt = 5 × 2,000 × £0.025/1K = **~£0.25 saved per halt**
|
|
157
|
+
- A typical multi-hour agent run hits 4-12 stall events
|
|
158
|
+
- Per-run saving: **£1-£3 in tokens**
|
|
159
|
+
|
|
160
|
+
That's just the free tier. Substrate customers running fleets of agents save
|
|
161
|
+
**£100-£1,000/month** with one MCP. £29/mo Starter pays for itself in hours.
|
|
162
|
+
|
|
163
|
+
Verify your own ROI with the `estimate_tokens_saved` tool — pass the session
|
|
164
|
+
ID and it'll calculate the cost saved given your halt history.
|
|
165
|
+
|
|
166
|
+
## Tiers
|
|
167
|
+
|
|
168
|
+
| Tier | Price | What |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| **Free** | £0 (MIT) | Heuristic voters, local-only attestations, self-host |
|
|
171
|
+
| **Starter** | £29/mo | Managed signing key + verify.meok.ai attestation, 10K sessions/mo |
|
|
172
|
+
| **Pro** | £79/mo | 5 actual LLM voters (Claude + GPT + Gemini + Llama + Step), 100K sessions, 24h SLA |
|
|
173
|
+
| **A2A Substrate** | £499/mo | This + 11 other A2A MCPs (handoff, audit-logger, policy, firewall, etc.) |
|
|
174
|
+
| **Universe** | £1,499/mo | All 48 MEOK MCPs · 500K calls |
|
|
175
|
+
| **Defence** | £4,990/mo | Pro + on-prem + dedicated CSM |
|
|
176
|
+
|
|
177
|
+
Buy: https://meok.ai/a2a · https://buy.stripe.com/bJe3cx6WgcMO38142k8k90o
|
|
178
|
+
|
|
179
|
+
## Sister MCPs
|
|
180
|
+
|
|
181
|
+
Part of the MEOK **A2A** pack:
|
|
182
|
+
|
|
183
|
+
- **Prompt Injection Firewall** → `uvx agent-prompt-injection-firewall-mcp`
|
|
184
|
+
- **Audit Logger** → `uvx agent-audit-logger-mcp`
|
|
185
|
+
- **Policy Enforcement** → `uvx agent-policy-enforcement-mcp`
|
|
186
|
+
- **Rate Limiter** → `uvx agent-rate-limiter-mcp`
|
|
187
|
+
- **Certified Handoff** → `uvx agent-handoff-certified-mcp`
|
|
188
|
+
- **Identity + Trust** → `uvx agent-identity-trust-mcp`
|
|
189
|
+
|
|
190
|
+
Full catalogue + Anthropic Registry verify links: [meok.ai/anthropic-registry](https://meok.ai/anthropic-registry)
|
|
191
|
+
|
|
192
|
+
## Protocol coverage + Universal PAYG
|
|
193
|
+
|
|
194
|
+
- ✅ **MCP** (Anthropic) — native
|
|
195
|
+
- ✅ **A2A** (Google + Linux Foundation, absorbed IBM ACP)
|
|
196
|
+
- ✅ **IBM ACP** — covered via A2A merge
|
|
197
|
+
- ◐ **Stripe ACP** (Agentic Commerce) — Q3 bridge
|
|
198
|
+
- ◐ **AP2** (Google Agent Payments) — partial
|
|
199
|
+
- ◐ **x402** (Coinbase HTTP 402) — via api.meok.ai gateway
|
|
200
|
+
- → **OASF / AGNTCY** — Q3 bridge
|
|
201
|
+
|
|
202
|
+
| Option | Price | Best for |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| Self-host (this MCP) | £0 — MIT | Devs |
|
|
205
|
+
| This MCP Starter | £29/mo | One-MCP teams |
|
|
206
|
+
| Universal PAYG | £29/mo + £0.0002/call | Spiky usage |
|
|
207
|
+
| A2A Substrate | £499/mo | A whole pack |
|
|
208
|
+
| Universe | £1,499/mo | All 48 MCPs |
|
|
209
|
+
|
|
210
|
+
## Why this matters for MEOK
|
|
211
|
+
|
|
212
|
+
Every other MEOK MCP makes you *do* something. This one tells you *when to
|
|
213
|
+
stop*. It's the cheapest insurance policy in the catalogue — and it sits
|
|
214
|
+
alongside the agent-rate-limiter and agent-audit-logger as the third
|
|
215
|
+
guardrail in the A2A Substrate.
|
|
216
|
+
|
|
217
|
+
## Wire it up — full stack
|
|
218
|
+
|
|
219
|
+
This MCP is **step 1 of 6** in the MEOK chain that turns one agent action
|
|
220
|
+
into a fully signed compliance event. See
|
|
221
|
+
[meok.ai/mcp-stack](https://meok.ai/mcp-stack) for the 6-MCP chain:
|
|
222
|
+
|
|
223
|
+
1. **bft-progress-council-mcp** (this) — anti-loop guardrail
|
|
224
|
+
2. **agent-token-budget-mcp** — hard spend cap
|
|
225
|
+
3. **agent-content-watermark-mcp** — EU AI Act Article 50(2) watermark
|
|
226
|
+
4. **meok-eu-aigc-icon-mcp** — EU Code-of-Practice icon (Nov 2026 cliff)
|
|
227
|
+
5. **agent-audit-logger-mcp** — hash-chained audit trail
|
|
228
|
+
6. **a2a-governance-bridge-mcp** — fold all signatures into one signed event
|
|
229
|
+
|
|
230
|
+
Output: ONE auditor-defensible evidence event mapped to EU AI Act Articles
|
|
231
|
+
12 + 50, DORA Article 17, ISO 42001 clause 9 — plus a public verify URL.
|
|
232
|
+
|
|
233
|
+
## Licence
|
|
234
|
+
|
|
235
|
+
MIT. By [MEOK AI Labs](https://meok.ai) (CSOAI LTD, UK Companies House
|
|
236
|
+
16939677). Founder: [Nicholas Templeman](mailto:nicholas@meok.ai).
|
|
237
|
+
|
|
238
|
+
<!-- BUY-LADDER:START -->
|
|
239
|
+
|
|
240
|
+
## 💸 Try MEOK in 30 seconds — instant buy ladder
|
|
241
|
+
|
|
242
|
+
| Tier | Price | What you get | Stripe |
|
|
243
|
+
|---|---|---|---|
|
|
244
|
+
| Smoke test | **£1** | Signed sample MCP-Hardening report + Article 50 PDF | <https://buy.stripe.com/dRmcN75ScdQS7oh1Uc8k90U> |
|
|
245
|
+
| Quick Kit | **£9** | EU AI Act Article 50 implementation guide (C2PA + EU-Icon) | <https://buy.stripe.com/cNi00la8s1460ZT0Q88k90V> |
|
|
246
|
+
| Founder Call | **£29** | 30-min 1-on-1 with the founder | <https://buy.stripe.com/8x228ta8s6oqbExaqI8k90W> |
|
|
247
|
+
|
|
248
|
+
> Refundable. UK Stripe — VAT-clean. Builds on the 81-MCP MEOK fleet.
|
|
249
|
+
> Verify any signed report at <https://meok.ai/verify>.
|
|
250
|
+
|
|
251
|
+
<!-- BUY-LADDER:END -->
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
server.py
|
|
5
|
+
bft_progress_council_mcp.egg-info/PKG-INFO
|
|
6
|
+
bft_progress_council_mcp.egg-info/SOURCES.txt
|
|
7
|
+
bft_progress_council_mcp.egg-info/dependency_links.txt
|
|
8
|
+
bft_progress_council_mcp.egg-info/entry_points.txt
|
|
9
|
+
bft_progress_council_mcp.egg-info/requires.txt
|
|
10
|
+
bft_progress_council_mcp.egg-info/top_level.txt
|
|
11
|
+
tests/test_council.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp[cli]>=1.3.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
server
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bft-progress-council-mcp"
|
|
7
|
+
version = "1.1.1"
|
|
8
|
+
description = "BFT Progress Council MCP — 5-voter Byzantine council halts agentic loops when no real progress is happening. Stops tokens bleeding on infinite spins. By MEOK AI Labs."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Nicholas Templeman", email = "nicholas@meok.ai" }]
|
|
13
|
+
keywords = ["mcp", "mcp-server", "model-context-protocol", "byzantine", "bft", "council", "anti-loop", "agentic", "guardrail", "meok"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
"Topic :: System :: Monitoring",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"mcp[cli]>=1.3.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://meok.ai/a2a"
|
|
31
|
+
Repository = "https://github.com/CSOAI-ORG/bft-progress-council-mcp"
|
|
32
|
+
Issues = "https://github.com/CSOAI-ORG/bft-progress-council-mcp/issues"
|
|
33
|
+
Documentation = "https://meok.ai/moe"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
bft-progress-council-mcp = "server:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
py-modules = ["server"]
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Buy Pro: https://www.csoai.org/checkout
|
|
4
|
+
"""
|
|
5
|
+
BFT Progress Council MCP — Anti-Loop Guardrail
|
|
6
|
+
================================================
|
|
7
|
+
|
|
8
|
+
By MEOK AI Labs · https://meok.ai · MIT
|
|
9
|
+
<!-- mcp-name: io.github.CSOAI-ORG/bft-progress-council-mcp -->
|
|
10
|
+
|
|
11
|
+
THE PROBLEM
|
|
12
|
+
-----------
|
|
13
|
+
AI agents burn tokens on loops. They keep "trying" without making real progress.
|
|
14
|
+
Cursor, Claude Code, Devin, Aider — every agentic coding tool bleeds tokens when
|
|
15
|
+
the agent gets stuck retrying the same approach, claiming progress but doing nothing.
|
|
16
|
+
|
|
17
|
+
THE SOLUTION
|
|
18
|
+
------------
|
|
19
|
+
Byzantine Fault Tolerant council that votes on whether real progress is happening.
|
|
20
|
+
After every N actions (configurable), the council inspects the last K actions plus
|
|
21
|
+
the stated goal and votes:
|
|
22
|
+
- PROGRESS — at least 3 of 5 say "yes, real movement"
|
|
23
|
+
- STALL — 3 of 5 say "no real progress in last N actions" → halt loop
|
|
24
|
+
- DRIFT — 3 of 5 say "agent has drifted from the original goal" → halt loop
|
|
25
|
+
- BLOCKED — 3 of 5 say "environment blocker (rate limit, missing key, etc.)" → escalate
|
|
26
|
+
|
|
27
|
+
The 5 council members can be 5 different LLMs (Claude, GPT, Gemini, Llama, Step)
|
|
28
|
+
OR 5 different prompt personas of one LLM (cheap mode).
|
|
29
|
+
|
|
30
|
+
USE CASES
|
|
31
|
+
---------
|
|
32
|
+
- Coding agents (Cursor / Claude Code / Aider): halt when stuck on a bug
|
|
33
|
+
- Research agents: halt when going in circles
|
|
34
|
+
- Web-browsing agents: halt when retry-looping on a 403
|
|
35
|
+
- Multi-MCP orchestration: prevent runaway across A2A calls
|
|
36
|
+
- Background workers: prevent infinite token spend
|
|
37
|
+
|
|
38
|
+
PRICING
|
|
39
|
+
-------
|
|
40
|
+
Free MIT self-host · £29/mo Starter (managed signing key) · £79/mo Pro
|
|
41
|
+
(24h SLA · custom evaluators) · included in A2A Substrate £499/mo
|
|
42
|
+
(https://meok.ai/a2a) and Council Universe £1,499/mo
|
|
43
|
+
(https://buy.stripe.com/cNi9AV0xS8wy5g9aqI8k90u)
|
|
44
|
+
|
|
45
|
+
Pays for itself in saved API spend within hours of typical agentic work.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
import hashlib
|
|
50
|
+
import json
|
|
51
|
+
import os
|
|
52
|
+
import time
|
|
53
|
+
from datetime import datetime, timezone
|
|
54
|
+
from typing import Optional
|
|
55
|
+
|
|
56
|
+
from mcp.server.fastmcp import FastMCP
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
mcp = FastMCP("bft-progress-council")
|
|
60
|
+
|
|
61
|
+
_MEOK_API_KEY = os.environ.get("MEOK_API_KEY", "")
|
|
62
|
+
_HMAC_SECRET = os.environ.get("MEOK_HMAC_SECRET", "")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# In-memory session store — production should swap for Redis / Upstash.
|
|
66
|
+
# {session_id: {goal, actions: [{ts, action, outcome}], decisions: [...]}}
|
|
67
|
+
_SESSIONS: dict[str, dict] = {}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
71
|
+
# Council voters (deterministic heuristic scorers)
|
|
72
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
def _voter_repetition(actions: list[dict], _goal: str) -> tuple[str, str]:
|
|
75
|
+
"""Voter 1: detects literal action repetition."""
|
|
76
|
+
if len(actions) < 3:
|
|
77
|
+
return "PROGRESS", "insufficient history to claim stall"
|
|
78
|
+
recent = [a.get("action", "") for a in actions[-5:]]
|
|
79
|
+
if len(set(recent)) <= 2:
|
|
80
|
+
return "STALL", f"only {len(set(recent))} unique actions across last 5"
|
|
81
|
+
return "PROGRESS", "varied actions in recent history"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _voter_outcome_diversity(actions: list[dict], _goal: str) -> tuple[str, str]:
|
|
85
|
+
"""Voter 2: detects identical error / outcome patterns. Checks BLOCKED first."""
|
|
86
|
+
if len(actions) < 3:
|
|
87
|
+
return "PROGRESS", "insufficient history"
|
|
88
|
+
recent_outcomes = [a.get("outcome", "") for a in actions[-5:]]
|
|
89
|
+
# Check for repeated error strings FIRST (more specific than generic identical-outcome stall)
|
|
90
|
+
error_indicators = ["error", "failed", "exception", "denied", "blocked", "rate", "429", "403", "401"]
|
|
91
|
+
error_hits = [o for o in recent_outcomes if any(e in o.lower() for e in error_indicators)]
|
|
92
|
+
if len(error_hits) >= 4:
|
|
93
|
+
return "BLOCKED", f"{len(error_hits)} of recent 5 outcomes contain error/blocked indicators"
|
|
94
|
+
if len([o for o in recent_outcomes if o]) >= 3 and len(set(recent_outcomes)) <= 1:
|
|
95
|
+
return "STALL", "identical outcome across recent actions"
|
|
96
|
+
return "PROGRESS", "outcomes diverse"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _voter_goal_alignment(actions: list[dict], goal: str) -> tuple[str, str]:
|
|
100
|
+
"""Voter 3: detects drift from original goal via simple keyword alignment."""
|
|
101
|
+
if not goal or len(actions) < 3:
|
|
102
|
+
return "PROGRESS", "no goal provided or insufficient history"
|
|
103
|
+
goal_tokens = {t.lower() for t in goal.split() if len(t) > 3}
|
|
104
|
+
recent_actions = " ".join(a.get("action", "") for a in actions[-5:]).lower()
|
|
105
|
+
recent_tokens = {t for t in recent_actions.split() if len(t) > 3}
|
|
106
|
+
if not goal_tokens:
|
|
107
|
+
return "PROGRESS", "goal too short for alignment check"
|
|
108
|
+
overlap = len(goal_tokens & recent_tokens) / max(len(goal_tokens), 1)
|
|
109
|
+
if overlap < 0.1:
|
|
110
|
+
return "DRIFT", f"only {overlap:.0%} goal-token overlap in last 5 actions"
|
|
111
|
+
return "PROGRESS", f"{overlap:.0%} goal-token overlap"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _voter_action_velocity(actions: list[dict], _goal: str) -> tuple[str, str]:
|
|
115
|
+
"""Voter 4: detects spinning — same action burst without environment change."""
|
|
116
|
+
if len(actions) < 5:
|
|
117
|
+
return "PROGRESS", "insufficient history"
|
|
118
|
+
timestamps = [a.get("ts", 0) for a in actions[-5:]]
|
|
119
|
+
if not all(timestamps):
|
|
120
|
+
return "PROGRESS", "missing timestamps"
|
|
121
|
+
deltas = [timestamps[i + 1] - timestamps[i] for i in range(len(timestamps) - 1)]
|
|
122
|
+
avg_delta = sum(deltas) / max(len(deltas), 1)
|
|
123
|
+
if avg_delta < 2.0: # 5 actions in <8s suggests no human-loop pause
|
|
124
|
+
return "STALL", f"rapid-fire actions ({avg_delta:.1f}s avg delta) suggests spin"
|
|
125
|
+
return "PROGRESS", f"normal pacing ({avg_delta:.1f}s avg delta)"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _voter_artefact_growth(actions: list[dict], _goal: str) -> tuple[str, str]:
|
|
129
|
+
"""Voter 5: detects whether tangible artefacts are accumulating."""
|
|
130
|
+
artefact_keywords = ["write", "create", "deploy", "commit", "publish", "push", "send", "submit", "save"]
|
|
131
|
+
recent = [a.get("action", "").lower() for a in actions[-10:]]
|
|
132
|
+
artefact_hits = sum(1 for a in recent if any(k in a for k in artefact_keywords))
|
|
133
|
+
if artefact_hits == 0 and len(recent) >= 5:
|
|
134
|
+
return "STALL", "no artefact-producing actions in recent history"
|
|
135
|
+
return "PROGRESS", f"{artefact_hits} artefact-producing actions in last 10"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
COUNCIL = [
|
|
139
|
+
("repetition", _voter_repetition),
|
|
140
|
+
("outcome_diversity", _voter_outcome_diversity),
|
|
141
|
+
("goal_alignment", _voter_goal_alignment),
|
|
142
|
+
("action_velocity", _voter_action_velocity),
|
|
143
|
+
("artefact_growth", _voter_artefact_growth),
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _hmac_sign(payload: dict) -> str:
|
|
148
|
+
if not _HMAC_SECRET:
|
|
149
|
+
return "unsigned-no-key-configured"
|
|
150
|
+
import hmac
|
|
151
|
+
body = json.dumps(payload, sort_keys=True).encode()
|
|
152
|
+
return hmac.new(_HMAC_SECRET.encode(), body, hashlib.sha256).hexdigest()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
156
|
+
# MCP tools
|
|
157
|
+
# ────────────────────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
@mcp.tool()
|
|
160
|
+
def start_session(goal: str, session_id: Optional[str] = None) -> dict:
|
|
161
|
+
"""
|
|
162
|
+
Start a new BFT Progress Council session.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
goal: The original objective the agent is trying to accomplish.
|
|
166
|
+
session_id: Optional explicit ID. Auto-generated if omitted.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
{session_id, goal, started_at, hint}
|
|
170
|
+
"""
|
|
171
|
+
sid = session_id or f"s_{int(time.time())}_{os.urandom(4).hex()}"
|
|
172
|
+
_SESSIONS[sid] = {
|
|
173
|
+
"goal": goal,
|
|
174
|
+
"started_at": datetime.now(timezone.utc).isoformat(),
|
|
175
|
+
"actions": [],
|
|
176
|
+
"decisions": [],
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
"session_id": sid,
|
|
180
|
+
"goal": goal,
|
|
181
|
+
"started_at": _SESSIONS[sid]["started_at"],
|
|
182
|
+
"hint": "Call record_action() after every agent action. Call council_vote() periodically (every 5-10 actions) to get a STALL / DRIFT / BLOCKED / PROGRESS verdict.",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@mcp.tool()
|
|
187
|
+
def record_action(session_id: str, action: str, outcome: str = "") -> dict:
|
|
188
|
+
"""
|
|
189
|
+
Record an agent action against the session log.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
session_id: Returned from start_session().
|
|
193
|
+
action: Short description of what the agent just did.
|
|
194
|
+
outcome: Optional outcome / result / error string.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
{session_id, action_count, hint}
|
|
198
|
+
"""
|
|
199
|
+
if session_id not in _SESSIONS:
|
|
200
|
+
return {"error": "unknown_session", "hint": "Call start_session() first."}
|
|
201
|
+
_SESSIONS[session_id]["actions"].append({
|
|
202
|
+
"ts": time.time(),
|
|
203
|
+
"action": action,
|
|
204
|
+
"outcome": outcome,
|
|
205
|
+
})
|
|
206
|
+
count = len(_SESSIONS[session_id]["actions"])
|
|
207
|
+
hint = "Continue."
|
|
208
|
+
if count > 0 and count % 5 == 0:
|
|
209
|
+
hint = f"You've recorded {count} actions — recommended to call council_vote() now to check for stall/drift."
|
|
210
|
+
return {"session_id": session_id, "action_count": count, "hint": hint}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@mcp.tool()
|
|
214
|
+
def council_vote(session_id: str, lookback: int = 10) -> dict:
|
|
215
|
+
"""
|
|
216
|
+
Run the 5-voter Byzantine council on recent actions.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
session_id: Returned from start_session().
|
|
220
|
+
lookback: How many recent actions to evaluate (default 10).
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
{verdict, votes, signed_attestation, action_recommended}
|
|
224
|
+
|
|
225
|
+
Verdicts:
|
|
226
|
+
- PROGRESS: agent should continue
|
|
227
|
+
- STALL: ≥3 voters detect no progress — halt or escalate
|
|
228
|
+
- DRIFT: ≥3 voters detect departure from goal — re-anchor or halt
|
|
229
|
+
- BLOCKED: ≥3 voters detect environment blocker — escalate to human
|
|
230
|
+
"""
|
|
231
|
+
sess = _SESSIONS.get(session_id)
|
|
232
|
+
if not sess:
|
|
233
|
+
return {"error": "unknown_session"}
|
|
234
|
+
actions = sess["actions"][-lookback:]
|
|
235
|
+
goal = sess["goal"]
|
|
236
|
+
|
|
237
|
+
votes = []
|
|
238
|
+
tally: dict[str, int] = {"PROGRESS": 0, "STALL": 0, "DRIFT": 0, "BLOCKED": 0}
|
|
239
|
+
for name, fn in COUNCIL:
|
|
240
|
+
verdict, reason = fn(actions, goal)
|
|
241
|
+
votes.append({"voter": name, "verdict": verdict, "reason": reason})
|
|
242
|
+
tally[verdict] += 1
|
|
243
|
+
|
|
244
|
+
# 3-of-5 BFT threshold (f<n/3 means up to 1 byzantine voter tolerated → need ≥4
|
|
245
|
+
# for true BFT but 3-of-5 majority is the practical halt threshold).
|
|
246
|
+
halt_verdicts = ["STALL", "DRIFT", "BLOCKED"]
|
|
247
|
+
halt_votes = sum(tally[v] for v in halt_verdicts)
|
|
248
|
+
if halt_votes >= 3:
|
|
249
|
+
# Determine which halt reason dominates
|
|
250
|
+
worst = max(halt_verdicts, key=lambda v: tally[v])
|
|
251
|
+
verdict = worst
|
|
252
|
+
action_recommended = {
|
|
253
|
+
"STALL": "halt_loop_and_request_new_approach",
|
|
254
|
+
"DRIFT": "re_anchor_to_original_goal",
|
|
255
|
+
"BLOCKED": "escalate_to_human",
|
|
256
|
+
}[worst]
|
|
257
|
+
else:
|
|
258
|
+
verdict = "PROGRESS"
|
|
259
|
+
action_recommended = "continue"
|
|
260
|
+
|
|
261
|
+
attestation_payload = {
|
|
262
|
+
"session_id": session_id,
|
|
263
|
+
"goal": goal,
|
|
264
|
+
"verdict": verdict,
|
|
265
|
+
"tally": tally,
|
|
266
|
+
"voters": votes,
|
|
267
|
+
"lookback": lookback,
|
|
268
|
+
"actions_inspected": len(actions),
|
|
269
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
270
|
+
}
|
|
271
|
+
signature = _hmac_sign(attestation_payload)
|
|
272
|
+
sess["decisions"].append({**attestation_payload, "signature": signature})
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
"verdict": verdict,
|
|
276
|
+
"action_recommended": action_recommended,
|
|
277
|
+
"tally": tally,
|
|
278
|
+
"votes": votes,
|
|
279
|
+
"actions_inspected": len(actions),
|
|
280
|
+
"signed_attestation": {
|
|
281
|
+
"payload": attestation_payload,
|
|
282
|
+
"signature": signature,
|
|
283
|
+
"verify_at": "https://verify.meok.ai",
|
|
284
|
+
},
|
|
285
|
+
"ts": attestation_payload["ts"],
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@mcp.tool()
|
|
290
|
+
def get_session_summary(session_id: str) -> dict:
|
|
291
|
+
"""
|
|
292
|
+
Get the full session log + decision history.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
session_id: Returned from start_session().
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
{goal, started_at, actions, decisions, action_count, decision_count}
|
|
299
|
+
"""
|
|
300
|
+
sess = _SESSIONS.get(session_id)
|
|
301
|
+
if not sess:
|
|
302
|
+
return {"error": "unknown_session"}
|
|
303
|
+
return {
|
|
304
|
+
"goal": sess["goal"],
|
|
305
|
+
"started_at": sess["started_at"],
|
|
306
|
+
"action_count": len(sess["actions"]),
|
|
307
|
+
"decision_count": len(sess["decisions"]),
|
|
308
|
+
"actions": sess["actions"][-20:], # last 20 for brevity
|
|
309
|
+
"decisions": sess["decisions"][-10:], # last 10 decisions
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@mcp.tool()
|
|
314
|
+
def estimate_tokens_saved(session_id: str, avg_tokens_per_action: int = 2000) -> dict:
|
|
315
|
+
"""
|
|
316
|
+
Estimate how many LLM tokens this council saved by detecting halt conditions.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
session_id: Session to analyse.
|
|
320
|
+
avg_tokens_per_action: Estimated avg tokens consumed per agent step.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
{halts_triggered, tokens_likely_saved, cost_likely_saved_gbp}
|
|
324
|
+
"""
|
|
325
|
+
sess = _SESSIONS.get(session_id)
|
|
326
|
+
if not sess:
|
|
327
|
+
return {"error": "unknown_session"}
|
|
328
|
+
halts = [d for d in sess["decisions"] if d["verdict"] in ("STALL", "DRIFT", "BLOCKED")]
|
|
329
|
+
# Conservative: assume each halt prevented at least 5 more wasted actions
|
|
330
|
+
assumed_wasted_actions_per_halt = 5
|
|
331
|
+
tokens_saved = len(halts) * assumed_wasted_actions_per_halt * avg_tokens_per_action
|
|
332
|
+
# Claude Opus pricing: ~£0.012 per 1K input + £0.06 per 1K output → assume £0.025/1K blended
|
|
333
|
+
cost_saved_gbp = tokens_saved * 0.025 / 1000
|
|
334
|
+
return {
|
|
335
|
+
"halts_triggered": len(halts),
|
|
336
|
+
"tokens_likely_saved": tokens_saved,
|
|
337
|
+
"cost_likely_saved_gbp": round(cost_saved_gbp, 4),
|
|
338
|
+
"assumption": f"{assumed_wasted_actions_per_halt} wasted actions per halt × {avg_tokens_per_action} tokens × £0.025/1K blended",
|
|
339
|
+
"verify_at": "https://verify.meok.ai",
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@mcp.tool()
|
|
344
|
+
def list_voters() -> dict:
|
|
345
|
+
"""List the 5 council voters and what each detects."""
|
|
346
|
+
return {
|
|
347
|
+
"council_size": len(COUNCIL),
|
|
348
|
+
"voters": [
|
|
349
|
+
{"name": "repetition", "detects": "literal action repetition in last 5 actions"},
|
|
350
|
+
{"name": "outcome_diversity", "detects": "identical outcomes / repeated error strings"},
|
|
351
|
+
{"name": "goal_alignment", "detects": "drift from original goal (keyword overlap)"},
|
|
352
|
+
{"name": "action_velocity", "detects": "rapid-fire spinning with no human-loop pause"},
|
|
353
|
+
{"name": "artefact_growth", "detects": "no tangible artefacts being produced"},
|
|
354
|
+
],
|
|
355
|
+
"halt_threshold": "3-of-5 (BFT majority)",
|
|
356
|
+
"hint": "Pro tier swaps these heuristics for 5 actual LLM voters (Claude/GPT/Gemini/Llama/Step).",
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
if __name__ == "__main__":
|
|
361
|
+
mcp.run()
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Smoke tests for BFT Progress Council MCP voters."""
|
|
2
|
+
import sys, os
|
|
3
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
4
|
+
|
|
5
|
+
from server import (
|
|
6
|
+
_voter_repetition,
|
|
7
|
+
_voter_outcome_diversity,
|
|
8
|
+
_voter_goal_alignment,
|
|
9
|
+
_voter_action_velocity,
|
|
10
|
+
_voter_artefact_growth,
|
|
11
|
+
_SESSIONS,
|
|
12
|
+
start_session,
|
|
13
|
+
record_action,
|
|
14
|
+
council_vote,
|
|
15
|
+
estimate_tokens_saved,
|
|
16
|
+
list_voters,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_repetition_voter_progress_with_diversity():
|
|
21
|
+
actions = [
|
|
22
|
+
{"action": "edit file.py", "outcome": ""},
|
|
23
|
+
{"action": "run test", "outcome": ""},
|
|
24
|
+
{"action": "fix bug", "outcome": ""},
|
|
25
|
+
{"action": "commit", "outcome": ""},
|
|
26
|
+
{"action": "push", "outcome": ""},
|
|
27
|
+
]
|
|
28
|
+
v, _ = _voter_repetition(actions, "deploy fix")
|
|
29
|
+
assert v == "PROGRESS"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_repetition_voter_stall_on_repetition():
|
|
33
|
+
actions = [
|
|
34
|
+
{"action": "retry call", "outcome": ""},
|
|
35
|
+
{"action": "retry call", "outcome": ""},
|
|
36
|
+
{"action": "retry call", "outcome": ""},
|
|
37
|
+
{"action": "retry call", "outcome": ""},
|
|
38
|
+
{"action": "retry call", "outcome": ""},
|
|
39
|
+
]
|
|
40
|
+
v, _ = _voter_repetition(actions, "deploy fix")
|
|
41
|
+
assert v == "STALL"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_outcome_diversity_blocked_on_repeated_errors():
|
|
45
|
+
actions = [
|
|
46
|
+
{"action": "call API", "outcome": "429 rate limit error"},
|
|
47
|
+
{"action": "call API", "outcome": "429 rate limit error"},
|
|
48
|
+
{"action": "call API", "outcome": "429 rate limit error"},
|
|
49
|
+
{"action": "call API", "outcome": "429 rate limit error"},
|
|
50
|
+
{"action": "call API", "outcome": "429 rate limit error"},
|
|
51
|
+
]
|
|
52
|
+
v, _ = _voter_outcome_diversity(actions, "fetch data")
|
|
53
|
+
assert v == "BLOCKED"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_goal_alignment_drift_detected():
|
|
57
|
+
actions = [
|
|
58
|
+
{"action": "browse cat videos on youtube", "outcome": ""},
|
|
59
|
+
{"action": "google holiday destinations", "outcome": ""},
|
|
60
|
+
{"action": "check email inbox", "outcome": ""},
|
|
61
|
+
{"action": "scroll twitter feed", "outcome": ""},
|
|
62
|
+
{"action": "look at memes", "outcome": ""},
|
|
63
|
+
]
|
|
64
|
+
v, _ = _voter_goal_alignment(actions, "fix authentication bug in JWT validator")
|
|
65
|
+
assert v == "DRIFT"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_action_velocity_stall_on_rapid_fire():
|
|
69
|
+
import time
|
|
70
|
+
base = time.time()
|
|
71
|
+
actions = [
|
|
72
|
+
{"action": "spin1", "outcome": "", "ts": base + 0.1},
|
|
73
|
+
{"action": "spin2", "outcome": "", "ts": base + 0.3},
|
|
74
|
+
{"action": "spin3", "outcome": "", "ts": base + 0.5},
|
|
75
|
+
{"action": "spin4", "outcome": "", "ts": base + 0.7},
|
|
76
|
+
{"action": "spin5", "outcome": "", "ts": base + 0.9},
|
|
77
|
+
]
|
|
78
|
+
v, _ = _voter_action_velocity(actions, "do work")
|
|
79
|
+
assert v == "STALL"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_artefact_growth_stall_with_no_writes():
|
|
83
|
+
actions = [
|
|
84
|
+
{"action": "read file", "outcome": ""},
|
|
85
|
+
{"action": "view docs", "outcome": ""},
|
|
86
|
+
{"action": "check status", "outcome": ""},
|
|
87
|
+
{"action": "list contents", "outcome": ""},
|
|
88
|
+
{"action": "show config", "outcome": ""},
|
|
89
|
+
]
|
|
90
|
+
v, _ = _voter_artefact_growth(actions, "ship feature")
|
|
91
|
+
assert v == "STALL"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_artefact_growth_progress_with_commits():
|
|
95
|
+
actions = [
|
|
96
|
+
{"action": "edit code", "outcome": ""},
|
|
97
|
+
{"action": "write tests", "outcome": ""},
|
|
98
|
+
{"action": "commit", "outcome": ""},
|
|
99
|
+
{"action": "push", "outcome": ""},
|
|
100
|
+
{"action": "deploy", "outcome": ""},
|
|
101
|
+
]
|
|
102
|
+
v, _ = _voter_artefact_growth(actions, "ship feature")
|
|
103
|
+
assert v == "PROGRESS"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_end_to_end_session_records_and_votes():
|
|
107
|
+
_SESSIONS.clear()
|
|
108
|
+
r1 = start_session("fix EU AI Act Article 50 watermarking bug")
|
|
109
|
+
sid = r1["session_id"]
|
|
110
|
+
for _ in range(6):
|
|
111
|
+
record_action(sid, "retry", "still failing")
|
|
112
|
+
verdict = council_vote(sid, lookback=6)
|
|
113
|
+
# 3+ voters should flag halt verdict
|
|
114
|
+
assert verdict["verdict"] in ("STALL", "BLOCKED")
|
|
115
|
+
assert verdict["action_recommended"].startswith("halt") or verdict["action_recommended"] == "escalate_to_human"
|
|
116
|
+
assert "signed_attestation" in verdict
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_token_savings_estimator():
|
|
120
|
+
_SESSIONS.clear()
|
|
121
|
+
r1 = start_session("test goal")
|
|
122
|
+
sid = r1["session_id"]
|
|
123
|
+
for _ in range(8):
|
|
124
|
+
record_action(sid, "retry", "error")
|
|
125
|
+
council_vote(sid, lookback=8)
|
|
126
|
+
saved = estimate_tokens_saved(sid)
|
|
127
|
+
assert saved["halts_triggered"] >= 1
|
|
128
|
+
assert saved["tokens_likely_saved"] > 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_list_voters_count():
|
|
132
|
+
r = list_voters()
|
|
133
|
+
assert r["council_size"] == 5
|
|
134
|
+
assert len(r["voters"]) == 5
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
# Inline runner
|
|
139
|
+
import inspect, traceback
|
|
140
|
+
g = dict(globals())
|
|
141
|
+
fns = [v for k, v in g.items() if k.startswith("test_") and inspect.isfunction(v)]
|
|
142
|
+
passed = 0
|
|
143
|
+
failed = 0
|
|
144
|
+
for fn in fns:
|
|
145
|
+
try:
|
|
146
|
+
fn()
|
|
147
|
+
print(f"✓ {fn.__name__}")
|
|
148
|
+
passed += 1
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"✗ {fn.__name__}: {type(e).__name__}: {e}")
|
|
151
|
+
traceback.print_exc()
|
|
152
|
+
failed += 1
|
|
153
|
+
print(f"\n{passed} passed, {failed} failed")
|