How to build an MCP-based agent knowledge layer for B2B sales
Enterprises deploy multiple AI sales agents that quote conflicting prices and outdated product facts. Here is how to implement a Model Context Protocol knowledge layer.
Enterprise revenue teams face a silent, compounding coordination failure as they scale their automation footprint. When you deploy five or ten AI agents in parallel to handle outbound email, update records, and run pre-call research, they operate in semantic silos. Each agent uses a different static system prompt, a separate retrieval-augmented generation index, and disconnected files.
Because these agents do not share a common repository of facts, they inevitably drift and make conflicting assertions to prospects. An email agent might pitch an outdated product feature, while a CRM updater writes meeting summaries containing deprecated pricing models. This operational fragmentation erodes prospect confidence and stalls deals before humans can intervene.
The Commercial Truth manifesto highlights that B2B teams have historically lacked the tooling to govern semantic positioning. Today, that gap is magnified by the proliferation of autonomous systems that act without unified guardrails. Resolving this requires shifting from static prompt templates to a dynamic, Model Context Protocol knowledge layer that serves as the central control plane for what your agents say.
Architectural assumptions
Our objective is to expose a single, governed semantic layer to any agent running in the revenue stack. Instead of loading raw documents into every vendor platform, we construct an MCP server that interfaces directly with a centralized Truth Graph. External agents, whether they are outbound sales tools or customer success handlers, query this server dynamically before composing any customer-facing copy.
This architecture relies on three primary components: defining a typed schema for positioning claims, translating natural language agent queries, and including a structured source-type pill in every response. The MCP server handles this translation dynamically against target claim nodes in the Truth Graph. Furthermore, every response contains a calibrated confidence interval to represent statistical uncertainty.
This setup prevents agents from generating unverified claims. We assume the client agent is MCP-compliant and can dynamically invoke tools exposed by our server. Enforcing access policies based on caller identity, the server returns a specific status if a claim lacks verification.
+------------------+ MCP Protocol +--------------------+
| External Sales | <======================> | Assay MCP Server |
| Agent (Client) | | (Tool Gateway) |
+------------------+ +---------+----------+
|
| Local Query
v
+--------------------+
| Local Truth Graph |
| (Claim Database) |
+--------------------+
The system maps upstream company positioning to downstream agent templates using a cascade tree to track every dependency. When a pricing node changes, the cascade tree flags all related claims for immediate re-verification. This ensures that no agent retrieves a stale pricing claim during its next execution cycle.
Step-by-step implementation
To build this layer, we initialize a Node.js project using TypeScript and the official Model Context Protocol SDK. This server will run on a secure endpoint within the corporate firewall and authenticate incoming client connections. We begin by defining the dependencies and project configuration in our package manifest.
Create a new directory for the server and save the following package configuration:
{
"name": "assay-gtm-mcp-server",
"version": "1.0.0",
"description": "MCP server exposing governed sales positioning and claims",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^0.6.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.3.3"
}
}
Next, configure the TypeScript environment to ensure clean compilation. Create a configuration file that targets modern JavaScript environments and enforces strict type safety. This configuration ensures our module imports resolve correctly:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
With the project structure set, we write the core server implementation in a source file. The server will define two primary tools that client agents can call. The first tool retrieves verified positioning claims, while the second verifies if a draft claim matches approved company messaging.
Create the server implementation file and define the tools:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
interface Claim {
id: string;
category: string;
statement: string;
sourceType: "FIRST_PARTY_VERIFIED" | "FIRST_PARTY_UNVERIFIED" | "THIRD_PARTY_PUBLIC";
confidenceInterval: [number, number];
lastAudited: string;
}
// In-memory mock representing the local Truth Graph
const truthGraph: Record<string, Claim> = {
"pricing-starter": {
id: "pricing-starter",
category: "pricing",
statement: "Starter plan costs $99 per user per month, billed annually.",
sourceType: "FIRST_PARTY_VERIFIED",
confidenceInterval: [1.0, 1.0],
lastAudited: "2026-05-15T08:00:00Z"
},
"pricing-growth": {
id: "pricing-growth",
category: "pricing",
statement: "Growth plan costs $199 per user per month, billed annually.",
sourceType: "FIRST_PARTY_VERIFIED",
confidenceInterval: [1.0, 1.0],
lastAudited: "2026-05-15T08:00:00Z"
},
"competitor-latency-comparison": {
id: "competitor-latency-comparison",
category: "competition",
statement: "Our search citation pipeline syncs 4x faster than standard CRM platforms.",
sourceType: "FIRST_PARTY_VERIFIED",
confidenceInterval: [0.88, 0.95],
lastAudited: "2026-05-20T12:30:00Z"
}
};
const server = new Server(
{
name: "assay-gtm-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_positioning_claims",
description: "Retrieve approved GTM positioning claims by category.",
inputSchema: {
type: "object",
properties: {
category: {
type: "string",
description: "The claim category (e.g., pricing, competition)",
},
},
required: ["category"],
},
},
{
name: "verify_draft_claim",
description: "Check if a draft message aligns with canonical facts.",
inputSchema: {
type: "object",
properties: {
claimId: {
type: "string",
description: "The identifier of the claim to verify against",
},
draftText: {
type: "string",
description: "The proposed text to validate",
},
},
required: ["claimId", "draftText"],
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_positioning_claims") {
const category = args?.category as string;
const claims = Object.values(truthGraph).filter(
(c) => c.category.toLowerCase() === category.toLowerCase()
);
return {
content: [
{
type: "text",
text: JSON.stringify({ claims }, null, 2),
},
],
};
}
if (name === "verify_draft_claim") {
const claimId = args?.claimId as string;
const draftText = args?.draftText as string;
const claim = truthGraph[claimId];
if (!claim) {
return {
content: [
{
type: "text",
text: JSON.stringify({ error: `Claim ID ${claimId} not found in Truth Graph.` }),
},
],
isError: true,
};
}
// In production, this runs a semantic similarity and confidence verification model.
const isAligned = draftText.toLowerCase().includes("billed annually") ||
draftText.toLowerCase().includes("faster");
return {
content: [
{
type: "text",
text: JSON.stringify({
aligned: isAligned,
claimId,
matchedClaim: claim.statement,
sourceType: claim.sourceType,
confidenceInterval: claim.confidenceInterval,
lastAudited: claim.lastAudited,
verdict: isAligned ? "APPROVED" : "REJECTED_OUT_OF_BOUNDS"
}, null, 2),
},
],
};
}
throw new Error(`Tool ${name} not found.`);
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Assay GTM MCP server running on Stdio");
}
main().catch((err) => {
console.error("Fatal error in MCP server:", err);
process.exit(1);
});
Save this code file inside your source directory and compile the server. The server reads stdin and writes stdout, using JSON-RPC messages to communicate with the client and routing queries automatically. The StdIO transport ensures compatibility with standard agent execution environments.
Verification and sample outputs
To verify the integration, we configure an external agent client to run our compiled server. We simulate a client calling our tools by writing a script that pipes JSON messages into the server process. This test validates that the server returns valid, schema-compliant JSON payloads containing our grounding evidence.
Below is an example of the raw JSON-RPC request a client agent sends to discover the available tools:
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
The server returns a structured response listing the tools. When a client agent wants to query the approved pricing details, it invokes the positioning tool. The request payload contains the target category and credentials:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_positioning_claims",
"arguments": {
"category": "pricing"
}
},
"id": 2
}
The server processes the request, queries the underlying Truth Graph, and returns the matching nodes. The response includes the verified statements, the source-type pill metadata, and the confidence interval. Here is the returned payload from the server:
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "{\n \"claims\": [\n {\n \"id\": \"pricing-starter\",\n \"category\": \"pricing\",\n \"statement\": \"Starter plan costs $99 per user per month, billed annually.\",\n \"sourceType\": \"FIRST_PARTY_VERIFIED\",\n \"confidenceInterval\": [1, 1],\n \"lastAudited\": \"2026-05-15T08:00:00Z\"\n },\n {\n \"id\": \"pricing-growth\",\n \"category\": \"pricing\",\n \"statement\": \"Growth plan costs $199 per user per month, billed annually.\",\n \"sourceType\": \"FIRST_PARTY_VERIFIED\",\n \"confidenceInterval\": [1, 1],\n \"lastAudited\": \"2026-05-15T08:00:00Z\"\n }\n ]\n}"
}
]
},
"id": 2
}
Now consider a scenario where an agent drafts a message quoting pricing but forgets the annual billing requirement. The agent invokes the validation tool, passing the draft text. The server compares the draft text against the canonical claim node:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "verify_draft_claim",
"arguments": {
"claimId": "pricing-starter",
"draftText": "The Starter plan is $99 per user month."
}
},
"id": 3
}
Because the draft text omits the critical billing constraint, the comparison model rejects the claim. The response contains a rejection verdict, highlighting that the draft text lacks alignment. Below is the server’s output payload:
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "{\n \"aligned\": false,\n \"claimId\": \"pricing-starter\",\n \"matchedClaim\": \"Starter plan costs $99 per user per month, billed annually.\",\n \"sourceType\": \"FIRST_PARTY_VERIFIED\",\n \"confidenceInterval\": [\n 1,\n 1\n ],\n \"lastAudited\": \"2026-05-15T08:00:00Z\",\n \"verdict\": \"REJECTED_OUT_OF_BOUNDS\"\n}"
}
]
},
"id": 3
}
The client agent reads this structured response and halts execution rather than sending a drift-prone message. The agent can then automatically adjust its draft or flag the exception for a human operator. The interaction history is logged in the audit timeline, providing clear accountability.
Production-readiness checklist
Before deploying this MCP server to your production environment, you must complete several configuration passes. These checks ensure that the server’s execution environment is locked down and that policy rules are properly mapped. This operational posture guarantees that any downstream agent behavior remains verifiable and secure.
Verify that your deployment satisfies the following requirements:
- Enforce read-only access. Ensure the credentials used by the MCP server only have read permissions on your primary database.
- Implement request rate limiting. Protect the server from internal loops by enforcing strict per-minute query ceilings on all connected client agents.
- Register every client agent. Maintain an active agent inventory, assigning unique identifiers and access tokens to each connected automation platform.
- Configure cascade alerts. Set up notifications to notify administrators when a parent positioning node changes in the Truth Graph.
- Enforce verification audits. Schedule a review pass every 30 days to re-verify every active claim against raw customer contracts.
The methodology Assay is developing for the Commercial Truth Index measures exactly this: whether the substance an agent emits is grounded, calibrated, coherent, and auditable. Implementing this MCP layer is the first step in ensuring your automated stack satisfies those criteria.
This essay is grounded in the agent-control-plane spec and the orchestration product canon.