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.
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/bft-progress-council-mcp)](https://pypi.org/project/bft-progress-council-mcp/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
43
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-Published-green)](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
+ [![PyPI](https://img.shields.io/pypi/v/bft-progress-council-mcp)](https://pypi.org/project/bft-progress-council-mcp/)
16
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
17
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-Published-green)](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
+ [![PyPI](https://img.shields.io/pypi/v/bft-progress-council-mcp)](https://pypi.org/project/bft-progress-council-mcp/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
43
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-Published-green)](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,2 @@
1
+ [console_scripts]
2
+ bft-progress-council-mcp = server:main
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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")