How to Integrate External Services with OpenWork: A Complete MCP Integration Guide

OpenWork integrates external services like Notion, Linear, and Stripe through the Model-Context-Protocol (MCP), enabling AI agents to discover and execute tools via a standardized OAuth-based lifecycle.

OpenWork uses the Model-Context-Protocol (MCP) to expose third-party APIs as callable capabilities for AI agents. To integrate external services with OpenWork, you will interact with the core MCP client in ee/apps/den-api/src/capability-sources/external-mcp-client.ts and the connection management routes defined in ee/apps/den-api/src/routes/org/mcp-connections.ts.

Understanding the MCP Integration Lifecycle

Every external service integration follows a five-phase lifecycle implemented in the OpenWork codebase:

  1. Discovery – OpenWork fetches the MCP server’s metadata, including OAuth endpoints and available tools, via the ExternalMcpClient class in external-mcp-client.ts.

  2. OAuth Registration – For services requiring authentication, OpenWork either registers a dynamic client or uses pre-registered credentials. The client metadata document is served at /oauth/client-metadata.json as documented in docs/external-mcp-oauth.md.

  3. Authorization – The user or admin completes the OAuth flow, and tokens are stored encrypted in Den via the routes in mcp-connections.ts.

  4. Tool Catalog Retrieval – OpenWork queries the MCP server’s tools/list endpoint, filters the results, and enforces size limits using validateToolCatalogField and measureSerializedJson.

  5. Tool Execution – Agents invoke tools through callExternalMcpTool, which forwards arguments to the MCP server and returns structured results.

Adding an External MCP Connection

You can add connections to external services either through predefined presets or by configuring custom MCP servers.

Quick-Add with Presets

OpenWork ships with EXTERNAL_MCP_PRESETS in ee/apps/den-api/src/capability-sources/external-mcp-presets.ts, providing one-click setup for popular services:

export const EXTERNAL_MCP_PRESETS = [
  { presetId: "notion", displayName: "Notion", url: "https://mcp.notion.com/mcp", authType: "oauth" },
  { presetId: "linear", displayName: "Linear", url: "https://mcp.linear.app/mcp", authType: "oauth" },
];

To create a connection using a preset or custom URL, send a POST request to the connections endpoint:

POST /v1/mcp-connections
{
  "name": "My Notion Connection",
  "url": "https://mcp.notion.com/mcp",
  "authType": "oauth",
  "credentialMode": "shared"
}

The API returns a connection ID and, if OAuth is required, an authorizeUrl for the admin to visit.

Custom MCP Servers

For services not listed in presets, supply the MCP server URL directly. The externalMcpUrlSchema in mcp-connections.ts validates that URLs use HTTP/HTTPS protocols, contain no fragments, and embed no credentials.

Implementing the OAuth Flow

When authType is set to oauth, complete the following steps to authorize the connection:

  1. Start the flow by calling the connect endpoint:
POST /v1/mcp-connections/:id/connect/start

If a valid token exists, the response returns {status: "connected"}. Otherwise, you receive {status: "needs_auth", authorizeUrl: "..."}.

  1. Redirect the admin to the authorizeUrl. The URL contains a signed state token generated by createOAuthStateToken.

  2. Complete authorization on the MCP provider site. The provider redirects back to OpenWork’s callback URL at /v1/mcp-connections/oauth/callback.

  3. Process the callback using handleExternalMcpOAuthCallback in mcp-connections.ts, which validates the signed state, discovers the expected issuer, and calls completeExternalMcpAuth to store tokens.

  4. Token storage – Encrypted tokens are saved in ExternalMcpConnectionRow (for shared mode) or ConnectedAccountTable (for per-member mode).

All callback and metadata URLs derive from the DEN_API_PUBLIC_URL environment variable.

Retrieving the Tool Catalog

After authorization, fetch available tools for a connection:

GET /v1/mcp-connections/:id/tools

This endpoint executes listExternalMcpTools, which:

  • Uses the MCP SDK’s client.listTools with pagination limits via collectExternalMcpToolPages.
  • Enforces strict size limits (512 KB maximum) on tool names, titles, descriptions, and JSON schemas.
  • Returns an array of ConnectionTool objects containing name, title, description, inputSchema, and outputSchema.

Executing Tools from Agents

Agents invoke external tools through the execution endpoint:

POST /v1/mcp-connections/:id/tools/:toolName/call
{
  "arguments": { "query": "search-term" }
}

The handler executes runExternalMcpToolCall (aliased as callExternalMcpTool), which:

  1. Builds an MCP client using buildClient and binds it to an ExternalMcpOAuthProvider transport.
  2. Invokes client.callTool({name, arguments}) inside runExternalMcpRequestWithinDeadline to enforce the EXTERNAL_MCP_TOOL_CALL_TIMEOUT_MS deadline.
  3. Returns the raw result or a structured error (tool_execution_failed).

Configuring Permissions and Access Control

OpenWork provides granular access control for external connections through two mechanisms:

Credential Mode – Set during connection creation:

  • shared: Stores a single encrypted token accessible to all permitted members.
  • per_member: Requires each user to authorize individually, storing tokens in ConnectedAccountTable.

Access Grants – Control who can use the connection via the access field supporting orgWide, memberIds, or teamIds scopes. Admins manage grants through:

POST /v1/mcp-connections/:id/access
{
  "access": { "orgWide": false, "memberIds": ["U123"], "teamIds": [] }
}

Permission checks occur in memberCanUseExternalMcpConnection and listVisibleExternalMcpConnections.

Debugging with Diagnostics

OpenWork emits structured diagnostics for every MCP phase defined in EXTERNAL_MCP_DIAGNOSTIC_PHASES. Errors include a referenceId, phase, code, and actionable suggestions via externalMcpDiagnosticForLog and externalMcpDiagnosticForResponse in external-mcp-diagnostics.ts.

For example, if a tool's JSON schema exceeds 512 KB, the system throws MCP_CATALOG_TOOL_SCHEMA_SIZE_LIMIT with an operator hint to reduce schema complexity.

Summary

  • OpenWork uses MCP (Model-Context-Protocol) to standardize external service integration through the external-mcp-client.ts core implementation.
  • Connections support preset configurations for popular services or custom URLs validated against externalMcpUrlSchema.
  • OAuth flows require calling the connect start endpoint, redirecting to the authorizeUrl, and completing the callback handled by handleExternalMcpOAuthCallback.
  • Tools are retrieved via listExternalMcpTools with strict size validation, then executed through callExternalMcpTool with enforced timeouts.
  • Access control uses credential modes (shared vs per_member) and access grants managed through memberCanUseExternalMcpConnection.
  • Structured diagnostics in external-mcp-diagnostics.ts provide detailed error reporting for troubleshooting integration failures.

Frequently Asked Questions

How do I add a custom MCP server not listed in the presets?

Supply the MCP server URL directly in the POST request body to /v1/mcp-connections. The externalMcpUrlSchema in mcp-connections.ts validates that the URL uses HTTP or HTTPS, contains no fragments, and includes no embedded credentials. You must also specify the authType (typically oauth or none) and credentialMode (shared or per_member).

What happens if an external tool call times out?

OpenWork enforces the EXTERNAL_MCP_TOOL_CALL_TIMEOUT_MS deadline through runExternalMcpRequestWithinDeadline. If the MCP server fails to respond within this window, the system returns a structured error with code tool_execution_failed and a referenceId for debugging. Check the diagnostics in external-mcp-diagnostics.ts to identify whether the timeout occurred during connection, discovery, or execution phases.

Can I restrict which team members use a specific external connection?

Yes. Use the access control endpoints to configure the access field on any connection. You can set orgWide: true to allow all members, or specify arrays of memberIds and teamIds to restrict access. The functions memberCanUseExternalMcpConnection and listVisibleExternalMcpConnections enforce these permissions when agents attempt to list or call tools.

Where are OAuth tokens stored and how are they secured?

Tokens are encrypted and stored either in the ExternalMcpConnectionRow table (for shared credential mode) or the ConnectedAccountTable (for per_member mode). The encryption occurs during the completeExternalMcpAuth phase after the OAuth callback. All token storage operations occur within the Den API routes defined in mcp-connections.ts, ensuring secure handling of third-party credentials.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →