Model Context Protocol (MCP)

Learn about Merge MCP and how to implement our MCP server

Overview

Merge offers an MCP server that integrates the Merge API with any LLM provider supporting the MCP protocol. This enables your AI agent to access hundreds of tools via a single MCP server.

The Merge MCP SDK can be found here.

Installation

The following items are prerequisites in order to implement Merge MCP:

  • A Merge API key and linked account token
  • Python 3.10 or higher
  • uv

MCP setup

Here is an example config file which you can use to set up Merge MCP

MCP Setup
1{
2 "mcpServers": {
3 "merge-mcp": {
4 "command": "uvx",
5 "args": ["merge-mcp"],
6 "env": {
7 "MERGE_API_KEY": "your_api_key",
8 "MERGE_ACCOUNT_TOKEN": "your_account_token"
9 }
10 }
11 }
12}

Note: If the uvx command does not work, try absolute path (i.e. /Users/username/.local/bin/uvx). You can find the absolute path by running which uvx through your terminal.

Example Claude Desktop setup

1

Ensure you have uvx installed

2

Download Claude Desktop from the official website

3

Once downloaded, open the app and follow the instructions to set up your account

4

Navigate to SettingsDeveloperEdit Config. This should open a file named claude_desktop_config.json in a text editor.

5

Copy the MCP server setup JSON above and paste it into the text editor

6

Replace your_api_key and your_account_token with your actual Merge API key and Linked Account token. You will also need to replace uvx with the absolute path to the command in the config file (i.e. /Users/username/.local/bin/uvx). You can find the absolute path by running which uvx through your terminal.

7

Save the config file

8

Restart Claude Desktop to see your tools. The tools may take a minute to appear.

Example Python client setup

1

Setting up your project

You will first need to set up a Python virtual environment and install the required dependencies.

You will also create a client.py file that will contain the code for your MCP client and be the entry point for your application.

Setup
$# Create project directory
>mkdir mcp-client
>cd mcp-client
>
># Create virtual environment
>python -m venv .venv
>
># Activate virtual environment
># On Windows:
>.venv\Scripts\activate
># On Unix or MacOS:
>source .venv/bin/activate
>
># Install required packages
>pip install mcp uv anthropic python-dotenv
>
># Create our main file
>touch client.py
2

Setting up your API keys

You will need to set up your API keys in the environment variables.

The ANTHROPIC_API_KEY will be used to authenticate your requests to the Anthropic API.

The MERGE_API_KEY will be needed to connect to the Merge MCP server.

Env
$# Add your ANTHROPIC_API_KEY and MERGE_API_KEY to .env
>echo "ANTHROPIC_API_KEY=<your Anthropic key here>" >> .env
>echo "MERGE_API_KEY=<your Merge key here>" >> .env
>
># Add .env file to .gitignore
>echo ".env" >> .gitignore
3

Set up an MCPClient class

The MCPClient class will handle the connection to the Merge MCP server and facilitate the calls made to the Anthropic API.

client.py
1import os
2import asyncio
3from typing import Optional
4from contextlib import AsyncExitStack
5
6from mcp import ClientSession, StdioServerParameters
7from mcp.client.stdio import stdio_client
8
9from anthropic import Anthropic
10from dotenv import load_dotenv
11
12load_dotenv() # load environment variables from .env
13
14class MCPClient:
15 def __init__(self):
16 # Initialize session and client objects
17 self.session: Optional[ClientSession] = None
18 self.exit_stack = AsyncExitStack()
19 self.anthropic = Anthropic()
20
21 # Methods will go here
4

Add a connect_to_server function to the MCPClient class

The connect_to_server function will be used to connect to the MCP server.

This function takes in a linked_account_token parameter which is the token associated with the Linked Account you want to connect to.

MCPClient.connect_to_server
1async def connect_to_server(self, linked_account_token: str):
2 """Connect to an MCP server
3 Args:
4 linked_account_token: The token for the associated Linked Account
5 """
6
7 server_params = StdioServerParameters(
8 command="uvx",
9 args=["merge-mcp"],
10 env={
11 "MERGE_API_KEY": os.getenv("MERGE_API_KEY"),
12 "MERGE_ACCOUNT_TOKEN": linked_account_token
13 }
14 )
15
16 stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
17 self.stdio, self.write = stdio_transport
18 self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
19
20 await self.session.initialize()
21
22 # List available tools
23 response = await self.session.list_tools()
24 tools = response.tools
25 print("\nConnected to server with tools:", [tool.name for tool in tools])
5

Add a process_query function to the MCPClient class

The process_query function will be used to process queries from the MCP server.

This function takes in a query parameter which is a user initiated prompt that will be sent to the Anthropic API.

The Anthropic API will return a response that will contain text or tool use. In the case of tool use, the Anthropic call will have returned a name and input corresponding to the tool that should be used and the arguments that should be passed to the tool.

This information is then surfaced to the user to prompt them for confirmation on executing the tool.

Once the user confirms, the tool name and arguments will be passed to the call_tool function of our existing session with the Merge MCP server.

The results of the tool call will be sent to another Anthropic API call to generate a response.

MCPClient.process_query
1async def process_query(self, query: str) -> str:
2 """Process a query using Claude and available tools"""
3 messages = [
4 {
5 "role": "user",
6 "content": query
7 }
8 ]
9
10 response = await self.session.list_tools()
11 available_tools = [{
12 "name": tool.name,
13 "description": tool.description,
14 "input_schema": tool.inputSchema
15 } for tool in response.tools]
16
17 # Initial Claude API call
18 response = self.anthropic.messages.create(
19 model="claude-3-5-sonnet-20241022",
20 max_tokens=1000,
21 messages=messages,
22 tools=available_tools
23 )
24
25 # Process response and handle tool calls
26 final_text = []
27 assistant_message_content = []
28 for content in response.content:
29 if content.type == 'text':
30 final_text.append(content.text)
31 assistant_message_content.append(content)
32
33 elif content.type == 'tool_use':
34 tool_name = content.name
35 tool_args = content.input
36
37 # Get confirmation for tool call execution
38 confirmation = input(f"Do you want to call tool '{tool_name}' with arguments {tool_args}? (y/n): ").strip().lower()
39 if confirmation.startswith('y'):
40 result = await self.session.call_tool(tool_name, tool_args)
41 final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
42 assistant_message_content.append(content)
43 messages.append({
44 "role": "assistant",
45 "content": assistant_message_content
46 })
47 messages.append({
48 "role": "user",
49 "content": [
50 {
51 "type": "tool_result",
52 "tool_use_id": content.id,
53 "content": result.content
54 }
55 ]
56 })
57
58 # Get next response from Claude
59 response = self.anthropic.messages.create(
60 model="claude-3-5-sonnet-20241022",
61 max_tokens=1000,
62 messages=messages,
63 tools=available_tools
64 )
65 final_text.append(response.content[0].text)
66
67 else:
68 final_text.append(f"[Skipped calling tool {tool_name} with args {tool_args}]")
69
70 return "\n".join(final_text)
6

Add a chat_loop function to the MCPClient class

The chat_loop function will be used to handle chat interactions with the MCP server.

This function handles the main chat loop where the user is prompted to input a query. That query is then sent to the process_query function.

MCPClient.chat_loop
1async def chat_loop(self):
2 """Run an interactive chat loop"""
3 print("\nMCP Client Started!")
4 print("Type your queries or 'quit' to exit.")
5
6 def get_query():
7 return input("\nQuery: ").strip()
8
9 query = get_query()
10
11 while query.lower() != 'quit':
12 try:
13 response = await self.process_query(query)
14 print("\n" + response)
15
16 query = get_query()
17
18 except Exception as e:
19 print(f"\nError: {str(e)}")
7

Add a cleanup function to the MCPClient class

The cleanup function will be used to clean up resources when the MCPClient is no longer needed.

This function will be called when the process is terminated.

MCPClient.cleanup
1async def cleanup(self):
2 """Clean up resources"""
3 await self.exit_stack.aclose()
8

Add a main function to the client.py file as the main entry point

The main function will be used to initiate a MCPClient instance, connect to the MCP server, and start the chat loop.

main
1async def main():
2 client = MCPClient()
3 try:
4 await client.connect_to_server("<your Linked Account token here>")
5 await client.chat_loop()
6 finally:
7 await client.cleanup()
8
9if __name__ == "__main__":
10 import sys
11 asyncio.run(main())
9

Running the client

You can run the client by executing the client.py file.

If the set up was completed successfully, you should be prompted to input a query.

Run the client
$python client.py

Scopes

Scopes determine which tools are enabled on the MCP server and are used to control access to different parts of the Merge API. If no scopes are specified, all available scopes will be enabled.

When starting the server, you can specify which scopes to enable. This is done by passing the --scopes flag with a list of scopes.

Scopes
1{
2 "mcpServers": {
3 "merge-mcp": {
4 "command": "uvx",
5 "args": [
6 "merge-mcp",
7 "--scopes",
8 "ats.Job:read",
9 "ats.Candidate",
10 "ats.Application:write"
11 ],
12 "env": {
13 "MERGE_API_KEY": "your_api_key",
14 "MERGE_ACCOUNT_TOKEN": "your_account_token"
15 }
16 }
17 }
18}

Scope format

Scopes in the Merge MCP server follow a specific format based on the Merge API category and Common Model names. Each scope is formatted as:

<category>.<common_model_name>:<permission>

Where:

  • <category> - The category of the Common Model (e.g. ats, hris, crm, etc.)
  • <common_model_name> - The name of the Common Model (e.g. Candidate, Application, etc.)
  • <permission> - The permission level of the Common Model (e.g. read or write)

Examples of valid scopes:

  • ats.Candidate:read - Read access to Candidate objects
  • hris.Employee:write - Write access to Employee objects
  • crm.Contact - Read + Write access to Contact objects

You can combine multiple scopes to grant different permissions.

Important note on scopes availability

Scopes specified with the MCP server will be validated against enabled scopes on the Linked Account. The server will only enable tools for valid authorized scopes.

The following situations can occur:

  • Category Mismatch: If you specify a scope for a category that doesn’t match your Linked Account (e.g., using ats.Job with an HRIS Linked Account), no tools for that scope will be returned.
  • Permission Mismatch: If you request a permission that isn’t enabled for your Linked Account (e.g., using hris.Employee:write when only read access is enabled), tools requiring that permission won’t be returned.
  • Validation: The server will automatically validate your requested scopes against what’s available in your Linked Account and will only enable tools for valid, authorized scopes.

Scopes typically correspond to different models or entity types in the Merge API, and they control both read and write access to these entities.

Available tools

The Merge MCP server provides access to various Merge API endpoints as tools. The available tools depend on your Merge API category (HRIS, ATS, etc.) and the scopes you have enabled.

Tools are dynamically generated based on your Merge API schema and include operations for:

  • Retrieving entity details
  • Listing entities
  • Creating new entities
  • Updating existing entities
  • And more, based on your specific Merge API configuration

Note: Download tools are not currently supported. This is a known limitation and will be addressed in a future release.

Environment variables

The following environment variables are used by the Merge MCP server:

  • MERGE_API_KEY: Your Merge API key
  • MERGE_ACCOUNT_TOKEN: Your Merge Linked Account token
  • MERGE_TENANT (Optional): The Merge API tenant associated with your Linked Account. Valid values are US, EU, and APAC. Defaults to US.