Skip to main content
MCP has three actors:
  • Host: The application the user interacts with (Claude Desktop, Cursor, etc.)
  • Client: The agent that connects to the MCP Server and supplies authentication or authorization
  • Server: The MCP endpoint that exposes tools
The Client often acts as the resource owner for tools like Gmail, Notion, Slack, etc. MCP Servers expose tools in multiple authorization modes. Start with the simplest pattern and move toward multi-user setups as your deployment grows.

Single-tenant

Single-tenant architecture is a model where each customer gets a dedicated application instance and infrastructure, providing full resource isolation.

Host-authenticated tool

The MCP Server already holds the credentials for the resource.
Example: You ship a Gmail MCP Server that already contains the OAuth token required to read emails.
Flow
  • The Server stores the Gmail token internally
  • The Client connects
  • The Client does not send any Gmail credentials
  • The Server accesses Gmail directly
When this works
  • Single-user setups
  • No multi-tenant requirements
  • Local or private workflows
  • Simple “agent runs with my credentials” tooling
from agentor.tools import GmailAPI

tool = GmailAPI(
    scopes=["readonly"],
    token="your-token-here",
)
result = tool.list_emails()
print(result)
If someone connects to the MCP Server, they gain full access to the Gmail account because the Server holds the token.
This becomes a security liability for any shared or public deployment.
Celesto Cloud adds authentication on top of your MCP Server, but we still recommend this pattern only for single-user setups.
Never share the Celesto API key and never expose this Server publicly.

Multi-tenant

Multi-tenant architecture is a model where multiple customers share the same application and infrastructure while keeping their data logically isolated from one another.

Authorization inside the MCP Server

In this pattern, each Client supplies its own credential—OAuth tokens, API keys, workspace keys, etc.—on every request. The Server reads the token from the request headers and performs the operation under that user’s identity.
from agentor.tools import SlackTool
from agentor.mcp import LiteMCP, get_headers

mcp = LiteMCP()

@mcp.tool(description="Send a message to a Slack channel")
def send_slack_message(channel: str, message: str):
    headers = get_headers()
    token = headers["authorization"]

    slack = SlackTool(token=token)
    slack.send_message(channel=channel, text=message)

    return "Message sent successfully"

mcp.run()
This pattern scales cleanly across users:
  • Each Client injects its own token.
  • The Server performs the action on behalf of that user.
  • No user ever accesses data that is not theirs.
  • The Server remains simple, predictable, and safe to expose.