Custom MCP servers

Not every service your agent needs is in the catalog. Any MCP server can give it tools: DeepWiki, for example, answers questions about public GitHub repositories. One command adds the server and connects it for your app.

To find a ready server, search the official MCP Registry. An entry for a hosted server lists its URL and a type. Use a server of type streamable-http. A server's own docs also give its URL.

Add a public MCP server

DeepWiki takes no key, and nobody signs in to it.

Add the server

getstream agents connectors add custom_deepwiki https://mcp.deepwiki.com/mcp
Created connector custom_deepwiki (No authentication)
Connected: custom_deepwiki / deepwiki (<connection id>)

It adds the connector and an app connection to it, labelled deepwiki: the id without custom_. Set your own with --label.

Check that it is connected

getstream agents connections list --connector custom_deepwiki
ID               CONNECTOR        LABEL     OWNER  STATUS
<connection id>  custom_deepwiki  deepwiki  App    Connected

Use it in an agent

Warning:

The blank template has no guardrail.md, so nothing screens the question. If you use another template, delete or change its guardrail.md first, see Guardrails.

getstream agents init assistant --template blank

Replace agents/assistant/agent.yaml with:

agents/assistant/agent.yaml
name: assistant # an internal assistant for your team
mode: text
llm: llm-fast
connectors:
  - name: deepwiki
    connector: custom_deepwiki # the connector's id
    connection: deepwiki # the label of its connection
getstream agents test assistant

Ask: "What is the vercel/next.js repository?" to see the agent call DeepWiki's tools, such as deepwiki__ask_wiki_question.

See Tools to offer only some of its tools.

Add an MCP server that needs a key

Exa searches the web, with an API key it reads from the x-api-key header.

Add the server

--api-key - reads the key from stdin, so it never shows in your shell history:

printenv EXA_API_KEY | getstream agents connectors add custom_exa https://mcp.exa.ai/mcp \
  --auth api-key --header x-api-key --api-key -
Created connector custom_exa (API key)
Connected: custom_exa / exa (<connection id>)

Use it in an agent

Add the binding to the same agent.yaml:

agents/assistant/agent.yaml
connectors:
  - name: exa
    connector: custom_exa
    connection: exa

Run getstream agents test assistant, and ask: "Call exa__web_search_exa to find the Vision Agents page on getstream.io". The agent has a web search of its own, so name the tool to see it call Exa.

See Sign-in for the other ways a connection can sign in.

Remove a server

Take it out of agent.yaml and sync your agent, then remove it:

getstream agents sync assistant
getstream agents connectors remove custom_deepwiki --force
Removed connector custom_deepwiki and its connections

--force deletes its connection with it. Without it, a connector with a connection is not removed.

The CLI can't change a server. Use Edit in the Dashboard, or the API below.

In the Dashboard

Create the connector

In Library > Connectors, click Create connector. Type the ID, such as custom_deepwiki, a Name and the Endpoint. Under Authentication, pick No authentication for a public server or API key for one that needs a key. Click Create connector.

Connect it

On the connector's page, click Create connection. Pick App as the Owner, and type a Label, such as deepwiki. For a key, type the Header, such as x-api-key, and the API key. Click Create connection.

To change a server, open it and click Edit. Type the Endpoint again, since the Dashboard doesn't show the saved one, and click Save revision. A save that changes anything stores the next revision, and each connection stays on its old one. On a public server, changing the Endpoint makes the connection's Definition show Outdated. Open the connection and click Reconnect to move it to the new revision. A new Category leaves the connection Current. Other fields may also make it Outdated. For an API key connection, use the API section below.

With the API

The API changes a connector in place. $STREAM_ACCELERATION_URL is your router's URL, and $SERVER_TOKEN is a server token: an HS256 JWT of {"server": true} signed with your API secret.

Create the connector again with the same id. It stores the next revision:

curl -X POST "$STREAM_ACCELERATION_URL/v1/agents/connectors" \
  -H "api_key: $STREAM_API_KEY" -H "stream-auth-type: jwt" \
  -H "Authorization: Bearer $SERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "custom_deepwiki",
    "name": "DeepWiki",
    "description": "Answers questions about public GitHub repositories",
    "endpoint": "https://mcp.deepwiki.com/mcp",
    "schemes": ["none"]
  }'

Each connection keeps the old revision until its credentials are saved again. Use the connection's id from connections list as $CONNECTION_ID. expected_revision is the connection's revision, as GET /v1/agents/connections/$CONNECTION_ID returns it:

curl -X PUT "$STREAM_ACCELERATION_URL/v1/agents/connections/$CONNECTION_ID/credentials" \
  -H "api_key: $STREAM_API_KEY" -H "stream-auth-type: jwt" \
  -H "Authorization: Bearer $SERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 2}'

For a key, send it again in "values": {"api_key": "...", "header": "x-api-key"}.

Build an MCP server for your agent

If you write the server yourself, here is what the agent expects of it.

  • It speaks Streamable HTTP. A server that serves only the older HTTP with SSE fails when you add it, and connectors add keeps the connector and prints how to connect or remove it:

    Error: wiki did not work without a credential: mcp: connect to custom_wiki: calling "initialize": sending "initialize": Method Not Allowed.
  • It answers at a public https URL, with no query string or fragment: take the key in a header. To try a server on your laptop, put a tunnel in front of it, such as cloudflared tunnel --url http://localhost:8000.

  • The agent sees each tool as <name>__<tool>, with the name from agent.yaml, such as deepwiki__ask_wiki_question.

Warning:
  • A result over 32 KiB is cut, and ends with [connector result truncated] so the model knows it read only part. Return what the model needs, not a whole document.
  • A tool call has 5 seconds. Answer within 5 seconds, and return at once from slow work. A longer call is cut, and the model reads that the tool did not answer in time and may or may not have done what was asked.

To test it, add it with getstream agents connectors add, put it in an agent's agent.yaml, and ask a question only your tool can answer with getstream agents test.

Going further