English
Connect a client
Every MCP client needs the same two things: the address of the endpoint, <PUBLIC_BASE_URL>/api/v1/mcp, and an API key sent in the Authorization: Bearer header. This page gives the configuration of the most common clients and a raw curl session to check an instance by hand.
Create the key
- Open Settings › API keys and click New key.
- Give it a name that says which client holds it, for example "Claude Desktop, laptop".
- Choose the scopes. For each domain, pick nothing, read only, or read and write:
- Read-only exploration:
workflows:read,executions:read,mailboxes:read,connections:read,tables:read. The assistant can describe, search, debug and propose, but writes nothing. - Building workflows: the same, with
workflows:writeinstead ofworkflows:read. The assistant can also create drafts, update them and publish. Addtables:writeonly if the member is an administrator and wants the assistant to create tables.
- Read-only exploration:
- Copy the key when it is shown: it starts with
mk_and is displayed only once. Mankomail keeps only a hash of it.
A key never grants more than its member has; see Scopes and tools and API scopes for the grammar. Revoke the key from the same page when the client no longer needs it: the refusal is immediate.
Claude Code
Register the server with the http transport and the key as a header:
sh
claude mcp add --transport http <name> <PUBLIC_BASE_URL>/api/v1/mcp \
--header "Authorization: Bearer mk_…"Replace <name> with the name you want to see in Claude Code, for example your instance's name. The same server can be declared for a project in its .mcp.json:
json
{
"mcpServers": {
"<name>": {
"type": "http",
"url": "<PUBLIC_BASE_URL>/api/v1/mcp",
"headers": {
"Authorization": "Bearer mk_…"
}
}
}
}Avoid committing a file that holds the key: prefer the command, which stores the server in your user configuration, or keep the project file out of version control.
Cursor
Cursor reads .cursor/mcp.json in the project (or ~/.cursor/mcp.json for every project). A remote server is declared with url and headers:
json
{
"mcpServers": {
"<name>": {
"url": "<PUBLIC_BASE_URL>/api/v1/mcp",
"headers": {
"Authorization": "Bearer ${env:MANKO_MCP_KEY}"
}
}
}
}Cursor documents ${env:NAME} interpolation in headers, so the key can live in the MANKO_MCP_KEY environment variable rather than in the file. A literal "Bearer mk_…" value works too.
Claude Desktop
Claude Desktop adds remote servers through Settings › Connectors › Add custom connector, a flow where the server drives authentication, typically with OAuth. Mankomail authenticates with a fixed header instead, so the verified route is the mcp-remote bridge: a small local program that Claude Desktop launches as a stdio server and that forwards every message to the remote endpoint with your header.
Edit claude_desktop_config.json (Settings › Developer › Edit Config) and add:
json
{
"mcpServers": {
"<name>": {
"command": "npx",
"args": [
"mcp-remote",
"<PUBLIC_BASE_URL>/api/v1/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer mk_…"
}
}
}
}Two details come from the mcp-remote documentation: the header value is passed through an environment variable because some clients mangle spaces inside args, hence Authorization:${AUTH_HEADER} without a space; and mcp-remote tries Streamable HTTP first by default (--transport http-first), which is what Mankomail speaks. Restart Claude Desktop after saving; the tools appear under the server's name.
npx requires Node.js on the machine. If a client you use connects to remote Streamable HTTP servers with custom headers directly, prefer that over the bridge.
By hand, with curl
A raw session shows what a client does. Every message is a JSON-RPC request in a POST, with the key, Content-Type: application/json and an Accept header that lists both application/json and text/event-stream, as the transport requires.
1. Initialize. The server answers with its name, the instance's brand as title, its version and its capabilities:
sh
curl -s <PUBLIC_BASE_URL>/api/v1/mcp \
-H "Authorization: Bearer mk_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "curl", "version": "0" }
}
}'Because the transport is stateless, the response carries no Mcp-Session-Id, and the notifications/initialized message that clients normally send next is not required between two curl calls: nothing is kept on the server anyway.
2. List the tools. Send the negotiated version in MCP-Protocol-Version:
sh
curl -s <PUBLIC_BASE_URL>/api/v1/mcp \
-H "Authorization: Bearer mk_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'The result lists only the tools the key's scopes allow. Each one has a name (workflow_list), a title with the internal name (workflow.list), a description that ends with the required scope, an inputSchema, an outputSchema when the output is an object, and annotations (readOnlyHint, destructiveHint: false, openWorldHint: false).
3. Call a tool. Arguments follow the tool's inputSchema:
sh
curl -s <PUBLIC_BASE_URL>/api/v1/mcp \
-H "Authorization: Bearer mk_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{
"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": { "name": "workflow_list", "arguments": { "includeArchived": false } }
}'A successful result carries the output twice: as JSON text in content[0].text and as an object in structuredContent. A refusal comes back as a result with isError: true and { "error": { "code", "message" } }, see Scopes and tools.
Without a valid key, the endpoint answers 401 with the API's error format before any MCP message is read. GET or DELETE on the endpoint answer 405.
Related pages
- MCP server: what the server is and its trust model
- Tools reference
- Authentication and API scopes