LangChain
Give a LangChain agent in Python or TypeScript the Nextmail tools, through LangChain's MCP adapter and one sign-in.
Install
pip install "langchain[mcp]>=1.4" langchain-anthropicIn Python the MCP adapter is part of langchain from 1.4, behind the mcp extra. It is in beta there, and importing it shows a LangChainBetaWarning once. In TypeScript it is @langchain/mcp-adapters 2.0, which needs Node.js 20.10 or later. The examples run Claude through the Anthropic package, and any model LangChain supports works the same way.
Sign in once
npx @nextmail/cli loginChoose the browser sign-in. On the approval page, pick the workspace, the access, the addresses LangChain may reach and how long the approval lasts. Read only is the default, and it leaves out tools such as sendEmail, replyToEmail and createDraft, so choose Read and send when the agent should draft or reply. The sign-in is saved on this machine and listed in Account → Command line, where you can narrow it or remove it. A sign-in the bridge has not used for 7 days, or one whose approval has ended, has to sign in again.
On a server or over SSH, add --no-browser: open the link on any device, approve, and paste the code it shows back into the terminal. A browser sign-in needs a person once, so a CI job cannot make one.
The MCP server takes an approved sign-in and never an API key. A key sent to it gets 401, with a WWW-Authenticate header that points at the OAuth metadata. Code that holds a key calls the REST API or an SDK instead.
Run an agent
import asyncio from langchain.agents import create_agentfrom langchain.mcp import MCPAdapter SERVERS = { "mcpServers": { "nextmail": {"command": "npx", "args": ["-y", "@nextmail/cli", "mcp", "serve"]} }}TOOLS = {"whoAmI", "listThreads", "getThread"} async def main(): async with MCPAdapter(SERVERS) as adapter: tools = [tool for tool in await adapter.list_tools() if tool.name in TOOLS] agent = create_agent("claude-sonnet-5", tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Summarise my unread mail."}]} ) print(result["messages"][-1].text) asyncio.run(main())The framework starts the local bridge, npx -y @nextmail/cli mcp serve, as a stdio MCP server. The bridge sends each call to the MCP server with your saved sign-in and renews the access token when it runs out, so the agent never handles a token. To use another profile of the command line, add --profile <name> to the arguments. The bridge runs on Node.js 20.12 or later.
- In Python, a config with one server keeps the tool names as the server lists them, such as
listThreads. - In TypeScript,
MCPAdapterputs the server name in front of every tool, solistThreadsarrives asnextmail_listThreads. - In Python the tools hold the client, so the agent stays usable after the
async withblock ends. In TypeScript, close the adapter when the agent is done.
What the agent can do
The server registers up to 600 tools, and LangChain sees the ones both your role and the approval allow. A tool the approval leaves out is never listed, so the model never tries it. Every tool loaded is sent to the model on each turn, so give the agent the ones its job needs. The example starts from whoAmI, listThreads and getThread.
| Tool | What it does | Access it needs |
|---|---|---|
| whoAmI | The role you hold in the active workspace, and every permission it grants | None |
| listThreads, getThread | List conversations with filters, and read the latest message of one | Read only |
| waitForEmail | Wait up to 60 seconds for a message that matches, such as a sign-in code | Read only |
| createDraft | Save a draft without sending it, as a reply when given a thread | Read and send |
| replyToEmail, sendEmail | Reply inside a thread, or send a new email | Read and send |
| modifyLabels | Add or remove labels on conversations | Read and send |
The MCP server never asks before a tool acts, so a tool that sends or deletes does it at once. A few tools make a change the REST API guards with a verification code, such as creating a rule or removing a domain, and the bridge cannot ask for a code. Run npx @nextmail/cli verify in a terminal with the same profile, or choose Allow changes for 60 minutes on this sign-in in Account → Command line, and those tools run for the next 60 minutes.
Without the command line
The adapter can also reach the remote server, https://api.nextmail.sh/mcp, and sign in with OAuth itself. In Python, LangChain hands authentication to FastMCP: give it a client with auth="oauth", and it registers with the server, opens your browser to approve, and exchanges the code. It keeps the tokens in memory unless you give it token storage, so each run asks again.
from fastmcp.client import Clientfrom langchain.mcp import MCPAdapter async def load_tools(): async with MCPAdapter(Client("https://api.nextmail.sh/mcp", auth="oauth")) as adapter: return await adapter.list_tools()In TypeScript, set url on the server entry and pass an authProvider, an OAuthClientProvider from the MCP TypeScript SDK. The SDK runs the registration and the code exchange, and your app stores the tokens and handles the redirect back.
With an API key instead
The MCP server never takes a key, so a person has to sign in once before an agent can use it. Python code that holds a key, such as a job that runs with nobody at hand, can give the agent the tools of langchain-nextmail instead. They are built on the Python SDK, they reach what the key’s scopes allow, and with LangChain’s approval step they ask a person before they send.
Google Workspace