AJERIS/Docs
  • Connect your MCP server

Connect your MCP server

How to plug a Model Context Protocol server into Ajeris - protocol, auth, and the connect flow.

Connect Your MCP Server to Ajeris

Ajeris speaks the Model Context Protocol. If you operate an MCP server - yours, your company's, or one from the community - you can point Ajeris at it and the agent will reach every tool the server exposes alongside Ajeris's built-in integrations on the next message you send.

This page is for the engineer or operator setting up the server. It covers what protocol Ajeris speaks, what it sends to your server, how to connect, and what to write in a tool's description so the agent will actually call it.


Prerequisites

To plug in a server you need:

  • A Streamable HTTP MCP server on a reachable HTTPS URL. Plain http://, localhost, private-network, and link-local addresses are rejected at validation time.
  • An auth credential (optional). Whatever your server expects in the Authorization header - typically Bearer <token> or a raw API key. Ajeris encrypts this at rest before storing it and sends it verbatim on every request.
  • At least one tool registered. Servers that return an empty tool list are rejected at preview time.

The server must implement MCP Streamable HTTP (the current transport as of the MCP 2025-06-18 spec). Stdio servers cannot be reached from Ajeris's cloud-hosted agent service.


Connect via the web UI

  1. Text manage to your Ajeris number. The agent replies with a personal management URL. The link is single-use and expires on a short TTL.
  2. Open the URL. Under Your own, tap Connect your own tools.
  3. Fill the form:
    • Label - any short name you'll recognize. Max 80 chars.
    • URL - the HTTPS endpoint, e.g. https://mcp.example.com/mcp. Non-HTTPS URLs are rejected.
    • Authorization header - paste the entire header value verbatim, including the scheme if any (Bearer sk-live-…, Token abc123, or just abc123 if your server doesn't care about schemes). Leave blank for unauthenticated servers.
  4. Tap Check connection. Ajeris opens a real MCP session against your URL - it doesn't just probe the TCP port. If the round-trip succeeds, you'll see the full list of tools the agent will see, verbatim from your server's listTools() response.
  5. Review the tools. Every tool name + description comes straight from your server. If anything is wrong (unexpected tool, typo in an arg schema, stale build), fix on the server side and re-validate.
  6. Tap Connect. Your next message to the agent sees the new tools.

The page also lists already-connected servers with Disable and Remove buttons. Disabling skips the server at agent-turn load time without deleting it.


What Ajeris sends to your server

Ajeris calls your server twice in two different moments:

  • At connect time, when you tap Check connection: a single Streamable HTTP session, an MCP initialize, then listTools, then close. A 10-second timeout applies.
  • On every agent turn after that, while the server is enabled: a fresh Streamable HTTP session is opened, the tool list is fetched, and any tool calls the model decides to make are routed through that session. The session closes at the end of the turn.

Both flows send only one header from your config: the Authorization value you pasted, sent as-is. The MCP SDK adds the standard Streamable HTTP protocol headers (Accept, Content-Type, Mcp-Session-Id on follow-up requests, etc.) - those are protocol concerns, not Ajeris identity.

Ajeris does not currently forward user identity, tenant ID, or conversation metadata to your server. If you need to know which Ajeris user is calling your tools, either:

  • Issue one long-lived token per user and infer identity from the token, or
  • Host a proxy in front of your MCP server that maps a shared token to per-user identity using some out-of-band signal (IP allowlist, header signed by the proxy, …).

No CORS required. Ajeris connects server-to-server, never from a browser. Your MCP server does not need to set Access-Control-Allow-Origin or any other CORS header. If a code generator or scaffolding tool added CORS middleware to your MCP route, you can remove it.


Security

  • HTTPS only. Non-HTTPS URLs are rejected at validation.
  • SSRF guard. Loopback (127.0.0.0/8, ::1, localhost), RFC1918 (10/8, 172.16/12, 192.168/16), link-local (169.254/16, fe80::/10), and ULA (fc00::/7) hostnames are rejected. Deploy on a public host.
  • Encryption at rest. Your Authorization header is encrypted before it touches storage and decrypted only inside the agent service, for the turn that needs it.
  • Tenant isolation. A connected server is visible only to the user who connected it. Server connections cannot leak across Ajeris accounts.
  • Single header per server. Ajeris sends only the Authorization value you configured. It does not add cookies, forwarded identity, user metadata, or tracing headers. If your server requires additional auth (X-Company-Tenant, mTLS, …), host a thin proxy that injects them after validating the Ajeris Authorization.
  • Connection lifetime. A fresh client is opened per agent turn and closed at the end. There is no long-lived connection to your server between turns.

Errors you might see

Every validation and save response carries a structured reason that maps to a specific UI message and a specific remedy. The reasons you'll actually see while integrating:

reasonWhen it happensFix
invalid_urlURL fails to parseCheck for typos; URL must include protocol + host
http_not_allowedURL is http:// or any non-HTTPS schemeTerminate TLS in front of your server (Cloudflare, Railway, AWS, etc.)
blocked_hostHostname matches the SSRF block-listDeploy your server to a public host, not a private network or your laptop
unreachableTCP/HTTP connect failed or timed out (>10s)Check DNS, firewall, server is running, and responds within 10 seconds
auth_failedYour server returned 401 or 403 during connectRotate/verify the token; check scheme (Bearer vs Token vs bare)
protocol_errorServer connected but didn't speak MCPVerify you're serving the Streamable HTTP transport, not SSE-only or custom JSON-RPC
no_toolslistTools() returned an empty arrayRegister at least one tool before saving
invalid_tokenManagement link expired between page-load and submitText manage again for a fresh link

Build a minimum working MCP server

This section is the canonical recipe. The pattern below is the shape Ajeris's validator expects and has been verified end-to-end (Bearer-auth gate + listTools() round-trip).

TypeScript

npm install @modelcontextprotocol/sdk@^1.0.0 express zod
// server.ts
import express from 'express';
import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
 
const SECRET = process.env.MCP_BEARER!; // set in your host's env
 
const app = express();
app.use(express.json());
 
// Bearer-auth gate. The user pastes "Bearer <SECRET>" into the
// Authorization header field on the Ajeris connect form.
app.use('/mcp', (req, res, next) => {
  if (req.header('authorization') !== `Bearer ${SECRET}`) {
    res.status(401).json({ error: 'unauthorized' });
    return;
  }
  next();
});
 
const transports = new Map<string, StreamableHTTPServerTransport>();
 
function buildServer(): McpServer {
  const mcp = new McpServer({ name: 'my-mcp-server', version: '1.0.0' });
 
  // One illustrative tool. Replace with whatever your service does;
  // register one tool per discrete operation you want the agent
  // to be able to perform.
  mcp.registerTool(
    'example_echo',
    {
      description: 'Echoes the input string back. Replace with a real tool.',
      inputSchema: { text: z.string() },
    },
    async ({ text }) => ({
      content: [{ type: 'text', text: `you said: ${text}` }],
    }),
  );
 
  return mcp;
}
 
// Single endpoint, three verbs - this is the Streamable HTTP shape.
const handle: express.RequestHandler = async (req, res) => {
  const sid = req.header('mcp-session-id');
  let t = sid ? transports.get(sid) : undefined;
 
  if (!t) {
    if (!isInitializeRequest(req.body)) { res.status(400).end(); return; }
    t = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
    });
    const mcp = buildServer();
    await mcp.connect(t);
    t.onclose = () => { if (t!.sessionId) transports.delete(t!.sessionId); };
    await t.handleRequest(req, res, req.body);
    if (t.sessionId) transports.set(t.sessionId, t);
    return;
  }
  await t.handleRequest(req, res, req.body);
};
 
app.post('/mcp', handle);
app.get('/mcp', async (req, res) => {
  const t = transports.get(req.header('mcp-session-id') ?? '');
  if (!t) { res.status(404).end(); return; }
  await t.handleRequest(req, res);
});
app.delete('/mcp', async (req, res) => {
  const t = transports.get(req.header('mcp-session-id') ?? '');
  if (!t) { res.status(404).end(); return; }
  await t.handleRequest(req, res);
});
 
app.listen(process.env.PORT ?? 8080);

That is a complete server. Deploy it behind HTTPS (any host that terminates TLS - Railway, Fly.io, Cloudflare Workers, AWS App Runner, Vercel Functions, Kubernetes, bare VM) and hand the user the public URL plus the Bearer <SECRET> value to paste into Ajeris.

Notes on the pattern:

  • Build a fresh McpServer per session. A given McpServer instance can only be connected to one transport at a time; reusing one across sessions breaks the second connection.
  • Keep the transports map keyed by mcp-session-id and clean up on onclose so abandoned sessions don't leak.
  • The first POST without a session ID must be the JSON-RPC initialize request - that's what isInitializeRequest checks.
  • The route handles three verbs on one path: POST for requests, GET for server-streamed responses, DELETE for session cleanup. Don't expose two paths (/sse + /message); that's the legacy HTTP+SSE transport, deprecated as of MCP 2025-03-26.

Python

pip install mcp uvicorn

The Python SDK ships an equivalent streamable_http transport. See github.com/modelcontextprotocol/python-sdk for the canonical example.

Tool naming

  • Match ^[a-zA-Z0-9_-]+$.
  • Keep names ≤ 64 characters.
  • Prefix with a short namespace that identifies your server so tools are recognizable in the agent's tool list and don't collide with built-in tools.

Writing tool descriptions (this is the most important part)

Ajeris is SMS-first. The agent picks tools by reading natural-language messages from the user and matching intent to a tool description. Bad descriptions silently break the integration: the agent sees the tool, doesn't recognize when to use it, and replies "I don't have a way to do that."

Write each description so that it:

  • Starts with the user-facing job the tool does, in plain English.
  • Mentions the kinds of phrasings a user might use to ask for it.
  • Says what the tool returns or changes.

A weak description ("Returns rows", "Calls the API") is the most common reason a freshly-connected MCP server appears in the preview but never gets called by the agent. The shape of the input schema is not enough on its own; the model leans heavily on the prose.

Argument descriptions matter too. The agent fills arguments from the message context, so a description like "ISO 8601 date, e.g. 2026-04-28" produces far better calls than just string.

When the user pastes your URL into Ajeris and clicks Check connection, the preview screen shows your tool list with these descriptions verbatim. That preview is the user's last chance to catch a missing tool or unclear copy before saving.

For more reference servers, see github.com/modelcontextprotocol/servers.


Finding community servers

If you don't want to build your own:

  • Docker MCP Catalog - 300+ verified servers, install from Docker Desktop or docker mcp CLI.
  • MCP Registry - registry.modelcontextprotocol.io, the official cross-company catalog.
  • PulseMCP - pulsemcp.com/servers, thousands of indexed community servers.
  • Smithery - smithery.ai, with automated install guides.

Not every community server ships an HTTPS deployment. If a server is stdio-only, you'll need to host it yourself behind HTTPS before you can connect it through the web UI.


Troubleshooting

"Could not reach the server." (unreachable)

  • Is the host publicly resolvable? dig +short your-host.example.com from an off-network shell.
  • Does it respond inside 10s? curl -v -m 10 https://host/mcp.
  • Is the server bound to 0.0.0.0 (not 127.0.0.1)? Behind a firewall, a 127.0.0.1 binding looks reachable but fails from outside.

"Server rejected the auth header." (auth_failed)

  • Paste the exact header VALUE, including the scheme your server expects. Bearer abc vs bare abc vs Token abc are all different.
  • Rotate the token on the source service and try again. Some tokens silently expire on first use or after a TTL.

"Server connected but exposes zero tools." (no_tools)

  • Your server is up but listTools() returned { tools: [] }. Check the server's registered tool set.

"Protocol error." (protocol_error)

  • Almost always means the endpoint isn't speaking Streamable HTTP. SSE-only servers and custom JSON-RPC endpoints won't work.
  • Double-check your route mounts StreamableHTTPServerTransport.

"Too many tools slow down responses."

  • Each connected server's tools consume context on every turn. A server with dozens of tools meaningfully grows the per-turn prompt size.
  • Disable the ones you don't use daily from the same page. Per-tool filtering is on the roadmap.

Roadmap

  • Multiple auth headers - beyond the single Authorization field.
  • OAuth catalog - "Connect GitHub", "Connect Linear" buttons that pre-fill the URL and walk through OAuth.
  • Per-tool filtering - choose which subset of a server's tools to expose to the agent, cutting token overhead on multi-tool servers.
  • Marketplace - browse + one-click install community servers during onboarding.
PreviousAlexa+ integrationNextPlatform overview
ajeris.comPrivacyTermshi@ajeris.com