integrate-sdk 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Configuration Types
3
+ * Type-safe configuration with inference
4
+ */
5
+
6
+ import type { MCPPlugin } from "../plugins/types.js";
7
+ import type { AuthenticationError } from "../errors.js";
8
+
9
+ /**
10
+ * Re-authentication context provided to the callback
11
+ */
12
+ export interface ReauthContext {
13
+ /** The plugin/provider that needs re-authentication */
14
+ provider: string;
15
+ /** The error that triggered re-authentication */
16
+ error: AuthenticationError;
17
+ /** The tool name that was being called (if applicable) */
18
+ toolName?: string;
19
+ }
20
+
21
+ /**
22
+ * Re-authentication handler function
23
+ * Called when authentication fails and user needs to re-authenticate
24
+ * Should return true if re-authentication was successful, false otherwise
25
+ */
26
+ export type ReauthHandler = (context: ReauthContext) => Promise<boolean> | boolean;
27
+
28
+ /**
29
+ * Main client configuration
30
+ */
31
+ export interface MCPClientConfig<TPlugins extends readonly MCPPlugin[]> {
32
+ /** Array of plugins to enable */
33
+ plugins: TPlugins;
34
+
35
+ /** Optional HTTP headers to include in requests */
36
+ headers?: Record<string, string>;
37
+
38
+ /** Request timeout in milliseconds (default: 30000) */
39
+ timeout?: number;
40
+
41
+ /** Client information */
42
+ clientInfo?: {
43
+ name: string;
44
+ version: string;
45
+ };
46
+
47
+ /**
48
+ * Handler called when authentication fails and re-authentication is needed
49
+ * This is typically called when OAuth tokens expire or become invalid
50
+ *
51
+ * @param context - Information about the authentication failure
52
+ * @returns Promise<boolean> - true if re-authentication was successful, false otherwise
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const client = createMCPClient({
57
+ * plugins: [githubPlugin(...)],
58
+ * onReauthRequired: async (context) => {
59
+ * console.log(`Re-auth needed for ${context.provider}`);
60
+ * // Trigger your OAuth flow here
61
+ * // Return true if successful, false otherwise
62
+ * return await triggerOAuthFlow(context.provider);
63
+ * }
64
+ * });
65
+ * ```
66
+ */
67
+ onReauthRequired?: ReauthHandler;
68
+
69
+ /**
70
+ * Maximum number of automatic retry attempts when authentication fails
71
+ * Default: 1 (one retry after re-authentication)
72
+ * Set to 0 to disable automatic retries
73
+ */
74
+ maxReauthRetries?: number;
75
+
76
+ /**
77
+ * Connection behavior
78
+ *
79
+ * - 'lazy' (default): Automatically connects on first method call
80
+ * - 'eager': Connects immediately when createMCPClient is called
81
+ * - 'manual': Requires manual connect() call (original behavior)
82
+ *
83
+ * @default 'lazy'
84
+ */
85
+ connectionMode?: 'lazy' | 'eager' | 'manual';
86
+
87
+ /**
88
+ * Whether to use singleton pattern and reuse client instances
89
+ *
90
+ * - true (default): Reuses client with same configuration
91
+ * - false: Always creates a new instance
92
+ *
93
+ * @default true
94
+ */
95
+ singleton?: boolean;
96
+
97
+ /**
98
+ * Automatically cleanup (disconnect) on process exit
99
+ *
100
+ * @default true
101
+ */
102
+ autoCleanup?: boolean;
103
+
104
+ /**
105
+ * OAuth flow configuration
106
+ * Controls how OAuth authorization is handled (popup vs redirect)
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const client = createMCPClient({
111
+ * plugins: [githubPlugin({ ... })],
112
+ * oauthFlow: {
113
+ * mode: 'popup',
114
+ * popupOptions: { width: 600, height: 700 }
115
+ * }
116
+ * });
117
+ * ```
118
+ */
119
+ oauthFlow?: {
120
+ /** How to display OAuth authorization (default: 'redirect') */
121
+ mode?: 'popup' | 'redirect';
122
+ /** Popup window dimensions (only for popup mode) */
123
+ popupOptions?: {
124
+ width?: number;
125
+ height?: number;
126
+ };
127
+ /** Custom callback handler for receiving auth code */
128
+ onAuthCallback?: (provider: string, code: string, state: string) => Promise<void>;
129
+ };
130
+
131
+ /**
132
+ * Session token for authenticated requests
133
+ * Set automatically after OAuth flow completes
134
+ * Can be provided manually if you manage tokens externally
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const client = createMCPClient({
139
+ * plugins: [githubPlugin({ ... })],
140
+ * sessionToken: 'existing-session-token'
141
+ * });
142
+ * ```
143
+ */
144
+ sessionToken?: string;
145
+
146
+ /**
147
+ * Base URL for OAuth API routes
148
+ * These routes should be mounted in your application to handle OAuth securely
149
+ *
150
+ * The SDK will call:
151
+ * - POST {oauthApiBase}/authorize - Get authorization URL
152
+ * - POST {oauthApiBase}/callback - Exchange code for token
153
+ * - GET {oauthApiBase}/status - Check authorization status
154
+ *
155
+ * @default '/api/integrate/oauth'
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * const client = createMCPClient({
160
+ * plugins: [githubPlugin({ ... })],
161
+ * oauthApiBase: '/api/integrate/oauth'
162
+ * });
163
+ * ```
164
+ */
165
+ oauthApiBase?: string;
166
+ }
167
+
168
+ /**
169
+ * Helper type to infer enabled tools from plugins
170
+ */
171
+ export type InferEnabledTools<TPlugins extends readonly MCPPlugin[]> =
172
+ TPlugins[number]["tools"][number];
173
+
174
+ /**
175
+ * Helper type to create a tools object type from plugin array
176
+ */
177
+ export type InferToolsObject<TPlugins extends readonly MCPPlugin[]> = {
178
+ [K in TPlugins[number] as K["id"]]: K["tools"];
179
+ };
180
+
package/src/errors.ts ADDED
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Custom error types for the Integrate SDK
3
+ */
4
+
5
+ /**
6
+ * Base error class for all SDK errors
7
+ */
8
+ export class IntegrateSDKError extends Error {
9
+ constructor(message: string) {
10
+ super(message);
11
+ this.name = "IntegrateSDKError";
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Error thrown when authentication fails or tokens are invalid
17
+ */
18
+ export class AuthenticationError extends IntegrateSDKError {
19
+ public readonly statusCode?: number;
20
+ public readonly provider?: string;
21
+
22
+ constructor(message: string, statusCode?: number, provider?: string) {
23
+ super(message);
24
+ this.name = "AuthenticationError";
25
+ this.statusCode = statusCode;
26
+ this.provider = provider;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Error thrown when access is forbidden (insufficient permissions)
32
+ */
33
+ export class AuthorizationError extends IntegrateSDKError {
34
+ public readonly statusCode?: number;
35
+ public readonly requiredScopes?: string[];
36
+
37
+ constructor(message: string, statusCode?: number, requiredScopes?: string[]) {
38
+ super(message);
39
+ this.name = "AuthorizationError";
40
+ this.statusCode = statusCode;
41
+ this.requiredScopes = requiredScopes;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Error thrown when OAuth tokens have expired and need to be refreshed
47
+ */
48
+ export class TokenExpiredError extends AuthenticationError {
49
+ constructor(message: string, provider?: string) {
50
+ super(message, 401, provider);
51
+ this.name = "TokenExpiredError";
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Error thrown when a connection to the server fails
57
+ */
58
+ export class ConnectionError extends IntegrateSDKError {
59
+ public readonly statusCode?: number;
60
+
61
+ constructor(message: string, statusCode?: number) {
62
+ super(message);
63
+ this.name = "ConnectionError";
64
+ this.statusCode = statusCode;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Error thrown when a tool call fails
70
+ */
71
+ export class ToolCallError extends IntegrateSDKError {
72
+ public readonly toolName: string;
73
+ public readonly originalError?: unknown;
74
+
75
+ constructor(message: string, toolName: string, originalError?: unknown) {
76
+ super(message);
77
+ this.name = "ToolCallError";
78
+ this.toolName = toolName;
79
+ this.originalError = originalError;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Helper function to determine if an error is an authentication error
85
+ */
86
+ export function isAuthError(error: unknown): error is AuthenticationError {
87
+ return error instanceof AuthenticationError;
88
+ }
89
+
90
+ /**
91
+ * Helper function to determine if an error is a token expired error
92
+ */
93
+ export function isTokenExpiredError(error: unknown): error is TokenExpiredError {
94
+ return error instanceof TokenExpiredError;
95
+ }
96
+
97
+ /**
98
+ * Helper function to determine if an error is an authorization error
99
+ */
100
+ export function isAuthorizationError(error: unknown): error is AuthorizationError {
101
+ return error instanceof AuthorizationError;
102
+ }
103
+
104
+ /**
105
+ * Helper function to parse error responses from the MCP server
106
+ * and convert them to appropriate error types
107
+ */
108
+ export function parseServerError(
109
+ error: any,
110
+ context?: { toolName?: string; provider?: string }
111
+ ): IntegrateSDKError {
112
+ // Check if the error has an attached JSON-RPC error (from transport layer)
113
+ if (error && typeof error === "object" && "jsonrpcError" in error) {
114
+ const jsonrpcError = error.jsonrpcError;
115
+ if (jsonrpcError && typeof jsonrpcError === "object") {
116
+ return parseServerError(jsonrpcError, context);
117
+ }
118
+ }
119
+
120
+ // Check for JSON-RPC error format
121
+ if (error && typeof error === "object" && "code" in error && "message" in error) {
122
+ const code = error.code;
123
+ const message = error.message || "Unknown error";
124
+
125
+ // Standard JSON-RPC error codes
126
+ if (code === -32600) {
127
+ return new IntegrateSDKError(`Invalid request: ${message}`);
128
+ }
129
+ if (code === -32601) {
130
+ return new IntegrateSDKError(`Method not found: ${message}`);
131
+ }
132
+ if (code === -32602) {
133
+ return new IntegrateSDKError(`Invalid params: ${message}`);
134
+ }
135
+
136
+ // Custom authentication error codes
137
+ if (code === 401 || code === -32001) {
138
+ // Check if it's a token expiry
139
+ if (
140
+ message.toLowerCase().includes("expired") ||
141
+ message.toLowerCase().includes("token")
142
+ ) {
143
+ return new TokenExpiredError(message, context?.provider);
144
+ }
145
+ return new AuthenticationError(message, 401, context?.provider);
146
+ }
147
+
148
+ // Authorization errors
149
+ if (code === 403 || code === -32002) {
150
+ return new AuthorizationError(message, 403);
151
+ }
152
+
153
+ // Tool-specific errors
154
+ if (context?.toolName) {
155
+ return new ToolCallError(message, context.toolName, error);
156
+ }
157
+
158
+ return new IntegrateSDKError(message);
159
+ }
160
+
161
+ // Handle regular Error objects
162
+ if (error instanceof Error) {
163
+ const message = error.message;
164
+
165
+ // Check for HTTP status code attached to error
166
+ const statusCode = (error as any).statusCode;
167
+ if (statusCode === 401) {
168
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
169
+ return new TokenExpiredError(message, context?.provider);
170
+ }
171
+ return new AuthenticationError(message, 401, context?.provider);
172
+ }
173
+ if (statusCode === 403) {
174
+ return new AuthorizationError(message, 403);
175
+ }
176
+
177
+ // Check for common auth error patterns in message
178
+ if (
179
+ message.includes("401") ||
180
+ message.includes("Unauthorized") ||
181
+ message.includes("unauthenticated")
182
+ ) {
183
+ if (message.toLowerCase().includes("expired") || message.toLowerCase().includes("token")) {
184
+ return new TokenExpiredError(message, context?.provider);
185
+ }
186
+ return new AuthenticationError(message, 401, context?.provider);
187
+ }
188
+
189
+ if (
190
+ message.includes("403") ||
191
+ message.includes("Forbidden") ||
192
+ message.includes("unauthorized")
193
+ ) {
194
+ return new AuthorizationError(message, 403);
195
+ }
196
+
197
+ if (context?.toolName) {
198
+ return new ToolCallError(message, context.toolName, error);
199
+ }
200
+
201
+ return new IntegrateSDKError(message);
202
+ }
203
+
204
+ // Fallback
205
+ return new IntegrateSDKError(String(error));
206
+ }
207
+
package/src/index.ts ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Integrate SDK
3
+ * Type-safe TypeScript SDK for MCP Client
4
+ */
5
+
6
+ // Core client
7
+ export { MCPClient, createMCPClient, clearClientCache } from "./client.js";
8
+ export type { ToolInvocationOptions } from "./client.js";
9
+
10
+ // OAuth utilities
11
+ export { OAuthManager } from "./oauth/manager.js";
12
+ export { OAuthWindowManager, sendCallbackToOpener } from "./oauth/window-manager.js";
13
+ export { generateCodeVerifier, generateCodeChallenge, generateState } from "./oauth/pkce.js";
14
+ export type {
15
+ OAuthFlowConfig,
16
+ PopupOptions,
17
+ AuthStatus,
18
+ PendingAuth,
19
+ AuthorizationUrlResponse,
20
+ OAuthCallbackResponse,
21
+ OAuthCallbackParams,
22
+ OAuthCallbackHandlerConfig,
23
+ } from "./oauth/types.js";
24
+
25
+ // OAuth route adapters
26
+ export { OAuthHandler } from "./adapters/base-handler.js";
27
+ export type {
28
+ OAuthHandlerConfig,
29
+ AuthorizeRequest,
30
+ AuthorizeResponse,
31
+ CallbackRequest,
32
+ CallbackResponse,
33
+ StatusResponse,
34
+ } from "./adapters/base-handler.js";
35
+ export { createNextOAuthHandler } from "./adapters/nextjs.js";
36
+ export { createTanStackOAuthHandler } from "./adapters/tanstack-start.js";
37
+
38
+ // Configuration
39
+ export type { MCPClientConfig, ReauthContext, ReauthHandler } from "./config/types.js";
40
+
41
+ // Errors
42
+ export {
43
+ IntegrateSDKError,
44
+ AuthenticationError,
45
+ AuthorizationError,
46
+ TokenExpiredError,
47
+ ConnectionError,
48
+ ToolCallError,
49
+ isAuthError,
50
+ isTokenExpiredError,
51
+ isAuthorizationError,
52
+ parseServerError,
53
+ } from "./errors.js";
54
+
55
+ // Plugin system
56
+ export type {
57
+ MCPPlugin,
58
+ OAuthConfig,
59
+ ExtractPluginIds,
60
+ ExtractPluginTools,
61
+ } from "./plugins/types.js";
62
+
63
+ // Built-in plugins
64
+ export { githubPlugin } from "./plugins/github.js";
65
+ export type { GitHubPluginConfig, GitHubTools, GitHubPluginClient } from "./plugins/github.js";
66
+
67
+ export { gmailPlugin } from "./plugins/gmail.js";
68
+ export type { GmailPluginConfig, GmailTools, GmailPluginClient } from "./plugins/gmail.js";
69
+
70
+ // Server client
71
+ export type { ServerPluginClient } from "./plugins/server-client.js";
72
+
73
+ export {
74
+ genericOAuthPlugin,
75
+ createSimplePlugin,
76
+ } from "./plugins/generic.js";
77
+ export type { GenericOAuthPluginConfig } from "./plugins/generic.js";
78
+
79
+ // Integrations
80
+ export {
81
+ convertMCPToolToVercelAI,
82
+ convertMCPToolsToVercelAI,
83
+ getVercelAITools,
84
+ } from "./integrations/vercel-ai.js";
85
+ export type { VercelAITool } from "./integrations/vercel-ai.js";
86
+
87
+ // Protocol types
88
+ export type {
89
+ JSONRPCRequest,
90
+ JSONRPCResponse,
91
+ JSONRPCSuccessResponse,
92
+ JSONRPCErrorResponse,
93
+ JSONRPCNotification,
94
+ MCPTool,
95
+ MCPToolsListResponse,
96
+ MCPToolCallParams,
97
+ MCPToolCallResponse,
98
+ MCPInitializeParams,
99
+ MCPInitializeResponse,
100
+ } from "./protocol/messages.js";
101
+
102
+ export { MCPMethod } from "./protocol/messages.js";
103
+
104
+ // Transport
105
+ export { HttpSessionTransport } from "./transport/http-session.js";
106
+ export type {
107
+ MessageHandler,
108
+ HttpSessionTransportOptions,
109
+ } from "./transport/http-session.js";
110
+
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Vercel AI SDK Integration
3
+ *
4
+ * Helper functions to convert MCP tools to Vercel AI SDK format
5
+ */
6
+
7
+ import type { MCPClient } from "../client.js";
8
+ import type { MCPTool } from "../protocol/messages.js";
9
+
10
+ /**
11
+ * Tool definition for Vercel AI SDK
12
+ */
13
+ export interface VercelAITool {
14
+ description: string;
15
+ parameters: Record<string, unknown>;
16
+ execute: (args: Record<string, unknown>) => Promise<unknown>;
17
+ }
18
+
19
+ /**
20
+ * Convert MCP JSON Schema to Vercel AI SDK parameters format
21
+ *
22
+ * Note: Vercel AI SDK typically uses Zod, but we return plain JSON Schema
23
+ * which is compatible with the AI SDK's schema parameter
24
+ */
25
+ function convertMCPSchemaToParameters(inputSchema: Record<string, unknown>): Record<string, unknown> {
26
+ // MCP tools already use JSON Schema format
27
+ // Vercel AI SDK accepts JSON Schema or Zod schemas
28
+ return inputSchema;
29
+ }
30
+
31
+ /**
32
+ * Convert a single MCP tool to Vercel AI SDK format
33
+ *
34
+ * @param mcpTool - The MCP tool definition
35
+ * @param client - The MCP client instance (used for executing the tool)
36
+ * @returns Vercel AI SDK compatible tool definition
37
+ */
38
+ export function convertMCPToolToVercelAI(
39
+ mcpTool: MCPTool,
40
+ client: MCPClient
41
+ ): VercelAITool {
42
+ return {
43
+ description: mcpTool.description || `Execute ${mcpTool.name}`,
44
+ parameters: convertMCPSchemaToParameters(mcpTool.inputSchema),
45
+ execute: async (args: Record<string, unknown>) => {
46
+ // Use internal method to call tools by name for integration purposes
47
+ const result = await client._callToolByName(mcpTool.name, args);
48
+ return result;
49
+ },
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Convert all enabled MCP tools to Vercel AI SDK format
55
+ *
56
+ * @param client - The MCP client instance (must be connected)
57
+ * @returns Object mapping tool names to Vercel AI SDK tool definitions
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * import { createMCPClient, githubPlugin } from 'integrate-sdk';
62
+ * import { convertMCPToolsToVercelAI } from 'integrate-sdk/vercel-ai';
63
+ * import { generateText } from 'ai';
64
+ *
65
+ * const mcpClient = createMCPClient({
66
+ * plugins: [githubPlugin({ clientId: '...', clientSecret: '...' })],
67
+ * });
68
+ *
69
+ * await mcpClient.connect();
70
+ *
71
+ * const tools = convertMCPToolsToVercelAI(mcpClient);
72
+ *
73
+ * const result = await generateText({
74
+ * model: openai('gpt-5'),
75
+ * prompt: 'Create a GitHub issue in my repo',
76
+ * tools,
77
+ * });
78
+ * ```
79
+ */
80
+ export function convertMCPToolsToVercelAI(
81
+ client: MCPClient
82
+ ): Record<string, VercelAITool> {
83
+ const mcpTools = client.getEnabledTools();
84
+ const vercelTools: Record<string, VercelAITool> = {};
85
+
86
+ for (const mcpTool of mcpTools) {
87
+ vercelTools[mcpTool.name] = convertMCPToolToVercelAI(mcpTool, client);
88
+ }
89
+
90
+ return vercelTools;
91
+ }
92
+
93
+ /**
94
+ * Get tools in a format compatible with Vercel AI SDK's tools parameter
95
+ *
96
+ * This is an alternative that returns the tools in the exact format expected by ai.generateText()
97
+ *
98
+ * @param client - The MCP client instance (must be connected)
99
+ * @returns Tools object ready to pass to generateText({ tools: ... })
100
+ */
101
+ export function getVercelAITools(client: MCPClient) {
102
+ return convertMCPToolsToVercelAI(client);
103
+ }
104
+