Model Context Protocol (stdio) server that calls odin-deployer-ent over REST (default 9000) and gRPC (default 8080), odin-scout-ent over REST (default 8080), and user-auth over REST (default 8081) for org/team/member APIs. Business logic stays in the Java services; this package is a thin adapter.
Protos are loaded at runtime from this repo’s proto/ directory (mirrors odin-deployer-ent’s src/main/proto). Imports such as google/protobuf/*.proto resolve against proto/google/protobuf/, populated from the google-proto-files npm package via npm run sync-google-protobuf (run after upgrading that devDependency). When the server API changes, update the copy under proto/ to stay in sync.
- Node.js 18+
- For authenticated tools: a valid
Authorizationvalue (same asUserAuthClient.verifyToken). Use the full header value (oftenBearer <jwt>).
Scout REST calls use the same resolved token unless ODIN_SCOUT_AUTH_TOKEN is set (override for Scout only).
cd odin-mcp
npm install
npm run build # produces dist/index.js (single bundled binary of JS deps)Runtime artifact: dist/index.js embeds all npm dependencies (@grpc/*, @modelcontextprotocol/sdk, etc.). You do not need node_modules where the server runs—only Node 18+, dist/index.js, dist/package.json (written by the build so CommonJS resolution works under this repo’s "type": "module"), and the proto/ tree (same paths relative to the bundle’s parent directory; keep proto/ next to dist/ as in this repo).
Build tooling (esbuild, TypeScript, etc.) lives under devDependencies. If npm ci runs with NODE_ENV=production, devDependencies are skipped and npm run build will fail—use a builder stage without that flag, or npm ci --include=dev, until the bundle is produced.
To refresh vendored proto/google/protobuf after bumping google-proto-files:
npm run sync-google-protobuf| Variable | Required | Default | Description |
|---|---|---|---|
ODIN_DEPLOYER_REST_BASE |
No | http://127.0.0.1:9000 |
REST base URL (no trailing slash) |
ODIN_DEPLOYER_GRPC_HOST |
No | 127.0.0.1:8080 |
gRPC host:port (GrpcVerticle) |
ODIN_DEPLOYER_GRPC_USE_TLS |
No | false |
If true, uses TLS with default trust store (no custom CA in v1) |
ODIN_MCP_GRPC_STREAM_COMPLETION_DEFAULT |
No | full |
Server-streaming gRPC tools only: full = wait for stream end; first_progress = return after first chunk (short chat round-trip; operation continues server-side — poll describe/status). Aliases: first / detach → first_progress; wait / complete → full |
ODIN_DEPLOYER_AUTH_TOKEN |
For protected tools | — | Sent on REST Authorization and gRPC metadata Authorization |
USER_AUTH_REST_BASE |
No | http://127.0.0.1:8081 |
user-auth Spring base URL (orgs, teams, invites). Same bearer as deployer; ODIN_SCOUT_AUTH_TOKEN overrides Scout REST only. |
ODIN_DEPLOYER_REQUEST_TIMEOUT_MS |
No | 300000 |
HTTP timeout (ms) — deployer, Scout, and user-auth REST |
ODIN_SCOUT_REST_BASE |
No | http://127.0.0.1:8080 |
Scout HTTP base URL (no trailing slash); include gateway prefix if mounted under e.g. /api |
ODIN_SCOUT_AUTH_TOKEN |
No | — | If set, Scout REST Authorization uses this instead of ODIN_DEPLOYER_AUTH_TOKEN / resolved MCP token |
Do not commit real tokens or .env files.
| MCP tool | Method | Path / notes |
|---|---|---|
deployer_health |
GET | /healthcheck |
deployer_operation_schemas_list |
GET | /v1/operation-schemas/{component_type}/{deployment_type} |
deployer_operation_schema_get |
GET | /v1/operation-schemas/.../{operation_name} |
deployer_component_catalogue |
GET | /v1/component-catalogue (optional component_type) |
deployer_component_operations |
GET | /v1/component-operations |
deployer_operate_component |
POST | .../operate — OperateRequest body |
deployer_operate_status |
GET | /v1/operate/status/{service_task_id} |
| MCP tool | Method | Path / notes |
|---|---|---|
scout_accounts_list |
GET | /accounts — optional tool arg compact maps to ?compact=true |
scout_account_details_get |
GET | /account-details?accountName= — required tool arg accountName |
Uses USER_AUTH_REST_BASE and the same Authorization header as deployer (ODIN_DEPLOYER_AUTH_TOKEN or per-request overrides). Team routes require header X-Org-Id — pass org_id in the tool arguments (MCP sets the header).
| MCP tool | Method | Path / notes |
|---|---|---|
user_auth_health |
GET | /healthcheck — no auth |
user_auth_orgs_list |
GET | /api/v1/orgs |
user_auth_org_get |
GET | /api/v1/orgs/{org_id} |
user_auth_teams_list |
GET | /api/v1/teams + X-Org-Id — org_id optional if Bearer JWT includes orgid |
user_auth_team_get |
GET | /api/v1/teams/{team_name} + X-Org-Id — same |
user_auth_team_create |
POST | /api/v1/teams body { name } + X-Org-Id — same |
user_auth_team_members_list |
GET | /api/v1/teams/{team_name}/members + X-Org-Id — same |
user_auth_team_add_members |
POST | .../teams/{team_name}/members body { emails: [] } + X-Org-Id — same |
user_auth_org_members_list |
GET | /api/v1/orgs/{org_id}/members — org_id optional with org-scoped JWT |
user_auth_org_invite_members |
POST | /api/v1/orgs/{org_id}/members body { emails: [] } — same |
user_auth_user_get |
GET | /api/v1/users/{user_id} |
user_auth_user_orgs |
GET | /api/v1/users/orgs (current user’s orgs) |
user_auth_team_update |
PUT | /api/v1/teams/{team_name} body { name } + X-Org-Id |
user_auth_team_transfer_owner |
PUT | .../teams/{team_name}/owner — requires type_to_confirm = team_name |
user_auth_team_delete |
DELETE | /api/v1/teams/{team_name} — requires type_to_confirm = team_name |
user_auth_team_remove_member |
DELETE | .../teams/{team_name}/members/{email} — type_to_confirm = email |
user_auth_org_member_promote_admin |
PUT | /api/v1/orgs/{org_id}/members/{email}/admin — type_to_confirm = email |
user_auth_org_member_set_role |
PUT | .../members/{email}/role body { role } — type_to_confirm = email |
user_auth_org_member_remove |
DELETE | /api/v1/orgs/{org_id}/members/{email} — type_to_confirm = email |
user_auth_org_update |
PUT | /api/v1/orgs/{org_id} body { name } |
user_auth_org_delete |
DELETE | /api/v1/orgs/{org_id} — type_to_confirm must equal org_id (digits) |
The MCP server fills X-Org-Id from tool args org_id, or from the JWT orgid claim when the client uses an org-scoped access token (same as the Asgard dashboard after org selection). User-level tokens (no orgid) must pass org_id explicitly for these routes.
Destructive / high-risk user-auth tools require type_to_confirm: the user must explicitly agree in chat; the model passes the same team name, member email, or org id again so the MCP server can reject mistaken or automated calls. There is no HTTP API in user-auth to delete a whole user account from these controllers; use org/team member removal only.
| MCP tool | RPC | Notes |
|---|---|---|
deployer_grpc_health |
grpc.health.v1.Health/Check |
No auth on server |
deployer_grpc_list_tiers |
TierService/ListTiers |
Needs token |
deployer_grpc_list_environment |
EnvironmentService/ListEnvironment |
Needs token |
deployer_grpc_describe_environment |
EnvironmentService/DescribeEnvironment |
Needs token |
deployer_grpc_create_environment |
EnvironmentService/CreateEnvironment |
Server-streaming; destructive. Optional arg stream_completion: first_progress returns after first chunk (use when IDE may drop long waits); default from env ODIN_MCP_GRPC_STREAM_COMPLETION_DEFAULT. |
deployer_grpc_delete_environment |
EnvironmentService/DeleteEnvironment |
Server-streaming; destructive. Optional stream_completion (same as above). |
deployer_grpc_status_environment |
EnvironmentService/StatusEnvironment |
Server-streaming; aggregated. Optional stream_completion. |
deployer_grpc_list_services |
ServiceService/ListServices |
Needs token |
deployer_grpc_describe_service |
ServiceService/DescribeService |
Needs token |
deployer_grpc_deploy_service |
ServiceService/DeployService |
Server-streaming; deploy. Optional stream_completion. |
deployer_grpc_operate_service |
ServiceService/OperateService |
Server-streaming. Optional stream_completion. |
deployer_grpc_undeploy_service |
ServiceService/UndeployService |
Server-streaming; destructive. Optional stream_completion. |
deployer_grpc_operate_component_diff |
ServiceService/OperateComponentDiff |
Unary |
deployer_grpc_list_service_versions |
ServiceService/ListServiceVersions |
Unary |
deployer_grpc_release_service |
ServiceService/ReleaseService |
Unary |
deployer_grpc_check_service_release_name_unique |
ServiceService/CheckServiceReleaseNameUnique |
Unary |
deployer_grpc_export_service |
ServiceService/ExportService |
Unary |
Server-streaming RPCs return a JSON array of all messages received on the stream.
Use absolute paths for Node and dist/index.js. If command: "node" fails with ENOENT, use the output of which node.
{
"mcpServers": {
"odin-mcp": {
"command": "/full/path/from/which/node",
"args": ["/absolute/path/to/odin-mcp/dist/index.js"],
"env": {
"ODIN_DEPLOYER_REST_BASE": "http://127.0.0.1:9000",
"ODIN_SCOUT_REST_BASE": "http://127.0.0.1:8080",
"USER_AUTH_REST_BASE": "http://127.0.0.1:8081",
"ODIN_DEPLOYER_GRPC_HOST": "127.0.0.1:8080",
"ODIN_DEPLOYER_AUTH_TOKEN": "Bearer <your-token>"
}
}
}
}SyntaxError: Cannot use import statement outside a module/ MCPConnection closed: Prefernpm run buildand rundist/index.jswithdist/package.jsonfrom the same build (CommonJS bundle). If you use an older unbundleddist/*.jslayout, ensurepackage.jsonwith"type": "module"sits beside those files.spawn node ENOENT: Use the full path tonodeincommand.401/403(REST) or gRPC permission errors: Fix token; confirmAuthFilter/AuthInterceptorrules.- Timeouts on operate: Increase
ODIN_DEPLOYER_REQUEST_TIMEOUT_MS. - REST connection refused: Deployer REST on 9000 (
ODIN_DEPLOYER_REST_BASE); Scout HTTP on 8080 (ODIN_SCOUT_REST_BASE); user-auth on 8081 (USER_AUTH_REST_BASE). - gRPC errors (
UNKNOWN,UNAVAILABLE): Deployer gRPC on 8080; firewall; trygrpcurlto the same host/port; setODIN_DEPLOYER_GRPC_USE_TLS=trueif the server uses TLS.
npm run devRuns tsx src/index.ts (stdio; connect from an MCP host).