apcore-mcp 0.3.0 → 0.5.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/dist/adapters/annotations.d.ts +6 -2
- package/dist/adapters/annotations.d.ts.map +1 -1
- package/dist/adapters/annotations.js +27 -9
- package/dist/adapters/annotations.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -5
- package/dist/cli.js.map +1 -1
- package/dist/explorer/handler.d.ts +55 -0
- package/dist/explorer/handler.d.ts.map +1 -0
- package/dist/explorer/handler.js +175 -0
- package/dist/explorer/handler.js.map +1 -0
- package/dist/explorer/html.d.ts +9 -0
- package/dist/explorer/html.d.ts.map +1 -0
- package/dist/explorer/html.js +238 -0
- package/dist/explorer/html.js.map +1 -0
- package/dist/explorer/index.d.ts +6 -0
- package/dist/explorer/index.d.ts.map +1 -0
- package/dist/explorer/index.js +5 -0
- package/dist/explorer/index.js.map +1 -0
- package/dist/index.d.ts +41 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -18
- package/dist/index.js.map +1 -1
- package/dist/server/factory.d.ts +12 -0
- package/dist/server/factory.d.ts.map +1 -1
- package/dist/server/factory.js +73 -9
- package/dist/server/factory.js.map +1 -1
- package/dist/server/router.d.ts +7 -1
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +65 -5
- package/dist/server/router.js.map +1 -1
- package/dist/server/transport.d.ts +55 -1
- package/dist/server/transport.d.ts.map +1 -1
- package/dist/server/transport.js +114 -1
- package/dist/server/transport.js.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -2
|
@@ -21,10 +21,14 @@ export declare class AnnotationMapper {
|
|
|
21
21
|
/**
|
|
22
22
|
* Generate a description suffix string from annotations.
|
|
23
23
|
*
|
|
24
|
+
* Only includes annotation fields that differ from their default values:
|
|
25
|
+
* readonly=false, destructive=false, idempotent=false,
|
|
26
|
+
* requires_approval=false, open_world=true
|
|
27
|
+
*
|
|
24
28
|
* Returns a formatted string like:
|
|
25
|
-
* `\n\n[Annotations: readonly=true,
|
|
29
|
+
* `\n\n[Annotations: readonly=true, idempotent=true]`
|
|
26
30
|
*
|
|
27
|
-
* Returns an empty string if annotations are null.
|
|
31
|
+
* Returns an empty string if annotations are null or all fields are defaults.
|
|
28
32
|
*/
|
|
29
33
|
toDescriptionSuffix(annotations: ModuleAnnotations | null): string;
|
|
30
34
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/adapters/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEzE,qBAAa,gBAAgB;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,GAAG,kBAAkB;IAoB3E
|
|
1
|
+
{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/adapters/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEzE,qBAAa,gBAAgB;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,GAAG,kBAAkB;IAoB3E;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,GAAG,MAAM;IAgClE;;;;OAIG;IACH,mBAAmB,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO;CAOpE"}
|
|
@@ -37,22 +37,40 @@ export class AnnotationMapper {
|
|
|
37
37
|
/**
|
|
38
38
|
* Generate a description suffix string from annotations.
|
|
39
39
|
*
|
|
40
|
+
* Only includes annotation fields that differ from their default values:
|
|
41
|
+
* readonly=false, destructive=false, idempotent=false,
|
|
42
|
+
* requires_approval=false, open_world=true
|
|
43
|
+
*
|
|
40
44
|
* Returns a formatted string like:
|
|
41
|
-
* `\n\n[Annotations: readonly=true,
|
|
45
|
+
* `\n\n[Annotations: readonly=true, idempotent=true]`
|
|
42
46
|
*
|
|
43
|
-
* Returns an empty string if annotations are null.
|
|
47
|
+
* Returns an empty string if annotations are null or all fields are defaults.
|
|
44
48
|
*/
|
|
45
49
|
toDescriptionSuffix(annotations) {
|
|
46
50
|
if (annotations === null) {
|
|
47
51
|
return "";
|
|
48
52
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const DEFAULTS = {
|
|
54
|
+
readonly: false,
|
|
55
|
+
destructive: false,
|
|
56
|
+
idempotent: false,
|
|
57
|
+
requires_approval: false,
|
|
58
|
+
open_world: true,
|
|
59
|
+
};
|
|
60
|
+
const parts = [];
|
|
61
|
+
if (annotations.readonly !== DEFAULTS.readonly)
|
|
62
|
+
parts.push(`readonly=${annotations.readonly}`);
|
|
63
|
+
if (annotations.destructive !== DEFAULTS.destructive)
|
|
64
|
+
parts.push(`destructive=${annotations.destructive}`);
|
|
65
|
+
if (annotations.idempotent !== DEFAULTS.idempotent)
|
|
66
|
+
parts.push(`idempotent=${annotations.idempotent}`);
|
|
67
|
+
if (annotations.requiresApproval !== DEFAULTS.requires_approval)
|
|
68
|
+
parts.push(`requires_approval=${annotations.requiresApproval}`);
|
|
69
|
+
if (annotations.openWorld !== DEFAULTS.open_world)
|
|
70
|
+
parts.push(`open_world=${annotations.openWorld}`);
|
|
71
|
+
if (parts.length === 0) {
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
56
74
|
return `\n\n[Annotations: ${parts.join(", ")}]`;
|
|
57
75
|
}
|
|
58
76
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotations.js","sourceRoot":"","sources":["../../src/adapters/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,gBAAgB;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,WAAqC;QACpD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,eAAe,EAAE,KAAK;gBACtB,cAAc,EAAE,KAAK;gBACrB,aAAa,EAAE,IAAI;gBACnB,KAAK,EAAE,IAAI;aACZ,CAAC;QACJ,CAAC;QAED,OAAO;YACL,YAAY,EAAE,WAAW,CAAC,QAAQ;YAClC,eAAe,EAAE,WAAW,CAAC,WAAW;YACxC,cAAc,EAAE,WAAW,CAAC,UAAU;YACtC,aAAa,EAAE,WAAW,CAAC,SAAS;YACpC,KAAK,EAAE,IAAI;SACZ,CAAC;IACJ,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"annotations.js","sourceRoot":"","sources":["../../src/adapters/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,gBAAgB;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,WAAqC;QACpD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO;gBACL,YAAY,EAAE,KAAK;gBACnB,eAAe,EAAE,KAAK;gBACtB,cAAc,EAAE,KAAK;gBACrB,aAAa,EAAE,IAAI;gBACnB,KAAK,EAAE,IAAI;aACZ,CAAC;QACJ,CAAC;QAED,OAAO;YACL,YAAY,EAAE,WAAW,CAAC,QAAQ;YAClC,eAAe,EAAE,WAAW,CAAC,WAAW;YACxC,cAAc,EAAE,WAAW,CAAC,UAAU;YACtC,aAAa,EAAE,WAAW,CAAC,SAAS;YACpC,KAAK,EAAE,IAAI;SACZ,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,WAAqC;QACvD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,QAAQ,GAA4B;YACxC,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,KAAK;YACjB,iBAAiB,EAAE,KAAK;YACxB,UAAU,EAAE,IAAI;SACjB,CAAC;QAEF,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;YAC5C,KAAK,CAAC,IAAI,CAAC,YAAY,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjD,IAAI,WAAW,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;YAClD,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;QACvD,IAAI,WAAW,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;YAChD,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC;QACrD,IAAI,WAAW,CAAC,gBAAgB,KAAK,QAAQ,CAAC,iBAAiB;YAC7D,KAAK,CAAC,IAAI,CAAC,qBAAqB,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAClE,IAAI,WAAW,CAAC,SAAS,KAAK,QAAQ,CAAC,UAAU;YAC/C,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC;QAEpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,qBAAqB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,WAAqC;QACvD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,WAAW,CAAC,gBAAgB,CAAC;IACtC,CAAC;CACF"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAoCH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAkH1C"}
|
package/dist/cli.js
CHANGED
|
@@ -28,6 +28,9 @@ Options:
|
|
|
28
28
|
--name <string> MCP server name (default: apcore-mcp, max 255 chars)
|
|
29
29
|
--version <string> MCP server version (default: package version)
|
|
30
30
|
--log-level <level> Logging level: DEBUG, INFO, WARNING, ERROR (default: INFO)
|
|
31
|
+
--explorer Enable the browser-based Tool Explorer UI (HTTP only)
|
|
32
|
+
--explorer-prefix <path> URL prefix for the explorer UI (default: /explorer)
|
|
33
|
+
--allow-execute Allow tool execution from the explorer UI
|
|
31
34
|
--help Show this help message
|
|
32
35
|
`);
|
|
33
36
|
}
|
|
@@ -47,6 +50,9 @@ export async function main() {
|
|
|
47
50
|
name: { type: "string", default: "apcore-mcp" },
|
|
48
51
|
version: { type: "string" },
|
|
49
52
|
"log-level": { type: "string", default: "INFO" },
|
|
53
|
+
explorer: { type: "boolean", default: false },
|
|
54
|
+
"explorer-prefix": { type: "string", default: "/explorer" },
|
|
55
|
+
"allow-execute": { type: "boolean", default: false },
|
|
50
56
|
help: { type: "boolean", default: false },
|
|
51
57
|
},
|
|
52
58
|
strict: true,
|
|
@@ -93,22 +99,27 @@ export async function main() {
|
|
|
93
99
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
100
|
let Registry;
|
|
95
101
|
try {
|
|
96
|
-
|
|
97
|
-
const apcore = await import("apcore");
|
|
102
|
+
const apcore = await import("apcore-js");
|
|
98
103
|
Registry = apcore.Registry;
|
|
99
104
|
}
|
|
100
105
|
catch {
|
|
101
|
-
fail("Failed to import 'apcore' package. Install it with: npm install apcore");
|
|
106
|
+
fail("Failed to import 'apcore-js' package. Install it with: npm install apcore-js");
|
|
102
107
|
}
|
|
103
108
|
// Create Registry and discover modules
|
|
104
|
-
const registry = new Registry({
|
|
105
|
-
const numModules = registry.discover();
|
|
109
|
+
const registry = new Registry({ extensionsDir: resolvedDir });
|
|
110
|
+
const numModules = await registry.discover();
|
|
106
111
|
if (numModules === 0) {
|
|
107
112
|
console.warn(`Warning: No modules discovered in '${extensionsDir}'.`);
|
|
108
113
|
}
|
|
109
114
|
else {
|
|
110
115
|
console.info(`Discovered ${numModules} module(s) in '${extensionsDir}'.`);
|
|
111
116
|
}
|
|
117
|
+
// Validate log-level
|
|
118
|
+
const logLevel = values["log-level"];
|
|
119
|
+
const validLogLevels = ["DEBUG", "INFO", "WARNING", "ERROR"];
|
|
120
|
+
if (logLevel && !validLogLevels.includes(logLevel)) {
|
|
121
|
+
fail(`--log-level must be one of: ${validLogLevels.join(", ")}. Got '${logLevel}'.`);
|
|
122
|
+
}
|
|
112
123
|
// Launch the MCP server
|
|
113
124
|
try {
|
|
114
125
|
await serve(registry, {
|
|
@@ -117,6 +128,10 @@ export async function main() {
|
|
|
117
128
|
port,
|
|
118
129
|
name,
|
|
119
130
|
version: values.version ?? undefined,
|
|
131
|
+
logLevel: logLevel,
|
|
132
|
+
explorer: values.explorer,
|
|
133
|
+
explorerPrefix: values["explorer-prefix"],
|
|
134
|
+
allowExecute: values["allow-execute"],
|
|
120
135
|
});
|
|
121
136
|
}
|
|
122
137
|
catch (error) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5C,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;cACA,OAAO
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExplorerHandler - HTTP route handler for the MCP Tool Explorer.
|
|
3
|
+
*
|
|
4
|
+
* Provides a browser-based UI for inspecting and testing MCP tools.
|
|
5
|
+
* Handles explorer-prefixed HTTP routes within the existing HTTP server.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* - GET {prefix}/ → HTML explorer page
|
|
9
|
+
* - GET {prefix}/tools → JSON array of tool summaries
|
|
10
|
+
* - GET {prefix}/tools/{name} → JSON tool detail
|
|
11
|
+
* - POST {prefix}/tools/{name}/call → Execute tool (403 if disabled)
|
|
12
|
+
*/
|
|
13
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
14
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
+
import type { ExecutionRouter } from "../server/router.js";
|
|
16
|
+
/** Options for creating an ExplorerHandler. */
|
|
17
|
+
export interface ExplorerHandlerOptions {
|
|
18
|
+
/** Whether to allow tool execution from the explorer UI. Default: false */
|
|
19
|
+
allowExecute?: boolean;
|
|
20
|
+
/** URL prefix for the explorer. Default: "/explorer" */
|
|
21
|
+
prefix?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class ExplorerHandler {
|
|
24
|
+
private readonly _toolsByName;
|
|
25
|
+
private readonly _tools;
|
|
26
|
+
private readonly _router;
|
|
27
|
+
private readonly _allowExecute;
|
|
28
|
+
private readonly _prefix;
|
|
29
|
+
constructor(tools: Tool[], router: ExecutionRouter, options?: ExplorerHandlerOptions);
|
|
30
|
+
/** The URL prefix this handler is mounted at. */
|
|
31
|
+
get prefix(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Attempt to handle an HTTP request.
|
|
34
|
+
*
|
|
35
|
+
* @returns true if the request was handled, false if it should be passed through
|
|
36
|
+
*/
|
|
37
|
+
handleRequest(req: IncomingMessage, res: ServerResponse, url: URL): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Build a summary dict for a tool (used in the list endpoint).
|
|
40
|
+
*/
|
|
41
|
+
private _toolSummary;
|
|
42
|
+
/**
|
|
43
|
+
* Build a full detail dict for a tool (used in the detail endpoint).
|
|
44
|
+
*/
|
|
45
|
+
private _toolDetail;
|
|
46
|
+
/**
|
|
47
|
+
* Handle GET {prefix}/tools/{name} - return tool detail or 404.
|
|
48
|
+
*/
|
|
49
|
+
private _handleToolDetail;
|
|
50
|
+
/**
|
|
51
|
+
* Handle POST {prefix}/tools/{name}/call - execute tool or return 403/404.
|
|
52
|
+
*/
|
|
53
|
+
private _handleCallTool;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/explorer/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAI3D,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACrC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAG/B,KAAK,EAAE,IAAI,EAAE,EACb,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,sBAAsB;IASlC,iDAAiD;IACjD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;OAIG;IACG,aAAa,CACjB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,OAAO,CAAC;IA6CnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;OAEG;YACW,eAAe;CAqE9B"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExplorerHandler - HTTP route handler for the MCP Tool Explorer.
|
|
3
|
+
*
|
|
4
|
+
* Provides a browser-based UI for inspecting and testing MCP tools.
|
|
5
|
+
* Handles explorer-prefixed HTTP routes within the existing HTTP server.
|
|
6
|
+
*
|
|
7
|
+
* Routes:
|
|
8
|
+
* - GET {prefix}/ → HTML explorer page
|
|
9
|
+
* - GET {prefix}/tools → JSON array of tool summaries
|
|
10
|
+
* - GET {prefix}/tools/{name} → JSON tool detail
|
|
11
|
+
* - POST {prefix}/tools/{name}/call → Execute tool (403 if disabled)
|
|
12
|
+
*/
|
|
13
|
+
import { readBody } from "../server/transport.js";
|
|
14
|
+
import { EXPLORER_HTML } from "./html.js";
|
|
15
|
+
/** Maximum request body size for explorer call endpoint (1MB). */
|
|
16
|
+
const EXPLORER_MAX_BODY_BYTES = 1024 * 1024;
|
|
17
|
+
export class ExplorerHandler {
|
|
18
|
+
_toolsByName;
|
|
19
|
+
_tools;
|
|
20
|
+
_router;
|
|
21
|
+
_allowExecute;
|
|
22
|
+
_prefix;
|
|
23
|
+
constructor(tools, router, options) {
|
|
24
|
+
this._tools = tools;
|
|
25
|
+
this._router = router;
|
|
26
|
+
this._allowExecute = options?.allowExecute ?? false;
|
|
27
|
+
this._prefix = options?.prefix ?? "/explorer";
|
|
28
|
+
this._toolsByName = new Map(tools.map((t) => [t.name, t]));
|
|
29
|
+
}
|
|
30
|
+
/** The URL prefix this handler is mounted at. */
|
|
31
|
+
get prefix() {
|
|
32
|
+
return this._prefix;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Attempt to handle an HTTP request.
|
|
36
|
+
*
|
|
37
|
+
* @returns true if the request was handled, false if it should be passed through
|
|
38
|
+
*/
|
|
39
|
+
async handleRequest(req, res, url) {
|
|
40
|
+
const pathname = url.pathname;
|
|
41
|
+
// Normalize: strip trailing slash for matching (except root)
|
|
42
|
+
const prefixSlash = this._prefix + "/";
|
|
43
|
+
// GET {prefix}/ → HTML page
|
|
44
|
+
if (req.method === "GET" &&
|
|
45
|
+
(pathname === this._prefix || pathname === prefixSlash)) {
|
|
46
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
47
|
+
res.end(EXPLORER_HTML);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// GET {prefix}/tools → list all tools
|
|
51
|
+
if (req.method === "GET" && pathname === this._prefix + "/tools") {
|
|
52
|
+
const summaries = this._tools.map((t) => this._toolSummary(t));
|
|
53
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
54
|
+
res.end(JSON.stringify(summaries));
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// POST {prefix}/tools/{name}/call → execute tool
|
|
58
|
+
if (req.method === "POST" && pathname.startsWith(this._prefix + "/tools/") && pathname.endsWith("/call")) {
|
|
59
|
+
const toolName = decodeURIComponent(pathname.slice((this._prefix + "/tools/").length, -"/call".length));
|
|
60
|
+
await this._handleCallTool(req, res, toolName);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// GET {prefix}/tools/{name} → tool detail
|
|
64
|
+
if (req.method === "GET" && pathname.startsWith(this._prefix + "/tools/")) {
|
|
65
|
+
const toolName = decodeURIComponent(pathname.slice((this._prefix + "/tools/").length));
|
|
66
|
+
this._handleToolDetail(res, toolName);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build a summary dict for a tool (used in the list endpoint).
|
|
73
|
+
*/
|
|
74
|
+
_toolSummary(tool) {
|
|
75
|
+
const result = {
|
|
76
|
+
name: tool.name,
|
|
77
|
+
description: tool.description ?? "",
|
|
78
|
+
};
|
|
79
|
+
if (tool.annotations) {
|
|
80
|
+
result.annotations = tool.annotations;
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build a full detail dict for a tool (used in the detail endpoint).
|
|
86
|
+
*/
|
|
87
|
+
_toolDetail(tool) {
|
|
88
|
+
const result = {
|
|
89
|
+
name: tool.name,
|
|
90
|
+
description: tool.description ?? "",
|
|
91
|
+
inputSchema: tool.inputSchema,
|
|
92
|
+
};
|
|
93
|
+
if (tool.annotations) {
|
|
94
|
+
result.annotations = tool.annotations;
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Handle GET {prefix}/tools/{name} - return tool detail or 404.
|
|
100
|
+
*/
|
|
101
|
+
_handleToolDetail(res, toolName) {
|
|
102
|
+
const tool = this._toolsByName.get(toolName);
|
|
103
|
+
if (!tool) {
|
|
104
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
105
|
+
res.end(JSON.stringify({ error: `Tool not found: ${toolName}` }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
109
|
+
res.end(JSON.stringify(this._toolDetail(tool)));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Handle POST {prefix}/tools/{name}/call - execute tool or return 403/404.
|
|
113
|
+
*/
|
|
114
|
+
async _handleCallTool(req, res, toolName) {
|
|
115
|
+
if (!this._allowExecute) {
|
|
116
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
117
|
+
res.end(JSON.stringify({
|
|
118
|
+
error: "Tool execution is disabled. Launch with --allow-execute to enable.",
|
|
119
|
+
}));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const tool = this._toolsByName.get(toolName);
|
|
123
|
+
if (!tool) {
|
|
124
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
125
|
+
res.end(JSON.stringify({ error: `Tool not found: ${toolName}` }));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let body = {};
|
|
129
|
+
try {
|
|
130
|
+
const raw = await readBody(req, EXPLORER_MAX_BODY_BYTES);
|
|
131
|
+
if (raw) {
|
|
132
|
+
body = JSON.parse(raw);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Use empty body if parsing fails
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const [content, isError] = await this._router.handleCall(toolName, body);
|
|
140
|
+
// Extract text from content list
|
|
141
|
+
const texts = content
|
|
142
|
+
.filter((item) => item.type === "text")
|
|
143
|
+
.map((item) => item.text);
|
|
144
|
+
// Try to parse as JSON for a cleaner response
|
|
145
|
+
let result;
|
|
146
|
+
if (texts.length === 1) {
|
|
147
|
+
try {
|
|
148
|
+
result = JSON.parse(texts[0]);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
result = texts[0];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
result = texts;
|
|
156
|
+
}
|
|
157
|
+
if (isError) {
|
|
158
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
159
|
+
res.end(JSON.stringify({ error: result }));
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
163
|
+
res.end(JSON.stringify({ result }));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
console.error(`Explorer call_tool error for ${toolName}:`, err);
|
|
168
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
169
|
+
res.end(JSON.stringify({
|
|
170
|
+
error: err instanceof Error ? err.message : String(err),
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/explorer/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained HTML page for the MCP Tool Explorer.
|
|
3
|
+
*
|
|
4
|
+
* Single-page application with no external dependencies.
|
|
5
|
+
* Displays registered MCP tools, their schemas, annotations,
|
|
6
|
+
* and optionally allows executing tools from the browser.
|
|
7
|
+
*/
|
|
8
|
+
export declare const EXPLORER_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>MCP Tool Explorer</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, monospace;\n background: #f5f5f5; color: #333; padding: 24px; }\n h1 { font-size: 1.4rem; margin-bottom: 16px; }\n .tool-list { list-style: none; }\n .tool-item { background: #fff; border: 1px solid #ddd; border-radius: 6px;\n padding: 12px 16px; margin-bottom: 8px; cursor: pointer; }\n .tool-item:hover { border-color: #888; }\n .tool-name { font-weight: 600; }\n .tool-desc { color: #666; font-size: 0.9rem; margin-top: 4px; }\n .hint { display: inline-block; background: #e8e8e8; padding: 2px 8px;\n border-radius: 3px; font-size: 0.75rem; margin-right: 4px; }\n .hint-readonly { background: #d4edda; color: #155724; }\n .hint-destructive { background: #f8d7da; color: #721c24; }\n .hint-idempotent { background: #cce5ff; color: #004085; }\n .detail { background: #fff; border: 1px solid #ddd; border-radius: 6px;\n padding: 16px; margin-top: 16px; display: none; }\n .detail.active { display: block; }\n .detail h2 { font-size: 1.1rem; margin-bottom: 12px; }\n .schema-label { font-weight: 600; margin-top: 12px; display: block; }\n pre { background: #282c34; color: #abb2bf; padding: 12px; border-radius: 4px;\n overflow-x: auto; font-size: 0.85rem; margin-top: 4px; }\n #loading { color: #888; }\n .try-it { margin-top: 16px; border-top: 1px solid #eee; padding-top: 16px; }\n .try-it h3 { font-size: 0.95rem; margin-bottom: 8px; }\n .input-editor { width: 100%; min-height: 120px; font-family: monospace;\n font-size: 0.85rem; padding: 10px; border: 1px solid #ddd;\n border-radius: 4px; resize: vertical; background: #fafafa; }\n .execute-btn { margin-top: 8px; padding: 8px 20px; background: #4CAF50; color: #fff;\n border: none; border-radius: 4px; cursor: pointer; font-size: 0.9rem;\n font-weight: 600; }\n .execute-btn:hover { background: #45a049; }\n .execute-btn:disabled { background: #ccc; cursor: not-allowed; }\n .result-area { margin-top: 12px; }\n .result-area pre { background: #1a2332; }\n .result-error { color: #f93e3e; }\n .result-success { color: #49cc90; }\n .exec-disabled { color: #888; font-size: 0.85rem; font-style: italic; margin-top: 16px; }\n</style>\n</head>\n<body>\n<h1>MCP Tool Explorer</h1>\n<div id=\"loading\">Loading tools...</div>\n<ul class=\"tool-list\" id=\"tools\"></ul>\n<div class=\"detail\" id=\"detail\"></div>\n<script>\n(function() {\n var base = window.location.pathname.replace(/\\/$/, '');\n var toolsEl = document.getElementById('tools');\n var detailEl = document.getElementById('detail');\n var loadingEl = document.getElementById('loading');\n var executeEnabled = null;\n\n function esc(s) {\n var d = document.createElement('div');\n d.appendChild(document.createTextNode(s));\n return d.innerHTML;\n }\n\n function defaultFromSchema(schema) {\n if (!schema || !schema.properties) return {};\n var result = {};\n var props = schema.properties;\n for (var key in props) {\n if (!props.hasOwnProperty(key)) continue;\n var t = props[key].type;\n if (props[key]['default'] !== undefined) {\n result[key] = props[key]['default'];\n } else if (t === 'string') {\n result[key] = '';\n } else if (t === 'number' || t === 'integer') {\n result[key] = 0;\n } else if (t === 'boolean') {\n result[key] = false;\n } else if (t === 'array') {\n result[key] = [];\n } else if (t === 'object') {\n result[key] = {};\n } else {\n result[key] = null;\n }\n }\n return result;\n }\n\n function hintsHtml(annotations) {\n if (!annotations) return '';\n var parts = [];\n if (annotations.readOnlyHint) parts.push('<span class=\"hint hint-readonly\">readOnly</span>');\n if (annotations.destructiveHint) parts.push('<span class=\"hint hint-destructive\">destructive</span>');\n if (annotations.idempotentHint) parts.push('<span class=\"hint hint-idempotent\">idempotent</span>');\n if (annotations.openWorldHint === false) parts.push('<span class=\"hint\">closedWorld</span>');\n return parts.join('');\n }\n\n fetch(base + '/tools')\n .then(function(r) { return r.json(); })\n .then(function(tools) {\n loadingEl.style.display = 'none';\n tools.forEach(function(t) {\n var li = document.createElement('li');\n li.className = 'tool-item';\n li.innerHTML =\n '<span class=\"tool-name\">' + esc(t.name) + '</span> ' +\n hintsHtml(t.annotations) +\n '<div class=\"tool-desc\">' + esc(t.description || '') + '</div>';\n li.onclick = function() { loadDetail(t.name); };\n toolsEl.appendChild(li);\n });\n fetch(base + '/tools/__probe__/call', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: '{}'\n }).then(function(r) {\n executeEnabled = r.status !== 403;\n }).catch(function() {});\n })\n .catch(function(e) { loadingEl.textContent = 'Error: ' + e; });\n\n function loadDetail(name) {\n fetch(base + '/tools/' + encodeURIComponent(name))\n .then(function(r) { return r.json(); })\n .then(function(d) {\n detailEl.className = 'detail active';\n var html =\n '<h2>' + esc(d.name) + '</h2>' +\n '<p>' + esc(d.description || '') + '</p>' +\n '<span class=\"schema-label\">Input Schema</span>' +\n '<pre>' + esc(JSON.stringify(d.inputSchema, null, 2)) + '</pre>';\n\n if (d.annotations) {\n html += '<span class=\"schema-label\">Annotations</span>' +\n '<pre>' + esc(JSON.stringify(d.annotations, null, 2)) + '</pre>';\n }\n\n html += '<div class=\"try-it\" id=\"try-it-section\">' +\n '<h3>Try it</h3>' +\n '<textarea class=\"input-editor\" id=\"input-editor\">' +\n esc(JSON.stringify(defaultFromSchema(d.inputSchema), null, 2)) +\n '</textarea>' +\n '<button class=\"execute-btn\" id=\"execute-btn\">Execute</button>' +\n '<div class=\"result-area\" id=\"result-area\"></div>' +\n '</div>';\n\n detailEl.innerHTML = html;\n\n document.getElementById('execute-btn').onclick = function() {\n execTool(d.name);\n };\n\n if (executeEnabled === false) {\n var section = document.getElementById('try-it-section');\n if (section) section.innerHTML =\n '<p class=\"exec-disabled\">' +\n 'Tool execution is disabled. ' +\n 'Launch with --allow-execute to enable.</p>';\n }\n })\n .catch(function(e) {\n detailEl.className = 'detail active';\n detailEl.innerHTML = '<p class=\"result-error\">Failed to load tool details: ' + esc(e.message) + '</p>';\n });\n }\n\n function execTool(name) {\n var btn = document.getElementById('execute-btn');\n var editor = document.getElementById('input-editor');\n var resultArea = document.getElementById('result-area');\n\n var inputText = editor.value.trim();\n var inputs;\n try {\n inputs = inputText ? JSON.parse(inputText) : {};\n } catch (e) {\n resultArea.innerHTML = '<p class=\"result-error\">Invalid JSON: ' + esc(e.message) + '</p>';\n return;\n }\n\n btn.disabled = true;\n btn.textContent = 'Executing...';\n resultArea.innerHTML = '';\n\n fetch(base + '/tools/' + encodeURIComponent(name) + '/call', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify(inputs)\n })\n .then(function(r) {\n if (r.status === 403) {\n executeEnabled = false;\n var section = document.getElementById('try-it-section');\n if (section) section.innerHTML =\n '<p class=\"exec-disabled\">' +\n 'Tool execution is disabled. ' +\n 'Launch with --allow-execute to enable.</p>';\n return null;\n }\n return r.json().then(function(data) { return {status: r.status, data: data}; });\n })\n .then(function(result) {\n if (!result) return;\n btn.disabled = false;\n btn.textContent = 'Execute';\n if (result.status >= 400) {\n resultArea.innerHTML = '<span class=\"schema-label result-error\">Error (' + result.status + ')</span>' +\n '<pre>' + esc(JSON.stringify(result.data, null, 2)) + '</pre>';\n } else {\n resultArea.innerHTML = '<span class=\"schema-label result-success\">Result</span>' +\n '<pre>' + esc(JSON.stringify(result.data, null, 2)) + '</pre>';\n }\n })\n .catch(function(e) {\n btn.disabled = false;\n btn.textContent = 'Execute';\n resultArea.innerHTML = '<p class=\"result-error\">Request failed: ' + esc(e.message) + '</p>';\n });\n }\n})();\n</script>\n</body>\n</html>\n";
|
|
9
|
+
//# sourceMappingURL=html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/explorer/html.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,i/RAqOzB,CAAC"}
|