Ten MCP servers stay reviewable on one open-source platform
A client can hold connections to many MCP servers at once, so the tenth server is a naming and policy problem before it is a technical one. Kortix is the open-source AI Operating System. Your agents, skills, company memory, and connectors live in one git repo you own.
Three servers, one project rule, one grant
Three connectors, one project-wide rule and one grant, all in the manifest a reviewer reads first.
connectors
connectors:
- slug: github
provider: composio
app: github
- slug: linear
provider: mcp
url: https://mcp.linear.app/mcp
sensitive: true
policies:
- match: "get_*"
action: always_run
- slug: docs-search
provider: mcp
url: https://mcp.example.com/mcp
policies:
- match: "get_*"
action: always_run
- match: "*"
action: require_approvalrules and grant
policy:
default_mode: risk
policies:
- match: "*.delete*"
action: block
agents:
release-bot:
connectors: [github, linear, docs-search]Many servers, one client, no hard ceiling
A client can hold connections to many MCP servers at once. The protocol sets no ceiling on the number. Merge's account of the specification says an agent "can maintain simultaneous connections to multiple MCP servers, with each connection running in its own independent MCP session" (Merge). The same passage says who routes a call. The client manages the sessions, and the client decides which server handles each tool call.
Sessions keep the servers apart at the transport. One server's handshake does not disturb another's tool list. What grows with the tenth server is what the model sees and what a reviewer has to hold. The tool list gets longer, the names start to collide, and another place appears where a rule can live.
Kortix is the open-source AI Operating System. Your agents, skills, company memory, and connectors live in one git repo you own. Connectors are entries in that repo, which is where the rules around a call sit when a project runs more than one server.
One slug per server, a distinct name for every action
Two servers can each publish a tool called create_issue. Nothing in MCP keeps tool names unique across servers. The model picks from the list the client hands it, so a flat list of forty names with two create_issue entries leaves the choice open. A reviewer has the same problem. Which rule covers the call?
Kortix settles it at the connector. The first field is "a slug, unique in the project ([a-z0-9][a-z0-9_-], up to 128 characters)". It starts with a letter or a digit. Then come letters, digits, underscores and dashes, with no uppercase and no leading dash. A connector also carries a display name, a provider, its actions as <slug>.<action> and its policies, and it lives in kortix.yaml under connectors: (Connectors). Kortix proposes the slug from what it can read: the document's title, or else the URL's domain. An MCP server added from its URL is named after its domain, so https://mcp.linear.app/mcp arrives as linear.
The action name carries the namespace. linear.create_issue and atlassian.create_issue are different tools in the transcript, in a policy pattern and in an approval. One rule cannot catch the other by accident. At ten servers that distinction is what keeps the set readable, since the tool list is not the only place it has to exist.
Where a rule goes when it covers more than one server
A rule that must hold across ten servers is the first thing that strains. Take one: nothing may delete. Written into ten connector entries, it is ten chances to miss one. The eleventh server ships with whatever its author remembered.
Kortix evaluates every call against four levels. The first match wins:
- Project-wide
policies:inkortix.yaml, with fully qualified patterns such asgmail.send_emailor*.delete*. A connector rule cannot override them. - The connector's own
policies:, with patterns relative to that connector, such assend_*. sensitive: trueon the connector, after which every action no rule matched needs approval, reads included.policy.default_mode:allow_all(the default) runs every remaining action, whileriskruns reads and holds writes and destructive actions for approval.
Put the cross-cutting rule at the first level and local habits at the second. *.delete* at the top blocks a delete on every server in the project. On docs-search, get_* can run while * asks. A generous Allow on one connector cannot reopen what the project blocked.
The last two levels spare a team a rule per tool, and they are not the same gate. sensitive: true sends a connector's remaining actions to approval, reads included, which fits a server whose tools nobody has read yet. policy.default_mode: risk is the looser of the two: it runs reads and holds writes and destructive actions for approval, across every connector with no rule of its own.
Order matters here. A connector rule is read before sensitive, so a get_* Allow on a sensitive connector still lets reads through. Check that when a connector looks stricter than it behaves.
Not every connector is an MCP server. Kortix connects 3,000+ apps in a click, plus MCP, OpenAPI, Postman, GraphQL and raw HTTP. One project-wide pattern covers all of them. *.delete* reaches a managed Gmail connector and a remote MCP server with the same line.
Actions are always_run, require_approval, block, shown as Allow, Ask, Block. Approval holds the call; the agent's turn pauses and resumes.
One grant list per agent, and the cost of all
Reach is decided again at the agent. agents.<name>.connectors lists the slugs an agent may call, or all; an omitted value means none (Give an agent a connector). Ten connectors in a project and three on the release agent take one line in that file.
all is the value that changes a review. It covers every connector in the project, including the ones someone adds next month. A reviewer who approved all approved a list that keeps growing. A slug list has to be edited before reach changes, and the edit arrives as a diff beside the agent that gains the server.
What an agent can reach through a connector is the tool list, the grant and the account behind the call. At ten servers the grant is the cheapest of the three to read. It is also the one most often left at all.
One log at ten servers
Every action is recorded. Reading, exporting and streaming the audit log depend on the plan. One log across ten servers answers a question once: which agent called which action, as which account, at what time. Ten logs answer it in ten places. The specification only asks clients to log tool usage, so a set of servers can leave the record thinner than a team assumes.
The plan point deserves a plain line. Recording happens on every plan. A team that expects to read, export or stream the record during an incident should confirm the plan includes it first. A log nobody can open answers nothing.
Adding server number four
The first servers arrive one at a time, usually for one team and one job. The fourth is where a set either stays reviewable or turns into ten entries nobody has read together. Five steps, each one a field.
- Name it. Give the connector a slug that names the system it reaches, and check it is unique in the project. Field:
slug, underconnectors:. - Place the rule. A pattern that must hold across servers goes in the project-wide
policies:. A habit for this server alone goes in the connector's ownpolicies:. Field: the project'spolicies:or the connector'spolicies:. - Grant it. Add the slug to the agents that need it and leave
allalone. Field:agents.<name>.connectors. - Attach the account. Choose a shared account for work a team or a schedule owns. Use a private one only for a person's own sessions. Connector credentials are brokered server-side and never enter the machine, so this step decides identity. Field: the account's owner type, and whose permissions a call carries.
- Check the record. Send one read and find it in the log before the server carries real work. Field: the audit view of the session.
Questions about running more than one server
Tool-name collisions, policy levels and how many servers one client can hold.
How many MCP servers can I connect?
There is no fixed number in the protocol. A client can hold simultaneous connections to several MCP servers, each in its own session, so the practical limit comes from the client's session handling and from the team's naming and policy work. In Kortix each server is one connector entry with a unique slug.
Can two MCP servers expose the same tool name?
Yes. Tool names come from each server and nothing in the protocol makes them unique across servers. Kortix addresses an action as <slug>.<action>, so two servers can both publish create_issue and a call still names the system it means.
Where do MCP policies live?
In Kortix they live in kortix.yaml at two levels. Project-wide policies: take fully qualified patterns such as gmail.send_email, and each connector's own policies: take patterns relative to it such as send_*. The project-wide rule is evaluated first and a connector rule cannot override it.
Do I need a connector per MCP server?
Yes, in Kortix. One connector entry holds one server's URL, its slug and its policies, which is what gives that server's actions a distinct name and its own rules. Several accounts can sit on the same connector.
Name each server, write the rules, then let the agents work.
Connectors, policies and grants in one repo you own.