Resolve human project names in MCP project_id args

Auto-resolve project_id values that look like human names to canonical project identifiers when there is a clear match. Return actionable guidance with candidate slugs when ambiguous, and cover the behavior with structure tests and docs updates.
This commit is contained in:
Jason Thistlethwaite
2026-05-06 05:00:45 -04:00
parent 22c8e915e9
commit a7d23cd79a
4 changed files with 147 additions and 10 deletions
+41
View File
@@ -78,6 +78,8 @@ final class RedmineStructureTest
$this->testMcpFindProjectRecommendsExactIdentifier();
$this->testMcpFindProjectRecommendsExactName();
$this->testMcpFindProjectLeavesAmbiguousMatchesUnrecommended();
$this->testMcpGetProjectResolvesHumanProjectNameToIdentifier();
$this->testMcpGetProjectShowsHelpfulErrorForAmbiguousHumanName();
$this->testMcpSearchSanitizesNoisyTextFields();
$this->testMcpSearchCanDisableTextSanitization();
$this->testCreateRelationDefaultsToRelatesAndRequiresTarget();
@@ -241,6 +243,45 @@ final class RedmineStructureTest
$this->assertSame('quality-archive', $result['matches'][1]['identifier'], 'second ambiguous match is returned');
}
private function testMcpGetProjectResolvesHumanProjectNameToIdentifier(): void
{
$http = new RecordingClient();
$http->queueJson(['projects' => $this->projectFixtures()]);
$http->queueJson(['project' => ['id' => 78, 'identifier' => 'quality-tracker', 'name' => 'Quality Tracker']]);
$dispatcher = new McpDispatcher(new RedmineClient($http));
$result = $this->callToolJson($dispatcher, 'redmine_get_project', ['project_id' => 'Quality Tracker']);
$this->assertSame(78, $result['id'], 'human project name resolves to expected project');
$this->assertSame('/projects/quality-tracker.json', $http->requests[1]['path'], 'resolved project lookup uses project identifier slug');
}
private function testMcpGetProjectShowsHelpfulErrorForAmbiguousHumanName(): void
{
$http = new RecordingClient();
$http->queueJson(['projects' => $this->projectFixtures()]);
$dispatcher = new McpDispatcher(new RedmineClient($http));
$response = $dispatcher->handleMessage([
'jsonrpc' => '2.0',
'id' => 1,
'method' => 'tools/call',
'params' => [
'name' => 'redmine_get_project',
'arguments' => [
'project_id' => 'Quality',
],
],
]);
if (!is_array($response) || !isset($response['error']) || !is_array($response['error'])) {
throw new RuntimeException('Expected ambiguous project name to produce an MCP error.');
}
$message = (string) ($response['error']['message'] ?? '');
$this->assertStringContains('redmine_find_project', $message, 'ambiguous project error points to resolver tool');
$this->assertStringContains('quality-tracker', $message, 'ambiguous project error provides possible identifier matches');
}
private function testMcpSearchSanitizesNoisyTextFields(): void
{
$http = new RecordingClient();