@oomol-lab/open-connector 1.6.5 → 1.7.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/assets/open-connector/catalog/apps/aisa.json +6138 -312
- package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
- package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
- package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
- package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
- package/assets/open-connector/catalog/apps/framejet.json +273 -0
- package/assets/open-connector/catalog/apps/futunn.json +2967 -0
- package/assets/open-connector/catalog/apps/gmail.json +225 -0
- package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
- package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
- package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
- package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
- package/assets/open-connector/catalog/apps/googlechat.json +694 -7
- package/assets/open-connector/catalog/apps/googledocs.json +4 -1
- package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
- package/assets/open-connector/catalog/apps/granola.json +23 -0
- package/assets/open-connector/catalog/apps/keepa.json +3534 -987
- package/assets/open-connector/catalog/apps/linear.json +17 -0
- package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
- package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
- package/assets/open-connector/catalog/apps/notion.json +446 -1
- package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
- package/assets/open-connector/catalog/apps/odoo.json +619 -0
- package/assets/open-connector/catalog/apps/outlook.json +30 -8
- package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
- package/assets/open-connector/catalog/apps/postiz.json +334 -0
- package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
- package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
- package/assets/open-connector/catalog/apps/slack.json +223 -0
- package/assets/open-connector/catalog/apps/slackbot.json +214 -0
- package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
- package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
- package/assets/open-connector/catalog/apps/youtube.json +4 -1
- package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
- package/assets/open-connector/catalog/apps-index.json +1 -1
- package/package.json +1 -1
- package/src/core/cast.d.ts +10 -0
- package/src/core/cast.js +14 -0
- package/src/core/guarded-fetch.d.ts +6 -3
- package/src/core/guarded-fetch.js +6 -3
- package/src/core/provider-scenarios.js +67 -13
- package/src/core/request.js +4 -1
- package/src/core/types.d.ts +1 -1
- package/src/mcp.js +10 -7
- package/src/oauth/oauth-client-config-service.d.ts +16 -1
- package/src/oauth/oauth-client-config-service.js +59 -3
- package/src/oauth/oauth-flow-service.d.ts +6 -0
- package/src/oauth/oauth-flow-service.js +4 -2
- package/src/providers/17track/runtime.js +1 -1
- package/src/providers/action-contracts.generated.d.ts +17 -5
- package/src/providers/aifinmarket/runtime.js +1 -1
- package/src/providers/aisa/executors.js +734 -46
- package/src/providers/aliyun_ack/runtime.js +1 -1
- package/src/providers/aliyun_sls/runtime.js +1 -1
- package/src/providers/app_store_server/runtime-helpers.js +1 -1
- package/src/providers/baizhi_mcp/actions.d.ts +2 -0
- package/src/providers/baizhi_mcp/actions.js +40 -0
- package/src/providers/baizhi_mcp/executors.d.ts +3 -0
- package/src/providers/baizhi_mcp/executors.js +102 -0
- package/src/providers/bazhuayu/runtime.js +2 -2
- package/src/providers/bilibili/runtime.js +2 -4
- package/src/providers/bilibili/upload.js +9 -7
- package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
- package/src/providers/cloudflare_dns/executors.js +1 -1
- package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
- package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
- package/src/providers/cloudflare_dns/runtime.js +1 -1
- package/src/providers/cloudflare_r2/executors.js +1 -1
- package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
- package/src/providers/cloudflare_r2/runtime.js +1 -1
- package/src/providers/cloudflare_worker/executors.js +1 -1
- package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
- package/src/providers/cloudflare_worker/runtime.js +1 -1
- package/src/providers/dingtalk_mcp/runtime.js +1 -1
- package/src/providers/discordbot/executors.d.ts +0 -9
- package/src/providers/discordbot/executors.js +9 -101
- package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
- package/src/providers/discordbot/runtime-guilds.js +464 -0
- package/src/providers/discordbot/runtime.d.ts +42 -0
- package/src/providers/discordbot/runtime.js +124 -0
- package/src/providers/drawio_mcp/executors.d.ts +3 -0
- package/src/providers/drawio_mcp/executors.js +19 -0
- package/src/providers/drawio_mcp/runtime.d.ts +8 -0
- package/src/providers/drawio_mcp/runtime.js +175 -0
- package/src/providers/fastmoss_mcp/executors.js +2 -2
- package/src/providers/finchina/executors.js +1 -1
- package/src/providers/framejet/executors.d.ts +4 -0
- package/src/providers/framejet/executors.js +19 -0
- package/src/providers/framejet/runtime.d.ts +16 -0
- package/src/providers/framejet/runtime.js +148 -0
- package/src/providers/futunn/actions.d.ts +2 -0
- package/src/providers/futunn/actions.js +152 -0
- package/src/providers/futunn/actions.research.d.ts +2 -0
- package/src/providers/futunn/actions.research.js +423 -0
- package/src/providers/futunn/actions.trading.d.ts +2 -0
- package/src/providers/futunn/actions.trading.js +204 -0
- package/src/providers/futunn/executors.d.ts +3 -0
- package/src/providers/futunn/executors.js +24 -0
- package/src/providers/futunn/runtime-research.d.ts +3 -0
- package/src/providers/futunn/runtime-research.js +133 -0
- package/src/providers/futunn/runtime-trading.d.ts +3 -0
- package/src/providers/futunn/runtime-trading.js +39 -0
- package/src/providers/futunn/runtime.d.ts +2 -0
- package/src/providers/futunn/runtime.js +50 -0
- package/src/providers/futunn/transport.d.ts +11 -0
- package/src/providers/futunn/transport.js +76 -0
- package/src/providers/gildata/executors.js +1 -1
- package/src/providers/gmail/executors.js +3 -3
- package/src/providers/gmail/message.d.ts +6 -8
- package/src/providers/gmail/message.js +4 -0
- package/src/providers/gmail/scopes.d.ts +32 -0
- package/src/providers/gmail/scopes.js +37 -0
- package/src/providers/google_analytics/executors.js +2 -2
- package/src/providers/google_analytics/scopes.js +6 -1
- package/src/providers/google_bigquery/executors.js +2 -2
- package/src/providers/google_bigquery/scopes.js +2 -0
- package/src/providers/google_search_console/executors.js +2 -2
- package/src/providers/google_search_console/scopes.js +2 -0
- package/src/providers/googleads/executors.js +1 -1
- package/src/providers/googleads/scopes.d.ts +1 -3
- package/src/providers/googleads/scopes.js +2 -4
- package/src/providers/googlecalendar/executors.js +1 -1
- package/src/providers/googlecalendar/runtime-shared.js +2 -2
- package/src/providers/googlecalendar/scopes.js +2 -0
- package/src/providers/googlechat/executors.d.ts +1 -3
- package/src/providers/googlechat/executors.js +219 -28
- package/src/providers/googlechat/members.d.ts +55 -0
- package/src/providers/googlechat/members.js +293 -0
- package/src/providers/googlechat/runtime.d.ts +16 -0
- package/src/providers/googlechat/runtime.js +25 -0
- package/src/providers/googlechat/scopes.d.ts +22 -3
- package/src/providers/googlechat/scopes.js +30 -7
- package/src/providers/googledocs/executors.js +1 -1
- package/src/providers/googledrive/executors.js +2 -2
- package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
- package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
- package/src/providers/googledrive/runtime-collaboration.js +1 -1
- package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
- package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
- package/src/providers/googleforms/executors.js +2 -2
- package/src/providers/googleforms/scopes.d.ts +0 -3
- package/src/providers/googleforms/scopes.js +2 -6
- package/src/providers/googlemeet/executors.js +2 -2
- package/src/providers/googlemeet/scopes.d.ts +0 -3
- package/src/providers/googlemeet/scopes.js +2 -6
- package/src/providers/googlesheets/executors.js +1 -1
- package/src/providers/googlesheets/scopes.js +2 -0
- package/src/providers/googleslides/executors.js +2 -2
- package/src/providers/googleslides/scopes.d.ts +0 -3
- package/src/providers/googleslides/scopes.js +2 -6
- package/src/providers/googletasks/executors.js +2 -2
- package/src/providers/googletasks/scopes.d.ts +0 -3
- package/src/providers/googletasks/scopes.js +2 -6
- package/src/providers/granola/actions.js +2 -0
- package/src/providers/granola/runtime-mcp.js +2 -0
- package/src/providers/granola/runtime.js +2 -2
- package/src/providers/indiegogo/executors.js +1 -1
- package/src/providers/investoday_mcp/executors.js +2 -2
- package/src/providers/iyunbiao/runtime.js +2 -2
- package/src/providers/keepa/actions.d.ts +1 -1
- package/src/providers/keepa/actions.js +371 -149
- package/src/providers/keepa/deal-attributes.d.ts +1 -0
- package/src/providers/keepa/deal-attributes.js +26 -0
- package/src/providers/keepa/runtime.js +212 -18
- package/src/providers/kingdee/executors.js +1 -1
- package/src/providers/kingdee/runtime.js +2 -2
- package/src/providers/kuaidi100/runtime.js +1 -1
- package/src/providers/linear/executors.js +12 -9
- package/src/providers/linear_mcp/actions.d.ts +47 -0
- package/src/providers/linear_mcp/actions.js +102 -0
- package/src/providers/linear_mcp/endpoints.d.ts +3 -0
- package/src/providers/linear_mcp/endpoints.js +3 -0
- package/src/providers/linear_mcp/executors.d.ts +3 -0
- package/src/providers/linear_mcp/executors.js +9 -0
- package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
- package/src/providers/linear_mcp/runtime-mcp.js +238 -0
- package/src/providers/lingxing_mcp/runtime.js +1 -1
- package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
- package/src/providers/metabase/mcp-runtime.js +1 -1
- package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
- package/src/providers/moneyforward_mcp/executors.js +11 -0
- package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
- package/src/providers/moneyforward_mcp/manifest.js +204 -0
- package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
- package/src/providers/moneyforward_mcp/runtime.js +296 -0
- package/src/providers/notion/executors.js +39 -3
- package/src/providers/notion_mcp/endpoints.d.ts +3 -0
- package/src/providers/notion_mcp/endpoints.js +3 -0
- package/src/providers/notion_mcp/executors.d.ts +3 -0
- package/src/providers/notion_mcp/executors.js +176 -0
- package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
- package/src/providers/notion_mcp/mcp-response.js +314 -0
- package/src/providers/odoo/executors.d.ts +3 -0
- package/src/providers/odoo/executors.js +32 -0
- package/src/providers/odoo/runtime.d.ts +16 -0
- package/src/providers/odoo/runtime.js +140 -0
- package/src/providers/outlook/executors.js +27 -6
- package/src/providers/outlook/microsoft-graph.js +17 -6
- package/src/providers/outlook_calendar/executors.js +24 -7
- package/src/providers/patsnap_mcp/executors.js +2 -2
- package/src/providers/postiz/executors.d.ts +4 -0
- package/src/providers/postiz/executors.js +83 -0
- package/src/providers/provider-runtime.d.ts +15 -0
- package/src/providers/provider-runtime.js +57 -4
- package/src/providers/proworkflow/executors.d.ts +4 -0
- package/src/providers/proworkflow/executors.js +135 -0
- package/src/providers/registry.generated.js +12 -0
- package/src/providers/scrape_creators/catalog.js +1 -1
- package/src/providers/scrape_creators/runtime.js +1 -1
- package/src/providers/sellersprite_mcp/executors.js +2 -2
- package/src/providers/sellersprite_mcp/official-actions.js +3 -2
- package/src/providers/sentry/runtime.js +6 -7
- package/src/providers/slack/actions.js +52 -3
- package/src/providers/slack/runtime.js +58 -21
- package/src/providers/slickdeals/runtime.js +1 -1
- package/src/providers/sorftime_mcp/executors.js +2 -2
- package/src/providers/tikhub/bounded-response.d.ts +1 -1
- package/src/providers/tikhub/bounded-response.js +8 -34
- package/src/providers/tikhub/endpoint-catalog.js +1 -1
- package/src/providers/tikhub/runtime.js +2 -2
- package/src/providers/tongdaxin/executors.js +79 -15
- package/src/providers/uspto/executors.js +1 -1
- package/src/providers/waffo/executors.js +1 -1
- package/src/providers/waffo/runtime.js +1 -1
- package/src/providers/wecom_mcp/runtime.js +1 -1
- package/src/providers/wisburg/executors.d.ts +4 -0
- package/src/providers/wisburg/executors.js +13 -0
- package/src/providers/wisburg/runtime.d.ts +12 -0
- package/src/providers/wisburg/runtime.js +133 -0
- package/src/providers/xydc_mcp/executors.js +2 -2
- package/src/providers/youtube/executors.js +1 -1
- package/src/providers/youtube/runtime.js +1 -1
- package/src/providers/zerotier/executors.d.ts +9 -0
- package/src/providers/zerotier/executors.js +629 -0
- package/src/providers/zerotier/runtime.d.ts +54 -0
- package/src/providers/zerotier/runtime.js +207 -0
- package/src/server/api/openapi.js +12 -2
- package/src/server/api/runtime-api.d.ts +8 -1
- package/src/server/api/runtime-api.js +19 -1
- package/src/server/connect-server.js +10 -2
- package/src/providers/sentry/scopes.d.ts +0 -1
- package/src/providers/sentry/scopes.js +0 -7
- /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { ProtocolError, SdkHttpError, UnauthorizedError } from "@modelcontextprotocol/client";
|
|
2
|
+
import { compactObject, looseArray, optionalBoolean, optionalInteger, optionalRawString, optionalRecord, optionalString, pickOptionalString, } from "../../core/cast.js";
|
|
3
|
+
import { withMcpClient } from "../mcp-client.js";
|
|
4
|
+
import { providerResponseError, providerUserAgent, ProviderRequestError, requiredInputString, runProviderRequest, } from "../provider-runtime.js";
|
|
5
|
+
import { linearMcpEndpoint } from "./endpoints.js";
|
|
6
|
+
const service = "linear_mcp";
|
|
7
|
+
/** The Linear MCP tool behind each action. Sign-in requires every one of them (see the validator). */
|
|
8
|
+
export const linearMcpToolsByAction = {
|
|
9
|
+
get_self: "get_user",
|
|
10
|
+
list_issues: "list_issues",
|
|
11
|
+
get_issue: "get_issue",
|
|
12
|
+
list_comments: "list_comments",
|
|
13
|
+
};
|
|
14
|
+
const humanIdentifier = /^[A-Z][A-Z0-9]*-\d+$/;
|
|
15
|
+
const uuidIdentifier = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16
|
+
const truncationMarker = /\(truncated\b/i;
|
|
17
|
+
const maxToolListPages = 20;
|
|
18
|
+
export const linearMcpActionHandlers = {
|
|
19
|
+
async get_self(_input, context) {
|
|
20
|
+
const result = await callLinearMcpTool(context, "execute", linearMcpToolsByAction.get_self, { query: "me" });
|
|
21
|
+
return { user: parseLinearMcpUser(result.payload) ?? null, raw: result.text };
|
|
22
|
+
},
|
|
23
|
+
async list_issues(input, context) {
|
|
24
|
+
const result = await callLinearMcpTool(context, "execute", linearMcpToolsByAction.list_issues, compactObject({
|
|
25
|
+
updatedAt: optionalString(input.updatedAt),
|
|
26
|
+
orderBy: optionalString(input.orderBy),
|
|
27
|
+
limit: optionalInteger(input.limit),
|
|
28
|
+
cursor: optionalString(input.cursor),
|
|
29
|
+
}));
|
|
30
|
+
return { ...parseLinearMcpIssuePage(result.payload), raw: result.text };
|
|
31
|
+
},
|
|
32
|
+
async get_issue(input, context) {
|
|
33
|
+
const id = requiredInputString(input.id, "id");
|
|
34
|
+
const result = await callLinearMcpTool(context, "execute", linearMcpToolsByAction.get_issue, { id });
|
|
35
|
+
return { issue: parseLinearMcpIssue(result.payload) ?? null, raw: result.text };
|
|
36
|
+
},
|
|
37
|
+
async list_comments(input, context) {
|
|
38
|
+
const issueId = requiredInputString(input.issueId, "issueId");
|
|
39
|
+
const result = await callLinearMcpTool(context, "execute", linearMcpToolsByAction.list_comments, { issueId });
|
|
40
|
+
return { comments: parseLinearMcpComments(result.payload), raw: result.text };
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Validate the OAuth grant against the MCP server: every tool the actions map
|
|
45
|
+
* must be listed (a rename shows up here, at sign-in, never mid-poll), then the
|
|
46
|
+
* self tool supplies the profile with the same viewer ID the GraphQL API reports.
|
|
47
|
+
*/
|
|
48
|
+
export async function validateLinearMcpCredential(credential, options) {
|
|
49
|
+
const context = { accessToken: credential.accessToken, fetcher: options.fetcher, signal: options.signal };
|
|
50
|
+
return withLinearMcpClient(context, "validate", async (client, signal) => {
|
|
51
|
+
const available = await listLinearMcpToolNames(client, signal);
|
|
52
|
+
const missing = Object.values(linearMcpToolsByAction).filter((name) => !available.has(name));
|
|
53
|
+
if (missing.length > 0) {
|
|
54
|
+
throw new ProviderRequestError(502, `Linear MCP did not expose the ${missing.join(", ")} tool${missing.length > 1 ? "s" : ""} this connector needs; Linear may have renamed ${missing.length > 1 ? "them" : "it"}.`);
|
|
55
|
+
}
|
|
56
|
+
const result = readLinearMcpToolResult(linearMcpToolsByAction.get_self, await client.callTool({ name: linearMcpToolsByAction.get_self, arguments: { query: "me" } }, { signal }));
|
|
57
|
+
const user = parseLinearMcpUser(result.payload);
|
|
58
|
+
if (!user)
|
|
59
|
+
throw providerResponseError("Linear MCP did not return the signed-in user.");
|
|
60
|
+
return {
|
|
61
|
+
profile: { accountId: user.id, displayName: user.displayName ?? user.name ?? user.email ?? user.id },
|
|
62
|
+
grantedScopes: optionalString(credential.metadata.scope)?.split(" ") ?? [],
|
|
63
|
+
metadata: { mcpEndpoint: linearMcpEndpoint, discoveredToolCount: available.size },
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function listLinearMcpToolNames(client, signal) {
|
|
68
|
+
const names = new Set();
|
|
69
|
+
let cursor;
|
|
70
|
+
for (let page = 0; page < maxToolListPages; page += 1) {
|
|
71
|
+
const listed = await client.listTools(cursor ? { cursor } : {}, { signal });
|
|
72
|
+
for (const tool of listed.tools)
|
|
73
|
+
names.add(tool.name);
|
|
74
|
+
cursor = optionalString(listed.nextCursor);
|
|
75
|
+
if (!cursor)
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
return names;
|
|
79
|
+
}
|
|
80
|
+
/** Call one tool in a fresh MCP session and return its text and parsed payload. */
|
|
81
|
+
function callLinearMcpTool(context, phase, name, args) {
|
|
82
|
+
return withLinearMcpClient(context, phase, async (client, signal) => readLinearMcpToolResult(name, await client.callTool({ name, arguments: args }, { signal })));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Join the text blocks as `text` and read the JSON they carry as `payload`
|
|
86
|
+
* (structured content wins when the server sends it). Text that is not JSON
|
|
87
|
+
* is not an error: the typed fields stay empty and `raw` still reaches the caller.
|
|
88
|
+
*/
|
|
89
|
+
function readLinearMcpToolResult(name, result) {
|
|
90
|
+
if ("toolResult" in result) {
|
|
91
|
+
return { text: JSON.stringify(result.toolResult), payload: result.toolResult };
|
|
92
|
+
}
|
|
93
|
+
if (result.isError)
|
|
94
|
+
throw new ProviderRequestError(502, `Linear MCP tool ${name} failed.`, result);
|
|
95
|
+
const text = looseArray(result.content)
|
|
96
|
+
.map((block) => optionalRecord(block))
|
|
97
|
+
.filter((block) => block?.type === "text")
|
|
98
|
+
.map((block) => optionalRawString(block?.text) ?? "")
|
|
99
|
+
.join("\n");
|
|
100
|
+
return { text, payload: optionalRecord(result.structuredContent) ?? parseJsonText(text) };
|
|
101
|
+
}
|
|
102
|
+
function parseJsonText(text) {
|
|
103
|
+
const trimmed = text.trim();
|
|
104
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("["))
|
|
105
|
+
return undefined;
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(trimmed);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function parseLinearMcpUser(payload) {
|
|
114
|
+
const record = optionalRecord(payload);
|
|
115
|
+
const user = record && (optionalRecord(record.user) ?? optionalRecord(record.viewer) ?? record);
|
|
116
|
+
const id = user && pickOptionalString(user, "id", "uuid");
|
|
117
|
+
if (!user || !id)
|
|
118
|
+
return undefined;
|
|
119
|
+
return {
|
|
120
|
+
id,
|
|
121
|
+
email: pickOptionalString(user, "email"),
|
|
122
|
+
displayName: pickOptionalString(user, "displayName", "display_name"),
|
|
123
|
+
name: pickOptionalString(user, "name"),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function parseLinearMcpIssuePage(payload) {
|
|
127
|
+
const record = optionalRecord(payload);
|
|
128
|
+
const items = Array.isArray(payload) ? payload : looseArray(record?.issues ?? record?.nodes ?? record?.results);
|
|
129
|
+
const issues = items.map(parseLinearMcpIssue).filter((issue) => issue !== undefined);
|
|
130
|
+
const cursor = record ? (pickOptionalString(record, "cursor", "nextCursor", "endCursor") ?? null) : null;
|
|
131
|
+
const hasNextPage = (record && (optionalBoolean(record.hasNextPage) ?? optionalBoolean(record.hasMore))) ?? cursor !== null;
|
|
132
|
+
return { issues, hasNextPage, cursor };
|
|
133
|
+
}
|
|
134
|
+
function parseLinearMcpIssue(value) {
|
|
135
|
+
const record = optionalRecord(value);
|
|
136
|
+
const issue = record && (optionalRecord(record.issue) ?? record);
|
|
137
|
+
const id = issue && pickOptionalString(issue, "id", "identifier", "uuid");
|
|
138
|
+
if (!issue || !id)
|
|
139
|
+
return undefined;
|
|
140
|
+
const description = optionalRawString(issue.description);
|
|
141
|
+
const assignee = optionalRecord(issue.assignee);
|
|
142
|
+
const creator = optionalRecord(issue.creator);
|
|
143
|
+
return {
|
|
144
|
+
id,
|
|
145
|
+
identifier: pickOptionalString(issue, "identifier") ?? (humanIdentifier.test(id) ? id : undefined),
|
|
146
|
+
uuid: pickOptionalString(issue, "uuid") ?? (uuidIdentifier.test(id) ? id : undefined),
|
|
147
|
+
title: optionalRawString(issue.title),
|
|
148
|
+
description,
|
|
149
|
+
descriptionTruncated: description === undefined ? undefined : truncationMarker.test(description),
|
|
150
|
+
url: pickOptionalString(issue, "url"),
|
|
151
|
+
state: labelOf(issue.state ?? issue.status),
|
|
152
|
+
priority: issue.priority,
|
|
153
|
+
assignee: labelOf(issue.assignee),
|
|
154
|
+
assigneeId: assignee && pickOptionalString(assignee, "id"),
|
|
155
|
+
creator: labelOf(issue.creator),
|
|
156
|
+
creatorId: creator && pickOptionalString(creator, "id"),
|
|
157
|
+
team: labelOf(issue.team),
|
|
158
|
+
project: labelOf(issue.project),
|
|
159
|
+
labels: parseLabels(issue.labels),
|
|
160
|
+
createdAt: pickOptionalString(issue, "createdAt", "created_at"),
|
|
161
|
+
updatedAt: pickOptionalString(issue, "updatedAt", "updated_at"),
|
|
162
|
+
archivedAt: pickOptionalString(issue, "archivedAt", "archived_at"),
|
|
163
|
+
completedAt: pickOptionalString(issue, "completedAt", "completed_at"),
|
|
164
|
+
dueDate: pickOptionalString(issue, "dueDate", "due_date"),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function parseLinearMcpComments(payload) {
|
|
168
|
+
const record = optionalRecord(payload);
|
|
169
|
+
const items = Array.isArray(payload) ? payload : looseArray(record?.comments ?? record?.nodes);
|
|
170
|
+
return items.flatMap((item) => {
|
|
171
|
+
const comment = optionalRecord(item);
|
|
172
|
+
const id = comment && pickOptionalString(comment, "id");
|
|
173
|
+
if (!comment || !id)
|
|
174
|
+
return [];
|
|
175
|
+
const user = optionalRecord(comment.user);
|
|
176
|
+
return [
|
|
177
|
+
{
|
|
178
|
+
id,
|
|
179
|
+
body: optionalRawString(comment.body),
|
|
180
|
+
user: labelOf(comment.user),
|
|
181
|
+
userId: user && pickOptionalString(user, "id"),
|
|
182
|
+
createdAt: pickOptionalString(comment, "createdAt", "created_at"),
|
|
183
|
+
updatedAt: pickOptionalString(comment, "updatedAt", "updated_at"),
|
|
184
|
+
url: pickOptionalString(comment, "url"),
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/** A name for a value Linear may return as a string or as an object such as `{ id, name }`. */
|
|
190
|
+
function labelOf(value) {
|
|
191
|
+
const record = optionalRecord(value);
|
|
192
|
+
if (record)
|
|
193
|
+
return pickOptionalString(record, "name", "displayName", "title", "key", "id");
|
|
194
|
+
return optionalString(value);
|
|
195
|
+
}
|
|
196
|
+
function parseLabels(value) {
|
|
197
|
+
if (!Array.isArray(value))
|
|
198
|
+
return undefined;
|
|
199
|
+
return value.flatMap((label) => {
|
|
200
|
+
const name = labelOf(label);
|
|
201
|
+
return name ? [name] : [];
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function withLinearMcpClient(context, phase, run) {
|
|
205
|
+
return runProviderRequest({ signal: context.signal, label: "Linear MCP" }, (signal) => withMcpClient({
|
|
206
|
+
endpoint: new URL(linearMcpEndpoint),
|
|
207
|
+
transport: "streamable_http",
|
|
208
|
+
fetcher: context.fetcher,
|
|
209
|
+
headers: { authorization: `Bearer ${context.accessToken}`, "user-agent": providerUserAgent },
|
|
210
|
+
redirect: "manual",
|
|
211
|
+
signal,
|
|
212
|
+
mapError: (error) => mapLinearMcpError(error, phase),
|
|
213
|
+
}, (client) => run(client, signal)));
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* A rejected token is a bad credential (400) while validating and an expired
|
|
217
|
+
* authorization (401) while executing; other SDK failures keep a 4xx status or
|
|
218
|
+
* become 502. Aborts and unknown errors pass through for the request wrapper.
|
|
219
|
+
*/
|
|
220
|
+
function mapLinearMcpError(error, phase) {
|
|
221
|
+
if (error instanceof ProviderRequestError)
|
|
222
|
+
return error;
|
|
223
|
+
if (error instanceof UnauthorizedError)
|
|
224
|
+
return credentialError(phase);
|
|
225
|
+
if (error instanceof SdkHttpError) {
|
|
226
|
+
const status = error.status;
|
|
227
|
+
if (status === 401 || status === 403)
|
|
228
|
+
return credentialError(phase);
|
|
229
|
+
return new ProviderRequestError(400 <= status && status < 500 ? status : 502, `Linear MCP request failed: ${error.message}`, error);
|
|
230
|
+
}
|
|
231
|
+
if (error instanceof ProtocolError) {
|
|
232
|
+
return new ProviderRequestError(502, `Linear MCP request failed: ${error.message}`, error);
|
|
233
|
+
}
|
|
234
|
+
return error;
|
|
235
|
+
}
|
|
236
|
+
function credentialError(phase) {
|
|
237
|
+
return new ProviderRequestError(phase === "validate" ? 400 : 401, "Linear MCP credential is invalid or expired; reconnect Linear.");
|
|
238
|
+
}
|
|
@@ -174,7 +174,7 @@ async function withLingxingMcpClient(context, run) {
|
|
|
174
174
|
transport: "streamable_http",
|
|
175
175
|
fetcher: createLingxingRateLimitedFetch(context.fetcher, hashLingxingRateLimitKey(context)),
|
|
176
176
|
headers,
|
|
177
|
-
redirect: "
|
|
177
|
+
redirect: "manual",
|
|
178
178
|
signal: context.signal,
|
|
179
179
|
mapError: mapLingxingMcpError,
|
|
180
180
|
}, run);
|
|
@@ -65,7 +65,7 @@ async function withMcdonaldsCnMcpClient(context, run) {
|
|
|
65
65
|
transport: "streamable_http",
|
|
66
66
|
fetcher: context.fetcher,
|
|
67
67
|
headers,
|
|
68
|
-
redirect: "
|
|
68
|
+
redirect: "manual",
|
|
69
69
|
signal: context.signal,
|
|
70
70
|
protocolVersion: "legacy",
|
|
71
71
|
mapError: mapMcdonaldsCnMcpError,
|
|
@@ -12,7 +12,7 @@ export async function runMetabaseMcp(context, run) {
|
|
|
12
12
|
endpoint: new URL(`${context.apiBaseUrl}/metabase-mcp`),
|
|
13
13
|
transport: "streamable_http",
|
|
14
14
|
headers: { "x-api-key": context.apiKey, "user-agent": providerUserAgent },
|
|
15
|
-
redirect: "
|
|
15
|
+
redirect: "manual",
|
|
16
16
|
fetcher: context.fetcher,
|
|
17
17
|
signal,
|
|
18
18
|
mapError(error) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createProviderFetch, defineApiKeyProviderExecutors } from "../provider-runtime.js";
|
|
2
|
+
import { moneyforwardService } from "./manifest.js";
|
|
3
|
+
import { moneyforwardMcpActionHandlers, validateMoneyforwardMcpCredential } from "./runtime.js";
|
|
4
|
+
// No proxy on purpose: a raw proxy could send any tools/call to Money Forward and bypass the
|
|
5
|
+
// office check, schema check and write-outcome handling the actions enforce.
|
|
6
|
+
export const executors = defineApiKeyProviderExecutors(moneyforwardService, moneyforwardMcpActionHandlers);
|
|
7
|
+
export const credentialValidators = {
|
|
8
|
+
apiKey(input, { fetcher, signal }) {
|
|
9
|
+
return validateMoneyforwardMcpCredential(input.apiKey, createProviderFetch({ fetch: fetcher }), signal);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const moneyforwardService = "moneyforward_mcp";
|
|
2
|
+
export declare const moneyforwardMcpEndpoint = "https://beta.mcp.developers.biz.moneyforward.com/mcp/ca/v3";
|
|
3
|
+
export declare const officeCodePattern = "^[0-9]{4}-[0-9]{4}$";
|
|
4
|
+
export declare const currentOfficeToolName = "mfc_ca_currentOffice";
|
|
5
|
+
/** One Money Forward MCP tool and the action that wraps it. */
|
|
6
|
+
export interface MoneyforwardToolEntry {
|
|
7
|
+
actionName: string;
|
|
8
|
+
toolName: string;
|
|
9
|
+
isWrite: boolean;
|
|
10
|
+
requiresOfficeCode: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* SHA-256 of the RFC 8785 canonical form of the tool's input schema as the server published it
|
|
13
|
+
* when this provider was written. Only the hash is kept; a write is refused when the live schema
|
|
14
|
+
* hashes differently, so any server-side change is reviewed before writes resume.
|
|
15
|
+
*/
|
|
16
|
+
schemaDigest: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const moneyforwardTools: readonly MoneyforwardToolEntry[];
|
|
19
|
+
/**
|
|
20
|
+
* SHA-256 of the RFC 8785 canonical form of the whole schema. Nothing is stripped: every stripping
|
|
21
|
+
* rule considered had a counterexample (fields named `description`, data inside `const`/`enum`),
|
|
22
|
+
* so a wording change on the server also blocks writes until reviewed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function schemaDigest(schema: unknown): string;
|
|
25
|
+
/** RFC 8785 JSON Canonicalization for the JSON values a tool schema can hold. */
|
|
26
|
+
export declare function canonicalJson(value: unknown): string;
|
|
27
|
+
/**
|
|
28
|
+
* Normalize an office name for the write guard: NFKC, drop all whitespace, and expand the explicit
|
|
29
|
+
* legal-form abbreviations. No fuzzy matching: a name that differs otherwise is refused.
|
|
30
|
+
*/
|
|
31
|
+
export declare function canonicalOfficeName(name: string): string;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { sha256Hex } from "../../core/aws-sigv4.js";
|
|
2
|
+
export const moneyforwardService = "moneyforward_mcp";
|
|
3
|
+
export const moneyforwardMcpEndpoint = "https://beta.mcp.developers.biz.moneyforward.com/mcp/ca/v3";
|
|
4
|
+
export const officeCodePattern = "^[0-9]{4}-[0-9]{4}$";
|
|
5
|
+
export const currentOfficeToolName = "mfc_ca_currentOffice";
|
|
6
|
+
export const moneyforwardTools = [
|
|
7
|
+
{
|
|
8
|
+
actionName: "current_office",
|
|
9
|
+
toolName: "mfc_ca_currentOffice",
|
|
10
|
+
isWrite: false,
|
|
11
|
+
requiresOfficeCode: true,
|
|
12
|
+
schemaDigest: "2bbfa71baa94a486be83265529c2f9a39071b5b0518ff5fea1c4c3f2488029be",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
actionName: "en_ja_dictionary",
|
|
16
|
+
toolName: "mfc_ca_en_ja_dictionary",
|
|
17
|
+
isWrite: false,
|
|
18
|
+
requiresOfficeCode: false,
|
|
19
|
+
schemaDigest: "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
actionName: "get_accessible_offices",
|
|
23
|
+
toolName: "mfc_ca_getAccessibleOffices",
|
|
24
|
+
isWrite: false,
|
|
25
|
+
requiresOfficeCode: false,
|
|
26
|
+
schemaDigest: "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
actionName: "get_accounts",
|
|
30
|
+
toolName: "mfc_ca_getAccounts",
|
|
31
|
+
isWrite: false,
|
|
32
|
+
requiresOfficeCode: true,
|
|
33
|
+
schemaDigest: "66ffd56141dc57fdaf68ca9be911ce5db97a7929f68e1ffe79b9cddb019fa69f",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
actionName: "get_connected_accounts",
|
|
37
|
+
toolName: "mfc_ca_getConnectedAccounts",
|
|
38
|
+
isWrite: false,
|
|
39
|
+
requiresOfficeCode: true,
|
|
40
|
+
schemaDigest: "2bbfa71baa94a486be83265529c2f9a39071b5b0518ff5fea1c4c3f2488029be",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
actionName: "get_departments",
|
|
44
|
+
toolName: "mfc_ca_getDepartments",
|
|
45
|
+
isWrite: false,
|
|
46
|
+
requiresOfficeCode: true,
|
|
47
|
+
schemaDigest: "2bbfa71baa94a486be83265529c2f9a39071b5b0518ff5fea1c4c3f2488029be",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
actionName: "get_journal_by_id",
|
|
51
|
+
toolName: "mfc_ca_getJournalById",
|
|
52
|
+
isWrite: false,
|
|
53
|
+
requiresOfficeCode: true,
|
|
54
|
+
schemaDigest: "c290ddfd2081cef2c119d8ee37ce8207e1dbda4407437870868c5c3c046e321d",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
actionName: "get_journals",
|
|
58
|
+
toolName: "mfc_ca_getJournals",
|
|
59
|
+
isWrite: false,
|
|
60
|
+
requiresOfficeCode: true,
|
|
61
|
+
schemaDigest: "9f3dbbc4efcf6e6783677ac737a293bd7d4ccea768be8d50f783a4a706b60821",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
actionName: "get_reports_transition_balance_sheet",
|
|
65
|
+
toolName: "mfc_ca_getReportsTransitionBalanceSheet",
|
|
66
|
+
isWrite: false,
|
|
67
|
+
requiresOfficeCode: true,
|
|
68
|
+
schemaDigest: "171c891292cc5f0e8c9300654a9977d3eeed0067f630076708274c6eb4b4aa5f",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
actionName: "get_reports_transition_profit_loss",
|
|
72
|
+
toolName: "mfc_ca_getReportsTransitionProfitLoss",
|
|
73
|
+
isWrite: false,
|
|
74
|
+
requiresOfficeCode: true,
|
|
75
|
+
schemaDigest: "171c891292cc5f0e8c9300654a9977d3eeed0067f630076708274c6eb4b4aa5f",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
actionName: "get_reports_trial_balance_balance_sheet",
|
|
79
|
+
toolName: "mfc_ca_getReportsTrialBalanceBalanceSheet",
|
|
80
|
+
isWrite: false,
|
|
81
|
+
requiresOfficeCode: true,
|
|
82
|
+
schemaDigest: "23e330f0fac53ef01afbbc34f04c8017f6974859bde7d28c0b941f6c177fdda9",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
actionName: "get_reports_trial_balance_profit_loss",
|
|
86
|
+
toolName: "mfc_ca_getReportsTrialBalanceProfitLoss",
|
|
87
|
+
isWrite: false,
|
|
88
|
+
requiresOfficeCode: true,
|
|
89
|
+
schemaDigest: "23e330f0fac53ef01afbbc34f04c8017f6974859bde7d28c0b941f6c177fdda9",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
actionName: "get_sub_accounts",
|
|
93
|
+
toolName: "mfc_ca_getSubAccounts",
|
|
94
|
+
isWrite: false,
|
|
95
|
+
requiresOfficeCode: true,
|
|
96
|
+
schemaDigest: "6f55eb15f3ad3d127bbc272cc4a93ebcad9834cc871539df38809a4dc73bf3b2",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
actionName: "get_taxes",
|
|
100
|
+
toolName: "mfc_ca_getTaxes",
|
|
101
|
+
isWrite: false,
|
|
102
|
+
requiresOfficeCode: true,
|
|
103
|
+
schemaDigest: "66ffd56141dc57fdaf68ca9be911ce5db97a7929f68e1ffe79b9cddb019fa69f",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
actionName: "get_term_settings",
|
|
107
|
+
toolName: "mfc_ca_getTermSettings",
|
|
108
|
+
isWrite: false,
|
|
109
|
+
requiresOfficeCode: true,
|
|
110
|
+
schemaDigest: "2bbfa71baa94a486be83265529c2f9a39071b5b0518ff5fea1c4c3f2488029be",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
actionName: "get_trade_partners",
|
|
114
|
+
toolName: "mfc_ca_getTradePartners",
|
|
115
|
+
isWrite: false,
|
|
116
|
+
requiresOfficeCode: true,
|
|
117
|
+
schemaDigest: "66ffd56141dc57fdaf68ca9be911ce5db97a7929f68e1ffe79b9cddb019fa69f",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
actionName: "get_transactions",
|
|
121
|
+
toolName: "mfc_ca_getTransactions",
|
|
122
|
+
isWrite: false,
|
|
123
|
+
requiresOfficeCode: true,
|
|
124
|
+
schemaDigest: "84ee966ac02851949650f79293e46b2355fe907305f1ed1a4376fa42ad4808c5",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
actionName: "post_journals",
|
|
128
|
+
toolName: "mfc_ca_postJournals",
|
|
129
|
+
isWrite: true,
|
|
130
|
+
requiresOfficeCode: true,
|
|
131
|
+
schemaDigest: "1fefc534d2dc71eba534851b611783618d9587c2dac33110480df2175326cf36",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
actionName: "post_trade_partners",
|
|
135
|
+
toolName: "mfc_ca_postTradePartners",
|
|
136
|
+
isWrite: true,
|
|
137
|
+
requiresOfficeCode: true,
|
|
138
|
+
schemaDigest: "1f9466a7f72dd6dc7c84c657e533165462f899cc0dd1050c8a061729b3a3fcec",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
actionName: "post_transaction_journalize",
|
|
142
|
+
toolName: "mfc_ca_postTransactionJournalize",
|
|
143
|
+
isWrite: true,
|
|
144
|
+
requiresOfficeCode: true,
|
|
145
|
+
schemaDigest: "139d06e6030ec46fee2eaf3f59a9f8f9e0b3e543f490b8469e9b8018454ff3b0",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
actionName: "post_transactions",
|
|
149
|
+
toolName: "mfc_ca_postTransactions",
|
|
150
|
+
isWrite: true,
|
|
151
|
+
requiresOfficeCode: true,
|
|
152
|
+
schemaDigest: "0fd32a246c8478db7cb475fa2bb66d01ea076d8fa484c174ca3ebc311805bb48",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
actionName: "put_journals",
|
|
156
|
+
toolName: "mfc_ca_putJournals",
|
|
157
|
+
isWrite: true,
|
|
158
|
+
requiresOfficeCode: true,
|
|
159
|
+
schemaDigest: "cb4219b165eac7d685122a203d501d9c50223b1c522f2549ae3a98d0ff7c369f",
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
/**
|
|
163
|
+
* SHA-256 of the RFC 8785 canonical form of the whole schema. Nothing is stripped: every stripping
|
|
164
|
+
* rule considered had a counterexample (fields named `description`, data inside `const`/`enum`),
|
|
165
|
+
* so a wording change on the server also blocks writes until reviewed.
|
|
166
|
+
*/
|
|
167
|
+
export function schemaDigest(schema) {
|
|
168
|
+
return sha256Hex(canonicalJson(schema));
|
|
169
|
+
}
|
|
170
|
+
/** RFC 8785 JSON Canonicalization for the JSON values a tool schema can hold. */
|
|
171
|
+
export function canonicalJson(value) {
|
|
172
|
+
if (Array.isArray(value))
|
|
173
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
174
|
+
if (value !== null && typeof value === "object") {
|
|
175
|
+
const record = value;
|
|
176
|
+
const members = Object.keys(record)
|
|
177
|
+
.filter((key) => record[key] !== undefined)
|
|
178
|
+
.sort(compareUtf16)
|
|
179
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`);
|
|
180
|
+
return `{${members.join(",")}}`;
|
|
181
|
+
}
|
|
182
|
+
return JSON.stringify(value);
|
|
183
|
+
}
|
|
184
|
+
// Legal-form abbreviations Money Forward and users write interchangeably. NFKC has already
|
|
185
|
+
// folded full-width brackets and the enclosed forms into these half-width spellings.
|
|
186
|
+
const legalFormAbbreviations = [
|
|
187
|
+
["(株)", "株式会社"],
|
|
188
|
+
["(有)", "有限会社"],
|
|
189
|
+
["(同)", "合同会社"],
|
|
190
|
+
];
|
|
191
|
+
/**
|
|
192
|
+
* Normalize an office name for the write guard: NFKC, drop all whitespace, and expand the explicit
|
|
193
|
+
* legal-form abbreviations. No fuzzy matching: a name that differs otherwise is refused.
|
|
194
|
+
*/
|
|
195
|
+
export function canonicalOfficeName(name) {
|
|
196
|
+
const folded = name.normalize("NFKC").replace(/\s/gu, "");
|
|
197
|
+
return legalFormAbbreviations.reduce((text, [short, full]) => text.split(short).join(full), folded);
|
|
198
|
+
}
|
|
199
|
+
// RFC 8785 orders member names by UTF-16 code units, which is what `<` on JS strings compares.
|
|
200
|
+
function compareUtf16(left, right) {
|
|
201
|
+
if (left === right)
|
|
202
|
+
return 0;
|
|
203
|
+
return left < right ? -1 : 1;
|
|
204
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CredentialValidationResult } from "../../core/types.ts";
|
|
2
|
+
import type { ApiKeyProviderContext, ProviderFetch, ProviderRuntimeHandler } from "../provider-runtime.ts";
|
|
3
|
+
export interface MoneyforwardRuntimeOptions {
|
|
4
|
+
/** One budget for the whole action: office check, tools/list and the tool call share it. */
|
|
5
|
+
deadlineMs: number;
|
|
6
|
+
/** Extra wait past the deadline before giving up on an SDK cleanup that never settles. */
|
|
7
|
+
closeGraceMs: number;
|
|
8
|
+
/** Write actions allowed per key per rolling minute, so a looping agent cannot flood the ledger. */
|
|
9
|
+
writesPerMinute?: number;
|
|
10
|
+
/** Expected input-schema digests by tool name, replacing the manifest's (tests use synthetic schemas). */
|
|
11
|
+
schemaDigests?: Readonly<Record<string, string>>;
|
|
12
|
+
}
|
|
13
|
+
type Handler = ProviderRuntimeHandler<ApiKeyProviderContext>;
|
|
14
|
+
export declare function createMoneyforwardMcpHandlers(timing?: MoneyforwardRuntimeOptions): Record<string, Handler>;
|
|
15
|
+
export declare const moneyforwardMcpActionHandlers: Record<string, Handler>;
|
|
16
|
+
/** Accept the key when the server exposes every captured tool; report schema drift without refusing. */
|
|
17
|
+
export declare function validateMoneyforwardMcpCredential(apiKey: string, fetcher: ProviderFetch, signal?: AbortSignal, schemaDigests?: Readonly<Record<string, string>>): Promise<CredentialValidationResult>;
|
|
18
|
+
export {};
|