Run your own MCP server

Cortex ships a Model Context Protocol server so AI agents can read and write your knowledge graph — grounded in your ontology and gated by the same human review as any other write.

What you get

When the starter is on the classpath, an MCP server (built on Spring AI's MCP support) is auto-configured and served over HTTP at /mcp. It is on by default; the single switch cortex.mcp.enabled=false turns it off without excluding individual beans. Configure the transport under Spring AI's own properties:

application.yaml
spring:
  ai:
    mcp:
      server:
        protocol: stateless
        type: sync
        instructions: Use this MCP for knowledge graph operations. Load cortex://ontology resource on connection
cortex:
  mcp:
    enabled: true   # the default

Tools

Six tools. The read-only ones are marked idempotent; Ingest is the only writer, and even it does not touch the approved graph — it stages a branch for review.

ToolParameterWhat it does
LintttlChecks assertions against cortex://ontology: classes/properties not defined there are rejected; only rdf:type, rdfs:label, rdfs:comment are allowed beyond it. Returns the validated TTL, or the violations. Call before Ingest. Read-only.
IngestttlLints, SHACL-validates against the approved assertions, trims already-known triples, and stages the rest on a new branch for human review. Returns the branch name (null if nothing was novel), or the errors.
QuerysparqlRuns a SPARQL SELECT (including inferred statements); returns a text table. Read-only.
AsksparqlAnswers a SPARQL ASKtrue/false. Read-only.
DescribesparqlRuns a SPARQL DESCRIBE; returns Turtle. Read-only.
SearchtextFuzzy full-text search over rdfs:label and rdfs:comment, tolerant of typos, ranked by relevance and by how often each resource is opened. Read-only.
The tools guide the agent. Ingest instructs agents to Search or Query for existing IRIs before generating new data and to reuse them, so the same instance is never ingested under two names — and to open /branches/<branch> for a human once a branch is returned.

The ontology resource

One MCP resource is exposed so agents can ground themselves before writing:

ResourceURIMedia type
Ontologycortex://ontologytext/turtle

Connect a client

Point any MCP client at the HTTP endpoint. For example, a Claude Code .mcp.json:

.mcp.json
{
  "mcpServers": {
    "cortex": { "type": "http", "url": "http://localhost:8080/mcp" }
  }
}

A typical agent flow: read cortex://ontologySearch/Query for existing resources → Lint the new Turtle → Ingest it → a human approves the branch at /branches.

Customization

Bring your own schema

The graph is defined entirely by the resources you supply — override cortex.ontologies, cortex.shapes, and cortex.rules (each a list, merged in order) to model your own domain. Namespaces are yours; only cortex:// is reserved. See Getting started.

Override any tool or resource

Every auto-configured bean is @ConditionalOnMissingBean, so declaring your own bean of the same type replaces the default. Want to tighten the Ingest description, add a tool, or swap the ontology resource? Define the bean and Cortex backs off:

Java
@Configuration
class MyMcpCustomizations {
  // Replaces the default QueryTools bean; add or restrict tools as you like.
  @Bean
  QueryTools queryTools(CortexQuery query, CortexSearch search) {
    return new QueryTools(query, search);
  }
}
Narrow to the role you need. Cortex extends nine role interfaces (CortexQuery, CortexSearch, CortexIngestor, …). Injecting the specific role instead of the whole Cortex keeps your beans focused and trivially testable.

Turn off what you don't need

The MCP server, the web UI, backup, and restore are each a single switch — flip cortex.web.enabled=false to run headless with just the MCP server, for instance. See the Configuration reference.

Embed without Spring

Need the graph outside a Spring context — in a library, a test, or another framework? Use CortexBuilder from cortex-core to assemble a Cortex directly:

Java
Cortex cortex = CortexBuilder.create()
    .ontologies(List.of(ontologyTtl))   // file *content*, not paths
    .shapes(List.of(shapesTtl))
    .rules(List.of(rulesText))
    .build();                           // in-memory; .persistent(dir) for on-disk

The same eleven services the Spring auto-configuration wires are assembled here, so the two paths produce identical objects.