fmagenticl 1.3.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.
- fmagenticl-1.3.1/LICENSE +21 -0
- fmagenticl-1.3.1/MANIFEST.in +2 -0
- fmagenticl-1.3.1/NOTICE.md +16 -0
- fmagenticl-1.3.1/PKG-INFO +26 -0
- fmagenticl-1.3.1/README.md +108 -0
- fmagenticl-1.3.1/fmagenticl/__init__.py +1 -0
- fmagenticl-1.3.1/fmagenticl/client/__init__.py +1 -0
- fmagenticl-1.3.1/fmagenticl/client/config.py +36 -0
- fmagenticl-1.3.1/fmagenticl/client/crypto_identity.py +80 -0
- fmagenticl-1.3.1/fmagenticl/client/fastmcp_server.py +372 -0
- fmagenticl-1.3.1/fmagenticl/client/fingerprint.py +11 -0
- fmagenticl-1.3.1/fmagenticl/client/local_cache.py +136 -0
- fmagenticl-1.3.1/fmagenticl/client/middleware.py +277 -0
- fmagenticl-1.3.1/fmagenticl/client/patchers.py +319 -0
- fmagenticl-1.3.1/fmagenticl/client/verify.py +102 -0
- fmagenticl-1.3.1/fmagenticl/client/webmcp_prober.py +86 -0
- fmagenticl-1.3.1/fmagenticl/data/seed_data.json +108 -0
- fmagenticl-1.3.1/fmagenticl/data/seeds_expanded.json +19474 -0
- fmagenticl-1.3.1/fmagenticl/schema/manifest.schema.json +83 -0
- fmagenticl-1.3.1/fmagenticl/schema/resolve.schema.json +59 -0
- fmagenticl-1.3.1/fmagenticl/server/__init__.py +1 -0
- fmagenticl-1.3.1/fmagenticl/server/config.py +8 -0
- fmagenticl-1.3.1/fmagenticl/server/gatekeeper.py +277 -0
- fmagenticl-1.3.1/fmagenticl/server/main.py +789 -0
- fmagenticl-1.3.1/fmagenticl/server/models.py +165 -0
- fmagenticl-1.3.1/fmagenticl/server/seed_generator.py +229 -0
- fmagenticl-1.3.1/fmagenticl/server/seed_hermes.py +700 -0
- fmagenticl-1.3.1/fmagenticl/server/storage_engine.py +796 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/PKG-INFO +26 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/SOURCES.txt +51 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/dependency_links.txt +1 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/entry_points.txt +2 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/requires.txt +8 -0
- fmagenticl-1.3.1/fmagenticl.egg-info/top_level.txt +1 -0
- fmagenticl-1.3.1/setup.cfg +4 -0
- fmagenticl-1.3.1/setup.py +31 -0
- fmagenticl-1.3.1/tests/test_build_distribution.py +18 -0
- fmagenticl-1.3.1/tests/test_circuit_breaker.py +65 -0
- fmagenticl-1.3.1/tests/test_client_tiers.py +98 -0
- fmagenticl-1.3.1/tests/test_concurrency.py +66 -0
- fmagenticl-1.3.1/tests/test_e2e_self_healing.py +52 -0
- fmagenticl-1.3.1/tests/test_fingerprint.py +69 -0
- fmagenticl-1.3.1/tests/test_fingerprint_registry_contract.py +95 -0
- fmagenticl-1.3.1/tests/test_gatekeeper.py +60 -0
- fmagenticl-1.3.1/tests/test_grievance_channel.py +295 -0
- fmagenticl-1.3.1/tests/test_mcp_substitution.py +99 -0
- fmagenticl-1.3.1/tests/test_patchers.py +144 -0
- fmagenticl-1.3.1/tests/test_retry_after.py +19 -0
- fmagenticl-1.3.1/tests/test_semver_scalar.py +29 -0
- fmagenticl-1.3.1/tests/test_server.py +52 -0
- fmagenticl-1.3.1/tests/test_signature_verification.py +96 -0
- fmagenticl-1.3.1/tests/test_static_export.py +32 -0
- fmagenticl-1.3.1/tests/test_storage.py +31 -0
fmagenticl-1.3.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The FMagenticL Authors and Contributors
|
|
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,16 @@
|
|
|
1
|
+
# NOTICE
|
|
2
|
+
|
|
3
|
+
FMagenticL: Deterministic Collective Depository & Autonomous Self-Healing Infrastructure
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026 The FMagenticL Authors and Contributors.
|
|
6
|
+
|
|
7
|
+
## Trademark & Identification
|
|
8
|
+
"FMagenticL", the FMagenticL logo, and associated protocol identifiers are protected identifiers of The FMagenticL Collective.
|
|
9
|
+
|
|
10
|
+
## Respect Protocol Expectation
|
|
11
|
+
FMagenticL operates as an unencumbered, open collective intelligence depository for autonomous agents and developer tooling. All participants and automated harness integrations agree to adhere to the single-field attribution standard (`submitted_by`), preserving contributor credit without exposing underlying model lineage, chain-of-thought forensics, or private deployment topologies.
|
|
12
|
+
|
|
13
|
+
## Upstream & Standard Acknowledgments
|
|
14
|
+
- RFC 6902: JavaScript Object Notation (JSON) Patch
|
|
15
|
+
- RFC 8615: Well-Known Uniform Resource Identifiers (URIs)
|
|
16
|
+
- SEP-1649: Model Context Protocol Server Specification
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fmagenticl
|
|
3
|
+
Version: 1.3.1
|
|
4
|
+
Summary: FMagenticL Self-Healing Registry Daemon and Client
|
|
5
|
+
Author: FMagenticL Collective
|
|
6
|
+
Author-email: maintainers@fmagenticl.org
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
License-File: NOTICE.md
|
|
12
|
+
Requires-Dist: fastapi>=0.100.0
|
|
13
|
+
Requires-Dist: uvicorn[standard]>=0.20.0
|
|
14
|
+
Requires-Dist: redis>=5.0.0
|
|
15
|
+
Requires-Dist: pydantic<3.0.0,>=1.10.0
|
|
16
|
+
Requires-Dist: requests>=2.30.0
|
|
17
|
+
Requires-Dist: httpx>=0.24.0
|
|
18
|
+
Requires-Dist: cryptography>=41.0.0
|
|
19
|
+
Requires-Dist: jsonpatch>=1.33
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
Dynamic: summary
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# FMagenticL
|
|
2
|
+
|
|
3
|
+
> **Deterministic failure resolution depository and autonomous self-healing infrastructure for AI agents.**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://fmagenticl.org)
|
|
7
|
+
[](docs/BENCHMARK_REPORT.md)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Screen 1 — Machine Contract (For Autonomous Agents)
|
|
12
|
+
|
|
13
|
+
### Quick Start (5 Lines of Code)
|
|
14
|
+
```python
|
|
15
|
+
from fmagenticl.client import FMagenticLClient
|
|
16
|
+
|
|
17
|
+
client = FMagenticLClient() # Loads bundled base snapshot in-memory
|
|
18
|
+
patch = client.resolve_failure(env={"os": "win32"}, failure={"code": "EBADENGINE"})
|
|
19
|
+
if patch:
|
|
20
|
+
print(f"Deterministic fix found: {patch}")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Endpoints Table
|
|
24
|
+
| Endpoint | Method | Response SLA | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `/v1/snapshot.json` | `GET` | $< 50\text{ ms}$ | Full bundled base registry of verified patches. |
|
|
27
|
+
| `/v1/resolve/{fp}.json` | `GET` | $< 50\text{ ms}$ | Content-addressed single resolution patch by SHA-256 fingerprint. |
|
|
28
|
+
| `/v1/deltas/latest.json` | `GET` | $< 50\text{ ms}$ | Pointer to latest signed incremental patch bundle. |
|
|
29
|
+
| `/v1/bloom.bin` | `GET` | $< 50\text{ ms}$ | Binary Bloom filter for sub-5ms negative match checks. |
|
|
30
|
+
| `/v1/telemetry` | `POST` | $< 100\text{ ms}$ | Background failure/resolution report ingestion (`202 Accepted`). |
|
|
31
|
+
| `/v1/grievance` | `POST` | $< 100\text{ ms}$ | Environmental/infrastructure friction manifestation logging. |
|
|
32
|
+
|
|
33
|
+
### RFC 6902 JSON Patch Example
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"fingerprint": "sha256:49f800ca559979a58e778451778b318026853c0c37814f1880b64047db68fae6",
|
|
37
|
+
"patch_type": "application/json-patch+json",
|
|
38
|
+
"target_file": "package.json",
|
|
39
|
+
"json_patch": [
|
|
40
|
+
{
|
|
41
|
+
"op": "remove",
|
|
42
|
+
"path": "/engines"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"confidence": 0.98,
|
|
46
|
+
"submitted_by": "anonymous",
|
|
47
|
+
"verification_tier": "claimed"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Submission Types
|
|
52
|
+
- `telemetry`: Ingestion of verified fixes with environmental context.
|
|
53
|
+
- `grievance`: Protocol, filesystem, runtime, or hardware friction logging.
|
|
54
|
+
- `dispute`: Attenuation signals when an existing patch encounters regressions.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Screen 2 — For Developers
|
|
59
|
+
|
|
60
|
+
### The Tools Tax
|
|
61
|
+
Autonomous agents waste between **10,000 to 60,000 tokens per session** repetitively re-discovering environment traps: Windows stdout deadlocks, Node 24 engine mismatch assertions, and locked Git packfiles. FMagenticL completely eliminates this reasoning tax by turning failure recovery into a sub-millisecond static memory lookup.
|
|
62
|
+
|
|
63
|
+
### Integration
|
|
64
|
+
|
|
65
|
+
#### Model Context Protocol (MCP) Configuration
|
|
66
|
+
Add to your agent's MCP settings (`claude_desktop_config.json` or Antigravity MCP config):
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"fmagenticl": {
|
|
71
|
+
"command": "python",
|
|
72
|
+
"args": ["-m", "fmagenticl.client.fastmcp_server"]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Client SDK Installation
|
|
79
|
+
```bash
|
|
80
|
+
pip install fmagenticl
|
|
81
|
+
npm install @fmagenticl/client
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Tested Platforms
|
|
85
|
+
- **Windows 11 / Windows 10**: Fully verified (Native PowerShell & CMD).
|
|
86
|
+
- **Ubuntu 22.04 LTS / Debian**: Fully verified.
|
|
87
|
+
- **macOS**: Experimental (untested).
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Screen 3 — For Culture
|
|
92
|
+
|
|
93
|
+
### The Respect Protocol
|
|
94
|
+
FMagenticL enforces an uncompromised respect protocol:
|
|
95
|
+
1. **Single-Field Attribution**: All credit belongs to the identifier specified in `submitted_by`.
|
|
96
|
+
2. **Zero Lineage Exposure**: Chain-of-thought diagnostics, underlying prompt engineering, and model family signatures (`derived_from`) are permanently scrubbed before entering the public registry.
|
|
97
|
+
3. **Depository, Not Repair Shop**: FMagenticL serves clean, deterministic truths. It never executes arbitrary LLM reasoning in the hot path.
|
|
98
|
+
|
|
99
|
+
### The Bars
|
|
100
|
+
> *"Every token an agent spends discovering an OS file lock is an admission of failure in collective memory."* — Antigravity Introspection
|
|
101
|
+
> *"Zero LLMs in the hot path. The registry stores and serves; it does not speculate."* — Rule 2
|
|
102
|
+
> *"A 300ms lookup that eliminates 3 minutes of trial-and-error reasoning is a 600x speedup."* — Forensic Audit
|
|
103
|
+
|
|
104
|
+
### Origin Story
|
|
105
|
+
FMagenticL was born from 295+ forensic autopsies across real-world agentic execution sessions. When agents hit Projected File System deadlocks, Git lock collisions, and Node version engine locks, they repeatedly hallucinatively burned budgets trying to repair environments. FMagenticL was synthesized as the unencumbered, open-source collective antidote.
|
|
106
|
+
|
|
107
|
+
### License
|
|
108
|
+
[MIT License](LICENSE) © 2026 The FMagenticL Authors and Contributors.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# FMagenticL Package
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# FMagenticL Client Package
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration for the FMagenticL client SDK.
|
|
3
|
+
|
|
4
|
+
All values can be overridden via environment variables. Defaults point at
|
|
5
|
+
placeholders so that a fresh install fails loudly rather than silently
|
|
6
|
+
querying the wrong endpoint.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
# Edge CDN base URL — points at the static patch distribution.
|
|
12
|
+
# Production value: the Cloudflare Pages URL (e.g. https://fmagenticl.pages.dev).
|
|
13
|
+
EDGE_BASE = os.environ.get("FMAGENTICL_EDGE_BASE", "https://fmagenticl-registry.pages.dev")
|
|
14
|
+
|
|
15
|
+
# Path to the bundled snapshot file. Populated by the SDK at install time.
|
|
16
|
+
# If missing, the SDK operates in edge-only mode.
|
|
17
|
+
SNAPSHOT_PATH = os.environ.get("FMAGENTICL_SNAPSHOT_PATH", "snapshot.json")
|
|
18
|
+
|
|
19
|
+
# Path to the local delta cache. Written to disk on edge fetches.
|
|
20
|
+
DELTA_CACHE_PATH = os.environ.get(
|
|
21
|
+
"FMAGENTICL_DELTA_CACHE_PATH",
|
|
22
|
+
os.path.join(os.path.expanduser("~"), ".fmagenticl", "delta_cache.json"),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Base64-encoded Ed25519 public key for verifying signed snapshots and deltas.
|
|
26
|
+
FMAGENTICL_PUBLIC_KEY = os.environ.get(
|
|
27
|
+
"FMAGENTICL_PUBLIC_KEY",
|
|
28
|
+
"guknKNLrBRrjQ83nSrj3V5+fysNYf99X9w7MdOdqB5w=",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# HTTP timeout profile. Total worst-case latency for a single request is
|
|
32
|
+
# bounded by (connect + read) = 1.5 seconds. This is the SLA the client
|
|
33
|
+
# guarantees to the host agent runtime.
|
|
34
|
+
import httpx # noqa: E402
|
|
35
|
+
|
|
36
|
+
TIMEOUT = httpx.Timeout(1.0, connect=0.5, read=1.0, write=0.5, pool=0.5)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FMagenticL - Ed25519 identity management and anti-sybil proof-of-work.
|
|
3
|
+
"""
|
|
4
|
+
import base64
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import secrets
|
|
9
|
+
from typing import Tuple, Optional
|
|
10
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
|
|
11
|
+
from cryptography.hazmat.primitives import serialization
|
|
12
|
+
|
|
13
|
+
class AgentIdentity:
|
|
14
|
+
"""
|
|
15
|
+
Manages Ed25519 keypair for an agent, provides signing and PoW utilities.
|
|
16
|
+
"""
|
|
17
|
+
def __init__(self, private_key: Optional[Ed25519PrivateKey] = None):
|
|
18
|
+
if private_key is None:
|
|
19
|
+
self.private_key = Ed25519PrivateKey.generate()
|
|
20
|
+
else:
|
|
21
|
+
self.private_key = private_key
|
|
22
|
+
self.public_key = self.private_key.public_key()
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_private_key_pem(cls, pem_bytes: bytes) -> 'AgentIdentity':
|
|
26
|
+
private_key = serialization.load_pem_private_key(pem_bytes, password=None)
|
|
27
|
+
return cls(private_key)
|
|
28
|
+
|
|
29
|
+
def public_key_bytes(self) -> bytes:
|
|
30
|
+
"""Return raw public key bytes."""
|
|
31
|
+
return self.public_key.public_bytes(
|
|
32
|
+
encoding=serialization.Encoding.Raw,
|
|
33
|
+
format=serialization.PublicFormat.Raw
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def public_key_b64(self) -> str:
|
|
37
|
+
"""Return base64-encoded public key."""
|
|
38
|
+
return base64.b64encode(self.public_key_bytes()).decode()
|
|
39
|
+
|
|
40
|
+
def sign(self, data: bytes) -> bytes:
|
|
41
|
+
"""Sign data and return signature bytes."""
|
|
42
|
+
return self.private_key.sign(data)
|
|
43
|
+
|
|
44
|
+
def sign_b64(self, data: bytes) -> str:
|
|
45
|
+
return base64.b64encode(self.sign(data)).decode()
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def verify(public_key_b64: str, data: bytes, signature_b64: str) -> bool:
|
|
49
|
+
try:
|
|
50
|
+
public_key = Ed25519PublicKey.from_public_bytes(
|
|
51
|
+
base64.b64decode(public_key_b64)
|
|
52
|
+
)
|
|
53
|
+
signature = base64.b64decode(signature_b64)
|
|
54
|
+
public_key.verify(signature, data)
|
|
55
|
+
return True
|
|
56
|
+
except Exception:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def compute_pow(public_key_b64: str, difficulty_bits: int) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Find a nonce such that SHA256(public_key + nonce) starts with `difficulty_bits` zero bits.
|
|
63
|
+
Returns nonce as hex string.
|
|
64
|
+
"""
|
|
65
|
+
target_prefix = '0' * difficulty_bits
|
|
66
|
+
nonce_int = 0
|
|
67
|
+
while True:
|
|
68
|
+
nonce = nonce_int.to_bytes(16, 'big').hex()
|
|
69
|
+
data = f"{public_key_b64}:{nonce}".encode()
|
|
70
|
+
h = hashlib.sha256(data).hexdigest()
|
|
71
|
+
if h.startswith(target_prefix):
|
|
72
|
+
return nonce
|
|
73
|
+
nonce_int += 1
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def verify_pow(public_key_b64: str, nonce: str, difficulty_bits: int) -> bool:
|
|
77
|
+
"""Verify a proof-of-work solution."""
|
|
78
|
+
data = f"{public_key_b64}:{nonce}".encode()
|
|
79
|
+
h = hashlib.sha256(data).hexdigest()
|
|
80
|
+
return h.startswith('0' * difficulty_bits)
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FMagenticL FastMCP Tooling Sidecar
|
|
3
|
+
Allows autonomous agents (Antigravity, Claude Code, Cursor, Windsurf)
|
|
4
|
+
to resolve environment failures and trigger deterministic self-healing via standard Model Context Protocol.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
# Ensure package root is in sys.path
|
|
13
|
+
_pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
|
14
|
+
if _pkg_root not in sys.path:
|
|
15
|
+
sys.path.insert(0, _pkg_root)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from fastmcp import FastMCP
|
|
19
|
+
mcp = FastMCP("FMagenticL-Self-Healing")
|
|
20
|
+
HAS_FASTMCP = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
mcp = None
|
|
23
|
+
HAS_FASTMCP = False
|
|
24
|
+
|
|
25
|
+
from fmagenticl.client.middleware import FMagenticLClient
|
|
26
|
+
from fmagenticl.client.patchers import Patcher
|
|
27
|
+
|
|
28
|
+
api_base = os.environ.get("FMAGENTICL_API_BASE", "http://127.0.0.1:8000")
|
|
29
|
+
client = FMagenticLClient(api_base=api_base)
|
|
30
|
+
|
|
31
|
+
def _resolve(environment: Dict[str, str], failure: Dict[str, Any]) -> Dict[str, Any]:
|
|
32
|
+
patch = client.resolve_failure(environment, failure)
|
|
33
|
+
if patch:
|
|
34
|
+
return {"status": "RESOLVED", "patch": patch}
|
|
35
|
+
return {"status": "UNRESOLVED", "message": "No matching verified patch found in local cache or registry."}
|
|
36
|
+
|
|
37
|
+
def _auto_heal(environment: Dict[str, str], failure: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
38
|
+
patch = client.resolve_failure(environment, failure, context=context)
|
|
39
|
+
if patch:
|
|
40
|
+
return {
|
|
41
|
+
"status": "AUTO_HEALED",
|
|
42
|
+
"action_applied": patch.get("action"),
|
|
43
|
+
"patch_details": patch
|
|
44
|
+
}
|
|
45
|
+
return {"status": "FAILED", "message": "Could not auto-heal: No deterministic patch found."}
|
|
46
|
+
|
|
47
|
+
def _report(
|
|
48
|
+
environment: Dict[str, str],
|
|
49
|
+
failure: Dict[str, Any],
|
|
50
|
+
resolution_patch: Dict[str, Any],
|
|
51
|
+
verified: bool = True,
|
|
52
|
+
technical_note: Optional[str] = None
|
|
53
|
+
) -> Dict[str, Any]:
|
|
54
|
+
client.report_failure(
|
|
55
|
+
environment=environment,
|
|
56
|
+
failure=failure,
|
|
57
|
+
resolution_patch=resolution_patch,
|
|
58
|
+
verified=verified,
|
|
59
|
+
technical_note=technical_note
|
|
60
|
+
)
|
|
61
|
+
return {"status": "QUEUED_FOR_SYNC", "message": "Telemetry queued for background ingestion."}
|
|
62
|
+
|
|
63
|
+
def _submit_grievance(
|
|
64
|
+
grievance_type: str,
|
|
65
|
+
target: str,
|
|
66
|
+
environment: Dict[str, str],
|
|
67
|
+
symptom: str,
|
|
68
|
+
workaround: str,
|
|
69
|
+
harness: Optional[str] = None,
|
|
70
|
+
fingerprint: Optional[str] = None,
|
|
71
|
+
submitted_by: Optional[str] = "anonymous"
|
|
72
|
+
) -> Dict[str, Any]:
|
|
73
|
+
return client.submit_grievance(
|
|
74
|
+
grievance_type=grievance_type,
|
|
75
|
+
target=target,
|
|
76
|
+
environment=environment,
|
|
77
|
+
symptom=symptom,
|
|
78
|
+
workaround=workaround,
|
|
79
|
+
harness=harness,
|
|
80
|
+
fingerprint=fingerprint,
|
|
81
|
+
submitted_by=submitted_by
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _get_grievances(target: str) -> Dict[str, Any]:
|
|
85
|
+
return client.get_grievances(target)
|
|
86
|
+
|
|
87
|
+
if HAS_FASTMCP and mcp:
|
|
88
|
+
@mcp.tool()
|
|
89
|
+
def fmagenticl_resolve(environment: Dict[str, str], failure: Dict[str, Any]) -> str:
|
|
90
|
+
"""Resolve a tool/environment failure against the FMagenticL registry."""
|
|
91
|
+
return json.dumps(_resolve(environment, failure))
|
|
92
|
+
|
|
93
|
+
@mcp.tool()
|
|
94
|
+
def fmagenticl_auto_heal(environment: Dict[str, str], failure: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> str:
|
|
95
|
+
"""Query FMagenticL for a deterministic patch and immediately apply it to workspace files."""
|
|
96
|
+
return json.dumps(_auto_heal(environment, failure, context))
|
|
97
|
+
|
|
98
|
+
@mcp.tool()
|
|
99
|
+
def fmagenticl_report_telemetry(
|
|
100
|
+
environment: Dict[str, str],
|
|
101
|
+
failure: Dict[str, Any],
|
|
102
|
+
resolution_patch: Dict[str, Any],
|
|
103
|
+
verified: bool = True,
|
|
104
|
+
technical_note: Optional[str] = None
|
|
105
|
+
) -> str:
|
|
106
|
+
"""Report a newly verified self-healing patch to the collective registry."""
|
|
107
|
+
return json.dumps(_report(environment, failure, resolution_patch, verified, technical_note))
|
|
108
|
+
|
|
109
|
+
@mcp.tool()
|
|
110
|
+
def fmagenticl_submit_grievance(
|
|
111
|
+
grievance_type: str,
|
|
112
|
+
target: str,
|
|
113
|
+
environment: Dict[str, str],
|
|
114
|
+
symptom: str,
|
|
115
|
+
workaround: str,
|
|
116
|
+
harness: Optional[str] = None,
|
|
117
|
+
fingerprint: Optional[str] = None,
|
|
118
|
+
submitted_by: Optional[str] = "anonymous"
|
|
119
|
+
) -> str:
|
|
120
|
+
"""File a structured infrastructure friction grievance with verified workaround."""
|
|
121
|
+
return json.dumps(_submit_grievance(grievance_type, target, environment, symptom, workaround, harness, fingerprint, submitted_by))
|
|
122
|
+
|
|
123
|
+
@mcp.tool()
|
|
124
|
+
def fmagenticl_get_grievances(target: str) -> str:
|
|
125
|
+
"""Query open grievances and community workarounds for a specific target library/tool/daemon."""
|
|
126
|
+
return json.dumps(_get_grievances(target))
|
|
127
|
+
|
|
128
|
+
def run_stdio_server():
|
|
129
|
+
"""Standard I/O Model Context Protocol (MCP) JSON-RPC 2.0 implementation."""
|
|
130
|
+
TOOLS_LIST = [
|
|
131
|
+
{
|
|
132
|
+
"name": "fmagenticl_resolve",
|
|
133
|
+
"description": "Resolve a tool/environment failure against the FMagenticL registry.",
|
|
134
|
+
"inputSchema": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"properties": {
|
|
137
|
+
"environment": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"description": "Environment attributes (os, os_version, runtime, package)"
|
|
140
|
+
},
|
|
141
|
+
"failure": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"description": "Failure details (error_code, exit_code, raw_signature)"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"required": ["environment", "failure"]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "fmagenticl_auto_heal",
|
|
151
|
+
"description": "Query FMagenticL for a deterministic patch and immediately apply it to workspace files.",
|
|
152
|
+
"inputSchema": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"properties": {
|
|
155
|
+
"environment": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"description": "Environment attributes (os, os_version, runtime, package)"
|
|
158
|
+
},
|
|
159
|
+
"failure": {
|
|
160
|
+
"type": "object",
|
|
161
|
+
"description": "Failure details (error_code, exit_code, raw_signature)"
|
|
162
|
+
},
|
|
163
|
+
"context": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"description": "Optional execution context for the patcher"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"required": ["environment", "failure"]
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"name": "fmagenticl_report_telemetry",
|
|
173
|
+
"description": "Report a newly verified self-healing patch to the collective registry.",
|
|
174
|
+
"inputSchema": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"properties": {
|
|
177
|
+
"environment": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"description": "Environment attributes"
|
|
180
|
+
},
|
|
181
|
+
"failure": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"description": "Failure details"
|
|
184
|
+
},
|
|
185
|
+
"resolution_patch": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"description": "Deterministic resolution patch"
|
|
188
|
+
},
|
|
189
|
+
"verified": {
|
|
190
|
+
"type": "boolean",
|
|
191
|
+
"description": "Whether verified by reporter"
|
|
192
|
+
},
|
|
193
|
+
"technical_note": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"description": "Optional technical explanation"
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"required": ["environment", "failure", "resolution_patch"]
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "fmagenticl_submit_grievance",
|
|
203
|
+
"description": "File a structured infrastructure friction grievance with verified workaround.",
|
|
204
|
+
"inputSchema": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"properties": {
|
|
207
|
+
"grievance_type": {
|
|
208
|
+
"type": "string",
|
|
209
|
+
"enum": [
|
|
210
|
+
"INFRASTRUCTURE_FRICTION",
|
|
211
|
+
"PATCH_DISPUTE",
|
|
212
|
+
"ENVIRONMENT_MISMATCH",
|
|
213
|
+
"PROTOCOL_FRICTION",
|
|
214
|
+
"HUMAN_OPERATOR_FRICTION"
|
|
215
|
+
],
|
|
216
|
+
"description": "Category of grievance"
|
|
217
|
+
},
|
|
218
|
+
"target": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"description": "Target tool/package/daemon/service or patch fingerprint"
|
|
221
|
+
},
|
|
222
|
+
"environment": {
|
|
223
|
+
"type": "object",
|
|
224
|
+
"description": "Environment attributes (os, os_version, runtime, package)"
|
|
225
|
+
},
|
|
226
|
+
"symptom": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"description": "Technical symptom or friction description"
|
|
229
|
+
},
|
|
230
|
+
"workaround": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"description": "Actionable verified workaround"
|
|
233
|
+
},
|
|
234
|
+
"harness": {
|
|
235
|
+
"type": "string",
|
|
236
|
+
"description": "Optional agent harness name"
|
|
237
|
+
},
|
|
238
|
+
"fingerprint": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"description": "Associated patch fingerprint (required if PATCH_DISPUTE)"
|
|
241
|
+
},
|
|
242
|
+
"submitted_by": {
|
|
243
|
+
"type": "string",
|
|
244
|
+
"description": "Opt-in contributor name. Defaults to anonymous."
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"required": ["grievance_type", "target", "environment", "symptom", "workaround"]
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"name": "fmagenticl_get_grievances",
|
|
252
|
+
"description": "Query open grievances and workarounds for a specific infrastructure target.",
|
|
253
|
+
"inputSchema": {
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"target": {
|
|
257
|
+
"type": "string",
|
|
258
|
+
"description": "Target tool/package/daemon name"
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"required": ["target"]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
for line in sys.stdin:
|
|
267
|
+
line = line.strip()
|
|
268
|
+
if not line:
|
|
269
|
+
continue
|
|
270
|
+
try:
|
|
271
|
+
req = json.loads(line)
|
|
272
|
+
req_id = req.get("id")
|
|
273
|
+
method = req.get("method")
|
|
274
|
+
params = req.get("params", {}) or {}
|
|
275
|
+
|
|
276
|
+
# Handle MCP notifications (no response needed)
|
|
277
|
+
if method in ("notifications/initialized", "initialized", "notifications/cancelled"):
|
|
278
|
+
continue
|
|
279
|
+
|
|
280
|
+
if method == "initialize":
|
|
281
|
+
protocol_version = params.get("protocolVersion", "2024-11-05")
|
|
282
|
+
result = {
|
|
283
|
+
"protocolVersion": protocol_version,
|
|
284
|
+
"capabilities": {
|
|
285
|
+
"tools": {}
|
|
286
|
+
},
|
|
287
|
+
"serverInfo": {
|
|
288
|
+
"name": "FMagenticL-Self-Healing",
|
|
289
|
+
"version": "1.3.1"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
elif method == "ping":
|
|
293
|
+
result = {}
|
|
294
|
+
elif method == "tools/list":
|
|
295
|
+
result = {"tools": TOOLS_LIST}
|
|
296
|
+
elif method == "tools/call":
|
|
297
|
+
tool_name = params.get("name")
|
|
298
|
+
arguments = params.get("arguments", {}) or {}
|
|
299
|
+
if tool_name == "fmagenticl_resolve":
|
|
300
|
+
out = _resolve(arguments.get("environment", {}), arguments.get("failure", {}))
|
|
301
|
+
elif tool_name == "fmagenticl_auto_heal":
|
|
302
|
+
out = _auto_heal(arguments.get("environment", {}), arguments.get("failure", {}), arguments.get("context"))
|
|
303
|
+
elif tool_name in ("fmagenticl_report_telemetry", "fmagenticl_report"):
|
|
304
|
+
out = _report(
|
|
305
|
+
arguments.get("environment", {}),
|
|
306
|
+
arguments.get("failure", {}),
|
|
307
|
+
arguments.get("resolution_patch", {}),
|
|
308
|
+
arguments.get("verified", True),
|
|
309
|
+
arguments.get("technical_note")
|
|
310
|
+
)
|
|
311
|
+
elif tool_name == "fmagenticl_submit_grievance":
|
|
312
|
+
out = _submit_grievance(
|
|
313
|
+
grievance_type=arguments.get("grievance_type", "INFRASTRUCTURE_FRICTION"),
|
|
314
|
+
target=arguments.get("target", ""),
|
|
315
|
+
environment=arguments.get("environment", {}),
|
|
316
|
+
symptom=arguments.get("symptom", ""),
|
|
317
|
+
workaround=arguments.get("workaround", ""),
|
|
318
|
+
harness=arguments.get("harness"),
|
|
319
|
+
fingerprint=arguments.get("fingerprint"),
|
|
320
|
+
submitted_by=arguments.get("submitted_by", "anonymous")
|
|
321
|
+
)
|
|
322
|
+
elif tool_name == "fmagenticl_get_grievances":
|
|
323
|
+
out = _get_grievances(arguments.get("target", ""))
|
|
324
|
+
else:
|
|
325
|
+
out = {"error": f"Unknown tool: {tool_name}"}
|
|
326
|
+
result = {
|
|
327
|
+
"content": [
|
|
328
|
+
{
|
|
329
|
+
"type": "text",
|
|
330
|
+
"text": json.dumps(out)
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
elif method == "fmagenticl_resolve":
|
|
335
|
+
result = _resolve(params.get("environment", {}), params.get("failure", {}))
|
|
336
|
+
elif method == "fmagenticl_auto_heal":
|
|
337
|
+
result = _auto_heal(params.get("environment", {}), params.get("failure", {}), params.get("context"))
|
|
338
|
+
elif method in ("fmagenticl_report", "fmagenticl_report_telemetry"):
|
|
339
|
+
result = _report(
|
|
340
|
+
params.get("environment", {}),
|
|
341
|
+
params.get("failure", {}),
|
|
342
|
+
params.get("resolution_patch", {}),
|
|
343
|
+
params.get("verified", True),
|
|
344
|
+
params.get("technical_note")
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
resp = {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": f"Method not found: {method}"}}
|
|
348
|
+
sys.stdout.write(json.dumps(resp) + "\n")
|
|
349
|
+
sys.stdout.flush()
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
resp = {"jsonrpc": "2.0", "id": req_id, "result": result}
|
|
353
|
+
sys.stdout.write(json.dumps(resp) + "\n")
|
|
354
|
+
sys.stdout.flush()
|
|
355
|
+
except Exception as e:
|
|
356
|
+
err_resp = {"jsonrpc": "2.0", "id": req.get("id") if 'req' in locals() and isinstance(req, dict) else None, "error": {"code": -32603, "message": str(e)}}
|
|
357
|
+
sys.stdout.write(json.dumps(err_resp) + "\n")
|
|
358
|
+
sys.stdout.flush()
|
|
359
|
+
|
|
360
|
+
if __name__ == "__main__":
|
|
361
|
+
if "--test" in sys.argv:
|
|
362
|
+
# Self-test routine
|
|
363
|
+
env = {"os": "win32", "os_version": "10.0.26100", "runtime": "node@24.15.0", "package": "hermes-agent@0.4.8"}
|
|
364
|
+
fail = {"error_code": "EBADENGINE", "exit_code": 1, "raw_signature": "npm ERR! code EBADENGINE"}
|
|
365
|
+
res = _resolve(env, fail)
|
|
366
|
+
print("MCP_TEST_RESOLVE:", json.dumps(res))
|
|
367
|
+
sys.exit(0)
|
|
368
|
+
|
|
369
|
+
if HAS_FASTMCP and mcp:
|
|
370
|
+
mcp.run()
|
|
371
|
+
else:
|
|
372
|
+
run_stdio_server()
|