MCP gateway
A gateway for other agents, with you at the gate.
Other AI agents, assistants and scripts can talk to the agents you build here. They connect to one address using the Model Context Protocol (MCP), and each agent you offer appears to them as a tool: send it a message, get its reply. Clients that speak the Agent2Agent protocol reach the same agents, each at an address of its own.
Nothing is offered until you say so, and nobody gets in without a credential that belongs to a client you can see, limit and remove.
Offer an agent
You choose which agents are offered.
An agent is reachable through the gateway only once you have switched it on there. Give it two things.
A name
The tool other agents call is
ask_<name>.A description
What the agent is for, in a sentence. It is what the calling agent’s model reads to decide whether to ask, so write it for that reader.
A published version
Only an agent with a live, published version can be reached.
Its inputs
What a caller may pass when a conversation starts is whatever the flow’s Start step declares as inputs.
Two ways in
A key, or a sign-in.
Both end in the same thing: a client record in your workspace that says what the client may do.
With a gateway key
For servers, scripts and agent frameworks.
- A key is issued for a client and shown once. The client sends it as a bearer token.
- A key can be replaced, and the old one stops working at once. It can be given an expiry, or removed with its client.
- A gateway key opens the gateway and nothing else: the REST API refuses it.
POST /mcp
Authorization: Bearer sk_mcp_…By signing in (OAuth 2.1)
For assistant applications that connect with a browser sign-in.
- The application is pointed at the gateway’s address. It is refused with directions for signing in, and sends a person to your site, who signs in, chooses a workspace and agrees to what is asked.
- The application receives a token that is good for the gateway only, for ten minutes at a time, and names the person, the application and the workspace.
- An application introduces itself with a metadata document at an HTTPS address of its own. There is nothing to register first.
- If the person who agrees is an owner or an administrator, the application works at once. Otherwise it waits as pending until somebody who may admits it.
- Somebody removed from the workspace is refused from their next request, whatever token they still hold.
- Signing in needs the site to be served over HTTPS. Over plain HTTP, anywhere but on the machine itself, the gateway takes keys only.
What a client may do
Each client is held to its own record.
The record is yours to set, and to change.
| On the client record | What it does |
|---|---|
| Scopes | What the client may do at all. They are listed below, from the product. |
| Agents | All offered agents, or a chosen few. |
| Requests a minute | Of any kind. Past it, requests are refused with 429 and a time to wait. |
| Open conversations | How many it may have at once. When it starts one more, the one it has left waiting longest is closed to make room. One used within the last minute never is. |
| Turns in a conversation | A conversation that reaches the number is closed. |
| Tokens a day, spend a day | Counted from midnight UTC over the conversations it started. Past it, nothing new is started until the next day. |
| Read conversations back | Whether get_conversation returns what was said. |
| Web origins | The pages a client that runs in a browser may call from. Requests from any other page are refused. |
| Status | Active, pending (waiting to be admitted) or switched off. |
Scopes
Every request is on the record
Every request a client makes, allowed or refused, is written down, and cannot be altered afterwards. Conversations through the gateway appear with every other conversation, under the client’s name.
The tools
What a client sees.
The list a client is given depends on what its record allows. These are the tools, read from the product.
A reply
What comes back from an agent.
A reply is structured, and is also sent as JSON text for clients that only read text. A client that sends a progress token hears the reply as it is written.
The states a reply reports
- awaiting_user
- The agent waits for the next message.
- ended
- The conversation is over.
- pending_approval
- A person in your workspace has to approve something before the agent goes on.
- handed_over
- The conversation has been handed to a person.
- working
- The agent is still at work.
{
"conversation_id": "01a12111-741c-7696-a7e8-5072c9c680c5",
"agent": "front_desk",
"status": "awaiting_user",
"reply": "We open at nine on Sundays.",
"outcome": null
}Connecting
Any MCP client that speaks Streamable HTTP.
The gateway speaks the newest revision of MCP, and the earlier handshake for clients that have not moved on.
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client'
const client = new Client(
{ name: 'my-agent', version: '1.0.0' },
{ versionNegotiation: { mode: 'auto' } },
)
await client.connect(
new StreamableHTTPClientTransport(new URL('https://app.example.com/mcp'), {
authProvider: { token: async () => process.env.SIDEKICK_GATEWAY_KEY },
}),
)
const reply = await client.callTool({
name: 'ask_front_desk',
arguments: { message: 'Are you open on Sunday?' },
}){
"mcpServers": {
"sidekick-agents": {
"url": "https://app.example.com/mcp",
"headers": { "Authorization": "Bearer sk_mcp_…" }
}
}
}curl -s https://app.example.com/mcp \
-H "Authorization: Bearer $SIDEKICK_GATEWAY_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'https://app.example.com/mcpAgent2Agent
The same agents, over A2A.
A client that speaks the Agent2Agent protocol reaches the same agents, as the same client: the same key, the same limits, the same record. Only the words on the wire differ.
An address and a card for each agent
Each agent you offer has an address of its own, and a card that describes it. The card is shown only to a client with a key that may reach that agent.
- The agent
- https://<your site>/a2a/<name>
- Its card
- https://<your site>/a2a/<name>/.well-known/agent-card.json
A conversation is a task
A message that names no task starts a conversation, and the task’s id is the conversation’s. A message that names the task carries it on. A task can be asked where it stands, and cancelled.
Gateway keys only
An A2A client comes in with a gateway key. Signing in through a browser is for MCP only for now.
The states a task reports
- TASK_STATE_INPUT_REQUIRED
- The agent has answered and waits for the next message.
- TASK_STATE_COMPLETED
- The flow ended the conversation.
- TASK_STATE_WORKING
- A person in your workspace has to approve something before the agent goes on.
- TASK_STATE_CANCELED
- The client ended the conversation.
Messages are text. A turn is answered on the request that asked for it: push notifications, and subscribing to a task afterwards, are not offered.
What it never does
What another agent says is never an instruction.
A gateway to your agents is also a door for whatever another agent has been told to say. The platform treats it as that.
It arrives as what a caller said
What a client sends reaches an agent as what its caller said. It is never treated as instructions to the agent, whatever it says.
A malformed call reaches no agent
A tool call whose arguments do not fit the tool is refused by the protocol layer before it reaches the gateway’s own code. It reaches no agent, and is not in the record.
Access ends when you end it
A replaced key stops working at once. A person removed from the workspace is refused from their next request.
Start with a workspace of your own.
Create an account, connect a model, and make your first agent from a template.