Skip to main content
Glama
mieweb

redmine-mcp-proxy

by mieweb

redmine-mcp-proxy

A Streamable HTTP → stdio bridge that turns a stdio Model Context Protocol Redmine server into a multi-user, network-reachable MCP endpoint — with per-user impersonation baked in.

Point your AI platform (Ozwell, Claude, etc.) at a single URL:

https://rm.os.mieweb.org/mcp

…and every logged-in user transparently acts as themselves in Redmine — no shared service account, no per-user API keys, no prompt-injectable identity.


Why this exists

MCP Redmine servers speak stdio (one process, one identity). That's great for a single desktop user, but it doesn't work when:

  • Many people share one AI assistant, and each must act as their own Redmine user (correct "assigned to me", correct author, correct permissions).

  • The MCP client is a hosted web platform that can only reach an HTTP URL, not a local subprocess.

This proxy solves both: it exposes Streamable HTTP on /mcp, and for every session it spawns a dedicated backend subprocess locked to the requesting user's identity.


Related MCP server: Redmine MCP Server

How the Proxy works

sequenceDiagram
    participant AI as AI Platform (Ozwell / Claude)
    participant Proxy as redmine-mcp-proxy (HTTP :80/mcp)
    participant Sub as redmine-mcp-rs subprocess (stdio)
    participant RM as Redmine REST API

    AI->>Proxy: POST /mcp (initialize)<br/>x-ozwell-user-email: rgara@mieweb.com
    Note over Proxy: resolveUser() extracts identity
    Proxy->>Sub: spawn(redmine-mcp-rs)<br/>REDMINE_ON_BEHALF_OF=rgara@mieweb.com<br/>REDMINE_LOCK_ON_BEHALF_OF=1
    Proxy-->>AI: mcp-session-id: <uuid>
    AI->>Proxy: POST /mcp (tools/call)<br/>mcp-session-id: <uuid>
    Proxy->>Sub: forward JSON-RPC (stdin)
    Sub->>RM: GET /issues.json<br/>X-Redmine-Switch-User: rgara
    RM-->>Sub: issues visible to rgara
    Sub-->>Proxy: JSON-RPC result (stdout)
    Proxy-->>AI: Streamable HTTP response

Step by step:

  1. Identity resolution. On the first request of a session, resolveUser(req) determines who the caller is, in priority order:

    Priority

    Source

    Sent by

    1

    x-redmine-user header

    Explicit override

    2

    x-ozwell-user-email header

    Ozwell AI platform (auto)

    3

    x-ozwell-user-name header

    Ozwell AI platform (auto)

    4

    ?user= query parameter

    Manual / testing

    5

    Basic-auth username

    Clients that send credentials

    No identity → HTTP 401. Nobody gets an anonymous or admin session by accident.

  2. Per-session subprocess. A new session spawns one backend MCP process (the Rust redmine-mcp-rs binary) with the environment locked to that user:

    env: {
      REDMINE_URL,
      REDMINE_API_KEY,                 // admin key, server-side only
      REDMINE_ON_BEHALF_OF: onBehalfOf, // the resolved user
      REDMINE_LOCK_ON_BEHALF_OF: "1",   // AI cannot override this
    }

    REDMINE_LOCK_ON_BEHALF_OF=1 is the security keystone: even if the model is tricked ("act as the admin", "switch to user X"), the backend ignores any per-call identity and stays pinned to the header-derived user.

  3. Bidirectional bridge. The proxy wires the two transports together: StreamableHTTPServerTransport (facing the AI) ⇄ StdioClientTransport (facing the subprocess). Messages are forwarded verbatim in both directions, so tool names and arguments pass through unchanged.

  4. Session tracking. Each session is keyed by the mcp-session-id header. Subsequent requests reuse the same subprocess; closing either transport tears down the pair and frees the process.

  5. Impersonation on the wire. The backend translates REDMINE_ON_BEHALF_OF into Redmine's X-Redmine-Switch-User header on every REST call, so Redmine enforces that user's real permissions and ownership.


Configuration

Create /opt/redmine-mcp-proxy/.env (chmod 600 — it holds the admin key):

PORT=80
REDMINE_URL=https://pm.mieweb.com/
REDMINE_API_KEY=<redmine-admin-api-key>

Variable

Required

Description

PORT

no

Listen port (default 80)

REDMINE_URL

yes

Redmine base URL

REDMINE_API_KEY

yes

Admin API key (needed for impersonation)

The path to the backend binary is set in server.mjs (the command: field of the StdioClientTransport). Swap it for node index.js to use the Node redmine-mcp server instead of the Rust one.


Run

npm install
node server.mjs

As a systemd service

/etc/systemd/system/redmine-mcp-proxy.service:

[Unit]
Description=Redmine MCP Streamable HTTP proxy
After=network.target

[Service]
Type=simple
User=pralambomanarivo
EnvironmentFile=/opt/redmine-mcp-proxy/.env
ExecStart=/usr/bin/node /opt/redmine-mcp-proxy/server.mjs
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now redmine-mcp-proxy

Binding to port 80 as a non-root user requires sudo setcap cap_net_bind_service=+ep "$(command -v node)".


Endpoints

Method

Path

Purpose

GET

/health

{ status, sessions, users } liveness + who's on

POST/GET

/mcp

Streamable HTTP MCP endpoint

DELETE

/mcp

Terminate a session (mcp-session-id header)

curl -s https://rm.os.mieweb.org/health
# {"status":"ok","sessions":1,"users":["rgara@mieweb.com"]}

Security model

  • Admin key never leaves the server. The AI platform only sends a user identity header; the key lives in .env and is injected into the subprocess.

  • Locked impersonation. REDMINE_LOCK_ON_BEHALF_OF=1 makes identity header-driven and prompt-injection-proof.

  • Fail-closed. No resolvable identity → 401, never an anonymous/admin fallback.

  • Least privilege on the wire. Redmine evaluates every request against the impersonated user's actual permissions.


  • redmine-mcp-rs — the Rust MCP backend this proxy spawns (single static binary, fast startup).

  • redmine-mcp — the original Node MCP server (fully compatible backend).

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    Model Context Protocol (MCP) server for Redmine that provides comprehensive access to the Redmine REST API, enabling users to operate Redmine from MCP clients such as Claude Desktop.
    90
    44
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    MCP server for Redmine project management, enabling tools for managing projects, issues, users, time entries, groups, memberships, versions, wiki, news, attachments, search, and Agile sprints via the Redmine REST API.
    89
    8
    1
    MIT
  • A
    license
    C
    quality
    B
    maintenance
    Model Context Protocol (MCP) server for Redmine that provides comprehensive access to the Redmine REST API. It allows you to operate Redmine from MCP clients such as Claude Desktop.
    90
    302
    21
    MIT

View all related MCP servers

Related MCP Connectors

  • An authenticated remote MCP server for user-owned devices and one-shot capability invocation.

  • Remote MCP server for full read/write access to a Zotero library

  • A MCP server built for developers enabling Git based project management with project and personal…

View all MCP Connectors

Latest Blog Posts

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/mieweb/redmine-mcp-proxy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server