cks-mcp 0.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.
cks_mcp-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deus-corp
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.
cks_mcp-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: cks-mcp
3
+ Version: 0.1.1
4
+ Summary: CKS MCP Server — Canonical Knowledge Structure for LLMs
5
+ Author: Vladyslav Hruznov
6
+ License: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: cks-core>=1.1.1
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
14
+ Dynamic: license-file
15
+
16
+ # CKS MCP Server
17
+
18
+ > Model Context Protocol server for Canonical Knowledge Structure.
19
+
20
+ ![Python](https://img.shields.io/badge/python-3.12%2B-blue)
21
+ ![License](https://img.shields.io/badge/license-MIT-green)
22
+ ![Tests](https://img.shields.io/badge/tests-20%20passing-brightgreen)
23
+
24
+ `cks-mcp` is an MCP (Model Context Protocol) server that provides LLMs
25
+ with structured, verifiable knowledge operations through the CKS
26
+ ecosystem. It exposes six tools—validate, query, compare, evolve,
27
+ derive, and construct—each backed by the deterministic, immutable
28
+ semantics of `cks-core`.
29
+
30
+ ---
31
+
32
+ # Why cks-mcp?
33
+
34
+ LLMs generate plausible but unverified statements. `cks-mcp` gives them
35
+ a **canonical knowledge backbone**: every piece of information must be
36
+ explicitly structured, validated against formal constraints, and
37
+ traceable to its origin. This minimises hallucinations and makes AI‑
38
+ generated knowledge auditable.
39
+
40
+ ---
41
+
42
+ # Installation
43
+
44
+ ```bash
45
+ pip install cks-mcp
46
+ ```
47
+
48
+ The server requires `cks-core` (installed automatically as a dependency).
49
+
50
+ ---
51
+
52
+ # Quick Start
53
+
54
+ Launch the server:
55
+
56
+ ```bash
57
+ cks-mcp
58
+ ```
59
+
60
+ An MCP client (Claude Desktop, any MCP-compatible LLM) can then connect
61
+ and call tools.
62
+
63
+ ---
64
+
65
+ # Available Tools
66
+
67
+ | Tool | Description |
68
+ |------|-------------|
69
+ | `validate_knowledge` | Validate a Knowledge Structure and return diagnostics. |
70
+ | `query_relations` | Find all relations for a given entity. |
71
+ | `compare_structures`| Check semantic equivalence of two structures. |
72
+ | `evolve_knowledge` | Apply Genesis/Decay operators to evolve a structure. |
73
+ | `derive_knowledge` | Derive a new Knowledge Object from existing premises. |
74
+ | `construct_knowledge`| Parse and construct a Knowledge Structure (coming soon). |
75
+
76
+ ---
77
+
78
+ # Usage Example
79
+
80
+ ```json
81
+ {
82
+ "jsonrpc": "2.0",
83
+ "id": 1,
84
+ "method": "tools/call",
85
+ "params": {
86
+ "name": "validate_knowledge",
87
+ "arguments": {
88
+ "json_data": "{\"objects\":[{\"identity\":{\"id\":\"obj-1\",\"type\":\"Definition\",\"name\":\"Test\"},\"structure\":{}}]}"
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ Response:
95
+
96
+ ```json
97
+ {
98
+ "jsonrpc": "2.0",
99
+ "id": 1,
100
+ "result": "{\"valid\": true, \"error_count\": 0, \"warning_count\": 0, \"diagnostics\": []}"
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ # Testing
107
+
108
+ ```bash
109
+ python -m pytest -v
110
+ ```
111
+
112
+ 20 tests, all passing.
113
+
114
+ ---
115
+
116
+ # Ecosystem
117
+
118
+ - **cks-core** — the canonical knowledge engine ([repo](https://github.com/Deus-corp/CKS))
119
+ - **CKS Specifications** — formal theory behind the system ([DOI](https://zenodo.org/records/21332624))
120
+
121
+ ---
122
+
123
+ # License
124
+
125
+ MIT
@@ -0,0 +1,110 @@
1
+ # CKS MCP Server
2
+
3
+ > Model Context Protocol server for Canonical Knowledge Structure.
4
+
5
+ ![Python](https://img.shields.io/badge/python-3.12%2B-blue)
6
+ ![License](https://img.shields.io/badge/license-MIT-green)
7
+ ![Tests](https://img.shields.io/badge/tests-20%20passing-brightgreen)
8
+
9
+ `cks-mcp` is an MCP (Model Context Protocol) server that provides LLMs
10
+ with structured, verifiable knowledge operations through the CKS
11
+ ecosystem. It exposes six tools—validate, query, compare, evolve,
12
+ derive, and construct—each backed by the deterministic, immutable
13
+ semantics of `cks-core`.
14
+
15
+ ---
16
+
17
+ # Why cks-mcp?
18
+
19
+ LLMs generate plausible but unverified statements. `cks-mcp` gives them
20
+ a **canonical knowledge backbone**: every piece of information must be
21
+ explicitly structured, validated against formal constraints, and
22
+ traceable to its origin. This minimises hallucinations and makes AI‑
23
+ generated knowledge auditable.
24
+
25
+ ---
26
+
27
+ # Installation
28
+
29
+ ```bash
30
+ pip install cks-mcp
31
+ ```
32
+
33
+ The server requires `cks-core` (installed automatically as a dependency).
34
+
35
+ ---
36
+
37
+ # Quick Start
38
+
39
+ Launch the server:
40
+
41
+ ```bash
42
+ cks-mcp
43
+ ```
44
+
45
+ An MCP client (Claude Desktop, any MCP-compatible LLM) can then connect
46
+ and call tools.
47
+
48
+ ---
49
+
50
+ # Available Tools
51
+
52
+ | Tool | Description |
53
+ |------|-------------|
54
+ | `validate_knowledge` | Validate a Knowledge Structure and return diagnostics. |
55
+ | `query_relations` | Find all relations for a given entity. |
56
+ | `compare_structures`| Check semantic equivalence of two structures. |
57
+ | `evolve_knowledge` | Apply Genesis/Decay operators to evolve a structure. |
58
+ | `derive_knowledge` | Derive a new Knowledge Object from existing premises. |
59
+ | `construct_knowledge`| Parse and construct a Knowledge Structure (coming soon). |
60
+
61
+ ---
62
+
63
+ # Usage Example
64
+
65
+ ```json
66
+ {
67
+ "jsonrpc": "2.0",
68
+ "id": 1,
69
+ "method": "tools/call",
70
+ "params": {
71
+ "name": "validate_knowledge",
72
+ "arguments": {
73
+ "json_data": "{\"objects\":[{\"identity\":{\"id\":\"obj-1\",\"type\":\"Definition\",\"name\":\"Test\"},\"structure\":{}}]}"
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Response:
80
+
81
+ ```json
82
+ {
83
+ "jsonrpc": "2.0",
84
+ "id": 1,
85
+ "result": "{\"valid\": true, \"error_count\": 0, \"warning_count\": 0, \"diagnostics\": []}"
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ # Testing
92
+
93
+ ```bash
94
+ python -m pytest -v
95
+ ```
96
+
97
+ 20 tests, all passing.
98
+
99
+ ---
100
+
101
+ # Ecosystem
102
+
103
+ - **cks-core** — the canonical knowledge engine ([repo](https://github.com/Deus-corp/CKS))
104
+ - **CKS Specifications** — formal theory behind the system ([DOI](https://zenodo.org/records/21332624))
105
+
106
+ ---
107
+
108
+ # License
109
+
110
+ MIT
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cks-mcp"
7
+ version = "0.1.1"
8
+ description = "CKS MCP Server — Canonical Knowledge Structure for LLMs"
9
+ readme = "README.md"
10
+ authors = [{name = "Vladyslav Hruznov"}]
11
+ license = {text = "MIT"}
12
+ requires-python = ">=3.12"
13
+ dependencies = [
14
+ "cks-core>=1.1.1",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0",
20
+ "pytest-asyncio>=0.24",
21
+ ]
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [project.scripts]
27
+ cks-mcp = "cks_mcp.server:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,212 @@
1
+ """
2
+ CKS MCP Server — JSON-RPC over stdio.
3
+
4
+ Implements the Model Context Protocol (MCP) to expose CKS tools
5
+ to LLM clients.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import json
12
+ import sys
13
+ from typing import Any, Callable, Dict, Optional
14
+
15
+ from .tools.validate import validate_knowledge
16
+ from .tools.query import query_relations
17
+ from .tools.compare import compare_structures
18
+ from .tools.evolve import evolve_knowledge
19
+ from .tools.derive import derive_knowledge
20
+
21
+
22
+ # Registry of available tools
23
+ TOOLS: Dict[str, Dict[str, Any]] = {
24
+ "validate_knowledge": {
25
+ "name": "validate_knowledge",
26
+ "description": "Validate a canonical knowledge structure and return diagnostics.",
27
+ "inputSchema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "json_data": {
31
+ "type": "string",
32
+ "description": "A JSON string representing a Knowledge Structure.",
33
+ },
34
+ },
35
+ "required": ["json_data"],
36
+ },
37
+ },
38
+ "query_relations": {
39
+ "name": "query_relations",
40
+ "description": "Query all relations for a given entity.",
41
+ "inputSchema": {
42
+ "type": "object",
43
+ "properties": {
44
+ "json_data": {
45
+ "type": "string",
46
+ "description": "A JSON string representing a Knowledge Structure.",
47
+ },
48
+ "entity_id": {
49
+ "type": "string",
50
+ "description": "The canonical identity of the entity to query.",
51
+ },
52
+ },
53
+ "required": ["json_data", "entity_id"],
54
+ },
55
+ },
56
+ "compare_structures": {
57
+ "name": "compare_structures",
58
+ "description": "Compare two knowledge structures for semantic equivalence.",
59
+ "inputSchema": {
60
+ "type": "object",
61
+ "properties": {
62
+ "json_data_a": {
63
+ "type": "string",
64
+ "description": "A JSON string representing the first Knowledge Structure.",
65
+ },
66
+ "json_data_b": {
67
+ "type": "string",
68
+ "description": "A JSON string representing the second Knowledge Structure.",
69
+ },
70
+ },
71
+ "required": ["json_data_a", "json_data_b"],
72
+ },
73
+ },
74
+ "evolve_knowledge": {
75
+ "name": "evolve_knowledge",
76
+ "description": "Apply a sequence of structural operators to a knowledge structure.",
77
+ "inputSchema": {
78
+ "type": "object",
79
+ "properties": {
80
+ "json_data": {
81
+ "type": "string",
82
+ "description": "A JSON string representing a Knowledge Structure.",
83
+ },
84
+ "operations": {
85
+ "type": "string",
86
+ "description": "A JSON string with an array of operation objects.",
87
+ },
88
+ },
89
+ "required": ["json_data", "operations"],
90
+ },
91
+ },
92
+ "derive_knowledge": {
93
+ "name": "derive_knowledge",
94
+ "description": "Derive a new Knowledge Object from existing premises.",
95
+ "inputSchema": {
96
+ "type": "object",
97
+ "properties": {
98
+ "json_data": {
99
+ "type": "string",
100
+ "description": "A JSON string representing a Knowledge Structure.",
101
+ },
102
+ "premises": {
103
+ "type": "string",
104
+ "description": "A JSON array of canonical identities used as premises.",
105
+ },
106
+ "rule": {
107
+ "type": "string",
108
+ "description": "The canonical inference rule identifier.",
109
+ },
110
+ "conclusion_id": {
111
+ "type": "string",
112
+ "description": "Canonical identity for the new Knowledge Object.",
113
+ },
114
+ "conclusion_type": {
115
+ "type": "string",
116
+ "description": "Canonical type for the new Knowledge Object.",
117
+ },
118
+ "conclusion_name": {
119
+ "type": "string",
120
+ "description": "Human-readable name for the new Knowledge Object.",
121
+ },
122
+ },
123
+ "required": ["json_data", "premises", "rule", "conclusion_id", "conclusion_type", "conclusion_name"],
124
+ },
125
+ },
126
+ }
127
+
128
+ # Mapping from tool name to callable
129
+ HANDLERS: Dict[str, Callable[..., str]] = {
130
+ "validate_knowledge": validate_knowledge,
131
+ "query_relations": query_relations,
132
+ "compare_structures": compare_structures,
133
+ "evolve_knowledge": evolve_knowledge,
134
+ "derive_knowledge": derive_knowledge,
135
+ }
136
+
137
+
138
+ async def handle_request(request: Dict[str, Any]) -> Optional[Dict[str, Any]]:
139
+ """Handle a single JSON-RPC request and return a response."""
140
+ method = request.get("method")
141
+ params = request.get("params", {})
142
+ req_id = request.get("id")
143
+
144
+ if method == "tools/list":
145
+ return {
146
+ "jsonrpc": "2.0",
147
+ "id": req_id,
148
+ "result": list(TOOLS.values()),
149
+ }
150
+ elif method == "tools/call":
151
+ tool_name = params.get("name")
152
+ tool_args = params.get("arguments", {})
153
+ handler = HANDLERS.get(tool_name)
154
+ if handler is None:
155
+ return {
156
+ "jsonrpc": "2.0",
157
+ "id": req_id,
158
+ "error": {"code": -32601, "message": f"Tool '{tool_name}' not found"},
159
+ }
160
+ try:
161
+ result = handler(**tool_args)
162
+ return {
163
+ "jsonrpc": "2.0",
164
+ "id": req_id,
165
+ "result": result,
166
+ }
167
+ except Exception as exc:
168
+ return {
169
+ "jsonrpc": "2.0",
170
+ "id": req_id,
171
+ "error": {"code": -32000, "message": str(exc)},
172
+ }
173
+
174
+ return {
175
+ "jsonrpc": "2.0",
176
+ "id": req_id,
177
+ "error": {"code": -32601, "message": f"Method '{method}' not found"},
178
+ }
179
+
180
+
181
+ async def main_loop() -> None:
182
+ """Read JSON-RPC requests from stdin and write responses to stdout."""
183
+ reader = asyncio.StreamReader()
184
+ await asyncio.get_event_loop().connect_read_pipe(
185
+ lambda: asyncio.StreamReaderProtocol(reader),
186
+ sys.stdin,
187
+ )
188
+
189
+ writer = asyncio.StreamWriter(sys.stdout.buffer, None, None, None)
190
+
191
+ while True:
192
+ line = await reader.readline()
193
+ if not line:
194
+ break
195
+ try:
196
+ request = json.loads(line.decode("utf-8"))
197
+ except json.JSONDecodeError:
198
+ continue
199
+
200
+ response = await handle_request(request)
201
+ if response is not None:
202
+ writer.write(json.dumps(response).encode("utf-8") + b"\n")
203
+ await writer.drain()
204
+
205
+
206
+ def main() -> None:
207
+ """Entry point for the MCP server."""
208
+ asyncio.run(main_loop())
209
+
210
+
211
+ if __name__ == "__main__":
212
+ main()
File without changes
@@ -0,0 +1,51 @@
1
+ """
2
+ MCP Tool: compare_structures.
3
+
4
+ Compares two knowledge structures for semantic equivalence.
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict
9
+
10
+ from cks.serialization import parse as cks_parse, SerializationError
11
+ from cks.core import KnowledgeStructure
12
+
13
+
14
+ def compare_structures(json_data_a: str, json_data_b: str) -> str:
15
+ """
16
+ Compare two knowledge structures for semantic equivalence.
17
+
18
+ Args:
19
+ json_data_a: A JSON string representing the first Knowledge Structure.
20
+ json_data_b: A JSON string representing the second Knowledge Structure.
21
+
22
+ Returns:
23
+ A JSON string with equivalence result and details.
24
+ """
25
+ try:
26
+ data_a = json.loads(json_data_a) if isinstance(json_data_a, str) else json_data_a
27
+ structure_a = cks_parse(data_a)
28
+ except (json.JSONDecodeError, SerializationError) as exc:
29
+ return json.dumps({
30
+ "error": f"Failed to parse first structure: {exc}",
31
+ "equivalent": False,
32
+ })
33
+
34
+ try:
35
+ data_b = json.loads(json_data_b) if isinstance(json_data_b, str) else json_data_b
36
+ structure_b = cks_parse(data_b)
37
+ except (json.JSONDecodeError, SerializationError) as exc:
38
+ return json.dumps({
39
+ "error": f"Failed to parse second structure: {exc}",
40
+ "equivalent": False,
41
+ })
42
+
43
+ is_equivalent = structure_a.structurally_equivalent(structure_b)
44
+
45
+ return json.dumps({
46
+ "equivalent": is_equivalent,
47
+ "objects_a": len(structure_a.objects),
48
+ "objects_b": len(structure_b.objects),
49
+ "relations_a": len(structure_a.relations()),
50
+ "relations_b": len(structure_b.relations()),
51
+ })
@@ -0,0 +1,58 @@
1
+ """
2
+ MCP Tool: derive_knowledge.
3
+
4
+ Creates a new Knowledge Object derived from existing ones via a canonical derivation.
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict, List
9
+
10
+ from cks.serialization import parse as cks_parse, SerializationError
11
+ from cks.core import KnowledgeObject, ObjectIdentity
12
+
13
+
14
+ def derive_knowledge(json_data: str, premises: str, rule: str, conclusion_id: str, conclusion_type: str, conclusion_name: str) -> str:
15
+ """
16
+ Derive a new Knowledge Object from existing premises.
17
+
18
+ Args:
19
+ json_data: A JSON string representing a Knowledge Structure.
20
+ premises: A JSON array of canonical identities used as premises.
21
+ rule: The canonical inference rule identifier.
22
+ conclusion_id: Canonical identity for the new Knowledge Object.
23
+ conclusion_type: Canonical type for the new Knowledge Object.
24
+ conclusion_name: Human-readable name for the new Knowledge Object.
25
+
26
+ Returns:
27
+ A JSON string with the updated Knowledge Structure containing the derivation.
28
+ """
29
+ try:
30
+ data = json.loads(json_data) if isinstance(json_data, str) else json_data
31
+ structure = cks_parse(data)
32
+ except (json.JSONDecodeError, SerializationError) as exc:
33
+ return json.dumps({"error": f"Failed to parse input: {exc}"})
34
+
35
+ premise_ids = json.loads(premises) if isinstance(premises, str) else premises
36
+ for pid in premise_ids:
37
+ if structure.get(pid) is None:
38
+ return json.dumps({"error": f"Premise '{pid}' not found in structure"})
39
+
40
+ if structure.get(conclusion_id) is not None:
41
+ return json.dumps({"error": f"Conclusion '{conclusion_id}' already exists in structure"})
42
+
43
+ conclusion = KnowledgeObject(
44
+ identity=ObjectIdentity(id=conclusion_id, type=conclusion_type, name=conclusion_name),
45
+ structure={
46
+ "derived_from": premise_ids,
47
+ "rule": rule,
48
+ },
49
+ )
50
+
51
+ # Build a new structure with the conclusion added
52
+ objects = list(structure.objects)
53
+ objects.append(conclusion)
54
+ from cks.core import KnowledgeStructure
55
+ new_structure = KnowledgeStructure(objects)
56
+
57
+ from cks.serialization import serialize as cks_serialize
58
+ return cks_serialize(new_structure)
@@ -0,0 +1,92 @@
1
+ """
2
+ MCP Tool: evolve_knowledge.
3
+
4
+ Applies a sequence of structural operators to a knowledge structure.
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict, List
9
+
10
+ from cks.serialization import parse as cks_parse, SerializationError
11
+ from cks.evolution import compose, AddObject, AddRelation, RemoveObject, RemoveRelation
12
+ from cks.core import KnowledgeObject, CanonicalRelation, ObjectIdentity
13
+
14
+
15
+ def _parse_operations(ops_data: List[Dict[str, Any]]) -> List[Any]:
16
+ """Parse a list of operation dictionaries into structural operators."""
17
+ operators = []
18
+ for i, op in enumerate(ops_data):
19
+ op_type = op.get("type")
20
+ if op_type is None:
21
+ raise ValueError(f"Operation #{i}: missing 'type' field")
22
+ if op_type == "add_object":
23
+ identity_data = op.get("identity")
24
+ if identity_data is None:
25
+ raise ValueError(f"Operation #{i}: missing 'identity' field")
26
+ identity = ObjectIdentity(**identity_data)
27
+ obj = KnowledgeObject(identity=identity, structure=op.get("structure", {}))
28
+ operators.append(AddObject(obj))
29
+ elif op_type == "add_relation":
30
+ identity_data = op.get("identity")
31
+ if identity_data is None:
32
+ raise ValueError(f"Operation #{i}: missing 'identity' field")
33
+ identity = ObjectIdentity(**identity_data)
34
+ participants = op.get("participants")
35
+ if participants is None:
36
+ raise ValueError(f"Operation #{i}: missing 'participants' field")
37
+ relation_type = op.get("relation_type")
38
+ if relation_type is None:
39
+ raise ValueError(f"Operation #{i}: missing 'relation_type' field")
40
+ relation = CanonicalRelation(
41
+ identity=identity,
42
+ participants=participants,
43
+ relation_type=relation_type,
44
+ structure=op.get("structure", {}),
45
+ )
46
+ operators.append(AddRelation(relation))
47
+ elif op_type == "remove_object":
48
+ object_id = op.get("object_id")
49
+ if object_id is None:
50
+ raise ValueError(f"Operation #{i}: missing 'object_id' field")
51
+ operators.append(RemoveObject(object_id))
52
+ elif op_type == "remove_relation":
53
+ relation_id = op.get("relation_id")
54
+ if relation_id is None:
55
+ raise ValueError(f"Operation #{i}: missing 'relation_id' field")
56
+ operators.append(RemoveRelation(relation_id))
57
+ else:
58
+ raise ValueError(f"Operation #{i}: unknown operation type '{op_type}'")
59
+ return operators
60
+
61
+
62
+ def evolve_knowledge(json_data: str, operations: str) -> str:
63
+ """
64
+ Apply a sequence of structural operators to a knowledge structure.
65
+
66
+ Args:
67
+ json_data: A JSON string representing a Knowledge Structure.
68
+ operations: A JSON string with an array of operation objects.
69
+
70
+ Returns:
71
+ A JSON string with the evolved Knowledge Structure.
72
+ """
73
+ try:
74
+ data = json.loads(json_data) if isinstance(json_data, str) else json_data
75
+ structure = cks_parse(data)
76
+ except (json.JSONDecodeError, SerializationError) as exc:
77
+ return json.dumps({"error": f"Failed to parse input: {exc}"})
78
+
79
+ try:
80
+ ops_data = json.loads(operations) if isinstance(operations, str) else operations
81
+ operators = _parse_operations(ops_data)
82
+ except (json.JSONDecodeError, ValueError) as exc:
83
+ return json.dumps({"error": f"Failed to parse operations: {exc}"})
84
+
85
+ try:
86
+ evolved = compose(structure, operators)
87
+ except Exception as exc:
88
+ return json.dumps({"error": f"Evolution failed: {exc}"})
89
+
90
+ # Serialize back to JSON
91
+ from cks.serialization import serialize as cks_serialize
92
+ return cks_serialize(evolved)
@@ -0,0 +1,45 @@
1
+ """
2
+ MCP Tool: query_relations.
3
+
4
+ Returns all canonical relations for a given entity.
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict, List
9
+
10
+ from cks.serialization import parse as cks_parse, SerializationError
11
+
12
+
13
+ def query_relations(json_data: str, entity_id: str) -> str:
14
+ """
15
+ Query all relations for a given entity in a knowledge structure.
16
+
17
+ Args:
18
+ json_data: A JSON string representing a Knowledge Structure.
19
+ entity_id: The canonical identity of the entity to query.
20
+
21
+ Returns:
22
+ A JSON string with the list of relations involving the entity.
23
+ """
24
+ try:
25
+ data = json.loads(json_data) if isinstance(json_data, str) else json_data
26
+ structure = cks_parse(data)
27
+ except (json.JSONDecodeError, SerializationError) as exc:
28
+ return json.dumps({
29
+ "error": f"Failed to parse input: {exc}",
30
+ "relations": [],
31
+ })
32
+
33
+ relations: List[Dict[str, Any]] = []
34
+ for rel in structure.relations():
35
+ if entity_id in rel.participants:
36
+ relations.append({
37
+ "relation_id": rel.identity.id,
38
+ "relation_type": rel.relation_type,
39
+ "participants": list(rel.participants),
40
+ })
41
+
42
+ return json.dumps({
43
+ "entity_id": entity_id,
44
+ "relations": relations,
45
+ })
@@ -0,0 +1,51 @@
1
+ """
2
+ MCP Tool: validate_knowledge.
3
+
4
+ Validates a canonical knowledge structure and returns diagnostics.
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict
9
+
10
+ from cks.serialization import parse as cks_parse, SerializationError
11
+ from cks.validator import validate as cks_validate
12
+ from cks.diagnostics import DiagnosticSeverity
13
+
14
+
15
+ def validate_knowledge(json_data: str) -> str:
16
+ """
17
+ Validate a knowledge structure in canonical JSON format.
18
+
19
+ Args:
20
+ json_data: A JSON string representing a Knowledge Structure.
21
+
22
+ Returns:
23
+ A JSON string with validation result and diagnostics.
24
+ """
25
+ try:
26
+ data = json.loads(json_data) if isinstance(json_data, str) else json_data
27
+ structure = cks_parse(data)
28
+ except (json.JSONDecodeError, SerializationError) as exc:
29
+ return json.dumps({
30
+ "valid": False,
31
+ "error": f"Failed to parse input: {exc}",
32
+ "diagnostics": [],
33
+ })
34
+
35
+ result = cks_validate(structure)
36
+
37
+ diagnostics = []
38
+ for diag in result.diagnostics:
39
+ diagnostics.append({
40
+ "identity": diag.identity,
41
+ "severity": diag.severity.value,
42
+ "message": diag.message,
43
+ "location": diag.location,
44
+ })
45
+
46
+ return json.dumps({
47
+ "valid": result.is_valid,
48
+ "error_count": result.error_count,
49
+ "warning_count": result.warning_count,
50
+ "diagnostics": diagnostics,
51
+ })
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: cks-mcp
3
+ Version: 0.1.1
4
+ Summary: CKS MCP Server — Canonical Knowledge Structure for LLMs
5
+ Author: Vladyslav Hruznov
6
+ License: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: cks-core>=1.1.1
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
14
+ Dynamic: license-file
15
+
16
+ # CKS MCP Server
17
+
18
+ > Model Context Protocol server for Canonical Knowledge Structure.
19
+
20
+ ![Python](https://img.shields.io/badge/python-3.12%2B-blue)
21
+ ![License](https://img.shields.io/badge/license-MIT-green)
22
+ ![Tests](https://img.shields.io/badge/tests-20%20passing-brightgreen)
23
+
24
+ `cks-mcp` is an MCP (Model Context Protocol) server that provides LLMs
25
+ with structured, verifiable knowledge operations through the CKS
26
+ ecosystem. It exposes six tools—validate, query, compare, evolve,
27
+ derive, and construct—each backed by the deterministic, immutable
28
+ semantics of `cks-core`.
29
+
30
+ ---
31
+
32
+ # Why cks-mcp?
33
+
34
+ LLMs generate plausible but unverified statements. `cks-mcp` gives them
35
+ a **canonical knowledge backbone**: every piece of information must be
36
+ explicitly structured, validated against formal constraints, and
37
+ traceable to its origin. This minimises hallucinations and makes AI‑
38
+ generated knowledge auditable.
39
+
40
+ ---
41
+
42
+ # Installation
43
+
44
+ ```bash
45
+ pip install cks-mcp
46
+ ```
47
+
48
+ The server requires `cks-core` (installed automatically as a dependency).
49
+
50
+ ---
51
+
52
+ # Quick Start
53
+
54
+ Launch the server:
55
+
56
+ ```bash
57
+ cks-mcp
58
+ ```
59
+
60
+ An MCP client (Claude Desktop, any MCP-compatible LLM) can then connect
61
+ and call tools.
62
+
63
+ ---
64
+
65
+ # Available Tools
66
+
67
+ | Tool | Description |
68
+ |------|-------------|
69
+ | `validate_knowledge` | Validate a Knowledge Structure and return diagnostics. |
70
+ | `query_relations` | Find all relations for a given entity. |
71
+ | `compare_structures`| Check semantic equivalence of two structures. |
72
+ | `evolve_knowledge` | Apply Genesis/Decay operators to evolve a structure. |
73
+ | `derive_knowledge` | Derive a new Knowledge Object from existing premises. |
74
+ | `construct_knowledge`| Parse and construct a Knowledge Structure (coming soon). |
75
+
76
+ ---
77
+
78
+ # Usage Example
79
+
80
+ ```json
81
+ {
82
+ "jsonrpc": "2.0",
83
+ "id": 1,
84
+ "method": "tools/call",
85
+ "params": {
86
+ "name": "validate_knowledge",
87
+ "arguments": {
88
+ "json_data": "{\"objects\":[{\"identity\":{\"id\":\"obj-1\",\"type\":\"Definition\",\"name\":\"Test\"},\"structure\":{}}]}"
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ Response:
95
+
96
+ ```json
97
+ {
98
+ "jsonrpc": "2.0",
99
+ "id": 1,
100
+ "result": "{\"valid\": true, \"error_count\": 0, \"warning_count\": 0, \"diagnostics\": []}"
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ # Testing
107
+
108
+ ```bash
109
+ python -m pytest -v
110
+ ```
111
+
112
+ 20 tests, all passing.
113
+
114
+ ---
115
+
116
+ # Ecosystem
117
+
118
+ - **cks-core** — the canonical knowledge engine ([repo](https://github.com/Deus-corp/CKS))
119
+ - **CKS Specifications** — formal theory behind the system ([DOI](https://zenodo.org/records/21332624))
120
+
121
+ ---
122
+
123
+ # License
124
+
125
+ MIT
@@ -0,0 +1,23 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/cks_mcp/__init__.py
5
+ src/cks_mcp/server.py
6
+ src/cks_mcp.egg-info/PKG-INFO
7
+ src/cks_mcp.egg-info/SOURCES.txt
8
+ src/cks_mcp.egg-info/dependency_links.txt
9
+ src/cks_mcp.egg-info/entry_points.txt
10
+ src/cks_mcp.egg-info/requires.txt
11
+ src/cks_mcp.egg-info/top_level.txt
12
+ src/cks_mcp/tools/__init__.py
13
+ src/cks_mcp/tools/compare.py
14
+ src/cks_mcp/tools/derive.py
15
+ src/cks_mcp/tools/evolve.py
16
+ src/cks_mcp/tools/query.py
17
+ src/cks_mcp/tools/validate.py
18
+ tests/test_compare.py
19
+ tests/test_derive.py
20
+ tests/test_evolve.py
21
+ tests/test_query.py
22
+ tests/test_server.py
23
+ tests/test_validate.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cks-mcp = cks_mcp.server:main
@@ -0,0 +1,5 @@
1
+ cks-core>=1.1.1
2
+
3
+ [dev]
4
+ pytest>=8.0
5
+ pytest-asyncio>=0.24
@@ -0,0 +1 @@
1
+ cks_mcp
@@ -0,0 +1,42 @@
1
+ """Tests for compare_structures tool."""
2
+ import json
3
+ from cks_mcp.tools.compare import compare_structures
4
+
5
+
6
+ def test_compare_equivalent():
7
+ """Two identical structures should be equivalent."""
8
+ data_a = json.dumps({
9
+ "objects": [
10
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
11
+ ]
12
+ })
13
+ data_b = json.dumps({
14
+ "objects": [
15
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
16
+ ]
17
+ })
18
+ result = json.loads(compare_structures(data_a, data_b))
19
+ assert result["equivalent"] is True
20
+
21
+
22
+ def test_compare_different():
23
+ """Structures with different objects should not be equivalent."""
24
+ data_a = json.dumps({
25
+ "objects": [
26
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
27
+ ]
28
+ })
29
+ data_b = json.dumps({
30
+ "objects": [
31
+ {"identity": {"id": "obj-2", "type": "Definition", "name": "Other"}, "structure": {}},
32
+ ]
33
+ })
34
+ result = json.loads(compare_structures(data_a, data_b))
35
+ assert result["equivalent"] is False
36
+
37
+
38
+ def test_compare_parse_error():
39
+ """Malformed input should return an error."""
40
+ result = json.loads(compare_structures("not json", json.dumps({"objects": []})))
41
+ assert result["equivalent"] is False
42
+ assert "error" in result
@@ -0,0 +1,59 @@
1
+ """Tests for derive_knowledge tool."""
2
+ import json
3
+ from cks_mcp.tools.derive import derive_knowledge
4
+
5
+
6
+ def test_derive_success():
7
+ """Derive a new object from existing premises."""
8
+ data = json.dumps({
9
+ "objects": [
10
+ {"identity": {"id": "prem-1", "type": "Axiom", "name": "A"}, "structure": {}},
11
+ {"identity": {"id": "prem-2", "type": "Axiom", "name": "B"}, "structure": {}},
12
+ ]
13
+ })
14
+ result = derive_knowledge(
15
+ json_data=data,
16
+ premises=json.dumps(["prem-1", "prem-2"]),
17
+ rule="modus_ponens",
18
+ conclusion_id="conc-1",
19
+ conclusion_type="Theorem",
20
+ conclusion_name="C",
21
+ )
22
+ assert "conc-1" in result
23
+
24
+
25
+ def test_derive_missing_premise():
26
+ """Derivation with missing premise should return an error."""
27
+ data = json.dumps({
28
+ "objects": [
29
+ {"identity": {"id": "prem-1", "type": "Axiom", "name": "A"}, "structure": {}},
30
+ ]
31
+ })
32
+ result = json.loads(derive_knowledge(
33
+ json_data=data,
34
+ premises=json.dumps(["prem-1", "prem-missing"]),
35
+ rule="modus_ponens",
36
+ conclusion_id="conc-1",
37
+ conclusion_type="Theorem",
38
+ conclusion_name="C",
39
+ ))
40
+ assert "error" in result
41
+
42
+
43
+ def test_derive_duplicate_id():
44
+ """Derivation with duplicate conclusion ID should return an error."""
45
+ data = json.dumps({
46
+ "objects": [
47
+ {"identity": {"id": "prem-1", "type": "Axiom", "name": "A"}, "structure": {}},
48
+ {"identity": {"id": "conc-1", "type": "Theorem", "name": "Already exists"}, "structure": {}},
49
+ ]
50
+ })
51
+ result = json.loads(derive_knowledge(
52
+ json_data=data,
53
+ premises=json.dumps(["prem-1"]),
54
+ rule="modus_ponens",
55
+ conclusion_id="conc-1",
56
+ conclusion_type="Theorem",
57
+ conclusion_name="C",
58
+ ))
59
+ assert "error" in result
@@ -0,0 +1,49 @@
1
+ """Tests for evolve_knowledge tool."""
2
+ import json
3
+ from cks_mcp.tools.evolve import evolve_knowledge
4
+
5
+
6
+ def test_evolve_add_object():
7
+ """Add a new object via evolution."""
8
+ data = json.dumps({
9
+ "objects": [
10
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
11
+ ]
12
+ })
13
+ ops = json.dumps([
14
+ {
15
+ "type": "add_object",
16
+ "identity": {"id": "obj-2", "type": "Definition", "name": "New"},
17
+ "structure": {},
18
+ }
19
+ ])
20
+ result = evolve_knowledge(data, ops)
21
+ assert "obj-2" in result
22
+ assert "obj-1" in result
23
+
24
+
25
+ def test_evolve_remove_object():
26
+ """Remove an object via evolution."""
27
+ data = json.dumps({
28
+ "objects": [
29
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
30
+ {"identity": {"id": "obj-2", "type": "Definition", "name": "Other"}, "structure": {}},
31
+ ]
32
+ })
33
+ ops = json.dumps([
34
+ {"type": "remove_object", "object_id": "obj-2"}
35
+ ])
36
+ result = evolve_knowledge(data, ops)
37
+ assert "obj-1" in result
38
+ assert "obj-2" not in result
39
+
40
+
41
+ def test_evolve_invalid_operations():
42
+ """Invalid operations should return an error."""
43
+ data = json.dumps({
44
+ "objects": [
45
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Test"}, "structure": {}},
46
+ ]
47
+ })
48
+ result = json.loads(evolve_knowledge(data, "invalid"))
49
+ assert "error" in result
@@ -0,0 +1,31 @@
1
+ """Tests for query_relations tool."""
2
+ import json
3
+ from cks_mcp.tools.query import query_relations
4
+
5
+
6
+ def test_query_relations():
7
+ """Query an entity that has relations."""
8
+ data = json.dumps({
9
+ "objects": [
10
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Alice"}, "structure": {}},
11
+ {"identity": {"id": "obj-2", "type": "Definition", "name": "Bob"}, "structure": {}},
12
+ {
13
+ "identity": {"id": "rel-1", "type": "Relation", "name": "knows"},
14
+ "structure": {"participants": ["obj-1", "obj-2"], "relation_type": "knows"},
15
+ },
16
+ ]
17
+ })
18
+ result = json.loads(query_relations(data, "obj-1"))
19
+ assert len(result["relations"]) == 1
20
+ assert result["relations"][0]["relation_type"] == "knows"
21
+
22
+
23
+ def test_query_no_relations():
24
+ """Query an entity with no relations."""
25
+ data = json.dumps({
26
+ "objects": [
27
+ {"identity": {"id": "obj-1", "type": "Definition", "name": "Alice"}, "structure": {}},
28
+ ]
29
+ })
30
+ result = json.loads(query_relations(data, "obj-1"))
31
+ assert len(result["relations"]) == 0
@@ -0,0 +1,109 @@
1
+ """Tests for MCP server."""
2
+ import json
3
+ import pytest
4
+ from cks_mcp.server import handle_request
5
+
6
+
7
+ @pytest.mark.asyncio
8
+ async def test_tools_list():
9
+ """tools/list should return available tools."""
10
+ request = {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}
11
+ response = await handle_request(request)
12
+ assert response["jsonrpc"] == "2.0"
13
+ assert response["id"] == 1
14
+ tools = response["result"]
15
+ assert len(tools) >= 1
16
+ assert any(t["name"] == "validate_knowledge" for t in tools)
17
+
18
+
19
+ @pytest.mark.asyncio
20
+ async def test_tools_call_validate():
21
+ """tools/call for validate_knowledge should succeed with valid input."""
22
+ request = {
23
+ "jsonrpc": "2.0",
24
+ "id": 2,
25
+ "method": "tools/call",
26
+ "params": {
27
+ "name": "validate_knowledge",
28
+ "arguments": {
29
+ "json_data": json.dumps({
30
+ "objects": [
31
+ {
32
+ "identity": {"id": "obj-1", "type": "Definition", "name": "Test"},
33
+ "structure": {},
34
+ }
35
+ ]
36
+ })
37
+ },
38
+ },
39
+ }
40
+ response = await handle_request(request)
41
+ assert response["jsonrpc"] == "2.0"
42
+ assert response["id"] == 2
43
+ result = json.loads(response["result"])
44
+ assert result["valid"] is True
45
+
46
+
47
+ @pytest.mark.asyncio
48
+ async def test_tools_call_validate_invalid():
49
+ """tools/call for validate_knowledge should return error for invalid input."""
50
+ request = {
51
+ "jsonrpc": "2.0",
52
+ "id": 3,
53
+ "method": "tools/call",
54
+ "params": {
55
+ "name": "validate_knowledge",
56
+ "arguments": {
57
+ "json_data": json.dumps({
58
+ "objects": [
59
+ {"identity": {"id": "dup", "type": "X", "name": "x"}, "structure": {}},
60
+ {"identity": {"id": "dup", "type": "Y", "name": "y"}, "structure": {}},
61
+ ]
62
+ })
63
+ },
64
+ },
65
+ }
66
+ response = await handle_request(request)
67
+ assert response["jsonrpc"] == "2.0"
68
+ assert response["id"] == 3
69
+ result = json.loads(response["result"])
70
+ assert result["valid"] is False
71
+
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_tools_call_unknown_tool():
75
+ """Calling an unknown tool should return an error."""
76
+ request = {
77
+ "jsonrpc": "2.0",
78
+ "id": 4,
79
+ "method": "tools/call",
80
+ "params": {
81
+ "name": "nonexistent",
82
+ "arguments": {},
83
+ },
84
+ }
85
+ response = await handle_request(request)
86
+ assert response["jsonrpc"] == "2.0"
87
+ assert response["id"] == 4
88
+ assert "error" in response
89
+ assert response["error"]["code"] == -32601
90
+
91
+
92
+ @pytest.mark.asyncio
93
+ async def test_unknown_method():
94
+ """Calling an unknown method should return an error."""
95
+ request = {"jsonrpc": "2.0", "id": 5, "method": "unknown/method", "params": {}}
96
+ response = await handle_request(request)
97
+ assert response["jsonrpc"] == "2.0"
98
+ assert response["id"] == 5
99
+ assert "error" in response
100
+
101
+
102
+ @pytest.mark.asyncio
103
+ async def test_missing_method():
104
+ """Request with missing method should return an error."""
105
+ request = {"jsonrpc": "2.0", "id": 6}
106
+ response = await handle_request(request)
107
+ assert response["jsonrpc"] == "2.0"
108
+ assert response["id"] == 6
109
+ assert "error" in response
@@ -0,0 +1,40 @@
1
+ """Tests for validate_knowledge tool."""
2
+ import json
3
+
4
+ from cks_mcp.tools.validate import validate_knowledge
5
+
6
+
7
+ def test_validate_valid_structure():
8
+ """A valid minimal structure should pass validation."""
9
+ valid_json = json.dumps({
10
+ "objects": [
11
+ {
12
+ "identity": {"id": "obj-1", "type": "Definition", "name": "Test"},
13
+ "structure": {},
14
+ }
15
+ ]
16
+ })
17
+ result = json.loads(validate_knowledge(valid_json))
18
+ assert result["valid"] is True
19
+ assert result["error_count"] == 0
20
+
21
+
22
+ def test_validate_invalid_structure():
23
+ """An invalid structure (duplicate IDs) should fail."""
24
+ invalid_json = json.dumps({
25
+ "objects": [
26
+ {"identity": {"id": "dup", "type": "Definition", "name": "First"}, "structure": {}},
27
+ {"identity": {"id": "dup", "type": "Definition", "name": "Second"}, "structure": {}},
28
+ ]
29
+ })
30
+ result = json.loads(validate_knowledge(invalid_json))
31
+ assert result["valid"] is False
32
+ # Should be a parse error (duplicate identity detected by parser)
33
+ assert "Duplicate" in result.get("error", "")
34
+
35
+
36
+ def test_validate_malformed_json():
37
+ """Malformed JSON should return an error."""
38
+ result = json.loads(validate_knowledge("not json"))
39
+ assert result["valid"] is False
40
+ assert "error" in result