vector-knowledge-graph-mcp 1.0.2__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.
Files changed (25) hide show
  1. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.github/FUNDING.yml +1 -1
  2. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/PKG-INFO +1 -1
  3. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/README.md +16 -0
  4. vector_knowledge_graph_mcp-1.0.4/auth_middleware.py +223 -0
  5. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/pyproject.toml +2 -2
  6. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/server.py +8 -6
  7. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.cursorrules +0 -0
  8. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.github/workflows/mcp-smithery-publish.yml +0 -0
  9. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.github/workflows/test.yml +0 -0
  10. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.gitignore +0 -0
  11. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.mcp.json +0 -0
  12. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/.well-known/mcp/server-card.json +0 -0
  13. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/CODE_OF_CONDUCT.md +0 -0
  14. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/CONTRIBUTING.md +0 -0
  15. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/Dockerfile +0 -0
  16. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/Dockerfile.glama +0 -0
  17. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/LICENSE +0 -0
  18. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/SECURITY.md +0 -0
  19. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/glama.json +0 -0
  20. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/llms.txt +0 -0
  21. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/mcp-wrapper.py +0 -0
  22. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/package.json +0 -0
  23. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/pytest.ini +0 -0
  24. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/smithery.yaml +0 -0
  25. {vector_knowledge_graph_mcp-1.0.2 → vector_knowledge_graph_mcp-1.0.4}/tests/test_server.py +0 -0
@@ -3,6 +3,6 @@
3
3
 
4
4
  github: [CSOAI-ORG]
5
5
  custom:
6
- - "https://buy.stripe.com/eVq9AV4O87sudMF42k8k839"
6
+ - "https://buy.stripe.com/00wfZjcgAeUW4c5cyQ8k90K"
7
7
  - "https://meok.ai/sponsor"
8
8
  - "https://nlnet.nl/propose"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vector-knowledge-graph-mcp
3
- Version: 1.0.2
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
@@ -1,3 +1,5 @@
1
+ mcp-name: io.github.CSOAI-ORG/vector-knowledge-graph-mcp
2
+
1
3
  # Vector Knowledge Graph
2
4
 
3
5
  [![PyPI](https://img.shields.io/pypi/v/vector-knowledge-graph-mcp)](https://pypi.org/project/vector-knowledge-graph-mcp/) [![Python](https://img.shields.io/pypi/pyversions/vector-knowledge-graph-mcp)](https://pypi.org/project/vector-knowledge-graph-mcp/)
@@ -94,3 +96,17 @@ Free tier: 10 calls/day per MCP. Pro tier (£79/mo): unlimited + cryptographical
94
96
  → Full catalogue: [councilof.ai/catalogue](https://councilof.ai/catalogue)
95
97
  → MEOK AI Labs: [meok.ai](https://meok.ai)
96
98
 
99
+ <!-- BUY-LADDER:START -->
100
+
101
+ ## 💸 Try MEOK in 30 seconds — instant buy ladder
102
+
103
+ | Tier | Price | What you get | Stripe |
104
+ |---|---|---|---|
105
+ | Smoke test | **£1** | Signed sample MCP-Hardening report + Article 50 PDF | <https://buy.stripe.com/dRmcN75ScdQS7oh1Uc8k90U> |
106
+ | Quick Kit | **£9** | EU AI Act Article 50 implementation guide (C2PA + EU-Icon) | <https://buy.stripe.com/cNi00la8s1460ZT0Q88k90V> |
107
+ | Founder Call | **£29** | 30-min 1-on-1 with the founder | <https://buy.stripe.com/8x228ta8s6oqbExaqI8k90W> |
108
+
109
+ > Refundable. UK Stripe — VAT-clean. Builds on the 81-MCP MEOK fleet.
110
+ > Verify any signed report at <https://meok.ai/verify>.
111
+
112
+ <!-- BUY-LADDER:END -->
@@ -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.2"
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,5 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- """Vector Knowledge Graph MCP Server — Neo4j-style graph + vector hybrid for compliance reasoning."""
2
+ """
3
+ Buy Pro: https://www.csoai.org/checkout
4
+ Vector Knowledge Graph MCP Server — Neo4j-style graph + vector hybrid for compliance reasoning."""
3
5
 
4
6
  import sys, os
5
7
  from auth_middleware import check_access
@@ -36,7 +38,7 @@ def add_node(label: str, properties: dict, node_id: Optional[str] = None, api_ke
36
38
  """Add a node to the knowledge graph with properties, embeddings, and metadata."""
37
39
  allowed, msg, tier = check_access(api_key)
38
40
  if not allowed:
39
- return {"error": msg, "upgrade_url": "https://meok.ai/pricing"}
41
+ return {"error": msg, "upgrade_url": "https://councilof.ai"}
40
42
  if err := _rl(): return err
41
43
 
42
44
  nid = node_id or hashlib.md5(label.encode()).hexdigest()[:12]
@@ -48,7 +50,7 @@ def add_edge(from_id: str, to_id: str, relation: str, weight: float = 1.0, api_k
48
50
  """Create a directed edge between two nodes with relationship type and weight."""
49
51
  allowed, msg, tier = check_access(api_key)
50
52
  if not allowed:
51
- return {"error": msg, "upgrade_url": "https://meok.ai/pricing"}
53
+ return {"error": msg, "upgrade_url": "https://councilof.ai"}
52
54
  if err := _rl(): return err
53
55
 
54
56
  _EDGES.append({"from": from_id, "to": to_id, "relation": relation, "weight": weight})
@@ -59,7 +61,7 @@ def semantic_node_search(query: str, top_k: int = 5, api_key: str = "") -> str:
59
61
  """Search for nodes using semantic similarity matching against stored embeddings."""
60
62
  allowed, msg, tier = check_access(api_key)
61
63
  if not allowed:
62
- return {"error": msg, "upgrade_url": "https://meok.ai/pricing"}
64
+ return {"error": msg, "upgrade_url": "https://councilof.ai"}
63
65
  if err := _rl(): return err
64
66
 
65
67
  q_vec = _embed(query)
@@ -75,7 +77,7 @@ def trace_compliance_chain(start_node_id: str, max_depth: int = 3, api_key: str
75
77
  """Trace the compliance chain from a requirement through controls to evidence."""
76
78
  allowed, msg, tier = check_access(api_key)
77
79
  if not allowed:
78
- return {"error": msg, "upgrade_url": "https://meok.ai/pricing"}
80
+ return {"error": msg, "upgrade_url": "https://councilof.ai"}
79
81
  if err := _rl(): return err
80
82
 
81
83
  visited = set()
@@ -97,7 +99,7 @@ def find_gaps(required_frameworks: list, api_key: str = "") -> str:
97
99
  """Find gaps in the knowledge graph where expected relationships or nodes are missing."""
98
100
  allowed, msg, tier = check_access(api_key)
99
101
  if not allowed:
100
- return {"error": msg, "upgrade_url": "https://meok.ai/pricing"}
102
+ return {"error": msg, "upgrade_url": "https://councilof.ai"}
101
103
  if err := _rl(): return err
102
104
 
103
105
  present = set()