Multiple MCP Servers

Understanding how to connect to multiple MCP servers with Kern

Kern's MCP integration also supports handling connections to multiple servers, specifying server parameters and using your own MCP servers

There are two approaches to this:

  1. Using multiple MCPTools instances
  2. Using a single MultiMCPTools instance

Using multiple MCPTools instances

1import asyncio
2import os
3
4from kern.agent import Agent
5from kern.tools.mcp import MCPTools
6
7
8async def run_agent(message: str) -> None:
9 """Run the Airbnb and Google Maps agent with the given message."""
10
11 env = {
12 **os.environ,
13 "GOOGLE_MAPS_API_KEY": os.getenv("GOOGLE_MAPS_API_KEY"),
14 }
15
16 # Initialize and connect to multiple MCP servers
17 airbnb_tools = MCPTools(command="npx -y @openbnb/mcp-server-airbnb --ignore-robots-txt")
18 google_maps_tools = MCPTools(command="npx -y @modelcontextprotocol/server-google-maps", env=env)
19 await airbnb_tools.connect()
20 await google_maps_tools.connect()
21
22 try:
23 agent = Agent(
24 tools=[airbnb_tools, google_maps_tools],
25 markdown=True,
26 )
27
28 await agent.aprint_response(message, stream=True)
29 finally:
30 await airbnb_tools.close()
31 await google_maps_tools.close()
32
33
34# Example usage
35if __name__ == "__main__":
36 # Pull request example
37 asyncio.run(
38 run_agent(
39 "What listings are available in Cape Town for 2 people for 3 nights from 1 to 4 August 2025?"
40 )
41 )

Using a single MultiMCPTools instance

1import asyncio
2import os
3
4from kern.agent import Agent
5from kern.tools.mcp import MultiMCPTools
6
7
8async def run_agent(message: str) -> None:
9 """Run the Airbnb and Google Maps agent with the given message."""
10
11 env = {
12 **os.environ,
13 "GOOGLE_MAPS_API_KEY": os.getenv("GOOGLE_MAPS_API_KEY"),
14 }
15
16 # Initialize and connect to multiple MCP servers
17 mcp_tools = MultiMCPTools(
18 commands=[
19 "npx -y @openbnb/mcp-server-airbnb --ignore-robots-txt",
20 "npx -y @modelcontextprotocol/server-google-maps",
21 ],
22 env=env,
23 )
24 await mcp_tools.connect()
25
26 try:
27 agent = Agent(
28 tools=[mcp_tools],
29 markdown=True,
30 )
31
32 await agent.aprint_response(message, stream=True)
33 finally:
34 # Always close the connection when done
35 await mcp_tools.close()
36
37
38# Example usage
39if __name__ == "__main__":
40 # Pull request example
41 asyncio.run(
42 run_agent(
43 "What listings are available in Cape Town for 2 people for 3 nights from 1 to 4 August 2025?"
44 )
45 )

Allowing partial failures with MultiMCPTools

If you are connecting to multiple MCP servers using the MultiMCPTools class, an error will be raised by default if connection to any MCP server fails.

If you want to avoid raising in that case, you can set the allow_partial_failures parameter to True.

This is useful if you are connecting to MCP servers that are not always available, and don't want to exit your program if one of the servers is not available.

1import asyncio
2from os import getenv
3
4from kern.agent import Agent
5from kern.tools.mcp import MultiMCPTools
6
7
8async def run_agent(message: str) -> None:
9 # Initialize the MCP tools
10 mcp_tools = MultiMCPTools(
11 [
12 "npx -y @openbnb/mcp-server-airbnb --ignore-robots-txt",
13 "npx -y @modelcontextprotocol/server-brave-search",
14 ],
15 env={
16 "BRAVE_API_KEY": getenv("BRAVE_API_KEY"),
17 },
18 timeout_seconds=30,
19 # Set the allow_partial_failure to True to allow for partial failure connecting to the MCP servers
20 allow_partial_failure=True,
21 )
22
23 # Connect to the MCP servers
24 await mcp_tools.connect()
25
26 # Use the MCP tools with an Agent
27 agent = Agent(
28 tools=[mcp_tools],
29 markdown=True,
30 )
31 await agent.aprint_response(message)
32
33 # Close the MCP connection
34 await mcp_tools.close()
35
36
37# Example usage
38if __name__ == "__main__":
39 asyncio.run(run_agent("What listings are available in Barcelona tonight?"))
40 asyncio.run(run_agent("What's the fastest way to get to Barcelona from London?"))

Avoiding tool name collisions

When using multiple MCP servers, you may encounter tool name collisions. This often happens when the same tool is available in multiple of the servers you are using.

To avoid this, you can use the tool_name_prefix parameter. This will add the given prefix to all tool names coming from the MCPTools instance.

1import asyncio
2
3from kern.agent import Agent
4from kern.tools.mcp import MCPTools
5
6
7async def run_agent():
8 # Development environment tools
9 dev_tools = MCPTools(
10 transport="streamable-http",
11 url="https://kern.ndx.rocks/mcp",
12 # By providing this tool_name_prefix, all the tool names will be prefixed with "dev_"
13 tool_name_prefix="dev",
14 )
15 await dev_tools.connect()
16
17 agent = Agent(tools=[dev_tools])
18 await agent.aprint_response("Which tools do you have access to? List them all.")
19
20 await dev_tools.close()
21
22
23if __name__ == "__main__":
24 asyncio.run(run_agent())