Files
semantica/mcp/tools/extraction.py
T
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

168 lines
5.7 KiB
Python

"""
Extraction tools — NER, relation extraction, event detection, triplets.
"""
from __future__ import annotations
import logging
from typing import Any
from mcp.schemas import EXTRACT_ALL, EXTRACT_ENTITIES, EXTRACT_RELATIONS
log = logging.getLogger("semantica.mcp.tools.extraction")
def _clear_cache() -> None:
try:
from semantica.semantic_extract.cache import _result_cache
_result_cache.clear()
except Exception:
pass
def handle_extract_entities(args: dict) -> dict:
"""Extract named entities from text using Semantica NER."""
text = args.get("text", "").strip()
if not text:
return {"error": "text is required", "entities": []}
_clear_cache()
try:
from semantica.semantic_extract import NamedEntityRecognizer
entities = NamedEntityRecognizer().extract(text) or []
return {
"entities": [
{
"label": getattr(e, "label", str(e)),
"type": getattr(e, "type", None),
"start": getattr(e, "start", None),
"end": getattr(e, "end", None),
"confidence": getattr(e, "confidence", None),
}
for e in entities
],
"count": len(entities),
}
except Exception as exc:
log.exception("extract_entities failed")
return {"error": str(exc), "entities": []}
def handle_extract_relations(args: dict) -> dict:
"""Extract relations and triplets from text."""
text = args.get("text", "").strip()
if not text:
return {"error": "text is required", "relations": [], "triplets": []}
_clear_cache()
try:
from semantica.semantic_extract import RelationExtractor, TripletExtractor
relations = RelationExtractor().extract(text) or []
triplets = TripletExtractor().extract(text) or []
return {
"relations": [
{
"source": getattr(r, "source", None),
"type": getattr(r, "type", None),
"target": getattr(r, "target", None),
"confidence": getattr(r, "confidence", None),
}
for r in relations
],
"triplets": [
{
"subject": getattr(t, "subject", None),
"predicate": getattr(t, "predicate", None),
"object": getattr(t, "object", None),
}
for t in triplets
],
"relation_count": len(relations),
"triplet_count": len(triplets),
}
except Exception as exc:
log.exception("extract_relations failed")
return {"error": str(exc), "relations": [], "triplets": []}
def handle_extract_all(args: dict) -> dict:
"""Run the full extraction pipeline: NER + relations + events + triplets."""
text = args.get("text", "").strip()
if not text:
return {"error": "text is required"}
include_events = args.get("include_events", True)
include_triplets = args.get("include_triplets", True)
_clear_cache()
result: dict[str, Any] = {}
try:
from semantica.semantic_extract import (
CoreferenceResolver,
EventDetector,
NamedEntityRecognizer,
RelationExtractor,
TripletExtractor,
)
entities = NamedEntityRecognizer().extract(text) or []
result["entities"] = [
{"label": getattr(e, "label", str(e)), "type": getattr(e, "type", None)}
for e in entities
]
resolved = CoreferenceResolver().resolve(text)
relations = RelationExtractor().extract(resolved) or []
result["relations"] = [
{"source": getattr(r, "source", None),
"type": getattr(r, "type", None),
"target": getattr(r, "target", None)}
for r in relations
]
if include_events:
events = EventDetector().extract(text) or []
result["events"] = [
{"type": getattr(ev, "type", None),
"trigger": getattr(ev, "trigger", str(ev))}
for ev in events
]
if include_triplets:
triplets = TripletExtractor().extract(resolved) or []
result["triplets"] = [
{"subject": getattr(t, "subject", None),
"predicate": getattr(t, "predicate", None),
"object": getattr(t, "object", None)}
for t in triplets
]
result["summary"] = {
"entities": len(result.get("entities", [])),
"relations": len(result.get("relations", [])),
"events": len(result.get("events", [])),
"triplets": len(result.get("triplets", [])),
}
return result
except Exception as exc:
log.exception("extract_all failed")
return {"error": str(exc)}
EXTRACTION_TOOLS = [
{
"name": "extract_entities",
"description": "Extract named entities (people, places, organisations, concepts) from text.",
"inputSchema": EXTRACT_ENTITIES,
"_handler": handle_extract_entities,
},
{
"name": "extract_relations",
"description": "Extract relations and (subject, predicate, object) triplets from text.",
"inputSchema": EXTRACT_RELATIONS,
"_handler": handle_extract_relations,
},
{
"name": "extract_all",
"description": "Run the full Semantica extraction pipeline: NER, coreference resolution, relation extraction, event detection, and triplet generation.",
"inputSchema": EXTRACT_ALL,
"_handler": handle_extract_all,
},
]