Files
KaifAhmad1andClaude Sonnet 4.6 7b31304e1e feat(mcp): add modular MCP server package at repo root
Adds a fully self-contained `mcp/` package that exposes Semantica as a
Model Context Protocol server over stdio (JSON-RPC 2.0).

17 tools across 5 domains:
- Extraction: extract_entities, extract_relations, extract_all
- Decision intelligence: record_decision, query_decisions, find_precedents,
  get_causal_chain, analyze_decision_impact
- Knowledge graph: add_entity, add_relationship, search_graph,
  get_graph_summary, get_graph_analytics
- Reasoning: run_reasoning, abductive_reasoning
- Export & provenance: export_graph (JSON/CSV/GraphML/Parquet/RDF), get_provenance

4 resources: semantica://graph/summary, semantica://decisions/list,
semantica://schema/info, semantica://ontology/schema

Package layout:
  mcp/__init__.py + __main__.py  — entry points (python -m mcp)
  mcp/server.py                  — SemanticaMCPServer + stdio event loop
  mcp/session.py                 — lazy ContextGraph singleton
  mcp/schemas.py                 — JSON Schema for all 17 tool inputs
  mcp/tools/{extraction,decisions,graph,reasoning,export}.py
  mcp/resources/registry.py      — URI → handler map
  mcp/README.md                  — per-tool setup (Claude Code, Cursor, Windsurf,
                                   Cline, Continue, VS Code, Amazon Q)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:38:23 +05:30

74 lines
2.6 KiB
Python

"""
Reasoning tools — forward chaining, abductive reasoning.
"""
from __future__ import annotations
import logging
from mcp.schemas import ABDUCTIVE_REASONING, RUN_REASONING
log = logging.getLogger("semantica.mcp.tools.reasoning")
def handle_run_reasoning(args: dict) -> dict:
"""Run forward-chaining IF/THEN rules over facts to derive new knowledge."""
facts = args.get("facts", [])
rules = args.get("rules", [])
if not facts:
return {"error": "facts list is required", "derived_facts": []}
if not rules:
return {"error": "rules list is required", "derived_facts": []}
try:
from semantica.reasoning import Reasoner
reasoner = Reasoner()
for rule in rules:
reasoner.add_rule(str(rule))
derived = reasoner.infer_facts(facts)
result = derived if isinstance(derived, list) else list(derived)
return {
"derived_facts": result,
"count": len(result),
"input_facts": len(facts),
"rules_applied": len(rules),
}
except Exception as exc:
log.exception("run_reasoning failed")
return {"error": str(exc), "derived_facts": []}
def handle_abductive_reasoning(args: dict) -> dict:
"""Generate plausible hypotheses that explain a set of observations."""
observations = args.get("observations", [])
if not observations:
return {"error": "observations list is required", "hypotheses": []}
max_hypotheses = int(args.get("max_hypotheses", 5))
try:
from semantica.reasoning import AbductiveReasoner
reasoner = AbductiveReasoner()
hypotheses = reasoner.generate_hypotheses(observations)
result = hypotheses if isinstance(hypotheses, list) else list(hypotheses)
return {
"hypotheses": result[:max_hypotheses],
"count": min(len(result), max_hypotheses),
}
except Exception as exc:
log.exception("abductive_reasoning failed")
return {"error": str(exc), "hypotheses": []}
REASONING_TOOLS = [
{
"name": "run_reasoning",
"description": "Run forward-chaining IF/THEN rules over a set of facts to derive new facts. E.g. facts=['Person(John)'], rules=['IF Person(?x) THEN Mortal(?x)'] → derives 'Mortal(John)'.",
"inputSchema": RUN_REASONING,
"_handler": handle_run_reasoning,
},
{
"name": "abductive_reasoning",
"description": "Generate plausible hypotheses that best explain a set of observed facts.",
"inputSchema": ABDUCTIVE_REASONING,
"_handler": handle_abductive_reasoning,
},
]