vector-knowledge-graph-mcp 1.0.3__tar.gz → 1.0.4__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.
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/PKG-INFO +1 -1
- vector_knowledge_graph_mcp-1.0.4/auth_middleware.py +223 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/pyproject.toml +2 -2
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/server.py +1 -1
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.cursorrules +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.github/FUNDING.yml +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.github/workflows/mcp-smithery-publish.yml +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.github/workflows/test.yml +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.gitignore +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.mcp.json +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.well-known/mcp/server-card.json +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/CODE_OF_CONDUCT.md +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/CONTRIBUTING.md +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/Dockerfile +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/Dockerfile.glama +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/LICENSE +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/README.md +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/SECURITY.md +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/glama.json +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/llms.txt +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/mcp-wrapper.py +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/package.json +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/pytest.ini +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/smithery.yaml +0 -0
- {vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vector-knowledge-graph-mcp
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: AI-powered vector knowledge graph MCP server for agents. Supports add node, add edge, semantic node search. By MEOK AI Labs.
|
|
5
5
|
Project-URL: Homepage, https://meok.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/CSOAI-ORG/vector-knowledge-graph-mcp
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MEOK Labs — Shared Authentication Middleware for MCP Servers
|
|
3
|
+
Deploy to: ~/clawd/meok-labs-engine/shared/auth_middleware.py
|
|
4
|
+
Every compliance MCP server imports this.
|
|
5
|
+
|
|
6
|
+
Usage in any server.py:
|
|
7
|
+
import sys, os
|
|
8
|
+
sys.path.insert(0, os.path.expanduser("~/clawd/meok-labs-engine/shared"))
|
|
9
|
+
from auth_middleware import check_access, require_tier, audit_log, Tier
|
|
10
|
+
|
|
11
|
+
@mcp.tool(name="my_tool")
|
|
12
|
+
async def my_tool(query: str, api_key: str = "") -> str:
|
|
13
|
+
allowed, msg, tier = check_access(api_key)
|
|
14
|
+
if not allowed:
|
|
15
|
+
return json.dumps({"error": msg, "upgrade_url": "https://buy.stripe.com/00wfZjcgAeUW4c5cyQ8k90K"})
|
|
16
|
+
# ... tool logic ...
|
|
17
|
+
audit_log(api_key, "my_tool", "eu_ai_act", "result_summary", tier)
|
|
18
|
+
return json.dumps(result)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import hashlib
|
|
23
|
+
import time
|
|
24
|
+
import json
|
|
25
|
+
from typing import Optional, Tuple
|
|
26
|
+
from enum import Enum
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Tier(str, Enum):
|
|
30
|
+
FREE = "free"
|
|
31
|
+
STARTER = "starter"
|
|
32
|
+
PROFESSIONAL = "professional"
|
|
33
|
+
ENTERPRISE = "enterprise"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
TIER_LIMITS = {
|
|
37
|
+
Tier.FREE: {"calls_per_day": 10, "frameworks": 1, "audit_trail": False},
|
|
38
|
+
Tier.STARTER: {"calls_per_day": 100, "frameworks": 1, "audit_trail": False},
|
|
39
|
+
Tier.PROFESSIONAL: {"calls_per_day": 1000, "frameworks": 5, "audit_trail": True},
|
|
40
|
+
Tier.ENTERPRISE: {"calls_per_day": -1, "frameworks": -1, "audit_trail": True},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
TIER_ORDER = [Tier.FREE, Tier.STARTER, Tier.PROFESSIONAL, Tier.ENTERPRISE]
|
|
44
|
+
|
|
45
|
+
MEOK_DIR = os.path.expanduser("~/.meok")
|
|
46
|
+
USAGE_FILE = os.path.join(MEOK_DIR, "usage.json")
|
|
47
|
+
KEYS_FILE = os.path.join(MEOK_DIR, "api_keys.json")
|
|
48
|
+
AUDIT_FILE = os.path.join(MEOK_DIR, "audit_trail.jsonl")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _ensure_dir():
|
|
52
|
+
os.makedirs(MEOK_DIR, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _load_json(path: str) -> dict:
|
|
56
|
+
_ensure_dir()
|
|
57
|
+
if os.path.exists(path):
|
|
58
|
+
try:
|
|
59
|
+
with open(path) as f:
|
|
60
|
+
return json.load(f)
|
|
61
|
+
except (json.JSONDecodeError, IOError):
|
|
62
|
+
return {}
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _save_json(path: str, data: dict):
|
|
67
|
+
_ensure_dir()
|
|
68
|
+
with open(path, "w") as f:
|
|
69
|
+
json.dump(data, f, indent=2)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def generate_api_key(tier: Tier, customer_name: str) -> str:
|
|
73
|
+
"""Generate a new API key for a customer. Run manually to onboard customers."""
|
|
74
|
+
raw = f"meok_{tier.value}_{customer_name}_{time.time()}"
|
|
75
|
+
key = f"meok_{hashlib.sha256(raw.encode()).hexdigest()[:32]}"
|
|
76
|
+
|
|
77
|
+
keys = _load_json(KEYS_FILE)
|
|
78
|
+
keys[key] = {
|
|
79
|
+
"tier": tier.value,
|
|
80
|
+
"customer": customer_name,
|
|
81
|
+
"created": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
82
|
+
"active": True,
|
|
83
|
+
}
|
|
84
|
+
_save_json(KEYS_FILE, keys)
|
|
85
|
+
return key
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_tier_from_api_key(api_key: str) -> Tier:
|
|
89
|
+
"""Look up tier for an API key."""
|
|
90
|
+
if not api_key:
|
|
91
|
+
return Tier.FREE
|
|
92
|
+
|
|
93
|
+
keys = _load_json(KEYS_FILE)
|
|
94
|
+
if api_key in keys and keys[api_key].get("active", True):
|
|
95
|
+
try:
|
|
96
|
+
return Tier(keys[api_key]["tier"])
|
|
97
|
+
except ValueError:
|
|
98
|
+
return Tier.FREE
|
|
99
|
+
|
|
100
|
+
return Tier.FREE
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def check_access(api_key: str = "", framework: str = None) -> Tuple[bool, str, Tier]:
|
|
104
|
+
"""
|
|
105
|
+
Main access control function. Returns (allowed, message, tier).
|
|
106
|
+
Call at the start of every tool.
|
|
107
|
+
"""
|
|
108
|
+
tier = get_tier_from_api_key(api_key)
|
|
109
|
+
limits = TIER_LIMITS[tier]
|
|
110
|
+
|
|
111
|
+
# Rate limit check
|
|
112
|
+
usage = _load_json(USAGE_FILE)
|
|
113
|
+
today = time.strftime("%Y-%m-%d")
|
|
114
|
+
key_hash = hashlib.sha256((api_key or "anon").encode()).hexdigest()[:12]
|
|
115
|
+
day_key = f"{key_hash}:{today}"
|
|
116
|
+
|
|
117
|
+
current = usage.get(day_key, 0)
|
|
118
|
+
max_calls = limits["calls_per_day"]
|
|
119
|
+
|
|
120
|
+
if max_calls != -1 and current >= max_calls:
|
|
121
|
+
return (
|
|
122
|
+
False,
|
|
123
|
+
f"Rate limit reached ({max_calls}/day on {tier.value} tier). "
|
|
124
|
+
f"Upgrade at https://buy.stripe.com/00wfZjcgAeUW4c5cyQ8k90K",
|
|
125
|
+
tier,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Record usage
|
|
129
|
+
usage[day_key] = current + 1
|
|
130
|
+
# Clean old entries (keep last 7 days)
|
|
131
|
+
cutoff = time.strftime("%Y-%m-%d", time.localtime(time.time() - 7 * 86400))
|
|
132
|
+
usage = {k: v for k, v in usage.items() if k.split(":")[1] >= cutoff}
|
|
133
|
+
_save_json(USAGE_FILE, usage)
|
|
134
|
+
|
|
135
|
+
return True, "OK", tier
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def require_tier(minimum: Tier, current: Tier) -> Tuple[bool, str]:
|
|
139
|
+
"""Check if current tier meets the minimum requirement for a tool."""
|
|
140
|
+
if TIER_ORDER.index(current) < TIER_ORDER.index(minimum):
|
|
141
|
+
return (
|
|
142
|
+
False,
|
|
143
|
+
f"Requires {minimum.value} tier. Current: {current.value}. "
|
|
144
|
+
f"Upgrade at https://buy.stripe.com/00wfZjcgAeUW4c5cyQ8k90K",
|
|
145
|
+
)
|
|
146
|
+
return True, "OK"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def audit_log(
|
|
150
|
+
api_key: str,
|
|
151
|
+
tool_name: str,
|
|
152
|
+
framework: str,
|
|
153
|
+
result_summary: str,
|
|
154
|
+
tier: Tier,
|
|
155
|
+
):
|
|
156
|
+
"""Append to audit trail. Only Professional and Enterprise tiers generate audit logs."""
|
|
157
|
+
if not TIER_LIMITS[tier]["audit_trail"]:
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
_ensure_dir()
|
|
161
|
+
entry = {
|
|
162
|
+
"ts": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
163
|
+
"tool": tool_name,
|
|
164
|
+
"framework": framework,
|
|
165
|
+
"result": result_summary[:200],
|
|
166
|
+
"tier": tier.value,
|
|
167
|
+
"key_prefix": (api_key or "")[:8] + "...",
|
|
168
|
+
}
|
|
169
|
+
with open(AUDIT_FILE, "a") as f:
|
|
170
|
+
f.write(json.dumps(entry) + "\n")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_usage_stats(api_key: str = "") -> dict:
|
|
174
|
+
"""Get usage statistics for an API key."""
|
|
175
|
+
usage = _load_json(USAGE_FILE)
|
|
176
|
+
tier = get_tier_from_api_key(api_key)
|
|
177
|
+
limits = TIER_LIMITS[tier]
|
|
178
|
+
|
|
179
|
+
key_hash = hashlib.sha256((api_key or "anon").encode()).hexdigest()[:12]
|
|
180
|
+
today = time.strftime("%Y-%m-%d")
|
|
181
|
+
day_key = f"{key_hash}:{today}"
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
"tier": tier.value,
|
|
185
|
+
"calls_today": usage.get(day_key, 0),
|
|
186
|
+
"limit": limits["calls_per_day"],
|
|
187
|
+
"remaining": max(0, limits["calls_per_day"] - usage.get(day_key, 0))
|
|
188
|
+
if limits["calls_per_day"] != -1 else "unlimited",
|
|
189
|
+
"audit_trail": limits["audit_trail"],
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# CLI for key management
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
import sys
|
|
196
|
+
if len(sys.argv) < 2:
|
|
197
|
+
print("Usage:")
|
|
198
|
+
print(" python auth_middleware.py generate <tier> <customer_name>")
|
|
199
|
+
print(" python auth_middleware.py list")
|
|
200
|
+
print(" python auth_middleware.py stats <api_key>")
|
|
201
|
+
print(f"\nTiers: {', '.join(t.value for t in Tier)}")
|
|
202
|
+
sys.exit(0)
|
|
203
|
+
|
|
204
|
+
cmd = sys.argv[1]
|
|
205
|
+
|
|
206
|
+
if cmd == "generate":
|
|
207
|
+
tier = Tier(sys.argv[2])
|
|
208
|
+
name = sys.argv[3]
|
|
209
|
+
key = generate_api_key(tier, name)
|
|
210
|
+
print(f"Generated key: {key}")
|
|
211
|
+
print(f"Tier: {tier.value}")
|
|
212
|
+
print(f"Customer: {name}")
|
|
213
|
+
|
|
214
|
+
elif cmd == "list":
|
|
215
|
+
keys = _load_json(KEYS_FILE)
|
|
216
|
+
for k, v in keys.items():
|
|
217
|
+
status = "active" if v.get("active", True) else "disabled"
|
|
218
|
+
print(f" {k[:20]}... | {v['tier']:15} | {v['customer']:20} | {status}")
|
|
219
|
+
|
|
220
|
+
elif cmd == "stats":
|
|
221
|
+
key = sys.argv[2]
|
|
222
|
+
stats = get_usage_stats(key)
|
|
223
|
+
print(json.dumps(stats, indent=2))
|
|
@@ -3,7 +3,7 @@ requires = ["hatchling"]
|
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
[project]
|
|
5
5
|
name = "vector-knowledge-graph-mcp"
|
|
6
|
-
version = "1.0.
|
|
6
|
+
version = "1.0.4"
|
|
7
7
|
description = "AI-powered vector knowledge graph MCP server for agents. Supports add node, add edge, semantic node search. By MEOK AI Labs."
|
|
8
8
|
license = {file = "LICENSE"}
|
|
9
9
|
requires-python = ">=3.10"
|
|
@@ -21,7 +21,7 @@ Homepage = "https://meok.ai"
|
|
|
21
21
|
Repository = "https://github.com/CSOAI-ORG/vector-knowledge-graph-mcp"
|
|
22
22
|
[tool.hatch.build.targets.wheel]
|
|
23
23
|
packages = ["."]
|
|
24
|
-
only-include = ["server.py"]
|
|
24
|
+
only-include = ["server.py", "auth_middleware.py"]
|
|
25
25
|
|
|
26
26
|
[project.scripts]
|
|
27
27
|
vector_knowledge_graph_mcp = "server:main"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
Buy Pro: https://www.csoai.org/checkout
|
|
4
|
-
|
|
4
|
+
Vector Knowledge Graph MCP Server — Neo4j-style graph + vector hybrid for compliance reasoning."""
|
|
5
5
|
|
|
6
6
|
import sys, os
|
|
7
7
|
from auth_middleware import check_access
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_knowledge_graph_mcp-1.0.3 → vector_knowledge_graph_mcp-1.0.4}/.github/workflows/test.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|