Skip to content

Commit 4565eab

Browse files
authored
DA-1055 investigate automated testing for tools mcp server (#86)
1 parent f599b5f commit 4565eab

File tree

13 files changed

+2467
-5
lines changed

13 files changed

+2467
-5
lines changed

.github/workflows/test.yml

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
name: Tests
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
push:
8+
branches: [main]
9+
pull_request:
10+
branches: [main]
11+
12+
env:
13+
# Default test credentials for Couchbase
14+
CB_USERNAME: Administrator
15+
CB_PASSWORD: password
16+
CB_MCP_TEST_BUCKET: travel-sample
17+
18+
jobs:
19+
# ============================================
20+
# Integration Tests - All Transport Modes
21+
# ============================================
22+
integration-tests:
23+
name: Integration (${{ matrix.transport }} transport)
24+
runs-on: ubuntu-latest
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
transport: ["stdio", "http", "sse"]
29+
30+
services:
31+
couchbase:
32+
image: couchbase:enterprise-8.0.0
33+
ports:
34+
- 8091:8091
35+
- 8092:8092
36+
- 8093:8093
37+
- 8094:8094
38+
- 8095:8095
39+
- 8096:8096
40+
- 9102:9102
41+
- 11210:11210
42+
- 11207:11207
43+
options: >-
44+
--health-cmd "curl -s http://localhost:8091/pools || exit 1"
45+
--health-interval 10s
46+
--health-timeout 5s
47+
--health-retries 30
48+
49+
steps:
50+
- name: Checkout code
51+
uses: actions/checkout@v4
52+
53+
- name: Install uv
54+
uses: astral-sh/setup-uv@v4
55+
with:
56+
version: "latest"
57+
58+
- name: Set up Python (latest)
59+
run: uv python install 3.13
60+
61+
- name: Install dependencies
62+
run: uv sync --extra dev
63+
64+
- name: Wait for Couchbase to be ready
65+
run: |
66+
echo "Waiting for Couchbase to be fully ready..."
67+
for i in {1..60}; do
68+
if curl -s http://localhost:8091/pools > /dev/null 2>&1; then
69+
echo "Couchbase REST API is responding"
70+
break
71+
fi
72+
echo "Waiting for Couchbase... ($i/60)"
73+
sleep 2
74+
done
75+
76+
- name: Initialize Couchbase cluster
77+
run: |
78+
echo "Initializing Couchbase cluster..."
79+
80+
# Initialize node
81+
curl -s -X POST http://localhost:8091/nodes/self/controller/settings \
82+
-d 'path=/opt/couchbase/var/lib/couchbase/data' \
83+
-d 'index_path=/opt/couchbase/var/lib/couchbase/data'
84+
85+
# Set up services
86+
curl -s -X POST http://localhost:8091/node/controller/setupServices \
87+
-d 'services=kv,n1ql,index,fts'
88+
89+
# Set memory quotas
90+
curl -s -X POST http://localhost:8091/pools/default \
91+
-d 'memoryQuota=512' \
92+
-d 'indexMemoryQuota=256' \
93+
-d 'ftsMemoryQuota=256'
94+
95+
# Set credentials
96+
curl -s -X POST http://localhost:8091/settings/web \
97+
-d "password=${{ env.CB_PASSWORD }}" \
98+
-d "username=${{ env.CB_USERNAME }}" \
99+
-d 'port=SAME'
100+
101+
echo "Cluster initialization complete"
102+
103+
- name: Create test bucket
104+
run: |
105+
echo "Creating test bucket..."
106+
107+
# Wait for cluster to be fully initialized
108+
sleep 5
109+
110+
# Create travel-sample bucket (or a test bucket)
111+
curl -s -X POST http://localhost:8091/pools/default/buckets \
112+
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
113+
-d 'name=${{ env.CB_MCP_TEST_BUCKET }}' \
114+
-d 'bucketType=couchbase' \
115+
-d 'ramQuota=256' \
116+
-d 'flushEnabled=1'
117+
118+
# Wait for bucket to be ready
119+
echo "Waiting for bucket to be ready..."
120+
for i in {1..30}; do
121+
if curl -s -u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
122+
http://localhost:8091/pools/default/buckets/${{ env.CB_MCP_TEST_BUCKET }} | grep -q '"status":"healthy"'; then
123+
echo "Bucket is healthy"
124+
break
125+
fi
126+
echo "Waiting for bucket... ($i/30)"
127+
sleep 2
128+
done
129+
130+
- name: Create primary index
131+
run: |
132+
echo "Creating primary index..."
133+
sleep 5
134+
curl -s -X POST http://localhost:8093/query/service \
135+
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
136+
-d "statement=CREATE PRIMARY INDEX ON \`${{ env.CB_MCP_TEST_BUCKET }}\`._default._default" \
137+
|| echo "Primary index may already exist or query service not ready"
138+
139+
- name: Insert test documents
140+
run: |
141+
echo "Inserting test documents..."
142+
curl -s -X POST http://localhost:8093/query/service \
143+
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
144+
-d "statement=INSERT INTO \`${{ env.CB_MCP_TEST_BUCKET }}\`._default._default (KEY, VALUE) VALUES {\"type\": \"test\", \"name\": \"Test Document 1\", \"id\": 1})" \
145+
|| echo "Insert may have failed"
146+
147+
curl -s -X POST http://localhost:8093/query/service \
148+
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
149+
-d "statement=INSERT INTO \`${{ env.CB_MCP_TEST_BUCKET }}\`._default._default (KEY, VALUE) VALUES ('test-doc-2', {\"type\": \"test\", \"name\": \"Test Document 2\", \"id\": 2})" \
150+
|| echo "Insert may have failed"
151+
152+
# ============================================
153+
# STDIO Transport Tests
154+
# ============================================
155+
- name: Run STDIO integration tests
156+
if: matrix.transport == 'stdio'
157+
env:
158+
CB_CONNECTION_STRING: couchbase://localhost
159+
CB_MCP_TRANSPORT: stdio
160+
PYTHONPATH: src
161+
run: |
162+
echo "Running tests with STDIO transport..."
163+
uv run pytest tests/ -v --tb=short
164+
165+
# ============================================
166+
# HTTP Transport Tests
167+
# ============================================
168+
- name: Start MCP server (HTTP)
169+
if: matrix.transport == 'http'
170+
env:
171+
CB_CONNECTION_STRING: couchbase://localhost
172+
CB_MCP_TRANSPORT: http
173+
CB_MCP_HOST: 127.0.0.1
174+
CB_MCP_PORT: 8000
175+
PYTHONPATH: src
176+
run: |
177+
echo "Starting MCP server with HTTP transport..."
178+
uv run python -m mcp_server &
179+
SERVER_PID=$!
180+
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
181+
182+
# Wait for server to be ready (check if port is listening)
183+
echo "Waiting for HTTP server to be ready..."
184+
for i in {1..30}; do
185+
if nc -z 127.0.0.1 8000 2>/dev/null; then
186+
echo "HTTP server is ready"
187+
break
188+
fi
189+
echo "Waiting for HTTP server... ($i/30)"
190+
sleep 1
191+
done
192+
193+
- name: Run HTTP transport tests
194+
if: matrix.transport == 'http'
195+
env:
196+
CB_CONNECTION_STRING: couchbase://localhost
197+
CB_MCP_TRANSPORT: http
198+
CB_MCP_HOST: 127.0.0.1
199+
CB_MCP_PORT: 8000
200+
MCP_SERVER_URL: http://127.0.0.1:8000/mcp
201+
PYTHONPATH: src
202+
run: |
203+
echo "Running tests with HTTP transport..."
204+
uv run pytest tests/ -v --tb=short
205+
206+
- name: Stop HTTP server
207+
if: matrix.transport == 'http' && always()
208+
run: |
209+
if [ -n "$SERVER_PID" ]; then
210+
kill $SERVER_PID 2>/dev/null || true
211+
fi
212+
213+
# ============================================
214+
# SSE Transport Tests
215+
# ============================================
216+
- name: Start MCP server (SSE)
217+
if: matrix.transport == 'sse'
218+
env:
219+
CB_CONNECTION_STRING: couchbase://localhost
220+
CB_MCP_TRANSPORT: sse
221+
CB_MCP_HOST: 127.0.0.1
222+
CB_MCP_PORT: 8000
223+
PYTHONPATH: src
224+
run: |
225+
echo "Starting MCP server with SSE transport..."
226+
uv run python -m mcp_server &
227+
SERVER_PID=$!
228+
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
229+
230+
# Wait for server to be ready (check if port is listening)
231+
echo "Waiting for SSE server to be ready..."
232+
for i in {1..30}; do
233+
if nc -z 127.0.0.1 8000 2>/dev/null; then
234+
echo "SSE server is ready"
235+
break
236+
fi
237+
echo "Waiting for SSE server... ($i/30)"
238+
sleep 1
239+
done
240+
241+
- name: Run SSE transport tests
242+
if: matrix.transport == 'sse'
243+
env:
244+
CB_CONNECTION_STRING: couchbase://localhost
245+
CB_MCP_TRANSPORT: sse
246+
CB_MCP_HOST: 127.0.0.1
247+
CB_MCP_PORT: 8000
248+
MCP_SERVER_URL: http://127.0.0.1:8000/sse
249+
PYTHONPATH: src
250+
run: |
251+
echo "Running tests with SSE transport..."
252+
uv run pytest tests/ -v --tb=short
253+
254+
- name: Stop SSE server
255+
if: matrix.transport == 'sse' && always()
256+
run: |
257+
if [ -n "$SERVER_PID" ]; then
258+
kill $SERVER_PID 2>/dev/null || true
259+
fi
260+
261+
# ============================================
262+
# Test Summary
263+
# ============================================
264+
test-summary:
265+
name: Test Summary
266+
permissions: {}
267+
runs-on: ubuntu-latest
268+
needs: [integration-tests]
269+
if: always()
270+
steps:
271+
- name: Check test results
272+
run: |
273+
echo "=== Test Results Summary ==="
274+
echo "Integration Tests: ${{ needs.integration-tests.result }}"
275+
echo ""
276+
277+
if [ "${{ needs.integration-tests.result }}" == "failure" ]; then
278+
echo "❌ Some tests failed"
279+
exit 1
280+
elif [ "${{ needs.integration-tests.result }}" == "cancelled" ]; then
281+
echo "⚠️ Tests were cancelled"
282+
exit 1
283+
else
284+
echo "✅ All tests passed!"
285+
fi

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,21 @@ The Couchbase MCP server can also be used as a managed server in your agentic ap
418418
- Check the logs for any errors or warnings that may indicate issues with the MCP server. The location of the logs depend on your MCP client.
419419
- If you are observing issues running your MCP server from source after updating your local MCP server repository, try running `uv sync` to update the [dependencies](https://docs.astral.sh/uv/concepts/projects/sync/#syncing-the-environment).
420420

421+
## Integration testing
422+
423+
We provide high-level MCP integration tests to verify that the server exposes the expected tools and that they can be invoked against a demo Couchbase cluster.
424+
425+
1. Export demo cluster credentials:
426+
- `CB_CONNECTION_STRING`
427+
- `CB_USERNAME`
428+
- `CB_PASSWORD`
429+
- Optional: `CB_MCP_TEST_BUCKET` (a bucket to probe during the tests)
430+
2. Run the tests:
431+
432+
```bash
433+
uv run pytest tests/ -v
434+
```
435+
421436
---
422437

423438
## 👩‍💻 Contributing

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "couchbase-mcp-server"
3-
version = "0.5.2"
3+
version = "0.5.3"
44
description = "Couchbase MCP Server - The Developer Data Platform for Critical Applications in Our AI World"
55
readme = "README.md"
66
requires-python = ">=3.10,<3.14"
@@ -35,6 +35,8 @@ couchbase-mcp-server = "mcp_server:main"
3535
dev = [
3636
"ruff==0.12.5",
3737
"pre-commit==4.2.0",
38+
"pytest==8.3.3",
39+
"pytest-asyncio==0.24.0",
3840
]
3941

4042
# Ruff configuration
@@ -124,6 +126,11 @@ indent-style = "space"
124126
skip-magic-trailing-comma = false
125127
line-ending = "auto"
126128

129+
# Pytest configuration
130+
[tool.pytest.ini_options]
131+
asyncio_mode = "strict"
132+
asyncio_default_fixture_loop_scope = "function"
133+
127134
# Build system configuration
128135
[build-system]
129136
requires = ["hatchling"]

server.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
"url": "https://github.com/Couchbase-Ecosystem/mcp-server-couchbase",
77
"source": "github"
88
},
9-
"version": "0.5.2",
9+
"version": "0.5.3",
1010
"packages": [
1111
{
1212
"registryType": "pypi",
1313
"identifier": "couchbase-mcp-server",
14-
"version": "0.5.2",
14+
"version": "0.5.3",
1515
"transport": {
1616
"type": "stdio"
1717
},
@@ -172,7 +172,7 @@
172172
},
173173
{
174174
"registryType": "oci",
175-
"identifier": "docker.io/couchbaseecosystem/mcp-server-couchbase:0.5.2",
175+
"identifier": "docker.io/couchbaseecosystem/mcp-server-couchbase:0.5.3",
176176
"transport": {
177177
"type": "stdio"
178178
},

0 commit comments

Comments
 (0)