The core stack we use for every build: FastMCP for server construction, LM Studio for local inference, ChromaDB for agent memory, and NATS JetStream for message queuing. This page gets you oriented before you dive into the pattern-specific docs.
Every build in the Rebel Devel ecosystem uses the same base layer. Once you understand how these pieces fit together, every post and repo will make sense immediately.
This is the minimal FastMCP server we start every build with. It wires up the protocol layer, defines one tool, and runs. Everything else — auth, rate limiting, error handling — gets layered on after you have this working.
# Python 3.11+ required
pip install fastmcp pydantic structlog
from fastmcp import FastMCP
import structlog
log = structlog.get_logger()
mcp = FastMCP("my-skill-server")
@mcp.tool()
async def do_thing(input: str) -> dict:
"""
Does the thing. Describe it here — agents read this.
Use this when you need to [specific situation].
Returns a dict with 'result' and 'status' keys.
Args:
input: Description of what input should look like.
"""
log.info("tool_called", tool="do_thing", input=input)
return {"result": f"Did: {input}", "status": "ok"}
if __name__ == "__main__":
mcp.run()
Always return a dict, not a string. Agents parse structured output more reliably than free text. Include a status field so the agent can check for success without parsing the result.
LM Studio exposes an OpenAI-compatible API on localhost. Point your agent client at it and it works exactly like the cloud API — except data stays local and there's no per-token cost.
import openai
client = openai.OpenAI(
base_url="http://localhost:1234/v1",
api_key="lm-studio" # any string — not validated locally
)
# Test the connection
response = client.chat.completions.create(
model="qwen2.5-coder-32b-instruct", # match your loaded model
messages=[{"role": "user", "content": "ping"}],
max_tokens=10
)
print(response.choices[0].message.content)
Make sure the model name in your API call exactly matches the model ID shown in LM Studio's UI. LM Studio is case-sensitive about model IDs and will return a 404 with no helpful message if they don't match.
This is the minimal agent loop that connects an LM Studio model to an MCP server. It handles tool parsing and result injection. The lm-studio-agent-loop repo has the production version with retry logic and token tracking.
import json
import openai
from mcp_client import MCPClient # your MCP client of choice
client = openai.OpenAI(base_url="http://localhost:1234/v1", api_key="x")
mcp = MCPClient("http://localhost:8000")
async def run_agent(task: str) -> str:
messages = [{"role": "user", "content": task}]
tools = await mcp.list_tools()
while True:
response = client.chat.completions.create(
model="qwen2.5-coder-32b-instruct",
messages=messages,
tools=tools,
)
msg = response.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for call in msg.tool_calls:
result = await mcp.call_tool(
call.function.name,
json.loads(call.function.arguments)
)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result)
})
The agent-memory-server repo implements all 7 memory types as an MCP server. If you just need semantic search over stored content, this gets you there in three steps:
python memory_server.pyThe server exposes store_memory, retrieve_memory, and search_memories as MCP tools. Your agent calls them the same way it calls any other tool. Read the 7 Types of Agent Memory post for the full pattern breakdown.