From 2f5d90e53f0059e0e4d418bc3eb4846cfb0ca970 Mon Sep 17 00:00:00 2001 From: Nathan Oyler Date: Thu, 7 May 2026 07:29:41 -0700 Subject: [PATCH 1/2] fix: accept non-hyphenated UUIDs and correct install instructions OpenStack Keystone returns project/domain IDs as 32-char hex strings without hyphens (e.g., "2bac466eed364d8a92e477459e908736"). Our UUID validator only accepted the hyphenated format, causing Limes and other tools that take domain_id/project_id to reject valid OpenStack IDs. Also updates README configuration section: Claude Code reads MCP server config from ~/.claude.json (via `claude mcp add`), not from ~/.claude/settings.json which doesn't support mcpServers. --- README.md | 37 +++++++++++++++++++++++---- internal/tools/shared/helpers.go | 4 ++- internal/tools/shared/helpers_test.go | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8b3a86f..7bbfcb3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,15 @@ go install github.com/notque/openstack-mcp-server/cmd/openstack-mcp-server@lates # 2. Store your password in the system keychain (macOS) security add-generic-password -a your-user -s openstack -w "your-password" -# 3. Add to ~/.claude/settings.json (see Configuration below) +# 3. Register as an MCP server (see Configuration below) +claude mcp add openstack openstack-mcp-server \ + -e OS_AUTH_URL=https://identity-3.eu-de-1.cloud.sap/v3 \ + -e OS_USERNAME=your-user \ + -e OS_PW_CMD="security find-generic-password -a your-user -s openstack -w" \ + -e OS_USER_DOMAIN_NAME=your-domain \ + -e OS_PROJECT_NAME=your-project \ + -e OS_PROJECT_DOMAIN_NAME=your-domain \ + -e OS_REGION_NAME=eu-de-1 # 4. Try asking Claude: # "List my servers and their status" @@ -56,9 +64,28 @@ security add-generic-password -a your-user -s openstack -w "your-password" ## Configuration -### Claude Code / Cursor +### Claude Code -Add to your `~/.claude/settings.json`: +Register the MCP server using the CLI: + +```bash +claude mcp add openstack openstack-mcp-server \ + -e OS_AUTH_URL=https://identity-3.eu-de-1.cloud.sap/v3 \ + -e OS_USERNAME=your-user \ + -e OS_PW_CMD="security find-generic-password -a your-user -s openstack -w" \ + -e OS_USER_DOMAIN_NAME=your-domain \ + -e OS_PROJECT_NAME=your-project \ + -e OS_PROJECT_DOMAIN_NAME=your-domain \ + -e OS_REGION_NAME=eu-de-1 +``` + +This writes to `~/.claude.json` which Claude Code reads at session start. Use `--scope project` to scope to a single repo (writes to `.mcp.json`). + +To verify: `claude mcp list` + +### Cursor / Other MCP Clients + +Add to your MCP client's configuration file (e.g., `.cursor/mcp.json`): ```json { @@ -67,8 +94,8 @@ Add to your `~/.claude/settings.json`: "command": "openstack-mcp-server", "env": { "OS_AUTH_URL": "https://identity-3.eu-de-1.cloud.sap/v3", - "OS_USERNAME": "I-number", - "OS_PW_CMD": "security find-generic-password -a I-number -s openstack -w", + "OS_USERNAME": "your-user", + "OS_PW_CMD": "security find-generic-password -a your-user -s openstack -w", "OS_USER_DOMAIN_NAME": "your-domain", "OS_PROJECT_NAME": "your-project", "OS_PROJECT_DOMAIN_NAME": "your-domain", diff --git a/internal/tools/shared/helpers.go b/internal/tools/shared/helpers.go index 650f1b2..4ed5de7 100644 --- a/internal/tools/shared/helpers.go +++ b/internal/tools/shared/helpers.go @@ -13,8 +13,10 @@ import ( ) // uuidPattern validates that a string is a proper UUID format. +// Accepts both hyphenated (8-4-4-4-12) and non-hyphenated (32 hex chars) formats, +// because OpenStack Keystone commonly returns IDs without hyphens. // Used to prevent path traversal attacks via ID parameters in URL construction. -var uuidPattern = regexp.MustCompile(`(?i)^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) +var uuidPattern = regexp.MustCompile(`(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$`) // safePathSegmentPattern validates that a string is a safe URL path segment. // Allows alphanumeric, hyphens, underscores, dots, and forward slashes (for repo paths). diff --git a/internal/tools/shared/helpers_test.go b/internal/tools/shared/helpers_test.go index 9d9f8c4..968af1e 100644 --- a/internal/tools/shared/helpers_test.go +++ b/internal/tools/shared/helpers_test.go @@ -13,6 +13,9 @@ func TestValidateUUID_ValidUUIDs(t *testing.T) { "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "00000000-0000-0000-0000-000000000000", "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + // OpenStack Keystone returns UUIDs without hyphens + "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", + "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3", } for _, uuid := range validUUIDs { if result := ValidateUUID(uuid, "test_id"); result != nil { From cb863536ff1942f591ba79680942a0c9e0a2e1dc Mon Sep 17 00:00:00 2001 From: Nathan Oyler Date: Thu, 7 May 2026 07:40:08 -0700 Subject: [PATCH 2/2] fix: use alternation regex to reject partially-hyphenated UUIDs The -? approach allowed mixed-hyphenation (e.g., "550e8400-e29b41d4...") which would pass validation but confuse users with 404s from OpenStack. Now uses alternation: either fully hyphenated (8-4-4-4-12) OR fully non-hyphenated (32 hex chars). Adds negative test cases for partial hyphenation to document this as explicitly rejected. --- internal/tools/shared/helpers.go | 2 +- internal/tools/shared/helpers_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/tools/shared/helpers.go b/internal/tools/shared/helpers.go index 4ed5de7..909ed26 100644 --- a/internal/tools/shared/helpers.go +++ b/internal/tools/shared/helpers.go @@ -16,7 +16,7 @@ import ( // Accepts both hyphenated (8-4-4-4-12) and non-hyphenated (32 hex chars) formats, // because OpenStack Keystone commonly returns IDs without hyphens. // Used to prevent path traversal attacks via ID parameters in URL construction. -var uuidPattern = regexp.MustCompile(`(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$`) +var uuidPattern = regexp.MustCompile(`(?i)^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{32})$`) // safePathSegmentPattern validates that a string is a safe URL path segment. // Allows alphanumeric, hyphens, underscores, dots, and forward slashes (for repo paths). diff --git a/internal/tools/shared/helpers_test.go b/internal/tools/shared/helpers_test.go index 968af1e..b868845 100644 --- a/internal/tools/shared/helpers_test.go +++ b/internal/tools/shared/helpers_test.go @@ -37,6 +37,8 @@ func TestValidateUUID_InvalidUUIDs(t *testing.T) { {"empty", ""}, {"with spaces", "550e8400 e29b 41d4 a716 446655440000"}, {"newlines", "550e8400-e29b-41d4-a716\n-446655440000"}, + {"partial hyphens", "550e8400-e29b41d4a716446655440000"}, + {"mixed hyphens", "550e8400e29b-41d4-a716-446655440000"}, } for _, tt := range invalidUUIDs { t.Run(tt.name, func(t *testing.T) {