offrouter-mcp 0.0.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.
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ const inputSchema = {};
3
+ const outputSchema = {
4
+ status: z.object({
5
+ configuredProfiles: z.array(z.string()),
6
+ configuredProviders: z.array(z.string()),
7
+ registeredAdapters: z.array(z.string()),
8
+ invokableProviders: z.array(z.string()),
9
+ configuredButNotInvokableProviders: z.array(z.string()),
10
+ candidateCount: z.number().int().nonnegative(),
11
+ activeTasks: z.number().int().nonnegative(),
12
+ }),
13
+ };
14
+ export function registerStatusTool(server, context) {
15
+ server.registerTool("offrouter_status", {
16
+ title: "OffRouter status",
17
+ description: "Report configured profiles/providers and active in-process delegation task count for this MCP server.",
18
+ inputSchema,
19
+ outputSchema,
20
+ }, async () => {
21
+ const activeTasks = context.runtime
22
+ .listTasks()
23
+ .filter((t) => t.status === "running" || t.status === "queued").length;
24
+ const registeredAdapters = Object.keys(context.adapters).sort((a, b) => a.localeCompare(b));
25
+ const candidateProviders = new Set(context.candidates.map((candidate) => candidate.providerId));
26
+ const invokableProviders = registeredAdapters.filter((providerId) => candidateProviders.has(providerId));
27
+ const invokable = new Set(invokableProviders);
28
+ const configuredButNotInvokableProviders = context.configuredProviders.filter((providerId) => !invokable.has(providerId));
29
+ const structuredContent = {
30
+ status: {
31
+ configuredProfiles: [...context.configuredProfiles],
32
+ configuredProviders: [...context.configuredProviders],
33
+ registeredAdapters,
34
+ invokableProviders,
35
+ configuredButNotInvokableProviders,
36
+ candidateCount: context.candidates.length,
37
+ activeTasks,
38
+ },
39
+ };
40
+ return {
41
+ content: [
42
+ {
43
+ type: "text",
44
+ text: JSON.stringify(structuredContent, null, 2),
45
+ },
46
+ ],
47
+ structuredContent,
48
+ };
49
+ });
50
+ }
51
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,YAAY,GAAG;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACvC,mBAAmB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACxC,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACvC,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACvC,kCAAkC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACvD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAC9C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KAC5C,CAAC;CACH,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,OAA4B;IAE5B,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,uGAAuG;QACzG,WAAW;QACX,YAAY;KACb,EACD,KAAK,IAAI,EAAE;QACT,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO;aAChC,SAAS,EAAE;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QACzE,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACrE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CACnB,CAAC;QACF,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAChC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAC5D,CAAC;QACF,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAClE,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CACnC,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC9C,MAAM,kCAAkC,GACtC,OAAO,CAAC,mBAAmB,CAAC,MAAM,CAChC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAC3C,CAAC;QAEJ,MAAM,iBAAiB,GAAG;YACxB,MAAM,EAAE;gBACN,kBAAkB,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;gBACnD,mBAAmB,EAAE,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;gBACrD,kBAAkB;gBAClB,kBAAkB;gBAClB,kCAAkC;gBAClC,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM;gBACzC,WAAW;aACZ;SACF,CAAC;QAEF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;iBACjD;aACF;YACD,iBAAiB;SAClB,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "offrouter-mcp",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "offrouter-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json",
12
+ "test": "vitest run --root ../.. packages/mcp/src/server.test.ts"
13
+ },
14
+ "dependencies": {
15
+ "offrouter-core": "0.0.0",
16
+ "@modelcontextprotocol/sdk": "0.0.0",
17
+ "zod": "0.0.0"
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "offrouter-mcp",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "offrouter-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.json",
12
+ "test": "vitest run --root ../.. packages/mcp/src/server.test.ts"
13
+ },
14
+ "dependencies": {
15
+ "offrouter-core": "*",
16
+ "@modelcontextprotocol/sdk": "^1.29.0",
17
+ "zod": "^3.23.0"
18
+ },
19
+ "private": true
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * offrouter-mcp — stdio MCP server surface for OffRouter.
4
+ */
5
+ import { realpathSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
9
+ import {
10
+ createOffRouterMcpServer,
11
+ loadOffRouterMcpContext,
12
+ type OffRouterMcpContext,
13
+ } from "./server.js";
14
+
15
+ export {
16
+ createOffRouterMcpContext,
17
+ createOffRouterMcpServer,
18
+ loadOffRouterMcpContext,
19
+ newTaskId,
20
+ type LoadOffRouterMcpContextOptions,
21
+ type OffRouterMcpContext,
22
+ type OffRouterMcpContextOptions,
23
+ } from "./server.js";
24
+
25
+ export const MCP_PACKAGE = "offrouter-mcp" as const;
26
+
27
+ export interface McpIoStream {
28
+ write(chunk: string): boolean | void;
29
+ }
30
+
31
+ export interface RunMcpCliOptions {
32
+ cwd?: string;
33
+ env?: NodeJS.ProcessEnv;
34
+ stdout?: McpIoStream;
35
+ stderr?: McpIoStream;
36
+ transport?: Transport;
37
+ context?: OffRouterMcpContext;
38
+ }
39
+
40
+ const HELP = `OffRouter MCP stdio server
41
+
42
+ Usage: offrouter-mcp [--help]
43
+
44
+ Starts the OffRouter MCP server over stdio. Tools exposed:
45
+ offrouter_route
46
+ offrouter_delegate
47
+ offrouter_status
48
+ offrouter_explain_decision
49
+ offrouter_cancel
50
+
51
+ V1 background tasks live only while this MCP server process remains alive.
52
+ `;
53
+
54
+ function defaultStream(stream: NodeJS.WriteStream): McpIoStream {
55
+ return {
56
+ write(chunk: string) {
57
+ return stream.write(chunk);
58
+ },
59
+ };
60
+ }
61
+
62
+ export async function runMcpCli(
63
+ argv: string[] = process.argv.slice(2),
64
+ options: RunMcpCliOptions = {},
65
+ ): Promise<number> {
66
+ const stdout = options.stdout ?? defaultStream(process.stdout);
67
+ const stderr = options.stderr ?? defaultStream(process.stderr);
68
+
69
+ if (argv.includes("--help") || argv.includes("-h")) {
70
+ stdout.write(HELP);
71
+ return 0;
72
+ }
73
+
74
+ if (argv.length > 0) {
75
+ stderr.write(`Unknown arguments: ${argv.join(" ")}\n\n${HELP}`);
76
+ return 1;
77
+ }
78
+
79
+ const context =
80
+ options.context ??
81
+ (await loadOffRouterMcpContext({
82
+ cwd: options.cwd ?? process.cwd(),
83
+ env: options.env ?? process.env,
84
+ }));
85
+ const server = createOffRouterMcpServer(context);
86
+ await server.connect(options.transport ?? new StdioServerTransport());
87
+ return 0;
88
+ }
89
+
90
+ function isEntrypoint(): boolean {
91
+ if (!process.argv[1]) {
92
+ return false;
93
+ }
94
+ try {
95
+ return (
96
+ realpathSync(process.argv[1]) ===
97
+ realpathSync(fileURLToPath(import.meta.url))
98
+ );
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+
104
+ if (isEntrypoint()) {
105
+ void runMcpCli(process.argv.slice(2)).then((code) => {
106
+ process.exitCode = code;
107
+ });
108
+ }