Expand redMCP safe issue operations and HTTP handling

This commit is contained in:
Jason Thistlethwaite
2026-05-04 09:50:11 -04:00
parent b305544f63
commit 4c931bae1a
8 changed files with 1725 additions and 45 deletions
+127 -7
View File
@@ -45,7 +45,6 @@ $created = $client->createIssue([
]);
$client->updateIssue((int) $created['id'], ['notes' => 'Follow-up note']);
$client->deleteIssue((int) $created['id']);
```
Native Redmine search is exposed separately from issue filtering. Use
@@ -101,6 +100,33 @@ $users = $client->users(['status' => 1, 'limit' => 25]);
$user = $client->user(1, ['include' => 'memberships,groups']);
```
MCP clients that do not know the exact Redmine project identifier should call
`redmine_find_project` first. Redmine identifiers are often slug-like strings
and are not always the same as the display name.
```json
{
"name": "redmine_find_project",
"arguments": {
"query": "Quality Tracker"
}
}
```
Use `matches[0].project_id_to_use` or `recommended_project_id` when it is
non-null in later calls:
```json
{
"name": "redmine_create_issue",
"arguments": {
"project_id": "quality-tracker",
"subject": "Front warehouse deadbolt key gets stuck in lock",
"description": "Problem: The deadbolt on the front door is sticking.\n\n~HermesBot"
}
}
```
`updateIssue()` is intentionally safe by default: on Helpdesk-backed issues, a
normal Redmine note does **not** send an email to the customer. To send through
the Helpdesk plugin, opt in explicitly:
@@ -120,6 +146,58 @@ Use the default non-email update for internal notes, status/category/assignee
changes, and automation cleanup. Use the Helpdesk email path only when the
caller deliberately wants the customer to receive mail.
Issue structure operations are exposed explicitly. Issue create/update preserve
Redmine structure fields such as `parent_issue_id`, `parent_id`,
`category_id`, and `uploads`, so callers can create subtasks, categorize issues,
and attach previously uploaded files without falling through the bundled API
client's sanitized XML helpers.
```php
$upload = $client->uploadAttachment([
'path' => '/tmp/redmine-note.txt',
'content_type' => 'text/plain',
]);
$pdfUpload = $client->uploadAttachment([
'data_url' => 'data:application/pdf;base64,...',
]);
$fileEnvelopeUpload = $client->uploadAttachment([
'file' => [
'name' => 'quote.pdf',
'mime_type' => 'application/pdf',
'data' => 'JVBERi0xLjQK...',
],
]);
$parent = $client->createIssue([
'project_id' => 'fud-nohelpdesk',
'subject' => 'Parent example',
]);
$child = $client->createIssue([
'project_id' => 'fud-nohelpdesk',
'subject' => 'Child example',
'parent_issue_id' => (int) $parent['id'],
'uploads' => [$upload],
]);
$client->createIssueRelation((int) $parent['id'], [
'issue_to_id' => (int) $child['id'],
]);
```
The MCP server exposes explicit tools for issue relations, children/parents,
project issue categories, and attachments. It intentionally does not expose
tools for deleting issues, projects, users, categories, or attachments. The only
removal tool is `redmine_remove_issue_relation`, which unlinks the relationship
only and does not delete either issue.
For MCP attachment uploads, prefer `redmine_upload_attachment` with `path`,
`base64_content`, `data_url`, or a `file` envelope. PDFs and other non-image
files should be passed as file/data URL inputs such as
`data:application/pdf;base64,...`, not as `image_url`.
## MCP server
`redMCP` can run as either a stdio MCP server or a network MCP server. It reads
@@ -129,12 +207,16 @@ Redmine credentials from environment variables or `redMCP/.env`.
redMCP/bin/redmcp-server.php
```
For local network testing, run the Streamable HTTP server:
For local testing, run the Streamable HTTP server:
```sh
MCP_SERVER_TOKEN=test-token redMCP/bin/redmcp-http-server.php --host 0.0.0.0 --port 8765
MCP_SERVER_TOKEN=test-token redMCP/bin/redmcp-http-server.php --port 8765
```
For LAN testing, pass `--host 0.0.0.0` deliberately. Browser-origin requests
from non-localhost origins require `MCP_ALLOWED_ORIGINS` as a comma-separated
allowlist.
Generate a bearer token with:
```sh
@@ -153,10 +235,44 @@ Example Streamable HTTP request:
curl -sS \
-H 'Authorization: Bearer test-token' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
http://127.0.0.1:8765/mcp \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
When the request `Accept` header includes `text/event-stream`, redMCP returns a
short SSE response with one `message` event per JSON-RPC response. Clients that
send only `Accept: application/json` receive the traditional JSON response.
`GET /mcp` returns `405 Method Not Allowed` with `Allow: POST`; redMCP does not
currently expose standalone server-to-client notification streams.
Issue create and update tools accept either canonical nested `fields` or common
issue fields at the top level. These two create calls are equivalent:
```json
{
"name": "redmine_create_issue",
"arguments": {
"fields": {
"project_id": "quality-tracker",
"subject": "Front warehouse deadbolt key gets stuck in lock",
"description": "Problem: The deadbolt on the front door is sticking.\n\n~HermesBot"
}
}
}
```
```json
{
"name": "redmine_create_issue",
"arguments": {
"project_id": "quality-tracker",
"subject": "Front warehouse deadbolt key gets stuck in lock",
"description": "Problem: The deadbolt on the front door is sticking.\n\n~HermesBot"
}
}
```
HTTP server process helpers:
```sh
@@ -195,15 +311,19 @@ Example stdio client configuration:
```
Both transports expose tools for native Redmine project listing/detail, project
memberships, users, filtering/search, issue CRUD, Helpdesk-aware issue reads,
and explicit Helpdesk email responses. Tools that can send customer-visible mail
require an explicit tool call such as `redmine_send_helpdesk_response` or
`redmine_update_issue` with `send_helpdesk_email=true`.
memberships, users, filtering/search, issue create/update, issue relations,
subtasks/parents, project issue categories, attachments, Helpdesk-aware issue
reads, and explicit Helpdesk email responses. Tools that can send
customer-visible mail require an explicit tool call such as
`redmine_send_helpdesk_response` or `redmine_update_issue` with
`send_helpdesk_email=true`.
Run the local no-network query normalizer checks with:
```sh
php redMCP/bin/test-query-normalizer.php
php redMCP/bin/test-redmine-structure.php
php redMCP/bin/test-mcp-http-handler.php
```
## Test instance