Key points
- A pipeline names these resources; it cannot define them. That is the boundary that makes AI use reviewable.
- Model resolution runs task, step, pipeline, team, then platform default.
- Deleting a profile still in use answers 409 rather than breaking the pipelines that name it.
- The test routes make a real provider or server call — the difference between "configured" and "working".
- Tool discovery is what makes a profile reviewable: you grant named tools rather than whatever a server exposes.
- The profile is the only place tool access narrows; pipeline, step, and task profiles are additive at run time.
allowed_scopesbounds where a profile may be used, which keeps a production tool set out of development runs.
Operations
GET/v1/system/modelsAuthorized
Lists platform model profiles.
Notes
A profile names a provider, a model, and a credential reference. model: in a pipeline names the profile, never the provider model id directly.
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/models" | jq '.[] | {name, provider, enabled, is_default}'Responses
Model profiles.
[{
"name": "reasoning-large",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"credential_ref": "platform/anthropic",
"enabled": true,
"is_default": true,
"allowed_scopes": ["platform/production"]
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable, so the list cannot be filtered. | Check AAA. |
Side effects
- None.
Proven by
services/nopsai/llm_config_test.goservices/nopsai/llm_profiles.go
PUT/v1/system/modelsAuthorized
Replaces the platform model profile set.
Notes
This replaces the whole set. To change one profile use the single-profile PUT, which leaves the others alone.
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/models" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @models.json | jqResponses
Profiles stored.
[{
"name": "reasoning-large",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"credential_ref": "platform/anthropic",
"enabled": true,
"is_default": true,
"allowed_scopes": ["platform/production"]
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown provider, a missing model, or a credential reference that does not resolve. | The message names the profile and field. |
| 500 | The profiles could not be stored. | Retry. |
Side effects
- Removing a profile makes every pipeline naming it fail authorization on its next run.
- Writes an audit record.
Proven by
services/nopsai/llm_config_test.goservices/nopsai/llm_profiles.go
PUT/v1/system/models/defaultAuthorized
Sets the platform default model profile.
Notes
Resolution order is task, step, pipeline, team, then this. Changing it moves the floor, not the ceiling.
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/models/default" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"reasoning-large"}' | jqResponses
Default set.
{ "name": "reasoning-large", "is_default": true }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The profile does not exist or is disabled. | List profiles first. |
| 500 | The default could not be stored. | Retry. |
Side effects
- Changes the last step of model resolution for every team without its own default.
- Writes an audit record.
Proven by
services/nopsai/llm_config_test.goservices/nopsai/llm_profiles.go
PUT/v1/system/models/{profileName}Authorized
Creates or replaces one model profile.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile name. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/models/reasoning-large" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider":"anthropic","model":"claude-sonnet-4-5","credential_ref":"platform/anthropic","enabled":true}' | jqResponses
Profile stored.
{
"name": "reasoning-large",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"credential_ref": "platform/anthropic",
"enabled": true,
"is_default": true,
"allowed_scopes": ["platform/production"]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown provider, missing model, or unresolvable credential. | The message names the field. |
| 500 | The profile could not be stored. | Retry. |
Side effects
- Takes effect for the next run that resolves this profile.
- Writes an audit record.
Proven by
services/nopsai/llm_config_test.goservices/nopsai/llm_profiles.go
DELETE/v1/system/models/{profileName}Authorized
Deletes a model profile.
Notes
The 409 is a feature: deleting a model out from under a pipeline is refused rather than discovered at the next run.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/models/reasoning-large" -w "%{http_code}\n"Responses
Profile deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The name is missing. | Name the profile. |
| 404 | No profile with that name. | It may already be deleted. |
| 409 | The profile is still in use — as a default, or by a pipeline. | Reassign the dependants first. The platform refuses rather than breaking them. |
| 500 | The delete failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/llm_config_test.goservices/nopsai/llm_profiles.go
POST/v1/system/models/{profileName}/testAuthorized
Tests a model profile against its provider.
Notes
Run this after every credential rotation. A rotated key that was never tested fails at the least convenient moment.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile to test. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/models/reasoning-large/test" | jqResponses
The provider answered.
{ "ok": true, "provider": "anthropic", "model": "claude-sonnet-4-5" }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The profile is incomplete or disabled. | Finish configuring it first. |
| 404 | No profile with that name. | Confirm the name. |
| 502 | The provider rejected the call or could not be reached. | The message carries the provider’s response — usually a bad key, a wrong model id, or egress being blocked. |
Side effects
- Makes one real call to the provider, which may be billable.
Proven by
services/nopsai/llm_profile_provider_clients_test.goservices/nopsai/llm_profiles.go
GET/v1/system/agent-rolesAuthorized
Lists platform agent roles.
Notes
An agent role is behaviour, not capability: it shapes the prompt and does not grant tools or models.
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/agent-roles" | jq '.[] | {id, name, enabled, is_default}'Responses
Agent roles.
[{
"id": "senior-release-engineer",
"name": "Senior release engineer",
"enabled": true,
"is_default": false,
"instructions": "Review changes for release risk before recommending a promotion."
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.gopkg/models/agent_profile.go
POST/v1/system/agent-rolesAuthorized
Creates an agent role.
Call it
curl -sX POST "$NOPSAI_URL/v1/system/agent-roles" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"senior-release-engineer","name":"Senior release engineer","instructions":"Review changes for release risk."}' | jqResponses
Agent role created.
{
"id": "senior-release-engineer",
"name": "Senior release engineer",
"enabled": true,
"is_default": false,
"instructions": "Review changes for release risk before recommending a promotion."
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A missing id, name, or instructions. | Validate first with the validate route. |
| 409 | A role with that id already exists. | Update it instead. |
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The role could not be created. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
GET/v1/system/agent-roles/{profileID}Authorized
Reads one agent role.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileID | path | string | Required | Agent role id. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/agent-roles/senior-release-engineer" | jqResponses
The agent role.
{
"id": "senior-release-engineer",
"name": "Senior release engineer",
"enabled": true,
"is_default": false,
"instructions": "Review changes for release risk before recommending a promotion."
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No role with that id. | Confirm the id from the list. |
| 500 | The role could not be loaded. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
PUT/v1/system/agent-roles/{profileID}Authorized
Replaces an agent role.
Notes
Editing a widely used role changes behaviour across many pipelines at once. Check its usage first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileID | path | string | Required | Agent role id. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/agent-roles/senior-release-engineer" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @role.json | jqResponses
Role stored.
{
"id": "senior-release-engineer",
"name": "Senior release engineer",
"enabled": true,
"is_default": false,
"instructions": "Review changes for release risk before recommending a promotion."
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | Invalid instructions. | Validate first. |
| 404 | No role with that id. | Create it instead. |
| 500 | The update failed. | Retry. |
Side effects
- Changes the prompt for every future step naming this role.
- Writes an audit record.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
PUT/v1/system/agent-roles/defaultAuthorized
Sets the platform default agent role.
Notes
default is a literal path segment, so no agent role may use it as an id.
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/agent-roles/default" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"senior-release-engineer"}' | jqResponses
Default set.
{ "id": "senior-release-engineer", "is_default": true }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The role does not exist or is disabled. | List roles first. |
| 500 | The default could not be stored. | Retry. |
Side effects
- Changes behaviour for every LLM step that names no role.
- Writes an audit record.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
DELETE/v1/system/agent-roles/{profileID}Authorized
Deletes an agent role.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileID | path | string | Required | Agent role to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/agent-roles/senior-release-engineer" -w "%{http_code}\n"Responses
Role deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The id is missing. | Name the role. |
| 403 | The caller may not delete this role. | Team-owned roles are deleted through the team routes. |
| 404 | No role with that id. | It may already be deleted. |
| 409 | The role is still in use, or is a default. | Check its usage and reassign first. |
| 500 | The delete failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
GET/v1/system/agent-roles/{profileID}/usageAuthorized
Reports what currently uses an agent role.
Notes
Call this before editing a role, not just before deleting one: an edit changes behaviour everywhere without any error.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileID | path | string | Required | Agent role id. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/agent-roles/senior-release-engineer/usage" | jqResponses
Usage across pipelines and teams.
{
"pipelines": ["platform/release-service"],
"teams": ["platform"],
"is_default": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The id is missing. | Name the role. |
| 500 | The usage query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
POST/v1/system/agent-roles/validateAuthorized
Validates an agent role definition without storing it.
Call it
curl -sX POST "$NOPSAI_URL/v1/system/agent-roles/validate" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @role.json | jqResponses
Validation ran. Read valid rather than the status code.
{
"valid": true,
"errors": [],
"warnings": []
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The payload could not be read. | Send a JSON role document. |
Side effects
- None.
Proven by
services/nopsai/agent_profiles_test.goservices/nopsai/agent_profiles.go
GET/v1/system/mcp/serversAuthorized
Lists configured MCP servers.
Notes
A server is a connection; a profile is permission to use some of its tools. Pipelines name profiles, never servers.
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/servers" | jq '.[] | {name, transport, enabled}'Responses
MCP servers.
[{
"name": "jira",
"display_name": "Jira",
"enabled": true,
"transport": "http",
"url": "https://mcp.example.com/jira",
"auth_type": "credential",
"credential_ref": "platform/jira-mcp"
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
POST/v1/system/mcp/serversAuthorized
Registers an MCP server.
Call it
curl -sX POST "$NOPSAI_URL/v1/system/mcp/servers" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"jira","transport":"http","url":"https://mcp.example.com/jira","auth_type":"credential","credential_ref":"platform/jira-mcp"}' | jqResponses
Server registered.
{
"name": "jira",
"display_name": "Jira",
"enabled": true,
"transport": "http",
"url": "https://mcp.example.com/jira",
"auth_type": "credential",
"credential_ref": "platform/jira-mcp"
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown transport, a missing URL, or an unresolvable credential. | The message names the field. |
| 500 | The server could not be stored. | Retry. |
Side effects
- Registers a connection target. Registration alone grants nothing.
- Writes an audit record.
Proven by
services/nopsai/mcp_registry_persistence_test.goservices/nopsai/mcp_registry.go
GET/v1/system/mcp/servers/{serverName}Authorized
Reads one MCP server.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
serverName | path | string | Required | Server name. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/servers/jira" | jqResponses
The server.
{
"name": "jira",
"display_name": "Jira",
"enabled": true,
"transport": "http",
"url": "https://mcp.example.com/jira",
"auth_type": "credential",
"credential_ref": "platform/jira-mcp"
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No server with that name. | Confirm the name from the list. |
Side effects
- None.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
PUT/v1/system/mcp/servers/{serverName}Authorized
Creates or replaces an MCP server.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
serverName | path | string | Required | Server name. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/mcp/servers/jira" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
--data @server.json | jqResponses
Server stored.
{
"name": "jira",
"display_name": "Jira",
"enabled": true,
"transport": "http",
"url": "https://mcp.example.com/jira",
"auth_type": "credential",
"credential_ref": "platform/jira-mcp"
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An invalid transport, URL, or credential reference. | The message names the field. |
| 500 | The server could not be stored. | Retry. |
Side effects
- Every profile naming this server picks up the change.
- Writes an audit record.
Proven by
services/nopsai/mcp_registry_persistence_test.goservices/nopsai/mcp_registry.go
DELETE/v1/system/mcp/servers/{serverName}Authorized
Removes an MCP server.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
serverName | path | string | Required | Server to remove. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/servers/jira" -w "%{http_code}\n"Responses
Server removed.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The name is missing. | Name the server. |
| 404 | No server with that name. | It may already be removed. |
| 409 | A profile still references the server. | Remove it from the profiles first. |
| 500 | The delete failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/mcp_registry_persistence_test.goservices/nopsai/mcp_registry.go
POST/v1/system/mcp/servers/{serverName}/testAuthorized
Tests connectivity and authentication to an MCP server.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
serverName | path | string | Required | Server to test. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/servers/jira/test" | jqResponses
The server answered.
{ "ok": true, "server": "jira" }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The server is incomplete or disabled. | Finish configuring it. |
| 404 | No server with that name. | Confirm the name. |
| 502 | The server could not be reached or rejected the credentials. | The message carries the failure — usually egress or an expired credential. |
Side effects
- Makes one real connection to the server.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
POST/v1/system/mcp/servers/{serverName}/discover-toolsAuthorized
Asks a server which tools it offers.
Notes
Discovery is what makes a profile reviewable: you grant named tools rather than "whatever this server exposes".
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
serverName | path | string | Required | Server to query. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/servers/jira/discover-tools" | jqResponses
Tools the server offers.
{
"server": "jira",
"tools": ["search", "get_issue", "create_issue"]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The server is incomplete or disabled. | Finish configuring it. |
| 404 | No server with that name. | Confirm the name. |
| 502 | The server could not be reached. | Test the server first. |
| 500 | The discovery response could not be read. | The server answered something unexpected. |
Side effects
- Makes one real call to the server.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
GET/v1/system/mcp/profilesAuthorized
Lists platform MCP profiles.
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/profiles" | jqResponses
MCP profiles.
[{
"name": "jira-readonly",
"enabled": true,
"allowed_scopes": ["platform/production"],
"servers": [{ "server": "jira", "tools": ["search", "get_issue"] }]
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
POST/v1/system/mcp/profilesAuthorized
Creates an MCP profile.
Notes
allowed_scopes bounds where the profile may be used, which is how a production-only tool set stays out of development runs.
Call it
curl -sX POST "$NOPSAI_URL/v1/system/mcp/profiles" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"jira-readonly","servers":[{"server":"jira","tools":["search","get_issue"]}],"allowed_scopes":["platform/production"]}' | jqResponses
Profile created.
{
"name": "jira-readonly",
"enabled": true,
"allowed_scopes": ["platform/production"],
"servers": [{ "server": "jira", "tools": ["search", "get_issue"] }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown server, an empty tool list, or an unusable scope. | Discover the server’s tools first. |
| 500 | The profile could not be stored. | Retry. |
Side effects
- Creates a grantable tool set.
- Writes an audit record.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
GET/v1/system/mcp/profiles/{profileName}Authorized
Reads one MCP profile.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile name. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/profiles/jira-readonly" | jqResponses
The profile.
{
"name": "jira-readonly",
"enabled": true,
"allowed_scopes": ["platform/production"],
"servers": [{ "server": "jira", "tools": ["search", "get_issue"] }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No profile with that name. | Confirm the name. |
Side effects
- None.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
PUT/v1/system/mcp/profiles/{profileName}Authorized
Creates or replaces an MCP profile.
Notes
This is the only place tool access narrows. Pipeline, step, and task profiles are additive at run time.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile name. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/mcp/profiles/jira-readonly" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"servers":[{"server":"jira","tools":["search"]}]}' | jqResponses
Profile stored.
{
"name": "jira-readonly",
"enabled": true,
"allowed_scopes": ["platform/production"],
"servers": [{ "server": "jira", "tools": ["search", "get_issue"] }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown server or tool. | Rediscover the server’s tools; the offer may have changed. |
| 500 | The profile could not be stored. | Retry. |
Side effects
- Changes what every pipeline naming the profile can reach.
- Writes an audit record.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
DELETE/v1/system/mcp/profiles/{profileName}Authorized
Deletes an MCP profile.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/profiles/jira-readonly" -w "%{http_code}\n"Responses
Profile deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The name is missing. | Name the profile. |
| 404 | No profile with that name. | It may already be deleted. |
| 409 | A pipeline still names the profile. | Remove the reference first. |
| 500 | The delete failed. | Retry. |
Side effects
- Writes an audit record.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
POST/v1/system/mcp/profiles/{profileName}/testAuthorized
Tests every server a profile names.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
profileName | path | string | Required | Profile to test. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/mcp/profiles/jira-readonly/test" | jqResponses
Per-server results.
{
"profile": "jira-readonly",
"servers": [{ "server": "jira", "ok": true }]
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The profile is empty or disabled. | Add at least one server. |
| 404 | No profile with that name. | Confirm the name. |
| 502 | A server could not be reached. | Test that server directly to isolate it. |
| 500 | The test could not run. | Platform fault. |
Side effects
- Makes one real call per server in the profile.
Proven by
services/nopsai/mcp_config_test.goservices/nopsai/mcp_registry.go
How it works
Read the three registries as three separate questions. A model profile answers "which provider and model may this run use", an agent role answers "how should it behave", and an MCP profile answers "which tools may it call". Conflating them is the usual reason a change has no effect: raising a governance level does not add tools, and swapping an agent role does not change the model.
The 409 on delete is deliberate. A model or profile that a pipeline still names cannot be removed, so the failure happens at configuration time with a clear message rather than at the next run with an authorization error.
Test after every credential rotation. A rotated key that was never tested fails at the least convenient moment, and the test route is one call.
Implementation evidence
services/nopsai/llm_profiles.goModel profile storage, defaults, and provider tests.
services/nopsai/agent_profiles.goAgent role storage, usage, and validation.
services/nopsai/mcp_registry.goMCP server registration, discovery, and profiles.

