diff --git a/README.md b/README.md index b4ab491..4202402 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ Both require a Bearer API token with the `mcp` scope. Tool access is further limited to secrets with `allow_mcp=true`. +Deployment and AI agent connection instructions are documented in +[`docs/deployment-and-mcp.md`](docs/deployment-and-mcp.md). + ## Frontend The frontend lives in `frontend/` and uses Vue 3 plus `gnexus-ui-kit` as a git diff --git a/docs/deployment-and-mcp.md b/docs/deployment-and-mcp.md new file mode 100644 index 0000000..92ab981 --- /dev/null +++ b/docs/deployment-and-mcp.md @@ -0,0 +1,401 @@ +# Deployment and MCP Connection Guide + +This document describes how to deploy `gnexus-creds` and connect the MCP server +to an AI agent. + +## Runtime Components + +`gnexus-creds` is one FastAPI application that serves: + +- Vue UI from the built frontend bundle. +- REST API under `/api/v1`. +- OAuth session routes under `/auth`. +- `gnexus-auth` profile webhook under `/webhooks/gnexus-auth`. +- Legacy HTTP/SSE MCP adapter under `/mcp`. +- Official MCP Streamable HTTP endpoint under `/mcp-protocol/`. + +PostgreSQL is required. SQLite is not supported for production. + +## Required Environment + +Create `.env` from `.env.example` and set real values: + +```env +GNEXUS_CREDS_ENV=production +GNEXUS_CREDS_DATABASE_URL=postgresql+psycopg://gnexus_creds:change-me@postgres:5432/gnexus_creds +GNEXUS_CREDS_MASTER_KEY=replace-with-a-long-random-master-key +GNEXUS_CREDS_SESSION_SECRET=replace-with-a-long-random-session-secret +GNEXUS_CREDS_SESSION_COOKIE_NAME=gnexus_creds_session + +GNEXUS_AUTH_BASE_URL=https://auth.gnexus.space +GNEXUS_AUTH_CLIENT_ID=replace-with-client-id +GNEXUS_AUTH_CLIENT_SECRET=replace-with-client-secret +GNEXUS_AUTH_REDIRECT_URI=https://creds.gnexus.space/auth/callback +GNEXUS_AUTH_WEBHOOK_SECRET=replace-with-webhook-secret + +GNEXUS_CREDS_MCP_RESOURCE_URL=https://creds.gnexus.space/mcp-protocol/ + +GNEXUS_CREDS_RATE_LIMIT_WINDOW_SECONDS=60 +GNEXUS_CREDS_RATE_LIMIT_MAX_SENSITIVE_REQUESTS=120 +``` + +For local development on port `8018`, use HTTP values: + +```env +GNEXUS_CREDS_ENV=development +GNEXUS_CREDS_DATABASE_URL=postgresql+psycopg://gnexus_creds:gnexus_creds@127.0.0.1:5432/gnexus_creds +GNEXUS_CREDS_MASTER_KEY=local-long-random-master-key +GNEXUS_CREDS_SESSION_SECRET=local-long-random-session-secret + +GNEXUS_AUTH_BASE_URL=http://gnexus-auth.local +GNEXUS_AUTH_CLIENT_ID=gnexus-creds +GNEXUS_AUTH_CLIENT_SECRET=replace-with-local-client-secret +GNEXUS_AUTH_REDIRECT_URI=http://localhost:8018/auth/callback +GNEXUS_AUTH_WEBHOOK_SECRET=replace-with-local-webhook-secret + +GNEXUS_CREDS_MCP_RESOURCE_URL=http://localhost:8018/mcp-protocol/ +``` + +Production mode fails fast if default secrets are used or if public auth/MCP +URLs do not use HTTPS. + +## gnexus-auth Integration + +Register `gnexus-creds` as an OAuth client in `gnexus-auth`. + +Local development values: + +- Client ID: `gnexus-creds`, or the value from `GNEXUS_AUTH_CLIENT_ID`. +- Client secret: the value from `GNEXUS_AUTH_CLIENT_SECRET`. +- Redirect URI: `http://localhost:8018/auth/callback`. +- Requested scopes: `openid`, `email`, `profile`. +- Webhook URL: `http://localhost:8018/webhooks/gnexus-auth`. +- Webhook secret: the same value as `GNEXUS_AUTH_WEBHOOK_SECRET`. + +Production values: + +- Redirect URI: `https://creds.gnexus.space/auth/callback`. +- Webhook URL: `https://creds.gnexus.space/webhooks/gnexus-auth`. +- Auth base URL: `https://auth.gnexus.space`. + +User profile data and user status come from `gnexus-auth`. The supported system +roles are: + +- `user` +- `admin` + +If `gnexus-auth` sends a disabled, blocked, or deleted status, `gnexus-creds` +keeps the local user data but blocks login. + +## Local Development Deploy + +Install backend dependencies: + +```bash +uv sync --extra dev +``` + +Start PostgreSQL through Docker Compose: + +```bash +docker-compose up -d postgres +``` + +Run migrations: + +```bash +uv run alembic upgrade head +``` + +Build the frontend bundle served by FastAPI: + +```bash +cd frontend +npm install +npm run build +cd .. +``` + +Start the server on port `8018`: + +```bash +uv run uvicorn gnexus_creds.main:app --host 0.0.0.0 --port 8018 --reload +``` + +Open the UI: + +```text +http://localhost:8018/ +``` + +Login starts through: + +```text +http://localhost:8018/auth/login +``` + +## Docker Compose Deploy + +Prepare `.env`, then build and start PostgreSQL: + +```bash +docker-compose build app +docker-compose up -d postgres +``` + +Run migrations explicitly: + +```bash +docker-compose run --rm app alembic upgrade head +``` + +Start the app: + +```bash +docker-compose up -d app +``` + +The included `docker-compose.yml` maps the app to host port `8000`: + +```yaml +ports: + - "8000:8000" +``` + +For local testing on `8018`, change it to: + +```yaml +ports: + - "8018:8000" +``` + +The Docker image contains the built Vue UI. Migrations are not run +automatically by the container. + +## Reverse Proxy + +The reverse proxy must forward these paths to the FastAPI app: + +- `/` +- `/api/v1/*` +- `/auth/*` +- `/webhooks/*` +- `/mcp/*` +- `/mcp-protocol/*` +- `/health` +- `/ready` + +For MCP Streamable HTTP, do not buffer or rewrite request bodies. Keep the +trailing slash in the MCP URL: + +```text +https://creds.gnexus.space/mcp-protocol/ +``` + +## Operational Checks + +Liveness: + +```bash +curl -fsS http://localhost:8018/health +``` + +Readiness, including database connectivity: + +```bash +curl -fsS http://localhost:8018/ready +``` + +Apply migrations before every deploy: + +```bash +uv run alembic upgrade head +``` + +or inside Docker: + +```bash +docker-compose run --rm app alembic upgrade head +``` + +## Preparing MCP Access + +MCP uses `gnexus-creds` API tokens, not `gnexus-auth` access tokens. + +1. Log in to the UI. +2. Open `Tokens`. +3. Create a token for the AI agent. +4. Select scopes based on the agent's required access: + +```text +mcp, read +``` + +Allows search and metadata reads. + +```text +mcp, read, reveal +``` + +Allows search, metadata reads, and decrypted reveal. + +```text +mcp, read, reveal, write +``` + +Allows full MCP access, including creating and updating secrets. + +The `admin` scope is not required for normal MCP access. + +Only secrets with `allow_mcp=true` are visible through MCP. Archived secrets are +not visible through normal MCP access. + +## Recommended MCP Endpoint + +Use the official Streamable HTTP endpoint when the AI agent supports MCP over +HTTP: + +```text +https://creds.gnexus.space/mcp-protocol/ +``` + +Local development: + +```text +http://localhost:8018/mcp-protocol/ +``` + +The agent must send: + +```http +Authorization: Bearer +``` + +Generic agent configuration shape: + +```json +{ + "mcpServers": { + "gnexus-creds": { + "type": "streamable-http", + "url": "https://creds.gnexus.space/mcp-protocol/", + "headers": { + "Authorization": "Bearer " + } + } + } +} +``` + +Some agents name the transport `http`, `streamable_http`, or `mcp-http`. +Use the transport name expected by the concrete agent, but keep the URL and +Authorization header unchanged. + +`stdio` is not supported and should not be used: the AI agent and +`gnexus-creds` server are expected to run on different hosts. + +## Legacy MCP HTTP/SSE Adapter + +Use the legacy adapter only for clients that do not support MCP Streamable HTTP. + +Discovery stream: + +```bash +curl -N \ + -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \ + http://localhost:8018/mcp/sse +``` + +Search secrets: + +```bash +curl -sS \ + -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"arguments":{"q":"github","limit":5}}' \ + http://localhost:8018/mcp/tools/search_secrets +``` + +Reveal one secret: + +```bash +curl -sS \ + -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"arguments":{"secret_id":""}}' \ + http://localhost:8018/mcp/tools/reveal_secret +``` + +Create a secret: + +```bash +curl -sS \ + -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "arguments": { + "title": "Example", + "purpose": "example.com", + "category": "site", + "tags": ["example"], + "allow_ui": true, + "allow_rest_api": true, + "allow_mcp": true, + "fields": [ + {"name": "username", "value": "user@example.com", "encrypted": false}, + {"name": "password", "value": "secret-password", "encrypted": true, "masked": true} + ] + } + }' \ + http://localhost:8018/mcp/tools/create_secret +``` + +## MCP Tools + +Current tools: + +- `search_secrets` +- `get_secret` +- `reveal_secret` +- `create_secret` +- `update_secret` +- `set_secret_status` +- `archive_secret` + +Search returns metadata and unencrypted fields only. Encrypted values are +returned only by `reveal_secret`. + +Reveal and write actions create audit events with `channel=mcp`. + +## Troubleshooting + +`401 Unauthorized`: + +- Token is missing, revoked, or copied incorrectly. +- The AI agent is not sending the `Authorization` header. + +`403 Forbidden`: + +- Token does not have the `mcp` scope. +- Tool-specific scope is missing: `read`, `reveal`, or `write`. +- User is disabled by `gnexus-auth`. + +Secret is missing from MCP search: + +- Secret has `allow_mcp=false`. +- Secret is archived. +- Search query targets an encrypted field value. Encrypted values are not + searchable. + +OAuth callback fails: + +- `GNEXUS_AUTH_REDIRECT_URI` does not exactly match the client redirect URI in + `gnexus-auth`. +- `GNEXUS_AUTH_CLIENT_SECRET` is wrong. +- `GNEXUS_AUTH_BASE_URL` points to the wrong auth instance. + +Production app refuses to start: + +- `GNEXUS_CREDS_ENV=production` requires HTTPS for auth redirect and MCP + resource URLs. +- Default secrets from `.env.example` must be replaced.