The Gap Between Chatting and Acting
Most AI integrations stop at chat: the AI can talk about your data, summarize it, answer questions about it. But it cannot do anything. It cannot create a record in your CRM, send an email, update a status, or trigger a workflow. It is a consultant, not an operator.
MCP (Model Context Protocol) closes this gap. It gives AI agents a standardized way to connect to real tools — your CRM, your database, your email, your project management — and take actions through them. The agent does not just suggest 'you should update the lead status'; it updates the lead status directly.
The challenge is safety. An AI agent with unrestricted access to your tools is a liability. The architecture needs permissions, audit trails, and human checkpoints. Here is how I build it.
MCP Architecture: Agent, Server, Tool
The architecture has three layers. The agent layer: the LLM that reasons about what to do. The MCP server layer: a bridge that exposes your tools as typed, callable functions. The tool layer: the actual APIs and systems the agent interacts with.
I build MCP servers in TypeScript that wrap each tool system. Each tool has a strict schema: what parameters it accepts, what it returns, and what side effects it has. The agent sees the tool definitions and their descriptions, and decides when to call them.
The MCP server handles authentication, rate limiting, and error translation. The agent never touches raw API keys or deals with HTTP errors — it calls a clean, typed function and gets a structured response back.
MCP tool definition for CRM update
// Tool definition exposed to the agent via MCP
const tools = [
{
name: "update_lead_status",
description: "Update the status of a lead in the CRM. Requires lead_id and new_status.",
inputSchema: {
type: "object",
properties: {
lead_id: { type: "string", description: "The CRM lead ID" },
new_status: {
type: "string",
enum: ["new", "contacted", "qualified", "won", "lost"]
},
notes: { type: "string", description: "Optional notes about the status change" }
},
required: ["lead_id", "new_status"]
}
}
];
// Server-side handler with permission checks and audit logging
async function handleUpdateLeadStatus(input: UpdateLeadInput) {
// Permission check: can this agent update lead statuses?
await assertPermission("crm:leads:write");
// Audit log: record the action before executing
await auditLog({
action: "update_lead_status",
actor: "ai-agent",
input,
timestamp: new Date(),
});
// Execute the tool
const result = await crmApi.updateLead(input.lead_id, {
status: input.new_status,
notes: input.notes,
updated_by: "ai-agent",
});
return { success: true, lead_id: input.lead_id, new_status: result.status };
}Permission Scoping: Least Privilege by Default
Every MCP tool is scoped with the principle of least privilege. An agent that handles support tickets gets read access to the knowledge base and write access to the ticket system — but not to billing, not to user accounts, and not to deployment pipelines.
I implement permissions as a capability system: each agent has a list of allowed actions, and every tool call checks the capability before executing. The permission model is declarative and version-controlled, so changes to agent capabilities go through the same review process as code changes.
Read-only tools are low-risk and can run without human approval. Write tools that modify data require a higher permission level. Destructive tools (delete, archive, bulk update) always require human-in-the-loop confirmation.
The permission model is the difference between an AI agent that helps your team and one that accidentally deletes your customer database.
Audit Logging: Every Action Recorded
Every tool call — successful or failed — is logged with the full context: which agent made the call, what parameters it used, what the tool returned, and how long it took. This audit log is immutable and searchable.
The log serves three purposes. First, debugging: when something goes wrong, you can trace exactly what the agent did and why. Second, compliance: regulated industries need to show who (or what) modified data and when. Third, improvement: by reviewing the audit log, you can identify patterns where the agent is making suboptimal decisions and refine its instructions.
I also build a dashboard that shows agent activity in real time: what tools are being called, how often, and with what success rates. This gives the operations team visibility into what the AI is doing without having to read raw logs.