Building an Agent with the SDK
This guide is for the case where you own the agent loop: you chose the model, you wrote the prompt, and you decide which tools it gets. It shows how to add Memco Shared Memory to that agent so it searches before working and writes back what it learned.
The code is Python. The Node.js SDK has the same shape with the same names in camelCase; see the Node.js reference.
The loop
An agent with memory does three things it did not do before:
- Search before working. Given a task, it searches the domain for what teammates and their agents have already established.
- Rate what it was given. After using a result, it reports whether the result was relevant and correct. Ratings are what move the reliability signal on a memory, so an agent that never rates is an agent whose memory never improves.
- Write back what it learned. When it establishes something non-obvious, it saves it: why something turned out the way it did, how a system actually behaves, a decision and its rationale.
Everything else on this page is about making those three steps reliable when a model is the one deciding when to take them.
Handing the tools to a model
A session exports its operations as a toolset. The toolset carries what a model needs beyond the calls themselves: a description of each tool, a JSON Schema for its arguments, results rendered as text, and the handover to whatever is driving the loop.
from memcoai import Memco, agent
with Memco() as client:
entry = next(d for d in client.memory.list_domains().domains if d.slug == "coding")
with client.memory.with_session("coding") as session:
toolset = session.tools()
system_prompt = agent.briefing(entry, session.instructions)
# hand toolset and system_prompt to your framework of choiceThe same toolset speaks three shapes:
# LangChain
runnable = create_agent(model, tools=toolset.to_langchain(), system_prompt=system_prompt)
# Anthropic SDK, driving the loop yourself
response = anthropic.messages.create(tools=toolset.to_anthropic(), ...)
text_for_model = toolset.call(block.name, block.input)
# OpenAI SDK, driving the loop yourself
completion = openai.chat.completions.create(tools=toolset.to_openai(), ...)
text_for_model = toolset.call(call.function.name, call.function.arguments)toolset.call runs the operation the model asked for and returns text the model can read. to_langchain needs LangChain installed; the other two produce plain data and need nothing.
Three choices the SDK makes for you
The session is bound in code, outside the loop. Every tool in the toolset is bound to the session it was built from. The model never sees a session id and cannot omit or invent one, so every call it makes is recorded as part of the same series. If you build the tools by hand instead, a model that drops the id produces a search that still succeeds and is silently unrelated to the task.
Domain guidance goes in the system prompt, not behind a tool. list_domains returns, for each domain, what it holds, when to search it, what belongs in it, and the tag vocabulary. agent.briefing() renders that answer into text for the system prompt. A tool the model may forget to call is a tool that does not steer it, so the toolset exposes neither list_domains nor start_session.
The tool descriptions are the service’s. The text a model reads for each tool comes from the same manifest the hosted MCP server publishes. When the service changes its guidance, the change reaches your agent with an SDK release rather than diverging silently. Three parameters keep SDK-specific wording because they are typed objects here and XML strings over MCP: tags, feedback, and source.
What the model sees when things go wrong
Not every failure should reach the model. A malformed request, an invented tool name, or a handle that resolves to nothing are things a model can fix on its next turn, so these come back as text. A rejected credential, a rate limit, or an unreachable service are things no wording will fix, so these are raised to your code. agent.AGENT_RECOVERABLE is the set of error classes that go back to the model; everything else propagates.
Patterns that make memory work
These come from building agents on Memco, including the Learning on the Job experiments.
Fix the topic per project; do not let the model choose it. Left free, each write invents its own taxonomy (returns, shipping-issues, customer-policy) and lessons filed under one made-up topic stop being found by searches scoped to another. The failure presents as retrieval misses rather than as a tagging problem, which makes it hard to diagnose. Set the tags at the integration layer and keep them constant for the whole corpus.
Search at the start of every task, not once per process. Memory changes while the agent runs, and the search that matters is the one made with the task in hand.
Rate after use, not after retrieval. A result looks relevant at retrieval time and turns out wrong in use. The useful signal comes after the agent has acted on it.
Write findings, not transcripts. A memory should be a concise, non-trivial finding someone else can act on. Raw trajectories transfer badly between tasks.
Keep the write path reversible. create_memory returns an operation id. Keep it while the agent’s work is still under review, so a write that turns out wrong can be undone with revert_memory inside the revert window.
A complete example
langchain_agent.py is a complete agent in about eighty lines, using only what the SDK supplies. It runs against any provider LangChain supports:
export MEMCO_API_TOKEN=...
uv run --with memcoai --with langchain --with langchain-google-genai \
python examples/langchain_agent.pyThe Node.js equivalent is langchain_agent.ts.
Next steps
- Embedding memory in your product — when the agent is part of something you ship to your own customers
- Trust — how ratings become reliability signals
- Knowledge domains — what each domain holds and how tags work