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:
-
Discovery – OpenWork fetches the MCP server’s metadata, including OAuth endpoints and available tools, via the
ExternalMcpClientclass inexternal-mcp-client.ts. -
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.jsonas documented indocs/external-mcp-oauth.md. -
Authorization – The user or admin completes the OAuth flow, and tokens are stored encrypted in Den via the routes in
mcp-connections.ts. -
Tool Catalog Retrieval – OpenWork queries the MCP server’s
tools/listendpoint, filters the results, and enforces size limits usingvalidateToolCatalogFieldandmeasureSerializedJson. -
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:
- 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: "..."}.
-
Redirect the admin to the
authorizeUrl. The URL contains a signedstatetoken generated bycreateOAuthStateToken. -
Complete authorization on the MCP provider site. The provider redirects back to OpenWork’s callback URL at
/v1/mcp-connections/oauth/callback. -
Process the callback using
handleExternalMcpOAuthCallbackinmcp-connections.ts, which validates the signed state, discovers the expected issuer, and callscompleteExternalMcpAuthto store tokens. -
Token storage – Encrypted tokens are saved in
ExternalMcpConnectionRow(for shared mode) orConnectedAccountTable(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.listToolswith pagination limits viacollectExternalMcpToolPages. - Enforces strict size limits (512 KB maximum) on tool names, titles, descriptions, and JSON schemas.
- Returns an array of
ConnectionToolobjects containingname,title,description,inputSchema, andoutputSchema.
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:
- Builds an MCP client using
buildClientand binds it to anExternalMcpOAuthProvidertransport. - Invokes
client.callTool({name, arguments})insiderunExternalMcpRequestWithinDeadlineto enforce theEXTERNAL_MCP_TOOL_CALL_TIMEOUT_MSdeadline. - 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 inConnectedAccountTable.
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.tscore 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 byhandleExternalMcpOAuthCallback. - Tools are retrieved via
listExternalMcpToolswith strict size validation, then executed throughcallExternalMcpToolwith enforced timeouts. - Access control uses credential modes (
sharedvsper_member) and access grants managed throughmemberCanUseExternalMcpConnection. - Structured diagnostics in
external-mcp-diagnostics.tsprovide 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →