@veertu/anka-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/dist/anka.d.ts +41 -0
- package/dist/anka.js +65 -0
- package/dist/anka.js.map +1 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +43 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +69 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/controller.d.ts +73 -0
- package/dist/controller.js +125 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +107 -0
- package/dist/log.js +254 -0
- package/dist/log.js.map +1 -0
- package/dist/security/host.d.ts +2 -0
- package/dist/security/host.js +9 -0
- package/dist/security/host.js.map +1 -0
- package/dist/security/rate-limit.d.ts +18 -0
- package/dist/security/rate-limit.js +71 -0
- package/dist/security/rate-limit.js.map +1 -0
- package/dist/security/sanitize.d.ts +6 -0
- package/dist/security/sanitize.js +33 -0
- package/dist/security/sanitize.js.map +1 -0
- package/dist/security/schemas.d.ts +9 -0
- package/dist/security/schemas.js +20 -0
- package/dist/security/schemas.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +13 -0
- package/dist/server.js.map +1 -0
- package/dist/ssh-key.d.ts +23 -0
- package/dist/ssh-key.js +73 -0
- package/dist/ssh-key.js.map +1 -0
- package/dist/tokens/cleanup.d.ts +10 -0
- package/dist/tokens/cleanup.js +28 -0
- package/dist/tokens/cleanup.js.map +1 -0
- package/dist/tokens/ownership.d.ts +6 -0
- package/dist/tokens/ownership.js +31 -0
- package/dist/tokens/ownership.js.map +1 -0
- package/dist/tokens/schema.d.ts +3 -0
- package/dist/tokens/schema.js +35 -0
- package/dist/tokens/schema.js.map +1 -0
- package/dist/tokens/store.d.ts +45 -0
- package/dist/tokens/store.js +145 -0
- package/dist/tokens/store.js.map +1 -0
- package/dist/tools/controller/get-vm.d.ts +3 -0
- package/dist/tools/controller/get-vm.js +34 -0
- package/dist/tools/controller/get-vm.js.map +1 -0
- package/dist/tools/controller/index.d.ts +3 -0
- package/dist/tools/controller/index.js +12 -0
- package/dist/tools/controller/index.js.map +1 -0
- package/dist/tools/controller/list-templates.d.ts +1 -0
- package/dist/tools/controller/list-templates.js +21 -0
- package/dist/tools/controller/list-templates.js.map +1 -0
- package/dist/tools/controller/request-vm.d.ts +8 -0
- package/dist/tools/controller/request-vm.js +101 -0
- package/dist/tools/controller/request-vm.js.map +1 -0
- package/dist/tools/controller/results.d.ts +5 -0
- package/dist/tools/controller/results.js +23 -0
- package/dist/tools/controller/results.js.map +1 -0
- package/dist/tools/controller/terminate-vm.d.ts +3 -0
- package/dist/tools/controller/terminate-vm.js +32 -0
- package/dist/tools/controller/terminate-vm.js.map +1 -0
- package/dist/tools/define-tool.d.ts +34 -0
- package/dist/tools/define-tool.js +51 -0
- package/dist/tools/define-tool.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +20 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/local/delete-vm.d.ts +3 -0
- package/dist/tools/local/delete-vm.js +20 -0
- package/dist/tools/local/delete-vm.js.map +1 -0
- package/dist/tools/local/index.d.ts +3 -0
- package/dist/tools/local/index.js +14 -0
- package/dist/tools/local/index.js.map +1 -0
- package/dist/tools/local/list-templates.d.ts +1 -0
- package/dist/tools/local/list-templates.js +17 -0
- package/dist/tools/local/list-templates.js.map +1 -0
- package/dist/tools/local/show-vm.d.ts +3 -0
- package/dist/tools/local/show-vm.js +23 -0
- package/dist/tools/local/show-vm.js.map +1 -0
- package/dist/tools/local/ssh-access.d.ts +3 -0
- package/dist/tools/local/ssh-access.js +68 -0
- package/dist/tools/local/ssh-access.js.map +1 -0
- package/dist/tools/local/start-vm.d.ts +7 -0
- package/dist/tools/local/start-vm.js +52 -0
- package/dist/tools/local/start-vm.js.map +1 -0
- package/dist/tools/local/vms.d.ts +62 -0
- package/dist/tools/local/vms.js +91 -0
- package/dist/tools/local/vms.js.map +1 -0
- package/dist/transports/admin.d.ts +3 -0
- package/dist/transports/admin.js +74 -0
- package/dist/transports/admin.js.map +1 -0
- package/dist/transports/http.d.ts +14 -0
- package/dist/transports/http.js +283 -0
- package/dist/transports/http.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { config } from "../config.js";
|
|
2
|
+
import { limitActorFromContext, logLimitReached, logToolCall, logToolError } from "../log.js";
|
|
3
|
+
/**
|
|
4
|
+
* Declare an MCP tool in one place. Add the returned object to the `tools`
|
|
5
|
+
* array in `tools/index.ts` and it is wired up automatically.
|
|
6
|
+
*/
|
|
7
|
+
export function defineTool(spec) {
|
|
8
|
+
return {
|
|
9
|
+
...spec,
|
|
10
|
+
register(server) {
|
|
11
|
+
const handler = this.handler;
|
|
12
|
+
const name = this.name;
|
|
13
|
+
server.registerTool(this.name, this.config, (async (args, extra) => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await handler(args, extra);
|
|
16
|
+
logToolCall(name, args, result);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
logToolError(name, args, error);
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Serialize a value into a CallToolResult with a single JSON text block. */
|
|
28
|
+
export function jsonResult(value, isError = false) {
|
|
29
|
+
let text = JSON.stringify(value, null, 2);
|
|
30
|
+
const originalLength = text.length;
|
|
31
|
+
if (text.length > config.maxResponseChars) {
|
|
32
|
+
logLimitReached({
|
|
33
|
+
limit: "MCP_MAX_RESPONSE_CHARS",
|
|
34
|
+
configured: String(config.maxResponseChars),
|
|
35
|
+
actor: limitActorFromContext(),
|
|
36
|
+
detail: `response_chars=${originalLength}`
|
|
37
|
+
});
|
|
38
|
+
text = JSON.stringify({
|
|
39
|
+
truncated: true,
|
|
40
|
+
preview: text.slice(0, Math.max(0, config.maxResponseChars - 64))
|
|
41
|
+
}, null, 2);
|
|
42
|
+
if (text.length > config.maxResponseChars) {
|
|
43
|
+
text = `${text.slice(0, config.maxResponseChars - 1)}…`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text }],
|
|
48
|
+
isError
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=define-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-tool.js","sourceRoot":"","sources":["../../src/tools/define-tool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAuB9F;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAgC,IAIzD;IACC,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,CAAC,MAAiB;YACxB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;gBACjE,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;oBAC1C,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;oBAChC,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;oBAChC,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC,CAA4B,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,OAAO,GAAG,KAAK;IACxD,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC1C,eAAe,CAAC;YACd,KAAK,EAAE,wBAAwB;YAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;YAC3C,KAAK,EAAE,qBAAqB,EAAE;YAC9B,MAAM,EAAE,kBAAkB,cAAc,EAAE;SAC3C,CAAC,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,SAAS,CACnB;YACE,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;SAClE,EACD,IAAI,EACJ,CAAC,CACF,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC1C,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC,GAAG,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACjC,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ToolDefinition } from "./define-tool.js";
|
|
3
|
+
export { defineTool, jsonResult } from "./define-tool.js";
|
|
4
|
+
export type { ToolDefinition } from "./define-tool.js";
|
|
5
|
+
/** Build the list of tools to expose, based on which backends are enabled. */
|
|
6
|
+
export declare function enabledTools(): ToolDefinition<any>[];
|
|
7
|
+
/** Register every enabled tool on the given server. */
|
|
8
|
+
export declare function registerTools(server: McpServer): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { config } from "../config.js";
|
|
2
|
+
import { controllerTools } from "./controller/index.js";
|
|
3
|
+
import { localTools } from "./local/index.js";
|
|
4
|
+
export { defineTool, jsonResult } from "./define-tool.js";
|
|
5
|
+
/** Build the list of tools to expose, based on which backends are enabled. */
|
|
6
|
+
export function enabledTools() {
|
|
7
|
+
const tools = [];
|
|
8
|
+
if (config.controllerEnabled)
|
|
9
|
+
tools.push(...controllerTools);
|
|
10
|
+
if (config.localEnabled)
|
|
11
|
+
tools.push(...localTools);
|
|
12
|
+
return tools;
|
|
13
|
+
}
|
|
14
|
+
/** Register every enabled tool on the given server. */
|
|
15
|
+
export function registerTools(server) {
|
|
16
|
+
for (const tool of enabledTools()) {
|
|
17
|
+
tool.register(server);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG1D,8EAA8E;AAC9E,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAA0B,EAAE,CAAC;IACxC,IAAI,MAAM,CAAC,iBAAiB;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,aAAa,CAAC,MAAiB;IAC7C,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { runAnka } from "../../anka.js";
|
|
2
|
+
import { defineTool } from "../define-tool.js";
|
|
3
|
+
import { localResult, vmNameSchema } from "./vms.js";
|
|
4
|
+
export const localDeleteVmTool = defineTool({
|
|
5
|
+
name: "local_delete_vm",
|
|
6
|
+
config: {
|
|
7
|
+
title: "Delete a local VM",
|
|
8
|
+
description: "Delete a single local VM by name or uuid. A name is always required; this tool " +
|
|
9
|
+
"can only delete one specific VM and can never delete all VMs.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
name: vmNameSchema.describe("Name or uuid of the VM to delete.")
|
|
12
|
+
},
|
|
13
|
+
annotations: { title: "Delete a local VM", destructiveHint: true }
|
|
14
|
+
},
|
|
15
|
+
handler: async ({ name }) => {
|
|
16
|
+
const result = await runAnka(["delete", "--yes", name]);
|
|
17
|
+
return localResult(result, { name, deleted: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=delete-vm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-vm.js","sourceRoot":"","sources":["../../../src/tools/local/delete-vm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAErD,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;IAC1C,IAAI,EAAE,iBAAiB;IACvB,MAAM,EAAE;QACN,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,iFAAiF;YACjF,+DAA+D;QACjE,WAAW,EAAE;YACX,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,mCAAmC,CAAC;SACjE;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,IAAI,EAAE;KACnE;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QACxD,OAAO,WAAW,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { localListTemplatesTool } from "./list-templates.js";
|
|
2
|
+
import { localStartVmTool } from "./start-vm.js";
|
|
3
|
+
import { localShowVmTool } from "./show-vm.js";
|
|
4
|
+
import { localDeleteVmTool } from "./delete-vm.js";
|
|
5
|
+
import { localSshAccessTool } from "./ssh-access.js";
|
|
6
|
+
/** Tools exposed when the local anka CLI backend is enabled. */
|
|
7
|
+
export const localTools = [
|
|
8
|
+
localListTemplatesTool,
|
|
9
|
+
localStartVmTool,
|
|
10
|
+
localShowVmTool,
|
|
11
|
+
localDeleteVmTool,
|
|
12
|
+
localSshAccessTool
|
|
13
|
+
];
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/local/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,gEAAgE;AAChE,MAAM,CAAC,MAAM,UAAU,GAA0B;IAC/C,sBAAsB;IACtB,gBAAgB;IAChB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;CACnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const localListTemplatesTool: import("../define-tool.js").ToolDefinition<{}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineTool, jsonResult } from "../define-tool.js";
|
|
2
|
+
import { listVms } from "./vms.js";
|
|
3
|
+
export const localListTemplatesTool = defineTool({
|
|
4
|
+
name: "local_list_templates",
|
|
5
|
+
config: {
|
|
6
|
+
title: "List local VMs and templates",
|
|
7
|
+
description: "List the local Anka VM library (templates and clones) with their name, uuid, " +
|
|
8
|
+
"and status. Use this to find the template to clone from.",
|
|
9
|
+
inputSchema: {},
|
|
10
|
+
annotations: { title: "List local VMs and templates", readOnlyHint: true }
|
|
11
|
+
},
|
|
12
|
+
handler: async () => {
|
|
13
|
+
const vms = await listVms();
|
|
14
|
+
return jsonResult({ vms: vms.map((vm) => ({ name: vm.name, uuid: vm.uuid })) });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=list-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-templates.js","sourceRoot":"","sources":["../../../src/tools/local/list-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;IAC/C,IAAI,EAAE,sBAAsB;IAC5B,MAAM,EAAE;QACN,KAAK,EAAE,8BAA8B;QACrC,WAAW,EACT,+EAA+E;YAC/E,0DAA0D;QAC5D,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,YAAY,EAAE,IAAI,EAAE;KAC3E;IACD,OAAO,EAAE,KAAK,IAAI,EAAE;QAClB,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;QAC5B,OAAO,UAAU,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClF,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { runAnka } from "../../anka.js";
|
|
2
|
+
import { defineTool, jsonResult } from "../define-tool.js";
|
|
3
|
+
import { ankaError, vmNameSchema } from "./vms.js";
|
|
4
|
+
export const localShowVmTool = defineTool({
|
|
5
|
+
name: "local_show_vm",
|
|
6
|
+
config: {
|
|
7
|
+
title: "Show a local VM",
|
|
8
|
+
description: "Get a local VM's IP address by name or uuid. The IP is only present once the VM is running.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
name: vmNameSchema.describe("Name or uuid of the VM to show.")
|
|
11
|
+
},
|
|
12
|
+
annotations: { title: "Show a local VM", readOnlyHint: true }
|
|
13
|
+
},
|
|
14
|
+
handler: async ({ name }) => {
|
|
15
|
+
const result = await runAnka(["show", name]);
|
|
16
|
+
if (!result.ok) {
|
|
17
|
+
return jsonResult({ ok: false, error: ankaError(result) }, true);
|
|
18
|
+
}
|
|
19
|
+
const body = (result.body ?? {});
|
|
20
|
+
return jsonResult({ ok: true, ip: body.ip ?? null });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=show-vm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show-vm.js","sourceRoot":"","sources":["../../../src/tools/local/show-vm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMnD,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;IACxC,IAAI,EAAE,eAAe;IACrB,MAAM,EAAE;QACN,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,6FAA6F;QAC1G,WAAW,EAAE;YACX,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,iCAAiC,CAAC;SAC/D;QACD,WAAW,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,IAAI,EAAE;KAC9D;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAiB,CAAC;QACjD,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { runAnka } from "../../anka.js";
|
|
3
|
+
import { config } from "../../config.js";
|
|
4
|
+
import { buildSshCommand, generateSshKeypair } from "../../ssh-key.js";
|
|
5
|
+
import { defineTool, jsonResult } from "../define-tool.js";
|
|
6
|
+
import { ankaError, showVm, vmNameSchema, waitForVmIp } from "./vms.js";
|
|
7
|
+
export const localSshAccessTool = defineTool({
|
|
8
|
+
name: "local_ssh_access",
|
|
9
|
+
config: {
|
|
10
|
+
title: "Prepare SSH access to a local VM",
|
|
11
|
+
description: "Provision SSH access to a running local VM: generates a temporary SSH key, " +
|
|
12
|
+
"installs the public key into the VM's authorized_keys (via anka cp + anka run), " +
|
|
13
|
+
"and returns the private key path plus a ready-to-use ssh command. The VM must be " +
|
|
14
|
+
"running and have Remote Login (sshd) enabled in its template.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
name: vmNameSchema.describe("Name or uuid of the running VM to access.")
|
|
17
|
+
},
|
|
18
|
+
annotations: { title: "Prepare SSH access to a local VM" }
|
|
19
|
+
},
|
|
20
|
+
handler: async ({ name }) => {
|
|
21
|
+
const shown = await showVm(name);
|
|
22
|
+
if (!shown.ok) {
|
|
23
|
+
return jsonResult({ ok: false, error: shown.error }, true);
|
|
24
|
+
}
|
|
25
|
+
if (shown.info.status !== "running") {
|
|
26
|
+
return jsonResult({ ok: false, error: `VM "${name}" is not running; start it first.` }, true);
|
|
27
|
+
}
|
|
28
|
+
// The VM is up but its IP may still be coming online; wait it out.
|
|
29
|
+
let ip = shown.info.ip;
|
|
30
|
+
if (!ip) {
|
|
31
|
+
const waited = await waitForVmIp(name);
|
|
32
|
+
if (!waited.found) {
|
|
33
|
+
return jsonResult({ ok: false, error: waited.error }, true);
|
|
34
|
+
}
|
|
35
|
+
ip = waited.ip;
|
|
36
|
+
}
|
|
37
|
+
const { privateKeyPath } = await generateSshKeypair();
|
|
38
|
+
// Copy the public key into the VM, then install it into authorized_keys.
|
|
39
|
+
const remotePubPath = `/tmp/anka-mcp-${randomUUID()}.pub`;
|
|
40
|
+
const copyIn = await runAnka(["cp", `${privateKeyPath}.pub`, `${name}:${remotePubPath}`], {
|
|
41
|
+
machineReadable: false
|
|
42
|
+
});
|
|
43
|
+
if (!copyIn.ok) {
|
|
44
|
+
return jsonResult({ ok: false, error: `anka cp failed: ${ankaError(copyIn)}` }, true);
|
|
45
|
+
}
|
|
46
|
+
const installScript = `set -e; mkdir -p ~/.ssh; chmod 700 ~/.ssh; ` +
|
|
47
|
+
`cat ${remotePubPath} >> ~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys; ` +
|
|
48
|
+
`rm -f ${remotePubPath}`;
|
|
49
|
+
const install = await runAnka(["run", name, "sh", "-c", installScript], {
|
|
50
|
+
machineReadable: false
|
|
51
|
+
});
|
|
52
|
+
if (!install.ok) {
|
|
53
|
+
return jsonResult({ ok: false, error: `Installing the key failed: ${ankaError(install)}` }, true);
|
|
54
|
+
}
|
|
55
|
+
const port = config.vmSshGuestPort;
|
|
56
|
+
const user = config.vmSshUser;
|
|
57
|
+
const command = buildSshCommand({ privateKeyPath, host: ip, port, user });
|
|
58
|
+
return jsonResult({
|
|
59
|
+
ok: true,
|
|
60
|
+
ip,
|
|
61
|
+
port,
|
|
62
|
+
user,
|
|
63
|
+
private_key_path: privateKeyPath,
|
|
64
|
+
command
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=ssh-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-access.js","sourceRoot":"","sources":["../../../src/tools/local/ssh-access.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { runAnka } from "../../anka.js";
|
|
4
|
+
import { timeoutSecondsSchema } from "../../security/schemas.js";
|
|
5
|
+
import { defineTool, jsonResult } from "../define-tool.js";
|
|
6
|
+
import { ankaError, assertRunningCapacity, vmNameSchema, waitForVmIp } from "./vms.js";
|
|
7
|
+
export const localStartVmTool = defineTool({
|
|
8
|
+
name: "local_start_vm",
|
|
9
|
+
config: {
|
|
10
|
+
title: "Start a local VM from a template",
|
|
11
|
+
description: "Clone the given template into a fresh, disposable VM and start it. The original " +
|
|
12
|
+
"template is never started or modified. Use local_list_templates to find a template. " +
|
|
13
|
+
"Refused if the running-VM limit (ANKA_LOCAL_MAX_VMS) is already met. By default this " +
|
|
14
|
+
"waits for the new VM to boot and obtain an IP, then returns its name and IP so you do " +
|
|
15
|
+
"not need to poll. Delete the VM with local_delete_vm when finished.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
template: vmNameSchema.describe("Name or uuid of the template to clone from."),
|
|
18
|
+
name: vmNameSchema
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Optional name for the new VM. Defaults to an auto-generated name."),
|
|
21
|
+
wait: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Wait for the VM to obtain an IP before returning. Defaults to true."),
|
|
25
|
+
timeoutSeconds: timeoutSecondsSchema
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Override how long to wait for the IP, in seconds.")
|
|
28
|
+
},
|
|
29
|
+
annotations: { title: "Start a local VM from a template" }
|
|
30
|
+
},
|
|
31
|
+
handler: async ({ template, name, wait, timeoutSeconds }) => {
|
|
32
|
+
await assertRunningCapacity("start a VM");
|
|
33
|
+
const vmName = name ?? `mcp-${randomUUID().slice(0, 8)}`;
|
|
34
|
+
const cloned = await runAnka(["clone", template, vmName]);
|
|
35
|
+
if (!cloned.ok) {
|
|
36
|
+
return jsonResult({ ok: false, error: `Clone failed: ${ankaError(cloned)}` }, true);
|
|
37
|
+
}
|
|
38
|
+
const started = await runAnka(["start", vmName]);
|
|
39
|
+
if (!started.ok) {
|
|
40
|
+
return jsonResult({ ok: false, name: vmName, error: ankaError(started) }, true);
|
|
41
|
+
}
|
|
42
|
+
if (wait === false) {
|
|
43
|
+
return jsonResult({ ok: true, name: vmName, source: template });
|
|
44
|
+
}
|
|
45
|
+
const waited = await waitForVmIp(vmName, timeoutSeconds ? { timeoutMs: timeoutSeconds * 1000 } : {});
|
|
46
|
+
if (!waited.found) {
|
|
47
|
+
return jsonResult({ ok: false, name: vmName, source: template, error: waited.error }, true);
|
|
48
|
+
}
|
|
49
|
+
return jsonResult({ ok: true, name: vmName, source: template, ip: waited.ip });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=start-vm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-vm.js","sourceRoot":"","sources":["../../../src/tools/local/start-vm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { type AnkaResult } from "../../anka.js";
|
|
3
|
+
/**
|
|
4
|
+
* A VM/template identifier. Rejects empty values and anything starting with
|
|
5
|
+
* "-" so a name can never be reinterpreted as a CLI flag (e.g. "--all").
|
|
6
|
+
*/
|
|
7
|
+
export declare const vmNameSchema: import("zod").ZodString;
|
|
8
|
+
/** A row from `anka -j list`. */
|
|
9
|
+
export interface AnkaVm {
|
|
10
|
+
name?: string;
|
|
11
|
+
uuid?: string;
|
|
12
|
+
status?: string;
|
|
13
|
+
version?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** Return the local VM library as parsed by `anka -j list`. */
|
|
17
|
+
export declare function listVms(): Promise<AnkaVm[]>;
|
|
18
|
+
/** Extract a concise error string from a failed anka invocation. */
|
|
19
|
+
export declare function ankaError(result: AnkaResult): string;
|
|
20
|
+
/**
|
|
21
|
+
* Turn an anka result into a narrow tool result: a clean error on failure, or
|
|
22
|
+
* the provided success fields on success. Keeps verbose CLI output (stdout,
|
|
23
|
+
* args, exit codes) out of the agent's context.
|
|
24
|
+
*/
|
|
25
|
+
export declare function localResult(result: AnkaResult, success: Record<string, unknown>): CallToolResult;
|
|
26
|
+
/** The two fields of `anka -j show` the local backend relies on. */
|
|
27
|
+
export interface VmShowInfo {
|
|
28
|
+
status?: string;
|
|
29
|
+
ip?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Run `anka -j show <name>` and return its parsed body. */
|
|
32
|
+
export declare function showVm(name: string): Promise<{
|
|
33
|
+
ok: true;
|
|
34
|
+
info: VmShowInfo;
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
error: string;
|
|
38
|
+
}>;
|
|
39
|
+
export type WaitForIpResult = {
|
|
40
|
+
found: true;
|
|
41
|
+
ip: string;
|
|
42
|
+
} | {
|
|
43
|
+
found: false;
|
|
44
|
+
error: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Poll `anka show` until the VM is running and has an IP, so callers can wait
|
|
48
|
+
* out VM boot in a single tool call instead of re-checking repeatedly. Fails
|
|
49
|
+
* fast on a hard CLI error (e.g. unknown VM) and gives up after the timeout.
|
|
50
|
+
*/
|
|
51
|
+
export declare function waitForVmIp(name: string, opts?: {
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
intervalMs?: number;
|
|
54
|
+
}): Promise<WaitForIpResult>;
|
|
55
|
+
/** Count VMs currently in the "running" state. */
|
|
56
|
+
export declare function countRunningVms(): Promise<number>;
|
|
57
|
+
/**
|
|
58
|
+
* Throw if starting another VM would exceed the configured running-VM limit.
|
|
59
|
+
* Anka's local (non-Build) license permits a small number of concurrent VMs,
|
|
60
|
+
* so this keeps the agent from blowing past it.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertRunningCapacity(action: string): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { runAnka } from "../../anka.js";
|
|
2
|
+
import { config } from "../../config.js";
|
|
3
|
+
import { boundedName } from "../../security/schemas.js";
|
|
4
|
+
import { sanitizeCliError } from "../../security/sanitize.js";
|
|
5
|
+
import { jsonResult } from "../define-tool.js";
|
|
6
|
+
/**
|
|
7
|
+
* A VM/template identifier. Rejects empty values and anything starting with
|
|
8
|
+
* "-" so a name can never be reinterpreted as a CLI flag (e.g. "--all").
|
|
9
|
+
*/
|
|
10
|
+
export const vmNameSchema = boundedName;
|
|
11
|
+
/** Return the local VM library as parsed by `anka -j list`. */
|
|
12
|
+
export async function listVms() {
|
|
13
|
+
const result = await runAnka(["list"]);
|
|
14
|
+
if (!result.ok) {
|
|
15
|
+
throw new Error(result.message || "Failed to list local VMs");
|
|
16
|
+
}
|
|
17
|
+
return Array.isArray(result.body) ? result.body : [];
|
|
18
|
+
}
|
|
19
|
+
/** Extract a concise error string from a failed anka invocation. */
|
|
20
|
+
export function ankaError(result) {
|
|
21
|
+
if (result.message)
|
|
22
|
+
return result.message;
|
|
23
|
+
const stderr = result.stderr.trim();
|
|
24
|
+
if (stderr)
|
|
25
|
+
return sanitizeCliError(stderr);
|
|
26
|
+
return `anka exited with code ${result.exitCode}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Turn an anka result into a narrow tool result: a clean error on failure, or
|
|
30
|
+
* the provided success fields on success. Keeps verbose CLI output (stdout,
|
|
31
|
+
* args, exit codes) out of the agent's context.
|
|
32
|
+
*/
|
|
33
|
+
export function localResult(result, success) {
|
|
34
|
+
if (!result.ok) {
|
|
35
|
+
return jsonResult({ ok: false, error: ankaError(result) }, true);
|
|
36
|
+
}
|
|
37
|
+
return jsonResult({ ok: true, ...success });
|
|
38
|
+
}
|
|
39
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
40
|
+
/** Run `anka -j show <name>` and return its parsed body. */
|
|
41
|
+
export async function showVm(name) {
|
|
42
|
+
const result = await runAnka(["show", name]);
|
|
43
|
+
if (!result.ok)
|
|
44
|
+
return { ok: false, error: ankaError(result) };
|
|
45
|
+
return { ok: true, info: (result.body ?? {}) };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Poll `anka show` until the VM is running and has an IP, so callers can wait
|
|
49
|
+
* out VM boot in a single tool call instead of re-checking repeatedly. Fails
|
|
50
|
+
* fast on a hard CLI error (e.g. unknown VM) and gives up after the timeout.
|
|
51
|
+
*/
|
|
52
|
+
export async function waitForVmIp(name, opts = {}) {
|
|
53
|
+
const timeoutMs = opts.timeoutMs ?? config.localIpTimeoutMs;
|
|
54
|
+
const intervalMs = opts.intervalMs ?? config.localPollIntervalMs;
|
|
55
|
+
const deadline = Date.now() + timeoutMs;
|
|
56
|
+
let lastStatus;
|
|
57
|
+
for (;;) {
|
|
58
|
+
const shown = await showVm(name);
|
|
59
|
+
if (!shown.ok)
|
|
60
|
+
return { found: false, error: shown.error };
|
|
61
|
+
lastStatus = shown.info.status;
|
|
62
|
+
if (shown.info.status === "running" && shown.info.ip) {
|
|
63
|
+
return { found: true, ip: shown.info.ip };
|
|
64
|
+
}
|
|
65
|
+
if (Date.now() >= deadline) {
|
|
66
|
+
return {
|
|
67
|
+
found: false,
|
|
68
|
+
error: `Timed out after ${Math.round(timeoutMs / 1000)}s waiting for an IP (last status: ${lastStatus ?? "unknown"}).`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
await sleep(intervalMs);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Count VMs currently in the "running" state. */
|
|
75
|
+
export async function countRunningVms() {
|
|
76
|
+
const vms = await listVms();
|
|
77
|
+
return vms.filter((vm) => vm.status === "running").length;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Throw if starting another VM would exceed the configured running-VM limit.
|
|
81
|
+
* Anka's local (non-Build) license permits a small number of concurrent VMs,
|
|
82
|
+
* so this keeps the agent from blowing past it.
|
|
83
|
+
*/
|
|
84
|
+
export async function assertRunningCapacity(action) {
|
|
85
|
+
const running = await countRunningVms();
|
|
86
|
+
if (running >= config.localMaxVms) {
|
|
87
|
+
throw new Error(`Cannot ${action}: ${running} VM(s) already running, which meets the limit of ` +
|
|
88
|
+
`${config.localMaxVms} (ANKA_LOCAL_MAX_VMS). Stop or delete a VM first.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=vms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vms.js","sourceRoot":"","sources":["../../../src/tools/local/vms.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAmB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AAWxC,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,IAAI,0BAA0B,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,IAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,SAAS,CAAC,MAAkB;IAC1C,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,MAAM;QAAE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,yBAAyB,MAAM,CAAC,QAAQ,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAkB,EAAE,OAAgC;IAC9E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAQtF,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAe,EAAE,CAAC;AAC/D,CAAC;AAID;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,OAAoD,EAAE;IAEtD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,gBAAgB,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,mBAAmB,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,UAA8B,CAAC;IAEnC,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3D,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;QAC/B,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QAC5C,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC3B,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,mBAAmB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,qCAAqC,UAAU,IAAI,SAAS,IAAI;aACvH,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;IAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAc;IACxD,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;IACxC,IAAI,OAAO,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,UAAU,MAAM,KAAK,OAAO,mDAAmD;YAC7E,GAAG,MAAM,CAAC,WAAW,mDAAmD,CAC3E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import express, { type NextFunction, type Request, type Response } from "express";
|
|
2
|
+
/** Register admin routes for client token lifecycle management. */
|
|
3
|
+
export declare function registerAdminRoutes(app: express.Application, rateLimit: (req: Request, res: Response, next: NextFunction) => void): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import express, { Router } from "express";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import { clientSourceFromRequest, logAdminEvent, logAuthFailure } from "../log.js";
|
|
5
|
+
import { cleanupCredentialInstances } from "../tokens/cleanup.js";
|
|
6
|
+
import { getTokenStore } from "../tokens/store.js";
|
|
7
|
+
/** Constant-time string comparison that tolerates differing lengths. */
|
|
8
|
+
function safeEqual(a, b) {
|
|
9
|
+
const bufA = Buffer.from(a);
|
|
10
|
+
const bufB = Buffer.from(b);
|
|
11
|
+
if (bufA.length !== bufB.length)
|
|
12
|
+
return false;
|
|
13
|
+
return timingSafeEqual(bufA, bufB);
|
|
14
|
+
}
|
|
15
|
+
function bearerToken(req) {
|
|
16
|
+
const header = req.headers.authorization ?? "";
|
|
17
|
+
return header.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : "";
|
|
18
|
+
}
|
|
19
|
+
function adminGuard(req, res, next) {
|
|
20
|
+
const token = bearerToken(req);
|
|
21
|
+
if (!token || !safeEqual(token, config.adminToken)) {
|
|
22
|
+
logAuthFailure(clientSourceFromRequest(req), "/admin");
|
|
23
|
+
res
|
|
24
|
+
.status(401)
|
|
25
|
+
.set("WWW-Authenticate", "Bearer")
|
|
26
|
+
.json({ ok: false, error: "Unauthorized" });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
next();
|
|
30
|
+
}
|
|
31
|
+
/** Register admin routes for client token lifecycle management. */
|
|
32
|
+
export function registerAdminRoutes(app, rateLimit) {
|
|
33
|
+
if (!config.adminToken)
|
|
34
|
+
return;
|
|
35
|
+
const router = Router();
|
|
36
|
+
router.use(express.json({ limit: config.maxBodyBytes }));
|
|
37
|
+
router.use(rateLimit);
|
|
38
|
+
router.use(adminGuard);
|
|
39
|
+
router.post("/tokens", (req, res) => {
|
|
40
|
+
const label = typeof req.body?.label === "string" ? req.body.label : "";
|
|
41
|
+
const created = getTokenStore().createToken(label);
|
|
42
|
+
logAdminEvent("token created", { id: created.id, label: created.label });
|
|
43
|
+
res.status(201).json({ ok: true, id: created.id, label: created.label, token: created.token });
|
|
44
|
+
});
|
|
45
|
+
router.get("/tokens", (_req, res) => {
|
|
46
|
+
const tokens = getTokenStore().listTokens();
|
|
47
|
+
res.json({ tokens });
|
|
48
|
+
});
|
|
49
|
+
router.delete("/tokens/:id", async (req, res) => {
|
|
50
|
+
const id = String(req.params.id);
|
|
51
|
+
const existing = getTokenStore().getTokenById(id);
|
|
52
|
+
if (!existing) {
|
|
53
|
+
res.status(404).json({ ok: false, error: `Token not found: ${id}` });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (existing.revoked) {
|
|
57
|
+
res.status(400).json({ ok: false, error: `Token already revoked: ${id}` });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
let instanceIds = [];
|
|
61
|
+
try {
|
|
62
|
+
({ instanceIds } = getTokenStore().revokeToken(id));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
res.status(400).json({ ok: false, error: String(error) });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
logAdminEvent("token revoked", { id, label: existing.label });
|
|
69
|
+
const cleanup = await cleanupCredentialInstances(id, instanceIds);
|
|
70
|
+
res.json({ ok: true, id, revoked: true, cleanup });
|
|
71
|
+
});
|
|
72
|
+
app.use("/admin", router);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/transports/admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module "express-serve-static-core" {
|
|
2
|
+
interface Request {
|
|
3
|
+
mcpCredential?: {
|
|
4
|
+
credentialId: string;
|
|
5
|
+
credentialLabel?: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Run the MCP server over streamable HTTP. Each MCP session gets its own
|
|
11
|
+
* server + transport pair, keyed by the session id the SDK assigns on
|
|
12
|
+
* initialize.
|
|
13
|
+
*/
|
|
14
|
+
export declare function startHttp(): Promise<void>;
|