@oomol-lab/open-connector 1.6.3 → 1.6.5
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/assets/open-connector/catalog/apps/aisa.json +568 -0
- package/assets/open-connector/catalog/apps/bilibili.json +2586 -0
- package/assets/open-connector/catalog/apps/bitwarden.json +1423 -0
- package/assets/open-connector/catalog/apps/cronfree.json +303 -0
- package/assets/open-connector/catalog/apps/cufinder.json +368 -0
- package/assets/open-connector/catalog/apps/feishu_app_bot.json +2 -2
- package/assets/open-connector/catalog/apps/gmail.json +73 -0
- package/assets/open-connector/catalog/apps/investoday_mcp.json +22228 -197
- package/assets/open-connector/catalog/apps/ixspy.json +2274 -0
- package/assets/open-connector/catalog/apps/jin10.json +1 -1
- package/assets/open-connector/catalog/apps/osv.json +663 -0
- package/assets/open-connector/catalog/apps/pdf_vector.json +329 -0
- package/assets/open-connector/catalog/apps/pulsetic.json +406 -0
- package/assets/open-connector/catalog/apps/shopee.json +1785 -0
- package/assets/open-connector/catalog/apps/slack.json +81 -0
- package/assets/open-connector/catalog/apps/slackbot.json +81 -0
- package/assets/open-connector/catalog/apps/speak_ai.json +360 -0
- package/assets/open-connector/catalog/apps/tongdaxin.json +11 -440
- package/assets/open-connector/catalog/apps/zyhub.json +1585 -0
- package/assets/open-connector/catalog/apps-index.json +1 -1
- package/package.json +1 -1
- package/src/core/json-schema.d.ts +5 -1
- package/src/core/json-schema.js +14 -2
- package/src/core/types.d.ts +9 -0
- package/src/oauth/oauth-credential-refresh-service.js +3 -1
- package/src/oauth/oauth-flow-service.js +17 -2
- package/src/oauth/oauth-token.d.ts +13 -0
- package/src/providers/action-contracts.generated.d.ts +16 -4
- package/src/providers/aisa/executors.d.ts +4 -0
- package/src/providers/aisa/executors.js +176 -0
- package/src/providers/bilibili/articles.d.ts +3 -0
- package/src/providers/bilibili/articles.js +369 -0
- package/src/providers/bilibili/executors.d.ts +8 -0
- package/src/providers/bilibili/executors.js +262 -0
- package/src/providers/bilibili/runtime.d.ts +95 -0
- package/src/providers/bilibili/runtime.js +238 -0
- package/src/providers/bilibili/upload.d.ts +14 -0
- package/src/providers/bilibili/upload.js +105 -0
- package/src/providers/bitwarden/executors.d.ts +3 -0
- package/src/providers/bitwarden/executors.js +146 -0
- package/src/providers/cronfree/executors.d.ts +4 -0
- package/src/providers/cronfree/executors.js +89 -0
- package/src/providers/cufinder/executors.d.ts +4 -0
- package/src/providers/cufinder/executors.js +105 -0
- package/src/providers/dropbox/executors.js +21 -3
- package/src/providers/elevenreader/runtime.js +6 -5
- package/src/providers/feishu_app_bot/scopes.js +1 -1
- package/src/providers/gmail/attachment-stream.d.ts +2 -0
- package/src/providers/gmail/attachment-stream.js +162 -0
- package/src/providers/gmail/executors.d.ts +3 -1
- package/src/providers/gmail/executors.js +53 -25
- package/src/providers/investoday_mcp/official-actions.js +19 -372
- package/src/providers/investoday_mcp/official-tool-types.d.ts +10 -0
- package/src/providers/investoday_mcp/official-tool-types.js +4 -0
- package/src/providers/investoday_mcp/official-tools/charts.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/charts.js +32 -0
- package/src/providers/investoday_mcp/official-tools/fixed-income.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/fixed-income.js +326 -0
- package/src/providers/investoday_mcp/official-tools/funds.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/funds.js +1085 -0
- package/src/providers/investoday_mcp/official-tools/hong-kong.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/hong-kong.js +275 -0
- package/src/providers/investoday_mcp/official-tools/indices-industries.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/indices-industries.js +642 -0
- package/src/providers/investoday_mcp/official-tools/macro-calendar.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/macro-calendar.js +398 -0
- package/src/providers/investoday_mcp/official-tools/other.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/other.js +131 -0
- package/src/providers/investoday_mcp/official-tools/research-news.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/research-news.js +302 -0
- package/src/providers/investoday_mcp/official-tools/stocks.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/stocks.js +2829 -0
- package/src/providers/ixspy/executors.d.ts +3 -0
- package/src/providers/ixspy/executors.js +220 -0
- package/src/providers/mcp-client.d.ts +1 -0
- package/src/providers/mcp-client.js +41 -27
- package/src/providers/osv/executors.d.ts +2 -0
- package/src/providers/osv/executors.js +11 -0
- package/src/providers/osv/runtime.d.ts +9 -0
- package/src/providers/osv/runtime.js +66 -0
- package/src/providers/pdf_vector/executors.d.ts +4 -0
- package/src/providers/pdf_vector/executors.js +108 -0
- package/src/providers/provider-runtime.d.ts +3 -0
- package/src/providers/provider-runtime.js +4 -1
- package/src/providers/pulsetic/executors.d.ts +4 -0
- package/src/providers/pulsetic/executors.js +126 -0
- package/src/providers/registry.generated.js +12 -0
- package/src/providers/sellersprite_mcp/executors.js +2 -1
- package/src/providers/shopee/actions.d.ts +2 -0
- package/src/providers/shopee/actions.js +474 -0
- package/src/providers/shopee/executors.d.ts +3 -0
- package/src/providers/shopee/executors.js +85 -0
- package/src/providers/shopee/runtime.d.ts +79 -0
- package/src/providers/shopee/runtime.js +693 -0
- package/src/providers/slack/actions.js +17 -0
- package/src/providers/slack/runtime.js +62 -4
- package/src/providers/speak_ai/executors.d.ts +3 -0
- package/src/providers/speak_ai/executors.js +187 -0
- package/src/providers/tongdaxin/executors.js +5 -4
- package/src/providers/tongdaxin/named-action-output.js +3 -9
- package/src/providers/tongdaxin/named-action-routing.js +1 -1
- package/src/providers/tongdaxin/named-actions.js +1 -6
- package/src/providers/tushare/runtime.js +22 -9
- package/src/providers/twenty_crm/runtime.js +4 -2
- package/src/providers/zyhub/executors.d.ts +3 -0
- package/src/providers/zyhub/executors.js +240 -0
- package/src/server/files/transit-files.d.ts +3 -2
- package/src/server/files/transit-files.js +31 -35
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { compactObject, looseArray, optionalInteger, optionalNumber, optionalRecord, optionalString, } from "../../core/cast.js";
|
|
2
|
+
import { defineApiKeyProviderExecutors, providerInputError, ProviderRequestError, providerResponseError, providerUserAgent, readProviderJsonBody, requiredResponseRecord, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
const service = "ixspy";
|
|
4
|
+
const baseUrl = "https://open.ixspy.com";
|
|
5
|
+
const configs = {
|
|
6
|
+
get_category_resource: { path: "/api/v1/aliexpress/category-resource", kind: "category_resource" },
|
|
7
|
+
list_category_children: { path: "/api/v1/aliexpress/category-children", kind: "category_children" },
|
|
8
|
+
search_categories: { path: "/api/v1/aliexpress/category-lookup", kind: "list" },
|
|
9
|
+
search_products: { path: "/api/v1/aliexpress/product-search", kind: "list" },
|
|
10
|
+
get_product_details: { path: "/api/v1/aliexpress/product-detail", kind: "list" },
|
|
11
|
+
search_stores: { path: "/api/v1/aliexpress/store-search", kind: "list" },
|
|
12
|
+
get_store_details: { path: "/api/v1/aliexpress/store-detail", kind: "list" },
|
|
13
|
+
get_product_sku_sales: { path: "/api/v1/aliexpress/product-orders-sku-stat", kind: "product_statistics" },
|
|
14
|
+
get_product_shipping_stats: { path: "/api/v1/aliexpress/product-orders-shipping-stat", kind: "product_statistics" },
|
|
15
|
+
get_product_region_sales: { path: "/api/v1/aliexpress/product-orders-region-stat", kind: "product_statistics" },
|
|
16
|
+
get_product_trends: { path: "/api/v1/aliexpress/product-trend-data", kind: "product_trends" },
|
|
17
|
+
get_store_category_distribution: { path: "/api/v1/aliexpress/store-category-stat", kind: "store_statistics" },
|
|
18
|
+
get_store_region_sales: { path: "/api/v1/aliexpress/store-orders-region-stat", kind: "store_statistics" },
|
|
19
|
+
get_store_trends: { path: "/api/v1/aliexpress/store-trend-data", kind: "store_trends" },
|
|
20
|
+
get_product_rank: { path: "/api/v1/aliexpress/product-rank", kind: "list" },
|
|
21
|
+
get_region_product_rank: { path: "/api/v1/aliexpress/product-rank-by-region", kind: "list" },
|
|
22
|
+
get_store_rank: { path: "/api/v1/aliexpress/store-rank", kind: "list" },
|
|
23
|
+
get_keyword_rank: { path: "/api/v1/aliexpress/keyword-rank", kind: "list" },
|
|
24
|
+
list_rank_dates: { path: "/api/v1/aliexpress/rank-date-list", kind: "rank_dates", method: "GET" },
|
|
25
|
+
research_categories: { path: "/api/v1/aliexpress/category-research", kind: "list" },
|
|
26
|
+
get_market_insights: { path: "/api/v1/aliexpress/market-insights", kind: "list" },
|
|
27
|
+
};
|
|
28
|
+
const keyOverrides = {
|
|
29
|
+
averagePriceMax: "avg_price_max",
|
|
30
|
+
averagePriceMin: "avg_price_min",
|
|
31
|
+
choiceTypes: "choice_type",
|
|
32
|
+
createdAfter: "created_time_min",
|
|
33
|
+
createdBefore: "created_time_max",
|
|
34
|
+
discoveredAfter: "discovered_time_start",
|
|
35
|
+
discoveredBefore: "discovered_time_end",
|
|
36
|
+
linkIds: "store_link_id",
|
|
37
|
+
merchantIds: "store_merchant_id",
|
|
38
|
+
productIds: "product_id_list",
|
|
39
|
+
sales7dMax: "sales_count_7d_max",
|
|
40
|
+
sales7dMin: "sales_count_7d_min",
|
|
41
|
+
updatedAfter: "updated_time_min",
|
|
42
|
+
updatedBefore: "updated_time_max",
|
|
43
|
+
};
|
|
44
|
+
const orderValues = {
|
|
45
|
+
clickRate: "click_rate",
|
|
46
|
+
conversionRate: "conversion_rate",
|
|
47
|
+
createdTime: "created_time",
|
|
48
|
+
discoveredTime: "discovered_time",
|
|
49
|
+
sales7d: "sales_count_7d",
|
|
50
|
+
searchGrowth: "searches_rise_index",
|
|
51
|
+
searchPopularity: "search_cookies_count",
|
|
52
|
+
totalFollowers: "total_followers",
|
|
53
|
+
totalReviews: "total_reviews",
|
|
54
|
+
totalSales: "total_sales",
|
|
55
|
+
};
|
|
56
|
+
const rankValues = {
|
|
57
|
+
holidayNew: "holiday_new",
|
|
58
|
+
holidayNormal: "holiday_normal",
|
|
59
|
+
holidayRank: "holiday_rank",
|
|
60
|
+
keywordRank: "keyword_rank",
|
|
61
|
+
newGrowth: "new_inc",
|
|
62
|
+
newHot: "new_hot",
|
|
63
|
+
normalGrowth: "normal_inc",
|
|
64
|
+
normalHot: "normal_hot",
|
|
65
|
+
normalRank: "normal_rank",
|
|
66
|
+
};
|
|
67
|
+
const handlers = Object.fromEntries(Object.entries(configs).map(([name, config]) => [
|
|
68
|
+
name,
|
|
69
|
+
async (input, context) => normalize(config.kind, await request(name, config, input, context, false)),
|
|
70
|
+
]));
|
|
71
|
+
export const executors = defineApiKeyProviderExecutors(service, handlers, {
|
|
72
|
+
skipDnsValidation: true,
|
|
73
|
+
});
|
|
74
|
+
export const credentialValidators = {
|
|
75
|
+
async apiKey(input, context) {
|
|
76
|
+
const payload = await request("list_category_children", configs.list_category_children, { parentId: 0 }, { ...context, apiKey: input.apiKey }, true);
|
|
77
|
+
const data = requiredResponseRecord(payload.data, "IXSPY category data");
|
|
78
|
+
return {
|
|
79
|
+
profile: { accountId: "ixspy-api-key", displayName: "IXSPY API Key" },
|
|
80
|
+
grantedScopes: [],
|
|
81
|
+
metadata: {
|
|
82
|
+
apiBaseUrl: baseUrl,
|
|
83
|
+
validationEndpoint: configs.list_category_children.path,
|
|
84
|
+
categoryCount: optionalInteger(data.count) ?? looseArray(data.children).length,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
async function request(name, config, input, context, validating) {
|
|
90
|
+
const method = config.method ?? "POST";
|
|
91
|
+
const body = upstream(name, input);
|
|
92
|
+
return runProviderRequest({ signal: context.signal, label: "IXSPY" }, async (signal) => {
|
|
93
|
+
const url = new URL(config.path, baseUrl);
|
|
94
|
+
if (method === "GET")
|
|
95
|
+
for (const [key, value] of Object.entries(body)) {
|
|
96
|
+
if (Array.isArray(value))
|
|
97
|
+
for (const item of value)
|
|
98
|
+
url.searchParams.append(key, String(item));
|
|
99
|
+
else
|
|
100
|
+
url.searchParams.set(key, String(value));
|
|
101
|
+
}
|
|
102
|
+
const response = await context.fetcher(url, {
|
|
103
|
+
method,
|
|
104
|
+
headers: {
|
|
105
|
+
accept: "application/json",
|
|
106
|
+
authorization: `Bearer ${context.apiKey}`,
|
|
107
|
+
...(method === "POST" ? { "content-type": "application/json" } : {}),
|
|
108
|
+
"user-agent": providerUserAgent,
|
|
109
|
+
},
|
|
110
|
+
body: method === "POST" ? JSON.stringify(body) : undefined,
|
|
111
|
+
signal,
|
|
112
|
+
});
|
|
113
|
+
const payload = requiredResponseRecord(await readProviderJsonBody(response, { emptyBody: {}, invalidJsonMessage: "IXSPY returned invalid JSON" }), "IXSPY response");
|
|
114
|
+
const error = optionalRecord(payload.error);
|
|
115
|
+
const code = optionalInteger(error?.code);
|
|
116
|
+
if (code !== undefined && code !== 0)
|
|
117
|
+
throw ixspyError(code, optionalString(error?.message), validating);
|
|
118
|
+
if (!response.ok)
|
|
119
|
+
throw new ProviderRequestError(response.status, optionalString(error?.message) ?? `IXSPY request failed with HTTP ${response.status}`, payload);
|
|
120
|
+
if (code !== 0)
|
|
121
|
+
throw providerResponseError("IXSPY response is missing a success code");
|
|
122
|
+
return payload;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function upstream(name, input) {
|
|
126
|
+
return compactObject(Object.fromEntries(Object.entries(input).map(([key, value]) => {
|
|
127
|
+
let field = keyOverrides[key] ?? snake(key);
|
|
128
|
+
if ((name === "get_product_rank" || name === "get_region_product_rank") &&
|
|
129
|
+
(key === "priceMin" || key === "priceMax"))
|
|
130
|
+
field = key === "priceMin" ? "product_price_min" : "product_price_max";
|
|
131
|
+
if (key === "hasVideo")
|
|
132
|
+
value = value === true ? 1 : value === false ? 2 : value;
|
|
133
|
+
if ((key === "isLeaf" || key === "styleZone") && typeof value === "boolean")
|
|
134
|
+
value = value ? 1 : 0;
|
|
135
|
+
if (key === "orderBy" && typeof value === "string")
|
|
136
|
+
value = orderValues[value] ?? value;
|
|
137
|
+
if (key === "rankType" && typeof value === "string")
|
|
138
|
+
value = rankValues[value] ?? value;
|
|
139
|
+
return [field, value];
|
|
140
|
+
})));
|
|
141
|
+
}
|
|
142
|
+
function snake(value) {
|
|
143
|
+
let result = "";
|
|
144
|
+
for (const character of value)
|
|
145
|
+
result += character === character.toLowerCase() ? character : `_${character.toLowerCase()}`;
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
function ixspyError(code, message, validating) {
|
|
149
|
+
const detail = message?.trim() || `IXSPY error ${code}`;
|
|
150
|
+
if (code === 10402 || code === 10404)
|
|
151
|
+
return validating ? providerInputError(detail) : new ProviderRequestError(409, detail);
|
|
152
|
+
if (code === 10429)
|
|
153
|
+
return new ProviderRequestError(429, detail);
|
|
154
|
+
if (code === 5024)
|
|
155
|
+
return new ProviderRequestError(402, detail);
|
|
156
|
+
if (code === 10422)
|
|
157
|
+
return providerInputError(detail);
|
|
158
|
+
return new ProviderRequestError(code === 5016 ? 404 : 502, detail);
|
|
159
|
+
}
|
|
160
|
+
function normalize(kind, payload) {
|
|
161
|
+
const data = requiredResponseRecord(payload.data, "IXSPY data");
|
|
162
|
+
const credits = optionalNumber(payload.credits) ?? null;
|
|
163
|
+
if (kind === "category_resource")
|
|
164
|
+
return { url: responseString(data.link, "category resource link"), credits };
|
|
165
|
+
if (kind === "category_children")
|
|
166
|
+
return {
|
|
167
|
+
parentId: identifier(data.parent_id, "parent category ID"),
|
|
168
|
+
count: optionalInteger(data.count) ?? looseArray(data.children).length,
|
|
169
|
+
categories: looseArray(data.children),
|
|
170
|
+
credits,
|
|
171
|
+
};
|
|
172
|
+
if (kind === "rank_dates")
|
|
173
|
+
return {
|
|
174
|
+
rankType: responseString(data.rank_type, "rank type"),
|
|
175
|
+
dateTypes: looseArray(data.date_type_list),
|
|
176
|
+
credits,
|
|
177
|
+
};
|
|
178
|
+
if (kind === "product_statistics" || kind === "store_statistics")
|
|
179
|
+
return {
|
|
180
|
+
entityId: identifier(data[kind === "product_statistics" ? "product_id" : "store_merchant_id"], "entity ID"),
|
|
181
|
+
statistics: looseArray(data.stat_data),
|
|
182
|
+
credits,
|
|
183
|
+
};
|
|
184
|
+
if (kind === "product_trends" || kind === "store_trends")
|
|
185
|
+
return {
|
|
186
|
+
entityId: identifier(data[kind === "product_trends" ? "product_id" : "store_merchant_id"], "entity ID"),
|
|
187
|
+
startDate: optionalString(data.start_date) ?? null,
|
|
188
|
+
endDate: optionalString(data.end_date) ?? null,
|
|
189
|
+
points: looseArray(data.trend_data),
|
|
190
|
+
credits,
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
items: looseArray(data.list),
|
|
194
|
+
page: numberLike(data.page),
|
|
195
|
+
pages: numberLike(data.pages),
|
|
196
|
+
total: numberLike(data.total),
|
|
197
|
+
credits,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function identifier(value, label) {
|
|
201
|
+
if (typeof value === "string" || (typeof value === "number" && Number.isFinite(value)))
|
|
202
|
+
return value;
|
|
203
|
+
throw providerResponseError(`IXSPY ${label} is required`);
|
|
204
|
+
}
|
|
205
|
+
function responseString(value, label) {
|
|
206
|
+
const result = optionalString(value);
|
|
207
|
+
if (result != null)
|
|
208
|
+
return result;
|
|
209
|
+
throw providerResponseError(`IXSPY ${label} is required`);
|
|
210
|
+
}
|
|
211
|
+
function numberLike(value) {
|
|
212
|
+
if (typeof value === "number")
|
|
213
|
+
return value;
|
|
214
|
+
if (typeof value === "string" && value.trim()) {
|
|
215
|
+
const result = Number(value);
|
|
216
|
+
if (Number.isFinite(result))
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
@@ -10,6 +10,7 @@ export interface McpClientOptions {
|
|
|
10
10
|
signal?: AbortSignal;
|
|
11
11
|
protocolVersion?: McpProtocolVersion;
|
|
12
12
|
terminateSession?: boolean;
|
|
13
|
+
retryOnSessionNotFound?: boolean;
|
|
13
14
|
mapError?: (error: unknown) => unknown;
|
|
14
15
|
}
|
|
15
16
|
export declare function withMcpClient<T>(options: McpClientOptions, run: (client: Client) => Promise<T>): Promise<T>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/client";
|
|
1
|
+
import { Client, SdkHttpError } from "@modelcontextprotocol/client";
|
|
2
2
|
import { SSEClientTransport } from "@modelcontextprotocol/client";
|
|
3
3
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
4
4
|
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/client/validators/cf-worker";
|
|
@@ -6,33 +6,47 @@ const mcpConnectTimeoutMs = 60_000;
|
|
|
6
6
|
const modernMcpProtocolVersion = "2026-07-28";
|
|
7
7
|
const mcpJsonSchemaValidator = new CfWorkerJsonSchemaValidator();
|
|
8
8
|
export async function withMcpClient(options, run) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
9
|
+
let retriedSession = false;
|
|
10
|
+
while (true) {
|
|
11
|
+
const transportOptions = {
|
|
12
|
+
fetch: options.fetcher,
|
|
13
|
+
requestInit: {
|
|
14
|
+
headers: options.headers,
|
|
15
|
+
redirect: options.redirect,
|
|
16
|
+
signal: options.signal,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const transport = options.transport === "sse"
|
|
20
|
+
? new SSEClientTransport(options.endpoint, transportOptions)
|
|
21
|
+
: new StreamableHTTPClientTransport(options.endpoint, transportOptions);
|
|
22
|
+
const client = new Client({ name: "open-connector", version: "1.0.0" }, {
|
|
23
|
+
jsonSchemaValidator: mcpJsonSchemaValidator,
|
|
24
|
+
versionNegotiation: { mode: resolveVersionNegotiationMode(options.protocolVersion) },
|
|
25
|
+
});
|
|
26
|
+
let connected = false;
|
|
27
|
+
try {
|
|
28
|
+
await client.connect(transport, { timeout: mcpConnectTimeoutMs, signal: options.signal });
|
|
29
|
+
connected = true;
|
|
30
|
+
return await run(client);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (connected &&
|
|
34
|
+
options.transport === "streamable_http" &&
|
|
35
|
+
options.retryOnSessionNotFound === true &&
|
|
36
|
+
!retriedSession &&
|
|
37
|
+
error instanceof SdkHttpError &&
|
|
38
|
+
error.status === 404) {
|
|
39
|
+
retriedSession = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
throw options.mapError ? options.mapError(error) : error;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (options.terminateSession && transport instanceof StreamableHTTPClientTransport) {
|
|
46
|
+
await terminateMcpSession(transport);
|
|
47
|
+
}
|
|
48
|
+
await client.close().catch(() => undefined);
|
|
34
49
|
}
|
|
35
|
-
await client.close().catch(() => undefined);
|
|
36
50
|
}
|
|
37
51
|
}
|
|
38
52
|
async function terminateMcpSession(transport) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineProviderExecutors } from "../provider-runtime.js";
|
|
2
|
+
import { osvActionHandlers } from "./runtime.js";
|
|
3
|
+
const service = "osv";
|
|
4
|
+
export const executors = defineProviderExecutors({
|
|
5
|
+
service,
|
|
6
|
+
handlers: osvActionHandlers,
|
|
7
|
+
skipDnsValidation: true,
|
|
8
|
+
createContext(context, fetcher) {
|
|
9
|
+
return { fetcher, signal: context.signal };
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProviderActionHandlers, ProviderFetch } from "../provider-runtime.ts";
|
|
2
|
+
export declare const osvApiBaseUrl = "https://api.osv.dev";
|
|
3
|
+
export interface OsvRuntimeContext {
|
|
4
|
+
fetcher: ProviderFetch;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
type OsvActionHandler = (input: Record<string, unknown>, context: OsvRuntimeContext) => Promise<unknown>;
|
|
8
|
+
export declare const osvActionHandlers: ProviderActionHandlers<"osv", OsvActionHandler>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { compactObject, optionalRawString, optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { providerResponseError, providerUserAgent, ProviderRequestError, readProviderJsonBody, requiredInputString, requiredResponseRecord, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
export const osvApiBaseUrl = "https://api.osv.dev";
|
|
4
|
+
export const osvActionHandlers = {
|
|
5
|
+
async query_vulnerabilities(input, context) {
|
|
6
|
+
const payload = requiredResponseRecord(await requestOsvJson("/v1/query", {
|
|
7
|
+
method: "POST",
|
|
8
|
+
body: JSON.stringify(compactObject({
|
|
9
|
+
package: {
|
|
10
|
+
ecosystem: requiredInputString(input.ecosystem, "ecosystem"),
|
|
11
|
+
name: requiredInputString(input.package, "package"),
|
|
12
|
+
},
|
|
13
|
+
version: optionalString(input.version),
|
|
14
|
+
page_token: optionalString(input.pageToken),
|
|
15
|
+
})),
|
|
16
|
+
}, context), "OSV query response");
|
|
17
|
+
const vulnerabilities = payload.vulns === undefined ? [] : payload.vulns;
|
|
18
|
+
if (!Array.isArray(vulnerabilities)) {
|
|
19
|
+
throw providerResponseError("OSV query vulnerabilities must be an array of objects");
|
|
20
|
+
}
|
|
21
|
+
if (!vulnerabilities.every((vulnerability) => optionalRecord(vulnerability))) {
|
|
22
|
+
throw providerResponseError("OSV query vulnerabilities must be an array of objects");
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
vulnerabilities,
|
|
26
|
+
nextPageToken: optionalString(payload.next_page_token),
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
async get_vulnerability(input, context) {
|
|
30
|
+
const id = encodeURIComponent(requiredInputString(input.id, "id"));
|
|
31
|
+
return requiredResponseRecord(await requestOsvJson(`/v1/vulns/${id}`, { method: "GET" }, context), "OSV vulnerability response");
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
async function requestOsvJson(path, init, context) {
|
|
35
|
+
return runProviderRequest({ label: "OSV", signal: context.signal }, async (signal) => {
|
|
36
|
+
const response = await context.fetcher(`${osvApiBaseUrl}${path}`, {
|
|
37
|
+
...init,
|
|
38
|
+
headers: {
|
|
39
|
+
accept: "application/json",
|
|
40
|
+
"content-type": "application/json",
|
|
41
|
+
"user-agent": providerUserAgent,
|
|
42
|
+
},
|
|
43
|
+
signal,
|
|
44
|
+
});
|
|
45
|
+
const payload = await readProviderJsonBody(response, {
|
|
46
|
+
emptyBody: null,
|
|
47
|
+
invalidJsonMessage: "OSV returned invalid JSON",
|
|
48
|
+
invalidJsonFallback: response.ok ? undefined : (text) => text.trim(),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw createOsvError(response.status, payload);
|
|
52
|
+
}
|
|
53
|
+
return payload;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function createOsvError(status, payload) {
|
|
57
|
+
const record = optionalRecord(payload);
|
|
58
|
+
const message = optionalRawString(payload) ??
|
|
59
|
+
optionalString(record?.message) ??
|
|
60
|
+
optionalString(record?.error) ??
|
|
61
|
+
`OSV request failed with status ${status}`;
|
|
62
|
+
if (status === 401 || status === 403) {
|
|
63
|
+
return new ProviderRequestError(502, message, payload);
|
|
64
|
+
}
|
|
65
|
+
return new ProviderRequestError(status || 502, message, payload);
|
|
66
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
4
|
+
export declare const credentialValidators: CredentialValidators;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { compactObject, optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { defineApiKeyProviderExecutors, defineProviderProxy, providerInputError, ProviderRequestError, providerUserAgent, readProviderJsonBody, requiredInputString, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
const service = "pdf_vector";
|
|
4
|
+
const pdfVectorApiBaseUrl = "https://global.pdfvector.com/api";
|
|
5
|
+
const credentialUrl = "https://global.pdfvector.com/rpc/authenticate/validateCredential";
|
|
6
|
+
const handlers = {
|
|
7
|
+
parse_document(input, context) {
|
|
8
|
+
return executeDocumentAction("/document/parse", input, context);
|
|
9
|
+
},
|
|
10
|
+
ask_document(input, context) {
|
|
11
|
+
return executeDocumentAction("/document/ask", input, context);
|
|
12
|
+
},
|
|
13
|
+
extract_document(input, context) {
|
|
14
|
+
return executeDocumentAction("/document/extract", input, context);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export const executors = defineApiKeyProviderExecutors(service, handlers, {
|
|
18
|
+
skipDnsValidation: true,
|
|
19
|
+
});
|
|
20
|
+
export const proxy = defineProviderProxy({
|
|
21
|
+
service,
|
|
22
|
+
baseUrl: pdfVectorApiBaseUrl,
|
|
23
|
+
auth: { type: "bearer" },
|
|
24
|
+
skipDnsValidation: true,
|
|
25
|
+
customizeRequest({ headers }) {
|
|
26
|
+
headers.set("accept", "application/json");
|
|
27
|
+
headers.set("x-pdfvector-source", "oomol-connector");
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
export const credentialValidators = {
|
|
31
|
+
async apiKey(input, { fetcher, signal }) {
|
|
32
|
+
const payload = await requestJson({
|
|
33
|
+
url: credentialUrl,
|
|
34
|
+
body: {},
|
|
35
|
+
context: { apiKey: input.apiKey, fetcher, signal },
|
|
36
|
+
phase: "validate",
|
|
37
|
+
});
|
|
38
|
+
const version = optionalString(optionalRecord(optionalRecord(payload)?.json)?.version);
|
|
39
|
+
if (!version)
|
|
40
|
+
throw new ProviderRequestError(502, "PDF Vector credential validation response did not include a server version");
|
|
41
|
+
return {
|
|
42
|
+
profile: { accountId: "pdf-vector-api-key", displayName: "PDF Vector API Key" },
|
|
43
|
+
grantedScopes: [],
|
|
44
|
+
metadata: {
|
|
45
|
+
apiBaseUrl: pdfVectorApiBaseUrl,
|
|
46
|
+
validationEndpoint: "/rpc/authenticate/validateCredential",
|
|
47
|
+
serverVersion: version,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
function executeDocumentAction(path, input, context) {
|
|
53
|
+
return requestJson({
|
|
54
|
+
url: `${pdfVectorApiBaseUrl}${path}`,
|
|
55
|
+
body: compactObject({
|
|
56
|
+
url: requiredInputString(input.documentUrl, "documentUrl"),
|
|
57
|
+
model: optionalString(input.model),
|
|
58
|
+
includePages: input.includePages,
|
|
59
|
+
question: optionalString(input.question),
|
|
60
|
+
prompt: optionalString(input.prompt),
|
|
61
|
+
schema: optionalRecord(input.schema),
|
|
62
|
+
}),
|
|
63
|
+
documentId: optionalString(input.documentId),
|
|
64
|
+
context,
|
|
65
|
+
phase: "execute",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function requestJson(input) {
|
|
69
|
+
return runProviderRequest({ signal: input.context.signal, label: "PDF Vector" }, async (signal) => {
|
|
70
|
+
const response = await input.context.fetcher(input.url, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
accept: "application/json",
|
|
74
|
+
authorization: `Bearer ${input.context.apiKey}`,
|
|
75
|
+
"content-type": "application/json",
|
|
76
|
+
"user-agent": providerUserAgent,
|
|
77
|
+
"x-pdfvector-source": "oomol-connector",
|
|
78
|
+
...(input.documentId ? { "x-pdfvector-document-id": input.documentId } : {}),
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify(input.body),
|
|
81
|
+
signal,
|
|
82
|
+
});
|
|
83
|
+
const payload = await readProviderJsonBody(response, {
|
|
84
|
+
emptyBody: null,
|
|
85
|
+
invalidJsonMessage: "PDF Vector returned invalid JSON",
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok)
|
|
88
|
+
throw createError(response.status, payload, input.phase);
|
|
89
|
+
return payload;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function createError(status, payload, phase) {
|
|
93
|
+
const record = optionalRecord(payload);
|
|
94
|
+
const json = optionalRecord(record?.json);
|
|
95
|
+
const error = optionalRecord(record?.error);
|
|
96
|
+
const message = optionalString(json?.message) ??
|
|
97
|
+
optionalString(error?.message) ??
|
|
98
|
+
optionalString(record?.message) ??
|
|
99
|
+
`PDF Vector request failed with status ${status}`;
|
|
100
|
+
const code = optionalString(json?.code) ?? optionalString(error?.code) ?? optionalString(record?.code);
|
|
101
|
+
if (phase === "validate" && status === 401 && code === "UNAUTHORIZED")
|
|
102
|
+
return providerInputError(message);
|
|
103
|
+
if (status === 429)
|
|
104
|
+
return new ProviderRequestError(429, message, payload);
|
|
105
|
+
if (phase === "execute" && [400, 404, 422].includes(status))
|
|
106
|
+
return providerInputError(message);
|
|
107
|
+
return new ProviderRequestError(status || 502, message, payload);
|
|
108
|
+
}
|
|
@@ -284,6 +284,8 @@ export interface ProviderProxyDefinition {
|
|
|
284
284
|
auth: ProviderProxyAuth;
|
|
285
285
|
allowedEndpoint?: (endpoint: string) => boolean;
|
|
286
286
|
customizeRequest?: (input: ProviderProxyRequestCustomizationInput) => Promise<void> | void;
|
|
287
|
+
/** Parse a failed HTTP response using the same provider error rules as actions. */
|
|
288
|
+
readError?: (response: Response) => Promise<ProviderRequestError>;
|
|
287
289
|
/** Provider-specific credential/signature headers that redirects must not forward cross-origin. */
|
|
288
290
|
sensitiveHeaders?: readonly string[];
|
|
289
291
|
/** Exact code-controlled origins that `customizeRequest` may select in addition to the resolved base origin. */
|
|
@@ -299,6 +301,7 @@ export interface ProviderProxyDefinition {
|
|
|
299
301
|
/** Deliberate provider-specific response byte cap; defaults to the shared 20 MiB limit. */
|
|
300
302
|
maxResponseBytes?: number;
|
|
301
303
|
}
|
|
304
|
+
export declare const defaultProviderJsonMaxResponseBytes: number;
|
|
302
305
|
export declare function createProviderProxyUrl(baseUrl: string, endpointInput: unknown, queryInput?: unknown, allowedOrigins?: readonly string[]): URL;
|
|
303
306
|
export declare function normalizeProviderProxyEndpoint(endpointInput: unknown, allowedOrigins?: readonly string[]): string;
|
|
304
307
|
export declare function normalizeProviderProxyQuery(queryInput: unknown): Record<string, string>;
|
|
@@ -125,7 +125,7 @@ const blockedProxyRequestHeaders = new Set([
|
|
|
125
125
|
"transfer-encoding",
|
|
126
126
|
]);
|
|
127
127
|
const defaultProviderProxyMaxResponseBytes = 20 * 1024 * 1024;
|
|
128
|
-
const defaultProviderJsonMaxResponseBytes = 20 * 1024 * 1024;
|
|
128
|
+
export const defaultProviderJsonMaxResponseBytes = 20 * 1024 * 1024;
|
|
129
129
|
const defaultProviderErrorMaxResponseBytes = 64 * 1024;
|
|
130
130
|
const defaultProviderRequestTimeoutMs = 30_000;
|
|
131
131
|
export function createProviderProxyUrl(baseUrl, endpointInput, queryInput, allowedOrigins) {
|
|
@@ -356,6 +356,9 @@ export function defineProviderProxy(input) {
|
|
|
356
356
|
}
|
|
357
357
|
const response = await egressFetch(url, init);
|
|
358
358
|
if (!response.ok) {
|
|
359
|
+
if (input.readError) {
|
|
360
|
+
throw await input.readError(response);
|
|
361
|
+
}
|
|
359
362
|
throw new ProviderRequestError(response.status, await readProviderProxyErrorMessage(response, `provider request failed with HTTP ${response.status}`));
|
|
360
363
|
}
|
|
361
364
|
return {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
4
|
+
export declare const credentialValidators: CredentialValidators;
|