sh-mcp/servers/sh-mcp-ops/README.md

98 lines
4.4 KiB
Markdown
Raw Normal View History

# sh-mcp-ops
Operations-tier Sea Haven MCP server. Exposes the **ops** tool registry
(internal-data, knowledge-base, google-maps, gmail, calendar, tasks, reminders —
see `docs/design.md §3`) over **two universal interfaces backed by one shared
dispatch path**:
- **MCP** (Streamable HTTP) at `POST /mcp` — via `@modelcontextprotocol/sdk`.
- **OpenAPI 3.1** — full document at `GET /openapi.json`, plus one
`POST /tools/{tool-name}` endpoint per tool.
All scope enforcement, audience binding, input validation, rate limiting, and
audit logging live in `@sh-mcp/shared` (`executeTool`) and are identical across
both interfaces (`docs/design.md §2.5`).
## Run locally (no AWS, no Cognito)
```bash
# from the repo root
npm install
npm run build # or: npx tsc -b
SH_MCP_ENV=local PORT=8081 npm run start -w @sh-mcp/server-ops
# or live-reload: SH_MCP_ENV=local npm run dev -w @sh-mcp/server-ops
```
`SH_MCP_ENV=local` wires **in-memory dev clients** (seeded fake data, no network)
and the `LocalAuthProvider`, which maps dev bearer tokens to identities. The
local provider refuses to construct unless `SH_MCP_ENV=local`.
### Endpoints
| Method | Path | Auth | Notes |
| ------ | -------------------- | ---- | -------------------------------------- |
| GET | `/healthz` | no | `{ "status": "ok" }` |
| GET | `/openapi.json` | no | full OpenAPI 3.1 document |
| POST | `/mcp` (+GET/DELETE) | yes | MCP Streamable HTTP |
| POST | `/tools/:name` | yes | one-shot tool call, JSON in / JSON out |
### Dev bearer tokens (local only)
| Token | Identity | Scopes |
| --------------- | ------------------------ | -------------------------------------------- |
| `dev-ops-only` | ops-only@seahavenind.com | `ops:read`, `ops:tasks` |
| `dev-assistant` | lauren@seahavenind.com | + `gmail:self`, `calendar:self` |
| `dev-ops-admin` | adam@seahavenind.com | `ops:read`, `ops:tasks`, gmail/calendar self |
Tokens minted for a different tier (e.g. `dev-finance`) are **rejected** by this
server (audience binding).
### Sample curl
```bash
TOKEN=dev-ops-only
curl -s localhost:8081/healthz
curl -s localhost:8081/openapi.json | jq '.openapi, (.paths | keys | length)'
curl -s -X POST localhost:8081/tools/lookup_work_order \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"workOrderId":"WO-1001"}' | jq
# tool-hiding is convenience; the boundary is server-side — a forced call to a
# tool you lack scope for is still 403:
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8081/tools/search_inbox \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"invoice"}' # → 403 (needs gmail:self)
```
### MCP Inspector
Point the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) at
`http://localhost:8081/mcp` (transport: **Streamable HTTP**) and set an
`Authorization: Bearer dev-assistant` header. `tools/list` reflects only the
tools your token's scopes permit.
## Environment
| Var | Mode | Purpose |
| ---------------------------- | ---- | --------------------------------------------- |
| `SH_MCP_ENV` | both | `local` (dev clients) or `aws` (real/Cognito) |
| `PORT` | both | listen port (default 8081) |
| `AWS_REGION` | aws | Cognito region |
| `COGNITO_USER_POOL_ID` | aws | issuer / JWKS source |
| `COGNITO_ALLOWED_CLIENT_IDS` | aws | comma-list; the audience boundary |
| `GOOGLE_MAPS_API_KEY` | aws | maps client |
| `REMINDER_TARGET_ARN` etc. | aws | scheduler wiring |
`aws` mode is not runtime-exercised in Phase 1 (real external clients are
deferred stubs; Cognito infra is out of scope — see `docs/build-plan-phase-1.md`).
## CDK
`cdk/app.ts` is a **synth-only** placeholder (`npm run synth -w @sh-mcp/server-ops`)
so the CI `cdk synth` gate has a valid app. It provisions **no** real IAM,
Cognito, API Gateway, or WAF — those land in a later phase behind the mandatory
IAM cross-review.