mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
feat(plugins): add MCP server + 4 new plugin bundles (Windsurf, Cline, Continue, VS Code)
MCP Server (semantica/mcp_server.py): - Full stdio-based MCP server compatible with Claude Desktop, Windsurf, Cline, Continue, VS Code, Roo Code, and any MCP-aware tool - 12 tools: extract_entities, extract_relations, record_decision, query_decisions, find_precedents, get_causal_chain, add_entity, add_relationship, run_reasoning, get_graph_analytics, export_graph, get_graph_summary - 3 resources: semantica://graph/summary, semantica://decisions/list, semantica://schema/info - Lazy graph session with optional SEMANTICA_KG_PATH env var - JSON-RPC 2.0 over stdin/stdout; run with: python -m semantica.mcp_server New plugin bundles (each: plugin.json + marketplace.json + README.md): - plugins/.windsurf-plugin/ — Windsurf MCP config + 17 skills + 3 agents - plugins/.cline-plugin/ — Cline MCP config + 17 skills + 3 agents - plugins/.continue-plugin/ — Continue MCP config + 17 skills + 3 agents - plugins/.vscode-plugin/ — VS Code MCP config + 17 skills + 3 agents Updated plugins/.claude-plugin/README.md: - Platform support table expanded to 9 tools - Full MCP server section: per-tool config snippets for Claude Desktop, Windsurf, Cline, Continue, VS Code; tool/resource reference tables; environment variables Updated README.md: - Hero line updated to mention MCP server - Visual grid: Windsurf/VS Code/Cline/Continue → 'MCP server + plugin'; Claude Desktop → 'MCP server' - Plugin Bundles section: expanded table listing all 7 bundles with dirs - New MCP Server section with quick-start snippet and tool/resource list - Detailed integrations table: corrected connection types and config paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f2eb3e1608
commit
ab93ec3e8f
@@ -55,7 +55,7 @@ pip install semantica
|
||||
|
||||
## 🔌 Works With Every AI Tool
|
||||
|
||||
Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex — and connects to any other tool via its **REST API** (FastAPI server, port 8000) or as an **MCP data source** you can pull from.
|
||||
Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex, an **MCP server** (`python -m semantica.mcp_server`) for Windsurf, Cline, Continue, VS Code, and Claude Desktop, and a **REST API** (FastAPI, port 8000) for any other tool.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -77,17 +77,17 @@ Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex —
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://windsurf.com"><img src="https://exafunction.github.io/public/brand/windsurf-black-symbol.svg" alt="Windsurf" width="48" height="48" /></a><br/>
|
||||
<strong>Windsurf</strong><br/>
|
||||
<sub>REST API</sub>
|
||||
<sub>MCP server + plugin</sub>
|
||||
</td>
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://claude.ai/download"><img src="https://github.com/anthropics.png?size=120" alt="Claude Desktop" width="48" height="48" /></a><br/>
|
||||
<strong>Claude Desktop</strong><br/>
|
||||
<sub>REST API</sub>
|
||||
<sub>MCP server</sub>
|
||||
</td>
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://github.com/microsoft/vscode"><img src="https://github.com/microsoft.png?size=120" alt="VS Code" width="48" height="48" /></a><br/>
|
||||
<strong>VS Code</strong><br/>
|
||||
<sub>REST API</sub>
|
||||
<sub>MCP server + plugin</sub>
|
||||
</td>
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://github.com/features/copilot"><img src="https://github.com/github.png?size=120" alt="GitHub Copilot" width="48" height="48" /></a><br/>
|
||||
@@ -97,7 +97,7 @@ Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex —
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://github.com/cline/cline"><img src="https://github.com/cline.png?size=120" alt="Cline" width="48" height="48" /></a><br/>
|
||||
<strong>Cline</strong><br/>
|
||||
<sub>REST API</sub>
|
||||
<sub>MCP server + plugin</sub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -109,7 +109,7 @@ Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex —
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://github.com/continuedev/continue"><img src="https://github.com/continuedev.png?size=120" alt="Continue" width="48" height="48" /></a><br/>
|
||||
<strong>Continue</strong><br/>
|
||||
<sub>REST API</sub>
|
||||
<sub>MCP server + plugin</sub>
|
||||
</td>
|
||||
<td align="center" width="12.5%">
|
||||
<a href="https://github.com/block/goose"><img src="https://github.com/block.png?size=120" alt="Goose" width="48" height="48" /></a><br/>
|
||||
@@ -146,7 +146,17 @@ Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex —
|
||||
|
||||
### Plugin Bundles (Claude Code · Cursor · Codex)
|
||||
|
||||
Native plugin bundles live under [`plugins/`](plugins/) — install once, works across all three tools.
|
||||
Native plugin bundles live under [`plugins/`](plugins/). Each directory contains a `plugin.json`, `marketplace.json`, and `README.md`.
|
||||
|
||||
| Bundle | Directory | Tools |
|
||||
|---|---|---|
|
||||
| Claude Code | [`plugins/.claude-plugin/`](plugins/.claude-plugin/) | 17 skills · 3 agents · hooks |
|
||||
| Cursor | [`plugins/.cursor-plugin/`](plugins/.cursor-plugin/) | 17 skills · 3 agents · hooks |
|
||||
| Codex CLI | [`plugins/.codex-plugin/`](plugins/.codex-plugin/) | 17 skills · 3 agents |
|
||||
| Windsurf | [`plugins/.windsurf-plugin/`](plugins/.windsurf-plugin/) | 17 skills · 3 agents · MCP config |
|
||||
| Cline | [`plugins/.cline-plugin/`](plugins/.cline-plugin/) | 17 skills · 3 agents · MCP config |
|
||||
| Continue | [`plugins/.continue-plugin/`](plugins/.continue-plugin/) | 17 skills · 3 agents · MCP config |
|
||||
| VS Code | [`plugins/.vscode-plugin/`](plugins/.vscode-plugin/) | 17 skills · 3 agents · MCP config |
|
||||
|
||||
**17 domain skills:**
|
||||
|
||||
@@ -182,9 +192,36 @@ Native plugin bundles live under [`plugins/`](plugins/) — install once, works
|
||||
|
||||
→ [`plugins/.claude-plugin/README.md`](plugins/.claude-plugin/README.md)
|
||||
|
||||
### MCP Server (expose Semantica to any MCP-aware tool)
|
||||
|
||||
Semantica ships a full **MCP server** (`semantica/mcp_server.py`) — run it once and any MCP-compatible tool connects automatically:
|
||||
|
||||
```bash
|
||||
python -m semantica.mcp_server
|
||||
```
|
||||
|
||||
Add to your tool's config (Claude Desktop, Windsurf, Cline, Continue, VS Code, Roo Code):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**12 tools exposed:** `extract_entities`, `extract_relations`, `record_decision`, `query_decisions`, `find_precedents`, `get_causal_chain`, `add_entity`, `add_relationship`, `run_reasoning`, `get_graph_analytics`, `export_graph`, `get_graph_summary`
|
||||
|
||||
**3 resources:** `semantica://graph/summary`, `semantica://decisions/list`, `semantica://schema/info`
|
||||
|
||||
See [`plugins/.claude-plugin/README.md`](plugins/.claude-plugin/README.md) for per-tool config snippets.
|
||||
|
||||
### MCP Client (Ingest from MCP Servers)
|
||||
|
||||
Semantica includes an **MCP client** (`semantica/ingest/mcp_client.py`) that lets you pull data from any Python/FastMCP server into a knowledge graph:
|
||||
Semantica also includes an **MCP client** (`semantica/ingest/mcp_client.py`) that lets you pull data from any Python/FastMCP server into a knowledge graph:
|
||||
|
||||
```python
|
||||
from semantica.ingest import MCPClient
|
||||
@@ -825,13 +862,13 @@ Start the Semantica server (`python -m semantica.server`, port 8000) and point a
|
||||
| [Claude Code](https://claude.com/product/claude-code) | **Native plugin** | `plugins/.claude-plugin/` — 17 skills, 3 agents, `hooks.json` |
|
||||
| [Cursor](https://cursor.com) | **Native plugin** | `plugins/.cursor-plugin/` — same 17 skills + 3 agents |
|
||||
| [Codex CLI](https://github.com/openai/codex) | **Native plugin** | `plugins/.codex-plugin/` — same 17 skills + 3 agents |
|
||||
| [Windsurf](https://windsurf.com) | REST API | Point at `http://localhost:8000/api` |
|
||||
| [Claude Desktop](https://claude.ai/download) | REST API | Point at `http://localhost:8000/api` |
|
||||
| [VS Code](https://github.com/microsoft/vscode) | REST API | Use any REST client extension |
|
||||
| [Windsurf](https://windsurf.com) | **MCP server** + plugin | `plugins/.windsurf-plugin/` · add `python -m semantica.mcp_server` to `~/.codeium/windsurf/mcp_config.json` |
|
||||
| [Claude Desktop](https://claude.ai/download) | **MCP server** | Add `python -m semantica.mcp_server` to `claude_desktop_config.json` |
|
||||
| [VS Code](https://github.com/microsoft/vscode) | **MCP server** + plugin | `plugins/.vscode-plugin/` · add to `settings.json` under `mcp.servers` |
|
||||
| [GitHub Copilot](https://github.com/features/copilot) | REST API | Use via Copilot Chat custom tools |
|
||||
| [Cline](https://github.com/cline/cline) | REST API | Add as a custom tool endpoint |
|
||||
| [Roo Code](https://github.com/RooCodeInc/Roo-Code) | REST API | Add as a custom tool endpoint |
|
||||
| [Continue](https://github.com/continuedev/continue) | REST API | Add to `~/.continue/config.json` as context provider |
|
||||
| [Cline](https://github.com/cline/cline) | **MCP server** + plugin | `plugins/.cline-plugin/` · add server in Cline MCP settings panel |
|
||||
| [Roo Code](https://github.com/RooCodeInc/Roo-Code) | **MCP server** | Add `python -m semantica.mcp_server` in Roo Code MCP settings |
|
||||
| [Continue](https://github.com/continuedev/continue) | **MCP server** + plugin | `plugins/.continue-plugin/` · add to `~/.continue/config.json` under `mcpServers` |
|
||||
| [Goose](https://github.com/block/goose) | REST API | Add to Goose toolset config |
|
||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | REST API | Add as custom REST tool |
|
||||
| [Aider](https://github.com/Aider-AI/aider) | REST API | Pass context from the API into prompts |
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
|
||||
Semantica ships a shared plugin bundle under `plugins/` with skills, agents, and hooks for knowledge graphs, context graphs, decision intelligence, reasoning, explainability, provenance, ontology, and export workflows.
|
||||
|
||||
This README is for community users who want to install or reuse the plugin package across Claude, Cursor, and Codex.
|
||||
This README covers installation across every supported platform.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Claude Code
|
||||
- Cursor
|
||||
- Codex
|
||||
| Platform | Method | Config file |
|
||||
|---|---|---|
|
||||
| Claude Code | Native plugin bundle | `plugins/.claude-plugin/plugin.json` |
|
||||
| Cursor | Native plugin bundle | `plugins/.cursor-plugin/plugin.json` |
|
||||
| Codex CLI | Native plugin bundle | `plugins/.codex-plugin/plugin.json` |
|
||||
| Windsurf | MCP server + plugin bundle | `plugins/.windsurf-plugin/plugin.json` |
|
||||
| Cline (VS Code) | MCP server + plugin bundle | `plugins/.cline-plugin/plugin.json` |
|
||||
| Continue | MCP server | `plugins/.continue-plugin/plugin.json` |
|
||||
| VS Code | MCP server | `plugins/.vscode-plugin/plugin.json` |
|
||||
| Claude Desktop | MCP server | — (see MCP section below) |
|
||||
| Any MCP client | MCP server | `python -m semantica.mcp_server` |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -23,12 +31,16 @@ cd semantica
|
||||
|
||||
```text
|
||||
plugins/
|
||||
skills/
|
||||
agents/
|
||||
hooks/
|
||||
.claude-plugin/
|
||||
.cursor-plugin/
|
||||
.codex-plugin/
|
||||
skills/ ← 17 domain skills
|
||||
agents/ ← 3 specialized agents
|
||||
hooks/ ← hooks.json
|
||||
.claude-plugin/ ← Claude Code manifest
|
||||
.cursor-plugin/ ← Cursor manifest
|
||||
.codex-plugin/ ← Codex CLI manifest
|
||||
.windsurf-plugin/← Windsurf manifest + MCP config
|
||||
.cline-plugin/ ← Cline manifest + MCP config
|
||||
.continue-plugin/← Continue manifest + MCP config
|
||||
.vscode-plugin/ ← VS Code manifest + MCP config
|
||||
```
|
||||
|
||||
## Plugin Contents
|
||||
@@ -128,6 +140,114 @@ After installing on any platform, these are good smoke tests:
|
||||
4. `/semantica:explain decision <decision_id>`
|
||||
5. `/semantica:validate graph`
|
||||
|
||||
## MCP Server (Windsurf · Cline · Continue · VS Code · Claude Desktop · Any tool)
|
||||
|
||||
Semantica includes a full MCP server (`semantica/mcp_server.py`) that exposes 12 tools and 3 resources over stdio — compatible with any MCP-aware tool.
|
||||
|
||||
### Start the server
|
||||
|
||||
```bash
|
||||
python -m semantica.mcp_server
|
||||
```
|
||||
|
||||
### Configure in your tool
|
||||
|
||||
**Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Windsurf** — `~/.codeium/windsurf/mcp_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cline** — Cline MCP settings panel → Add server:
|
||||
|
||||
```json
|
||||
{
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Continue** — `~/.continue/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**VS Code** — `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available MCP tools
|
||||
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `extract_entities` | Named entity recognition from text |
|
||||
| `extract_relations` | Relation and triplet extraction from text |
|
||||
| `record_decision` | Record a decision with full context and metadata |
|
||||
| `query_decisions` | Query recorded decisions by natural language or category |
|
||||
| `find_precedents` | Find past decisions similar to a scenario |
|
||||
| `get_causal_chain` | Trace upstream/downstream causal chain from a decision |
|
||||
| `add_entity` | Add a node/entity to the knowledge graph |
|
||||
| `add_relationship` | Add a directed edge between two entities |
|
||||
| `run_reasoning` | Run IF/THEN rules over facts to derive new facts |
|
||||
| `get_graph_analytics` | PageRank centrality and community detection |
|
||||
| `export_graph` | Export graph as Turtle, JSON-LD, N-Triples, or JSON |
|
||||
| `get_graph_summary` | Node count, decision count, graph status |
|
||||
|
||||
### Available MCP resources
|
||||
|
||||
| URI | Description |
|
||||
|---|---|
|
||||
| `semantica://graph/summary` | High-level graph statistics |
|
||||
| `semantica://decisions/list` | All recorded decisions |
|
||||
| `semantica://schema/info` | Server info and capability list |
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `SEMANTICA_KG_PATH` | Path to a persisted graph to load on start |
|
||||
| `SEMANTICA_LOG_LEVEL` | Log level: DEBUG, INFO, WARNING (default: WARNING) |
|
||||
|
||||
## Community Notes
|
||||
|
||||
- Keep plugin name/version/keywords updated in each manifest before publishing.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Semantica — Cline Plugin
|
||||
|
||||
Adds all 17 Semantica skills, 3 agents, and hook configuration to Cline (VS Code extension).
|
||||
|
||||
## MCP Server Setup (recommended)
|
||||
|
||||
In Cline settings, add a new MCP server:
|
||||
|
||||
```json
|
||||
{
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cline will discover all 12 Semantica tools automatically on connection.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- `pip install semantica`
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "semantica-cline",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"description": "Semantica plugin for Cline: knowledge graph skills, reasoning, extraction, and visualization.",
|
||||
"source": "./",
|
||||
"category": "Productivity",
|
||||
"tags": [
|
||||
"knowledge-graph",
|
||||
"reasoning",
|
||||
"semantica",
|
||||
"cline"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "semantica-cline",
|
||||
"displayName": "Semantica Cline Plugin",
|
||||
"description": "Semantica plugin for Cline: knowledge graph skills, decision intelligence, reasoning, extraction, and visualization.",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Semantica Contributors"
|
||||
},
|
||||
"homepage": "https://github.com/Hawksight-AI/semantica",
|
||||
"repository": "https://github.com/Hawksight-AI/semantica",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"semantica",
|
||||
"knowledge graph",
|
||||
"cline",
|
||||
"context graphs",
|
||||
"decision intelligence",
|
||||
"explainability",
|
||||
"causal analysis",
|
||||
"provenance",
|
||||
"ontology",
|
||||
"graph analytics",
|
||||
"semantic extraction",
|
||||
"visualization",
|
||||
"reasoning",
|
||||
"mcp"
|
||||
],
|
||||
"skills": "../skills",
|
||||
"agents": "../agents",
|
||||
"hooks": "../hooks/hooks.json",
|
||||
"mcp": {
|
||||
"server": "python -m semantica.mcp_server",
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Semantica — Continue Plugin
|
||||
|
||||
Adds Semantica as an MCP server and context provider to [Continue.dev](https://continue.dev).
|
||||
|
||||
## MCP Server Setup
|
||||
|
||||
Add to `~/.continue/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Continue will show all Semantica tools in the `@semantica` context provider dropdown.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- `pip install semantica`
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "semantica-continue",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"description": "Semantica plugin for Continue.dev: knowledge graph context provider, reasoning, and extraction.",
|
||||
"source": "./",
|
||||
"category": "Productivity",
|
||||
"tags": [
|
||||
"knowledge-graph",
|
||||
"reasoning",
|
||||
"semantica",
|
||||
"continue"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "semantica-continue",
|
||||
"displayName": "Semantica Continue Plugin",
|
||||
"description": "Semantica plugin for Continue.dev: knowledge graph context provider, decision intelligence, reasoning, and semantic extraction.",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Semantica Contributors"
|
||||
},
|
||||
"homepage": "https://github.com/Hawksight-AI/semantica",
|
||||
"repository": "https://github.com/Hawksight-AI/semantica",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"semantica",
|
||||
"knowledge graph",
|
||||
"continue",
|
||||
"context provider",
|
||||
"decision intelligence",
|
||||
"explainability",
|
||||
"causal analysis",
|
||||
"provenance",
|
||||
"ontology",
|
||||
"semantic extraction",
|
||||
"reasoning",
|
||||
"mcp"
|
||||
],
|
||||
"skills": "../skills",
|
||||
"agents": "../agents",
|
||||
"hooks": "../hooks/hooks.json",
|
||||
"mcp": {
|
||||
"server": "python -m semantica.mcp_server",
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# Semantica — VS Code Plugin
|
||||
|
||||
Adds Semantica as an MCP server to VS Code (via GitHub Copilot Chat or any MCP-aware extension).
|
||||
|
||||
## MCP Server Setup
|
||||
|
||||
Add to your VS Code `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"github.copilot.chat.mcp.servers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or if using the VS Code MCP extension directly:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- `pip install semantica`
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "semantica-vscode",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"description": "Semantica plugin for VS Code: knowledge graph skills, reasoning, extraction, and visualization.",
|
||||
"source": "./",
|
||||
"category": "Productivity",
|
||||
"tags": [
|
||||
"knowledge-graph",
|
||||
"reasoning",
|
||||
"semantica",
|
||||
"vscode"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "semantica-vscode",
|
||||
"displayName": "Semantica VS Code Plugin",
|
||||
"description": "Semantica plugin for VS Code: knowledge graph skills, decision intelligence, reasoning, extraction, and visualization via MCP server.",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Semantica Contributors"
|
||||
},
|
||||
"homepage": "https://github.com/Hawksight-AI/semantica",
|
||||
"repository": "https://github.com/Hawksight-AI/semantica",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"semantica",
|
||||
"knowledge graph",
|
||||
"vscode",
|
||||
"context graphs",
|
||||
"decision intelligence",
|
||||
"explainability",
|
||||
"causal analysis",
|
||||
"provenance",
|
||||
"ontology",
|
||||
"graph analytics",
|
||||
"semantic extraction",
|
||||
"visualization",
|
||||
"reasoning",
|
||||
"mcp"
|
||||
],
|
||||
"skills": "../skills",
|
||||
"agents": "../agents",
|
||||
"hooks": "../hooks/hooks.json",
|
||||
"mcp": {
|
||||
"server": "python -m semantica.mcp_server",
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Semantica — Windsurf Plugin
|
||||
|
||||
Adds all 17 Semantica skills, 3 agents, and hook configuration to Windsurf.
|
||||
|
||||
## MCP Server Setup (recommended)
|
||||
|
||||
Add to your Windsurf MCP config (`~/.codeium/windsurf/mcp_config.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Windsurf will then have access to all 12 Semantica tools (extract, record_decision, query_decisions, find_precedents, get_causal_chain, add_entity, add_relationship, run_reasoning, get_graph_analytics, export_graph, and more) directly in the AI panel.
|
||||
|
||||
## Skills
|
||||
|
||||
All 17 skills under `plugins/skills/` are available as slash commands once the plugin is loaded.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- `pip install semantica`
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "semantica-windsurf",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "semantica",
|
||||
"description": "Semantica plugin for Windsurf: knowledge graph skills, reasoning, extraction, and visualization.",
|
||||
"source": "./",
|
||||
"category": "Productivity",
|
||||
"tags": [
|
||||
"knowledge-graph",
|
||||
"reasoning",
|
||||
"semantica",
|
||||
"windsurf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "semantica-windsurf",
|
||||
"displayName": "Semantica Windsurf Plugin",
|
||||
"description": "Semantica plugin for Windsurf: knowledge graph skills, decision intelligence, reasoning, extraction, and visualization.",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Semantica Contributors"
|
||||
},
|
||||
"homepage": "https://github.com/Hawksight-AI/semantica",
|
||||
"repository": "https://github.com/Hawksight-AI/semantica",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"semantica",
|
||||
"knowledge graph",
|
||||
"windsurf",
|
||||
"context graphs",
|
||||
"decision intelligence",
|
||||
"explainability",
|
||||
"causal analysis",
|
||||
"provenance",
|
||||
"ontology",
|
||||
"graph analytics",
|
||||
"semantic extraction",
|
||||
"visualization",
|
||||
"reasoning",
|
||||
"mcp"
|
||||
],
|
||||
"skills": "../skills",
|
||||
"agents": "../agents",
|
||||
"hooks": "../hooks/hooks.json",
|
||||
"mcp": {
|
||||
"server": "python -m semantica.mcp_server",
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
"""
|
||||
Semantica MCP Server
|
||||
|
||||
Exposes Semantica's knowledge graph, decision intelligence, semantic extraction,
|
||||
reasoning, and analytics capabilities as an MCP (Model Context Protocol) server
|
||||
over stdio — compatible with Claude Desktop, Windsurf, Cline, Continue, VS Code,
|
||||
Roo Code, and any other MCP-aware tool.
|
||||
|
||||
Usage
|
||||
-----
|
||||
Configure in your tool's MCP settings:
|
||||
|
||||
Claude Desktop / Windsurf / Cline / Continue / VS Code:
|
||||
{
|
||||
"mcpServers": {
|
||||
"semantica": {
|
||||
"command": "python",
|
||||
"args": ["-m", "semantica.mcp_server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Run directly:
|
||||
python -m semantica.mcp_server
|
||||
|
||||
Environment variables:
|
||||
SEMANTICA_KG_PATH — path to a persisted graph to load on start (optional)
|
||||
SEMANTICA_LOG_LEVEL — log level: DEBUG, INFO, WARNING (default: WARNING)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
# ── logging ────────────────────────────────────────────────────────────────
|
||||
_log_level = getattr(logging, os.environ.get("SEMANTICA_LOG_LEVEL", "WARNING").upper(), logging.WARNING)
|
||||
logging.basicConfig(stream=sys.stderr, level=_log_level,
|
||||
format="%(asctime)s [semantica-mcp] %(levelname)s %(message)s")
|
||||
log = logging.getLogger("semantica.mcp_server")
|
||||
|
||||
# ── lazy graph session ──────────────────────────────────────────────────────
|
||||
_graph: Any = None
|
||||
|
||||
|
||||
def _get_graph():
|
||||
global _graph
|
||||
if _graph is None:
|
||||
from semantica.context import ContextGraph
|
||||
_graph = ContextGraph(advanced_analytics=True)
|
||||
kg_path = os.environ.get("SEMANTICA_KG_PATH")
|
||||
if kg_path and os.path.exists(kg_path):
|
||||
try:
|
||||
_graph.load(kg_path)
|
||||
log.info("Loaded graph from %s", kg_path)
|
||||
except Exception as exc:
|
||||
log.warning("Could not load graph from %s: %s", kg_path, exc)
|
||||
return _graph
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Tool implementations
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def _tool_extract_entities(args: dict) -> dict:
|
||||
"""Extract named entities from text."""
|
||||
text = args.get("text", "")
|
||||
if not text:
|
||||
return {"error": "text is required"}
|
||||
from semantica.semantic_extract import NamedEntityRecognizer
|
||||
from semantica.semantic_extract.cache import _result_cache
|
||||
_result_cache.clear()
|
||||
entities = NamedEntityRecognizer().extract(text)
|
||||
return {
|
||||
"entities": [
|
||||
{"label": getattr(e, "label", str(e)),
|
||||
"type": getattr(e, "type", None),
|
||||
"start": getattr(e, "start", None),
|
||||
"end": getattr(e, "end", None)}
|
||||
for e in (entities or [])
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def _tool_extract_relations(args: dict) -> dict:
|
||||
"""Extract relations and triplets from text."""
|
||||
text = args.get("text", "")
|
||||
if not text:
|
||||
return {"error": "text is required"}
|
||||
from semantica.semantic_extract import RelationExtractor, TripletExtractor
|
||||
from semantica.semantic_extract.cache import _result_cache
|
||||
_result_cache.clear()
|
||||
relations = RelationExtractor().extract(text)
|
||||
triplets = TripletExtractor().extract(text)
|
||||
return {
|
||||
"relations": [
|
||||
{"source": getattr(r, "source", None),
|
||||
"type": getattr(r, "type", None),
|
||||
"target": getattr(r, "target", None)}
|
||||
for r in (relations or [])
|
||||
],
|
||||
"triplets": [
|
||||
{"subject": getattr(t, "subject", None),
|
||||
"predicate": getattr(t, "predicate", None),
|
||||
"object": getattr(t, "object", None)}
|
||||
for t in (triplets or [])
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _tool_record_decision(args: dict) -> dict:
|
||||
"""Record a decision with full context into the graph."""
|
||||
required = ["category", "scenario", "reasoning", "outcome", "confidence"]
|
||||
for field in required:
|
||||
if field not in args:
|
||||
return {"error": f"missing required field: {field}"}
|
||||
graph = _get_graph()
|
||||
decision_id = graph.record_decision(
|
||||
category=args["category"],
|
||||
scenario=args["scenario"],
|
||||
reasoning=args["reasoning"],
|
||||
outcome=args["outcome"],
|
||||
confidence=float(args["confidence"]),
|
||||
entities=args.get("entities", []),
|
||||
decision_maker=args.get("decision_maker", "mcp_client"),
|
||||
valid_from=args.get("valid_from"),
|
||||
valid_until=args.get("valid_until"),
|
||||
)
|
||||
return {"decision_id": decision_id, "status": "recorded"}
|
||||
|
||||
|
||||
def _tool_query_decisions(args: dict) -> dict:
|
||||
"""Query decisions by natural language or structured filters."""
|
||||
query = args.get("query", "")
|
||||
category = args.get("category")
|
||||
limit = int(args.get("limit", 10))
|
||||
graph = _get_graph()
|
||||
try:
|
||||
if query:
|
||||
results = graph.find_similar_decisions(query, max_results=limit)
|
||||
elif category:
|
||||
nodes = graph.find_nodes(node_type="decision")
|
||||
results = [n for n in nodes if n.get("category") == category][:limit]
|
||||
else:
|
||||
results = graph.find_nodes(node_type="decision")[:limit]
|
||||
return {"decisions": results if isinstance(results, list) else list(results)}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc), "decisions": []}
|
||||
|
||||
|
||||
def _tool_find_precedents(args: dict) -> dict:
|
||||
"""Find past decisions similar to a given scenario."""
|
||||
scenario = args.get("scenario", "")
|
||||
if not scenario:
|
||||
return {"error": "scenario is required"}
|
||||
max_results = int(args.get("max_results", 5))
|
||||
graph = _get_graph()
|
||||
try:
|
||||
precedents = graph.find_similar_decisions(scenario, max_results=max_results)
|
||||
return {"precedents": precedents if isinstance(precedents, list) else list(precedents)}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc), "precedents": []}
|
||||
|
||||
|
||||
def _tool_get_causal_chain(args: dict) -> dict:
|
||||
"""Get the causal chain for a decision."""
|
||||
decision_id = args.get("decision_id", "")
|
||||
if not decision_id:
|
||||
return {"error": "decision_id is required"}
|
||||
direction = args.get("direction", "downstream")
|
||||
max_depth = int(args.get("max_depth", 5))
|
||||
graph = _get_graph()
|
||||
try:
|
||||
from semantica.context.causal_analyzer import CausalChainAnalyzer
|
||||
analyzer = CausalChainAnalyzer(graph_store=graph)
|
||||
chain = analyzer.get_causal_chain(decision_id, direction=direction, max_depth=max_depth)
|
||||
return {"chain": chain if isinstance(chain, list) else list(chain)}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc), "chain": []}
|
||||
|
||||
|
||||
def _tool_add_entity(args: dict) -> dict:
|
||||
"""Add a node/entity to the knowledge graph."""
|
||||
node_id = args.get("id", "")
|
||||
label = args.get("label", node_id)
|
||||
node_type = args.get("type", "Entity")
|
||||
if not node_id:
|
||||
return {"error": "id is required"}
|
||||
graph = _get_graph()
|
||||
graph.add_node(node_id=node_id, label=label, node_type=node_type,
|
||||
metadata=args.get("metadata", {}))
|
||||
return {"status": "added", "id": node_id}
|
||||
|
||||
|
||||
def _tool_add_relationship(args: dict) -> dict:
|
||||
"""Add a relationship (edge) between two entities."""
|
||||
source = args.get("source", "")
|
||||
target = args.get("target", "")
|
||||
rel_type = args.get("type", "RELATED_TO")
|
||||
if not source or not target:
|
||||
return {"error": "source and target are required"}
|
||||
graph = _get_graph()
|
||||
graph.add_edge(source_id=source, target_id=target, edge_type=rel_type,
|
||||
metadata=args.get("metadata", {}))
|
||||
return {"status": "added", "source": source, "target": target, "type": rel_type}
|
||||
|
||||
|
||||
def _tool_run_reasoning(args: dict) -> dict:
|
||||
"""Run forward-chaining reasoning rules over a set of facts."""
|
||||
facts = args.get("facts", [])
|
||||
rules = args.get("rules", [])
|
||||
if not facts or not rules:
|
||||
return {"error": "facts and rules are required"}
|
||||
from semantica.reasoning import Reasoner
|
||||
reasoner = Reasoner()
|
||||
for rule in rules:
|
||||
reasoner.add_rule(rule)
|
||||
derived = reasoner.infer_facts(facts)
|
||||
return {"derived_facts": derived if isinstance(derived, list) else list(derived)}
|
||||
|
||||
|
||||
def _tool_get_graph_analytics(args: dict) -> dict:
|
||||
"""Compute graph analytics: centrality, community detection, metrics."""
|
||||
graph = _get_graph()
|
||||
try:
|
||||
from semantica.kg import CentralityCalculator, CommunityDetector
|
||||
centrality = CentralityCalculator().calculate_pagerank(graph)
|
||||
communities = CommunityDetector().detect_communities(graph)
|
||||
node_count = len(list(graph.find_nodes()))
|
||||
edge_count = getattr(graph, "edge_count", lambda: 0)()
|
||||
return {
|
||||
"node_count": node_count,
|
||||
"edge_count": edge_count,
|
||||
"top_nodes_by_pagerank": sorted(
|
||||
centrality.items() if hasattr(centrality, "items") else [],
|
||||
key=lambda x: x[1], reverse=True
|
||||
)[:10],
|
||||
"community_count": len(communities) if isinstance(communities, (list, dict)) else 0,
|
||||
}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc)}
|
||||
|
||||
|
||||
def _tool_export_graph(args: dict) -> dict:
|
||||
"""Export the current knowledge graph to a serialised format."""
|
||||
fmt = args.get("format", "json-ld")
|
||||
graph = _get_graph()
|
||||
try:
|
||||
from semantica.export import RDFExporter, JSONExporter
|
||||
if fmt in ("turtle", "ttl", "nt", "xml", "json-ld"):
|
||||
result = RDFExporter().export_to_rdf(graph, format=fmt)
|
||||
else:
|
||||
result = JSONExporter().export(graph)
|
||||
return {"format": fmt, "data": result}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc)}
|
||||
|
||||
|
||||
def _tool_get_graph_summary(args: dict) -> dict:
|
||||
"""Return a high-level summary of the current graph."""
|
||||
graph = _get_graph()
|
||||
try:
|
||||
node_count = len(list(graph.find_nodes()))
|
||||
decisions = graph.find_nodes(node_type="decision")
|
||||
return {
|
||||
"node_count": node_count,
|
||||
"decision_count": len(list(decisions)),
|
||||
"graph_ready": True,
|
||||
}
|
||||
except Exception as exc:
|
||||
return {"error": str(exc), "graph_ready": False}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# MCP protocol tables
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "extract_entities",
|
||||
"description": "Extract named entities (people, places, organisations, concepts) from text using Semantica NER.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "Input text to extract entities from"}
|
||||
},
|
||||
"required": ["text"],
|
||||
},
|
||||
"_handler": _tool_extract_entities,
|
||||
},
|
||||
{
|
||||
"name": "extract_relations",
|
||||
"description": "Extract relations and (subject, predicate, object) triplets from text.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "Input text to extract relations from"}
|
||||
},
|
||||
"required": ["text"],
|
||||
},
|
||||
"_handler": _tool_extract_relations,
|
||||
},
|
||||
{
|
||||
"name": "record_decision",
|
||||
"description": "Record a decision into the Semantica knowledge graph with full context, causal links, and metadata.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string", "description": "Decision category, e.g. 'loan_approval'"},
|
||||
"scenario": {"type": "string", "description": "Natural-language situation description"},
|
||||
"reasoning": {"type": "string", "description": "Why this decision was made"},
|
||||
"outcome": {"type": "string", "description": "Decision outcome, e.g. 'approved'"},
|
||||
"confidence": {"type": "number", "description": "Confidence score 0–1"},
|
||||
"decision_maker":{"type": "string", "description": "Who/what made the decision"},
|
||||
"valid_from": {"type": "string", "description": "ISO date validity start (optional)"},
|
||||
"valid_until": {"type": "string", "description": "ISO date validity end (optional)"},
|
||||
},
|
||||
"required": ["category", "scenario", "reasoning", "outcome", "confidence"],
|
||||
},
|
||||
"_handler": _tool_record_decision,
|
||||
},
|
||||
{
|
||||
"name": "query_decisions",
|
||||
"description": "Query recorded decisions by natural language, category, or get all recent decisions.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string", "description": "Natural language query (optional)"},
|
||||
"category": {"type": "string", "description": "Filter by category (optional)"},
|
||||
"limit": {"type": "integer", "description": "Max results (default 10)"},
|
||||
},
|
||||
},
|
||||
"_handler": _tool_query_decisions,
|
||||
},
|
||||
{
|
||||
"name": "find_precedents",
|
||||
"description": "Find past decisions similar to a given scenario using hybrid similarity search.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scenario": {"type": "string", "description": "Scenario description to find precedents for"},
|
||||
"max_results": {"type": "integer", "description": "Max results (default 5)"},
|
||||
},
|
||||
"required": ["scenario"],
|
||||
},
|
||||
"_handler": _tool_find_precedents,
|
||||
},
|
||||
{
|
||||
"name": "get_causal_chain",
|
||||
"description": "Trace the causal chain upstream or downstream from a decision.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"decision_id": {"type": "string", "description": "Decision ID to trace"},
|
||||
"direction": {"type": "string", "enum": ["upstream", "downstream"], "description": "Trace direction"},
|
||||
"max_depth": {"type": "integer", "description": "Max chain depth (default 5)"},
|
||||
},
|
||||
"required": ["decision_id"],
|
||||
},
|
||||
"_handler": _tool_get_causal_chain,
|
||||
},
|
||||
{
|
||||
"name": "add_entity",
|
||||
"description": "Add a node/entity to the Semantica knowledge graph.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {"type": "string", "description": "Unique node ID"},
|
||||
"label": {"type": "string", "description": "Human-readable label"},
|
||||
"type": {"type": "string", "description": "Node type, e.g. 'Person', 'Organisation'"},
|
||||
"metadata": {"type": "object", "description": "Additional properties"},
|
||||
},
|
||||
"required": ["id"],
|
||||
},
|
||||
"_handler": _tool_add_entity,
|
||||
},
|
||||
{
|
||||
"name": "add_relationship",
|
||||
"description": "Add a directed relationship (edge) between two entities in the knowledge graph.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"type": "string", "description": "Source node ID"},
|
||||
"target": {"type": "string", "description": "Target node ID"},
|
||||
"type": {"type": "string", "description": "Relationship type, e.g. 'WORKS_AT'"},
|
||||
"metadata": {"type": "object", "description": "Additional edge properties"},
|
||||
},
|
||||
"required": ["source", "target"],
|
||||
},
|
||||
"_handler": _tool_add_relationship,
|
||||
},
|
||||
{
|
||||
"name": "run_reasoning",
|
||||
"description": "Run forward-chaining IF/THEN rules over a set of facts to derive new facts.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"facts": {
|
||||
"type": "array", "items": {"type": "string"},
|
||||
"description": "List of fact strings, e.g. ['Person(John)', 'Employee(John)']",
|
||||
},
|
||||
"rules": {
|
||||
"type": "array", "items": {"type": "string"},
|
||||
"description": "IF/THEN rule strings, e.g. ['IF Employee(?x) THEN WorkerBee(?x)']",
|
||||
},
|
||||
},
|
||||
"required": ["facts", "rules"],
|
||||
},
|
||||
"_handler": _tool_run_reasoning,
|
||||
},
|
||||
{
|
||||
"name": "get_graph_analytics",
|
||||
"description": "Compute PageRank centrality and community detection over the knowledge graph.",
|
||||
"inputSchema": {"type": "object", "properties": {}},
|
||||
"_handler": _tool_get_graph_analytics,
|
||||
},
|
||||
{
|
||||
"name": "export_graph",
|
||||
"description": "Export the current knowledge graph. Formats: turtle, ttl, nt, xml, json-ld, json.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["turtle", "ttl", "nt", "xml", "json-ld", "json"],
|
||||
"description": "Export format (default: json-ld)",
|
||||
}
|
||||
},
|
||||
},
|
||||
"_handler": _tool_export_graph,
|
||||
},
|
||||
{
|
||||
"name": "get_graph_summary",
|
||||
"description": "Return a high-level summary of the current knowledge graph: node count, decision count, status.",
|
||||
"inputSchema": {"type": "object", "properties": {}},
|
||||
"_handler": _tool_get_graph_summary,
|
||||
},
|
||||
]
|
||||
|
||||
RESOURCES = [
|
||||
{
|
||||
"uri": "semantica://graph/summary",
|
||||
"name": "Graph Summary",
|
||||
"description": "High-level statistics about the current knowledge graph",
|
||||
"mimeType": "application/json",
|
||||
},
|
||||
{
|
||||
"uri": "semantica://decisions/list",
|
||||
"name": "Decisions",
|
||||
"description": "List of all recorded decisions in the graph",
|
||||
"mimeType": "application/json",
|
||||
},
|
||||
{
|
||||
"uri": "semantica://schema/info",
|
||||
"name": "Schema Info",
|
||||
"description": "Semantica server info and available capabilities",
|
||||
"mimeType": "application/json",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _read_resource(uri: str) -> dict:
|
||||
if uri == "semantica://graph/summary":
|
||||
return _tool_get_graph_summary({})
|
||||
if uri == "semantica://decisions/list":
|
||||
return _tool_query_decisions({"limit": 50})
|
||||
if uri == "semantica://schema/info":
|
||||
return {
|
||||
"name": "Semantica",
|
||||
"version": "0.4.0",
|
||||
"tools": [t["name"] for t in TOOLS],
|
||||
"resources": [r["uri"] for r in RESOURCES],
|
||||
}
|
||||
return {"error": f"Unknown resource URI: {uri}"}
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# JSON-RPC / MCP protocol handler
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
SERVER_INFO = {
|
||||
"name": "semantica",
|
||||
"version": "0.4.0",
|
||||
}
|
||||
|
||||
CAPABILITIES = {
|
||||
"tools": {"listChanged": False},
|
||||
"resources": {"listChanged": False, "subscribe": False},
|
||||
}
|
||||
|
||||
|
||||
def _handle(req: dict) -> dict | None:
|
||||
"""Dispatch a single JSON-RPC request; return None for notifications."""
|
||||
method = req.get("method", "")
|
||||
params = req.get("params") or {}
|
||||
req_id = req.get("id")
|
||||
|
||||
def ok(result):
|
||||
return {"jsonrpc": "2.0", "id": req_id, "result": result}
|
||||
|
||||
def err(code, message):
|
||||
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": code, "message": message}}
|
||||
|
||||
# Notifications (no id) — acknowledge silently
|
||||
if req_id is None and method.startswith("notifications/"):
|
||||
return None
|
||||
|
||||
if method == "initialize":
|
||||
return ok({
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": CAPABILITIES,
|
||||
"serverInfo": SERVER_INFO,
|
||||
})
|
||||
|
||||
if method == "notifications/initialized":
|
||||
return None
|
||||
|
||||
if method == "ping":
|
||||
return ok({})
|
||||
|
||||
if method == "tools/list":
|
||||
tools_out = [
|
||||
{"name": t["name"], "description": t["description"], "inputSchema": t["inputSchema"]}
|
||||
for t in TOOLS
|
||||
]
|
||||
return ok({"tools": tools_out})
|
||||
|
||||
if method == "tools/call":
|
||||
name = params.get("name", "")
|
||||
arguments = params.get("arguments") or {}
|
||||
handler = next((t["_handler"] for t in TOOLS if t["name"] == name), None)
|
||||
if handler is None:
|
||||
return err(-32601, f"Unknown tool: {name}")
|
||||
try:
|
||||
result = handler(arguments)
|
||||
text = json.dumps(result, ensure_ascii=False, indent=2)
|
||||
return ok({"content": [{"type": "text", "text": text}]})
|
||||
except Exception as exc:
|
||||
log.exception("Tool %s raised", name)
|
||||
return err(-32603, str(exc))
|
||||
|
||||
if method == "resources/list":
|
||||
return ok({"resources": RESOURCES})
|
||||
|
||||
if method == "resources/read":
|
||||
uri = params.get("uri", "")
|
||||
data = _read_resource(uri)
|
||||
text = json.dumps(data, ensure_ascii=False, indent=2)
|
||||
return ok({"contents": [{"uri": uri, "mimeType": "application/json", "text": text}]})
|
||||
|
||||
if method == "prompts/list":
|
||||
return ok({"prompts": []})
|
||||
|
||||
return err(-32601, f"Method not found: {method}")
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# stdio event loop
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def _run_stdio():
|
||||
log.info("Semantica MCP server starting on stdio")
|
||||
# Use binary stdin/stdout for reliable newline handling on Windows
|
||||
stdin = sys.stdin.buffer
|
||||
stdout = sys.stdout.buffer
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
req = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
resp = {"jsonrpc": "2.0", "id": None,
|
||||
"error": {"code": -32700, "message": f"Parse error: {exc}"}}
|
||||
stdout.write(json.dumps(resp).encode() + b"\n")
|
||||
stdout.flush()
|
||||
continue
|
||||
|
||||
resp = _handle(req)
|
||||
if resp is not None:
|
||||
stdout.write(json.dumps(resp, ensure_ascii=False).encode() + b"\n")
|
||||
stdout.flush()
|
||||
except EOFError:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception as exc:
|
||||
log.exception("Unhandled error in MCP loop: %s", exc)
|
||||
|
||||
log.info("Semantica MCP server stopped")
|
||||
|
||||
|
||||
def main():
|
||||
_run_stdio()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user