iflow-mcp_hulupeep_ruvscan-mcp 0.5.0__py3-none-any.whl
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.
- iflow_mcp_hulupeep_ruvscan_mcp-0.5.0.dist-info/METADATA +1222 -0
- iflow_mcp_hulupeep_ruvscan_mcp-0.5.0.dist-info/RECORD +19 -0
- iflow_mcp_hulupeep_ruvscan_mcp-0.5.0.dist-info/WHEEL +4 -0
- iflow_mcp_hulupeep_ruvscan_mcp-0.5.0.dist-info/entry_points.txt +2 -0
- mcp/__init__.py +7 -0
- mcp/bindings/__init__.py +5 -0
- mcp/bindings/rust_client.py +166 -0
- mcp/endpoints/query.py +144 -0
- mcp/endpoints/scan.py +112 -0
- mcp/mcp_stdio_server.py +212 -0
- mcp/monitoring.py +126 -0
- mcp/reasoning/__init__.py +6 -0
- mcp/reasoning/embeddings.py +208 -0
- mcp/reasoning/fact_cache.py +212 -0
- mcp/reasoning/safla_agent.py +282 -0
- mcp/server.py +268 -0
- mcp/storage/__init__.py +21 -0
- mcp/storage/db.py +216 -0
- mcp/storage/models.py +77 -0
mcp/storage/db.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database layer for RuvScan
|
|
3
|
+
Handles SQLite storage for repos, leverage cards, and FACT cache
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sqlite3
|
|
7
|
+
import json
|
|
8
|
+
from typing import Optional, List, Dict, Any
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
import hashlib
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class RuvScanDB:
|
|
16
|
+
"""SQLite database manager for RuvScan"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, db_path: str = "data/ruvscan.db"):
|
|
19
|
+
self.db_path = db_path
|
|
20
|
+
self.conn = None
|
|
21
|
+
self._init_db()
|
|
22
|
+
|
|
23
|
+
def _init_db(self):
|
|
24
|
+
"""Initialize database connection and create tables"""
|
|
25
|
+
self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
|
26
|
+
self.conn.row_factory = sqlite3.Row
|
|
27
|
+
self._create_tables()
|
|
28
|
+
|
|
29
|
+
def _create_tables(self):
|
|
30
|
+
"""Create database schema"""
|
|
31
|
+
cursor = self.conn.cursor()
|
|
32
|
+
|
|
33
|
+
# Repos table
|
|
34
|
+
cursor.execute("""
|
|
35
|
+
CREATE TABLE IF NOT EXISTS repos (
|
|
36
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
+
name TEXT NOT NULL,
|
|
38
|
+
org TEXT NOT NULL,
|
|
39
|
+
full_name TEXT UNIQUE NOT NULL,
|
|
40
|
+
description TEXT,
|
|
41
|
+
topics TEXT,
|
|
42
|
+
readme TEXT,
|
|
43
|
+
embedding BLOB,
|
|
44
|
+
sublinear_hash TEXT,
|
|
45
|
+
stars INTEGER DEFAULT 0,
|
|
46
|
+
language TEXT,
|
|
47
|
+
last_scan TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
48
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
49
|
+
UNIQUE(org, name)
|
|
50
|
+
)
|
|
51
|
+
""")
|
|
52
|
+
|
|
53
|
+
# Leverage cards table
|
|
54
|
+
cursor.execute("""
|
|
55
|
+
CREATE TABLE IF NOT EXISTS leverage_cards (
|
|
56
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
57
|
+
repo_id INTEGER NOT NULL,
|
|
58
|
+
capabilities TEXT NOT NULL,
|
|
59
|
+
summary TEXT NOT NULL,
|
|
60
|
+
reasoning TEXT NOT NULL,
|
|
61
|
+
integration_hint TEXT,
|
|
62
|
+
relevance_score REAL NOT NULL,
|
|
63
|
+
runtime_complexity TEXT,
|
|
64
|
+
query_intent TEXT,
|
|
65
|
+
cached BOOLEAN DEFAULT 1,
|
|
66
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
67
|
+
FOREIGN KEY (repo_id) REFERENCES repos(id)
|
|
68
|
+
)
|
|
69
|
+
""")
|
|
70
|
+
|
|
71
|
+
# FACT cache table
|
|
72
|
+
cursor.execute("""
|
|
73
|
+
CREATE TABLE IF NOT EXISTS fact_cache (
|
|
74
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
+
hash TEXT UNIQUE NOT NULL,
|
|
76
|
+
prompt TEXT NOT NULL,
|
|
77
|
+
response TEXT NOT NULL,
|
|
78
|
+
version TEXT DEFAULT '0.5.0',
|
|
79
|
+
metadata TEXT,
|
|
80
|
+
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
81
|
+
)
|
|
82
|
+
""")
|
|
83
|
+
|
|
84
|
+
# Create indexes
|
|
85
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_repos_org ON repos(org)")
|
|
86
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_repos_full_name ON repos(full_name)")
|
|
87
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_cards_repo ON leverage_cards(repo_id)")
|
|
88
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_cards_score ON leverage_cards(relevance_score)")
|
|
89
|
+
cursor.execute("CREATE INDEX IF NOT EXISTS idx_fact_hash ON fact_cache(hash)")
|
|
90
|
+
|
|
91
|
+
self.conn.commit()
|
|
92
|
+
logger.info("Database tables created successfully")
|
|
93
|
+
|
|
94
|
+
def add_repo(self, repo_data: Dict[str, Any]) -> int:
|
|
95
|
+
"""Add or update repository"""
|
|
96
|
+
cursor = self.conn.cursor()
|
|
97
|
+
|
|
98
|
+
cursor.execute("""
|
|
99
|
+
INSERT OR REPLACE INTO repos
|
|
100
|
+
(name, org, full_name, description, topics, readme, stars, language, last_scan)
|
|
101
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
102
|
+
""", (
|
|
103
|
+
repo_data.get('name'),
|
|
104
|
+
repo_data.get('org'),
|
|
105
|
+
repo_data.get('full_name'),
|
|
106
|
+
repo_data.get('description'),
|
|
107
|
+
json.dumps(repo_data.get('topics', [])),
|
|
108
|
+
repo_data.get('readme'),
|
|
109
|
+
repo_data.get('stars', 0),
|
|
110
|
+
repo_data.get('language'),
|
|
111
|
+
datetime.utcnow()
|
|
112
|
+
))
|
|
113
|
+
|
|
114
|
+
self.conn.commit()
|
|
115
|
+
return cursor.lastrowid
|
|
116
|
+
|
|
117
|
+
def get_repo(self, full_name: str) -> Optional[Dict[str, Any]]:
|
|
118
|
+
"""Get repository by full name"""
|
|
119
|
+
cursor = self.conn.cursor()
|
|
120
|
+
cursor.execute("SELECT * FROM repos WHERE full_name = ?", (full_name,))
|
|
121
|
+
row = cursor.fetchone()
|
|
122
|
+
|
|
123
|
+
if row:
|
|
124
|
+
return dict(row)
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def add_leverage_card(self, card_data: Dict[str, Any]) -> int:
|
|
128
|
+
"""Add leverage card"""
|
|
129
|
+
cursor = self.conn.cursor()
|
|
130
|
+
|
|
131
|
+
cursor.execute("""
|
|
132
|
+
INSERT INTO leverage_cards
|
|
133
|
+
(repo_id, capabilities, summary, reasoning, integration_hint,
|
|
134
|
+
relevance_score, runtime_complexity, query_intent)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
136
|
+
""", (
|
|
137
|
+
card_data.get('repo_id'),
|
|
138
|
+
json.dumps(card_data.get('capabilities', [])),
|
|
139
|
+
card_data.get('summary'),
|
|
140
|
+
card_data.get('reasoning'),
|
|
141
|
+
card_data.get('integration_hint'),
|
|
142
|
+
card_data.get('relevance_score'),
|
|
143
|
+
card_data.get('runtime_complexity'),
|
|
144
|
+
card_data.get('query_intent')
|
|
145
|
+
))
|
|
146
|
+
|
|
147
|
+
self.conn.commit()
|
|
148
|
+
return cursor.lastrowid
|
|
149
|
+
|
|
150
|
+
def get_leverage_cards(
|
|
151
|
+
self,
|
|
152
|
+
limit: int = 50,
|
|
153
|
+
min_score: float = 0.0,
|
|
154
|
+
cached_only: bool = False
|
|
155
|
+
) -> List[Dict[str, Any]]:
|
|
156
|
+
"""Get leverage cards with filters"""
|
|
157
|
+
cursor = self.conn.cursor()
|
|
158
|
+
|
|
159
|
+
query = """
|
|
160
|
+
SELECT lc.*, r.full_name as repo
|
|
161
|
+
FROM leverage_cards lc
|
|
162
|
+
JOIN repos r ON lc.repo_id = r.id
|
|
163
|
+
WHERE lc.relevance_score >= ?
|
|
164
|
+
"""
|
|
165
|
+
params = [min_score]
|
|
166
|
+
|
|
167
|
+
if cached_only:
|
|
168
|
+
query += " AND lc.cached = 1"
|
|
169
|
+
|
|
170
|
+
query += " ORDER BY lc.relevance_score DESC, lc.created_at DESC LIMIT ?"
|
|
171
|
+
params.append(limit)
|
|
172
|
+
|
|
173
|
+
cursor.execute(query, params)
|
|
174
|
+
rows = cursor.fetchall()
|
|
175
|
+
|
|
176
|
+
return [dict(row) for row in rows]
|
|
177
|
+
|
|
178
|
+
def add_fact_cache(self, prompt: str, response: str, metadata: Optional[Dict] = None) -> str:
|
|
179
|
+
"""Add entry to FACT cache"""
|
|
180
|
+
# Generate deterministic hash
|
|
181
|
+
cache_hash = hashlib.sha256(prompt.encode()).hexdigest()
|
|
182
|
+
|
|
183
|
+
cursor = self.conn.cursor()
|
|
184
|
+
cursor.execute("""
|
|
185
|
+
INSERT OR REPLACE INTO fact_cache
|
|
186
|
+
(hash, prompt, response, metadata)
|
|
187
|
+
VALUES (?, ?, ?, ?)
|
|
188
|
+
""", (
|
|
189
|
+
cache_hash,
|
|
190
|
+
prompt,
|
|
191
|
+
response,
|
|
192
|
+
json.dumps(metadata) if metadata else None
|
|
193
|
+
))
|
|
194
|
+
|
|
195
|
+
self.conn.commit()
|
|
196
|
+
return cache_hash
|
|
197
|
+
|
|
198
|
+
def get_fact_cache(self, prompt: str) -> Optional[Dict[str, Any]]:
|
|
199
|
+
"""Get cached response from FACT"""
|
|
200
|
+
cache_hash = hashlib.sha256(prompt.encode()).hexdigest()
|
|
201
|
+
|
|
202
|
+
cursor = self.conn.cursor()
|
|
203
|
+
cursor.execute("SELECT * FROM fact_cache WHERE hash = ?", (cache_hash,))
|
|
204
|
+
row = cursor.fetchone()
|
|
205
|
+
|
|
206
|
+
if row:
|
|
207
|
+
result = dict(row)
|
|
208
|
+
if result.get('metadata'):
|
|
209
|
+
result['metadata'] = json.loads(result['metadata'])
|
|
210
|
+
return result
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
def close(self):
|
|
214
|
+
"""Close database connection"""
|
|
215
|
+
if self.conn:
|
|
216
|
+
self.conn.close()
|
mcp/storage/models.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic models for RuvScan data structures
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from typing import List, Optional, Dict, Any
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
class Repository(BaseModel):
|
|
10
|
+
"""Repository model"""
|
|
11
|
+
id: Optional[int] = None
|
|
12
|
+
name: str
|
|
13
|
+
org: str
|
|
14
|
+
full_name: str
|
|
15
|
+
description: Optional[str] = None
|
|
16
|
+
topics: List[str] = []
|
|
17
|
+
readme: Optional[str] = None
|
|
18
|
+
embedding: Optional[bytes] = None
|
|
19
|
+
sublinear_hash: Optional[str] = None
|
|
20
|
+
stars: int = 0
|
|
21
|
+
language: Optional[str] = None
|
|
22
|
+
last_scan: Optional[datetime] = None
|
|
23
|
+
created_at: Optional[datetime] = None
|
|
24
|
+
|
|
25
|
+
class LeverageCard(BaseModel):
|
|
26
|
+
"""Leverage card model"""
|
|
27
|
+
id: Optional[int] = None
|
|
28
|
+
repo_id: int
|
|
29
|
+
repo: Optional[str] = None
|
|
30
|
+
capabilities: List[str]
|
|
31
|
+
summary: str
|
|
32
|
+
reasoning: str = Field(..., alias='outside_box_reasoning')
|
|
33
|
+
integration_hint: Optional[str] = None
|
|
34
|
+
relevance_score: float = Field(..., ge=0.0, le=1.0)
|
|
35
|
+
runtime_complexity: Optional[str] = None
|
|
36
|
+
query_intent: Optional[str] = None
|
|
37
|
+
cached: bool = True
|
|
38
|
+
created_at: Optional[datetime] = None
|
|
39
|
+
|
|
40
|
+
class Config:
|
|
41
|
+
populate_by_name = True
|
|
42
|
+
|
|
43
|
+
class FACTCacheEntry(BaseModel):
|
|
44
|
+
"""FACT cache entry model"""
|
|
45
|
+
id: Optional[int] = None
|
|
46
|
+
hash: str
|
|
47
|
+
prompt: str
|
|
48
|
+
response: str
|
|
49
|
+
version: str = "0.5.0"
|
|
50
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
51
|
+
timestamp: Optional[datetime] = None
|
|
52
|
+
|
|
53
|
+
class ScanJob(BaseModel):
|
|
54
|
+
"""Scan job configuration"""
|
|
55
|
+
source_type: str = Field(..., pattern="^(org|user|topic)$")
|
|
56
|
+
source_name: str
|
|
57
|
+
limit: int = Field(50, gt=0, le=1000)
|
|
58
|
+
status: str = "pending"
|
|
59
|
+
repos_found: int = 0
|
|
60
|
+
repos_processed: int = 0
|
|
61
|
+
|
|
62
|
+
class SublinearComparison(BaseModel):
|
|
63
|
+
"""Result of sublinear comparison"""
|
|
64
|
+
repo_a: str
|
|
65
|
+
repo_b: str
|
|
66
|
+
similarity_score: float = Field(..., ge=0.0, le=1.0)
|
|
67
|
+
complexity: str = "O(log n)"
|
|
68
|
+
method_used: str = "sublinear_neumann"
|
|
69
|
+
computation_time_ms: Optional[float] = None
|
|
70
|
+
|
|
71
|
+
class ReasoningTrace(BaseModel):
|
|
72
|
+
"""Reasoning trace for FACT replay"""
|
|
73
|
+
repo: str
|
|
74
|
+
steps: List[Dict[str, Any]] = []
|
|
75
|
+
final_reasoning: str
|
|
76
|
+
cached: bool = False
|
|
77
|
+
confidence_score: float = Field(..., ge=0.0, le=1.0)
|