{"id":28747767,"url":"https://github.com/merge-api/merge-mcp","last_synced_at":"2026-01-31T05:08:47.663Z","repository":{"id":286748726,"uuid":"962402300","full_name":"merge-api/merge-mcp","owner":"merge-api","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-30T23:34:40.000Z","size":132,"stargazers_count":11,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-16T17:11:38.407Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/merge-api.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-08T05:31:30.000Z","updated_at":"2025-06-11T18:42:39.000Z","dependencies_parsed_at":"2025-04-08T07:28:38.743Z","dependency_job_id":"fca8c24b-b4ab-465f-abde-ad069ece114b","html_url":"https://github.com/merge-api/merge-mcp","commit_stats":null,"previous_names":["merge-api/merge-mcp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/merge-api/merge-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merge-api%2Fmerge-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merge-api%2Fmerge-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merge-api%2Fmerge-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merge-api%2Fmerge-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/merge-api","download_url":"https://codeload.github.com/merge-api/merge-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merge-api%2Fmerge-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28929866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T04:05:25.756Z","status":"ssl_error","status_checked_at":"2026-01-31T04:02:35.005Z","response_time":128,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-16T17:01:48.723Z","updated_at":"2026-01-31T05:08:47.656Z","avatar_url":"https://github.com/merge-api.png","language":"Python","funding_links":[],"categories":["Python","HR and Recruiting"],"sub_categories":[],"readme":"# Merge MCP Server\n\nThis MCP (Model Context Protocol) server provides integration between Merge API and any LLM provider supporting the MCP protocol (e.g., Claude for Desktop), allowing you to interact with your Merge data using natural language.\n\n## ✨ Features\n- Query Merge API entities using natural language\n- Get information about your Merge data models and their fields\n- Create and update entities through conversational interfaces\n- Support for multiple Merge API categories (HRIS, ATS, etc.)\n\n## 📦 Installation\n\n### Prerequisites\n\n- A Merge API key and account token\n- Python 3.10 or higher\n- [uv](https://github.com/astral-sh/uv)\n\nInstall `uv` with standalone installer:\n```bash\n# On macOS and Linux.\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# On Windows.\npowershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n```\n\nor through pip:\n```bash\n# With pip.\npip install uv\n\n# With pipx.\npipx install uv\n```\n\n## 🔌 MCP setup\nHere is an example config file which you can use to set up Merge MCP.\n\n```json\n{\n    \"mcpServers\": {\n        \"merge-mcp-server\": {\n            \"command\": \"uvx\",\n            \"args\": [\"merge-mcp\"],\n            \"env\": {\n                \"MERGE_API_KEY\": \"your_api_key\",\n                \"MERGE_ACCOUNT_TOKEN\": \"your_account_token\"\n            }\n        }\n    }\n}\n```\nNote: If \"uvx\" command does not work, try absolute path (i.e. /Users/username/.local/bin/uvx)\n\n### Example Claude Desktop configuration\n\n1. Ensure you have `uvx` installed\n\n2. Download [Claude Desktop](https://claude.ai/download) from the official website\n\n3. Once downloaded, open the app and follow the instructions to set up your account\n\n4. Navigate to **Settings → Developer → Edit Config**. This should open a file named `claude_desktop_config.json` in a text editor.\n\n5. Copy the MCP server setup JSON above and paste it into the text editor\n\n6. 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.\n\n7. Save the config file\n\n8. Restart Claude Desktop to see your tools. The tools may take a minute to appear\n\n### Example Python client configuration\n\n1. Setting up your environment\n\n```bash\n# Create project directory\nmkdir mcp-client\ncd mcp-client\n\n# Create virtual environment\npython -m venv .venv\n\n# Activate virtual environment\n# On Windows:\n.venv\\Scripts\\activate\n# On Unix or MacOS:\nsource .venv/bin/activate\n\n# Install required packages\npip install mcp uv anthropic python-dotenv\n\n# Create our main file\ntouch client.py\n```\n\n2. Setting up your API keys\n\n```bash\n# Add your ANTHROPIC_API_KEY and MERGE_API_KEY to .env\necho \"ANTHROPIC_API_KEY=\u003cyour Anthropic key here\u003e\" \u003e\u003e .env\necho \"MERGE_API_KEY=\u003cyour Merge key here\u003e\" \u003e\u003e .env\n\n# Add .env file to .gitignore\necho \".env\" \u003e\u003e .gitignore\n```\n\n3. Create a `client.py` file and add the following code\n\n```python\nimport os\nimport asyncio\nfrom typing import Optional\nfrom contextlib import AsyncExitStack\n\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\nfrom anthropic import Anthropic\nfrom dotenv import load_dotenv\n\nload_dotenv()  # load environment variables from .env\n\nclass MCPClient:\n    def __init__(self):\n        # Initialize session and client objects\n        self.session: Optional[ClientSession] = None\n        self.exit_stack = AsyncExitStack()\n        self.anthropic = Anthropic()\n\n    # Methods will go here\n```\n\n4. Add a `connect_to_server` function to the MCPClient class\n\n```python\nasync def connect_to_server(self, linked_account_token: str):\n    \"\"\"Connect to an MCP server\n    Args:\n        linked_account_token: The token for the associated Linked Account\n    \"\"\"\n\n    server_params = StdioServerParameters(\n        command=\"uvx\",\n        args=[\"merge-mcp\"],\n        env={\n            \"MERGE_API_KEY\": os.getenv(\"MERGE_API_KEY\"),\n            \"MERGE_ACCOUNT_TOKEN\": linked_account_token\n        }\n    )\n\n    stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))\n    self.stdio, self.write = stdio_transport\n    self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))\n\n    await self.session.initialize()\n\n    # List available tools\n    response = await self.session.list_tools()\n    tools = response.tools\n    print(\"\\nConnected to server with tools:\", [tool.name for tool in tools])\n```\n\n5. Add a `process_query` function to the MCPClient class\n\n```python\nasync def process_query(self, query: str) -\u003e str:\n    \"\"\"Process a query using Claude and available tools\"\"\"\n    messages = [\n        {\n            \"role\": \"user\",\n            \"content\": query\n        }\n    ]\n\n    response = await self.session.list_tools()\n    available_tools = [{\n        \"name\": tool.name,\n        \"description\": tool.description,\n        \"input_schema\": tool.inputSchema\n    } for tool in response.tools]\n\n    # Initial Claude API call\n    response = self.anthropic.messages.create(\n        model=\"claude-3-5-sonnet-20241022\",\n        max_tokens=1000,\n        messages=messages,\n        tools=available_tools\n    )\n\n    # Process response and handle tool calls\n    final_text = []\n    assistant_message_content = []\n    for content in response.content:\n        if content.type == 'text':\n            final_text.append(content.text)\n            assistant_message_content.append(content)\n\n        elif content.type == 'tool_use':\n            tool_name = content.name\n            tool_args = content.input\n\n            # Get confirmation for tool call execution\n            confirmation = input(f\"Do you want to call tool '{tool_name}' with arguments {tool_args}? (y/n): \").strip().lower()\n            if confirmation.startswith('y'):\n                result = await self.session.call_tool(tool_name, tool_args)\n                final_text.append(f\"[Calling tool {tool_name} with args {tool_args}]\")\n                assistant_message_content.append(content)\n                messages.append({\n                    \"role\": \"assistant\",\n                    \"content\": assistant_message_content\n                })\n                messages.append({\n                    \"role\": \"user\",\n                    \"content\": [\n                        {\n                            \"type\": \"tool_result\",\n                            \"tool_use_id\": content.id,\n                            \"content\": result.content\n                        }\n                    ]\n                })\n\n                # Get next response from Claude\n                response = self.anthropic.messages.create(\n                    model=\"claude-3-5-sonnet-20241022\",\n                    max_tokens=1000,\n                    messages=messages,\n                    tools=available_tools\n                )\n                final_text.append(response.content[0].text)\n\n            else:\n                final_text.append(f\"[Skipped calling tool {tool_name} with args {tool_args}]\")\n\n    return \"\\n\".join(final_text)\n```\n\n6. Add a `chat_loop` function to the MCPClient class\n\n```python\n    async def chat_loop(self):\n        \"\"\"Run an interactive chat loop\"\"\"\n        print(\"\\nMCP Client Started!\")\n        print(\"Type your queries or 'quit' to exit.\")\n\n        while True:\n            try:\n                query = input(\"\\nQuery: \").strip()\n\n            if query.lower() == 'quit':\n                break\n\n            response = await self.process_query(query)\n            print(\"\\n\" + response)\n\n        except Exception as e:\n            print(f\"\\nError: {str(e)}\")\n```\n\n7. Add a `cleanup` function to the MCPClient class\n\n```python\n    async def cleanup(self):\n        \"\"\"Clean up resources\"\"\"\n        await self.exit_stack.aclose()\n```\n\n8. Add a `main` function to the `client.py` file as the main entry point\n\n```python\nasync def main():\n    client = MCPClient()\n    try:\n        await client.connect_to_server(\"\u003cyour Linked Account token here\u003e\")\n        await client.chat_loop()\n    finally:\n        await client.cleanup()\n\nif __name__ == \"__main__\":\n    import sys\n    asyncio.run(main())\n```\n\n9. Running the client\n\n```bash\npython client.py\n```\n\n## 🔍 Scopes\n\nScopes 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.**\n\nWhen starting the server, you can specify which scopes to enable. This is done by passing the `--scopes` flag with a list of scopes.\n\n```json\n{\n    \"mcpServers\": {\n        \"merge-mcp-server\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"merge-mcp\",\n                \"--scopes\",\n                \"ats.Job:read\",\n                \"ats.Candidate\",\n                \"ats.Application:write\"\n            ],\n            \"env\": {\n                \"MERGE_API_KEY\": \"your_api_key\",\n                \"MERGE_ACCOUNT_TOKEN\": \"your_account_token\"\n            }\n        }\n    }\n}\n```\n\n### Scope Format\n\nScopes in the Merge MCP server follow a specific format based on the Merge API category and common model names. Each scope is formatted as:\n\n```\n\u003ccategory\u003e.\u003cCommonModel\u003e:\u003cpermission\u003e\n```\n\nWhere:\n- `\u003ccategory\u003e` is the Merge API category (e.g., `hris`, `ats`, `accounting`)\n- `\u003cCommonModel\u003e` is the name of the Merge Common Model (e.g., `Employee`, `Candidate`, `Account`)\n- `\u003cpermission\u003e` is either `read` or `write` (optional - if not specified, all permissions are granted)\n\nExamples of valid scopes:\n- `hris.Employee:read` - Allows reading employee data from HRIS category\n- `ats.Candidate:write` - Allows creating or updating candidate data in ATS category\n- `accounting.Account` - Allows all operations on account data in Accounting category\n\nYou can combine multiple scopes to grant different permissions.\n\n### Important Notes on Scope Availability\n\nThe available scopes depend on your Merge API account configuration and the models the Linked Account has access to. Scopes must be cross-referenced with enabled scopes on your Linked Account:\n\n- **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.\n\n- **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.\n\n- **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.\n\nScopes typically correspond to different models or entity types in the Merge API, and they control both read and write access to these entities.\n\n## 🚀 Available Tools\n\nThe 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.\n\nTools are dynamically generated based on your Merge API schema and include operations for:\n\n- Retrieving entity details\n- Listing entities\n- Creating new entities\n- Updating existing entities\n- And more, based on your specific Merge API configuration\n\n**Note:** Download tools are not currently supported. This is a known limitation and will be addressed in a future release.\n\n## 🔑 Environment Variables\n\nThe following environment variables are used by the Merge MCP server:\n\n- `MERGE_API_KEY`: Your Merge API key\n- `MERGE_ACCOUNT_TOKEN`: Your Merge Linked Account token\n- `MERGE_TENANT` (Optional): The Merge API tenant. Valid values are `US`, `EU`, and `APAC`. Defaults to `US`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerge-api%2Fmerge-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmerge-api%2Fmerge-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerge-api%2Fmerge-mcp/lists"}