Skip to main content
Version: ERPBridge + bridgectl ยท v0.5.0-alpha.2

MCP tool schema reference (V2)

ERPBridge uses a Kubernetes-style declarative resource format to define tools. This format versions tools, makes their intent clear, and separates them from the ERP structure.

Resource structureโ€‹

A tool definition is composed of four main sections: apiVersion/kind, metadata, spec, and status (internal only).

Tool resource skeleton
apiVersion: erpbridge.io/v1
kind: MCPTool
metadata:
name: list_employees
version: 1.2.0
module: hr
spec:
description: { ... }
annotations: { ... }
inputSchema: { ... }
execution: { ... }
security: { ... }
cache: { ... }
routing: { ... }

Field definitionsโ€‹

metadataโ€‹

Identity and grouping information. On first apply, ERPBridge records a canonical executable-content SHA-256 digest and server-authored admission bookkeeping. The digest includes the tool contract, schemas, policy, and ERP mapping. It excludes runtime lifecycle fields and review bookkeeping. Identical reapply requests are idempotent. Changed content under an admitted name and version returns REGISTRY_CONFLICT; create and review a new version.

The first active version becomes the unqualified serving revision. Set isServing: true while applying an admitted active version to move that pointer. Other active versions remain available through their exact MCP names or resource digests.

  • name: (String) Unique identifier. Use intent-based names such as list_employees, not technical names such as get_resource_employee.
  • version: (String) SemVer version (e.g., 1.0.0).
  • module: (String) Logical grouping for access control and organization.
  • isActive: (Boolean, optional) Soft-delete flag. When false, the exact revision cannot execute but stays in the registry.
  • isServing: (Boolean, optional) Selects this active version for future unqualified calls. It does not change in-flight calls.

spec.descriptionโ€‹

High-signal information to help the LLM select the correct tool.

  • short: (String) Concise summary of what the tool does.
  • whenToUse: (Array) List of scenarios where this tool is appropriate.
  • whenNotToUse: (Array) List of similar scenarios where you must not use this tool.
  • examples: (Array) Sample user queries that trigger this tool.

spec.annotationsโ€‹

Optional MCP behavioral hints. These fields are additive and do not change the input or output schema.

  • title: (String, optional) Human-readable display title.
  • readOnlyHint: (Boolean, optional) Whether the tool is expected not to modify its environment.
  • destructiveHint: (Boolean, optional) Whether a modifying operation may be destructive.
  • idempotentHint: (Boolean, optional) Whether repeated calls with the same arguments are expected to have no additional effect.
  • openWorldHint: (Boolean, optional) Whether the tool interacts with external entities.

Generated annotations are method-based draft hints. Review them before applying a generated manifest. They do not replace authorization or other server-side controls.

spec.rateLimit and spec.concurrencyโ€‹

Optional execution protection for high-volume or expensive tools.

  • rateLimit.requestsPerSecond: Positive finite per-tool token-bucket rate.
  • rateLimit.burst: Positive per-tool token-bucket burst size.
  • concurrency.limit: Positive maximum number of active calls.
  • concurrency.perPrincipal: When true, apply the active-call limit per authenticated principal or MCP session; when false, share it across the tool. The global principal/session limiter remains in effect for every tool.

Unconfigured tools retain the server defaults. Side-effecting ERP methods are not automatically retried.

MCP discovery metadataโ€‹

During MCP tools/list, ERPBridge projects existing guidance into namespaced _meta keys:

  • io.erpbridge/whenToUse
  • io.erpbridge/whenNotToUse
  • io.erpbridge/examples
  • io.erpbridge/allowedRoles
  • toolplane.resourceDigest
  • toolplane.resourceVersion
  • toolplane.serving

Every active revision also appears under a protocol-safe exact name ending in .rev_<encoded-version>. The .rev_ marker is reserved and cannot occur in a declarative tool name. An MCP client can instead send the discovered digest as params._meta.toolplane.resourceDigest. Exact and digest-bound calls validate and execute that active revision and never fall forward to another revision. ERPBridge clones the selected resource before schema validation and middleware construction, so serving-pointer changes do not alter an in-flight call. Cache entries are scoped by resource digest, or by version for programmatic tools without a digest.

These values are informational. allowedRoles describes the server's tool allow-list; it does not grant access or replace server-side authorization. MCP clients may show or ignore custom _meta, and they may not forward it to the model context.

spec.inputSchemaโ€‹

Standard JSON Schema defining the arguments. Strict typing is required.

  • Use properties to define fields and required to enforce them.
  • Nested objects use nested properties and required; arrays use items.
  • Avoid "filters" strings: Break down complex query requirements into individual typed properties.

spec.outputSchemaโ€‹

Optional JSON Schema describing the shape of a successful ERP response. When present, the server validates every 2xx response against it at runtime using jsonschema/v6.

  • A response that violates the schema fails the tool call with a validation error instead of returning malformed data to the agent.
  • Generated schemas come from the OpenAPI 200/201 response definitions and are dereferenced (no $ref).
  • The schema must have a non-empty top-level string type, such as object or array.
  • bridgectl tool validate rejects an untyped schema. MCP discovery omits an untyped legacy schema. Runtime accepts any JSON result for that legacy schema.
  • OpenAPI generation omits outputSchema when the response has no top-level type.

spec.executionโ€‹

Technical mapping to the ERP API.

  • type: (String) Execution style. Currently only http.
  • method: (String) GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, or TRACE.
  • endpoint: (String) The ERP API URL path.
  • approvedOrigins: (Array) Exact normalized scheme://host[:port] values allowed after runtime endpoint rewriting. When omitted, apply binds the effective origin derived from endpoint and the current ERP_BASE_URL. A later rewrite to another origin fails before connector entry.
  • mapping: (Map) Optional. Maps LLM arg names to ERP parameter names.
  • parameterLocations: (Map) Generated metadata mapping each LLM argument to path, query, header, or body. If absent, GET arguments use the query and other methods use one JSON object body for compatibility.
  • bodyArgument: (String) Generated complete-body argument for primitive or array JSON request bodies. Its value is serialized as the complete body instead of an object property.
  • responsePath: (String) Optional JSON key to extract from the ERP response. OpenAPI generation emits data only when the resolved top-level response schema is an object with a top-level data property; the output schema then describes that unwrapped value.

Generated path values are URL-escaped. Generated headers are allowlisted and cannot replace connector authentication or transport headers. Authorization, Proxy-Authorization, Cookie, Host, connection, transfer, upgrade, Content-Length, and Content-Type parameters are rejected during generation. A successful HEAD or 204 No Content response returns a successful nil result without JSON decoding.

spec.securityโ€‹

Authentication strategy.

  • authType: api-key, bearer, or basic.
  • authHeader: (Optional) The outbound HTTP header that carries the resolved credential, such as X-API-Key. When omitted, the connector uses Authorization for backward compatibility. This is a header name only; never put a credential value here.
  • credentialRef: A logical reference, normally the name of the environment variable containing the secret. Never embed raw secrets here.
  • credentialSource: (Optional) env or file; omitted means env. file reads <ERPBRIDGE_CREDENTIALS_DIR>/<credentialRef> immediately before each request and never falls back to the environment.
  • dataClass: (Optional string) Declared sensitivity: public, internal, pii, or restricted. Older manifests may omit this field.
  • allowedRoles: (Optional array) Roles that may call this tool. Values must match [a-z][a-z0-9_-]{0,63}, be unique, and contain no more than 32 roles. pii and restricted tools must provide at least one role.

When allowedRoles is present, the tool is guarded. MCP clients select a role with arguments.role; direct callers use X-ERPBridge-Role. The selector must be present in both the caller identity and the tool allow-list, and the MCP selector is removed before execution. A guarded tool cannot define or require its own role argument. Without allowedRoles, role remains an ordinary business argument. Use opaque, non-identifying role slugs; never use a person name, email, employee number, or ERP record. ERPBridge does not infer PII from arbitrary ERP response data. Authorization runs before cache lookup and ERP work.

Secrets never belong in schemas

The admission controller rejects tool definitions whose endpoint path contains secret-like patterns (e.g., token or key=). Credentials are resolved from environment variables at call time only.

spec.routingโ€‹

Optional hints that improve LLM tool selection.

  • priority: (Number) Relative preference when several tools could answer. Higher wins.
  • signals: (Array of strings) Positive trigger phrases for this tool.
  • antiSignals: (Array of strings) Phrases that must not trigger this tool.

spec.lifecycleโ€‹

Optional support status for a tool version.

  • status: stable, deprecated, or sunset.
  • deprecatedAt: (String, optional) ISO date when deprecation started.
  • sunsetAt: (String, optional) ISO date when the tool is removed.
  • replacement: (String, optional) Name of the tool that replaces this one.

Generated drafts and reviewed manifestsโ€‹

Generation is pure: bridgectl tool generate writes only to its selected output stream. Keep reviewed manifests under manifests/<module>/; do not create competing generated JSON files or use schemas/ as an authoritative source. Generated GET and HEAD tools default to a shared read-only cache with a five-minute TTL. Generated write methods default to no cache. Generated tools also include method-based annotation hints for recognized HTTP methods. Unknown methods retain only the HTTP-backed open-world hint. Review intent, roles, data class, annotations, schemas, and cache policy before applying.

The development-only system.*_test tools require MCP_ENABLE_TEST_TOOLS=true and must be absent from production discovery. RedisInsight is for local inspection and must remain loopback-only or opt-in.

Annotated exampleโ€‹

create_purchase_invoice โ€” full example
apiVersion: erpbridge.io/v1
kind: MCPTool
metadata:
name: create_purchase_invoice
version: 1.0.0
module: finance
spec:
description:
short: "Create a new purchase invoice draft."
whenToUse:
- "User wants to record a new supplier bill"
- "Adding an invoice to the finance module"
inputSchema:
type: object
properties:
supplier:
type: string
description: "The name of the vendor"
amount:
type: number
description: "Total invoice amount"
required: ["supplier", "amount"]
execution:
type: http
method: POST
endpoint: "/api/resource/Purchase Invoice"
responsePath: "data" # ERP returns { "data": { ... } }, we only want the inner object
security:
authType: api-key
authHeader: X-API-Key # Optional; defaults to Authorization
credentialRef: ERP_FINANCE_KEY # Logical reference; resolves from the environment by default
# credentialSource: file # Reads ERPBRIDGE_CREDENTIALS_DIR/ERP_FINANCE_KEY
allowedRoles: [finance_reader, finance_writer]
cache:
enabled: false # Don't cache write operations
flushOn: ["list_purchase_invoices"] # Flush list cache when a new one is created
flushOn is the schema field

The cache invalidation field is flushOn โ€” an array of tool names to flush when this tool runs. The older invalidateOn spelling is not read by the server and is ignored silently; use flushOn.

Transitioning from V1โ€‹

If you have old schemas, use bridgectl tool generate to convert them, or manually update the following:

  1. Wrap the schema in spec.
  2. Move name, version, module to metadata.
  3. Separate endpoint into execution and security.
  4. Use lowercase names with underscores.