Ramen
This server exposes three pure, stateless utility tools — arithmetic, unit conversion and text counting — for exact results a model would otherwise estimate.
demo_calculator_tool — add, subtract, multiply or divide two numbers (
func,var1,var2) and get a numericresult; division by zero returns a tool error.unit_convert — convert a value between length (mm, cm, m, km, inch, foot, yard, mile), mass (mg, g, kg, tonne, ounce, pound) or temperature (celsius, fahrenheit, kelvin) units; unknown units or cross-family conversions error out.
word_count — count characters (Unicode code points), whitespace-separated words and newline-separated lines of a text, returned as
{characters, words, lines}.
Common traits: stateless with no side effects, no auth beyond the group's key, validated outputs, and clear one-sentence errors for invalid input. No currencies, dates, compound units, tokenisation or multi-operation math.
Provides integration with Google Cloud Platform (GCP) and Google Workspace. Enables deploying MCP workers on GKE, signing in via Google Workspace with group-based role mapping, and using Google Cloud services as part of the deployment infrastructure.
Integrates NVIDIA NeMo Guardrails to apply deterministic or LLM-backed guardrails on tool calls. Guardrails run in the Python runtime next to the tool, with fail-closed behavior by default.
Uses Redis for throttling and rate limiting of MCP calls, enabling per-zone or per-tool limits and other enterprise controls.
Docs: https://bkraad47.github.io/ramen/ · Get started · How it works · The MCP repo · Connect a client with OAuth · Deploy on GCP · AWS · Azure · Contracts · Releases
Streamable HTTP is the front door. Every worker serves
POST /mcp— a URL and a bearer header, nothing to install — next to the gRPC service it has had since 0.3.1, on the same port, through the same guards. The stdio bridge stays for clients that only speak stdio.
Status. Current release 0.8.0, verified live on GKE, EKS and AKS on 2026-10-17 — smoke, the 63-check release suite and six load-test scenarios on each cloud — then torn down; CI proves both transports on Linux and Windows. The single record of what each run covered, and what is tested only, is Transport → What is verified.
Build and deploy your own MCP tools across zones. Ramen turns a git repo of tools, resources and prompts into a fleet of MCP workers behind a cloud load balancer, and keeps who-can-call-what, secrets, guardrails, canary gates and the audit trail in one console. It is a platform for your tools, not a gateway in front of someone else's. Each worker pairs a Rust MCP node (Streamable HTTP and gRPC on one port, bearer auth, IP allow-lists, health, logs) with a Python 3.12 runtime that pip-installs and runs your code: user code never runs in the process that holds the keys. The shape, the data model and why each piece is where it is: How it works.
Git → bucket → worker. Deploy syncs the repo to a bucket; workers load by content hash. No git creds on pods.
Canary by default, gated. Roll a canary, smoke-test
tools/list, diff its tool schemas against stable (breaking changes stop unless you saybreaking: true), run the repo's golden cases, then roll stable. Failure leaves stable untouched.Multi-zone from day one. Group → Environment → Zone → Worker; the load balancer routes on
ramen-group/ramen-zone, so one client config works for every zone.Guardrails inside the worker. Opt a tool into a check before the call and after the result, run in the Python runtime next to it: NVIDIA NeMo Guardrails rails (deterministic or LLM-backed, your model and key) or a
policy.pyof your own. A blocked call is a readable tool error and the access log names the rail. Fail-closed by default.Your identity provider decides, your admins override. Add Microsoft Entra ID, Google Workspace or any OIDC provider on the Config page, on any cloud; read a person's groups from Graph or Cloud Identity at sign-in, map them to roles per group, and keep what admins set by hand.
Roles per group, and roles you define. Group Admin, Viewer, MCP User or a custom role on one of those bases (
analyston Viewer), usable in memberships, provider rules and per-tool access; a role can be OAuth only. Decide per environment who may see and who may call each tool; a drift signal flags a caller repeating one call.Autoscaling by Ramen. Each zone runs between 1 worker and a max that admins set; the console scales up fast under load and back down when quiet, the same on all three clouds, with no HPA and every step in the audit log.
Three clouds. Terraform and Helm for GKE, EKS and AKS, with the same console and guards on each (Deploy to the cloud).
Enterprise controls.
rmk_MCP keys andrmn_API keys stored as salted hashes, IP rules per zone at the node and at the cloud edge, secrets that are never displayed, Redis throttles, an audit line for every action.One Deployment per zone, not one per tool. A worker loads every tool of the group, so thirty tools run on one Deployment per zone (two pods with a canary) behind one load balancer; autoscaling adds pods for load, not for tool count.
Start here · the demo group repo ramen-demo-mcp-group (point a group at it and press Deploy) · the stdio bridge ramen-mcp-bridge on PyPI (
uvx ramen-mcp-bridge@latest) · the images bkraad47/ramen-worker and bkraad47/ramen-console on Docker Hub and GHCR.
Quickstart (local, 5 commands)
Needs Docker with compose v2, uv, git and make. make up pulls the release's two images from Docker Hub,
about a minute; make up BUILD=1 builds them from source instead (three to five minutes).
git clone https://github.com/bkraad47/ramen && cd ramen
make up # Firestore emulator + console https://localhost:8443 + one worker (localhost:8080: Streamable HTTP + gRPC)
make demo # zone, group `demo` from the demo repo, an rmk_ key, a canary deploy, then a tools/call
# PASS: demo_calculator_tool(2,3,add) -> 5 <- the success line
open https://localhost:8443 # self-signed cert; admin@ramen.local + the generated password: grep ADMIN_PASSWORD deploy/local/.env
make down # stop and remove volumesmake demo is safe to re-run: an existing zone, group or environment answers "exists" and a fresh key is
generated each time. Nothing in the stack is a static secret: make up generates the admin password, Fernet key,
MCP key, admin key and session secret into deploy/local/.env (git-ignored), and scripts/check_secrets.py fails CI
if a literal one ever returns.
Related MCP server: MCP Manager
Connect a client
A worker is a URL (/mcp) and two routing headers, ramen-group and ramen-zone. The credential decides whose
name a call runs under:
A group key (
rmk_) inAuthorization: Bearer, for agents and automation. Any HTTP-capable client.A person's sign-in through the console (OAuth 2.1, with a Ramen password, Entra ID or Google): Claude Code over HTTP, or any stdio client through the bridge.
Every form, with the pages you will see: Get started → Connect a client · Connect with OAuth · Transport → Connecting. In a cloud, the group page's How MCP users sign in card prints the exact commands for your install.
Screenshots
Login | Dashboard, load per zone and group |
|
|
Group: environments, deploy jobs, zones/workers, MCP keys | Deploy job with streamed log |
|
|
Secrets (names only, values never shown) | Logs (one JSON line per MCP call, downloadable) |
|
|
Identity providers: Entra ID with group lookup and role rules | Custom roles, one OAuth-only |
|
|
Zone actions: count, max, last autoscale decision | Zones and workers with the max per group |
|
|
Transport and security, in short
Workers speak Streamable HTTP (POST /mcp, MCP spec 2025-06-18) and gRPC (ramen.v1.Mcp/Call) on one port
through one implementation of every guard, so a check is on both transports or on neither. TLS ends at the load
balancer. The node checks the credential, the source range at the provider's x-forwarded-for hop, blocked names
and the size and in-flight caps. The runtime reaches the bucket as the zone's own cloud identity, and guardrails run
between the runtime and your tool. Hop by hop, with what protects each and what is verified:
Transport. Boundaries and non-goals:
Threat model.
Deploy to the cloud
Target | What Terraform and Helm build | Guide |
GCP | GKE Autopilot, Firestore, GCS, Secret Manager, global HTTPS LB (GKE Gateway, header-routed gRPC and Streamable HTTP), Cloud Armor | |
AWS | EKS, DynamoDB, S3, Secrets Manager, ALB (gRPC and HTTP/1.1 target groups), WAF (Terraform or CloudFormation) | |
Azure | AKS (workload identity), Table Storage, Blob Storage, Key Vault, Envoy Gateway with a Let's Encrypt certificate | |
Local | docker compose: Firestore emulator, console, one worker; CI e2e on every push |
Bring-up is the same on each: terraform apply → helm upgrade --install (the release's public images, or
make push to your own registry) → add a zone and a group in the console → Deploy. About 25 minutes, mostly waiting
for the cluster and the load balancer. The edge gets a publicly trusted certificate on a free sslip.io hostname
(Let's Encrypt on AKS), so clients use https://<public_hostname>/mcp and the bridge points at
<public_hostname>:443 --tls with nothing to import. What each cloud run covered:
Transport → What is verified.
Write your own tools
A group repo is any git repo with mcp/tools/<name>/<name>.py + <name>.json (and resources/, prompts/,
requirements.txt). Secrets are referenced as {{$group.NAME}} or exported through mcp/env.yaml; nothing about
the transport leaks into tool code. Declare output and the worker publishes it as outputSchema and validates every
result; add mcp/tests.yaml and your cases gate every deploy; add mcp/guardrails.yaml and NeMo rails or a
policy.py check the arguments before a tool runs and its result before it leaves the worker. Start from
ramen-demo-mcp-group; the contract, local development and heavy
dependencies: The MCP repo ·
Guardrails.
Repository
Dir | What |
FastAPI + Jinja2 + HTMX manager UI and | |
Rust MCP server node (tonic: | |
Python 3.12 runtime (loads protos, pip installs, runs calls, resolves secrets, runs guardrails) | |
| |
compose, Helm charts, Terraform (GCP, AWS, Azure), CloudFormation | |
the self-contained directory image and the Smithery bundle; both pin the bridge | |
Cloud-ops agent skills: deploy-gcp, deploy-aws, rotate-keys, backup-restore, scale-zone | |
Black-box conformance (gRPC + bridge), e2e and cloud suites | |
This site's sources; |
Architecture: ARCHITECTURE.md · Changes: CHANGELOG.md · Versions: tracker · Which versions go together: Releases
Develop
make up BUILD=1 # the compose stack from source instead of the published images
make test # runtime-py and console (pytest, 90% coverage gate) plus node-rs (fmt, clippy, test)
make test-harness # tests/: conformance + e2e (skips without a running stack)
make proto # regenerate Python stubs from proto/ (Rust stubs build via tonic-build)
make demo-worker # node + runtime locally without Docker
uv run --project docs --group docs mkdocs serve # docs at http://127.0.0.1:8000Contributing
See CONTRIBUTING.md — use it, fork it, change it, with attribution; renaming it as a new commercial product of your own is not acceptable.
License
BSD-3-Clause © 2026 Raad. See LICENSE.
Available Tools
3 toolsdemo_calculator_toolA
Add, subtract, multiply or divide two numbers and return the result as a number. Pure and stateless: no side effects, no network, no auth beyond the group's key, no rate limit of its own. Division by zero returns a tool error (isError) with a one-sentence message instead of a value. Use it when an exact arithmetic result matters; for anything beyond one binary operation, chain calls or compute in the model.
| Name | Required | Description | Default |
|---|---|---|---|
| func | Yes | operation to apply as var1 <func> var2 | |
| var1 | Yes | first operand (left-hand side); any finite number | |
| var2 | Yes | second operand (right-hand side); must be non-zero for divide |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes | var1 <func> var2 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: pure/stateless, no side effects, no network, auth only via the group key, no own rate limit, and the division-by-zero path returns isError with a one-sentence message rather than a value. That is exactly the behavioral context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences, front-loaded with purpose, then behavior, then the error case, then routing guidance. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, and the description still covers the one non-obvious edge case (division by zero producing a tool error). Nothing needed to call this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents func's enum and both operands (including the non-zero constraint for divide). The description adds only the 'two numbers' framing and the left/right ordering implied by var1 <func> var2, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb set (add, subtract, multiply, divide) plus the resource (two numbers) and the return type. It is trivially distinguishable from the arithmetic-free siblings unit_convert and word_count.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names when to use it ('when an exact arithmetic result matters') and when not to ('for anything beyond one binary operation, chain calls or compute in the model'). The alternative path is stated, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unit_convertA
Convert a value between units of length (mm, cm, m, km, inch, foot, yard, mile), mass (mg, g, kg, tonne, ounce, pound) or temperature (celsius, fahrenheit, kelvin) and return the converted number. Pure and stateless, no side effects, no auth beyond the group's key. Units are matched case-insensitively by the names listed; an unknown unit or a conversion across families (length to mass) returns a tool error (isError) naming the accepted units. Use it for exact factor-based conversions; it does not handle currencies, dates or compound units such as km/h.
| Name | Required | Description | Default |
|---|---|---|---|
| value | Yes | the quantity to convert, in from_unit | |
| to_unit | Yes | unit of the result, same family as from_unit | |
| from_unit | Yes | unit of value, e.g. km, pound, celsius |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes | value expressed in to_unit |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: pure/stateless, no side effects, no auth beyond the group key, case-insensitive unit matching, and explicit error semantics (tool error with isError naming accepted units on unknown unit or cross-family conversion). That is unusually complete behavioral disclosure for a stateless utility.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and return value in the first sentence, then behavior, then routing guidance. Every clause carries distinct information (units, families, case rules, error behavior, exclusions) with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return formatting need not be elaborated, yet the description still states the result is a converted number. Combined with the auth, statelessness and error details, an agent has everything needed to call this correctly with three required parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description still adds real value by listing the accepted unit names, stating that matching is case-insensitive, and defining the cross-family failure mode for from_unit/to_unit. It stops short of documenting numeric ranges or precision/rounding behavior for value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb (convert) and resource (units of length, mass, temperature) and enumerates the exact accepted unit symbols, so an agent knows precisely what domain this covers. It also carves itself apart from a generic calculator sibling by excluding currencies, dates and compound units like km/h.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use it for exact factor-based conversions; it does not handle currencies, dates or compound units such as km/h" gives an explicit when-to-use plus a concrete when-not-to-use set. Although it does not name the sibling tools, the exclusions are the operationally relevant routing signal and leave nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
word_countA
Count the characters, words and lines of a text and return the three counts as an object. Pure and stateless: the text is not stored or sent anywhere; no auth beyond the group's key. Words are whitespace-separated runs; lines are newline-separated (a trailing newline does not add a line); characters are Unicode code points including whitespace. Use it when a model needs an exact count rather than an estimate; it does not tokenise or detect language.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | the text to count; may be empty, which yields zeros |
Output Schema
| Name | Required | Description |
|---|---|---|
| lines | Yes | newline-separated lines; 0 for an empty text |
| words | Yes | whitespace-separated runs |
| characters | Yes | Unicode code points, whitespace included |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden, and it delivers: 'Pure and stateless: the text is not stored or sent anywhere; no auth beyond the group's key.' It also pins down exact counting semantics (whitespace-separated words, newline-separated lines without a trailing-newline artifact, Unicode code points including whitespace), which is unusually complete for a no-annotation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, front-loaded with what it counts and what it returns, then safety, then precise definitions. Every sentence contributes something an agent cannot get from the schema, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value documentation is unnecessary, yet the description still names the object shape. For a stateless one-parameter utility it covers purpose, selection, safety/auth, and edge-case semantics, leaving nothing an agent needs before calling it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter is already documented, including the empty-string case. The description's word/line/character rules govern how the result is computed rather than adding new meaning to the `text` parameter, so the baseline of 3 for high schema coverage is the right level.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource ('Count the characters, words and lines of a text') plus the exact return shape ('return the three counts as an object'). It is trivially distinguishable from the unrelated siblings demo_calculator_tool and unit_convert, and no other tool in the set could be confused with it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly gives the selection condition ('Use it when a model needs an exact count rather than an estimate') and the when-not cases ('it does not tokenise or detect language'). An agent knows both when to reach for this tool and which related tasks it must not use it for, without any inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.7.1- Changed
demo_calculator_tool4 fields changed- changed
Input schema / properties / func / descriptionPrevious value: -"operation"New value: +"operation to apply as var1 <func> var2" - changed
Input schema / properties / var1 / descriptionPrevious value: -"first operand"New value: +"first operand (left-hand side); any finite number" - changed
Input schema / properties / var2 / descriptionPrevious value: -"second operand"New value: +"second operand (right-hand side); must be non-zero for divide" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "description": "var1 <func> var2", + "type": "number" + } + }, + "required": [ + "result" + ], + "type": "object" +}
- Added
unit_convert - Added
word_count
1 tool update
v0.1.0- First observed
demo_calculator_tool
TDQS
Scored across 3 tools
Each tool targets a completely different operation: arithmetic (demo_calculator_tool), unit conversion (unit_convert), and text counting (word_count). There is no overlap or plausible misselection between them, and each description clearly states its scope and limits.
All names are snake_case and readable, and two follow a clean noun_verb pattern (unit_convert, word_count). The outlier demo_calculator_tool adds an unnecessary 'demo_' prefix and '_tool' suffix, a minor deviation from the otherwise consistent convention.
Three tools is on the thin side for a general-purpose utility server, but each is distinct and clearly earns its place with no filler. It is reasonable if the server is intentionally a small demo/utility set.
Each individual tool is self-contained and complete for its narrow function, but the server has no coherent domain, so there is no lifecycle or CRUD surface to evaluate. As a set it reads as a few unrelated utilities rather than a complete toolkit.
Maintenance
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceA Cloudflare Workers-based MCP server that enables secure remote connections using built-in OAuth authentication via Cloudflare Access. It provides identity-based access control for MCP tools and supports persistent state management through Durable Objects and SSE.-
- AlicenseNot gradedqualityCmaintenanceSelf-hosted MCP proxy and aggregation platform. Register multiple upstream MCP servers and expose them through a single unified endpoint with namespace routing, multi-transport support (HTTP/SSE, stdio, OpenAPI→MCP), per-tool overrides, and a web admin UI.18MIT
- FlicenseNot gradedqualityDmaintenanceDeployable MCP server with Google OAuth for remote connections, enabling authenticated tool access via Cloudflare Workers.-
- FlicenseNot gradedqualityCmaintenanceRemote MCP server with built-in OAuth authentication via Cloudflare Access, enabling secure tool access and user identity-based restrictions.-









