MCP Integration
Endpoints for normalizing Model Context Protocol configuration, discovering the registered toolkit drivers and add-menu templates, and discovering tools from a remote MCP server.
The config-parsing, driver-listing, and template-listing routes need only an authenticated user. They are not AI-gated and carry no permission check. Tool discovery is the exception: it is AI-gated and requires write access to the toolkit's owner.
POST /api/toolkit-parse-config
Parse and normalize an MCP configuration into the shape Informer stores on a Toolkit.
Authentication: Required
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
config | string | Yes | The MCP configuration as a JSON string. The handler JSON.parses it server-side. |
config is a JSON string, not an object. The handler parses it and matches one
of five accepted shapes: a VS Code servers map, a Claude Desktop / Cursor
mcpServers map, a direct stdio entry ({ command }), a direct remote entry
({ url } or { type, url }), or a single wrapped entry. It returns the
normalized { name, slug, type, config } for the first entry it finds.
A stdio entry normalizes to type: "mcp-stdio" with config { command, defaultArgs, env } (note defaultArgs, not args). A remote entry normalizes
to type: "mcp-remote" with config { serverUrl, transportType } and optional
headers (note serverUrl/transportType, not url/transport). A type of
http maps to transportType: "streamable-http"; sse maps to sse.
Example Request (Claude Desktop format):
{
"config": "{\"mcpServers\":{\"filesystem\":{\"command\":\"npx\",\"args\":[\"-y\",\"@modelcontextprotocol/server-filesystem\",\"/workspace\"],\"env\":{\"LOG_LEVEL\":\"info\"}}}}"
}
Response:
Responds 200 with the normalized { name, slug, type, config } and a HAL
_links.self. The incoming args become defaultArgs on the normalized stdio
config; name is prettified from the entry key and slug is derived from it.
{
"config": "{\"mcpServers\":{\"filesystem\":{\"command\":\"npx\",\"args\":[\"-y\",\"@modelcontextprotocol/server-filesystem\",\"/workspace\"],\"env\":{\"LOG_LEVEL\":\"info\"}}}}"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-parse-config"
}
},
"name": "Filesystem",
"slug": "filesystem",
"type": "mcp-stdio",
"config": {
"command": "npx",
"defaultArgs": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/workspace"
],
"env": {
"LOG_LEVEL": "info"
}
}
}
Example Request (remote server URL):
{
"config": "{\"url\":\"https://mcp.example.com/sse\",\"type\":\"sse\",\"headers\":{\"Authorization\":\"Bearer example-token\"}}"
}
Response:
The direct remote shape normalizes to type: "mcp-remote", with the incoming
url surfaced as serverUrl and type surfaced as transportType. Optional
headers pass through unchanged.
{
"config": "{\"url\":\"https://mcp.example.com/sse\",\"type\":\"sse\",\"headers\":{\"Authorization\":\"Bearer example-token\"}}"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-parse-config"
}
},
"name": "",
"slug": "",
"type": "mcp-remote",
"config": {
"serverUrl": "https://mcp.example.com/sse",
"transportType": "sse",
"headers": {
"Authorization": "Bearer example-token"
}
}
}
Because config is JSON.parsed server-side, a bare command line such as
npx -y @modelcontextprotocol/server-filesystem /workspace is not valid JSON
and answers 400 ("Invalid JSON: ..."). Well-formed JSON that matches none of
the five accepted shapes also answers 400 ("Could not parse config...").
{
"config": "npx -y @modelcontextprotocol/server-filesystem /workspace"
}
{
"statusCode": 400,
"error": "Bad Request",
"message": "Invalid JSON: Unexpected token 'p', \"npx -y @mod\"... is not valid JSON"
}
GET /api/toolkit-drivers
List the registered toolkit driver types as a HAL collection.
Authentication: Required
Response:
A HAL collection under _embedded["inf:toolkit-driver"]. Each row is the full
driver descriptor, not just an id, name, and description.
| Field | Type | Description |
|---|---|---|
id | string | Driver id (mcp-stdio, mcp-remote, custom, informer, plus any from loaded bundles) |
name | string | Display name |
description | string | Driver description |
icon | string | Icon name |
image | string | Icon image path |
viewComponent | string | Client view component name |
executionLocation | string | client for mcp-stdio, server for the others |
allowUserInstances | boolean | Whether end users may create their own instances |
canManageTools | boolean | Whether tools can be managed on the toolkit |
supportsIntegration | boolean | Whether the driver can authenticate via an Integration |
configSchema | object | JSON Schema for the toolkit-level config (when applicable) |
instanceConfigSchema | object | JSON Schema for per-instance config (mcp-stdio only) |
The custom driver is named "Integration Toolkit" and the informer driver is
named "Informer API". Loaded bundles (for example, Ellucian ethos and
experience-tasks) contribute additional driver rows.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-drivers"
}
},
"_embedded": {
"inf:toolkit-driver": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-drivers/mcp-stdio"
}
},
"id": "mcp-stdio",
"name": "MCP Stdio",
"description": "Local MCP server via stdio. Runs on the client (Informer Go) and allows users to create their own instances.",
"icon": "terminal",
"image": "/images/icons/toolkit-client.svg",
"viewComponent": "mcpStdioToolkitView",
"executionLocation": "client",
"allowUserInstances": true,
"canManageTools": false,
"supportsIntegration": false,
"configSchema": "… configSchema (3 fields) omitted from docs",
"instanceConfigSchema": {
"type": "object",
"properties": {
"args": {
"type": "array",
"items": {
"type": "string"
},
"description": "Additional arguments for this instance"
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Environment variables to set"
},
"workingDirectory": {
"type": "string",
"description": "Working directory for the process"
}
}
}
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-drivers/mcp-remote"
}
},
"id": "mcp-remote",
"name": "MCP Remote",
"description": "Remote MCP server via SSE or HTTP. Runs on the server and can use integrations for authentication.",
"icon": "cloud",
"image": "/images/icons/toolkit-cloud.svg",
"viewComponent": "mcpRemoteToolkitView",
"executionLocation": "server",
"allowUserInstances": false,
"canManageTools": false,
"supportsIntegration": false,
"configSchema": "… configSchema (3 fields) omitted from docs"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-drivers/custom"
}
},
"id": "custom",
"name": "Integration Toolkit",
"description": "Create API tools that call external services via an Integration",
"icon": "build",
"image": "/images/icons/toolkit-server.svg",
"viewComponent": "customToolkitView",
"executionLocation": "server",
"allowUserInstances": true,
"canManageTools": true,
"supportsIntegration": true
},
"… 3 more items (6 total) — omitted from docs"
]
}
}
GET /api/toolkit-templates
List the toolkit add-menu templates as a detective-discovery collection.
Authentication: Required
Response:
A detective-discovery collection (server.dm("toolkit-builder")) under items,
with a HAL _links.self and start / count / total. Each entry is
{ detective, id, value }, where value is the add-menu descriptor.
| Field | Type | Description |
|---|---|---|
value.id | string | Template id (mcp, custom, informer, ...) |
value.group | string | Add-menu group (e.g. __top) |
value.priority | integer | Sort priority within the group |
value.name | string | Display name |
value.description | string | Display description |
value.icon | string | Icon class (e.g. fas fa-plug) |
value.type | string | Toolkit type this template creates |
The built-in detectives are mcp, custom, and informer. Loaded bundles add
more entries.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkit-templates"
}
},
"items": [
{
"detective": "00000000-0000-4000-8000-000000000001",
"id": "00000000-0000-4000-8000-000000000001:0",
"value": {
"id": "mcp",
"group": "__top",
"priority": 100,
"name": "MCP Server",
"description": "Connect to a Model Context Protocol (MCP) server by pasting its configuration.",
"icon": "fas fa-plug",
"type": "mcp"
}
},
{
"detective": "00000000-0000-4000-8000-000000000002",
"id": "00000000-0000-4000-8000-000000000002:0",
"value": {
"id": "custom",
"group": "__top",
"priority": 50,
"name": "Integration Toolkit",
"description": "Create an Integration Toolkit with API request Tools that you define.",
"icon": "fas fa-code",
"type": "custom"
}
},
{
"detective": "00000000-0000-4000-8000-000000000003",
"id": "00000000-0000-4000-8000-000000000003:0",
"value": {
"id": "informer",
"group": "__top",
"priority": 40,
"name": "Informer API Toolkit",
"description": "Create Tools that call Informer's internal API, authenticated as the current user.",
"icon": "fas fa-database",
"type": "informer"
}
}
],
"start": 0,
"count": 3,
"total": 3
}
POST /api/toolkits/{id}/discover
Discover tools from a remote MCP server and save them to the Toolkit.
Authentication: Required
Pre-blocks:
toolkit.lookup(params.id), which callsserver.methods.ai.verify(), so the route is AI-gated.permission.toolkit.write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Toolkit id (an mcp-remote toolkit) |
The handler delegates to the toolkit driver's discover(). For mcp-remote
this opens a live network connection to the configured remote MCP server and
queries its tool list. Discovery for mcp-stdio toolkits happens client-side in
Informer GO, not through this route.
Response:
The discovered tools, saved to the toolkit. Because discovery opens a live, non-deterministic network connection to an external MCP server, this route is not captured here.
This endpoint performs server-side discovery for mcp-remote toolkits.
mcp-stdio toolkits are discovered client-side in Informer GO and do not use
this route.