Senu
OfficialClick on "Deploy 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., "@SenuShow me the details for project ID 1."
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.
Senu
Each Rust operation, served as a web page, a JSON API and an MCP tool.
Senu is an experimental Rust seed. A single pure function, list_projects,
is shared by three transports: an HTML page rendered with
Askama, a JSON API on Axum, and
an opt-in MCP tool over stdio built with
the official rmcp SDK. Each
transport checks its own credential, picks a principal on the server side and
calls the same function, so all three return the same projects in the same
order. A test checks that. Senu also serves a small site of Markdown
articles, which holds a beginner course.
Aims (MCS): minimal, consistent, spec-driven
Minimal: application developers should own as little code, configuration and duplication as possible. Today a new article is one Markdown file, and a change of look is classes in a template plus
npm run css.Consistent: one set of conventions for people and AI tools, kept by
rustfmt,clippy -D warningsand, for the stylesheet, a CI drift gate. These check form; they do not prove that the business rules are right.Spec-driven: a spec in docs/specs/ comes first, then tests, then code; the development log maps spec 0004's acceptance criteria to their tests.
Rust is the foundation for its memory and type safety and its speed, but the language alone does not make a program secure or fast. Newer tools, such as Tailwind CSS here, are adopted where they serve these aims. Done: the shared operation on three transports, the article site and the Tailwind UI (spec 0004). Planned, not built: operations declared once for all transports (spec 0005).
Related MCP server: redmine-mcp
New to Rust? Start with the course
The beginner course is a small site that Senu serves on your own computer; it is not published on the web. Install Rust and Git as its installation chapter describes, then run:
cd ~
git clone https://github.com/efg-technologies/senu.git
cd ~/senu
cargo run --quiet -- siteThe first build takes several minutes. Then open http://127.0.0.1:3000/
(English) or http://127.0.0.1:3000/ja/ (Japanese). The course needs no
credentials; Beginner course and senu site
has the details. The rest of this README describes the transport demo.
Status: experimental local demo
Senu is a runnable seed, not a product, and it is not audited security software. It makes no performance or security claims.
Fixed demo data. The catalogue is five projects in two tenants, built into the binary and held in memory. Nothing is stored or persisted, and there are no mutations.
senu servelistens on a loopback address only./projectsuses HTTP Basic and/api/projectsuses a Bearer token. There is no TLS: credentials travel in cleartext over loopback, where other local users can reach them. Basic auth here is not a production login. There are no sessions, login UI, OAuth, users or roles, rate limiting, request logging or audit trail.senu mcp --enable-mcpspeaks MCP over stdio and is off unless that flag is given.SENU_MCP_TOKENmust be set before it starts. Under the MCP authorization spec, stdio servers take credentials from the environment, so this trusts the local process that launches Senu. It does not authenticate any remote party. There is no MCP over HTTP and no OAuth.Mutations need more work. Basic auth is an ambient browser credential, so any future write operation needs CSRF protection and a new spec.
There are no generators or scaffolding, and no published crate: run Senu from a clone of this repository.
Quickstart
You need the Rust toolchain pinned in rust-toolchain.toml (for example
through rustup), bash, curl and python3. From a clone, run
scripts/quickstart.sh. This is the whole script:
#!/usr/bin/env bash
# Senu quickstart: build, call the JSON API and the HTML page, then call the
# MCP tool over stdio and check every reply. Run it from a clone of the
# repository; it needs cargo, curl and python3. The credentials are demo values
# for a loopback-only server; choose your own for anything else.
set -euo pipefail
command -v python3 >/dev/null || { echo "quickstart: python3 is required" >&2; exit 1; }
cargo build --locked --quiet
senu=./target/debug/senu
log=$(mktemp)
SENU_BIND=127.0.0.1:0 SENU_WEB_USER=demo SENU_WEB_PASSWORD=demo-password \
SENU_API_TOKEN=demo-api-token "$senu" serve 2>"$log" &
server=$!
trap 'kill "$server" 2>/dev/null || true; wait "$server" 2>/dev/null || true; rm -f "$log"' EXIT
url=
for _ in $(seq 100); do
url=$(sed -n 's/^senu: listening on //p' "$log")
[ -n "$url" ] && break
sleep 0.1
done
[ -n "$url" ] || { cat "$log" >&2; exit 1; }
echo "== JSON API (Bearer): $url/api/projects?q=launch"
curl -fsS -H 'Authorization: Bearer demo-api-token' "$url/api/projects?q=launch"
echo
echo "== HTML page (Basic): $url/projects?q=launch"
curl -fsS -u demo:demo-password "$url/projects?q=launch" | grep 'data-project-id'
echo "== MCP over stdio: initialize, then tools/call list_projects {\"q\":\"launch\"}"
SENU_MCP_TOKEN=demo-mcp-token python3 scripts/check_mcp.py "$senu" mcp --enable-mcpThe MCP step runs scripts/check_mcp.py, a small client that uses only the
Python standard library. It waits for the initialize reply before calling
the tool, and it fails, killing the server, unless each reply carries the
request's id, no JSON-RPC error and no isError, and the call returns
acme-001 and acme-003. Any failed step makes the quickstart exit non-zero.
The HTML output shows Askama's escaping: the demo project named
<script>alert('x')</script> & "Launch" Notes is rendered as text.
Beginner course and senu site
senu site serves a beginner course for people who know no Rust, written as
Markdown articles in content/en and content/ja and served by Senu itself
on a loopback address:
cargo run --quiet -- siteThen open http://127.0.0.1:3000/ (English) or http://127.0.0.1:3000/ja/ (Japanese). The course starts with a visible page in the browser, has the learner write notes, and then rebuild the reading-time function test first; two optional appendices cover the catalogue, the API and MCP.
Any file named like content/en/51-my-note.md (two digits, a lowercase slug
and .md) becomes the page /my-note the next time cargo run or
cargo build runs: build.rs embeds the files, so the binary reads nothing
at run time and needs a rebuild after every change. The first line must be a
# Title. Raw HTML is shown as text, images are refused, and links must name
an existing page exactly or start with https://; a broken rule stops
startup with the file, the line and the reason. The content is trusted,
written by the repository's authors; this is not a system for untrusted
input. senu site takes only SENU_BIND and refuses every credential
variable; it has no /projects, /api/* or MCP routes.
Spec 0003 describes the
articles, the site and the course; it supersedes the mdBook tutorial of
spec 0002. The site is not published anywhere; CI keeps a static snapshot as
an artifact.
Commands
Command | What it does |
| HTML and JSON on |
| the Markdown articles as HTML on |
| MCP over stdio until stdin closes; exits 0 on EOF |
| usage |
serve needs SENU_WEB_USER (1–64 visible ASCII characters, no :),
SENU_WEB_PASSWORD and SENU_API_TOKEN (1–256 visible ASCII characters).
mcp needs SENU_MCP_TOKEN. All credentials must differ, and unknown
SENU_* variables are rejected. A configuration error exits 2 and names the
variable without printing its value; a bind failure exits 1. Tenants and
roles cannot be configured.
Routes and the MCP tools
GET /projects?q=(Basic): an HTML list with a search form.GET /api/projects?q=(Bearer): JSON{"projects": [{"id", "name"}]}.GET /project?id=(Basic): one project's HTML page.GET /api/project?id=(Bearer): JSON{"id", "name"}.GET /_senu/ui.css(no credentials, also undersenu site): the stylesheet,text/css, served from memory./_senu/is reserved for Senu; every other path below it is 404.MCP tool
list_projects:readOnlyHint: true, optional stringq, no other fields. Results come back asstructuredContentand as JSON text; operation errors areisErrorresults with{"error": code}. An unknown tool gets JSON-RPC error-32602and a method that rmcp does not recognise gets-32601; both carry fixed messages that do not repeat the requested name.MCP tool
get_project:readOnlyHint: true, required stringid, no other fields, with the same results and errors.
id is required: 1 to 32 characters of a-z, 0-9 and -, matched
exactly; any other value is invalid_id (400). An id that is unknown or
belongs to another tenant is not_found (404) on every transport.
q is trimmed, then matched case-insensitively against project names. After
trimming it may hold up to 80 Unicode scalar values and no control characters. Unknown fields are rejected.
Every other path returns 404 and every other method 405. Request bodies
(413) and targets over 2048 bytes (414) are refused, and credentials in the
query string are never read. Responses carry Cache-Control: no-store and
X-Content-Type-Options: nosniff, and errors never repeat input or secrets.
Templates
Templates are Askama .html files with HTML escaping on. askama.toml
configures an ERB-like syntax:
<%# comments %>
<%! for p in projects %>
<li data-project-id="<%= p.id %>"><%= p.name %></li>
<%! endfor %>Blocks use <%! %>, expressions <%= %> and comments <%# %>. Plain <%
for blocks does not work, because Askama rejects a delimiter that is a prefix
of another. A test fails if a template or the configuration uses the safe
filter or turns escaping off. The one exception is the article body, which
is TrustedHtml from the Markdown policy: the same test requires exactly
one HtmlSafe impl in src/, on that type.
Look: Tailwind CSS and Typography
The pages use Tailwind CSS utility classes in
templates/*.html and the official Typography plugin's prose styles for
article text. Change the look by editing classes in a template, or theme
variables for every page in assets/ui.css, the only CSS source:
@theme {
--font-sans: "Trebuchet MS", "Noto Sans JP", sans-serif;
}Tailwind writes rules only for the classes it finds in the templates, into
assets/ui.generated.css. That file is committed and built into the
binary, so Cargo alone builds and runs Senu; Node is needed only to change
the look. With Node 24 and its npm:
npm ci # once: installs the exact versions in package-lock.json
npm run css # after changing classes or the theme
cargo run -- site # or serve, as beforeCommit the source together with assets/ui.generated.css. CI runs
npm ci, npm run css and git diff --exit-code -- assets/ui.generated.css,
and cargo test fails when a template class has no rule. Typography keeps
its own colours, so @theme does not change link colours inside articles;
a prose-a: class on <main> in templates/site_base.html does. The
licenses of both packages are in THIRD-PARTY-NOTICES.txt,
and the stylesheet keeps their notices.
Development
cargo fmt --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked -- --test-threads=2
scripts/quickstart.sh
python3 scripts/check_tutorial.py
python3 scripts/snapshot_site.py target/siteThe tests include tests/quickstart_check.rs, which runs the quickstart's MCP
checker against fake servers, and tests/tutorial_checker.rs, which runs the
own tests of the tutorial checker and of the site snapshot; both need
python3. check_tutorial.py runs
the course in English and then in Japanese, each in a temporary copy of the
last commit, and leaves the working tree unchanged; commit your changes
first, because it refuses to run while tracked files differ from HEAD.
The course's optional last section runs npm ci, so check_tutorial.py
also needs Node 24, npm and access to the npm registry.
snapshot_site.py needs a new or empty directory; it starts senu site,
saves every page as a static file with relative links, the stylesheet and
the notices, and checks them. CI
runs the same commands on GitHub-hosted Ubuntu and uploads the snapshot as an
artifact; nothing is deployed. Read
the specs, the development log and
the research notes for the details.
License
Senu is licensed under Apache-2.0.
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage Pipefork API projects, drafts, connections, secrets, and deployments through MCP.
Read project context and manage tasks in MCPBinder, with access you control.
Project management MCP for AI agents with safe task reads and writes.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides project management capabilities through terminal, web UI, or MCP stdio, including plans, code analysis, and filesystem-safe operations.6 npmMIT
- FlicenseAqualityBmaintenanceProvides read-only MCP tools over stdio for interacting with a Redmine instance, enabling users to list and retrieve projects, issues, queries, wiki pages, and other Redmine resources via the REST API.35-
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to query projects, fetch findings, trigger analysis, upload CycloneDX BOMs, and check async token status against an OWASP Dependency-Track instance via stdio.20 npmMIT
- AlicenseBqualityAmaintenanceEnables MCP clients to manage a project-local tracker by exposing sprint, task, and subtask operations as tools over stdio.19117 PyPIMIT