Externalize the Knowledge
Three months ago I wrote about embedding domain expertise directly into an MCP server — 40+ agent definitions, workflow patterns, platform concepts, all in knowledge.ts. It worked at small scale. Then knowledge started changing faster than we could deploy.
Thesis: MCP servers that embed knowledge become deployment bottlenecks. Externalizing knowledge to a dedicated knowledge service decouples the knowledge lifecycle from the MCP lifecycle — enabling real-time updates, personalization, and zero-downtime evolution. This is the discovery protocol principle from Post 1 applied to knowledge itself: MCP should query knowledge, not contain it.
This is Post 2 of 2 in the "MCP Architecture" series. Read Post 1: MCP as Discovery Protocol for the architectural principle that motivates this pattern.
The Knowledge Embedding Trap
In MCP in Practice: Knowledge-First Builder, I made the case for structured knowledge over fuzzy RAG retrieval. I embedded an agent catalog, workflow patterns, validation rules, and platform terminology directly into the MCP server's codebase. It was version-controlled, type-checked, and directly accessible to tools.
That post's conclusion still holds: structured knowledge beats fuzzy retrieval for builder guidance. What doesn't hold is where that structured knowledge should live.
Here's what happened at scale:
| Event | Required Action | Cycle Time |
|---|---|---|
| New agent added to platform | Update knowledge.ts, PR, review, build, deploy MCP |
2–3 days |
| Workflow pattern revised | Update knowledge.ts, PR, review, build, deploy MCP |
2–3 days |
| Anti-pattern discovered in production | Update knowledge.ts, PR, review, build, deploy MCP |
2–3 days |
| Platform terminology clarified | Update knowledge.ts, PR, review, build, deploy MCP |
2–3 days |
Every knowledge update followed the same path as a code change. The knowledge team depended on the MCP team's release cycle. A terminology fix — changing three words in a description — required the same review and deployment process as a new feature.
The embedded knowledge was correct. The deployment coupling was the problem.
Why Externalize
In Post 1, I argued that the discovery protocol unlocks pluggability, backend evolution without client changes, and independent lifecycles. Embedded knowledge breaks all three:
- No pluggability. Adding a new agent definition to the catalog requires a code change to
knowledge.ts, not a publish action. The knowledge team can't plug in new content — they file a PR and wait. - No independent evolution. The backend team adds a new agent to the platform on Monday. The knowledge team writes the documentation on Tuesday. But agents don't see it until the MCP server deploys on Friday — because the description is baked into the binary.
- No independent lifecycles. Knowledge and protocol share a release cycle. A three-word terminology fix rides the same deployment as a schema migration. The blast radius of every release grows because unrelated changes are bundled.
Externalizing knowledge restores all three properties. Knowledge becomes pluggable (publish to the knowledge service), independently evolvable (update without touching MCP), and separately lifecycled (domain experts ship on their own cadence).
The Externalization Pattern
The fix is architectural: move knowledge out of the MCP server and into an external knowledge service. Not a text index — a full knowledge platform that handles ingestion, structuring, search, ranking, and retrieval. The MCP server becomes a routing shim between the agent and the knowledge service — exactly the discovery protocol pattern from Post 1.
We use Vertex AI Search for this. It's not just keyword search — it's a managed knowledge service that ingests structured documents, indexes them with metadata, ranks results contextually, and returns grounded answers. The distinction matters: the MCP server doesn't need to parse, rank, or compose search results. It passes a query with context and gets back structured knowledge ready to serve.
flowchart LR
Agent["Agent<br/>(Claude, Cursor, etc.)"] --> MCP["MCP Server<br/>(thin shim)"]
MCP --> API["Platform APIs"]
MCP --> KS
subgraph KS["Knowledge Service (Vertex AI Search)"]
direction TB
Ingest["Ingestion"] --> Index["Index + Metadata"]
Index --> Rank["Search + Ranking"]
Rank --> Ground["Grounding +<br/>Structured Retrieval"]
end
style MCP fill:#5a5a8a,color:#fff
style KS fill:#2d6a4f,color:#fff
The separation is clean. Here's what stays in the MCP server and what moves out:
| Stays in MCP | Moves to Knowledge Service |
|---|---|
| Tool schemas (name, parameters, return type) | Agent catalog (definitions, rules, examples) |
| Input validation (type checks, required fields) | Workflow patterns (templates, anti-patterns) |
| Transport and auth | Platform terminology and concepts |
| Server instructions skeleton | Best practices and guidance |
| Tool handler routing (which backend to call) | Troubleshooting knowledge |
| Error formatting | Release notes and changelog context |
The MCP server keeps what's structural — the shape of the protocol surface. Knowledge that changes with the domain moves to a service that domain experts can update independently.
Dynamic Instructions at Init
This is where the pattern gets interesting. MCP's server instructions — the guidance sent to the agent when it connects — don't have to be static strings in your codebase. They can be composed dynamically at connection time.
The init sequence:
sequenceDiagram
participant Agent
participant MCP as MCP Server
participant KS as Knowledge Service<br/>(Vertex AI Search)
Agent->>MCP: Connect (client_info)
MCP->>KS: Query: "server instructions"<br/>filters: {clientType, role}
Note over KS: Search, rank, ground<br/>against latest published atoms
KS-->>MCP: Structured results
MCP-->>Agent: Composed instructions<br/>(fresh, current-as-of-now)
Note over KS: Domain experts publish<br/>directly to knowledge service —<br/>no MCP involvement
In practice, this looks like:
// MCP server — init handler
async function getServerInstructions(clientInfo: ClientInfo): Promise<string> {
// Query knowledge service for latest instructions
const results = await knowledgeService.query({
query: "mcp server instructions",
filters: {
content_type: "server-instructions",
audience: clientInfo.clientType, // "cursor", "claude", "api"
},
limit: 5,
});
// Compose instructions from structured results
const sections = results.map(r => r.content);
return sections.join("\n\n---\n\n");
}
The MCP server doesn't contain the instructions — it queries for them. Update the knowledge service, and the next agent connection gets the updated guidance. No MCP deployment required.
The knowledge service holds structured documents — each one a focused knowledge atom under 2000 tokens — published independently by domain experts. When the MCP server queries at init, it gets the latest published atoms, ranked and grounded by the knowledge service, composed into instructions that reflect current platform state.
Decoupling the Lifecycle
This is the business case. When knowledge is embedded, two teams are coupled:
flowchart LR
subgraph before["Before: Embedded (2–3 days)"]
direction LR
KT1["Knowledge<br/>Team"] --> PR["PR against<br/>MCP repo"] --> Review["MCP team<br/>reviews"] --> CI["CI build<br/>& test"] --> Deploy["Deploy<br/>MCP"] --> Live1["Agents get<br/>updated knowledge"]
end
subgraph after["After: Externalized (minutes)"]
direction LR
KT2["Knowledge<br/>Team"] --> Publish["Publish to<br/>knowledge service"] --> Index["Index<br/>updates"] --> Live2["Next connection<br/>gets new knowledge"]
end
style before fill:#9d4444,color:#fff
style after fill:#2d6a4f,color:#fff
The MCP server's version doesn't change for knowledge updates. The MCP team focuses on protocol concerns — tool schemas, transport, auth. The knowledge team focuses on domain accuracy — agent definitions, workflow patterns, best practices. Each team ships on its own cadence.
| Metric | Embedded | Externalized |
|---|---|---|
| MCP deploys for knowledge changes | Every update | Zero |
| Teams blocked on each other | Always | Never |
| Knowledge freshness guarantee | As of last deploy | As of last publish |
| Rollback granularity | Full MCP rollback | Per-document revert |
The rollback granularity matters. With embedded knowledge, rolling back a bad agent definition means rolling back the entire MCP server — including any unrelated changes in that release. With externalized knowledge, you revert one document in the search index.
Personalization at Connection Time
If the search query includes context about who's connecting, the returned guidance is personalized without the MCP server containing personalization logic.
const results = await knowledgeService.query({
query: "mcp server instructions",
filters: {
audience: clientInfo.clientType,
user_role: clientInfo.userRole, // "devops", "pm", "developer"
environment: clientInfo.environment, // "production", "staging", "dev"
},
});
flowchart TD
Dev["DevOps Engineer<br/>(CLI agent)"] --> MCP["MCP Server<br/>(passes context, no logic)"]
PM["Product Manager<br/>(Claude Desktop)"] --> MCP
NewHire["New Developer<br/>(Cursor)"] --> MCP
MCP -->|"role: devops<br/>env: production"| KS["Knowledge Service<br/>(Vertex AI Search)"]
MCP -->|"role: pm<br/>env: demo"| KS
MCP -->|"role: developer<br/>env: dev"| KS
KS --> R1["Infra guidance:<br/>deployment, monitoring"]
KS --> R2["Workflow guidance:<br/>templates, best practices"]
KS --> R3["Onboarding guidance:<br/>setup, tutorials"]
style MCP fill:#5a5a8a,color:#fff
style KS fill:#2d6a4f,color:#fff
A DevOps engineer connecting through a CLI agent gets infrastructure-focused guidance — deployment patterns, environment management, monitoring hooks. A product manager connecting through Claude Desktop gets workflow-design guidance — templates, qualifying questions, best practices for non-technical builders.
The MCP server doesn't contain role-based logic. It passes context to the knowledge service and returns what comes back. This is the discovery protocol principle applied to knowledge: route the query, return the result, don't reason about it.
This also means personalization improves without MCP changes. As the knowledge team publishes more role-specific content with better audience tags, the knowledge service returns more relevant results. The MCP server is unaware of the improvement — it's still just querying and returning.
When Embedded Knowledge Still Wins
The externalization pattern isn't universally correct. Embedded knowledge wins in specific scenarios:
Latency-critical paths. In our setup, a knowledge service query typically added 50–200ms — the exact figure varies with network conditions, index size, and query volume, but the overhead is real. If your MCP tools are called in tight loops where latency compounds, embedded knowledge avoids the network roundtrip.
Small, stable domains. If your knowledge base is 10 items and changes quarterly, the deployment coupling isn't a meaningful cost. The operational overhead of a knowledge service isn't worth it.
Offline or air-gapped environments. If the MCP server can't reach external services, embedded is the only option.
Structural knowledge that's tightly coupled to tool schemas. Input validation rules, type definitions, and schema constraints are part of the protocol surface — they should stay in the MCP server. The line: if changing the knowledge requires changing a tool schema, it belongs in MCP. If it can change independently, externalize it.
The three-tier fallback pattern from MCP: From Hardcoded to Live Data applies here:
flowchart TD
Q["Query knowledge service"] -->|Success| Fresh["Return fresh results"]
Q -->|Failure / timeout| Cache["Check local cache<br/>(TTL + stale-while-revalidate)"]
Cache -->|Cache hit| Stale["Return cached results<br/>(may be stale)"]
Cache -->|Cache miss| Default["Return minimal<br/>embedded defaults"]
style Fresh fill:#2d6a4f,color:#fff
style Stale fill:#5a5a8a,color:#fff
style Default fill:#9d4444,color:#fff
The trade-off is explicit: you accept a network dependency in exchange for eliminating a deployment dependency. For enterprise teams where deployment coordination is the bottleneck — where a three-word terminology fix waits behind a sprint boundary — this trade-off is favorable.
Note the distinction: this isn't "add a search index." It's "externalize to a knowledge service that handles the full lifecycle — ingestion, structuring, indexing, ranking, grounding, and retrieval." The MCP server doesn't need search logic, ranking algorithms, or result composition. The knowledge service handles all of that. MCP asks a question and gets back a structured answer.
The Arc
This series describes an evolution in how I think about MCP:
-
The MCP Mental Model — MCP as three primitives: tools, resources, prompts. Foundation.
-
MCP: The Semantic Data Layer — MCP as an enterprise abstraction layer with five responsibilities. Useful but too generous — invited accumulation.
-
MCP in Practice: Knowledge-First Builder — Embedded knowledge in MCP. Solved the knowledge gap, created deployment coupling.
-
MCP as Discovery Protocol — MCP as a thin discovery shim. Narrower than "layer." Describe and delegate.
-
This post — Externalize the knowledge. Apply the discovery principle to knowledge itself. MCP queries, it doesn't contain.
Each step was correct given what we knew. Each step also revealed the next constraint. That's how architecture works — you don't get the right answer first, you iterate toward it by feeling the pain of the wrong one.
What to Do Next
- Catalog your MCP server's embedded knowledge. List every piece of domain content that lives in your codebase — agent definitions, workflow templates, terminology glossaries, best practice guides.
- Identify what changes more than monthly. That's your migration candidate. Stable schemas and type definitions can stay embedded. Volatile domain knowledge should externalize.
- Set up a knowledge service. Vertex AI Search, Elasticsearch with a retrieval layer, or a managed knowledge platform. The specific technology matters less than the decoupling — but prefer a service that handles ingestion, ranking, and structured retrieval, not just text search.
- Implement dynamic server instructions. Replace static instruction strings with a knowledge service query at init time. Start with one section — you don't need to externalize everything at once.
- Measure the right metric. Track: "How often do we deploy the MCP server solely because knowledge changed?" Drive that number to zero.
- Apply the three-tier fallback. Knowledge service → cached results → embedded defaults. Never let a service outage degrade the MCP experience below a baseline.
This concludes the "MCP Architecture" series. The prescription: MCP as a discovery protocol (thin, descriptive, delegates reasoning) with externalized knowledge (decoupled lifecycle, dynamic instructions, personalization without MCP logic).
Related reading: MCP: From Hardcoded to Live Data for the caching patterns referenced here, and MCP Isn't the Problem for broader MCP ecosystem analysis.