mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-03 06:53:21 +00:00
98 lines
4.4 KiB
Markdown
98 lines
4.4 KiB
Markdown
|
|
# 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.
|