MCP Server Production Readiness

Validation checklist for SteelEngine's MCP server — what to run before shipping changes that touch the LLM-facing surface.

SteelEngine exposes its own MCP server at /api/mcp/steelengine (consumed by Claude Desktop, Cursor, and any external MCP client). This page is the checklist for verifying that surface is production-ready after changes. The legacy /api/mcp/copilot route remains available as a compatibility alias.

This is the server-side readiness check. For configuring MCP clients (external tools you connect to SteelEngine), see Using MCP Tools.

What the MCP server can expose

tools/list is principal-specific. The table below is the complete server catalog, not a promise that every authenticated client receives every tool or operation. Discovery can prune operation enums and oneOf branches, and omits a tool when no operation remains visible.

ToolResourceOperations
steelengine_workspacesWorkspaces9 ops including list, get, create, rename, update, member management
steelengine_workflowsWorkflows + workflow folders47 ops including block editing, run, deploy, version, clone/export/import, folder CRUD
steelengine_docsWorkspace docs + doc folders17 ops including read, write, patch, glob, search, folder CRUD
steelengine_tablesWorkspace tables + databases25 ops including row CRUD, schema, column management, import, database grouping
steelengine_appsApps (dashboard builder)15 ops including app/page CRUD, widget editing, validate, publish, rollback, versions
steelengine_knowledgeKnowledge bases18 ops including KB CRUD, documents, tags, connectors
steelengine_jobsScheduled jobs9 ops including create, pause, resume, logs
steelengine_envEnvironment variables + BYOK keys7 ops including list, get, set, delete, BYOK key management
steelengine_credentialsOAuth credentials + API keyslist, auth_link, rename, delete, generate_api_key
steelengine_mcp_serversWorkflow + external MCP servers8 ops
steelengine_skillsWorkspace skillslist, add, edit, delete
steelengine_memoryWorkspace memorylist, search, add, correct, delete
steelengine_logsExecution / workflow / job logsexecution_summary, workflow_logs, job_logs, audit
steelengine_platformPlatform discovery (blocks, triggers, docs search, VFS)9 read-only ops
steelengine_a2aA2A agents7 ops including list, get, create, update, publish/unpublish

The server advertises domain tools only. Natural-language subagent tools are not part of the public MCP contract; external clients should call the appropriate steelengine_* domain tool directly.

Pre-ship checklist

1. Type check passes

cd apps/web && bun run type-check

Must exit cleanly. TypeScript catches the most common breakage class (renamed types, missing imports, schema/handler drift). The MCP server code lives entirely under apps/web (lib/copilot/tools/mcp/, app/api/mcp/), so this one check covers the whole surface.

2. PBAC contract tests pass

Run the exact-authorization and discovery tests first:

cd apps/web && bunx vitest run \
  lib/mcp/copilot/pbac.test.ts \
  lib/mcp/copilot/tool-discovery.test.ts \
  app/api/mcp/copilot/route.test.ts

These tests must prove:

  • Catalog permission metadata resolves to the correct object and action.
  • tools/list prunes both the operation enum and discriminated oneOf branches.
  • A tool with zero visible operations is omitted.
  • tools/call re-authorizes a hidden or stale operation.
  • API-key scope is applied after owner PBAC authorization as a second cap.
  • A denial includes the catalog permission key and target when exact mapping is available.

3. Unit + integration tests pass

# bun:test files (MCP definitions, tool-id-aliases)
cd apps/web && bun test ./lib/copilot/tools/mcp/definitions.test.ts \
                       ./lib/copilot/tools/tool-id-aliases.test.ts

# vitest files (routes, hooks, services)
cd apps/web && bunx vitest run \
  lib/copilot/ \
  lib/folders/ \
  app/api/mcp/ \
  app/api/v1/ \
  app/api/workspaces/ \
  app/api/files/ \
  app/api/chat/ \
  hooks/queries/

Expected: ~1,300 tests pass.

4. End-to-end certification harness against a live deployment

mcp-certify.ts is the production gate. It calls every tool, every operation, against a real workspace and validates response envelopes, error codes, confirmation gates, and structured output contracts.

cd apps/web

# Required environment:
export STEELENGINE_BASE_URL="https://www.steelengine.com"   # or staging
export STEELENGINE_MCP_API_KEY="sk-steelengine-..."        # workspace API key
export STEELENGINE_ORGANIZATION_ID="org_..."                # required for workspace-create probes
# Optional:
export STEELENGINE_WORKSPACE_ID="wsp_..."                  # reuse existing workspace
export STEELENGINE_OAUTH_BEARER_TOKEN="..."                # if testing bearer auth path

bun run mcp:certify

To run the restricted-role gate, set STEELENGINE_MCP_PBAC_FIXTURES to a JSON array of pre-provisioned API-key fixtures. The harness requires scenarios named custom_role, group_assignment, explicit_deny, role_inheritance, organization_scope, workspace_scope, and api_key_scope. Each fixture can assert visible operations, hidden operations, and direct calls that must return permission_denied:

[
  {
    "name": "document reader with explicit table deny",
    "scenario": "explicit_deny",
    "apiKey": "sk-steelengine-...",
    "visible": [{ "tool": "steelengine_docs", "operation": "read" }],
    "hidden": [{ "tool": "steelengine_tables", "operation": "query_rows" }],
    "deniedCalls": [
      {
        "tool": "steelengine_tables",
        "args": { "operation": "query_rows", "tableId": "tbl_..." }
      }
    ]
  }
]

Provide at least one fixture for every required scenario in the complete array. This makes the live gate verify the actual persisted role, group, inheritance, deny, assignment-scope, and API-key configuration instead of only simulating those decisions in unit tests.

The harness exercises (in order):

  • Auth: unauthenticated requests rejected; invalid API key rejected; optional bearer-token auth
  • tools/list public contract
  • Workspace CRUD via steelengine_workspaces
  • All domain operation envelopes
  • Workflows + workflow folders
  • Docs + doc folders (including create_folder, rename_folder, move_folder, delete_folder cascade)
  • Tables
  • Knowledge bases
  • Jobs
  • Environment + credentials
  • MCP servers (workflow + external)
  • Skills + memory
  • Logs + platform discovery
  • Credential OAuth link generation through steelengine_credentials.auth_link
  • Response envelope + redaction invariants
  • Restricted custom-role fixtures: read-only discovery, create-without-update, group-derived access, explicit deny, workspace scope, and API-key scope intersection

Harness runs authenticate with STEELENGINE_MCP_API_KEY (the legacy copilot key variable is no longer read by the scripts).

5. Smoke test (lighter weight)

cd apps/web && bun run mcp:smoke

The smoke harness runs a single happy-path call against each tool. Useful for fast verification after small changes; not a replacement for the cert harness.

Production-readiness criteria

The MCP server is production-ready when:

  • Every domain tool has a rich description (when-to-use, when-not, ops, errors, examples, side effects) - see apps/web/lib/copilot/tools/mcp/domain-tools.ts
  • Every domain tool has per-op discriminated JSON Schema (oneOf branches keyed on operation) - see apps/web/lib/copilot/tools/mcp/per-op-schemas.ts
  • Every advertised operation declares one or more Permission Catalog requirements; no domain relies on the compatibility read/write/admin capability projection
  • Every catalog requirement resolves its target only after the execution plan has resolved workspace and resource ownership
  • tools/list filters operations from owner PBAC plus API-key scope and omits empty tools
  • tools/call performs the same exact check again and does not trust discovery
  • Custom-role, group, org-wide, workspace-scoped, inheritance, and explicit-deny cases are certified
  • Every destructive operation requires confirm: true and returns confirmation_required when missing
  • Every response is a structured envelope: {success, message?, data?, error?}
  • steelengine_credentials.auth_link returns structured OAuth URL data
  • No legacy steelengine_files references in tool descriptions or prompts
  • No legacy 'workspace_file' etc. canonical tool IDs (alias map handles historical chat replay)
  • Folder ops live on the parent resource tool (steelengine_docs.{,*}_folder, steelengine_workflows.{,*}_folder)
  • Public REST /api/v1/docs and /api/v1/docs/folders match MCP semantics
  • OpenAPI spec documents folderId on all relevant ops + DocMetadata.folderId
  • Audit log writes on every folder mutation (create, update, delete) regardless of surface

All of these must be enforced by the unit tests, the cert harness, or both. Exact PBAC metadata and call enforcement cover the complete domain and direct-tool catalog; changes must keep the zero-legacy-metadata grep gates and restricted-role certification green.

When something fails

  • Type check fails → fix the type error. Don't ship.
  • Unit test fails (new failure) → fix. Pre-existing failures on main are tracked but don't block.
  • Cert harness fails on a domain tool → that tool has a regression. The harness output tells you which operation. Check the dispatch in apps/web/app/api/mcp/copilot/route.ts for that tool's case; /api/mcp/steelengine currently re-exports that implementation.
  • Cert harness fails on response envelope (returned success=true without message or data) → the response builder for that operation forgot to set message or data. Common when extending a tool.
  • Cert harness fails on credential OAuth link shape → the credentials domain plan or OAuth URL extraction changed. Check apps/web/lib/mcp/copilot/domain-plans/credentials.ts and apps/web/lib/copilot/orchestrator/tool-executor/oauth-links.ts.

Building production-ready workflows + agents via MCP

The tool surface is intentionally designed so an LLM client can construct a full SteelEngine application without falling through to natural-language subagents:

  1. Discover the workspacesteelengine_platform.blocks to learn block types, steelengine_platform.triggers for trigger types, steelengine_platform.tool_search to discover integration tools by regex.
  2. Set up infrastructuresteelengine_credentials.auth_link to OAuth-connect external services; steelengine_env.set for API keys; steelengine_knowledge.create for KBs.
  3. Build workflowssteelengine_workflows.create_folder to organize, steelengine_workflows.create to make a workflow, then steelengine_workflows.edit_blocks for canvas edits.
  4. Iteratesteelengine_workflows.run / run_until_block / run_from_block for partial execution; steelengine_workflows.get_logs for execution logs.
  5. Deploysteelengine_workflows.deploy_api, deploy_chat, or deploy_mcp (all destructive + confirm-gated).
  6. Manage runtime artifactssteelengine_docs.{write,read,patch} for prompts/configs/outputs; steelengine_tables.{insert_row,query_rows} for structured data; steelengine_jobs.create for scheduled runs.

The MCP surface is the canonical way to programmatically build on SteelEngine. Every operation here has a matching dashboard control, but the dashboard is for humans; MCP is for agents and external code.

Capabilities the server advertises

The MCP server advertises only the tools primitive in its initialize response:

{ "capabilities": { "tools": {} } }

resources and prompts are not advertised today. This is intentional:

  • Tools are the right primitive for SteelEngine's API surface — every operation is an action the LLM can take.
  • Resources would let MCP clients reference docs/tables as MCP-native URIs. Not currently needed because every doc and table is already addressable via steelengine_docs and steelengine_tables tools. Adding resources would create two ways to do the same thing.
  • Prompts would expose canned prompt templates. Not currently needed because the LLM-facing prompt-building happens client-side.

If a future use case requires either, both can be added without breaking existing tools/* callers.

Idempotency and replay

The MCP server does not implement idempotency keys. Duplicate tools/call requests execute twice. This is by design: every mutating operation is either:

  1. Naturally idempotent (e.g. rename to the same name, set env var to the same value)
  2. Auto-deduplicated by the underlying handler (e.g. create_folder auto-suffixes on name collision)
  3. Confirm-gated and destructive (e.g. delete, deploy_*, revert_to_version) — the LLM must explicitly opt in with confirm: true, making accidental double-execution unlikely

If your client retries on transient failures, prefer exponential backoff over immediate retry, and check the previous call's outcome via the equivalent list/get operation before retrying a destructive write.

Error code reference

The MCP server emits these structured error codes via error.code on failed envelopes:

CodeWhen
invalid_paramsRequired field missing or fails JSON Schema validation
permission_deniedThe caller lacks the required catalog permission, an explicit deny applies, or the API-key scope cap excludes the target
not_foundTargeted resource doesn't exist or is soft-deleted
workspace_mismatchResource exists but belongs to a different workspace
confirmation_requiredDestructive op called without confirm: true
duplicate_nameAuto-suffix retry cap exhausted on a folder/resource name
cycle_detectedFolder move would create a parent-child cycle
ambiguous_table_nametableName resolves to multiple tables in the workspace
unsupported_operationOperation name not recognized for this tool
tool_failedUnderlying server tool raised; original error in error.details.output
internal_errorUnexpected error; check logs

Tool descriptions reference these by name in their ERRORS: section. Errors from the underlying tool layer (e.g. embedding failures, OAuth misconfiguration, storage quota) are surfaced as tool_failed with the original message in error.details.output rather than getting their own top-level codes — keeps the public contract small.

On this page