Skip to content

Knowledge Graphs — Hybrid Pattern with External MCP

Knowledge Graphs are for slow-changing domain models. They are not the right choice for high-volume transactional data such as a large SKU catalog with real-time stock, an order history with millions of rows, or a customer database that updates every minute. Record allowances depend on your plan or Enterprise agreement; this guidance is about change rate and ownership, not a fixed graph size.

The right pattern for these domains is hybrid: put the domain structure in a Knowledge Graph and the live data behind an external MCP server. The agent uses both — the Knowledge Graph to understand “how my domain works” and the MCP server to answer “what is in stock right now”.

LayerKnowledge GraphExternal MCP server
CardinalityCurated reference sets within the deployment quotaOperational systems sized for their workload
Change rateHours / days (slow-changing)Seconds / minutes (real-time)
Source of truthA reviewed bundle maintained by your teamYour existing operational system (Shopify, SAP, custom database)
Update patternbrewctl kg apply (atomic)Live API calls
Tools agent useslist_X, get_X, list_X_idsCustom tools per MCP server (search_products, get_inventory, …)
Question answered”How does my domain work?""What is in my system right now?”

A running-shoes e-commerce store with a large, frequently changing SKU catalog is best modeled in layers.

The structure of the shoe domain:

  • Category tree (~50 categories: running → road → trail → …)
  • Attribute taxonomy (10-50 attributes: size, color, material, pronation_type)
  • Attribute value enums (color: red/blue/black/…, size: EU 36..48)
  • Brand registry (~50-500 brands with positioning metadata)
  • Size conversion tables (US ↔ EU ↔ UK)
my-store/manifest.yaml
bundle_name: shoe-store-taxonomy
version: 2026-05-27.1
entity_types:
- name: category
schema_file: schemas/category.schema.json
entities_file: entities/categories.yaml
- name: brand
schema_file: schemas/brand.schema.json
entities_file: entities/brands.yaml
- name: attribute_definition
schema_file: schemas/attribute_definition.schema.json
entities_file: entities/attributes.yaml
Terminal window
brewctl kg apply ./my-store

After you add and apply the entity files, SyntheticBrew stores one entity for each record, subject to the deployment’s plan or Enterprise quota, and generates tools such as:

list_category(filters={parent_id?, surface?})
get_category(ids)
list_brand(filters={tier?, in_category?})
get_brand(ids)
list_attribute_definition(filters={for_category?})
get_attribute_definition(ids)

The inventory — 20K SKUs with prices, stock, photos, reviews. This is the customer’s existing system (Shopify, custom API). They put an MCP server in front of it:

With brewctl, keep the MCP server and agent as separate resources:

mcp_servers/shoe-inventory.yaml
apiVersion: syntheticbrew/v1
kind: MCPServer
name: shoe-inventory
type: http
url: https://shop.example.com/mcp
auth_type: api_key
auth_key_env: SHOE_INVENTORY_TOKEN
enabled: true
agents/shop-assistant.yaml
apiVersion: syntheticbrew/v1
kind: Agent
name: shop-assistant
model: glm-5
mcp_servers: [shoe-inventory]
capabilities:
- type: knowledge_graphs
enabled: true
config:
bundles: [shoe-store-taxonomy]

The direct REST equivalent also uses separate MCP-server, agent, and capability endpoints. Do not put capabilities inside a config-import agent block; that nested field belongs to the brewctl resource format.

The shoe-inventory MCP server provides:

search_products(filters={category_id, brand_id, size, color, in_stock, price_range})
get_product(sku)
get_inventory(sku)
list_recent_reviews(product_id, limit)

A multi-step user query like “I want red running shoes for the road, neutral pronation, under $150”:

Step 1: list_category(filters={parent: "running", surface: "road"})
→ KG returns: [
{id: "road-running-neutral", label: "Neutral Road Running"},
{id: "road-running-stability", label: "Stability Road Running"},
...
]
Step 2: get_attribute_definition(ids=["pronation_type"])
→ KG returns: {
entities: [{id: "pronation_type", data: {
enum: ["neutral", "overpronation", "underpronation"]
}}],
not_found: []
}
Step 3: list_brand(filters={tier: "mid", category: "road-running-neutral"})
→ KG returns: ~8 brands matching
Step 4: search_products(
category_id: "road-running-neutral",
pronation: "neutral",
color: "red",
brand_in: [...],
price_lte: 150
)
→ EXTERNAL MCP returns: 23 actual SKUs in stock, with prices and photos
Step 5: get_product(sku="ASICS-GEL-NIMBUS-25-RED-42")
→ EXTERNAL MCP returns: full product data, reviews, alternative sizes

The Knowledge Graph gives the agent a map of the domain — what categories exist, what “neutral pronation” means, which brands are relevant for road running. The external MCP server gives live availability.

The Knowledge Graph supplies the reviewed brand classifications and allowed pronation values. The external MCP server supplies current stock. The agent can use both sources in one workflow without copying changing inventory into the graph bundle.

A medical reference application providing drug recommendations. Knowledge Graph holds the medical taxonomy, an external MCP server provides the patient-specific formulary.

list_condition(filters={icd10_chapter, severity})
list_symptom(filters={condition})
list_treatment_class(filters={condition})
list_active_ingredient(filters={treatment_class})

Conditions, symptoms, treatment classes, and active ingredients with ATC codes. This is slow-changing reference data curated by domain experts.

get_patient_allergies(patient_id)
list_available_medications(active_ingredient, country)
get_drug_interactions(drug_id_a, drug_id_b)

Patient-specific, jurisdiction-specific, frequently updated. Lives in the hospital’s existing pharmacy system, exposed via MCP.

User: “What can I prescribe for a patient with flu symptoms?”

Step 1: list_condition(filters={symptom_group: "flu_like"})
→ KG: list of flu-like conditions with ICD-10 codes
Step 2: list_treatment_class(filters={condition: "J10"})
→ KG: antiviral classes for influenza
Step 3: list_active_ingredient(filters={treatment_class: "antivirals"})
→ KG: oseltamivir, zanamivir, ...
Step 4: get_patient_allergies(patient_id="PT-12345")
→ EXTERNAL MCP: patient allergy list
Step 5: list_available_medications(active_ingredient="oseltamivir", country="DE")
→ EXTERNAL MCP: products available in Germany

A simple rule: if the data answers questions about your domain itself (taxonomy, categories, attributes, relationships, controlled vocabularies), it goes in a Knowledge Graph. If the data answers questions about a specific instance or current state (stock, prices, patient records, transactions), it goes in an external MCP server.

A second rule: if records change continuously or must reflect the latest transaction, keep them in the operational system. Knowledge Graph bundles are declarative and atomically reconciled, so they fit reviewed reference data better than transactional state.

❌ All fast-changing SKUs in a Knowledge Graph

Section titled “❌ All fast-changing SKUs in a Knowledge Graph”

Even when a plan permits the record count, frequently changing prices and stock defeat the purpose of a declarative reference bundle. Use an external MCP server for transactional inventory.

User profiles change frequently. They are also tenant-specific data that should live in your application’s primary data store, not in a customer-declared bundle. Use a dedicated API or external MCP server.

❌ Hardcoding inventory in agent system prompts

Section titled “❌ Hardcoding inventory in agent system prompts”

A common workaround is to list the product catalog in the system prompt. That becomes difficult to update, does not provide typed filters, and can lead to invented or stale IDs. Use a Knowledge Graph for the structure plus an external MCP server for live data.