@agiflowai/one-mcp 0.3.14 → 0.3.16
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/cli.cjs +15 -5
- package/dist/cli.mjs +15 -5
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +196 -156
- package/dist/index.d.mts +196 -156
- package/dist/index.mjs +2 -2
- package/dist/{src-iTE9Cero.cjs → src-BRqEdbha.cjs} +212 -72
- package/dist/{src-D7Yq1bTx.mjs → src-Dn6vMZIk.mjs} +201 -73
- package/package.json +2 -2
|
@@ -1495,23 +1495,11 @@ var McpClientManagerService = class {
|
|
|
1495
1495
|
clients = /* @__PURE__ */ new Map();
|
|
1496
1496
|
serverConfigs = /* @__PURE__ */ new Map();
|
|
1497
1497
|
connectionPromises = /* @__PURE__ */ new Map();
|
|
1498
|
-
constructor() {
|
|
1499
|
-
process.on("exit", () => {
|
|
1500
|
-
this.cleanupOnExit();
|
|
1501
|
-
});
|
|
1502
|
-
process.on("SIGINT", () => {
|
|
1503
|
-
this.cleanupOnExit();
|
|
1504
|
-
process.exit(0);
|
|
1505
|
-
});
|
|
1506
|
-
process.on("SIGTERM", () => {
|
|
1507
|
-
this.cleanupOnExit();
|
|
1508
|
-
process.exit(0);
|
|
1509
|
-
});
|
|
1510
|
-
}
|
|
1511
1498
|
/**
|
|
1512
|
-
*
|
|
1499
|
+
* Synchronously kill all stdio MCP server child processes.
|
|
1500
|
+
* Must be called by the owner (e.g. transport/command layer) during shutdown.
|
|
1513
1501
|
*/
|
|
1514
|
-
|
|
1502
|
+
cleanupChildProcesses() {
|
|
1515
1503
|
for (const [serverName, client] of this.clients) try {
|
|
1516
1504
|
const childProcess = client["childProcess"];
|
|
1517
1505
|
if (childProcess && !childProcess.killed) {
|
|
@@ -2779,7 +2767,7 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
|
|
|
2779
2767
|
|
|
2780
2768
|
//#endregion
|
|
2781
2769
|
//#region package.json
|
|
2782
|
-
var version = "0.3.
|
|
2770
|
+
var version = "0.3.15";
|
|
2783
2771
|
|
|
2784
2772
|
//#endregion
|
|
2785
2773
|
//#region src/server/index.ts
|
|
@@ -2790,6 +2778,7 @@ var version = "0.3.13";
|
|
|
2790
2778
|
* - Factory pattern for server creation
|
|
2791
2779
|
* - Tool registration pattern
|
|
2792
2780
|
* - Dependency injection for services
|
|
2781
|
+
* - Shared services pattern for multi-session HTTP transport
|
|
2793
2782
|
*
|
|
2794
2783
|
* CODING STANDARDS:
|
|
2795
2784
|
* - Register all tools, resources, and prompts here
|
|
@@ -2893,7 +2882,13 @@ function buildProxyInstructions(serverDefinitions, mode, includeSkillsTool) {
|
|
|
2893
2882
|
"Use describe_tools to inspect capabilities and use_tool to execute them."
|
|
2894
2883
|
].join("\n\n");
|
|
2895
2884
|
}
|
|
2896
|
-
|
|
2885
|
+
/**
|
|
2886
|
+
* Initialize shared services and tools once for use across multiple sessions.
|
|
2887
|
+
* Use with createSessionServer() for HTTP transport where multiple agents
|
|
2888
|
+
* connect concurrently. This avoids duplicating downstream connections,
|
|
2889
|
+
* file watchers, caches, and tool instances per session.
|
|
2890
|
+
*/
|
|
2891
|
+
async function initializeSharedServices(options) {
|
|
2897
2892
|
const clientManager = new McpClientManagerService();
|
|
2898
2893
|
let configSkills;
|
|
2899
2894
|
let configId;
|
|
@@ -2966,13 +2961,47 @@ async function createServer(options) {
|
|
|
2966
2961
|
definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
|
|
2967
2962
|
}
|
|
2968
2963
|
else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
|
|
2964
|
+
const proxyMode = options?.proxyMode || "meta";
|
|
2969
2965
|
const describeTools = new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService);
|
|
2970
|
-
const
|
|
2966
|
+
const useTool = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
|
|
2971
2967
|
const searchListTools = new SearchListToolsTool(clientManager, definitionsCacheService);
|
|
2972
2968
|
toolsRef.describeTools = describeTools;
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2969
|
+
if (skillService) skillService.startWatching().catch((error) => {
|
|
2970
|
+
console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2971
|
+
});
|
|
2972
|
+
if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
|
|
2973
|
+
configPath: options.configFilePath,
|
|
2974
|
+
configHash,
|
|
2975
|
+
oneMcpVersion: version,
|
|
2976
|
+
serverId
|
|
2977
|
+
}).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
|
|
2978
|
+
console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
|
|
2979
|
+
}).catch((error) => {
|
|
2980
|
+
console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2981
|
+
});
|
|
2982
|
+
const dispose = async () => {
|
|
2983
|
+
await clientManager.disconnectAll();
|
|
2984
|
+
if (skillService) skillService.stopWatching();
|
|
2985
|
+
};
|
|
2986
|
+
return {
|
|
2987
|
+
clientManager,
|
|
2988
|
+
definitionsCacheService,
|
|
2989
|
+
skillService,
|
|
2990
|
+
describeTools,
|
|
2991
|
+
useTool,
|
|
2992
|
+
searchListTools,
|
|
2993
|
+
serverId,
|
|
2994
|
+
proxyMode,
|
|
2995
|
+
dispose
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Create a lightweight per-session MCP Server instance that delegates
|
|
3000
|
+
* to shared services and tools. Use with initializeSharedServices()
|
|
3001
|
+
* for multi-session HTTP transport.
|
|
3002
|
+
*/
|
|
3003
|
+
async function createSessionServer(shared) {
|
|
3004
|
+
const { clientManager, definitionsCacheService, skillService, describeTools, useTool: useToolWithCache, searchListTools, serverId, proxyMode } = shared;
|
|
2976
3005
|
const server = new Server({
|
|
2977
3006
|
name: "@agiflowai/one-mcp",
|
|
2978
3007
|
version: "0.1.0"
|
|
@@ -2982,10 +3011,7 @@ async function createServer(options) {
|
|
|
2982
3011
|
resources: {},
|
|
2983
3012
|
prompts: {}
|
|
2984
3013
|
},
|
|
2985
|
-
instructions: buildProxyInstructions(
|
|
2986
|
-
});
|
|
2987
|
-
if (skillService) skillService.startWatching().catch((error) => {
|
|
2988
|
-
console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3014
|
+
instructions: buildProxyInstructions(await definitionsCacheService.getServerDefinitions(), proxyMode, await hasAnySkills(definitionsCacheService, skillService))
|
|
2989
3015
|
});
|
|
2990
3016
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: proxyMode === "flat" ? await (async () => {
|
|
2991
3017
|
const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
|
|
@@ -3022,14 +3048,14 @@ async function createServer(options) {
|
|
|
3022
3048
|
throw new Error(`Unknown tool: ${name}`);
|
|
3023
3049
|
});
|
|
3024
3050
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
3025
|
-
const
|
|
3051
|
+
const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
|
|
3026
3052
|
const resourceToServers = /* @__PURE__ */ new Map();
|
|
3027
|
-
for (const serverDefinition of
|
|
3053
|
+
for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
|
|
3028
3054
|
if (!resourceToServers.has(resource.uri)) resourceToServers.set(resource.uri, []);
|
|
3029
3055
|
resourceToServers.get(resource.uri)?.push(serverDefinition.serverName);
|
|
3030
3056
|
}
|
|
3031
3057
|
const resources = [];
|
|
3032
|
-
for (const serverDefinition of
|
|
3058
|
+
for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
|
|
3033
3059
|
const hasClash = (resourceToServers.get(resource.uri) || []).length > 1;
|
|
3034
3060
|
resources.push({
|
|
3035
3061
|
...resource,
|
|
@@ -3048,10 +3074,10 @@ async function createServer(options) {
|
|
|
3048
3074
|
return await (await clientManager.ensureConnected(matchingServers[0])).readResource(actualUri);
|
|
3049
3075
|
});
|
|
3050
3076
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
3051
|
-
const
|
|
3077
|
+
const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
|
|
3052
3078
|
const promptToServers = /* @__PURE__ */ new Map();
|
|
3053
3079
|
const serverPromptsMap = /* @__PURE__ */ new Map();
|
|
3054
|
-
for (const serverDefinition of
|
|
3080
|
+
for (const serverDefinition of currentServerDefinitions) {
|
|
3055
3081
|
serverPromptsMap.set(serverDefinition.serverName, serverDefinition.prompts);
|
|
3056
3082
|
for (const prompt of serverDefinition.prompts) {
|
|
3057
3083
|
if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
|
|
@@ -3059,7 +3085,7 @@ async function createServer(options) {
|
|
|
3059
3085
|
}
|
|
3060
3086
|
}
|
|
3061
3087
|
const aggregatedPrompts = [];
|
|
3062
|
-
for (const serverDefinition of
|
|
3088
|
+
for (const serverDefinition of currentServerDefinitions) {
|
|
3063
3089
|
const prompts = serverPromptsMap.get(serverDefinition.serverName) || [];
|
|
3064
3090
|
for (const prompt of prompts) {
|
|
3065
3091
|
const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
|
|
@@ -3074,29 +3100,26 @@ async function createServer(options) {
|
|
|
3074
3100
|
});
|
|
3075
3101
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
3076
3102
|
const { name, arguments: args } = request.params;
|
|
3077
|
-
const
|
|
3103
|
+
const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
|
|
3078
3104
|
const { serverName, actualToolName: actualPromptName } = parseToolName(name);
|
|
3079
3105
|
if (serverName) return await (await clientManager.ensureConnected(serverName)).getPrompt(actualPromptName, args);
|
|
3080
3106
|
const serversWithPrompt = [];
|
|
3081
|
-
for (const serverDefinition of
|
|
3107
|
+
for (const serverDefinition of currentServerDefinitions) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
|
|
3082
3108
|
if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
|
|
3083
3109
|
if (serversWithPrompt.length > 1) throw new Error(`Prompt "${name}" exists on multiple servers: ${serversWithPrompt.join(", ")}. Use the prefixed format (e.g., "${serversWithPrompt[0]}__${name}") to specify which server to use.`);
|
|
3084
3110
|
const client = clientManager.getClient(serversWithPrompt[0]);
|
|
3085
3111
|
if (!client) return await (await clientManager.ensureConnected(serversWithPrompt[0])).getPrompt(name, args);
|
|
3086
3112
|
return await client.getPrompt(name, args);
|
|
3087
3113
|
});
|
|
3088
|
-
if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
|
|
3089
|
-
configPath: options.configFilePath,
|
|
3090
|
-
configHash,
|
|
3091
|
-
oneMcpVersion: version,
|
|
3092
|
-
serverId
|
|
3093
|
-
}).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
|
|
3094
|
-
console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
|
|
3095
|
-
}).catch((error) => {
|
|
3096
|
-
console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3097
|
-
});
|
|
3098
3114
|
return server;
|
|
3099
3115
|
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Create a single MCP server instance (backward-compatible wrapper).
|
|
3118
|
+
* For multi-session HTTP transport, use initializeSharedServices() + createSessionServer() instead.
|
|
3119
|
+
*/
|
|
3120
|
+
async function createServer(options) {
|
|
3121
|
+
return createSessionServer(await initializeSharedServices(options));
|
|
3122
|
+
}
|
|
3100
3123
|
|
|
3101
3124
|
//#endregion
|
|
3102
3125
|
//#region src/types/index.ts
|
|
@@ -3147,16 +3170,26 @@ var HttpFullSessionManager = class {
|
|
|
3147
3170
|
server
|
|
3148
3171
|
});
|
|
3149
3172
|
}
|
|
3150
|
-
deleteSession(sessionId) {
|
|
3173
|
+
async deleteSession(sessionId) {
|
|
3151
3174
|
const session = this.sessions.get(sessionId);
|
|
3152
|
-
if (session)
|
|
3175
|
+
if (session) try {
|
|
3176
|
+
await session.server.close();
|
|
3177
|
+
} catch (error) {
|
|
3178
|
+
throw new Error(`Failed to close MCP server for session '${sessionId}': ${toErrorMessage$2(error)}`);
|
|
3179
|
+
}
|
|
3153
3180
|
this.sessions.delete(sessionId);
|
|
3154
3181
|
}
|
|
3155
3182
|
hasSession(sessionId) {
|
|
3156
3183
|
return this.sessions.has(sessionId);
|
|
3157
3184
|
}
|
|
3158
|
-
clear() {
|
|
3159
|
-
|
|
3185
|
+
async clear() {
|
|
3186
|
+
try {
|
|
3187
|
+
await Promise.all(Array.from(this.sessions.values()).map(async (session) => {
|
|
3188
|
+
await session.server.close();
|
|
3189
|
+
}));
|
|
3190
|
+
} catch (error) {
|
|
3191
|
+
throw new Error(`Failed to clear sessions: ${toErrorMessage$2(error)}`);
|
|
3192
|
+
}
|
|
3160
3193
|
this.sessions.clear();
|
|
3161
3194
|
}
|
|
3162
3195
|
};
|
|
@@ -3197,7 +3230,7 @@ var HttpTransportHandler = class {
|
|
|
3197
3230
|
adminOptions;
|
|
3198
3231
|
adminRateLimiter = new AdminRateLimiter();
|
|
3199
3232
|
constructor(serverFactory, config, adminOptions) {
|
|
3200
|
-
this.serverFactory =
|
|
3233
|
+
this.serverFactory = serverFactory;
|
|
3201
3234
|
this.app = express();
|
|
3202
3235
|
this.sessionManager = new HttpFullSessionManager();
|
|
3203
3236
|
this.config = {
|
|
@@ -3320,7 +3353,7 @@ var HttpTransportHandler = class {
|
|
|
3320
3353
|
let transport;
|
|
3321
3354
|
if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId).transport;
|
|
3322
3355
|
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
3323
|
-
const mcpServer = this.serverFactory();
|
|
3356
|
+
const mcpServer = await this.serverFactory();
|
|
3324
3357
|
transport = new StreamableHTTPServerTransport({
|
|
3325
3358
|
sessionIdGenerator: () => randomUUID(),
|
|
3326
3359
|
enableJsonResponse: true,
|
|
@@ -3328,8 +3361,12 @@ var HttpTransportHandler = class {
|
|
|
3328
3361
|
this.sessionManager.setSession(initializedSessionId, transport, mcpServer);
|
|
3329
3362
|
}
|
|
3330
3363
|
});
|
|
3331
|
-
transport.onclose = () => {
|
|
3332
|
-
if (transport.sessionId)
|
|
3364
|
+
transport.onclose = async () => {
|
|
3365
|
+
if (transport.sessionId) try {
|
|
3366
|
+
await this.sessionManager.deleteSession(transport.sessionId);
|
|
3367
|
+
} catch (error) {
|
|
3368
|
+
console.error(`Failed to clean up session '${transport.sessionId}': ${toErrorMessage$2(error)}`);
|
|
3369
|
+
}
|
|
3333
3370
|
};
|
|
3334
3371
|
try {
|
|
3335
3372
|
await mcpServer.connect(transport);
|
|
@@ -3378,7 +3415,7 @@ var HttpTransportHandler = class {
|
|
|
3378
3415
|
} catch (error) {
|
|
3379
3416
|
throw new Error(`Failed handling MCP DELETE request for session '${sessionId}': ${toErrorMessage$2(error)}`);
|
|
3380
3417
|
}
|
|
3381
|
-
this.sessionManager.deleteSession(sessionId);
|
|
3418
|
+
await this.sessionManager.deleteSession(sessionId);
|
|
3382
3419
|
}
|
|
3383
3420
|
async start() {
|
|
3384
3421
|
try {
|
|
@@ -3401,7 +3438,11 @@ var HttpTransportHandler = class {
|
|
|
3401
3438
|
}
|
|
3402
3439
|
async stop() {
|
|
3403
3440
|
if (!this.server) return;
|
|
3404
|
-
|
|
3441
|
+
try {
|
|
3442
|
+
await this.sessionManager.clear();
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
throw new Error(`Failed to clear sessions during HTTP transport stop: ${toErrorMessage$2(error)}`);
|
|
3445
|
+
}
|
|
3405
3446
|
const closeServer = promisify(this.server.close.bind(this.server));
|
|
3406
3447
|
try {
|
|
3407
3448
|
await closeServer();
|
|
@@ -3738,11 +3779,11 @@ var StdioHttpTransportHandler = class {
|
|
|
3738
3779
|
*/
|
|
3739
3780
|
const RUNTIME_DIR_NAME = "runtimes";
|
|
3740
3781
|
const RUNTIME_FILE_SUFFIX = ".runtime.json";
|
|
3741
|
-
function isObject
|
|
3782
|
+
function isObject(value) {
|
|
3742
3783
|
return typeof value === "object" && value !== null;
|
|
3743
3784
|
}
|
|
3744
3785
|
function isRuntimeStateRecord(value) {
|
|
3745
|
-
if (!isObject
|
|
3786
|
+
if (!isObject(value)) return false;
|
|
3746
3787
|
return typeof value.serverId === "string" && typeof value.host === "string" && typeof value.port === "number" && value.transport === "http" && typeof value.shutdownToken === "string" && typeof value.startedAt === "string" && typeof value.pid === "number" && (value.configPath === void 0 || typeof value.configPath === "string");
|
|
3747
3788
|
}
|
|
3748
3789
|
function toErrorMessage$1(error) {
|
|
@@ -3792,7 +3833,7 @@ var RuntimeStateService = class RuntimeStateService {
|
|
|
3792
3833
|
const parsed = JSON.parse(content);
|
|
3793
3834
|
return isRuntimeStateRecord(parsed) ? parsed : null;
|
|
3794
3835
|
} catch (error) {
|
|
3795
|
-
if (isObject
|
|
3836
|
+
if (isObject(error) && "code" in error && error.code === "ENOENT") return null;
|
|
3796
3837
|
throw new Error(`Failed to read runtime state for server '${serverId}' from '${filePath}': ${toErrorMessage$1(error)}`);
|
|
3797
3838
|
}
|
|
3798
3839
|
}
|
|
@@ -3813,7 +3854,7 @@ var RuntimeStateService = class RuntimeStateService {
|
|
|
3813
3854
|
}
|
|
3814
3855
|
}))).filter((record) => record !== null);
|
|
3815
3856
|
} catch (error) {
|
|
3816
|
-
if (isObject
|
|
3857
|
+
if (isObject(error) && "code" in error && error.code === "ENOENT") return [];
|
|
3817
3858
|
throw new Error(`Failed to list runtime states from '${this.runtimeDir}': ${toErrorMessage$1(error)}`);
|
|
3818
3859
|
}
|
|
3819
3860
|
}
|
|
@@ -3828,18 +3869,105 @@ var RuntimeStateService = class RuntimeStateService {
|
|
|
3828
3869
|
};
|
|
3829
3870
|
|
|
3830
3871
|
//#endregion
|
|
3831
|
-
//#region src/services/StopServerService.ts
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3872
|
+
//#region src/services/StopServerService/constants.ts
|
|
3873
|
+
/**
|
|
3874
|
+
* StopServerService constants.
|
|
3875
|
+
*/
|
|
3876
|
+
/** Maximum time in milliseconds to wait for a shutdown to complete. */
|
|
3877
|
+
const DEFAULT_STOP_TIMEOUT_MS = 5e3;
|
|
3878
|
+
/** Minimum timeout in milliseconds for individual health check requests. */
|
|
3879
|
+
const HEALTH_REQUEST_TIMEOUT_FLOOR_MS = 250;
|
|
3880
|
+
/** Delay in milliseconds between shutdown polling attempts. */
|
|
3881
|
+
const SHUTDOWN_POLL_INTERVAL_MS = 200;
|
|
3882
|
+
/** Path for the runtime health check endpoint. */
|
|
3883
|
+
const HEALTH_CHECK_PATH = "/health";
|
|
3884
|
+
/** Path for the authenticated admin shutdown endpoint. */
|
|
3885
|
+
const ADMIN_SHUTDOWN_PATH = "/admin/shutdown";
|
|
3886
|
+
/** HTTP GET method identifier. */
|
|
3887
|
+
const HTTP_METHOD_GET = "GET";
|
|
3888
|
+
/** HTTP POST method identifier. */
|
|
3889
|
+
const HTTP_METHOD_POST = "POST";
|
|
3890
|
+
/** HTTP header name for bearer token authorization. */
|
|
3891
|
+
const AUTHORIZATION_HEADER_NAME = "Authorization";
|
|
3892
|
+
/** Prefix for bearer token values in the Authorization header. */
|
|
3893
|
+
const BEARER_TOKEN_PREFIX = "Bearer ";
|
|
3894
|
+
/** HTTP protocol scheme prefix for URL construction. */
|
|
3895
|
+
const HTTP_PROTOCOL = "http://";
|
|
3896
|
+
/** Separator between host and port in URL construction. */
|
|
3897
|
+
const URL_PORT_SEPARATOR = ":";
|
|
3898
|
+
/** Loopback hostname. */
|
|
3899
|
+
const LOOPBACK_HOST_LOCALHOST = "localhost";
|
|
3900
|
+
/** IPv4 loopback address. */
|
|
3901
|
+
const LOOPBACK_HOST_IPV4 = "127.0.0.1";
|
|
3902
|
+
/** IPv6 loopback address. */
|
|
3903
|
+
const LOOPBACK_HOST_IPV6 = "::1";
|
|
3904
|
+
/** Hosts that are safe to send admin requests to (loopback only). */
|
|
3905
|
+
const ALLOWED_HOSTS = new Set([
|
|
3906
|
+
LOOPBACK_HOST_LOCALHOST,
|
|
3907
|
+
LOOPBACK_HOST_IPV4,
|
|
3908
|
+
LOOPBACK_HOST_IPV6
|
|
3909
|
+
]);
|
|
3910
|
+
/** Expected status value in a healthy runtime response. */
|
|
3911
|
+
const HEALTH_STATUS_OK = "ok";
|
|
3912
|
+
/** Expected transport value in a healthy runtime response. */
|
|
3913
|
+
const HEALTH_TRANSPORT_HTTP = "http";
|
|
3914
|
+
/** Property key for status field in health responses. */
|
|
3915
|
+
const KEY_STATUS = "status";
|
|
3916
|
+
/** Property key for transport field in health responses. */
|
|
3917
|
+
const KEY_TRANSPORT = "transport";
|
|
3918
|
+
/** Property key for serverId field in runtime responses. */
|
|
3919
|
+
const KEY_SERVER_ID = "serverId";
|
|
3920
|
+
/** Property key for ok field in shutdown responses. */
|
|
3921
|
+
const KEY_OK = "ok";
|
|
3922
|
+
/** Property key for message field in shutdown responses. */
|
|
3923
|
+
const KEY_MESSAGE = "message";
|
|
3924
|
+
|
|
3925
|
+
//#endregion
|
|
3926
|
+
//#region src/services/StopServerService/types.ts
|
|
3927
|
+
/**
|
|
3928
|
+
* Safely cast a non-null object to a string-keyed record for property access.
|
|
3929
|
+
* @param value - Object value already verified as non-null
|
|
3930
|
+
* @returns The same value typed as a record
|
|
3931
|
+
*/
|
|
3932
|
+
function toRecord(value) {
|
|
3933
|
+
return value;
|
|
3837
3934
|
}
|
|
3935
|
+
/**
|
|
3936
|
+
* Type guard for health responses.
|
|
3937
|
+
* @param value - Candidate payload to validate
|
|
3938
|
+
* @returns True when payload matches health response shape
|
|
3939
|
+
*/
|
|
3838
3940
|
function isHealthResponse(value) {
|
|
3839
|
-
|
|
3941
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3942
|
+
const record = toRecord(value);
|
|
3943
|
+
return KEY_STATUS in record && record[KEY_STATUS] === HEALTH_STATUS_OK && KEY_TRANSPORT in record && record[KEY_TRANSPORT] === HEALTH_TRANSPORT_HTTP && (!(KEY_SERVER_ID in record) || record[KEY_SERVER_ID] === void 0 || typeof record[KEY_SERVER_ID] === "string");
|
|
3840
3944
|
}
|
|
3945
|
+
/**
|
|
3946
|
+
* Type guard for shutdown responses.
|
|
3947
|
+
* @param value - Candidate payload to validate
|
|
3948
|
+
* @returns True when payload matches shutdown response shape
|
|
3949
|
+
*/
|
|
3841
3950
|
function isShutdownResponse(value) {
|
|
3842
|
-
|
|
3951
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3952
|
+
const record = toRecord(value);
|
|
3953
|
+
return KEY_OK in record && typeof record[KEY_OK] === "boolean" && KEY_MESSAGE in record && typeof record[KEY_MESSAGE] === "string" && (!(KEY_SERVER_ID in record) || record[KEY_SERVER_ID] === void 0 || typeof record[KEY_SERVER_ID] === "string");
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
//#endregion
|
|
3957
|
+
//#region src/services/StopServerService/StopServerService.ts
|
|
3958
|
+
/**
|
|
3959
|
+
* Format runtime endpoint URL after validating the host is a loopback address.
|
|
3960
|
+
* Rejects non-loopback hosts to prevent SSRF via tampered runtime state files.
|
|
3961
|
+
* @param runtime - Runtime record to format
|
|
3962
|
+
* @param path - Request path to append
|
|
3963
|
+
* @returns Full runtime URL
|
|
3964
|
+
*/
|
|
3965
|
+
function buildRuntimeUrl(runtime, path) {
|
|
3966
|
+
if (!ALLOWED_HOSTS.has(runtime.host)) throw new Error(`Refusing to connect to non-loopback host '${runtime.host}'. Only ${Array.from(ALLOWED_HOSTS).join(", ")} are allowed.`);
|
|
3967
|
+
return `${HTTP_PROTOCOL}${runtime.host}${URL_PORT_SEPARATOR}${runtime.port}${path}`;
|
|
3968
|
+
}
|
|
3969
|
+
function toErrorMessage(error) {
|
|
3970
|
+
return error instanceof Error ? error.message : String(error);
|
|
3843
3971
|
}
|
|
3844
3972
|
function sleep(delayMs) {
|
|
3845
3973
|
return new Promise((resolve$1) => {
|
|
@@ -3860,7 +3988,7 @@ var StopServerService = class {
|
|
|
3860
3988
|
* @returns Stop result payload
|
|
3861
3989
|
*/
|
|
3862
3990
|
async stop(request) {
|
|
3863
|
-
const timeoutMs = request.timeoutMs ??
|
|
3991
|
+
const timeoutMs = request.timeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
|
|
3864
3992
|
const runtime = await this.resolveRuntime(request);
|
|
3865
3993
|
const health = await this.fetchHealth(runtime, timeoutMs);
|
|
3866
3994
|
if (!health.reachable) {
|
|
@@ -3906,7 +4034,7 @@ var StopServerService = class {
|
|
|
3906
4034
|
*/
|
|
3907
4035
|
async fetchHealth(runtime, timeoutMs) {
|
|
3908
4036
|
try {
|
|
3909
|
-
const response = await this.fetchWithTimeout(
|
|
4037
|
+
const response = await this.fetchWithTimeout(buildRuntimeUrl(runtime, HEALTH_CHECK_PATH), { method: HTTP_METHOD_GET }, timeoutMs);
|
|
3910
4038
|
if (!response.ok) return { reachable: false };
|
|
3911
4039
|
const payload = await response.json();
|
|
3912
4040
|
if (!isHealthResponse(payload)) throw new Error("Received invalid health response payload.");
|
|
@@ -3926,9 +4054,9 @@ var StopServerService = class {
|
|
|
3926
4054
|
* @returns Parsed shutdown response payload
|
|
3927
4055
|
*/
|
|
3928
4056
|
async requestShutdown(runtime, shutdownToken, timeoutMs) {
|
|
3929
|
-
const response = await this.fetchWithTimeout(
|
|
3930
|
-
method:
|
|
3931
|
-
headers: {
|
|
4057
|
+
const response = await this.fetchWithTimeout(buildRuntimeUrl(runtime, ADMIN_SHUTDOWN_PATH), {
|
|
4058
|
+
method: HTTP_METHOD_POST,
|
|
4059
|
+
headers: { [AUTHORIZATION_HEADER_NAME]: `${BEARER_TOKEN_PREFIX}${shutdownToken}` }
|
|
3932
4060
|
}, timeoutMs);
|
|
3933
4061
|
const payload = await response.json();
|
|
3934
4062
|
if (!isShutdownResponse(payload)) throw new Error("Received invalid shutdown response payload.");
|
|
@@ -3944,8 +4072,8 @@ var StopServerService = class {
|
|
|
3944
4072
|
async waitForShutdown(runtime, timeoutMs) {
|
|
3945
4073
|
const deadline = Date.now() + timeoutMs;
|
|
3946
4074
|
while (Date.now() < deadline) {
|
|
3947
|
-
if (!(await this.fetchHealth(runtime, Math.max(
|
|
3948
|
-
await sleep(
|
|
4075
|
+
if (!(await this.fetchHealth(runtime, Math.max(HEALTH_REQUEST_TIMEOUT_FLOOR_MS, deadline - Date.now()))).reachable) return;
|
|
4076
|
+
await sleep(SHUTDOWN_POLL_INTERVAL_MS);
|
|
3949
4077
|
}
|
|
3950
4078
|
throw new Error(`Timed out waiting for runtime '${runtime.serverId}' to stop at http://${runtime.host}:${runtime.port}.`);
|
|
3951
4079
|
}
|
|
@@ -3975,4 +4103,4 @@ var StopServerService = class {
|
|
|
3975
4103
|
};
|
|
3976
4104
|
|
|
3977
4105
|
//#endregion
|
|
3978
|
-
export {
|
|
4106
|
+
export { DefinitionsCacheService as _, SseTransportHandler as a, ConfigFetcherService as b, createServer as c, version as d, UseToolTool as f, McpClientManagerService as g, SkillService as h, StdioTransportHandler as i, createSessionServer as l, DescribeToolsTool as m, RuntimeStateService as n, HttpTransportHandler as o, SearchListToolsTool as p, StdioHttpTransportHandler as r, TRANSPORT_MODE as s, StopServerService as t, initializeSharedServices as u, generateServerId as v, findConfigFile as y };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agiflowai/one-mcp",
|
|
3
3
|
"description": "One MCP server package",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.16",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"js-yaml": "^4.1.0",
|
|
28
28
|
"liquidjs": "^10.21.0",
|
|
29
29
|
"zod": "^3.24.1",
|
|
30
|
-
"@agiflowai/aicode-utils": "1.0.
|
|
30
|
+
"@agiflowai/aicode-utils": "1.0.20"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/express": "^5.0.0",
|