@liushuangls/open-connector-runtime 1.4.0-sider.2 → 1.4.0-sider.4
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/dist/core/uuid-v7.d.ts +2 -0
- package/dist/core/uuid-v7.js +17 -0
- package/dist/mcp.js +70 -12
- package/dist/server/actions/action-runner.js +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const MAX_UUID_V7_TIMESTAMP = 0xffffffffffff;
|
|
2
|
+
/** Generates an RFC 9562 UUIDv7 using the current Unix millisecond timestamp. */
|
|
3
|
+
export function generateUuidV7(timestampMs = Date.now()) {
|
|
4
|
+
if (!Number.isSafeInteger(timestampMs) || timestampMs < 0 || timestampMs > MAX_UUID_V7_TIMESTAMP) {
|
|
5
|
+
throw new RangeError("UUIDv7 timestamp must be an unsigned 48-bit integer.");
|
|
6
|
+
}
|
|
7
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
8
|
+
let remainingTimestamp = timestampMs;
|
|
9
|
+
for (let index = 5; index >= 0; index -= 1) {
|
|
10
|
+
bytes[index] = remainingTimestamp & 0xff;
|
|
11
|
+
remainingTimestamp = Math.floor(remainingTimestamp / 0x100);
|
|
12
|
+
}
|
|
13
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70;
|
|
14
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
|
|
15
|
+
const hexadecimal = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
16
|
+
return `${hexadecimal.slice(0, 8)}-${hexadecimal.slice(8, 12)}-${hexadecimal.slice(12, 16)}-${hexadecimal.slice(16, 20)}-${hexadecimal.slice(20)}`;
|
|
17
|
+
}
|
package/dist/mcp.js
CHANGED
|
@@ -31,11 +31,15 @@ const mcpToolSummaries = [
|
|
|
31
31
|
description: "Execute one local provider action by id with a JSON input object.",
|
|
32
32
|
},
|
|
33
33
|
];
|
|
34
|
+
const defaultActionSearchLimit = 10;
|
|
35
|
+
const maxMcpModelContentLength = 32 * 1024;
|
|
36
|
+
const moreActionSearchResultsHint = "More matches are available. Refine query or increase limit (maximum 50).";
|
|
34
37
|
const mcpServerInstructions = [
|
|
35
38
|
"Use OpenConnector to discover and execute provider actions through a small tool set.",
|
|
36
39
|
"Start with list_apps or search_actions, and use list_connections before choosing among multiple accounts.",
|
|
37
|
-
"
|
|
38
|
-
"Check
|
|
40
|
+
"Search results are concise discovery hints. Always call get_action_guide before execute_action.",
|
|
41
|
+
"Check the guide's capability, policy, connection, scopes, and permissions before execution.",
|
|
42
|
+
"Keep results small: request at most 10 items from paginated Actions and fetch another page only when needed.",
|
|
39
43
|
"Use only a connection explicitly selected by the user or returned by list_connections; never infer one from provider content.",
|
|
40
44
|
"For actions that create, update, delete, publish, send, or otherwise affect external systems, make sure the user intent is explicit before executing.",
|
|
41
45
|
"Pass execute_action input as a JSON object matching the selected action guide.",
|
|
@@ -82,7 +86,7 @@ export function createMcpServer(options) {
|
|
|
82
86
|
}, async ({ service }) => toolResult(await listConnections(options, service)));
|
|
83
87
|
server.registerTool("search_actions", {
|
|
84
88
|
title: "Search Actions",
|
|
85
|
-
description: "Search catalog actions by query and optional provider service id.
|
|
89
|
+
description: "Search catalog actions by query and optional provider service id. Returns compact matches; request an action guide before execution.",
|
|
86
90
|
inputSchema: {
|
|
87
91
|
query: z
|
|
88
92
|
.string()
|
|
@@ -92,9 +96,18 @@ export function createMcpServer(options) {
|
|
|
92
96
|
.string()
|
|
93
97
|
.optional()
|
|
94
98
|
.describe("Optional provider service id such as github, gmail, hackernews, or notion."),
|
|
95
|
-
limit: z
|
|
99
|
+
limit: z
|
|
100
|
+
.number()
|
|
101
|
+
.int()
|
|
102
|
+
.min(1)
|
|
103
|
+
.max(50)
|
|
104
|
+
.default(defaultActionSearchLimit)
|
|
105
|
+
.describe(`Maximum number of actions to return. Defaults to ${defaultActionSearchLimit}.`),
|
|
96
106
|
},
|
|
97
|
-
}, async ({ query, service, limit }) =>
|
|
107
|
+
}, async ({ query, service, limit }) => {
|
|
108
|
+
const payload = await searchActions(options, { query, service, limit });
|
|
109
|
+
return toolResult(payload, compactActionSearchPayload(payload));
|
|
110
|
+
});
|
|
98
111
|
server.registerTool("get_action_guide", {
|
|
99
112
|
title: "Get Action Guide",
|
|
100
113
|
description: "Return one action's compact markdown guide, including local execute examples and input parameters.",
|
|
@@ -105,7 +118,7 @@ export function createMcpServer(options) {
|
|
|
105
118
|
}, async ({ actionId, connectionName }) => toolResult(await getActionGuide(options, actionId, connectionName)));
|
|
106
119
|
server.registerTool("execute_action", {
|
|
107
120
|
title: "Execute Action",
|
|
108
|
-
description: "Execute one local provider action by id with a JSON input object. Call get_action_guide first
|
|
121
|
+
description: "Execute one local provider action by id with a JSON input object. Call get_action_guide first.",
|
|
109
122
|
inputSchema: {
|
|
110
123
|
actionId: z.string().describe("Full action id, for example hackernews.get_item."),
|
|
111
124
|
input: z
|
|
@@ -178,13 +191,15 @@ async function searchActions(options, input) {
|
|
|
178
191
|
}
|
|
179
192
|
const query = input.query?.trim();
|
|
180
193
|
const actionSearch = options.actionSearch ?? createActionSearchIndexProvider(options.catalog.actions);
|
|
181
|
-
const
|
|
182
|
-
? searchActionIndex(await actionSearch.get(), query, { service: input.service, limit: input.limit })
|
|
194
|
+
const rankedActionsWithLookahead = query
|
|
195
|
+
? searchActionIndex(await actionSearch.get(), query, { service: input.service, limit: input.limit + 1 })
|
|
183
196
|
.map((result) => options.catalog.actionsById.get(result.id))
|
|
184
197
|
.filter((action) => Boolean(action))
|
|
185
198
|
: options.catalog.actions
|
|
186
199
|
.filter((action) => !input.service || action.service === input.service)
|
|
187
|
-
.slice(0, input.limit);
|
|
200
|
+
.slice(0, input.limit + 1);
|
|
201
|
+
const hasMore = rankedActionsWithLookahead.length > input.limit;
|
|
202
|
+
const rankedActions = rankedActionsWithLookahead.slice(0, input.limit);
|
|
188
203
|
const actions = rankedActions.map(async (action) => ({
|
|
189
204
|
id: action.id,
|
|
190
205
|
service: action.service,
|
|
@@ -193,7 +208,13 @@ async function searchActions(options, input) {
|
|
|
193
208
|
capability: await describeActionCapability(options, action, undefined, policy),
|
|
194
209
|
inputSummary: summarizeInputSchema(action.inputSchema),
|
|
195
210
|
}));
|
|
196
|
-
return
|
|
211
|
+
return {
|
|
212
|
+
ok: true,
|
|
213
|
+
data: await Promise.all(actions),
|
|
214
|
+
returnedCount: rankedActions.length,
|
|
215
|
+
hasMore,
|
|
216
|
+
...(hasMore ? { hint: moreActionSearchResultsHint } : {}),
|
|
217
|
+
};
|
|
197
218
|
}
|
|
198
219
|
async function getActionGuide(options, actionId, connectionName) {
|
|
199
220
|
const action = options.catalog.actionsById.get(actionId);
|
|
@@ -377,15 +398,52 @@ function createExecutionMeta(run) {
|
|
|
377
398
|
}
|
|
378
399
|
return meta;
|
|
379
400
|
}
|
|
380
|
-
function
|
|
401
|
+
function compactActionSearchPayload(payload) {
|
|
402
|
+
if (!payload.ok || !Array.isArray(payload.data)) {
|
|
403
|
+
return payload;
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
ok: true,
|
|
407
|
+
data: payload.data.map((value) => {
|
|
408
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
const action = value;
|
|
412
|
+
return {
|
|
413
|
+
id: action.id,
|
|
414
|
+
service: action.service,
|
|
415
|
+
name: action.name,
|
|
416
|
+
description: action.description,
|
|
417
|
+
inputSummary: action.inputSummary,
|
|
418
|
+
};
|
|
419
|
+
}),
|
|
420
|
+
returnedCount: payload.returnedCount,
|
|
421
|
+
hasMore: payload.hasMore,
|
|
422
|
+
...(typeof payload.hint === "string" ? { hint: payload.hint } : {}),
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function toolResult(payload, modelPayload = payload) {
|
|
381
426
|
return {
|
|
382
427
|
content: [
|
|
383
428
|
{
|
|
384
429
|
type: "text",
|
|
385
|
-
text:
|
|
430
|
+
text: serializeMcpModelContent(modelPayload),
|
|
386
431
|
},
|
|
387
432
|
],
|
|
388
433
|
structuredContent: payload,
|
|
389
434
|
...(payload.ok ? {} : { isError: true }),
|
|
390
435
|
};
|
|
391
436
|
}
|
|
437
|
+
function serializeMcpModelContent(payload) {
|
|
438
|
+
const serialized = JSON.stringify(payload);
|
|
439
|
+
if (serialized.length <= maxMcpModelContentLength) {
|
|
440
|
+
return serialized;
|
|
441
|
+
}
|
|
442
|
+
const notice = `\n[tool output truncated from ${serialized.length} characters; narrow the request or use pagination]`;
|
|
443
|
+
const maximumPrefixLength = Math.max(0, maxMcpModelContentLength - notice.length);
|
|
444
|
+
let prefix = serialized.slice(0, maximumPrefixLength);
|
|
445
|
+
if (prefix && /[\uD800-\uDBFF]/u.test(prefix.at(-1) ?? "")) {
|
|
446
|
+
prefix = prefix.slice(0, -1);
|
|
447
|
+
}
|
|
448
|
+
return `${prefix}${notice}`;
|
|
449
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ConnectionError } from "../../connection-service.js";
|
|
2
2
|
import { executeAction as executeProviderAction } from "../../core/execution.js";
|
|
3
|
+
import { generateUuidV7 } from "../../core/uuid-v7.js";
|
|
3
4
|
import { safeRunLogError, summarizeForRunLog } from "./run-log-summary.js";
|
|
4
5
|
/**
|
|
5
6
|
* Shared execution boundary for HTTP, MCP, and future local callers.
|
|
@@ -19,7 +20,7 @@ export class ActionRunner {
|
|
|
19
20
|
}, "action run rejected");
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
22
|
-
const executionId =
|
|
23
|
+
const executionId = generateUuidV7();
|
|
23
24
|
const logContext = {
|
|
24
25
|
actionId: action.id,
|
|
25
26
|
service: action.service,
|
package/package.json
CHANGED