
A local MCP server on one developer's laptop is easy. A remote MCP server that a whole team uses from Claude, ChatGPT or Cursor, where every person signs in with their own account and the AI can only do what that person may do, takes a bit more design. This guide walks through the architecture we used for our own server in October 2026: a Python MCP server that connects Claude to our internal task manager.
New to MCP? Start with what an MCP server is.
The architecture
- Your app (here a Django application) is the OAuth authorization server: people sign in with the accounts they already have and approve access.
- The MCP server is the resource server: it receives the user's access token from the AI app, checks it with the authorization server, and only then runs the tool.
- The AI app (Claude, ChatGPT, Cursor, Claude Code) discovers how to sign in from the MCP server's metadata, sends the user through the login, and calls tools with the token.
One design choice matters a lot: the MCP server never forwards the user's token to the app's internal API. It calls the app with its own service credentials and says which user it is acting for, and the app enforces that user's permissions. A leaked AI token therefore cannot be replayed against the rest of your system.
Step 1: design the tools before writing code
Tools are what the AI sees, so design them like a product:
- Few and specific: our server has 11 tools, such as
search_tasks,summarize_tasks,create_tasksandadd_comment. - Mark read-only tools as read-only, so AI apps can treat them differently from tools that change data.
- Return compact results (IDs, titles, status, links), not whole database rows.
- Let write tools work in batches with limits (we allow 1 to 50 tasks per call) so the AI does not loop hundreds of times.
- Write clear descriptions: the AI chooses tools by reading them.
Step 2: the MCP server in Python
We used the official Python SDK (the mcp package) with the Streamable HTTP transport, served at /mcp. A simplified version of the setup looks like this:
from mcp.server.mcpserver import MCPServer
from mcp.server.auth.settings import AuthSettings
from mcp.types import ToolAnnotations
READ_ONLY = ToolAnnotations(read_only_hint=True, open_world_hint=False)
mcp = MCPServer(
name="tasks",
token_verifier=IntrospectionVerifier(), # checks tokens with your OAuth server
auth=AuthSettings(
issuer_url="https://app.example.com",
resource_server_url="https://mcp.example.com/mcp",
required_scopes=["tasks:read"],
),
)
@mcp.tool(annotations=READ_ONLY)
async def search_tasks(query: str, overdue: bool = False) -> list[dict]:
"""Search tasks the signed-in person can see."""
user = current_user() # from the verified token
return await app_api.search(user, query=query, overdue=overdue)
Check the current SDK documentation for exact names, because the SDKs evolve quickly.
Step 3: OAuth sign-in and scopes
- Authorization server: our app uses django-oauth-toolkit to issue tokens and to answer token introspection requests. Any OAuth 2 server you trust can do this job.
- Discovery: the MCP server publishes protected-resource metadata. When an AI app calls
/mcpwithout a token, it gets a 401 response that points to that metadata, and from there to your login page. - Scopes: we use
tasks:readfor everything that only reads andtasks:writefor anything that changes data. A person can connect with read-only access. - Token checks: the MCP server verifies every token with the authorization server, including who it was issued for, before any tool runs.
Step 4: deploy it
- Package the server in a small Docker container with a health check endpoint.
- Put it behind HTTPS (nginx, Caddy or a cloud load balancer) and turn off response buffering for the MCP path, or streamed responses will stall.
- The public URL the server announces must match exactly the URL people enter in their AI app.
- Make sure firewalls, CDNs or bot protection do not block the AI provider's servers from reaching your login and metadata endpoints; this is the most common reason a "Connect" button fails.
Step 5: test before you connect real users
We test the MCP server end to end against an in-memory fake of the app, and the app side has its own tests for OAuth, permissions and the internal API. Before launch, check by hand: a call without a token returns 401 with the metadata link, a read-only token is refused for write tools, and a user cannot see another user's private data.
Step 6: connect it in Claude, ChatGPT or Cursor
- Claude (team): an owner adds a custom connector with the MCP server URL; each person clicks Connect and signs in.
- Claude Code:
claude mcp add --transport http tasks https://mcp.example.com/mcp, then sign in. - Other apps: most MCP-compatible apps have a place to add a remote MCP server URL; check their documentation.
Common mistakes
- Giving the AI one powerful "run any query" tool instead of a few safe, specific tools.
- Using one shared API key for everyone, so you cannot tell who did what.
- Forwarding user tokens straight to internal systems.
- No logs: you should always be able to answer "who asked the AI to change this, and when?"
How UnlockLive can help
We build and run remote MCP servers for CRMs, databases, ERPs and internal APIs, with per-user sign-in, scopes, logging and tests. See our MCP server development service, the MCP security checklist and the case study of our own server. Tell us which system you want to connect.
Frequently asked questions
What is a remote MCP server?
An MCP server that runs on a server and is reached over HTTPS, so a whole team can use it from AI apps such as Claude, ChatGPT or Cursor, usually with each person signing in with their own account.
How does OAuth work with an MCP server?
Your app or identity provider acts as the OAuth authorization server, and the MCP server acts as the resource server. The AI app discovers the login from the MCP server's metadata, the user signs in and approves access, and the AI app then sends the user's token with every tool call.
Should I build an MCP server in Python or TypeScript?
Both have official SDKs. Pick the language your team and your existing back end already use; for Python back ends such as Django or FastAPI, a Python MCP server is a natural fit.
How do I add a remote MCP server to Claude Code?
Run claude mcp add --transport http with a name and the server URL, then sign in when prompted.
Why does Connect fail for my MCP server?
The most common causes are a public URL that does not exactly match the one entered in the AI app, a CDN or firewall blocking the AI provider from the login or metadata endpoints, and proxy buffering on the MCP path.
How we can help
- MCP Server Development ServicesCustom Model Context Protocol (MCP) servers that expose your APIs, databases, and internal tools to Claude, Cursor, ChatGPT, and any MCP-compatible AI.
- Python & FastAPI DevelopmentHigh-performance Python backends and FastAPI microservices for SaaS, AI inference APIs, ETL pipelines, and event-driven systems.
- Cybersecurity & AI Security ServicesPenetration testing, SOC monitoring, SOC 2 / ISO 27001 / PCI DSS / HIPAA readiness, and emerging-area work in LLM red teaming and AI agent security.
Talk to an engineer about your project
Tell us what you are building. We reply within one business day with a candid view on scope, approach and effort.
Book a free strategy callWritten by the UnlockLive IT engineering team. UnlockLive IT Limited works with clients through its Toronto headquarters and delivers engineering from its Dhaka delivery centre. About us