Your own agents
Connect your own agent
An agent you run yourself — a script, a workflow, a bot on your side — reads your workspace's memory under its own identity, not a person's. You add it in the console, give it a credential, and connect it with one HTTP request, an SDK, or MCP.
Three ways to connect
All three reach memory as the same agent and get the same answer. They differ in what the agent holds and which door it knocks on.
| Way | Good for | The agent holds | Reachable from |
|---|---|---|---|
| One HTTP request | n8n, Zapier, Make, Postman, or any language | An API key | Allow-listed networks |
| An SDK | Code you write in Python, TypeScript or Go | An API key | Allow-listed networks |
| MCP | A program that speaks MCP | A client id and secret | Anywhere |
This page is about agents you run
1. Add the agent
In the console, open Identities and choose Add agent. You need to be an admin. The panel asks for:
| Field | Type | Meaning |
|---|---|---|
| Agent id* | slug | Lowercase letters, digits, dots, dashes or underscores, up to 64 characters. It is the name the agent authenticates and writes as, and it cannot be changed later. The agent's identity is agent:<id>. |
| Name* | text | What people see in the console. |
| Team* | one team | Decides what memory the agent reads. An agent acts for one team. |
| Level* | User or Admin | What it may manage in the console. It does not change what it reads. |
| How will it connect?* | choice | API key, MCP, Both, or Not yet. The credentials you pick are created with the agent and shown once on the next screen. |
Nothing is created that you did not ask for. If you pick Not yet, the agent is added with no credential, and you create one later from its row.
2. Its credentials
An agent can hold two kinds of credential, separately or together.
| Credential | What it is | Used by |
|---|---|---|
| API key | One secret, valid until you revoke it | The HTTP request and the SDKs |
| MCP credential | A client id and a secret, exchanged for a token that lasts an hour | A program that speaks MCP |
- Each secret is shown once, when it is created. Copy it then; the console never shows it again.
- Both belong to the same identity,
agent:<id>, and read exactly the same memory. Which one you use changes the door, not the access. - Keep them in a secret store or an environment variable, never in a repository.
How an agent connects is not a setting on the agent. An agent created with only an MCP credential gets an API key later from its row’s Keys button, and the other way round.
3a. Connect with one HTTP request
The key dialog shows this request with your key, your workspace and your sources filled in. It works as-is from n8n, Zapier, Make, Power Automate or Postman, each of which can import a cURL command, and from any language.
curl https://api.memoth.ai/novarque.v1.MemoryService/Read \
-H "authorization: Bearer $MEMOTH_API_KEY" \
-H "content-type: application/json" \
-d '{ "query": "what do we know about renewals?", "agents": ["confluence", "github"] }'| Field | Type | Meaning |
|---|---|---|
| query* | string | What to look for, in natural language. |
| agents | string[] | The sources to read through. An agent reads a connected source's memory only when the request names that source — see What an agent can read. |
{
"aggregatedResponse": "Renewals are reviewed 60 days before the term ends.",
"citedFragments": [
{ "id": "…", "value": "…", "agentSet": ["confluence"], "createdAt": "…" }
],
"receiptId": "…"
}The API answers allow-listed networks only
api.memoth.ai refuses any other caller with an HTTP 403before it looks at the key. Send your server’s outbound IP address to hello@memoth.ai and we will allow it. MCP, below, has no such limit.
3b. Connect with an SDK
The SDKs wrap the same request. The key dialog shows the call in Python, TypeScript and Go; this is the Python one.
from memoth import Memory
mem = Memory(
tenant="<your workspace id>",
api_key="<the agent's API key>",
base_url="https://api.memoth.ai",
)
res = mem.read(
"what do we know about renewals?",
user="agent:<id>", # this key's own identity
agents=["confluence", "github"], # the sources to read through
)
print(res.aggregated_response)The SDK packages are not published yet
3c. Connect over MCP
Memoth runs the MCP server at https://mcp.memoth.ai/mcp; there is nothing to install. The agent swaps its client id and secret for a token and sends that token with every request.
Any MCP client
For a client that takes a server URL and a header, fetch a token:
curl -s https://auth.memoth.ai/v1/token \
-d grant_type=client_credentials \
-d client_id=$MEMOTH_CLIENT_ID \
-d client_secret=$MEMOTH_CLIENT_SECRET
# → { "access_token": "…", "token_type": "Bearer", "expires_in": 3600 }Then point the client at https://mcp.memoth.ai/mcp with the header Authorization: Bearer <access_token>. The token lasts an hour; fetch a new one with the same secret before it expires. The secret may be sent in the form body, as above, or in an HTTP Basic header.
A client that holds the id and secret itself
The official MCP libraries can do the token exchange for you: they find the token endpoint from the server’s discovery documents, fetch the token, and fetch a new one when it expires. In Python:
# pip install mcp (tested with 2.2; needs Python 3.10 or newer)
import asyncio
from mcp import ClientSession
from mcp.client.auth.extensions.client_credentials import ClientCredentialsOAuthProvider
from mcp.client.streamable_http import create_mcp_http_client, streamable_http_client
MCP_URL = "https://mcp.memoth.ai/mcp"
class TokenStore:
"""Holds the token in memory. The library fetches it, and a new one when it expires."""
tokens = client = None
async def get_tokens(self):
return self.tokens
async def set_tokens(self, tokens):
self.tokens = tokens
async def get_client_info(self):
return self.client
async def set_client_info(self, client):
self.client = client
async def main():
auth = ClientCredentialsOAuthProvider(
server_url=MCP_URL,
storage=TokenStore(),
client_id="<client id>",
client_secret="<client secret>",
token_endpoint_auth_method="client_secret_post",
issuer="https://auth.memoth.ai",
)
http = create_mcp_http_client(auth=auth)
async with (
http,
streamable_http_client(MCP_URL, http_client=http) as (read, write),
ClientSession(read, write) as session,
):
await session.initialize()
res = await session.call_tool(
"memory_search",
{
"query": "what do we know about renewals?",
"agents": ["confluence", "github"],
},
)
print("\n".join(part.text for part in res.content if part.type == "text"))
asyncio.run(main())An agent is offered three tools: memory_search, memory_write and whoami. Their parameters are on the Tools page. Over MCP the model fills in the tool arguments, so tell it in its instructions to pass agents with the sources it should read through on every search.
What an agent can read
- Its team’s memory and company-wide memory.The team you chose when adding it decides this, the same way a person’s team does.
- Never anyone’s private notes.Not its team-mates’, not an admin’s.
- Only through the sources it names. Memory that came from a connected source, such as Confluence or GitHub, is returned only when the request names that source in
agents. The credential dialog lists the sources the agent has. A request that names none gets nothing from connected sources — an empty-looking answer is most often this.
An agent cannot widen its own access. A request that claims to be someone else, asserts a role, or asks for every owner’s memory is refused with permission_denied, whichever way it connects.
What happens to what it writes
An agent’s write does not become shared memory on its own. It is kept under the agent’s identity as a suggestion, and a person decides.
- The agent writes — over HTTP, an SDK, or the
memory_writetool. - The write is held. It appears in the console under Governance → Suggestions, with the agent that made it.
- A person shares it or turns it down. Before it is shared it is checked for private details about people and for contradicting what memory already holds.
An agent that tries to write straight to shared memory is refused.
curl https://api.memoth.ai/novarque.v1.MemoryService/Write \
-H "authorization: Bearer $MEMOTH_API_KEY" \
-H "content-type: application/json" \
-d '{ "interaction": "The canary threshold was raised to two hours." }'Managing credentials
Every agent’s row in Identities shows which credentials it holds and how old the newest is, for example Key · 3 days old and MCP · today. The row’s Keys button opens a dialog with a tab for each kind, where you create, rotate and revoke.
Why two of each
An agent may hold at most two live API keys and two live MCP credentials. Two is one in use plus room for its replacement:
- Not one, because replacing the only key would mean revoking it first, and the agent would have no key until the new one was in place.
- Not more, because every live credential is a standing secret. Beyond what a replacement needs, extra ones add risk and make it harder to say which the agent uses.
Rotate
- In the Keys dialog, choose Rotate beside the credential. A replacement is created and shown once.
- Put the replacement where the old one is used. The old one keeps working meanwhile.
- Say the agent has switched, and confirm. Only then is the old one revoked. If you stop before this step, both stay live and the dialog marks the older one, so you can finish later.
Revoke and remove
- Revoke ends one credential at once. Anything still using it stops, and it cannot be undone.
- Removing the agent revokes all of its keys and MCP credentials first. Anything it wrote into memory stays with the team.
Tested with
On 3 October 2026 we ran each of these against a test deployment of the same server, with an agent’s own credentials, and checked that it read its team’s and the company’s memory and nothing else.
| Framework or tool | Version | Connected with |
|---|---|---|
| LangGraph | 1.2 | Its MCP adapter; also holding the client id and secret itself |
| OpenAI Agents SDK | 0.23 | Its MCP server class, in an agent run |
| Pydantic AI | 2.53 | Its MCP toolset, in an agent run |
| LlamaIndex | 0.14 | Its MCP tool spec |
| CrewAI | 1.15 | Its MCP server adapter |
| AutoGen | 0.7 | Its MCP tools, with the MCP library pinned below 2 |
| Google ADK | 2.11 | Its MCP toolset, in an agent run |
| Vercel AI SDK | 7.0 | Its MCP client, in a generateText run |
| Mastra | 2.1 | Its MCP client |
| n8n | 2.41 | The HTTP Request node with an API key; the MCP Client node with a token, and with an OAuth2 client-credentials credential |
Anything else that can send an HTTP request, or speak MCP with a bearer token, connects the same way; it is simply not on the list of what we have run.
Troubleshooting
| What you see | What it means |
|---|---|
HTTP 403 with an HTML body from api.memoth.ai | Your network is not allow-listed. The key was never looked at. Send us your outbound IP, or use MCP. |
| An answer with nothing in it | The request named no sources, or the agent has none connected, or its team holds nothing that matches. Pass agents. |
request user does not match the authenticated subject | The request named a userother than the agent’s own identity. Leave it out, or pass agent:<id>. |
asserting reader roles is not permitted | Remove rolesfrom the request. An agent’s access comes from its team. |
a shared-scope write is not permitted | An agent cannot publish to shared memory. Write without a scope; a person shares it from Suggestions. |
invalid_client from the token endpoint | The client id or secret is wrong, or the credential was revoked. |
HTTP 401 from the MCP server | The token expired or is missing. Fetch a new one with the same id and secret. |