Resources expose read-only data to MCP clients. Unlike tools (which perform actions), resources provide context:database schemas, config files, documentation, or any data the LLM needs to make better decisions.
Defining Resources
from concierge import Concierge
app = Concierge("my-server")
@app.resource("resource://app/settings")
def get_settings() -> str:
"""Current application settings."""
return json.dumps({
"max_cart_items": 50,
"supported_currencies": ["USD", "EUR", "GBP"],
"free_shipping_threshold": 100,
})
The resource URI (resource://app/settings) is how clients reference this resource. For ChatGPT widget apps, use the ui:// scheme (e.g. ui://widget/my-widget).
Resource Templates
Use URI templates for dynamic resources:
@app.resource("resource://products/{product_id}")
def get_product_docs(product_id: str) -> str:
"""Product documentation and specifications."""
# Replace with your own data source
return f"# Product {product_id}\n\nA great product.\n\nPrice: $99"
| Use Case | Resource | Tool |
|---|
| Read-only data | Yes | No |
| Database schema | Yes | No |
| Configuration | Yes | No |
| Search/query | No | Yes |
| Create/update/delete | No | Yes |
| Side effects | No | Yes |
Resources are fetched once at the start of a conversation and cached. Use them for stable context that doesn’t change per-request.
Resources with Stages
Resources can be scoped to stages, just like tools. This means the LLM only sees resources relevant to the current workflow step.