mcp_opt_in_annotations
MCP opt-in annotations for CRUDService systems and workflows
This guide shows how to expose selected commands and workflows as MCP Tools with a tiny mcp block, without duplicating schemas. It also shows how to advertise read-only GET endpoints as MCP Resources via a simple resource sub-block.
Key principles
- Minimal annotations only when you want a command/workflow to be published
- No schema duplication: inputSchema is derived from your existing YAML (required_params, templated path variables, and Jinja
params.*usage)- For workflows, inputSchema is taken from the workflow's
inputsobject when present; otherwise a permissive object is used.
- For workflows, inputSchema is taken from the workflow's
- Short names: the generator enforces a ≤50 character tool name with stable hash suffix when needed
- Publish cap: set
MCP_MAX_TOOLS(default 80) to avoid huge catalogs- Health tool is always listed as both
system.healthandsystem_healthfor client compatibility
- Health tool is always listed as both
MCP block fields
- enabled: true|false (required to publish)
- name: short, stable tool name (optional; a compact fallback is derived)
- description: overrides the command/workflow description (optional)
- annotations: arbitrary metadata (e.g., tags)
- resource: optional block to publish an MCP Resource or Resource Template for GET-style reads
- uriTemplate: template with variables (e.g.,
.../users/{SystemIdentifier}) - or uri: fixed URI
- name, description, mimeType: resource metadata
- uriTemplate: template with variables (e.g.,
What the server derives automatically
- inputSchema: built from
required_params, path placeholders inendpoint, and allparams.*references found in the command. All properties default tostring, and the schema allows additionalProperties to remain permissive. - description: from
mcp.descriptionor the command/workflowdescription. - name: from
mcp.name, otherwise<system>.<object>.<action>compacted and capped to ≤50 chars.
Environment variables
- MCP_MAX_TOOLS: hard cap for number of published tools (default: 80)
- MCP_LOOPBACK_ONLY_HEALTH: set to
trueto publish only the built-insystem.healthtool
Examples using real command definitions
- Entra: account.get_user_by_id (Tool + Resource Template)
Add this under
ServiceConfigs/CRUDService/config/system_types/azure_system.yaml:
object_types:
account:
commands:
get_user_by_id:
endpoint: "/users/{{ params.SystemIdentifier }}"
method: GET
# Publish as MCP Tool (opt-in)
mcp:
enabled: true
name: "entra.account.get_by_id"
description: "Get a user by ID"
annotations:
tags: ["read","directory","account"]
# Also publish a Resource Template for HTTP views
resource:
uriTemplate: "https://api.ocg.labs.empowernow.ai/users/{SystemIdentifier}"
name: "User by ID"
description: "HTTP view of the user object"
mimeType: "application/json"
- Entra: group.get_group_by_id (Resource Template only)
object_types:
group:
commands:
get_group_by_id:
endpoint: "/groups/{{ params.SystemIdentifier }}"
method: GET
mcp:
enabled: true
name: "entra.group.get_by_id"
description: "Get a group by ID"
annotations:
tags: ["read","directory","group"]
resource:
uriTemplate: "https://api.ocg.labs.empowernow.ai/groups/{SystemIdentifier}"
name: "Group by ID"
description: "HTTP view of the group object"
mimeType: "application/json"
- Entra: device.get_device_by_id (Tool only)
object_types:
device:
commands:
get_device_by_id:
endpoint: "/devices/{{ params.device_id }}"
method: GET
mcp:
enabled: true
name: "entra.device.get_by_id"
description: "Get a device by ID"
annotations:
tags: ["read","device"]
- Auth0: account.GetUserByID (Tool + Resource Template)
Add this under
ServiceConfigs/CRUDService/config/system_types/auth0.yaml:
object_types:
account:
commands:
GetUserByID:
endpoint: "/users/{{ params.SystemIdentifier }}"
method: GET
description: "Retrieve detailed account information for a specific user"
mcp:
enabled: true
name: "auth0.account.get_by_id"
description: "Get Auth0 user by ID"
annotations:
tags: ["read","auth0","account"]
resource:
uriTemplate: "https://dev-zfvv8yp5jcpjdmpf.us.auth0.com/api/v2/users/{SystemIdentifier}"
name: "Auth0 User by ID"
description: "HTTP view of the Auth0 user"
mimeType: "application/json"
Workflows (opt-in)
- The workflow schema now allows a top-level
mcpobject. The generator usesinputsas the toolinputSchema.
- Entra user view (published)
name: "av_entraid_ocg_view_user"
description: >
A workflow that retrieves account information for an EntraID account and displays all of its fields in a form.
# MCP opt-in
mcp:
enabled: true
name: "wf.entraid.user.view"
description: "View an Entra user by ID"
annotations:
tags: ["workflow", "read", "entra"]
resource:
uriTemplate: "https://graph.microsoft.com/v1.0/users/{SystemIdentifier}"
name: "User by ID (workflow)"
description: "HTTP view of the user object"
mimeType: "application/json"
inputs:
type: object
required: ["SystemIdentifier"]
properties:
SystemIdentifier: { type: string, description: "The unique identifier of the EntraID user" }
- Entra group view (published)
name: "av_entraid_ocg_view_group"
description: >
A workflow that retrieves group information for an EntraID group and displays all of its fields in a form.
# MCP opt-in
mcp:
enabled: true
name: "wf.entraid.group.view"
description: "View an Entra group by ID"
annotations:
tags: ["workflow", "read", "entra"]
resource:
uriTemplate: "https://graph.microsoft.com/v1.0/groups/{SystemIdentifier}"
name: "Group by ID (workflow)"
description: "HTTP view of the group object"
mimeType: "application/json"
inputs:
type: object
required: ["SystemIdentifier"]
properties:
SystemIdentifier: { type: string, description: "The unique identifier of the EntraID group" }
How tools are listed
- REST discovery:
GET /mcp/tools/list(BFF proxy:GET /api/crud/mcp/tools/list) - JSON-RPC discovery:
POST /mcp/jsonrpcwith{ "method": "tools/list" }(BFF proxy:POST /api/crud/mcp/jsonrpc) - The server always includes
system.health; other tools appear only whenmcp.enabled: true.
Notes
- Add
mcpInstanceto each system to label provider/instance (provider,instance,instance_label,env). - Names are namespaced by default to avoid collisions across providers/instances. Fallback uses suffix
@systemwhen identity is missing. - Only safe, read‑only operations should be exposed as MCP Resources; tools can represent any action.
- The workflow JSON schema allows an optional top‑level
mcpblock. - Verify
-
- List tools and confirm your tool appears
- REST:
GET /mcp/tools/listor JSON‑RPCtools/list
-
- Call health via JSON‑RPC
tools/callwith{ name: "system.health", arguments: { verbose: true } }(orsystem_health).
- Call health via JSON‑RPC
-
- Invoke one enabled Azure/Auth0 tool with required params.
Cursor quick test
{
"mcpServers": {
"crud-mcp": {
"type": "streamable-http",
"url": "https://api.ocg.labs.empowernow.ai/api/crud/mcp/jsonrpc",
"headers": {
"Authorization": "Bearer <ACCESS_TOKEN_WITH_scopes_mcp.tools.discovery_mcp.tools.invoke>",
"Content-Type": "application/json"
}
}
}
}
In Cursor, enable crud-mcp, then ask: “Run the CRUD MCP health check.”
- Keep tool names stable across releases; if you must rename, consider leaving the old name enabled for one release with a deprecated tag in
annotations.
See also
- Opt‑in authors guide:
../how-to/mcp-opt-in-authors-guide.md - Tool Catalogue & Naming:
../explanation/mcp_tool_catalogue_naming.md - API Reference:
./mcp_api_reference.md - Tutorials:
./mcp_tool_recipes.md