Sage MCP Server
OfficialProvides tools for querying sensor data, submitting jobs, and managing Sage nodes in the Sage Grande Testbed.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Sage MCP ServerShow me temperature readings from node W023 in the last hour"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Sage MCP Server
A Model Context Protocol server for the Sage Grande Testbed — cyberinfrastructure for AI@Edge. It exposes ~30 MCP tools, resources, and prompts for querying sensor data, submitting edge-computing jobs, discovering plugins, and browsing Sage documentation from any MCP-compatible client (Claude Desktop, Cursor, custom agents, etc.).
Built on FastMCP 2.10+ and the
MCP SDK 1.12+; supports
stdio, sse, and streamable-http transports.
Choose your setup
There are three ways to run this — pick the one that fits.
# | Path | Who it's for | You need |
1 | Everyone. Zero install. | An MCP client + a Sage token. | |
2 | IDE users who want to run the server themselves. | Python 3.11+ and this repo. | |
3 | Devs, custom agents, curl testing. | Python 3.11+ or Docker. |
All three expose the exact same tools. The only difference between hosted and local is how much data you can reach: without Sage credentials, every tool still runs but falls back to public data only. See Authentication & credentials for how to add your token.
Related MCP server: Jetson MCP Server
1. Hosted (mcp.sagecontinuum.org)
The server is already running at https://mcp.sagecontinuum.org/mcp. Point
any MCP client at it — no install needed.
Cursor (~/.cursor/mcp.json) or Claude Desktop
(~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"sage": {
"url": "https://mcp.sagecontinuum.org/mcp",
"headers": {
"Authorization": "Bearer YOUR_USERNAME:YOUR_ACCESS_TOKEN"
}
}
}
}Get YOUR_ACCESS_TOKEN from
https://portal.sagecontinuum.org/account/access. The Bearer value must be
in username:token form for anything beyond public data.
Restart your client, then ask natural questions:
"Show me temperature readings from node W023 in the last hour." "Find nodes in Chicago with recent camera images." "What's the highest temperature recorded today across all nodes?"
2. Local — stdio (for IDE clients)
Run the server as a subprocess of your IDE. Nothing binds to a network port. This is the most private mode — data and credentials never leave your machine.
Install:
git clone https://github.com/waggle-sensor/sage-mcp.git
cd sage-mcp
pip install -r requirements.txt
cp .env.example .env # edit and add SAGE_USER / SAGE_PASS if you have themWire it into Cursor/Claude Desktop:
{
"mcpServers": {
"sage-local": {
"command": "/absolute/path/to/sage-mcp/scripts/run-local-stdio.sh",
"env": {
"SAGE_USER": "your-sage-username",
"SAGE_PASS": "your-sage-access-token"
}
}
}
}env: values here override anything in .env — useful when you want
different tokens per IDE profile. Omit them entirely to run against public
data only.
3. Local — HTTP (for development or non-IDE clients)
Bind the server to a local port. Useful for curl, custom agents, browser
extensions, or anything that connects to a URL rather than spawning a
subprocess.
Option A — bare Python
pip install -r requirements.txt
cp .env.example .env # optional
./scripts/run-local-http.shServer is now on http://127.0.0.1:8000/mcp. Quick sanity check:
curl http://127.0.0.1:8000/health
# {"status":"ok"}Option B — Docker
cp .env.example .env # optional
docker compose up # http://127.0.0.1:8000/mcp — laptop-safe defaultThe default docker compose up publishes only to 127.0.0.1. To expose it
on all interfaces (cloud deployment), use the cloud profile:
docker compose --profile cloud up -dFor older MCP clients that only speak SSE:
docker compose --profile sse up # http://127.0.0.1:8001/ssePoint a client at your local server
{
"mcpServers": {
"sage-local-http": {
"url": "http://127.0.0.1:8000/mcp"
}
}
}Authentication & credentials
The server never requires a Sage token — it just does less without one.
Scenario | What works | What doesn't |
No token | Public sensor data, node listings, docs, plugin discovery, image proxy for public images | Protected datasets, |
| Everything the token's Sage account has access to | Anything not shared with your account |
How to provide a token, in order of preference:
.envfile (SAGE_USER=…+SAGE_PASS=…) — picked up by both./scripts/run-local-http.shand./scripts/run-local-stdio.shand bydocker compose.Per-request HTTP header — for the hosted server or any HTTP transport:
Authorization: Bearer username:token(recommended)Authorization: Basic <base64(username:token)>X-SAGE-Token: username:token
Query parameter —
?token=username:tokenas a last resort (shows up in server logs, so avoid for anything sensitive).
Get your token from https://portal.sagecontinuum.org/account/access. Access to protected data also requires signing the Sage Data Use Agreement.
Configuration reference
Every option is an environment variable — see .env.example
for the full list with comments.
Variable | Default | Purpose |
|
|
|
|
| Bind address (network transports only) |
|
| Bind port (network transports only) |
|
| HTTP path (streamable-http only) |
|
| Python logging level |
| — | Basic auth for |
|
| Public URL prefix in generated proxy links |
| — | Required to hit |
| — | Skip ECR call on startup (offline/CI) |
HTTP endpoints (non-MCP)
Path | Auth | Purpose |
| none | Liveness / readiness probe |
|
| Authenticated image proxy to |
| admin key | Aggregate usage counts |
| admin key | Per-user stats |
| admin key | Per-tool stats |
| admin key | One user + their tool usage |
| admin key | Recent activity feed ( |
Admin API key is accepted via X-Admin-API-Key header,
Authorization: Bearer <key>, or ?api_key=<key>.
Tools, resources & prompts
29 tools, 2 resources, 7 prompts. Highlights:
Sensor data:
get_node_all_data,get_node_iio_data,get_environmental_summary,list_available_nodes,search_measurements,get_node_temperature,get_temperature_summaryNode metadata:
get_node_info,list_all_nodes,get_sensor_detailsJobs:
submit_sage_job,submit_plugin_job,submit_multi_plugin_job,check_job_status,query_job_data,force_remove_job,suspend_jobGeography:
get_nodes_by_location,get_measurement_stat_by_locationPlugins:
find_plugins_for_task,get_plugin_data,query_plugin_data_nl,create_pluginImages:
get_cloud_images,get_image_data,get_image_proxy_urlDocs:
ask_sage_docs,sage_faq,search_sage_docsResources:
query://{plugin},stats://temperaturePrompts:
getting_started_guide,plugin_development_guide,data_analysis_guide,troubleshooting_guide, and three "suggest" prompts.
More docs in the docs/ folder — Getting Started,
Authentication,
Custom Functions,
Docker Deployment.
Testing
pip install -r requirements.txt
python -m pytest tests/ # 61 tests, no network requiredThe test suite mocks sage_data_client and the ECR registry — nothing hits
Sage's servers during CI. Legacy interactive smoke scripts (test_auth.py,
test_server.py, test_image_proxy.py, ...) still work as manual tests;
they're excluded from pytest collection via pytest.ini.
Extending
Add a tool by decorating a function in one of the modules under
sage_mcp_server/tools/:
# sage_mcp_server/tools/sensor_tools.py
@mcp.tool
def my_custom_analysis(data_query: str, analysis_type: str = "basic") -> str:
"""Perform a custom analysis on Sage data."""
df = data_service.query_data(...)
return f"Analysis results: ..."See Custom Functions Guide for the full workflow (fork → add → deploy).
Project layout
sage_mcp.py # 30-line entrypoint; exposes `mcp`
sage_mcp_server/ # FastMCP factory + services
├── server.py # build_server(), main()
├── auth.py # HTTP request auth extraction
├── analytics_service.py # in-memory analytics
├── data_service.py # sage-data-client wrapper
├── docs_helper.py # docs search + FAQ
├── job_service.py # sesctl wrapper
├── job_templates.py # pre-baked plugin job templates
├── models.py # pydantic v2 domain models
├── plugin_generator.py # cookiecutter-style plugin scaffolding
├── plugin_metadata.py # ECR plugin registry
├── plugin_query_service.py # NL plugin query
├── plugin_registry.py # measurement/plugin catalog
├── utils.py # time parsing, timestamp formatting
└── tools/ # MCP tools split by concern
├── sensor_tools.py
├── job_tools.py
├── geo_tools.py
├── plugin_tools.py
├── docs_tools.py
├── prompts.py
└── http_routes.py # /health, /analytics/*, /proxy/imageLicense
MIT. See LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/waggle-sensor/sage-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server