mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
test(gitlab): probe the MCP tool inventory, not just the handshake
The probe asserted an HTTP 200 on initialize and treated that as a working MCP surface. It only proves the path exists and the token is accepted. This role deploys the CE images while GitLab serves MCP through Duo, which is Premium and Ultimate only, so an endpoint that answers but advertises nothing was indistinguishable from a working one. The probe now completes the handshake and calls tools/list, reading an SSE frame when the response is one, and reports the count. A separate assertion fails the deploy on zero tools and names the licence as the reason, which would put this role behind 'blocker: licence' like web-app-openproject. The assertion uses two substring checks rather than regex_search: backslash escaping inside YAML scalars is a trap this repository has been bitten by. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c0f01ab639
commit
c76de1a2d4
2 files changed
+47
-11
No files matched your search
@@ -4,11 +4,33 @@ require 'uri'
|
||||
|
||||
uri = URI("http://#{ENV.fetch('GITLAB_MCP_HOST')}:#{ENV.fetch('GITLAB_MCP_PORT')}#{ENV.fetch('GITLAB_MCP_PATH')}")
|
||||
|
||||
request = Net::HTTP::Post.new(uri)
|
||||
request['Authorization'] = "Bearer #{ENV.fetch('GITLAB_MCP_TOKEN')}"
|
||||
request['Content-Type'] = 'application/json'
|
||||
request['Accept'] = 'application/json, text/event-stream'
|
||||
request.body = JSON.generate(
|
||||
def post(uri, token, payload, session_id)
|
||||
request = Net::HTTP::Post.new(uri)
|
||||
request['Authorization'] = "Bearer #{token}"
|
||||
request['Content-Type'] = 'application/json'
|
||||
request['Accept'] = 'application/json, text/event-stream'
|
||||
request['Mcp-Session-Id'] = session_id if session_id
|
||||
request.body = JSON.generate(payload)
|
||||
Net::HTTP.start(uri.hostname, uri.port, read_timeout: 30, open_timeout: 10) do |http|
|
||||
http.request(request)
|
||||
end
|
||||
end
|
||||
|
||||
def rpc_body(response)
|
||||
raw = response.body.to_s
|
||||
return JSON.parse(raw) if response['Content-Type'].to_s.include?('application/json')
|
||||
|
||||
data = raw.each_line.select { |line| line.start_with?('data:') }
|
||||
.map { |line| line.sub(/\Adata:\s*/, '').strip }
|
||||
.join
|
||||
data.empty? ? {} : JSON.parse(data)
|
||||
rescue JSON::ParserError
|
||||
{}
|
||||
end
|
||||
|
||||
token = ENV.fetch('GITLAB_MCP_TOKEN')
|
||||
|
||||
response = post(uri, token, {
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'initialize',
|
||||
@@ -17,12 +39,15 @@ request.body = JSON.generate(
|
||||
capabilities: {},
|
||||
clientInfo: { name: 'infinito-nexus', version: '1' }
|
||||
}
|
||||
)
|
||||
|
||||
response = Net::HTTP.start(uri.hostname, uri.port, read_timeout: 30, open_timeout: 10) do |http|
|
||||
http.request(request)
|
||||
end
|
||||
}, nil)
|
||||
|
||||
abort("REJECTED #{response.code}") unless response.code.to_i == 200
|
||||
|
||||
puts "ACCEPTED #{response.code}"
|
||||
session_id = response['Mcp-Session-Id']
|
||||
post(uri, token, { jsonrpc: '2.0', method: 'notifications/initialized' }, session_id)
|
||||
|
||||
listing = post(uri, token, { jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} }, session_id)
|
||||
tools = rpc_body(listing).dig('result', 'tools')
|
||||
tools = [] unless tools.is_a?(Array)
|
||||
|
||||
puts "ACCEPTED #{response.code} tools=#{tools.length}"
|
||||
@@ -31,3 +31,14 @@
|
||||
The instance rejected the token stored for {{ application_id }}, so every
|
||||
MCP client would be configured with a bearer {{ GITLAB_MCP_PATH }} answers
|
||||
401 to.
|
||||
|
||||
- name: "🛑 Refuse an instance that authenticates but advertises no MCP tool"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'tools=' in (gitlab_mcp_probe.stdout | default(''))"
|
||||
- "'tools=0' not in (gitlab_mcp_probe.stdout | default(''))"
|
||||
fail_msg: >-
|
||||
{{ GITLAB_MCP_PATH }} accepted the token but advertised no tool. GitLab
|
||||
serves MCP only on Premium and Ultimate, so this Community deployment
|
||||
cannot carry a served MCP surface and the role belongs behind
|
||||
'blocker: licence' instead.
|
||||
Reference in new issue
Block a user