DEV Community

Cover image for Domain Knowledge for AI agents: static vs just-in-time teaching
Vlastimil Elias
Vlastimil Elias

Posted on

Domain Knowledge for AI agents: static vs just-in-time teaching

MCP connects agents to tools and data. That is only half of Agent Experience. The other half is agent teaching: how the agent should interpret tool results, what to ask user next, and when not to invent an answer from general knowledge.

What you are teaching is Domain Knowledge — the curated rules raw APIs and databases do not carry safely on their own (funnels, status meanings, what must not be mixed up). Without it, agents misread codes and invent incorrect next steps. Getting that knowledge into the agent at the right moment is a classic Knowledge Engineering problem: capture how experts reason about the domain, then deliver it through channels the runtime actually honors — not only as a wiki page nobody loads.

Early LLMs were mostly knowledge recall machines — answer from what was stored in the weights. Modern models are intentionally trained and productized toward agentic behavior: plan tool calls, read each result, decide the next hop, and ask the user when something is missing (the tool-use / agentic loop). Domain Knowledge teaching is what makes that loop trustworthy in your domain — so the model composes the right next steps instead of inventing them from general memory.

This post is for two audiences: people building MCP servers who want agents to use their tools safely, and people wiring multi-agent stacks (or Agent Skills) on top of those servers who need to know what belongs in a system prompt versus what must ride every tool result.

The delivery split

How Domain Knowledge is delivered matters as much as how it is authored. Illustrating with a simple e-commerce stack (customers → orders → shipping / help articles); the same split applies to any domain MCP:

  • Static teaching — stable Domain Knowledge (funnel, core enums, policy). Loaded once per session or agent role.
  • Dynamic / just-in-time teaching — observation-local Domain Knowledge: scope and next steps for this result. Travels with the tool response.

Dynamic teaching has two practical implementations.

  1. Structured fields — machine-actionable truth (status, next-step arguments, ids, closed codes).
  2. Contextual hints — short, data-driven English (agentHints) assembled only when this payload needs it.

Do not treat Skills or system prompts as a substitute for the response-local half.

Static Domain Knowledge: MCP first

On the MCP server (portable contract)

Channel Role
MCP server instructions Short shared core: resolve funnel, core grades/enums, “act from structured fields; follow hints when present”
Tool descriptions Dense per-tool how-to — size-capped and always-on in context, so summary, not an encyclopedia

Every consumer of the server gets tools when connected. Server instructions are weaker than that sounds. The MCP spec treats them as optional (clients MAY surface them), so support varies:

  • OpenAI Codex — documents support, but does not reliably expose them as server-wide agent guidance (often demoted to namespace metadata).
  • Claude Desktop / Claude.ai — silently drop server instructions, Claude Code honors them.

Static text that does land still has a budget. Hosts such as Claude truncate server and tool descriptions around ~2 KB each. Whatever you ship there is loaded into the model context for the session — so an encyclopedia burns tokens on every turn, with inference speed and cost consequences even when the agent never needs that paragraph.

Put a short shared core in server instructions and mirror essentials in tool descriptions. Treat instructions alone as insufficient. Prefer case-local structured fields and hints for the long tail: they ride every call without bloating always-on context.

Off the MCP server (useful, not sufficient)

Channel Role
Agent Skills (SKILL.md) Task-level progressive disclosure: host loads metadata - agent pulls the body when the task looks relevant
Multi-agent system prompts Role packaging: “you are the orders / fulfillment specialist” — stable playbook for that agent only

The usual vendor split is right as far as it goes: MCP connects; Skills teach procedures. In a multi-agent stack the same idea shows up as orchestrator and specialist system prompts. Both are good homes for the static guide of a given agent. Both are weak for “explain this rare code” or “follow these related ids” — they load for a task or role, not for this tool result.

Two delivery traps today:

  • Skills ≠ MCP install. Enabling a MCP server does not install a companion Skill. Until Skills ship with the MCP server (in progress), Skill-only teaching is optional and unreliable.
  • System prompts are private. A specialist’s prompt is invisible to every other client of the same MCP. If correct interpretation depends on that prompt alone, the portable contract is broken, knowledge is fragmented and may be inconsistent.

Why the static slice is not enough

Static Domain Knowledge is usually authored as natural language — server instructions, tool descriptions, Skills, system prompts. A happy-path guide reads like this:

Resolve the customer first, then list orders for that customer.
If resolve returns status need_order, ask which order they mean —
show recentOrders; do not invent an order id.
Order status is the source of truth for fulfillment; help articles
explain policy, they do not override status.
Prefer structured fields on the tool result; follow agentHints when present.
Enter fullscreen mode Exit fullscreen mode

That works for the stable funnel. Reality adds storefront-specific scope, optional filters not yet pinned, rare answer-shaping flags, and bridges to sibling domains (shipping, returns, payments) that only matter when related ids are present in the data. Encoding every case in always-on instructions produces an encyclopedia that burns context every turn, still misses the rare code that is not on this call, and fails for callers that never saw your Skill or system prompt. Those cases need teaching on the tool result itself.

Dynamic Domain Knowledge on the tool result

Industry names for this half vary — progressive disclosure, just-in-time instructions, tool-response steering, next-action hints. In Knowledge Engineering terms: put the rule next to the case. It can be implemented as two layers on the tool result:

Walk the same short conversation through both layers.

User: “What’s the status of my order?”

Structured fields (primary)

Machine-actionable truth the agent can act on even if it ignores English: workflow status, ready-made next-call args, closed codes, thin stubs for a follow-on tool.

Tool: resolve_customer

{
  "status": "need_order",
  "askNext": "orderId",
  "recentOrders": ["ORD-1042", "ORD-1038", "ORD-1021"],
  "listOrdersArgs": { "customerId": "cust_9f3a" }
}
Enter fullscreen mode Exit fullscreen mode

The next hop is in the payload: ask which order, or call list_orders with customerId already filled — no extra always-on instructions required.

User: “The latest one — ORD-1042.”

Tool: get_order

{
  "orderId": "ORD-1042",
  "status": "shipped",
  "shipmentStub": { "trackingId": "1Z999", "carrier": "ups" },
  "relatedDocs": { "count": 2, "preferredId": "kb-returns-eu" }
}
Enter fullscreen mode Exit fullscreen mode

Stubs (shipmentStub, relatedDocs) are portable ids for later joins — still on this orders MCP. Following them onto sibling servers is the next section.

Contextual hints (reinforcement)

Short, data-driven English assembled only when this payload needs it — not a glossary pasted on every call. Replay the same two turns with agentHints added — still on the orders MCP.

Opening turn — resolve_customer:

{
  "status": "need_order",
  "askNext": "orderId",
  "recentOrders": ["ORD-1042", "ORD-1038", "ORD-1021"],
  "listOrdersArgs": { "customerId": "cust_9f3a" },
  "agentHints": [
    "Ask which order they mean. Show recentOrders. Do not invent an order id.",
    "This customer resolved on the EU storefront only — do not merge US marketplace orders into the same answer."
  ]
}
Enter fullscreen mode Exit fullscreen mode

After the user picks ORD-1042 — same get_order, with a rare-code gloss when the user asks for an ETA:

User: “When will ORD-1042 arrive?”

Tool: get_order

{
  "orderId": "ORD-1042",
  "status": "shipped",
  "flags": ["etaUnknown"],
  "shipmentStub": { "trackingId": "1Z999", "carrier": "ups" },
  "relatedDocs": { "count": 2, "preferredId": "kb-returns-eu" },
  "agentHints": [
    "etaUnknown: carrier accepted the parcel but has not published a delivery window — say in transit, do not invent a date."
  ]
}
Enter fullscreen mode Exit fullscreen mode

Core statuses (placed / shipped / delivered / …) stay in the static guide once. Behavioral gloss fires when the answer-shaping code is present. Hints are not a dump of every tally in the payload.

Connecting company data across domains

Enterprises rarely keep one system of record. Orders, shipping, payments, returns policy, inventory — often different teams, different APIs, and increasingly different MCP servers (or specialist agents) in the same stack.

Static manuals that say “also try the shipping server” on every turn are noise. Dynamic teaching is a practical join layer for the workflow engine:

  • Structured fields carry portable ids, stubs, and counts, but only when a cross-domain link is in scope.
  • Contextual hints say when and why to follow them — and what not to overwrite (e.g. help articles do not replace order status).

Continuing after the agent said ORD-1042 is in transit with no ETA. The orders payload already had stubs; now hints say when and why to leave this MCP:

{
  "agentHints": [
    "Tracking exists — call get_shipment on the shipping MCP with trackingId.",
    "Returns policy is in relatedDocs; call get_help_article — it does not override order status."
  ]
}
Enter fullscreen mode Exit fullscreen mode

User: “OK — where is the parcel right now, and what’s my return window?”

Tools: get_shipment (shipping MCP) and get_help_article (help MCP):

// get_shipment
{ "trackingId": "1Z999", "lastScan": "Brussels hub", "state": "in_transit" }

// get_help_article
{ "id": "kb-returns-eu", "title": "EU returns — 30 days from delivery", "body": "EU return policy is ...." }
Enter fullscreen mode Exit fullscreen mode

Structured stubs + hints teach the join; sibling servers answer the detail. An orchestrator or sibling agent can act on those cues even if it never shared your Skill: the bridge rides the tool result, not a private system prompt.

You are not federating every backend into one payload. You teach the next hop across company Domain Knowledge boundaries — whether the follow-on lives on this MCP, another MCP, or another agent.

Can a Skill or system prompt replace dynamic teaching?

No — not for the case-local half. Skills and system prompts are fine for packaging the static guide (“you are the orders agent; resolve customer first”). They are a poor home for “explain this etaUnknown” or “follow these related ids.”

Why, briefly:

  • Agent loads Skill when the task looks relevant; a system prompt loads for the Agent role. Neither sees what is on this tool result unless you paste every edge case into the prompt.
  • Encoding those edge cases as a long “if status X and flag Y…” Skill burns context and is easy to mis-apply — especially on smaller models. Hints are already matched to the payload.
  • Skills and prompts do not invent status / next-step fields. Those have to come from the tool.
  • Other callers of the same MCP (evals, APIs, sibling agents) never see your prompt. Teaching that must always work has to ride the response.

Use Skills / prompts as optional packaging of the static core. Keep dynamic teaching on the wire.

How the pieces fit

Static guide / Skills / system prompts  →  how the domain usually works
Response Structured fields              →  what to do next (primary)
Response Contextual hints               →  this result only — rare codes, scope, next MCP hop
Enter fullscreen mode Exit fullscreen mode

Put the stable funnel and core statuses in the MCP static guide (and optionally in a Skill or agent prompt). Put product- or case-specific routing, rare-code glosses, and cross-domain bridges on the tool result — structured fields first, hints as reinforcement.

For anything the agent must get right:

  • Tool descriptions (plus server instructions when the client honors them) — what hosts actually surface; keep them short (~2 KB caps, always-on context cost).
  • Structured fields + hints on results — guaranteed every call, scoped to the answer.
  • Skills / System prompts — nice for a given host or role; never the only place correct interpretation lives.

Later, if hosts ship Skills with the MCP server, more of the static guide can move there. Keep response-local fields and hints for facts that exist only on the current result.

For MCP server builders

Treat Domain Knowledge delivery as part of the API contract — Knowledge Engineering for agents, not only schema design:

  1. Author a short static core in server instructions and tool descriptions — do not rely on instructions alone (Codex / Claude Desktop gaps above).
  2. Design responses for agents: closed codes, next-step args, thin stubs for follow-on tools.
  3. Assemble contextual hints from the payload — skip empty glossaries.
  4. Keep case-specific sentences out of the static manual when they can be stamped onto the response.
  5. Emit cross-domain bridges as data + hints when a join is in scope.
  6. Document Skills / multi-agent prompts as optional packaging of the same static core — not as the home of rare-code explanations.

One MCP surface then works for a lone IDE agent, a CI eval, and a specialist inside a multi-agent orchestrator. Static Domain Knowledge can be thinned per host; the dynamic slice — including cross-domain next hops — stays on the wire.

Conclusion

MCP gives agents tools. Domain Knowledge teaching keeps them from misreading the answers, fuels what modern models are trained to do well — the agentic loop over those tools — and helps that loop join company data across domains, even when each domain is served by a different MCP server or specialist agent.

That is Knowledge Engineering in practice: capture how the domain should be reasoned about, then place each piece where the agent will see it. Stable model → static channels (MCP first; Skills and system prompts optional). Case-local truth → dynamic channels on every result (structured fields primary, contextual hints as reinforcement).

It also helps smaller and cheaper models stay on the rails: they get the next hop and rare-code gloss on the payload instead of reconstructing a long playbook from a bloated static prompt. Same workflow quality, lower inference cost.

Start with that split and let just-in-time Domain Knowledge ride the tool response. Feedback welcome if you try the pattern on your own domain tools.

Further reading

Dynamic / response-local teaching first:

Static packaging (Skills / server instructions):

Top comments (2)

Collapse
 
anciwasim profile image
Wasim Sheikh •

The point that a specialist's system prompt is invisible to every other client of the same MCP stuck with me. It also means the static half can drift quietly: if that prompt gets edited in a dashboard with no diff or rollback, a behavior change could be the model, the prompt, or the data, and nobody can say which. Keeping the response-local hints in the server's own code at least puts them under review and version control. Do you tie the prompt version to each run's log, or is that still informal?

Collapse
 
velias profile image
Vlastimil Elias •

This is more observability and change management question, which is orthogonal to the principles described in this article. I agree that "prompt gets edited in a dashboard with no diff or rollback" is not the best idea ;-)