S
supero.docs
Developers/MCP/MCP Integration Services

MCP Integration Services

How a generated app calls external MCP tool servers (services.mcpClient) and exposes its own schemas as MCP tools (services.mcpServer) — the services.* wrapper pair for Model Context Protocol integrations.

Overview

This page is about the two MCP integration services a GENERATED app can call — mcp_client and mcp_server — not about connecting your own AI editor to Supero's App Builder. If you are looking to point Claude Code, Cursor, or Copilot at Supero to build and deploy apps, see the Connect Your AI Editor over MCP page instead. Everything below is for app.js / workflow logic running inside an app your users interact with.
Like email, Slack, or Stripe, mcp_client and mcp_server are Managed Services Catalog entries. They are called the same way as every other integration — through the global services.* proxy from ui/app.js, or as a service_call step inside a workflow — and every call is logged as an ordinary CRUD record you can query later.
  • •services.mcpClient — a real JSON-RPC 2.0 MCP client. Connect to, list tools on, and call tools on any external MCP-compatible server (Jira, Figma, Slack-style tool servers, or your own).
  • •services.mcpServer — generates MCP tool definitions (list_/get_/create_/update_) from your app's own domain schemas and executes them against the CRUD API. It is a tool-shape generator, not a hosted MCP listener external clients connect to.
javascript
services.mcpClient.callTool({
  serverUrl: "https://mcp.example.com/sse",
  toolName: "create_issue",
  arguments: { project: "ENG", summary: "Bug reported from the app" }
}).then(function (r) {
  showToast(r.output.result_text, "success");
});

ℹ️ Importing the service first

Like any catalog service, mcp_client and mcp_server must be imported into your project before app.js can call them. Add their service_id to config.py's services list (services = ["mcp_client", "mcp_server"]), or import them via POST /api/v1/services/import in the raw REST flow. See the Managed Integrations & Provider Catalog page for the full import/configure lifecycle.

services.mcpClient — calling an external MCP server

mcp_client speaks JSON-RPC 2.0 over HTTP to any MCP-compatible server: it sends initialize, then tools/list or tools/call, and returns the result through the usual service envelope. It supports bearer, api_key, and basic auth against the target server.

connectServer — register and validate a server (optional)

connectServer round-trips initialize + tools/list against the target before recording it, so a bad URL or bad credentials fail immediately instead of on your first real call. Registering is optional — callTool and listTools both accept a raw serverUrl directly without a prior connectServer call.
javascript
services.mcpClient.connectServer({
  serverUrl: "https://mcp.atlassian.com/sse",
  name: "jira",
  authType: "bearer",
  authToken: jiraAccessToken
});
authTypeHeader sent to the MCP server
none(no auth header)
bearerAuthorization: Bearer <authToken>
api_keyX-API-Key: <authToken>
basicAuthorization: Basic <base64(authToken)>

listTools and callTool

Once a server is registered, reference it by serverName instead of repeating the URL and auth every time. If you skip connectServer, pass serverUrl directly — an unregistered URL is treated as an anonymous, unauthenticated server.
javascript
var tools = (await services.mcpClient.listTools({ serverName: "jira" })).output;
// tools.tools -> [{ name, description, inputSchema }, ...]

var r = (await services.mcpClient.callTool({
  serverName: "jira",
  toolName: "create_issue",
  arguments: { project: "ENG", summary: form.summary, description: form.description }
})).output;

if (r.is_error) {
  showToast("Jira call failed: " + r.result_text, "error");
} else {
  showToast("Filed: " + r.result_text, "success");
}
json
{
  "success": true,
  "service_id": "mcp_client",
  "operation": "call_tool",
  "output": {
    "record_uuid": "mccall-a1b2c3d4-...",
    "mcp_server_name": "jira",
    "mcp_tool_name": "create_issue",
    "result_text": "Created issue ENG-482",
    "result_content": [{ "type": "text", "text": "Created issue ENG-482" }],
    "is_error": false,
    "mcp_call_duration_ms": 812.4,
    "mcp_call_status": "completed",
    "status": "completed",
    "provider": "mcp",
    "request_id": "..."
  }
}

💡 arguments can be an object or a JSON string

Pass arguments as a plain object — mcp_client also accepts a JSON-encoded string for the arguments field and parses it for you, which is convenient when a value is coming out of a workflow template.

Worked example: a workflow step to an external tool server

mcp_client is equally usable from a workflow's service_call step, which is the right place for it when the call is triggered by an event binding or needs saga-style error handling rather than a direct button click. input_map keys are the server-side wire field names (server_url, tool_name, arguments), not the JS wrapper's camelCase args.
python
WORKFLOW_DEFINITIONS = [
  {
    "id": "wf_file_bug",
    "name": "File a bug in Jira",
    "input_schema": {
      "summary": {"type": "string", "required": True},
      "description": {"type": "string", "required": True},
      "reporter_email": {"type": "string", "required": True},
    },
    "steps": [
      {
        "id": "create_issue",
        "type": "service_call",
        "service": "mcp_client",
        "operation": "call_tool",
        "input_map": {
          "server_name": "jira",
          "tool_name": "create_issue",
          "arguments": {
            "project": "ENG",
            "summary": "{{input.summary}}",
            "description": "{{input.description}}",
          },
        },
        "on_error": "continue",
      },
      {
        "id": "notify",
        "type": "crud_operation",
        "operation": "update",
        "object_type": "<namespace>:bug_report",
        "record_uuid": "{{input.record_uuid}}",
        "data": { "workflow_status": "processed", "processed_at": "{{context.timestamp}}" },
      },
    ],
  },
]
Trigger it from app.js with services.workflow.run, or wire it to an EVENT_BINDINGS entry so it fires automatically on a CRUD event (e.g. a new bug_report record):
javascript
services.workflow.run("wf_file_bug", {
  summary: form.summary,
  description: form.description,
  reporter_email: currentUser.email,
  record_uuid: report.uuid
});

services.mcpServer — generating MCP tools from your schemas

mcp_server reads your domain's schemas over the platform's schema API and turns each non-system schema into four MCP tool definitions, mapped straight onto the CRUD API. It is a schema-to-tool-shape generator and executor you call from your own app.js or workflow logic — it does not stand up a public MCP endpoint that an external client like Claude Desktop connects to directly.
Generated toolCRUD op it maps toArguments
list_{schema}GET /api/v1/crud/{domain}/{type}limit (optional), status (optional)
get_{schema}GET /api/v1/crud/{domain}/{type}/{uuid}uuid (required)
create_{schema}POST /api/v1/crud/{domain}/{type}name (required) plus the schema's required fields
update_{schema}PUT /api/v1/crud/{domain}/{type}/{uuid}uuid (required) plus any fields to change
javascript
var reg = (await services.mcpServer.registerTools()).output;
// reg.tools       -> [{ name: "list_property", description, inputSchema, ... }, ...]
// reg.schemas     -> ["property", "booking", ...]
// reg.tool_count / reg.schema_count
javascript
var r = (await services.mcpServer.handleToolCall({
  toolName: "create_property",
  arguments: { name: "Lakeview Cabin", address: "12 Lake Rd", nightly_rate: 220 }
})).output;
// r.result.content -> [{ type: "text", text: "Created property: uuid=..." }]
listResources returns the same schemas as MCP resource descriptors (uri, name, description, mimeType) instead of tool definitions — useful if the caller wants to browse what is available before deciding which tool to invoke.
Config fieldTypeDefaultWhat it does
allowed_schemasstring (comma-separated)all non-system schemasRestrict tool generation to just these schema names
read_onlybooleanfalseOnly generate list_/get_ tools — create_/update_ are omitted
rate_limit_per_minuteinteger60Max tool calls per minute per client
mcp_api_keystring (encrypted)domain API keyKey used when handleToolCall executes the underlying CRUD request

⚠️ Not a hostable listener

registerTools, handleToolCall, and listResources are ordinary service calls — you invoke them yourself and do something with the result (render a menu, execute a call, log it). mcp_server does not open a public MCP endpoint that an external MCP client can connect to and drive on its own. If your goal is letting an AI editor build or manage a Supero app itself, that is the separate flow on the Connect Your AI Editor over MCP page.

Reference: methods, permissions, and errors

services.mcpClient methodservice_id / wire operationRequired args
connectServer(p)mcp_client / connect_serverserverUrl, name (authType/authToken optional)
listTools(p)mcp_client / list_toolsserverUrl or serverName
callTool(p)mcp_client / call_toolserverUrl or serverName, toolName (arguments optional)
services.mcpServer methodservice_id / wire operationRequired args
registerTools()mcp_server / register_toolsnone
handleToolCall(p)mcp_server / handle_tool_calltoolName (arguments optional)
listResources()mcp_server / list_resourcesnone
OperationPermission required
mcp_client: call_tooloperational:create
mcp_client: connect_serveroperational:create
mcp_client: list_toolsdata:read
mcp_server: handle_tool_calloperational:create
mcp_server: register_toolsdata:read
mcp_server: list_resourcesdata:read
SituationWhat you get back
callTool without toolName400 — "tool_name required"
connectServer without serverUrl400 — "server_url required"
callTool/listTools reference an unregistered serverName with no serverUrl fallback404 — "MCP server not found: <name>"
handleToolCall with a tool name that does not exist for this domain404 — "Tool '<name>' not found" (response includes available_tools)
Target MCP server unreachable, times out, or fails its handshake502 — "MCP tool call failed: ..." or "Cannot connect to MCP server: ..."
Every call also writes an audit record you can query with ordinary CRUD: mcp_client logs to the mcpc: namespace (server_connection, tool_call), mcp_server logs to the mcp: namespace (tool_registry, call_log). Both carry the standard uuid/created_at/updated_at system fields.
Calling either service through the raw REST API (rather than the services.* wrapper) uses the same execute endpoint every catalog service does — POST /api/v1/services/execute with { service_id, operation, input }, where input holds the wire-field arguments shown in the tables above.

Next steps