Ontology Integration¶
This document describes ProEthica's integration with OntServe for ontology management.
Overview¶
ProEthica uses OntServe as its central ontology repository. The integration provides:
- Concept definitions for extraction validation
- Class hierarchy for entity classification
- SPARQL queries for knowledge retrieval
- Candidate concept submission
Architecture¶
ProEthica OntServe
+------------+ +------------+
| MCP Client |-- Streamable HTTP -->| FastMCP 3 |
| |<------ SSE ----------| :8082 |
+------------+ +------+-----+
|
v
+------------+
| PostgreSQL |
| (ontologies|
+------------+
Storage Flow¶
ProEthica Extraction Pipeline
|
v
+---------------------------+
| TemporaryRDFStorage | (ProEthica DB: ai_ethical_dm)
| - Draft entities |
| - JSON-LD in rdf_json_ld |
| - is_published flag |
+---------------------------+
|
| commit_case_versioned()
v
+---------------------------+
| OntServe concepts table | (OntServe DB: ontserve)
| - Versioned entities |
| - is_current flag |
| - extraction_run_version |
+---------------------------+
|
| TTL generation
v
+---------------------------+
| proethica-case-N.ttl | (Single file, overwritten)
| proethica-intermediate- |
| extracted.ttl | (Classes, appended)
+---------------------------+
Versioning¶
Case TTL files are overwritten on re-extraction (no accumulation). The OntServe concepts table preserves version history via is_current and extraction_run_version columns. Classes are versioned individually.
TTL File Structure¶
Ontology files in OntServe/ontologies/:
| File | Purpose |
|---|---|
proethica-core.ttl |
9 base concepts (Role, Principle, etc.) |
proethica-intermediate.ttl |
Domain classes and ObjectProperties |
proethica-intermediate-extracted.ttl |
LLM-extracted classes |
proethica-case-N.ttl |
Case-specific individuals |
proethica-provenance.ttl |
Provenance properties |
URI namespaces:
@prefix proeth-core: <http://proethica.org/ontology/core#> .
@prefix proeth: <http://proethica.org/ontology/intermediate#> .
@prefix proeth-cases: <http://proethica.org/ontology/cases#> .
@prefix proeth-prov: <http://proethica.org/provenance#> .
@prefix caseN: <http://proethica.org/ontology/case/N#> .
Model Context Protocol (MCP)¶
Connection¶
ProEthica connects to OntServe via the MCP Streamable HTTP transport:
| Setting | Default |
|---|---|
| URL | http://localhost:8082 |
| Protocol | MCP (JSON-RPC 2.0 over Streamable HTTP) |
| Server | FastMCP 3.x |
Configuration¶
Environment variables:
ONTSERVE_MCP_URL=http://localhost:8082
ProEthica auto-detects OntServe availability on startup and sets ONTSERVE_MCP_ENABLED accordingly.
MCP Client¶
Client Implementation¶
Two layers handle MCP communication:
| File | Purpose |
|---|---|
app/services/mcp_transport.py |
Low-level MCP Streamable HTTP transport (session management, SSE parsing) |
app/services/external_mcp_client.py |
High-level tool wrappers used by extraction pipeline |
Connection Management¶
from app.services.external_mcp_client import get_external_mcp_client
client = get_external_mcp_client()
result = client.get_entities_by_category("Role")
Available Methods¶
OntServe MCP provides 11 tools. The three most commonly used in ProEthica's extraction pipeline are documented below. See Architecture for the full tool list.
get_entities_by_category¶
Retrieves entities of a specific type from the ontology.
Request (MCP call_tool format):
{
"jsonrpc": "2.0",
"method": "call_tool",
"params": {
"name": "get_entities_by_category",
"arguments": {
"category": "Role",
"domain_id": "proethica-intermediate"
}
},
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": {
"content": [{
"type": "text",
"text": "{\"entities\": [{\"uri\": \"proeth:Engineer\", \"label\": \"Engineer\"}]}"
}]
},
"id": 1
}
sparql_query¶
Executes SPARQL queries against the ontology.
Request:
{
"jsonrpc": "2.0",
"method": "call_tool",
"params": {
"name": "sparql_query",
"arguments": {
"query": "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10",
"domain_id": "proethica-intermediate"
}
},
"id": 2
}
submit_candidate_concept¶
Submits new concept for ontology review.
Request:
{
"jsonrpc": "2.0",
"method": "call_tool",
"params": {
"name": "submit_candidate_concept",
"arguments": {
"label": "AI Verification",
"category": "Capability",
"definition": "Ability to verify AI-generated outputs",
"source": "Case 24-2",
"domain_id": "proethica-intermediate"
}
},
"id": 3
}
Ontology Structure¶
Three-Layer Architecture¶
ProEthica uses a three-layer ontology:
| Layer | Purpose | Example Classes |
|---|---|---|
| Core | 9 foundational concepts | Role, Principle, Obligation |
| Intermediate | Domain-specific | Engineer, PublicSafety |
| Case | Instance data | Case24_2_Engineer |
Core Ontology (proethica-core)¶
Defines the 9 component types:
| Class | URI | Description |
|---|---|---|
| Role | proeth-core:Role | Professional positions |
| Principle | proeth-core:Principle | Abstract standards |
| Obligation | proeth-core:Obligation | Concrete duties |
| State | proeth-core:State | Situational context |
| Resource | proeth-core:Resource | Available knowledge |
| Action | proeth-core:Action | Professional behaviors |
| Event | proeth-core:Event | Precipitating occurrences |
| Capability | proeth-core:Capability | Permissions |
| Constraint | proeth-core:Constraint | Prohibitions |
Intermediate Ontology (proethica-intermediate)¶
Domain-specific subclasses:
proeth-int:Engineer rdfs:subClassOf proeth-core:Role .
proeth-int:CompetenceRequirement rdfs:subClassOf proeth-core:Obligation .
proeth-int:NSPECodeProvision rdfs:subClassOf proeth-core:Resource .
Case Analysis Classes (Step 4)¶
Step 4 produces 8 additional entity types beyond the 9 base concepts:
| Class | Phase | Description |
|---|---|---|
CodeProvisionReference |
2A | NSPE code sections cited in the case |
PrecedentCaseReference |
2B | BER cases referenced in the discussion |
EthicalQuestion |
2C | Questions posed to Board for ethical review |
EthicalConclusion |
2C | Board's formal determinations |
CanonicalDecisionPoint |
Phase 3 | Points where ethical choices must be made (E1-E3 synthesis) |
ResolutionPattern |
2E | Patterns of how ethical tensions are resolved |
CausalNormativeLink |
2E | Connections between causal factors and normative principles |
QuestionEmergence |
2E | How ethical questions arise from case facts |
Properties:
| Property | Domain | Range | Description |
|---|---|---|---|
hasOption |
DecisionPoint | DecisionOption | Links decision point to options |
involvesRole |
DecisionPoint | Role | Professional roles involved |
appliesProvision |
DecisionPoint | EthicalCode | Applicable code provisions |
isBoardChoice |
DecisionOption | boolean | Whether this option was chosen |
answersQuestion |
BoardConclusion | EthicalQuestion | Links conclusion to question |
The full extraction pipeline produces 17 entity types across all 4 steps (9 base concepts + 8 Step 4 types).
Entity Review Integration¶
Available Classes Display¶
During entity review, ProEthica fetches available classes:
# In entity_review.py
from app.services.external_mcp_client import get_external_mcp_client
def get_available_classes(category):
client = get_external_mcp_client()
return client.get_entities_by_category(category)
Class Assignment¶
When user assigns entity to existing class:
- Entity
class_uriupdated to match ontology class - Definition may be inherited or customized
- Relationship to parent class preserved
New Class Approval¶
When approving new class:
submit_candidate_concept()called- OntServe creates candidate entry
- Admin reviews in OntServe
- Approved classes added to ontology
SPARQL Queries¶
Common Queries¶
Get all Roles:
PREFIX proeth: <http://proethica.org/ontology#>
SELECT ?role ?label ?definition
WHERE {
?role rdfs:subClassOf proeth:Role .
?role rdfs:label ?label .
OPTIONAL { ?role rdfs:comment ?definition }
}
Get Obligations for a Role:
PREFIX proeth: <http://proethica.org/ontology#>
SELECT ?obligation ?label
WHERE {
?role rdfs:label "Engineer" .
?obligation proeth:generatedBy ?role .
?obligation rdfs:label ?label .
}
Query Execution¶
Via MCP client:
query = """
SELECT ?s ?label WHERE {
?s rdfs:label ?label .
?s a proeth:Role .
}
"""
results = await client.sparql_query(query)
Error Handling¶
Connection Failures¶
If OntServe unavailable:
try:
entities = await client.get_entities_by_category("Role")
except MCPConnectionError:
# Fallback to cached data or empty list
entities = []
Timeout Handling¶
Configurable timeout with retry:
from app.services.mcp_transport import MCPTransport
transport = MCPTransport(timeout=30)
Graceful Degradation¶
ProEthica continues functioning without OntServe:
- Available Classes section shows empty
- Extraction still works
- Entities stored locally
- Manual ontology sync later
Caching¶
Class Cache¶
Available classes cached to reduce queries:
| Setting | Value |
|---|---|
| Cache duration | 5 minutes |
| Invalidation | On new class approval |
Query Cache¶
SPARQL results cached:
| Setting | Value |
|---|---|
| Cache duration | 1 minute |
| Key | Query hash |
Monitoring¶
Connection Status¶
Header shows connection status:
- Green: Connected
- Yellow: Degraded
- Red: Disconnected
Health Check¶
Periodic health check:
async def health_check():
try:
await client.get_entities_by_category("Role")
return True
except:
return False
Troubleshooting¶
MCP Not Responding¶
-
Check OntServe MCP is running:
curl -s http://localhost:8082/health -
Verify port 8082 not blocked
-
Check OntServe logs
Empty Available Classes¶
- Verify MCP connection active
- Check ontology loaded in OntServe
- Query database directly:
SELECT * FROM ontology_classes WHERE type = 'Role';
Query Failures¶
- Validate SPARQL syntax
- Check ontology namespaces
- Review OntServe error logs