
Write HTML. Render video. Built for agents.
Concierge is a Python SDK for building MCP servers that expose tools in steps instead of all at once. It wraps an existing FastMCP server or creates a new one, then adds stages, transitions, shared state, and optional semantic search for large tool sets. You can run it over stdio or streamable HTTP, and you can scaffold a new project with `concierge init`. The repo also includes adapters, backends, state storage, and example app code that shows the workflow pattern end to end.
Builders who want their MCP server to show only the right tools at each step and keep workflow state between calls.
You can make agent tool use more controlled, stateful, and easier to ship without rewriting your existing MCP tools.
Groups tools into stages so only the current step's tools are returned to the agent.
Defines which workflow steps can follow others, so the agent moves only along paths you allow.
Stores and reads values with `set_state` and `get_state` across steps and replicas.
Collapses large APIs behind `search_tools` and `call_tool` meta-tools using a search backend.
Wraps an existing FastMCP server without changing your `@app.tool()` decorators.
Runs over stdio, streamable HTTP, or SSE for different deployment setups.
Creates a ready-to-run project with tools, stages, and transitions wired in.
pip install concierge-sdk
concierge init my-store # Generate a ready to run project cd my-store # Enter project python main.py # Start the MCP server
search_tools(query: str) → Find tools by description call_tool(tool_name: str, args: dict) → Execute a discovered tool
The Model Context Protocol (MCP) is a standardized way to connect AI agents to tools. Instead of exposing a flat list of every tool on every request, Concierge progressively discloses only what's relevant. Concierge guarantees deterministic results and reliable tool invocation.
[!NOTE] Concierge requires Python 3.9+. We recommend installing with uv for faster dependency resolution, but pip works just as well.
pip install concierge-sdk
Scaffold a new project:
concierge init my-store # Generate a ready to run project
cd my-store # Enter project
python main.py # Start the MCP server
Or wrap an existing MCP server two lines, nothing else changes:
# Before
from mcp.server.fastmcp import FastMCP
app = FastMCP("my-server")
# After: just wrap it
from concierge import Concierge
app = Concierge(FastMCP("my-server"))
[!TIP] Concierge works at the MCP protocol level. It dynamically changes which tools are returned by
tools/listbased on the current workflow step. The agent and client don't need to know Concierge exists, they just see fewer, more relevant tools at each point.
from concierge import Concierge
from mcp.server.fastmcp import FastMCP
app = Concierge(FastMCP("my-server"))
# Your @app.tool() decorators stay exactly the same.
# You can additionally add app.stages and app.transitions.
[!NOTE] The wrap and go gives you progressive tool disclosure immediately. Add
app.stagesandapp.transitionswhen you want full workflow control, no code changes required.
Instead of exposing everything at once, group related tools together. Only the current step's tools are visible to the agent:
app.stages = {
"browse": ["search_products", "view_product"],
"cart": ["add_to_cart", "remove_from_cart", "view_cart"],
"checkout": ["apply_coupon", "complete_purchase"],
}
Control which steps can follow which. The agent moves forward (or backward) only along paths you allow:
app.transitions = {
"browse": ["cart"], # Can only move to cart
"cart": ["browse", "checkout"], # Can go back or proceed
"checkout": [], # Terminal step
}
Pass data between workflow steps without round-tripping through the LLM. State is session-scoped and works across distributed replicas:
# In the "browse" step - save a selection
app.set_state("selected_product", {"id": "p1", "name": "Laptop"})
# In the "cart" step retrieve it directly
product = app.get_state("selected_product")
When you have hundreds of tools, enable semantic search to collapse your entire API behind two meta-tools:
from concierge import Concierge, Config, ProviderType
app = Concierge("large-api", config=Config(
provider_type=ProviderType.SEARCH,
max_results=5,
))
No matter how many tools you register, the agent only ever sees:
search_tools(query: str) → Find tools by description
call_tool(tool_name: str, args: dict) → Execute a discovered tool
Concierge supports multiple transports. Use streamable HTTP for web deployments:
# Streamable HTTP (recommended for web)
http_app = app.streamable_http_app()
# Or run over stdio (default, for CLI-based clients)
app.run()
[!TIP] All of the above: stages, transitions, state, semantic search are optional and independent. Use any combination. Start simple and add structure as your workflow grows.
| Progressive Disclosure: Only expose the tools that matter right now. Fewer tools in context means less confusion and lower cost. | Enforced Tool Ordering: Define which tools unlock which. The agent follows your business logic, not its own guesses. |
| Shared State: Pass data between workflow steps server-side. No tool-call chaining through the LLM, no re-injecting data into prompts. | Semantic Search: For large APIs (100+ tools), collapse everything behind two meta-tools. The agent searches by description, then invokes. |
Protocol Compatible: Wraps any MCP server. Your existing @app.tool() decorators, resources, and prompts work unchanged. | Session Isolation: Each conversation gets its own workflow state. Atomic, consistent, works across distributed replicas. |
| Multiple Transports: Run over stdio, streamable HTTP, or SSE. Deploy anywhere: serverless, containers, bare metal. | Scaffolding CLI: concierge init generates a ready to run project with tools, stages, and transitions wired up ready to go. |
A complete e-commerce workflow in under 30 lines:
from concierge import Concierge
app = Concierge("shopping")
@app.tool()
def search_products(query: str) -> dict:
"""Search the product catalog."""
return {"products": [{"id": "p1", "name": "Laptop", "price": 999}]}
@app.tool()
def add_to_cart(product_id: str) -> dict:
"""Add a product to the cart."""
cart = app.get_state("cart", [])
cart.append(product_id)
app.set_state("cart", cart)
return {"cart": cart}
@app.tool()
def checkout(payment_method: str) -> dict:
"""Complete the purchase."""
cart = app.get_state("cart", [])
return {"order_id": "ORD-123", "items": len(cart), "status": "confirmed"}
app.stages = {
"browse": ["search_products"],
"cart": ["add_to_cart"],
"checkout": ["checkout"],
}
app.transitions = {
"browse": ["cart"],
"cart": ["browse", "checkout"],
"checkout": [],
}
app.run() # Start over stdio
The agent starts at browse. It can move to cart, then to checkout. It cannot call checkout from browse. Concierge enforces this at the protocol level, no prompt engineering required.
Full guides, API reference, and deployment patterns are available at docs.getconcierge.app.
We are building the agentic web. Come join us.
Sign in to join the discussion.
No comments yet. Be the first to say what this is good for.

Write HTML. Render video. Built for agents.
Ultra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps
SkillOpt is a text-space optimizer that trains reusable natural-language skills for frozen LLM agents through trajectory-driven edits, validation-gated updates, and deployable best_skill.md artifacts.

Omnigent is an open-source AI agent framework and meta-harness: orchestrate Claude Code, Codex, Cursor, Pi, and custom agents — swap harnesses without rewriting, enforce policies and sandboxing, and collaborate in real time from any device.
A theoretical reconstruction of the Claude Mythos architecture, built from first principles using the available research literature.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!