docs: add read-only mode specification, architecture, and ADR
- Specify OPENPROJECT_READ_ONLY and --read-only in docs/ARCHITECTURE.md - Add ADR-011 in docs/DECISIONS.md for tool manifest filtering and execution guards - Update docs/TODO.md with read-only implementation tasks - Update README.md, AGENTS.md, and .env.example with read-only configuration reference
This commit is contained in:
@@ -22,3 +22,4 @@ SECRET_KEY_BASE=openproject-secret-key-base-for-local-testing-at-least-64-charac
|
|||||||
# OPENPROJECT_API_KEY will be automatically written to .env.test
|
# OPENPROJECT_API_KEY will be automatically written to .env.test
|
||||||
OPENPROJECT_BASE_URL=http://localhost:8080
|
OPENPROJECT_BASE_URL=http://localhost:8080
|
||||||
OPENPROJECT_API_KEY=
|
OPENPROJECT_API_KEY=
|
||||||
|
OPENPROJECT_READ_ONLY=false
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ Welcome to the `openproject-mcp` project. This document defines operating princi
|
|||||||
- **Secrets Management**: Read `OPENPROJECT_BASE_URL` and `OPENPROJECT_API_KEY` from environment variables. Never hardcode credentials in tests, scripts, or examples.
|
- **Secrets Management**: Read `OPENPROJECT_BASE_URL` and `OPENPROJECT_API_KEY` from environment variables. Never hardcode credentials in tests, scripts, or examples.
|
||||||
- **Input Validation**: All tool parameters must be validated via Zod schemas before being used in HTTP requests.
|
- **Input Validation**: All tool parameters must be validated via Zod schemas before being used in HTTP requests.
|
||||||
- **Safe Error Propagation**: Sanitize error messages returned to the MCP client. Never leak authorization headers or system paths in tool error responses.
|
- **Safe Error Propagation**: Sanitize error messages returned to the MCP client. Never leak authorization headers or system paths in tool error responses.
|
||||||
|
- **Read-Only Mode Enforcement**: Support `OPENPROJECT_READ_ONLY=true` and `--read-only`. When active, mutating tools must not be registered in the MCP tool manifest and must be rejected by execution guards with `SERVER_READ_ONLY`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -126,10 +126,23 @@ The MCP server accepts configuration through environment variables or a `.env` f
|
|||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| `OPENPROJECT_BASE_URL` | Base URL of the OpenProject instance | `http://localhost:8080` |
|
| `OPENPROJECT_BASE_URL` | Base URL of the OpenProject instance | `http://localhost:8080` |
|
||||||
| `OPENPROJECT_API_KEY` | Personal API token (created under My Account > Access tokens) | - |
|
| `OPENPROJECT_API_KEY` | Personal API token (created under My Account > Access tokens) | - |
|
||||||
|
| `OPENPROJECT_READ_ONLY` | Run server in read-only mode (`true` / `false` or `--read-only`) | `false` |
|
||||||
| `PORT` | Local port for the Docker Compose web container | `8080` |
|
| `PORT` | Local port for the Docker Compose web container | `8080` |
|
||||||
| `TAG` | OpenProject container image tag | `17-slim` |
|
| `TAG` | OpenProject container image tag | `17-slim` |
|
||||||
| `POSTGRES_VERSION` | PostgreSQL container image tag | `17` |
|
| `POSTGRES_VERSION` | PostgreSQL container image tag | `17` |
|
||||||
|
|
||||||
|
### Read-Only Mode
|
||||||
|
|
||||||
|
To ensure AI assistants cannot make any modifications to your OpenProject instance, enable read-only mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OPENPROJECT_READ_ONLY=true bun run src/index.ts
|
||||||
|
# or
|
||||||
|
bun run src/index.ts --read-only
|
||||||
|
```
|
||||||
|
|
||||||
|
When active, write tools are omitted from the tool manifest and blocked by execution guards.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Development Roadmap
|
## Development Roadmap
|
||||||
|
|||||||
@@ -71,8 +71,15 @@ Domain services map MCP tool calls to concrete business logic and OpenProject AP
|
|||||||
- Parses and validates configuration at startup:
|
- Parses and validates configuration at startup:
|
||||||
- `OPENPROJECT_BASE_URL`: Base URL of the OpenProject instance (e.g., `https://openproject.example.com`).
|
- `OPENPROJECT_BASE_URL`: Base URL of the OpenProject instance (e.g., `https://openproject.example.com`).
|
||||||
- `OPENPROJECT_API_KEY`: User API key generated in OpenProject under "My Account > Access tokens".
|
- `OPENPROJECT_API_KEY`: User API key generated in OpenProject under "My Account > Access tokens".
|
||||||
|
- `OPENPROJECT_READ_ONLY`: Boolean flag (`true` / `false`, default: `false`). Can also be set via `--read-only` CLI argument.
|
||||||
- Fails fast with actionable setup advice if required credentials are missing or invalid.
|
- Fails fast with actionable setup advice if required credentials are missing or invalid.
|
||||||
|
|
||||||
|
### 2.5. Read-Only Execution Mode
|
||||||
|
The server supports a dedicated read-only operating mode designed for auditing, reporting, and exploratory agent workflows:
|
||||||
|
- **Tool Filtering**: When read-only mode is active, only read/browse/query tools are registered in the MCP tool registry. Mutating tools (creating work packages, updating attributes, logging time) are completely excluded from the tool manifest exposed to the LLM.
|
||||||
|
- **Defense-in-Depth Guard**: In addition to tool manifest omission, the internal tool router validates whether an operation is a mutating action. Any write attempt is immediately rejected with a structured error (`SERVER_READ_ONLY: Server is operating in read-only mode. Write operations are disabled.`).
|
||||||
|
- **Safe Auditing**: Guarantees zero unintentional data modification or state changes in the OpenProject instance.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Tool Specifications (Browse & Query Scope)
|
## 3. Tool Specifications (Browse & Query Scope)
|
||||||
@@ -125,6 +132,7 @@ The client layer provides helper builders so tools can accept friendly parameter
|
|||||||
2. **Permission Boundary**: The MCP server operates with the exact permission scope of the provided API key. No elevated access is granted beyond what the user possesses in OpenProject.
|
2. **Permission Boundary**: The MCP server operates with the exact permission scope of the provided API key. No elevated access is granted beyond what the user possesses in OpenProject.
|
||||||
3. **Input Sanitization**: All arguments from the LLM are validated via Zod schemas prior to making network requests, preventing request-smuggling or path traversal.
|
3. **Input Sanitization**: All arguments from the LLM are validated via Zod schemas prior to making network requests, preventing request-smuggling or path traversal.
|
||||||
4. **Transport Isolation**: The default stdio transport communicates only through standard input/output with the host client process, with no exposed network ports.
|
4. **Transport Isolation**: The default stdio transport communicates only through standard input/output with the host client process, with no exposed network ports.
|
||||||
|
5. **Read-Only Enforcement**: Configurable read-only mode (`OPENPROJECT_READ_ONLY=true`) guarantees zero write side-effects on the OpenProject instance when operating in exploratory or untrusted agent sessions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -234,4 +234,29 @@ Adopt **Bun** (>= 1.2 / 1.3) as the exclusive runtime, package manager, and test
|
|||||||
- **Negative**:
|
- **Negative**:
|
||||||
- Requires the host machine to have Bun installed rather than traditional Node.js/npm.
|
- Requires the host machine to have Bun installed rather than traditional Node.js/npm.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-011: Configurable Read-Only Execution Mode
|
||||||
|
|
||||||
|
### Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
### Context
|
||||||
|
When integrating MCP servers with LLMs, users and administrators require granular safety boundaries. In many scenarios (e.g., exploratory analysis, code reviews, status summaries, onboarding, or automated audit agents), users want to grant the LLM visibility into OpenProject projects, work packages, and queries without granting write permissions that could accidentally modify tickets or corrupt project state.
|
||||||
|
|
||||||
|
### Decision
|
||||||
|
Implement an explicit, configurable **Read-Only Mode**:
|
||||||
|
1. Enabled via the `OPENPROJECT_READ_ONLY=true` environment variable or `--read-only` command-line argument.
|
||||||
|
2. **Tool Manifest Level**: When read-only mode is active, mutating tools (Phase 2 write tools such as create/update work package) are entirely omitted from the `ListTools` response sent to the MCP client. The LLM only sees browsing and query tools.
|
||||||
|
3. **Execution Guard Level**: As defense-in-depth, the server's tool execution dispatcher checks the read-only flag before invoking any tool marked as mutating, immediately rejecting write attempts with `SERVER_READ_ONLY`.
|
||||||
|
|
||||||
|
### Consequences
|
||||||
|
- **Positive**:
|
||||||
|
- Complete safety for users wanting strictly observational LLM capabilities.
|
||||||
|
- Zero token waste exposing mutation tool definitions that the agent is forbidden from using.
|
||||||
|
- Clean separation between queries and commands.
|
||||||
|
- **Negative**:
|
||||||
|
- Tools must explicitly declare whether they are mutating or read-only during registration.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -45,7 +45,7 @@
|
|||||||
## 2. Next Up (Phase 1: Read/Browse Implementation)
|
## 2. Next Up (Phase 1: Read/Browse Implementation)
|
||||||
|
|
||||||
- [ ] **Task 1: OpenProject REST API v3 Client Layer**
|
- [ ] **Task 1: OpenProject REST API v3 Client Layer**
|
||||||
- [ ] Implement `src/config/index.ts` to parse and validate `OPENPROJECT_BASE_URL` and `OPENPROJECT_API_KEY` via Zod.
|
- [ ] Implement `src/config/index.ts` to parse and validate `OPENPROJECT_BASE_URL`, `OPENPROJECT_API_KEY`, and `OPENPROJECT_READ_ONLY` (or `--read-only`) via Zod.
|
||||||
- [ ] Implement `src/client/api-client.ts` with native `fetch`, HTTP Basic Auth injection, error normalization, and rate/timeout handling.
|
- [ ] Implement `src/client/api-client.ts` with native `fetch`, HTTP Basic Auth injection, error normalization, and rate/timeout handling.
|
||||||
- [ ] Implement `src/client/hal-parser.ts` to unpack HAL+JSON (`_links`, `_embedded`) into clean, token-efficient JSON models.
|
- [ ] Implement `src/client/hal-parser.ts` to unpack HAL+JSON (`_links`, `_embedded`) into clean, token-efficient JSON models.
|
||||||
- [ ] Implement `src/client/filter-builder.ts` to translate LLM-friendly filter arguments into OpenProject's JSON filter syntax.
|
- [ ] Implement `src/client/filter-builder.ts` to translate LLM-friendly filter arguments into OpenProject's JSON filter syntax.
|
||||||
@@ -73,8 +73,10 @@
|
|||||||
|
|
||||||
- [ ] **Task 4: MCP Stdio Server Assembly & Integration**
|
- [ ] **Task 4: MCP Stdio Server Assembly & Integration**
|
||||||
- [ ] Wire domain tools into `@modelcontextprotocol/sdk` Server using `StdioServerTransport` in `src/server.ts` and `src/index.ts`.
|
- [ ] Wire domain tools into `@modelcontextprotocol/sdk` Server using `StdioServerTransport` in `src/server.ts` and `src/index.ts`.
|
||||||
|
- [ ] Implement read-only mode tool filtering (omits mutating tools when `readOnly` is enabled) and execution guard (`SERVER_READ_ONLY`).
|
||||||
- [ ] Ensure all logging is strictly redirected to `stderr`.
|
- [ ] Ensure all logging is strictly redirected to `stderr`.
|
||||||
- [ ] Add end-to-end integration tests in `tests/mcp-server.test.ts` verifying tool calling against the running OpenProject 17 instance.
|
- [ ] Add end-to-end integration tests in `tests/mcp-server.test.ts` verifying tool calling against the running OpenProject 17 instance.
|
||||||
|
- [ ] Add read-only mode verification tests in `tests/read-only.test.ts`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user