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
});| authType | Header sent to the MCP server |
|---|---|
| none | (no auth header) |
| bearer | Authorization: Bearer <authToken> |
| api_key | X-API-Key: <authToken> |
| basic | Authorization: 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 tool | CRUD op it maps to | Arguments |
|---|---|---|
| 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_countjavascript
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 field | Type | Default | What it does |
|---|---|---|---|
| allowed_schemas | string (comma-separated) | all non-system schemas | Restrict tool generation to just these schema names |
| read_only | boolean | false | Only generate list_/get_ tools — create_/update_ are omitted |
| rate_limit_per_minute | integer | 60 | Max tool calls per minute per client |
| mcp_api_key | string (encrypted) | domain API key | Key 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 method | service_id / wire operation | Required args |
|---|---|---|
| connectServer(p) | mcp_client / connect_server | serverUrl, name (authType/authToken optional) |
| listTools(p) | mcp_client / list_tools | serverUrl or serverName |
| callTool(p) | mcp_client / call_tool | serverUrl or serverName, toolName (arguments optional) |
| services.mcpServer method | service_id / wire operation | Required args |
|---|---|---|
| registerTools() | mcp_server / register_tools | none |
| handleToolCall(p) | mcp_server / handle_tool_call | toolName (arguments optional) |
| listResources() | mcp_server / list_resources | none |
| Operation | Permission required |
|---|---|
| mcp_client: call_tool | operational:create |
| mcp_client: connect_server | operational:create |
| mcp_client: list_tools | data:read |
| mcp_server: handle_tool_call | operational:create |
| mcp_server: register_tools | data:read |
| mcp_server: list_resources | data:read |
| Situation | What you get back |
|---|---|
| callTool without toolName | 400 — "tool_name required" |
| connectServer without serverUrl | 400 — "server_url required" |
| callTool/listTools reference an unregistered serverName with no serverUrl fallback | 404 — "MCP server not found: <name>" |
| handleToolCall with a tool name that does not exist for this domain | 404 — "Tool '<name>' not found" (response includes available_tools) |
| Target MCP server unreachable, times out, or fails its handshake | 502 — "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
- •Connect Your AI Editor over MCP — the separate MCP endpoint for pointing Claude Code, Cursor, or Copilot at Supero's App Builder itself
- •API Integrations — the full Managed Services Catalog quick start (import, configure, execute) that mcp_client and mcp_server both follow
- •Managed Integrations & Provider Catalog — browsing and configuring every catalog service, not just MCP
- •Workflows & Automation — service_call steps, event bindings, and saga-style compensation for wiring integrations into multi-step processes
On this page