feat: minimal mcp server#316
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThis PR introduces a complete MCP (Model Context Protocol) server that enables Claude Desktop to invoke Mat3ra platform operations. It includes configuration templates, Python packaging setup, core server implementation with tool registration and dispatch logic, and comprehensive documentation for deployment and usage. ChangesMat3ra MCP Server
Sequence DiagramsequenceDiagram
participant Client as MCP Client
participant Server as MCP Server
participant Dispatch as _dispatch Router
participant Mat3raAPI as Mat3ra API Client
Client->>Server: call_tool(tool_name, arguments)
Server->>Dispatch: route by tool_name
Dispatch->>Mat3raAPI: invoke endpoint method (list/get/create)
Mat3raAPI-->>Dispatch: return result or raise exception
Dispatch-->>Server: structured response or error
Server-->>Client: TextContent(formatted result)
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@mcp_server/pyproject.toml`:
- Around line 11-13: The console script currently points to the async function
main(), which will be invoked without awaiting; add a synchronous wrapper
function (e.g., define def cli(): import asyncio and call asyncio.run(main()))
in mcp_server.server and update the project script entry so mat3ra-mcp points to
mcp_server.server:cli instead of mcp_server.server:main; ensure the new cli
wraps the existing async def main() and is exported for the entry point.
In `@mcp_server/README.md`:
- Around line 31-32: The README currently instructs users to set ACCOUNT_ID and
AUTH_TOKEN but the server expects MAT3RA_ACCOUNT_ID and MAT3RA_AUTH_TOKEN;
update the docs to use the exact environment variable names MAT3RA_ACCOUNT_ID
and MAT3RA_AUTH_TOKEN (and mention where to obtain their values) so they match
the code that reads MAT3RA_ACCOUNT_ID / MAT3RA_AUTH_TOKEN.
- Line 35: Replace the brittle shell example "export $(cat mcp_server/.env |
grep -v '^#' | xargs)" with a safe, whitespace-preserving pattern: enable
automatic export, source the file, then disable automatic export (e.g. use "set
-a; source mcp_server/.env; set +a"), or alternatively show a robust while-read
loop that ignores comments and preserves spacing; update the README to use one
of these safer approaches and ensure the exact original command string is
removed.
In `@mcp_server/server.py`:
- Around line 54-57: Remove the duplicate/unused mcp.server.stdio import: keep a
single import of mcp.server.stdio at the top-level and delete the redundant
import inside main(); ensure you still import Server and InitializationOptions
from mcp.server and mcp.server.models respectively, and update any references to
stdio to use the single retained import so lint F401/F811 are resolved.
- Around line 263-267: The handler currently uses q.setdefault("owner._id",
owner) which allows a client-supplied owner._id to persist; replace those
setdefault calls with an unconditional assignment q["owner._id"] = owner to
enforce server-side owner scoping (locate the q dict created from
args.get("query", {}), the q.setdefault("owner._id", owner) occurrences and
change them to q["owner._id"] = owner). Apply the same change for the other
similar occurrences mentioned so that any client-provided owner._id is
overridden by the authenticated owner before creating MaterialEndpoints or
executing the query.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6aa7ad52-a8f0-41ef-b46b-a5bbbf18d018
📒 Files selected for processing (4)
mcp_server/.env.examplemcp_server/README.mdmcp_server/pyproject.tomlmcp_server/server.py
| [project.scripts] | ||
| mat3ra-mcp = "mcp_server.server:main" | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify entrypoint target and callable type in repo sources
rg -n '^\[project\.scripts\]|mat3ra-mcp\s*=|^async def main|^def cli' mcp_server/pyproject.toml mcp_server/server.pyRepository: Exabyte-io/api-examples
Length of output: 224
🏁 Script executed:
#!/bin/bash
# Search for any cli wrapper function and check full context around main
rg -A 5 -B 2 'def cli|def main' mcp_server/server.pyRepository: Exabyte-io/api-examples
Length of output: 336
Console script points to an async function and won't run correctly.
mat3ra-mcp = "mcp_server.server:main" targets async def main() (line 313), so the entry point will call it without awaiting. Expose a sync wrapper (e.g., def cli(): asyncio.run(main())) and point the script to that wrapper.
Suggested change
# mcp_server/server.py
+def cli() -> None:
+ import asyncio
+ asyncio.run(main())
# mcp_server/pyproject.toml
[project.scripts]
-mat3ra-mcp = "mcp_server.server:main"
+mat3ra-mcp = "mcp_server.server:cli"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@mcp_server/pyproject.toml` around lines 11 - 13, The console script currently
points to the async function main(), which will be invoked without awaiting; add
a synchronous wrapper function (e.g., define def cli(): import asyncio and call
asyncio.run(main())) in mcp_server.server and update the project script entry so
mat3ra-mcp points to mcp_server.server:cli instead of mcp_server.server:main;
ensure the new cli wraps the existing async def main() and is exported for the
entry point.
| q: dict = args.get("query", {}) | ||
| if formula := args.get("formula"): | ||
| q["formula"] = formula | ||
| q.setdefault("owner._id", owner) | ||
| endpoint = MaterialEndpoints(*ep) |
There was a problem hiding this comment.
Enforce owner scoping instead of allowing query override.
Using setdefault("owner._id", owner) lets client input keep a custom owner._id value. Force this field to the authenticated owner to prevent cross-owner query attempts.
Suggested change
if name == "list_materials":
q: dict = args.get("query", {})
if formula := args.get("formula"):
q["formula"] = formula
- q.setdefault("owner._id", owner)
+ q["owner._id"] = owner
endpoint = MaterialEndpoints(*ep)
return endpoint.list(q)
@@
if name == "list_workflows":
q = args.get("query", {})
- q.setdefault("owner._id", owner)
+ q["owner._id"] = owner
endpoint = WorkflowEndpoints(*ep)
return endpoint.list(q)
@@
if name == "list_jobs":
q = args.get("query", {})
- q.setdefault("owner._id", owner)
+ q["owner._id"] = owner
endpoint = JobEndpoints(*ep)
return endpoint.list(q)Also applies to: 279-280, 285-286
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@mcp_server/server.py` around lines 263 - 267, The handler currently uses
q.setdefault("owner._id", owner) which allows a client-supplied owner._id to
persist; replace those setdefault calls with an unconditional assignment
q["owner._id"] = owner to enforce server-side owner scoping (locate the q dict
created from args.get("query", {}), the q.setdefault("owner._id", owner)
occurrences and change them to q["owner._id"] = owner). Apply the same change
for the other similar occurrences mentioned so that any client-provided
owner._id is overridden by the authenticated owner before creating
MaterialEndpoints or executing the query.
Summary by CodeRabbit