@lingochunk/mcp 0.3.0 → 0.8.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/README.md +243 -22
- package/dist/client.d.ts +192 -0
- package/dist/client.js +139 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +23 -0
- package/dist/config.js +41 -14
- package/dist/config.js.map +1 -1
- package/dist/generated/guides.d.ts +13 -0
- package/dist/generated/guides.js +50 -0
- package/dist/generated/guides.js.map +1 -0
- package/dist/http.d.ts +22 -0
- package/dist/http.js +174 -0
- package/dist/http.js.map +1 -0
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts +13 -0
- package/dist/prompts.js +26 -0
- package/dist/prompts.js.map +1 -0
- package/dist/tools.d.ts +7 -1
- package/dist/tools.js +652 -51
- package/dist/tools.js.map +1 -1
- package/package.json +9 -5
package/dist/http.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createServer, } from "node:http";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { LingoChunkClient } from "./client.js";
|
|
5
|
+
import { registerTools } from "./tools.js";
|
|
6
|
+
import { registerPrompts } from "./prompts.js";
|
|
7
|
+
// Remote MCP clients (claude.ai, ChatGPT, Le Chat, ...) call server-to-server,
|
|
8
|
+
// but browser-based clients (the MCP inspector, web IDEs) preflight with CORS.
|
|
9
|
+
// The wildcard origin is safe: authentication is a per-request Bearer token,
|
|
10
|
+
// never a cookie, so no ambient credentials can be replayed cross-origin.
|
|
11
|
+
const CORS_HEADERS = {
|
|
12
|
+
"Access-Control-Allow-Origin": "*",
|
|
13
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
14
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",
|
|
15
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id, Mcp-Protocol-Version",
|
|
16
|
+
};
|
|
17
|
+
function applyCors(res) {
|
|
18
|
+
for (const [name, value] of Object.entries(CORS_HEADERS)) {
|
|
19
|
+
res.setHeader(name, value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function sendJson(res, status, body, headers = {}) {
|
|
23
|
+
res.writeHead(status, { "Content-Type": "application/json", ...headers });
|
|
24
|
+
res.end(JSON.stringify(body));
|
|
25
|
+
}
|
|
26
|
+
/** Extract the Bearer credential, or null when absent/malformed. */
|
|
27
|
+
function bearerToken(req) {
|
|
28
|
+
const header = req.headers.authorization;
|
|
29
|
+
if (!header)
|
|
30
|
+
return null;
|
|
31
|
+
const match = /^Bearer\s+(.+)$/i.exec(header);
|
|
32
|
+
const token = match?.[1]?.trim();
|
|
33
|
+
return token ? token : null;
|
|
34
|
+
}
|
|
35
|
+
function unauthorized(res, publicOrigin) {
|
|
36
|
+
// JSON-RPC error shape so MCP clients surface the message; -32001 is the
|
|
37
|
+
// de-facto "auth required" code used across MCP servers. resource_metadata
|
|
38
|
+
// (RFC 9728) is what OAuth-capable clients (claude.ai, ChatGPT, Gemini)
|
|
39
|
+
// follow to discover the sign-in flow served by the main app.
|
|
40
|
+
sendJson(res, 401, {
|
|
41
|
+
jsonrpc: "2.0",
|
|
42
|
+
error: {
|
|
43
|
+
code: -32001,
|
|
44
|
+
message: "Authentication required. Sign in via OAuth (your client offers " +
|
|
45
|
+
"this automatically), or create a personal access token in your " +
|
|
46
|
+
"LingoChunk account settings (Settings -> API tokens, it starts " +
|
|
47
|
+
"with 'lcp_') and send it as 'Authorization: Bearer <token>'. " +
|
|
48
|
+
"For clients without a token field, put it in the URL instead: " +
|
|
49
|
+
"/mcp/t/<token>.",
|
|
50
|
+
},
|
|
51
|
+
id: null,
|
|
52
|
+
}, {
|
|
53
|
+
"WWW-Authenticate": 'Bearer realm="LingoChunk", error="invalid_token", ' +
|
|
54
|
+
`resource_metadata="${publicOrigin}/.well-known/oauth-protected-resource/mcp"`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Serve the MCP server over Streamable HTTP for remote clients.
|
|
59
|
+
*
|
|
60
|
+
* Stateless by design: every POST builds a fresh McpServer + transport bound
|
|
61
|
+
* to the caller's own Bearer token, so one process serves many users with no
|
|
62
|
+
* session affinity and no token ever outliving its request. The token is not
|
|
63
|
+
* validated here - it is forwarded verbatim and the LingoChunk API stays the
|
|
64
|
+
* single authority (a bad token fails at the first tool call with the API's
|
|
65
|
+
* own 401 message).
|
|
66
|
+
*
|
|
67
|
+
* Endpoints: POST /mcp (the MCP wire), GET /health (liveness). GET/DELETE
|
|
68
|
+
* /mcp return 405: stateless mode has no server-push stream and no session
|
|
69
|
+
* to delete.
|
|
70
|
+
*/
|
|
71
|
+
export function startHttpServer(options) {
|
|
72
|
+
const httpServer = createServer((req, res) => {
|
|
73
|
+
void handle(req, res, options).catch((err) => {
|
|
74
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75
|
+
process.stderr.write(`lingochunk-mcp http error: ${message}\n`);
|
|
76
|
+
if (!res.headersSent) {
|
|
77
|
+
sendJson(res, 500, {
|
|
78
|
+
jsonrpc: "2.0",
|
|
79
|
+
error: { code: -32603, message: "Internal server error." },
|
|
80
|
+
id: null,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
res.end();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
httpServer.once("error", reject);
|
|
90
|
+
httpServer.listen(options.port, () => {
|
|
91
|
+
httpServer.removeListener("error", reject);
|
|
92
|
+
resolve(httpServer);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function handle(req, res, options) {
|
|
97
|
+
applyCors(res);
|
|
98
|
+
// Serve the MCP wire on both /mcp and / : deployed behind a path-stripping
|
|
99
|
+
// proxy (kamal-proxy --path-prefix /mcp strips by default), requests to
|
|
100
|
+
// https://host/mcp arrive here as "/"; run directly (docker -p), they
|
|
101
|
+
// arrive as "/mcp". Health likewise answers /health and /mcp/health.
|
|
102
|
+
const rawPath = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
103
|
+
let path = rawPath === "/mcp" || rawPath === "/"
|
|
104
|
+
? "/"
|
|
105
|
+
: rawPath.startsWith("/mcp/")
|
|
106
|
+
? rawPath.slice("/mcp".length)
|
|
107
|
+
: rawPath;
|
|
108
|
+
// Tokened-URL auth: /t/<token> is the MCP wire with the credential in the
|
|
109
|
+
// path, for clients whose connector UI has no header/token field and no
|
|
110
|
+
// OAuth-less fallback (claude.ai custom connectors). The URL is then a
|
|
111
|
+
// secret - it lands in proxy/access logs - which is acceptable as a
|
|
112
|
+
// documented stopgap because PATs are scoped and one-click revocable.
|
|
113
|
+
// An Authorization header, when present, still wins below.
|
|
114
|
+
let pathToken = null;
|
|
115
|
+
const tokened = /^\/t\/([^/]+)$/.exec(path);
|
|
116
|
+
if (tokened?.[1]) {
|
|
117
|
+
pathToken = decodeURIComponent(tokened[1]).trim() || null;
|
|
118
|
+
path = "/";
|
|
119
|
+
}
|
|
120
|
+
if (req.method === "OPTIONS") {
|
|
121
|
+
res.writeHead(204).end();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (path === "/health" && req.method === "GET") {
|
|
125
|
+
sendJson(res, 200, {
|
|
126
|
+
status: "ok",
|
|
127
|
+
name: "lingochunk-mcp",
|
|
128
|
+
version: options.version,
|
|
129
|
+
transport: "streamable-http",
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (path !== "/") {
|
|
134
|
+
sendJson(res, 404, { detail: "Not found. The MCP endpoint is POST /mcp." });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (req.method !== "POST") {
|
|
138
|
+
sendJson(res, 405, {
|
|
139
|
+
jsonrpc: "2.0",
|
|
140
|
+
error: {
|
|
141
|
+
code: -32000,
|
|
142
|
+
message: "Method not allowed. This server is stateless: POST /mcp only.",
|
|
143
|
+
},
|
|
144
|
+
id: null,
|
|
145
|
+
}, { Allow: "POST" });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const token = bearerToken(req) ?? pathToken;
|
|
149
|
+
if (!token) {
|
|
150
|
+
unauthorized(res, options.publicOrigin);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Fresh server per request: tools close over THIS caller's client. clipDir
|
|
154
|
+
// is never used because remote mode does not register get_audio_clip.
|
|
155
|
+
const client = new LingoChunkClient({
|
|
156
|
+
baseUrl: options.baseUrl,
|
|
157
|
+
token,
|
|
158
|
+
clipDir: "",
|
|
159
|
+
});
|
|
160
|
+
const server = new McpServer({ name: "lingochunk", version: options.version });
|
|
161
|
+
registerTools(server, client, { baseUrl: options.baseUrl, token, clipDir: "" }, "remote");
|
|
162
|
+
registerPrompts(server);
|
|
163
|
+
const transport = new StreamableHTTPServerTransport({
|
|
164
|
+
sessionIdGenerator: undefined,
|
|
165
|
+
enableJsonResponse: true,
|
|
166
|
+
});
|
|
167
|
+
res.on("close", () => {
|
|
168
|
+
void transport.close();
|
|
169
|
+
void server.close();
|
|
170
|
+
});
|
|
171
|
+
await server.connect(transport);
|
|
172
|
+
await transport.handleRequest(req, res);
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { loadConfig } from "./config.js";
|
|
5
|
+
import { loadConfig, loadHttpConfig } from "./config.js";
|
|
5
6
|
import { LingoChunkClient } from "./client.js";
|
|
7
|
+
import { startHttpServer } from "./http.js";
|
|
6
8
|
import { registerTools } from "./tools.js";
|
|
7
|
-
|
|
9
|
+
import { registerPrompts } from "./prompts.js";
|
|
10
|
+
// Single source of truth for the version: the package manifest. A hardcoded
|
|
11
|
+
// copy here once drifted (the server reported 0.2.1 while npm shipped 0.3.0),
|
|
12
|
+
// which made "is my server up to date?" unanswerable.
|
|
13
|
+
const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
14
|
+
// `npx -y @lingochunk/mcp --version` prints the running version and exits,
|
|
15
|
+
// so users can compare against `npm view @lingochunk/mcp version` (README
|
|
16
|
+
// "Updating" section). Checked before any config loading: no token needed.
|
|
17
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
18
|
+
process.stdout.write(`${VERSION}\n`);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
8
21
|
async function main() {
|
|
22
|
+
// --http runs the hosted multi-user server (Streamable HTTP, per-request
|
|
23
|
+
// Bearer tokens) instead of the single-user stdio process.
|
|
24
|
+
if (process.argv.includes("--http")) {
|
|
25
|
+
const httpConfig = loadHttpConfig();
|
|
26
|
+
await startHttpServer({ ...httpConfig, version: VERSION });
|
|
27
|
+
process.stderr.write(`lingochunk-mcp ${VERSION} listening on :${httpConfig.port} ` +
|
|
28
|
+
`(POST /mcp), API ${httpConfig.baseUrl}\n`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
9
31
|
// Fail fast (to stderr, never stdout - stdout is the MCP wire) if the token
|
|
10
32
|
// is missing, so onboarding surfaces the problem immediately.
|
|
11
33
|
const config = loadConfig();
|
|
12
34
|
const client = new LingoChunkClient(config);
|
|
13
35
|
const server = new McpServer({ name: "lingochunk", version: VERSION });
|
|
14
36
|
registerTools(server, client, config);
|
|
37
|
+
registerPrompts(server);
|
|
15
38
|
const transport = new StdioServerTransport();
|
|
16
39
|
await server.connect(transport);
|
|
17
40
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,4EAA4E;AAC5E,8EAA8E;AAC9E,sDAAsD;AACtD,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,CACR,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAEpE,CAAC,OAAO,CAAC;AAEV,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,yEAAyE;IACzE,2DAA2D;IAC3D,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;QACpC,MAAM,eAAe,CAAC,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,OAAO,kBAAkB,UAAU,CAAC,IAAI,GAAG;YAC3D,oBAAoB,UAAU,CAAC,OAAO,IAAI,CAC7C,CAAC;QACF,OAAO;IACT,CAAC;IAED,4EAA4E;IAC5E,8DAA8D;IAC9D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,OAAO,IAAI,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Register one MCP prompt per authoring skill (name `lingochunk-<skill>`),
|
|
4
|
+
* each returning that skill's full markdown as a user-role message. This is
|
|
5
|
+
* how remote clients (claude.ai, ChatGPT, ...) can pull the craft guidance a
|
|
6
|
+
* slash-command / prompt picker exposes - the same content get_authoring_guide
|
|
7
|
+
* serves as a tool, but surfaced through the prompts capability.
|
|
8
|
+
*
|
|
9
|
+
* Calling registerPrompt makes the high-level McpServer advertise the prompts
|
|
10
|
+
* capability in the initialize handshake, so this must run before
|
|
11
|
+
* server.connect() in BOTH transports (stdio in index.ts, HTTP in http.ts).
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerPrompts(server: McpServer): void;
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { GUIDES, GUIDE_TOPICS } from "./generated/guides.js";
|
|
2
|
+
/**
|
|
3
|
+
* Register one MCP prompt per authoring skill (name `lingochunk-<skill>`),
|
|
4
|
+
* each returning that skill's full markdown as a user-role message. This is
|
|
5
|
+
* how remote clients (claude.ai, ChatGPT, ...) can pull the craft guidance a
|
|
6
|
+
* slash-command / prompt picker exposes - the same content get_authoring_guide
|
|
7
|
+
* serves as a tool, but surfaced through the prompts capability.
|
|
8
|
+
*
|
|
9
|
+
* Calling registerPrompt makes the high-level McpServer advertise the prompts
|
|
10
|
+
* capability in the initialize handshake, so this must run before
|
|
11
|
+
* server.connect() in BOTH transports (stdio in index.ts, HTTP in http.ts).
|
|
12
|
+
*/
|
|
13
|
+
export function registerPrompts(server) {
|
|
14
|
+
for (const topic of GUIDE_TOPICS) {
|
|
15
|
+
const guide = GUIDES[topic];
|
|
16
|
+
server.registerPrompt(guide.promptName, { title: guide.promptName, description: guide.description }, () => ({
|
|
17
|
+
messages: [
|
|
18
|
+
{
|
|
19
|
+
role: "user",
|
|
20
|
+
content: { type: "text", text: guide.body },
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,CAAC,cAAc,CACnB,KAAK,CAAC,UAAU,EAChB,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,EAC3D,GAAG,EAAE,CAAC,CAAC;YACL,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;iBAC5C;aACF;SACF,CAAC,CACH,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { type LingoChunkClient } from "./client.js";
|
|
3
3
|
import type { Config } from "./config.js";
|
|
4
|
-
|
|
4
|
+
/** Where the server runs relative to the user. "local": a stdio process on
|
|
5
|
+
* the user's machine (files it writes are the user's files). "remote": a
|
|
6
|
+
* hosted multi-user HTTP server, where writing to the local filesystem is
|
|
7
|
+
* meaningless to the caller - so get_audio_clip is not offered and sibling
|
|
8
|
+
* descriptions stop pointing at it. */
|
|
9
|
+
export type ToolMode = "local" | "remote";
|
|
10
|
+
export declare function registerTools(server: McpServer, client: LingoChunkClient, config: Config, mode?: ToolMode): void;
|