# MCP-Strapi Integration Tests
Integration test scripts that validate MCP tool access, role-based authorization, content CRUD, publish workflows, ownership enforcement, media uploads, and JWT authentication against a running Strapi 5.x instance with the MCP plugin.
## Prerequisites
- **Running Strapi 5.x** with the MCP plugin enabled
- **Node.js 20+** (uses native `fetch`)
- At least one API collection content type (e.g., `api::article.article`)
## Quick Start
```bash
# Start Strapi
docker compose up -d
# Set credentials
export STRAPI_URL=http://localhost:1337
export STRAPI_ADMIN_EMAIL=admin@example.com
export STRAPI_ADMIN_PASSWORD=Admin1234!
# Run all tests (suites 01-07 work with auth: 'admin' mode)
node tests/run-all.js
# Run specific test suites
node tests/run-all.js 01 05
# List available suites
node tests/run-all.js --list
```
## Test Suites
| # | Suite | Auth Mode | What it tests |
|---|-------|-----------|---------------|
| 01 | content-crud-by-role | admin | CRUD operations per role (Admin, Editor, Author, Reader), unauthenticated/invalid token rejection |
| 02 | publish-unpublish | admin | Publish/unpublish by Admin and Editor, author publish denial, expired content unpublish, draft vs published visibility |
| 03 | content-search-scope | admin | Content type scoped search, cross-type isolation, pagination, sort, schema-aware filtering, relation population |
| 04 | auto-user-create | admin | User provisioning, MCP session for new users, non-existent user rejection, role-based default access, deactivated user handling, concurrent sessions |
| 05 | private-own-entries | admin | Ownership enforcement: Author A vs Author B cross-ownership, Admin bypasses ownership, read unrestricted |
| 06 | attachment-upload | admin | Media upload (PNG, JPEG, PDF), invalid/empty/oversize data, upload by Author role, linking media to entries |
| 07 | role-design | admin | Full role matrix validation, role derivation mapping, tool availability, field sanitization rules, session identity |
| **08** | **jwt-auth-flow** | **jwt** | **Full external IdP simulation: RS256 JWKS, role/tenant/permission claims, expired/wrong-issuer/wrong-audience rejection, email→user mapping, MCP_SECRET_KEY** |
## JWT Auth Testing (Suite 08)
Suite 08 tests the full production auth path using a **local JWT provider** that simulates an external OAuth/OIDC identity provider:
```
┌────────────────────┐ ┌──────────────────────┐ ┌──────────────────┐
│ Test Script │ │ MCP Plugin │ │ JWT Provider │
│ │ │ │ │ (local:9876) │
│ 1. Sign JWT ──────┼────►│ 2. Bearer token │ │ │
│ (RS256) │ │ 3. auth.service │ │ │
│ │ │ .verifyJWT() │ │ │
│ │ │ 4. jose.jwtVerify() ─┼────►│ 5. JWKS endpoint│
│ │ │ ← verified ◄──────┼─────┤ /.well-known/ │
│ │ │ 6. email → admin │ │ jwks.json │
│ │ │ 7. _mcpClaims.role │ │ │
│ │ │ 8. authorize() │ │ │
│ 9. Tool result ◄──┼─────┤ ← response │ │ │
└────────────────────┘ └──────────────────────┘ └──────────────────┘
```
### Setup for JWT mode testing
```bash
# 1. Configure MCP plugin for JWT auth mode
# In your Strapi plugin config (config/plugins.ts):
# 'mcp-server': { config: { auth: 'jwt' } }
# 2. Set JWT environment variables (the test prints these)
export JWT_JWKS_URI=http://localhost:9876/.well-known/jwks.json
export JWT_ISSUER=http://localhost:9876
export JWT_AUDIENCE=mcp-test
# 3. Restart Strapi with new config
docker compose restart strapi
# 4. Run JWT auth tests
node tests/08-jwt-auth-flow.js
```
### What suite 08 validates
| Test | Expected |
|------|----------|
| Valid Admin JWT | Full MCP access, CRUD works |
| JWT role override | JWT `role` claim overrides Strapi admin role |
| Author JWT | Ownership enforcement via _mcpClaims |
| Tenant claim | `tenant` claim accepted, filter injection at authorization layer |
| Expired JWT | 401 rejection |
| Wrong issuer | 401 rejection |
| Wrong audience | 401 rejection |
| Missing email | 401 rejection |
| Unknown email | 403 (no matching Strapi admin user) |
| MCP_SECRET_KEY | x-mcp-internal-secret header enforced if configured |
### Standalone JWT provider (for debugging)
```bash
# Start the provider standalone — serves JWKS and prints sample tokens
node tests/jwt-provider.js
# Output:
# JWT Provider running on port 9876
# JWKS endpoint: http://localhost:9876/.well-known/jwks.json
# Sample tokens: Admin, Author, Reader, Tenant
```
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `STRAPI_URL` | `http://localhost:1337` | Strapi base URL |
| `STRAPI_ADMIN_EMAIL` | `admin@example.com` | Super admin email |
| `STRAPI_ADMIN_PASSWORD` | `Admin1234!` | Super admin password |
| `STRAPI_API_TOKEN` | — | Optional API token for token-based tests |
| `JWT_JWKS_URI` | — | JWKS endpoint URL (for suite 08, auto-set by provider) |
| `JWT_ISSUER` | — | Expected JWT issuer (for suite 08) |
| `JWT_AUDIENCE` | — | Expected JWT audience (for suite 08) |
## Test Architecture
Tests use the **MCP Streamable HTTP** protocol directly — they open MCP sessions, call tools via JSON-RPC, and validate responses. No MCP SDK client is required.
```
Test Script → HTTP POST /api/mcp-server/mcp (JSON-RPC)
→ MCP Auth Middleware (resolves user)
→ MCP Server (tool execution)
→ Authorization Service (role check + ownership)
→ Strapi Document Service
```
### Shared Utilities
| File | Purpose |
|------|---------|
| `helpers.js` | `McpTestClient`, admin auth, user CRUD, assertions, health checks |
| `jwt-provider.js` | Local RS256 key pair + JWKS HTTP server + JWT signing with custom claims |
### Test Cleanup
All tests clean up after themselves:
- Created entries are deleted after each test section
- Test users are removed at the end of each suite
- Uploaded files are deleted via the upload API
- JWT provider server is stopped on exit
## Running Individual Tests
```bash
node tests/01-content-crud-by-role.js # Content CRUD by role
node tests/02-publish-unpublish.js # Publish/unpublish workflows
node tests/03-content-search-scope.js # Content search scope
node tests/04-auto-user-create.js # Auto user creation
node tests/05-private-own-entries.js # Private content / ownership
node tests/06-attachment-upload.js # Attachment/media upload
node tests/07-role-design.js # Role design validation
node tests/08-jwt-auth-flow.js # JWT auth flow (requires jwt mode)
```
## Notes
- **Suites 01-07** work with `auth: 'admin'` mode (default). No external IdP needed.
- **Suite 08** requires `auth: 'jwt'` mode with JWT_* env vars pointing to the local provider. It auto-detects the auth mode and prints setup instructions if not in JWT mode.
- Tests create temporary admin users with `Editor` and `Author` roles. If these roles don't exist in your Strapi instance, related tests will be skipped.
- Tests are non-destructive — they only create/modify/delete their own test data.
- The JWT provider generates a fresh RS256 key pair on each run (ephemeral, no key files stored).