@withone/mcp 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/build/client.d.ts +102 -0
- package/build/client.js +362 -0
- package/build/helpers.d.ts +53 -0
- package/build/helpers.js +150 -0
- package/build/index.d.ts +10 -0
- package/build/index.js +281 -0
- package/build/schemas.d.ts +285 -0
- package/build/schemas.js +110 -0
- package/build/server.d.ts +27 -0
- package/build/server.js +215 -0
- package/build/types.d.ts +182 -0
- package/build/types.js +9 -0
- package/package.json +47 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* This module contains reusable utility functions used throughout the Pica MCP server.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Utility functions for data processing and API interactions
|
|
7
|
+
* @author Pica
|
|
8
|
+
*/
|
|
9
|
+
import { PermissionLevel } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Paginates through API results by repeatedly calling a fetch function until all data is retrieved.
|
|
12
|
+
* @param fetchFn - Function that fetches paginated data, takes skip and limit parameters
|
|
13
|
+
* @param limit - Maximum number of items to fetch per request (default: 100)
|
|
14
|
+
* @returns Promise that resolves to an array of all paginated results
|
|
15
|
+
*/
|
|
16
|
+
export declare function paginateResults<T>(fetchFn: (skip: number, limit: number) => Promise<{
|
|
17
|
+
rows: T[];
|
|
18
|
+
total: number;
|
|
19
|
+
skip: number;
|
|
20
|
+
limit: number;
|
|
21
|
+
}>, limit?: number): Promise<T[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Generic function to fetch paginated data from any API endpoint
|
|
24
|
+
* @param baseUrl - The base URL for the API endpoint (without query parameters)
|
|
25
|
+
* @param headers - Headers to include in the request
|
|
26
|
+
* @param additionalParams - Optional additional parameters to include in the request
|
|
27
|
+
* @returns Promise that resolves to an array of all paginated results
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchPaginatedData<T>(baseUrl: string, headers: Record<string, string>, additionalParams?: Record<string, string | number | boolean>): Promise<T[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Builds action knowledge with API request structure guidance.
|
|
32
|
+
* @param knowledge - The raw knowledge content for the action
|
|
33
|
+
* @param method - The HTTP method for the action
|
|
34
|
+
* @param baseUrl - The base URL for Pica API
|
|
35
|
+
* @param platform - The platform name (used for connection key)
|
|
36
|
+
* @param actionId - The action ID
|
|
37
|
+
* @returns Complete formatted knowledge with API guidance
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildActionKnowledgeWithGuidance(knowledge: string, method: string, baseUrl: string, platform: string, actionId: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Replaces path variables in a template string with actual values.
|
|
42
|
+
* Path variables can be in either format: {variableName} or {{variableName}} and will be replaced with corresponding values.
|
|
43
|
+
* @param path - The template string containing path variables in {variableName} or {{variableName}} format
|
|
44
|
+
* @param variables - Object containing variable names as keys and their replacement values
|
|
45
|
+
* @returns The path string with all variables replaced by their encoded values
|
|
46
|
+
* @throws Error if any required variable is missing, null, undefined, or empty string
|
|
47
|
+
*/
|
|
48
|
+
export declare function replacePathVariables(path: string, variables: Record<string, string | number | boolean>): string;
|
|
49
|
+
export declare function filterByPermissions<T extends {
|
|
50
|
+
method: string;
|
|
51
|
+
}>(actions: T[], permissions: PermissionLevel): T[];
|
|
52
|
+
export declare function isMethodAllowed(method: string, permissions: PermissionLevel): boolean;
|
|
53
|
+
export declare function isActionAllowed(actionId: string, allowedActionIds: string[]): boolean;
|
package/build/helpers.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* This module contains reusable utility functions used throughout the One MCP server.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Utility functions for data processing and API interactions
|
|
7
|
+
* @author One
|
|
8
|
+
*/
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
/**
|
|
11
|
+
* Paginates through API results by repeatedly calling a fetch function until all data is retrieved.
|
|
12
|
+
* @param fetchFn - Function that fetches paginated data, takes skip and limit parameters
|
|
13
|
+
* @param limit - Maximum number of items to fetch per request (default: 100)
|
|
14
|
+
* @returns Promise that resolves to an array of all paginated results
|
|
15
|
+
*/
|
|
16
|
+
export async function paginateResults(fetchFn, limit = 100) {
|
|
17
|
+
let page = 1;
|
|
18
|
+
let allResults = [];
|
|
19
|
+
let total = 0;
|
|
20
|
+
try {
|
|
21
|
+
do {
|
|
22
|
+
const response = await fetchFn(page, limit);
|
|
23
|
+
const { rows, total: totalCount } = response;
|
|
24
|
+
total = totalCount;
|
|
25
|
+
allResults = [...allResults, ...rows];
|
|
26
|
+
page++;
|
|
27
|
+
} while (allResults.length < total);
|
|
28
|
+
return allResults;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("Error in pagination:", error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generic function to fetch paginated data from any API endpoint
|
|
37
|
+
* @param baseUrl - The base URL for the API endpoint (without query parameters)
|
|
38
|
+
* @param headers - Headers to include in the request
|
|
39
|
+
* @param additionalParams - Optional additional parameters to include in the request
|
|
40
|
+
* @returns Promise that resolves to an array of all paginated results
|
|
41
|
+
*/
|
|
42
|
+
export async function fetchPaginatedData(baseUrl, headers, additionalParams) {
|
|
43
|
+
const fetchFn = async (page, limit) => {
|
|
44
|
+
const params = {
|
|
45
|
+
page,
|
|
46
|
+
limit,
|
|
47
|
+
...additionalParams
|
|
48
|
+
};
|
|
49
|
+
try {
|
|
50
|
+
const response = await axios.get(baseUrl, {
|
|
51
|
+
headers,
|
|
52
|
+
params
|
|
53
|
+
});
|
|
54
|
+
return response.data;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error(`Failed to fetch from ${baseUrl}:`, error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return paginateResults(fetchFn);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds action knowledge with API request structure guidance.
|
|
65
|
+
* @param knowledge - The raw knowledge content for the action
|
|
66
|
+
* @param method - The HTTP method for the action
|
|
67
|
+
* @param baseUrl - The base URL for One API
|
|
68
|
+
* @param platform - The platform name (used for connection key)
|
|
69
|
+
* @param actionId - The action ID
|
|
70
|
+
* @returns Complete formatted knowledge with API guidance
|
|
71
|
+
*/
|
|
72
|
+
export function buildActionKnowledgeWithGuidance(knowledge, method, baseUrl, platform, actionId) {
|
|
73
|
+
const cleanBaseUrl = baseUrl.replace(/\/$/, '');
|
|
74
|
+
return `${knowledge}
|
|
75
|
+
|
|
76
|
+
API REQUEST STRUCTURE
|
|
77
|
+
======================
|
|
78
|
+
URL: ${cleanBaseUrl}/v1/passthrough/{{PATH}}
|
|
79
|
+
|
|
80
|
+
IMPORTANT: When constructing the URL, only include the API endpoint path after the base URL.
|
|
81
|
+
Do NOT include the full third-party API URL.
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
✅ Correct: ${cleanBaseUrl}/v1/passthrough/crm/v3/objects/contacts/search
|
|
85
|
+
❌ Incorrect: ${cleanBaseUrl}/v1/passthrough/https://api.hubapi.com/crm/v3/objects/contacts/search
|
|
86
|
+
|
|
87
|
+
METHOD: ${method}
|
|
88
|
+
|
|
89
|
+
HEADERS:
|
|
90
|
+
- x-one-secret: {{process.env.ONE_SECRET}}
|
|
91
|
+
- x-one-connection-key: {{process.env.ONE_${platform.toUpperCase()}_CONNECTION_KEY}}
|
|
92
|
+
- x-one-action-id: ${actionId}
|
|
93
|
+
- ... (other headers)
|
|
94
|
+
|
|
95
|
+
BODY: {{BODY}}
|
|
96
|
+
|
|
97
|
+
QUERY PARAMS: {{QUERY_PARAMS}}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Replaces path variables in a template string with actual values.
|
|
101
|
+
* Path variables can be in either format: {variableName} or {{variableName}} and will be replaced with corresponding values.
|
|
102
|
+
* @param path - The template string containing path variables in {variableName} or {{variableName}} format
|
|
103
|
+
* @param variables - Object containing variable names as keys and their replacement values
|
|
104
|
+
* @returns The path string with all variables replaced by their encoded values
|
|
105
|
+
* @throws Error if any required variable is missing, null, undefined, or empty string
|
|
106
|
+
*/
|
|
107
|
+
export function replacePathVariables(path, variables) {
|
|
108
|
+
if (!path)
|
|
109
|
+
return path;
|
|
110
|
+
let result = path;
|
|
111
|
+
// First, replace double bracket variables {{variableName}}
|
|
112
|
+
result = result.replace(/\{\{([^}]+)\}\}/g, (match, variable) => {
|
|
113
|
+
const trimmedVariable = variable.trim();
|
|
114
|
+
const value = variables[trimmedVariable];
|
|
115
|
+
if (value === undefined || value === null || value === '') {
|
|
116
|
+
throw new Error(`Missing value for path variable: ${trimmedVariable}`);
|
|
117
|
+
}
|
|
118
|
+
return encodeURIComponent(value.toString());
|
|
119
|
+
});
|
|
120
|
+
// Then, replace single bracket variables {variableName}
|
|
121
|
+
result = result.replace(/\{([^}]+)\}/g, (match, variable) => {
|
|
122
|
+
const trimmedVariable = variable.trim();
|
|
123
|
+
const value = variables[trimmedVariable];
|
|
124
|
+
if (value === undefined || value === null || value === '') {
|
|
125
|
+
throw new Error(`Missing value for path variable: ${trimmedVariable}`);
|
|
126
|
+
}
|
|
127
|
+
return encodeURIComponent(value.toString());
|
|
128
|
+
});
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
const PERMISSION_METHODS = {
|
|
132
|
+
read: ["GET"],
|
|
133
|
+
write: ["GET", "POST", "PUT", "PATCH"],
|
|
134
|
+
admin: null,
|
|
135
|
+
};
|
|
136
|
+
export function filterByPermissions(actions, permissions) {
|
|
137
|
+
const allowed = PERMISSION_METHODS[permissions];
|
|
138
|
+
if (allowed === null)
|
|
139
|
+
return actions;
|
|
140
|
+
return actions.filter((a) => allowed.includes(a.method.toUpperCase()));
|
|
141
|
+
}
|
|
142
|
+
export function isMethodAllowed(method, permissions) {
|
|
143
|
+
const allowed = PERMISSION_METHODS[permissions];
|
|
144
|
+
if (allowed === null)
|
|
145
|
+
return true;
|
|
146
|
+
return allowed.includes(method.toUpperCase());
|
|
147
|
+
}
|
|
148
|
+
export function isActionAllowed(actionId, allowedActionIds) {
|
|
149
|
+
return allowedActionIds.includes("*") || allowedActionIds.includes(actionId);
|
|
150
|
+
}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pica MCP Server - Main Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This is the main MCP (Model Context Protocol) server implementation for Pica.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Main MCP server implementation with Pica API integration
|
|
8
|
+
* @author Pica
|
|
9
|
+
*/
|
|
10
|
+
export { createPicaServer, type CreatePicaServerOptions } from './server.js';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* One MCP Server - Main Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This is the main MCP (Model Context Protocol) server implementation for One.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Main MCP server implementation with One API integration
|
|
8
|
+
* @author One
|
|
9
|
+
*/
|
|
10
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
|
+
import { ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
+
import { OneClient } from './client.js';
|
|
14
|
+
import { buildActionKnowledgeWithGuidance, filterByPermissions, isMethodAllowed, isActionAllowed, } from './helpers.js';
|
|
15
|
+
import { listOneIntegrationsToolConfig, searchOnePlatformActionsToolConfig, getOneActionKnowledgeToolConfig, executeOneActionToolConfig } from './schemas.js';
|
|
16
|
+
const SERVER_NAME = "one-mcp-server";
|
|
17
|
+
const SERVER_VERSION = "1.0.0";
|
|
18
|
+
const ONE_SECRET = process.env.ONE_SECRET;
|
|
19
|
+
if (!ONE_SECRET) {
|
|
20
|
+
console.error("ONE_SECRET environment variable is required");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const ONE_BASE_URL = process.env.ONE_BASE_URL || "https://api.withone.ai";
|
|
24
|
+
const ONE_IDENTITY = process.env.ONE_IDENTITY;
|
|
25
|
+
const ONE_IDENTITY_TYPE = process.env.ONE_IDENTITY_TYPE;
|
|
26
|
+
const ONE_PERMISSIONS = process.env.ONE_PERMISSIONS || "admin";
|
|
27
|
+
if (!["read", "write", "admin"].includes(ONE_PERMISSIONS)) {
|
|
28
|
+
console.error(`Invalid ONE_PERMISSIONS value: "${ONE_PERMISSIONS}". Must be one of: read, write, admin`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const ONE_CONNECTION_KEYS = process.env.ONE_CONNECTION_KEYS
|
|
32
|
+
? process.env.ONE_CONNECTION_KEYS.split(",").map(s => s.trim()).filter(Boolean)
|
|
33
|
+
: ["*"];
|
|
34
|
+
const ONE_ACTION_IDS = process.env.ONE_ACTION_IDS
|
|
35
|
+
? process.env.ONE_ACTION_IDS.split(",").map(s => s.trim()).filter(Boolean)
|
|
36
|
+
: ["*"];
|
|
37
|
+
const ONE_KNOWLEDGE_AGENT = process.env.ONE_KNOWLEDGE_AGENT === "true";
|
|
38
|
+
const oneClient = new OneClient({
|
|
39
|
+
secret: ONE_SECRET,
|
|
40
|
+
baseUrl: ONE_BASE_URL,
|
|
41
|
+
identity: ONE_IDENTITY,
|
|
42
|
+
identityType: ONE_IDENTITY_TYPE,
|
|
43
|
+
connectionKeys: ONE_CONNECTION_KEYS,
|
|
44
|
+
});
|
|
45
|
+
let oneInitialized = false;
|
|
46
|
+
let initializationPromise = null;
|
|
47
|
+
const initializeOne = async () => {
|
|
48
|
+
if (oneInitialized) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (initializationPromise) {
|
|
52
|
+
return initializationPromise;
|
|
53
|
+
}
|
|
54
|
+
initializationPromise = (async () => {
|
|
55
|
+
try {
|
|
56
|
+
await oneClient.initialize();
|
|
57
|
+
oneInitialized = true;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error("Failed to initialize One client:", error);
|
|
61
|
+
initializationPromise = null;
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
return initializationPromise;
|
|
66
|
+
};
|
|
67
|
+
const server = new McpServer({
|
|
68
|
+
name: SERVER_NAME,
|
|
69
|
+
version: SERVER_VERSION,
|
|
70
|
+
});
|
|
71
|
+
server.registerTool("list_one_integrations", listOneIntegrationsToolConfig, async (args) => {
|
|
72
|
+
await initializeOne();
|
|
73
|
+
return await handleGetIntegrations(args);
|
|
74
|
+
});
|
|
75
|
+
server.registerTool("search_one_platform_actions", searchOnePlatformActionsToolConfig, async (args) => {
|
|
76
|
+
await initializeOne();
|
|
77
|
+
return await handleSearchPlatformActions(args);
|
|
78
|
+
});
|
|
79
|
+
server.registerTool("get_one_action_knowledge", getOneActionKnowledgeToolConfig, async (args) => {
|
|
80
|
+
await initializeOne();
|
|
81
|
+
return await handleGetActionKnowledge(args);
|
|
82
|
+
});
|
|
83
|
+
if (!ONE_KNOWLEDGE_AGENT) {
|
|
84
|
+
server.registerTool("execute_one_action", executeOneActionToolConfig, async (args) => {
|
|
85
|
+
await initializeOne();
|
|
86
|
+
return await handleExecuteOneAction(args);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function handleGetIntegrations(args) {
|
|
90
|
+
try {
|
|
91
|
+
const connectedIntegrations = oneClient.getUserConnections();
|
|
92
|
+
const availableIntegrations = oneClient.getAvailableConnectors();
|
|
93
|
+
const activeConnections = connectedIntegrations.filter(conn => conn.active);
|
|
94
|
+
let activePlatforms = availableIntegrations.filter(def => def.active && !def.deprecated);
|
|
95
|
+
// When connection keys are scoped, only show platforms that match active connections
|
|
96
|
+
if (!ONE_CONNECTION_KEYS.includes("*")) {
|
|
97
|
+
const connectedPlatforms = new Set(activeConnections.map(conn => conn.platform));
|
|
98
|
+
activePlatforms = activePlatforms.filter(def => connectedPlatforms.has(def.platform));
|
|
99
|
+
}
|
|
100
|
+
const structuredResponse = {
|
|
101
|
+
connections: activeConnections.map(conn => ({
|
|
102
|
+
platform: conn.platform,
|
|
103
|
+
key: conn.key,
|
|
104
|
+
tags: conn.tags ?? []
|
|
105
|
+
})),
|
|
106
|
+
availablePlatforms: activePlatforms.map(def => ({
|
|
107
|
+
platform: def.platform,
|
|
108
|
+
name: def.name,
|
|
109
|
+
category: def.category
|
|
110
|
+
})),
|
|
111
|
+
summary: {
|
|
112
|
+
connectedCount: activeConnections.length,
|
|
113
|
+
availableCount: activePlatforms.length
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: "text",
|
|
120
|
+
text: JSON.stringify(structuredResponse, null, 2),
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
structuredContent: structuredResponse,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new McpError(ErrorCode.InternalError, `Failed to retrieve integrations: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function handleSearchPlatformActions(args) {
|
|
131
|
+
try {
|
|
132
|
+
// Force knowledge mode when ONE_KNOWLEDGE_AGENT is enabled
|
|
133
|
+
const agentType = ONE_KNOWLEDGE_AGENT ? "knowledge" : args.agentType;
|
|
134
|
+
let actions = await oneClient.searchAvailableActions(args.platform, args.query, agentType);
|
|
135
|
+
// Apply permission-level filtering
|
|
136
|
+
actions = filterByPermissions(actions, ONE_PERMISSIONS);
|
|
137
|
+
// Apply action allowlist filtering
|
|
138
|
+
actions = actions.filter(a => isActionAllowed(a.systemId, ONE_ACTION_IDS));
|
|
139
|
+
const cleanedActions = actions.map(action => ({
|
|
140
|
+
actionId: action.systemId,
|
|
141
|
+
title: action.title,
|
|
142
|
+
method: action.method,
|
|
143
|
+
path: action.path
|
|
144
|
+
}));
|
|
145
|
+
// Handle empty results with helpful suggestions
|
|
146
|
+
if (cleanedActions.length === 0) {
|
|
147
|
+
const suggestionsText = `No actions found for platform '${args.platform}' matching query '${args.query}'.
|
|
148
|
+
|
|
149
|
+
SUGGESTIONS:
|
|
150
|
+
- Try a more general query (e.g., 'list', 'get', 'search', 'create')
|
|
151
|
+
- Verify the platform name is correct
|
|
152
|
+
- Check that actions exist for this platform using list_one_integrations
|
|
153
|
+
|
|
154
|
+
EXAMPLES OF GOOD QUERIES:
|
|
155
|
+
- "search contacts"
|
|
156
|
+
- "send email"
|
|
157
|
+
- "create customer"
|
|
158
|
+
- "list orders"`;
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: suggestionsText,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Build structured response
|
|
169
|
+
const structuredResponse = {
|
|
170
|
+
actions: cleanedActions,
|
|
171
|
+
metadata: {
|
|
172
|
+
platform: args.platform,
|
|
173
|
+
query: args.query,
|
|
174
|
+
count: cleanedActions.length
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const responseText = `Found ${cleanedActions.length} action(s) for platform '${args.platform}' matching query '${args.query}':
|
|
178
|
+
|
|
179
|
+
${JSON.stringify(structuredResponse, null, 2)}
|
|
180
|
+
|
|
181
|
+
NEXT STEP: Use get_one_action_knowledge with an actionId to get detailed documentation before building requests or executing actions.`;
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: "text",
|
|
186
|
+
text: responseText,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
structuredContent: structuredResponse,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw new McpError(ErrorCode.InternalError, `Failed to search platform actions: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function handleGetActionKnowledge(args) {
|
|
197
|
+
try {
|
|
198
|
+
const actionId = args.actionId;
|
|
199
|
+
if (!isActionAllowed(actionId, ONE_ACTION_IDS)) {
|
|
200
|
+
throw new McpError(ErrorCode.InvalidRequest, `Action "${actionId}" is not in the allowed action list`);
|
|
201
|
+
}
|
|
202
|
+
if (!ONE_CONNECTION_KEYS.includes("*")) {
|
|
203
|
+
const connectedPlatforms = oneClient.getUserConnections().map(c => c.platform);
|
|
204
|
+
if (!connectedPlatforms.includes(args.platform)) {
|
|
205
|
+
throw new McpError(ErrorCode.InvalidRequest, `Platform "${args.platform}" has no allowed connections`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const { knowledge, method } = await oneClient.getActionKnowledge(actionId);
|
|
209
|
+
const knowledgeWithGuidance = buildActionKnowledgeWithGuidance(knowledge, method, oneClient.getBaseUrl(), args.platform, actionId);
|
|
210
|
+
return {
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: knowledgeWithGuidance,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
throw new McpError(ErrorCode.InternalError, `Failed to retrieve action knowledge: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function handleExecuteOneAction(args) {
|
|
224
|
+
try {
|
|
225
|
+
if (!isActionAllowed(args.actionId, ONE_ACTION_IDS)) {
|
|
226
|
+
throw new McpError(ErrorCode.InvalidRequest, `Action "${args.actionId}" is not in the allowed action list`);
|
|
227
|
+
}
|
|
228
|
+
if (!ONE_CONNECTION_KEYS.includes("*")) {
|
|
229
|
+
const allowedKeys = oneClient.getUserConnections().map(c => c.key);
|
|
230
|
+
if (!allowedKeys.includes(args.connectionKey)) {
|
|
231
|
+
throw new McpError(ErrorCode.InvalidRequest, `Connection key "${args.connectionKey}" is not allowed`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const actionDetails = await oneClient.getActionDetails(args.actionId);
|
|
235
|
+
if (!isMethodAllowed(actionDetails.method, ONE_PERMISSIONS)) {
|
|
236
|
+
throw new McpError(ErrorCode.InvalidRequest, `Method "${actionDetails.method}" is not allowed under "${ONE_PERMISSIONS}" permission level`);
|
|
237
|
+
}
|
|
238
|
+
const result = await oneClient.executePassthroughRequest(args, actionDetails);
|
|
239
|
+
return {
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: JSON.stringify(result, null, 2),
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
throw new McpError(ErrorCode.InternalError, `Failed to execute One action: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async function main() {
|
|
253
|
+
try {
|
|
254
|
+
const transport = new StdioServerTransport();
|
|
255
|
+
await server.connect(transport);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error("Failed to start server:", error);
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
process.on('SIGINT', async () => {
|
|
263
|
+
console.error('Shutting down server...');
|
|
264
|
+
process.exit(0);
|
|
265
|
+
});
|
|
266
|
+
process.on('SIGTERM', async () => {
|
|
267
|
+
console.error('Shutting down server...');
|
|
268
|
+
process.exit(0);
|
|
269
|
+
});
|
|
270
|
+
process.on('uncaughtException', (error) => {
|
|
271
|
+
console.error('Uncaught exception:', error);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
});
|
|
274
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
275
|
+
console.error('Unhandled rejection at:', promise, 'reason:', reason);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
});
|
|
278
|
+
main().catch((error) => {
|
|
279
|
+
console.error('Server error:', error);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
});
|