Generative AIBuilding with foundation modelsBuilding agentsAgent communication

Model Context Protocol

11 minutes read

Modern AI applications, such as agents, often rely on multiple tools—APIs, databases, search engines, etc.—to perform various tasks. However, these diverse tools work independently. Writing code to interact with them can create code duplication, version conflicts, unclear permissions, and custom logic whenever you try to scale or change tools. Let's see how MCP can help.

What is MCP?

The Model Context Protocol is a standard for connecting AI-powered applications with external systems. It enables them to interact with data stores, development environments, productivity tools, knowledge bases, and other systems. The image below shows some systems that MCP allows your AI agents and assistants to communicate with:

Various applications and tools that connect via MCP.

From the image, you can see that MCP enables capabilities such as:

  • Translating natural language into SQL statements (read, and optionally write) to access organizational data and return results from databases.

  • Automating business tasks like PayPal transactions (orders, invoices, subscriptions, disputes, analytics) through natural language.

  • Accessing Slack data (messages, files, channels) to retrieve insights and automate responses and workflows.

  • Querying and modifying project management tools like Jira (issues, transitions, projects) through natural language to retrieve data and manage workflows.

  • Using developer tools like Claude Code to understand, modify, and generate code and perform other actions directly in your development environment.

With MCP, you can build more capable and scalable AI applications.

Core concepts

The MCP standard follows the client-server architecture. This means there will be an MCP host, like an AI assistant or agent, that connects to an MCP server like Notion MCP to retrieve content or perform actions. An MCP host uses one or more MCP clients to connect to multiple MCP servers for different purposes. The client maintains communication between the host and the specific server.

MCP consists of two layers:

  • Data layer

  • Transport layer

The data layer defines:

  • The communication protocol (JSON-RPC 2.0) — message format, structure, and meaning.

  • Lifecycle management — covers how clients and servers establish connections, negotiate capabilities, maintain state, and terminate the connection.

  • Server capabilities — the tools (actions), resources (data/context), and prompts (if applicable) that a server can offer.

  • Client capabilities — what the client (host) can do: asking for user input (known as elicitation), prompting LLMs (known as sampling), logging, etc.

  • Additional features — utilities like notifications (for real-time updates), progress tracking, error handling, and any other protocol extensions.

The transport layer manages how clients and servers communicate with each other. It handles connection establishment, authentication, and message framing between clients and servers. Here are the transport mechanisms that MCP supports:

  • Stdio (local server) — useful for direct communications between clients and servers on the same machine.

  • Streamable HTTP — enables communication with remote servers. You can also enable Server-Sent Events (SSE) for streaming capabilities. Local servers can use this mode too.

When using streamable HTTP, you can authenticate via OAuth tokens, API keys, or custom headers.

Using MCP

Now that you understand MCP, how do you use it? First, you need a client. Many clients support the MCP protocol, and you can build custom clients to meet your specific needs. Claude Code, the coding assistant from Anthropic, is a good example. As an interactive coding agent, it requires access to various tools and resources in your development environment, such as gathering context or the ability to create pull requests automatically.

Next, you require an MCP server. Various providers maintain MCP servers for their products, and you can build a custom server if existing options don't meet your requirements. After selecting an appropriate server, you need to configure your client's connection to it.

MCP hosts let you specify MCP servers in their JSON configuration files. You typically find these files in your home folder, such as ~/.claude.json for Claude Code. Sometimes, you can store the config in a project directory for project-scoped MCP servers. Here's an example config for a Slack MCP server running locally:

"mcpServers": {
  "slack": {
    "type": "http",
    "url": "http://0.0.0.0:3000/mcp",
    "headers": {
      "Authentication": "Bearer b96aa1b5-08bb-4cdc-bcf2-cd11194084cc"
    }
  }

For a remote server, the config would look slightly different:

{
  "mcpServers": {
    "Notion": {
      "url": "https://mcp.notion.com/mcp"
    }
  }
}

In the next section, we will connect to a local MCP server for Claude Code to use.

Example with local MCP server

Running an MCP server locally offers many advantages. It provides low-latency access, complete control over your data, offline functionality, and simplified development and debugging. You can run MCP servers locally through either Docker or Node.js. Let's explore how to run an MCP server, specifically a Notion MCP server, using Docker.

Before configuring the server to run locally, you need to create an integration with your Notion workspace. This integration connects your workspace to external tools (like Slack, GitHub, and others) built using Notion's API, enabling them to perform actions and exchange data. After that, name your integration, choose a workspace, and keep the type as "internal".

After creation, navigate to the integration's settings and choose permissions, such as read, update, or insert content. For this example, we'll only grant read permissions for content, no permissions for comments, and no access to workspace user information. Make sure to copy the "Internal Integration Secret" (ntn_****), as we'll need it later. Click save to update your settings. Then, go to the Access tab and select which pages you want the integration to access.

Finally, add the server to your MCP client's JSON config. If you have Docker installed, add this config to your ~/.claude.json file (substitute ntn_**** with the integration secret you copied earlier):

{
  "mcpServers": {
    "notionApi": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "NOTION_TOKEN=ntn_****",
        "mcp/notion"
      ]
    }
  }
}

Now, your MCP client should connect to your Notion workspace:

$ claude mcp list
Checking MCP server health...

notionApi: docker run --rm -i -e NOTION_TOKEN=ntn_**** mcp/notion - ✓ Connected

It should also be able to retrieve data from the workspace:

Claude Code using Notion MCP server to retrieve information.

Be aware that large documents can quickly use up tokens, which can be costly.

10-word summary from retrieved document on Chain of Though Prompting via Notion MCP.

As shown above, Claude Code successfully retrieved content from the connected Notion workspace through the configured server. Next, let's configure a remote MCP server for another host, Cursor, to use.

Example with remote MCP server

For remote connections, the JSON config would look like that below (with SSE):

{
  "mcpServers": {
    "Notion": {
      "type": "sse",
      "url": "https://mcp.notion.com/sse"
    }
  }
}

A supported client, such as the Cursor code editor, handles authentication with the server when you first install it and complete the auth flow via the browser. After configuration, you can see the tools and resources that Cursor can use:

Tools and resources available to Cursor via Notion MCP.

Now, Cursor can access documents from your workspace as needed:

Cursor can access a specied document from the configured Notion workspace using MCP.

The configuration process may vary depending on your provider, but the setup generally follows this pattern. Check the documentation for each server you want to use for specific details.

Conclusion

MCP offers a straightforward, standardized approach for AI assistants to communicate with external systems. It minimizes redundant integration code, defines permissions and lifecycle clearly, and simplifies tool management and scalability. MCP divides the system into a protocol-style data layer (JSON-RPC, capabilities, lifecycle management) and flexible transport options (stdio, streamable HTTP/SSE). This enables agents and assistants to interact with organizational data, development environments, and productivity services without relying on fragile, ad hoc integrations or temporary fixes.

You can deploy servers locally for better control and reduced latency, or connect to remote providers for production workloads. The MCP approach reduces engineering complexity and enables secure, traceable automation. This makes your AI applications more capable, easier to maintain, and simpler to adapt as your toolkit expands.

How did you like the theory?
Report a typo