@mcpc-tech/core 0.3.37 → 0.3.39

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/index.cjs CHANGED
@@ -13969,6 +13969,147 @@ var ExperimentalServerTasks = class {
13969
13969
  requestStream(request, resultSchema, options) {
13970
13970
  return this._server.requestStream(request, resultSchema, options);
13971
13971
  }
13972
+ /**
13973
+ * Sends a sampling request and returns an AsyncGenerator that yields response messages.
13974
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
13975
+ *
13976
+ * For task-augmented requests, yields 'taskCreated' and 'taskStatus' messages
13977
+ * before the final result.
13978
+ *
13979
+ * @example
13980
+ * ```typescript
13981
+ * const stream = server.experimental.tasks.createMessageStream({
13982
+ * messages: [{ role: 'user', content: { type: 'text', text: 'Hello' } }],
13983
+ * maxTokens: 100
13984
+ * }, {
13985
+ * onprogress: (progress) => {
13986
+ * // Handle streaming tokens via progress notifications
13987
+ * console.log('Progress:', progress.message);
13988
+ * }
13989
+ * });
13990
+ *
13991
+ * for await (const message of stream) {
13992
+ * switch (message.type) {
13993
+ * case 'taskCreated':
13994
+ * console.log('Task created:', message.task.taskId);
13995
+ * break;
13996
+ * case 'taskStatus':
13997
+ * console.log('Task status:', message.task.status);
13998
+ * break;
13999
+ * case 'result':
14000
+ * console.log('Final result:', message.result);
14001
+ * break;
14002
+ * case 'error':
14003
+ * console.error('Error:', message.error);
14004
+ * break;
14005
+ * }
14006
+ * }
14007
+ * ```
14008
+ *
14009
+ * @param params - The sampling request parameters
14010
+ * @param options - Optional request options (timeout, signal, task creation params, onprogress, etc.)
14011
+ * @returns AsyncGenerator that yields ResponseMessage objects
14012
+ *
14013
+ * @experimental
14014
+ */
14015
+ createMessageStream(params, options) {
14016
+ const clientCapabilities = this._server.getClientCapabilities();
14017
+ if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
14018
+ throw new Error("Client does not support sampling tools capability.");
14019
+ }
14020
+ if (params.messages.length > 0) {
14021
+ const lastMessage = params.messages[params.messages.length - 1];
14022
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
14023
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
14024
+ const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
14025
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
14026
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
14027
+ if (hasToolResults) {
14028
+ if (lastContent.some((c) => c.type !== "tool_result")) {
14029
+ throw new Error("The last message must contain only tool_result content if any is present");
14030
+ }
14031
+ if (!hasPreviousToolUse) {
14032
+ throw new Error("tool_result blocks are not matching any tool_use from the previous message");
14033
+ }
14034
+ }
14035
+ if (hasPreviousToolUse) {
14036
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
14037
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
14038
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
14039
+ throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
14040
+ }
14041
+ }
14042
+ }
14043
+ return this.requestStream({
14044
+ method: "sampling/createMessage",
14045
+ params
14046
+ }, CreateMessageResultSchema, options);
14047
+ }
14048
+ /**
14049
+ * Sends an elicitation request and returns an AsyncGenerator that yields response messages.
14050
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
14051
+ *
14052
+ * For task-augmented requests (especially URL-based elicitation), yields 'taskCreated'
14053
+ * and 'taskStatus' messages before the final result.
14054
+ *
14055
+ * @example
14056
+ * ```typescript
14057
+ * const stream = server.experimental.tasks.elicitInputStream({
14058
+ * mode: 'url',
14059
+ * message: 'Please authenticate',
14060
+ * elicitationId: 'auth-123',
14061
+ * url: 'https://example.com/auth'
14062
+ * }, {
14063
+ * task: { ttl: 300000 } // Task-augmented for long-running auth flow
14064
+ * });
14065
+ *
14066
+ * for await (const message of stream) {
14067
+ * switch (message.type) {
14068
+ * case 'taskCreated':
14069
+ * console.log('Task created:', message.task.taskId);
14070
+ * break;
14071
+ * case 'taskStatus':
14072
+ * console.log('Task status:', message.task.status);
14073
+ * break;
14074
+ * case 'result':
14075
+ * console.log('User action:', message.result.action);
14076
+ * break;
14077
+ * case 'error':
14078
+ * console.error('Error:', message.error);
14079
+ * break;
14080
+ * }
14081
+ * }
14082
+ * ```
14083
+ *
14084
+ * @param params - The elicitation request parameters
14085
+ * @param options - Optional request options (timeout, signal, task creation params, etc.)
14086
+ * @returns AsyncGenerator that yields ResponseMessage objects
14087
+ *
14088
+ * @experimental
14089
+ */
14090
+ elicitInputStream(params, options) {
14091
+ const clientCapabilities = this._server.getClientCapabilities();
14092
+ const mode = params.mode ?? "form";
14093
+ switch (mode) {
14094
+ case "url": {
14095
+ if (!clientCapabilities?.elicitation?.url) {
14096
+ throw new Error("Client does not support url elicitation.");
14097
+ }
14098
+ break;
14099
+ }
14100
+ case "form": {
14101
+ if (!clientCapabilities?.elicitation?.form) {
14102
+ throw new Error("Client does not support form elicitation.");
14103
+ }
14104
+ break;
14105
+ }
14106
+ }
14107
+ const normalizedParams = mode === "form" && params.mode === void 0 ? { ...params, mode: "form" } : params;
14108
+ return this.requestStream({
14109
+ method: "elicitation/create",
14110
+ params: normalizedParams
14111
+ }, ElicitResultSchema, options);
14112
+ }
13972
14113
  /**
13973
14114
  * Gets the current status of a task.
13974
14115
  *
@@ -16224,22 +16365,45 @@ async function auth(provider, options) {
16224
16365
  }
16225
16366
  }
16226
16367
  async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
16368
+ const cachedState = await provider.discoveryState?.();
16227
16369
  let resourceMetadata;
16228
16370
  let authorizationServerUrl;
16229
- try {
16230
- resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl }, fetchFn);
16231
- if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
16232
- authorizationServerUrl = resourceMetadata.authorization_servers[0];
16371
+ let metadata;
16372
+ let effectiveResourceMetadataUrl = resourceMetadataUrl;
16373
+ if (!effectiveResourceMetadataUrl && cachedState?.resourceMetadataUrl) {
16374
+ effectiveResourceMetadataUrl = new URL(cachedState.resourceMetadataUrl);
16375
+ }
16376
+ if (cachedState?.authorizationServerUrl) {
16377
+ authorizationServerUrl = cachedState.authorizationServerUrl;
16378
+ resourceMetadata = cachedState.resourceMetadata;
16379
+ metadata = cachedState.authorizationServerMetadata ?? await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn });
16380
+ if (!resourceMetadata) {
16381
+ try {
16382
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl }, fetchFn);
16383
+ } catch {
16384
+ }
16233
16385
  }
16234
- } catch {
16235
- }
16236
- if (!authorizationServerUrl) {
16237
- authorizationServerUrl = new URL("/", serverUrl);
16386
+ if (metadata !== cachedState.authorizationServerMetadata || resourceMetadata !== cachedState.resourceMetadata) {
16387
+ await provider.saveDiscoveryState?.({
16388
+ authorizationServerUrl: String(authorizationServerUrl),
16389
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
16390
+ resourceMetadata,
16391
+ authorizationServerMetadata: metadata
16392
+ });
16393
+ }
16394
+ } else {
16395
+ const serverInfo = await discoverOAuthServerInfo(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl, fetchFn });
16396
+ authorizationServerUrl = serverInfo.authorizationServerUrl;
16397
+ metadata = serverInfo.authorizationServerMetadata;
16398
+ resourceMetadata = serverInfo.resourceMetadata;
16399
+ await provider.saveDiscoveryState?.({
16400
+ authorizationServerUrl: String(authorizationServerUrl),
16401
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
16402
+ resourceMetadata,
16403
+ authorizationServerMetadata: metadata
16404
+ });
16238
16405
  }
16239
16406
  const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
16240
- const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
16241
- fetchFn
16242
- });
16243
16407
  let clientInformation = await Promise.resolve(provider.clientInformation());
16244
16408
  if (!clientInformation) {
16245
16409
  if (authorizationCode !== void 0) {
@@ -16494,6 +16658,26 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
16494
16658
  }
16495
16659
  return void 0;
16496
16660
  }
16661
+ async function discoverOAuthServerInfo(serverUrl, opts) {
16662
+ let resourceMetadata;
16663
+ let authorizationServerUrl;
16664
+ try {
16665
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: opts?.resourceMetadataUrl }, opts?.fetchFn);
16666
+ if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
16667
+ authorizationServerUrl = resourceMetadata.authorization_servers[0];
16668
+ }
16669
+ } catch {
16670
+ }
16671
+ if (!authorizationServerUrl) {
16672
+ authorizationServerUrl = String(new URL("/", serverUrl));
16673
+ }
16674
+ const authorizationServerMetadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn: opts?.fetchFn });
16675
+ return {
16676
+ authorizationServerUrl,
16677
+ authorizationServerMetadata,
16678
+ resourceMetadata
16679
+ };
16680
+ }
16497
16681
  async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
16498
16682
  let authorizationUrl;
16499
16683
  if (metadata) {
@@ -17391,18 +17575,6 @@ var cleanToolSchema = (schema) => {
17391
17575
  var import_node_process3 = require("node:process");
17392
17576
  var import_node_process4 = __toESM(require("node:process"), 1);
17393
17577
  var import_node_crypto = require("node:crypto");
17394
- var mcpClientPool = /* @__PURE__ */ new Map();
17395
- var mcpClientConnecting = /* @__PURE__ */ new Map();
17396
- var shortHash = (s) => (0, import_node_crypto.createHash)("sha256").update(s).digest("hex").slice(0, 8);
17397
- function defSignature(def) {
17398
- const defCopy = {
17399
- ...def
17400
- };
17401
- if (defCopy.transportType === "memory" || defCopy.transport) {
17402
- return `memory:${Date.now()}:${Math.random()}`;
17403
- }
17404
- return JSON.stringify(defCopy);
17405
- }
17406
17578
  function createTransport(def) {
17407
17579
  const defAny = def;
17408
17580
  const explicitType = defAny.transportType || defAny.type;
@@ -17450,90 +17622,43 @@ function createTransport(def) {
17450
17622
  }
17451
17623
  throw new Error(`Unsupported transport configuration: ${JSON.stringify(def)}`);
17452
17624
  }
17453
- async function getOrCreateMcpClient(defKey, def) {
17454
- const pooled = mcpClientPool.get(defKey);
17455
- if (pooled) {
17456
- pooled.refCount += 1;
17457
- return pooled.client;
17458
- }
17459
- const existingConnecting = mcpClientConnecting.get(defKey);
17460
- if (existingConnecting) {
17461
- const client = await existingConnecting;
17462
- const entry = mcpClientPool.get(defKey);
17463
- if (entry) entry.refCount += 1;
17464
- return client;
17465
- }
17466
- const transport = createTransport(def);
17467
- const connecting = (async () => {
17468
- const client = new Client({
17469
- name: `mcp_${shortHash(defSignature(def))}`,
17470
- version: "1.0.0"
17471
- });
17472
- await client.connect(transport, {
17473
- timeout: 6e4 * 10
17474
- });
17475
- return client;
17476
- })();
17477
- mcpClientConnecting.set(defKey, connecting);
17478
- try {
17479
- const client = await connecting;
17480
- mcpClientPool.set(defKey, {
17481
- client,
17482
- refCount: 1
17483
- });
17484
- return client;
17485
- } finally {
17486
- mcpClientConnecting.delete(defKey);
17625
+ function defSignature(def) {
17626
+ const defCopy = {
17627
+ ...def
17628
+ };
17629
+ if (defCopy.transportType === "memory" || defCopy.transport) {
17630
+ return `memory:${Date.now()}:${Math.random()}`;
17487
17631
  }
17632
+ return JSON.stringify(defCopy);
17488
17633
  }
17489
- async function releaseMcpClient(defKey) {
17490
- const entry = mcpClientPool.get(defKey);
17491
- if (!entry) return;
17492
- entry.refCount -= 1;
17493
- if (entry.refCount <= 0) {
17494
- mcpClientPool.delete(defKey);
17495
- try {
17496
- await entry.client.close();
17497
- } catch (err) {
17498
- console.error("Error closing MCP client:", err);
17499
- }
17500
- }
17634
+ var shortHash = (s) => (0, import_node_crypto.createHash)("sha256").update(s).digest("hex").slice(0, 8);
17635
+ async function createMcpClient(def) {
17636
+ const transport = createTransport(def);
17637
+ const client = new Client({
17638
+ name: `mcp_${shortHash(defSignature(def))}`,
17639
+ version: "1.0.0"
17640
+ });
17641
+ await client.connect(transport, {
17642
+ timeout: 6e4 * 10
17643
+ });
17644
+ return client;
17501
17645
  }
17502
- var cleanupAllPooledClients = async () => {
17503
- const entries = Array.from(mcpClientPool.entries());
17504
- mcpClientPool.clear();
17505
- await Promise.all(entries.map(async ([, { client }]) => {
17506
- try {
17507
- await client.close();
17508
- } catch (err) {
17509
- console.error("Error closing MCP client:", err);
17510
- }
17511
- }));
17512
- };
17513
- import_node_process4.default.once?.("exit", () => {
17514
- cleanupAllPooledClients();
17515
- });
17516
- import_node_process4.default.once?.("SIGINT", () => {
17517
- cleanupAllPooledClients().finally(() => import_node_process4.default.exit(0));
17518
- });
17519
17646
  async function composeMcpDepTools(mcpConfig, filterIn) {
17520
17647
  const allTools = {};
17521
17648
  const allClients = {};
17522
- const acquiredKeys = [];
17649
+ const clientsToClose = [];
17523
17650
  for (const [name, definition] of Object.entries(mcpConfig.mcpServers)) {
17524
17651
  const def = definition;
17525
17652
  if (def.disabled) continue;
17526
- const defKey = shortHash(defSignature(def));
17527
- const serverId = name;
17528
17653
  try {
17529
- const client = await getOrCreateMcpClient(defKey, def);
17530
- acquiredKeys.push(defKey);
17531
- allClients[serverId] = client;
17654
+ const client = await createMcpClient(def);
17655
+ clientsToClose.push(client);
17656
+ allClients[name] = client;
17532
17657
  const { tools } = await client.listTools();
17533
17658
  tools.forEach((tool2) => {
17534
17659
  const toolNameWithScope = `${name}.${tool2.name}`;
17535
17660
  const internalToolName = tool2.name;
17536
- const rawToolId = `${serverId}_${internalToolName}`;
17661
+ const rawToolId = `${name}_${internalToolName}`;
17537
17662
  const toolId = sanitizePropertyKey(rawToolId);
17538
17663
  if (filterIn && !filterIn({
17539
17664
  action: internalToolName,
@@ -17545,7 +17670,7 @@ async function composeMcpDepTools(mcpConfig, filterIn) {
17545
17670
  })) {
17546
17671
  return;
17547
17672
  }
17548
- const execute = (args) => allClients[serverId].callTool({
17673
+ const execute = (args) => allClients[name].callTool({
17549
17674
  name: internalToolName,
17550
17675
  arguments: args
17551
17676
  }, void 0, {
@@ -17562,10 +17687,12 @@ async function composeMcpDepTools(mcpConfig, filterIn) {
17562
17687
  }
17563
17688
  }
17564
17689
  const cleanupClients = async () => {
17565
- await Promise.all(acquiredKeys.map((k) => releaseMcpClient(k)));
17566
- acquiredKeys.length = 0;
17567
- Object.keys(allTools).forEach((key) => delete allTools[key]);
17568
- Object.keys(allClients).forEach((key) => delete allClients[key]);
17690
+ await Promise.all(clientsToClose.map((client) => {
17691
+ try {
17692
+ return client.close();
17693
+ } catch {
17694
+ }
17695
+ }));
17569
17696
  };
17570
17697
  return {
17571
17698
  tools: allTools,
package/index.mjs CHANGED
@@ -13957,6 +13957,147 @@ var ExperimentalServerTasks = class {
13957
13957
  requestStream(request, resultSchema, options) {
13958
13958
  return this._server.requestStream(request, resultSchema, options);
13959
13959
  }
13960
+ /**
13961
+ * Sends a sampling request and returns an AsyncGenerator that yields response messages.
13962
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
13963
+ *
13964
+ * For task-augmented requests, yields 'taskCreated' and 'taskStatus' messages
13965
+ * before the final result.
13966
+ *
13967
+ * @example
13968
+ * ```typescript
13969
+ * const stream = server.experimental.tasks.createMessageStream({
13970
+ * messages: [{ role: 'user', content: { type: 'text', text: 'Hello' } }],
13971
+ * maxTokens: 100
13972
+ * }, {
13973
+ * onprogress: (progress) => {
13974
+ * // Handle streaming tokens via progress notifications
13975
+ * console.log('Progress:', progress.message);
13976
+ * }
13977
+ * });
13978
+ *
13979
+ * for await (const message of stream) {
13980
+ * switch (message.type) {
13981
+ * case 'taskCreated':
13982
+ * console.log('Task created:', message.task.taskId);
13983
+ * break;
13984
+ * case 'taskStatus':
13985
+ * console.log('Task status:', message.task.status);
13986
+ * break;
13987
+ * case 'result':
13988
+ * console.log('Final result:', message.result);
13989
+ * break;
13990
+ * case 'error':
13991
+ * console.error('Error:', message.error);
13992
+ * break;
13993
+ * }
13994
+ * }
13995
+ * ```
13996
+ *
13997
+ * @param params - The sampling request parameters
13998
+ * @param options - Optional request options (timeout, signal, task creation params, onprogress, etc.)
13999
+ * @returns AsyncGenerator that yields ResponseMessage objects
14000
+ *
14001
+ * @experimental
14002
+ */
14003
+ createMessageStream(params, options) {
14004
+ const clientCapabilities = this._server.getClientCapabilities();
14005
+ if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
14006
+ throw new Error("Client does not support sampling tools capability.");
14007
+ }
14008
+ if (params.messages.length > 0) {
14009
+ const lastMessage = params.messages[params.messages.length - 1];
14010
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
14011
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
14012
+ const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
14013
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
14014
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
14015
+ if (hasToolResults) {
14016
+ if (lastContent.some((c) => c.type !== "tool_result")) {
14017
+ throw new Error("The last message must contain only tool_result content if any is present");
14018
+ }
14019
+ if (!hasPreviousToolUse) {
14020
+ throw new Error("tool_result blocks are not matching any tool_use from the previous message");
14021
+ }
14022
+ }
14023
+ if (hasPreviousToolUse) {
14024
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
14025
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
14026
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
14027
+ throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
14028
+ }
14029
+ }
14030
+ }
14031
+ return this.requestStream({
14032
+ method: "sampling/createMessage",
14033
+ params
14034
+ }, CreateMessageResultSchema, options);
14035
+ }
14036
+ /**
14037
+ * Sends an elicitation request and returns an AsyncGenerator that yields response messages.
14038
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
14039
+ *
14040
+ * For task-augmented requests (especially URL-based elicitation), yields 'taskCreated'
14041
+ * and 'taskStatus' messages before the final result.
14042
+ *
14043
+ * @example
14044
+ * ```typescript
14045
+ * const stream = server.experimental.tasks.elicitInputStream({
14046
+ * mode: 'url',
14047
+ * message: 'Please authenticate',
14048
+ * elicitationId: 'auth-123',
14049
+ * url: 'https://example.com/auth'
14050
+ * }, {
14051
+ * task: { ttl: 300000 } // Task-augmented for long-running auth flow
14052
+ * });
14053
+ *
14054
+ * for await (const message of stream) {
14055
+ * switch (message.type) {
14056
+ * case 'taskCreated':
14057
+ * console.log('Task created:', message.task.taskId);
14058
+ * break;
14059
+ * case 'taskStatus':
14060
+ * console.log('Task status:', message.task.status);
14061
+ * break;
14062
+ * case 'result':
14063
+ * console.log('User action:', message.result.action);
14064
+ * break;
14065
+ * case 'error':
14066
+ * console.error('Error:', message.error);
14067
+ * break;
14068
+ * }
14069
+ * }
14070
+ * ```
14071
+ *
14072
+ * @param params - The elicitation request parameters
14073
+ * @param options - Optional request options (timeout, signal, task creation params, etc.)
14074
+ * @returns AsyncGenerator that yields ResponseMessage objects
14075
+ *
14076
+ * @experimental
14077
+ */
14078
+ elicitInputStream(params, options) {
14079
+ const clientCapabilities = this._server.getClientCapabilities();
14080
+ const mode = params.mode ?? "form";
14081
+ switch (mode) {
14082
+ case "url": {
14083
+ if (!clientCapabilities?.elicitation?.url) {
14084
+ throw new Error("Client does not support url elicitation.");
14085
+ }
14086
+ break;
14087
+ }
14088
+ case "form": {
14089
+ if (!clientCapabilities?.elicitation?.form) {
14090
+ throw new Error("Client does not support form elicitation.");
14091
+ }
14092
+ break;
14093
+ }
14094
+ }
14095
+ const normalizedParams = mode === "form" && params.mode === void 0 ? { ...params, mode: "form" } : params;
14096
+ return this.requestStream({
14097
+ method: "elicitation/create",
14098
+ params: normalizedParams
14099
+ }, ElicitResultSchema, options);
14100
+ }
13960
14101
  /**
13961
14102
  * Gets the current status of a task.
13962
14103
  *
@@ -16212,22 +16353,45 @@ async function auth(provider, options) {
16212
16353
  }
16213
16354
  }
16214
16355
  async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
16356
+ const cachedState = await provider.discoveryState?.();
16215
16357
  let resourceMetadata;
16216
16358
  let authorizationServerUrl;
16217
- try {
16218
- resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl }, fetchFn);
16219
- if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
16220
- authorizationServerUrl = resourceMetadata.authorization_servers[0];
16359
+ let metadata;
16360
+ let effectiveResourceMetadataUrl = resourceMetadataUrl;
16361
+ if (!effectiveResourceMetadataUrl && cachedState?.resourceMetadataUrl) {
16362
+ effectiveResourceMetadataUrl = new URL(cachedState.resourceMetadataUrl);
16363
+ }
16364
+ if (cachedState?.authorizationServerUrl) {
16365
+ authorizationServerUrl = cachedState.authorizationServerUrl;
16366
+ resourceMetadata = cachedState.resourceMetadata;
16367
+ metadata = cachedState.authorizationServerMetadata ?? await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn });
16368
+ if (!resourceMetadata) {
16369
+ try {
16370
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl }, fetchFn);
16371
+ } catch {
16372
+ }
16221
16373
  }
16222
- } catch {
16223
- }
16224
- if (!authorizationServerUrl) {
16225
- authorizationServerUrl = new URL("/", serverUrl);
16374
+ if (metadata !== cachedState.authorizationServerMetadata || resourceMetadata !== cachedState.resourceMetadata) {
16375
+ await provider.saveDiscoveryState?.({
16376
+ authorizationServerUrl: String(authorizationServerUrl),
16377
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
16378
+ resourceMetadata,
16379
+ authorizationServerMetadata: metadata
16380
+ });
16381
+ }
16382
+ } else {
16383
+ const serverInfo = await discoverOAuthServerInfo(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl, fetchFn });
16384
+ authorizationServerUrl = serverInfo.authorizationServerUrl;
16385
+ metadata = serverInfo.authorizationServerMetadata;
16386
+ resourceMetadata = serverInfo.resourceMetadata;
16387
+ await provider.saveDiscoveryState?.({
16388
+ authorizationServerUrl: String(authorizationServerUrl),
16389
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
16390
+ resourceMetadata,
16391
+ authorizationServerMetadata: metadata
16392
+ });
16226
16393
  }
16227
16394
  const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
16228
- const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
16229
- fetchFn
16230
- });
16231
16395
  let clientInformation = await Promise.resolve(provider.clientInformation());
16232
16396
  if (!clientInformation) {
16233
16397
  if (authorizationCode !== void 0) {
@@ -16482,6 +16646,26 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
16482
16646
  }
16483
16647
  return void 0;
16484
16648
  }
16649
+ async function discoverOAuthServerInfo(serverUrl, opts) {
16650
+ let resourceMetadata;
16651
+ let authorizationServerUrl;
16652
+ try {
16653
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: opts?.resourceMetadataUrl }, opts?.fetchFn);
16654
+ if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
16655
+ authorizationServerUrl = resourceMetadata.authorization_servers[0];
16656
+ }
16657
+ } catch {
16658
+ }
16659
+ if (!authorizationServerUrl) {
16660
+ authorizationServerUrl = String(new URL("/", serverUrl));
16661
+ }
16662
+ const authorizationServerMetadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn: opts?.fetchFn });
16663
+ return {
16664
+ authorizationServerUrl,
16665
+ authorizationServerMetadata,
16666
+ resourceMetadata
16667
+ };
16668
+ }
16485
16669
  async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
16486
16670
  let authorizationUrl;
16487
16671
  if (metadata) {
@@ -17379,18 +17563,6 @@ var cleanToolSchema = (schema) => {
17379
17563
  import { cwd } from "node:process";
17380
17564
  import process5 from "node:process";
17381
17565
  import { createHash } from "node:crypto";
17382
- var mcpClientPool = /* @__PURE__ */ new Map();
17383
- var mcpClientConnecting = /* @__PURE__ */ new Map();
17384
- var shortHash = (s) => createHash("sha256").update(s).digest("hex").slice(0, 8);
17385
- function defSignature(def) {
17386
- const defCopy = {
17387
- ...def
17388
- };
17389
- if (defCopy.transportType === "memory" || defCopy.transport) {
17390
- return `memory:${Date.now()}:${Math.random()}`;
17391
- }
17392
- return JSON.stringify(defCopy);
17393
- }
17394
17566
  function createTransport(def) {
17395
17567
  const defAny = def;
17396
17568
  const explicitType = defAny.transportType || defAny.type;
@@ -17438,90 +17610,43 @@ function createTransport(def) {
17438
17610
  }
17439
17611
  throw new Error(`Unsupported transport configuration: ${JSON.stringify(def)}`);
17440
17612
  }
17441
- async function getOrCreateMcpClient(defKey, def) {
17442
- const pooled = mcpClientPool.get(defKey);
17443
- if (pooled) {
17444
- pooled.refCount += 1;
17445
- return pooled.client;
17446
- }
17447
- const existingConnecting = mcpClientConnecting.get(defKey);
17448
- if (existingConnecting) {
17449
- const client = await existingConnecting;
17450
- const entry = mcpClientPool.get(defKey);
17451
- if (entry) entry.refCount += 1;
17452
- return client;
17453
- }
17454
- const transport = createTransport(def);
17455
- const connecting = (async () => {
17456
- const client = new Client({
17457
- name: `mcp_${shortHash(defSignature(def))}`,
17458
- version: "1.0.0"
17459
- });
17460
- await client.connect(transport, {
17461
- timeout: 6e4 * 10
17462
- });
17463
- return client;
17464
- })();
17465
- mcpClientConnecting.set(defKey, connecting);
17466
- try {
17467
- const client = await connecting;
17468
- mcpClientPool.set(defKey, {
17469
- client,
17470
- refCount: 1
17471
- });
17472
- return client;
17473
- } finally {
17474
- mcpClientConnecting.delete(defKey);
17613
+ function defSignature(def) {
17614
+ const defCopy = {
17615
+ ...def
17616
+ };
17617
+ if (defCopy.transportType === "memory" || defCopy.transport) {
17618
+ return `memory:${Date.now()}:${Math.random()}`;
17475
17619
  }
17620
+ return JSON.stringify(defCopy);
17476
17621
  }
17477
- async function releaseMcpClient(defKey) {
17478
- const entry = mcpClientPool.get(defKey);
17479
- if (!entry) return;
17480
- entry.refCount -= 1;
17481
- if (entry.refCount <= 0) {
17482
- mcpClientPool.delete(defKey);
17483
- try {
17484
- await entry.client.close();
17485
- } catch (err) {
17486
- console.error("Error closing MCP client:", err);
17487
- }
17488
- }
17622
+ var shortHash = (s) => createHash("sha256").update(s).digest("hex").slice(0, 8);
17623
+ async function createMcpClient(def) {
17624
+ const transport = createTransport(def);
17625
+ const client = new Client({
17626
+ name: `mcp_${shortHash(defSignature(def))}`,
17627
+ version: "1.0.0"
17628
+ });
17629
+ await client.connect(transport, {
17630
+ timeout: 6e4 * 10
17631
+ });
17632
+ return client;
17489
17633
  }
17490
- var cleanupAllPooledClients = async () => {
17491
- const entries = Array.from(mcpClientPool.entries());
17492
- mcpClientPool.clear();
17493
- await Promise.all(entries.map(async ([, { client }]) => {
17494
- try {
17495
- await client.close();
17496
- } catch (err) {
17497
- console.error("Error closing MCP client:", err);
17498
- }
17499
- }));
17500
- };
17501
- process5.once?.("exit", () => {
17502
- cleanupAllPooledClients();
17503
- });
17504
- process5.once?.("SIGINT", () => {
17505
- cleanupAllPooledClients().finally(() => process5.exit(0));
17506
- });
17507
17634
  async function composeMcpDepTools(mcpConfig, filterIn) {
17508
17635
  const allTools = {};
17509
17636
  const allClients = {};
17510
- const acquiredKeys = [];
17637
+ const clientsToClose = [];
17511
17638
  for (const [name, definition] of Object.entries(mcpConfig.mcpServers)) {
17512
17639
  const def = definition;
17513
17640
  if (def.disabled) continue;
17514
- const defKey = shortHash(defSignature(def));
17515
- const serverId = name;
17516
17641
  try {
17517
- const client = await getOrCreateMcpClient(defKey, def);
17518
- acquiredKeys.push(defKey);
17519
- allClients[serverId] = client;
17642
+ const client = await createMcpClient(def);
17643
+ clientsToClose.push(client);
17644
+ allClients[name] = client;
17520
17645
  const { tools } = await client.listTools();
17521
17646
  tools.forEach((tool2) => {
17522
17647
  const toolNameWithScope = `${name}.${tool2.name}`;
17523
17648
  const internalToolName = tool2.name;
17524
- const rawToolId = `${serverId}_${internalToolName}`;
17649
+ const rawToolId = `${name}_${internalToolName}`;
17525
17650
  const toolId = sanitizePropertyKey(rawToolId);
17526
17651
  if (filterIn && !filterIn({
17527
17652
  action: internalToolName,
@@ -17533,7 +17658,7 @@ async function composeMcpDepTools(mcpConfig, filterIn) {
17533
17658
  })) {
17534
17659
  return;
17535
17660
  }
17536
- const execute = (args) => allClients[serverId].callTool({
17661
+ const execute = (args) => allClients[name].callTool({
17537
17662
  name: internalToolName,
17538
17663
  arguments: args
17539
17664
  }, void 0, {
@@ -17550,10 +17675,12 @@ async function composeMcpDepTools(mcpConfig, filterIn) {
17550
17675
  }
17551
17676
  }
17552
17677
  const cleanupClients = async () => {
17553
- await Promise.all(acquiredKeys.map((k) => releaseMcpClient(k)));
17554
- acquiredKeys.length = 0;
17555
- Object.keys(allTools).forEach((key) => delete allTools[key]);
17556
- Object.keys(allClients).forEach((key) => delete allClients[key]);
17678
+ await Promise.all(clientsToClose.map((client) => {
17679
+ try {
17680
+ return client.close();
17681
+ } catch {
17682
+ }
17683
+ }));
17557
17684
  };
17558
17685
  return {
17559
17686
  tools: allTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpc-tech/core",
3
- "version": "0.3.37",
3
+ "version": "0.3.39",
4
4
  "homepage": "https://jsr.io/@mcpc/core",
5
5
  "dependencies": {
6
6
  "@ai-sdk/provider": "^2.0.0",
@@ -105,7 +105,7 @@ Usage:
105
105
  - ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
106
106
  - ${toolName}({ skill: "skill-name", ref: "path/to/file" }) - Load reference file
107
107
 
108
- Note: For scripts/ and assets/, use appropriate tools directly.`;
108
+ Note: For scripts/, use the bash tool with the script path to execute.`;
109
109
  }
110
110
  function createSkillsPlugin(options) {
111
111
  const { paths } = options;
@@ -205,7 +205,13 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
205
205
  "utf-8"
206
206
  );
207
207
  const body = extractBody(content);
208
- return { content: [{ type: "text", text: body }] };
208
+ const skillPathInfo = `
209
+ ---
210
+ Skill path: ${meta.basePath}
211
+ `;
212
+ return {
213
+ content: [{ type: "text", text: body + skillPathInfo }]
214
+ };
209
215
  } catch {
210
216
  return {
211
217
  content: [
@@ -83,7 +83,7 @@ Usage:
83
83
  - ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
84
84
  - ${toolName}({ skill: "skill-name", ref: "path/to/file" }) - Load reference file
85
85
 
86
- Note: For scripts/ and assets/, use appropriate tools directly.`;
86
+ Note: For scripts/, use the bash tool with the script path to execute.`;
87
87
  }
88
88
  function createSkillsPlugin(options) {
89
89
  const { paths } = options;
@@ -183,7 +183,13 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
183
183
  "utf-8"
184
184
  );
185
185
  const body = extractBody(content);
186
- return { content: [{ type: "text", text: body }] };
186
+ const skillPathInfo = `
187
+ ---
188
+ Skill path: ${meta.basePath}
189
+ `;
190
+ return {
191
+ content: [{ type: "text", text: body + skillPathInfo }]
192
+ };
187
193
  } catch {
188
194
  return {
189
195
  content: [
package/plugins.cjs CHANGED
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // __mcpc__core_latest/node_modules/@mcpc/core/plugins.ts
30
30
  var plugins_exports = {};
31
31
  __export(plugins_exports, {
32
+ createBashPlugin: () => createBashPlugin,
32
33
  createLargeResultPlugin: () => createLargeResultPlugin,
33
34
  createSearchPlugin: () => createSearchPlugin,
34
35
  createSkillsPlugin: () => createSkillsPlugin,
@@ -447,7 +448,7 @@ Usage:
447
448
  - ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
448
449
  - ${toolName}({ skill: "skill-name", ref: "path/to/file" }) - Load reference file
449
450
 
450
- Note: For scripts/ and assets/, use appropriate tools directly.`;
451
+ Note: For scripts/, use the bash tool with the script path to execute.`;
451
452
  }
452
453
  function createSkillsPlugin(options) {
453
454
  const { paths } = options;
@@ -555,11 +556,15 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
555
556
  try {
556
557
  const content = await (0, import_promises2.readFile)((0, import_node_path4.join)(meta.basePath, "SKILL.md"), "utf-8");
557
558
  const body = extractBody(content);
559
+ const skillPathInfo = `
560
+ ---
561
+ Skill path: ${meta.basePath}
562
+ `;
558
563
  return {
559
564
  content: [
560
565
  {
561
566
  type: "text",
562
- text: body
567
+ text: body + skillPathInfo
563
568
  }
564
569
  ]
565
570
  };
@@ -585,8 +590,147 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
585
590
  }
586
591
  };
587
592
  }
593
+
594
+ // __mcpc__core_latest/node_modules/@mcpc/core/src/plugins/bash.js
595
+ var import_node_child_process = require("node:child_process");
596
+ var import_node_process = __toESM(require("node:process"), 1);
597
+ var DEFAULT_MAX_BYTES = 1e5;
598
+ var DEFAULT_MAX_LINES = 2e3;
599
+ var DEFAULT_TIMEOUT_MS = 6e4;
600
+ function truncateOutput(stdout, stderr, maxBytes = DEFAULT_MAX_BYTES, maxLines = DEFAULT_MAX_LINES) {
601
+ const fullOutput = (stderr ? `STDERR:
602
+ ${stderr}
603
+
604
+ STDOUT:
605
+ ` : "") + stdout;
606
+ const lines = fullOutput.split("\n");
607
+ if (lines.length > maxLines) {
608
+ const truncatedLines = lines.slice(-maxLines);
609
+ return {
610
+ output: `[OUTPUT TRUNCATED] Showing last ${maxLines} lines of ${lines.length} total
611
+
612
+ ` + truncatedLines.join("\n"),
613
+ truncated: true
614
+ };
615
+ }
616
+ if (fullOutput.length > maxBytes) {
617
+ const truncatedBytes = fullOutput.slice(-maxBytes);
618
+ return {
619
+ output: `[OUTPUT TRUNCATED] Showing last ${maxBytes} bytes of ${fullOutput.length} total
620
+
621
+ ` + truncatedBytes,
622
+ truncated: true
623
+ };
624
+ }
625
+ return {
626
+ output: fullOutput,
627
+ truncated: false
628
+ };
629
+ }
630
+ function executeBash(command, cwd, timeoutMs) {
631
+ return new Promise((resolve3) => {
632
+ const stdout = [];
633
+ const stderr = [];
634
+ const proc = (0, import_node_child_process.spawn)("bash", [
635
+ "-c",
636
+ command
637
+ ], {
638
+ cwd,
639
+ stdio: [
640
+ "ignore",
641
+ "pipe",
642
+ "pipe"
643
+ ]
644
+ });
645
+ proc.stdout?.on("data", (data) => {
646
+ stdout.push(data.toString());
647
+ });
648
+ proc.stderr?.on("data", (data) => {
649
+ stderr.push(data.toString());
650
+ });
651
+ proc.on("close", (code) => {
652
+ resolve3({
653
+ stdout: stdout.join(""),
654
+ stderr: stderr.join(""),
655
+ exitCode: code
656
+ });
657
+ });
658
+ proc.on("error", (err) => {
659
+ resolve3({
660
+ stdout: "",
661
+ stderr: err.message,
662
+ exitCode: null
663
+ });
664
+ });
665
+ setTimeout(() => {
666
+ proc.kill("SIGTERM");
667
+ resolve3({
668
+ stdout: stdout.join(""),
669
+ stderr: stderr.join("") + "\n\n[TIMEOUT] Command execution timed out",
670
+ exitCode: null
671
+ });
672
+ }, timeoutMs);
673
+ });
674
+ }
675
+ function createBashPlugin(options = {}) {
676
+ const { maxBytes, maxLines, timeoutMs } = {
677
+ maxBytes: DEFAULT_MAX_BYTES,
678
+ maxLines: DEFAULT_MAX_LINES,
679
+ timeoutMs: DEFAULT_TIMEOUT_MS,
680
+ ...options
681
+ };
682
+ return {
683
+ name: "plugin-bash",
684
+ version: "1.0.0",
685
+ // Store server reference for tool registration
686
+ configureServer: (server) => {
687
+ server.tool("bash", "Execute a bash command and return its output.\n\nUse this for:\n- Running shell commands\n- Executing scripts\n- System operations\n\nNote: Output is truncated if too large.", {
688
+ type: "object",
689
+ properties: {
690
+ command: {
691
+ type: "string",
692
+ description: "The bash command to execute"
693
+ },
694
+ cwd: {
695
+ type: "string",
696
+ description: "Optional: Working directory for the command (defaults to current directory)"
697
+ }
698
+ },
699
+ required: [
700
+ "command"
701
+ ]
702
+ }, async (args) => {
703
+ const cwd = args.cwd || import_node_process.default.cwd();
704
+ const result = await executeBash(args.command, cwd, timeoutMs);
705
+ const { output, truncated } = truncateOutput(result.stdout, result.stderr, maxBytes, maxLines);
706
+ let finalOutput = output;
707
+ if (result.exitCode !== null && result.exitCode !== 0) {
708
+ finalOutput = `[EXIT CODE: ${result.exitCode}]
709
+ ` + finalOutput;
710
+ }
711
+ if (truncated) {
712
+ finalOutput += `
713
+
714
+ [Note: Output was truncated]`;
715
+ }
716
+ return {
717
+ content: [
718
+ {
719
+ type: "text",
720
+ text: finalOutput
721
+ }
722
+ ],
723
+ isError: result.exitCode !== null && result.exitCode !== 0
724
+ };
725
+ }, {
726
+ internal: true
727
+ });
728
+ }
729
+ };
730
+ }
588
731
  // Annotate the CommonJS export names for ESM import in node:
589
732
  0 && (module.exports = {
733
+ createBashPlugin,
590
734
  createLargeResultPlugin,
591
735
  createSearchPlugin,
592
736
  createSkillsPlugin,
package/plugins.mjs CHANGED
@@ -411,7 +411,7 @@ Usage:
411
411
  - ${toolName}({ skill: "skill-name" }) - Load main SKILL.md content
412
412
  - ${toolName}({ skill: "skill-name", ref: "path/to/file" }) - Load reference file
413
413
 
414
- Note: For scripts/ and assets/, use appropriate tools directly.`;
414
+ Note: For scripts/, use the bash tool with the script path to execute.`;
415
415
  }
416
416
  function createSkillsPlugin(options) {
417
417
  const { paths } = options;
@@ -519,11 +519,15 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
519
519
  try {
520
520
  const content = await readFile(join2(meta.basePath, "SKILL.md"), "utf-8");
521
521
  const body = extractBody(content);
522
+ const skillPathInfo = `
523
+ ---
524
+ Skill path: ${meta.basePath}
525
+ `;
522
526
  return {
523
527
  content: [
524
528
  {
525
529
  type: "text",
526
- text: body
530
+ text: body + skillPathInfo
527
531
  }
528
532
  ]
529
533
  };
@@ -549,7 +553,146 @@ Available skills: ${available.join(", ")}` : "\n\nNo skills available.";
549
553
  }
550
554
  };
551
555
  }
556
+
557
+ // __mcpc__core_latest/node_modules/@mcpc/core/src/plugins/bash.js
558
+ import { spawn } from "node:child_process";
559
+ import process from "node:process";
560
+ var DEFAULT_MAX_BYTES = 1e5;
561
+ var DEFAULT_MAX_LINES = 2e3;
562
+ var DEFAULT_TIMEOUT_MS = 6e4;
563
+ function truncateOutput(stdout, stderr, maxBytes = DEFAULT_MAX_BYTES, maxLines = DEFAULT_MAX_LINES) {
564
+ const fullOutput = (stderr ? `STDERR:
565
+ ${stderr}
566
+
567
+ STDOUT:
568
+ ` : "") + stdout;
569
+ const lines = fullOutput.split("\n");
570
+ if (lines.length > maxLines) {
571
+ const truncatedLines = lines.slice(-maxLines);
572
+ return {
573
+ output: `[OUTPUT TRUNCATED] Showing last ${maxLines} lines of ${lines.length} total
574
+
575
+ ` + truncatedLines.join("\n"),
576
+ truncated: true
577
+ };
578
+ }
579
+ if (fullOutput.length > maxBytes) {
580
+ const truncatedBytes = fullOutput.slice(-maxBytes);
581
+ return {
582
+ output: `[OUTPUT TRUNCATED] Showing last ${maxBytes} bytes of ${fullOutput.length} total
583
+
584
+ ` + truncatedBytes,
585
+ truncated: true
586
+ };
587
+ }
588
+ return {
589
+ output: fullOutput,
590
+ truncated: false
591
+ };
592
+ }
593
+ function executeBash(command, cwd, timeoutMs) {
594
+ return new Promise((resolve3) => {
595
+ const stdout = [];
596
+ const stderr = [];
597
+ const proc = spawn("bash", [
598
+ "-c",
599
+ command
600
+ ], {
601
+ cwd,
602
+ stdio: [
603
+ "ignore",
604
+ "pipe",
605
+ "pipe"
606
+ ]
607
+ });
608
+ proc.stdout?.on("data", (data) => {
609
+ stdout.push(data.toString());
610
+ });
611
+ proc.stderr?.on("data", (data) => {
612
+ stderr.push(data.toString());
613
+ });
614
+ proc.on("close", (code) => {
615
+ resolve3({
616
+ stdout: stdout.join(""),
617
+ stderr: stderr.join(""),
618
+ exitCode: code
619
+ });
620
+ });
621
+ proc.on("error", (err) => {
622
+ resolve3({
623
+ stdout: "",
624
+ stderr: err.message,
625
+ exitCode: null
626
+ });
627
+ });
628
+ setTimeout(() => {
629
+ proc.kill("SIGTERM");
630
+ resolve3({
631
+ stdout: stdout.join(""),
632
+ stderr: stderr.join("") + "\n\n[TIMEOUT] Command execution timed out",
633
+ exitCode: null
634
+ });
635
+ }, timeoutMs);
636
+ });
637
+ }
638
+ function createBashPlugin(options = {}) {
639
+ const { maxBytes, maxLines, timeoutMs } = {
640
+ maxBytes: DEFAULT_MAX_BYTES,
641
+ maxLines: DEFAULT_MAX_LINES,
642
+ timeoutMs: DEFAULT_TIMEOUT_MS,
643
+ ...options
644
+ };
645
+ return {
646
+ name: "plugin-bash",
647
+ version: "1.0.0",
648
+ // Store server reference for tool registration
649
+ configureServer: (server) => {
650
+ server.tool("bash", "Execute a bash command and return its output.\n\nUse this for:\n- Running shell commands\n- Executing scripts\n- System operations\n\nNote: Output is truncated if too large.", {
651
+ type: "object",
652
+ properties: {
653
+ command: {
654
+ type: "string",
655
+ description: "The bash command to execute"
656
+ },
657
+ cwd: {
658
+ type: "string",
659
+ description: "Optional: Working directory for the command (defaults to current directory)"
660
+ }
661
+ },
662
+ required: [
663
+ "command"
664
+ ]
665
+ }, async (args) => {
666
+ const cwd = args.cwd || process.cwd();
667
+ const result = await executeBash(args.command, cwd, timeoutMs);
668
+ const { output, truncated } = truncateOutput(result.stdout, result.stderr, maxBytes, maxLines);
669
+ let finalOutput = output;
670
+ if (result.exitCode !== null && result.exitCode !== 0) {
671
+ finalOutput = `[EXIT CODE: ${result.exitCode}]
672
+ ` + finalOutput;
673
+ }
674
+ if (truncated) {
675
+ finalOutput += `
676
+
677
+ [Note: Output was truncated]`;
678
+ }
679
+ return {
680
+ content: [
681
+ {
682
+ type: "text",
683
+ text: finalOutput
684
+ }
685
+ ],
686
+ isError: result.exitCode !== null && result.exitCode !== 0
687
+ };
688
+ }, {
689
+ internal: true
690
+ });
691
+ }
692
+ };
693
+ }
552
694
  export {
695
+ createBashPlugin,
553
696
  createLargeResultPlugin,
554
697
  createSearchPlugin,
555
698
  createSkillsPlugin,
@@ -38,6 +38,7 @@
38
38
  */ export { createSearchPlugin } from "./src/plugins/search-tool.js";
39
39
  export { createLargeResultPlugin } from "./src/plugins/large-result.js";
40
40
  export { createSkillsPlugin } from "./src/plugins/skills.js";
41
+ export { createBashPlugin } from "./src/plugins/bash.js";
41
42
  export { default as defaultSearchPlugin } from "./src/plugins/search-tool.js";
42
43
  export { default as defaultLargeResultPlugin } from "./src/plugins/large-result.js";
43
44
  export type { SearchOptions } from "./src/plugins/search-tool.js";
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.d.ts","sources":["../plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAGD,SAAS,kBAAkB,uCAAuC;AAClE,SAAS,uBAAuB,wCAAwC;AACxE,SAAS,kBAAkB,kCAAkC;AAG7D,SAAS,WAAW,mBAAmB,uCAAuC;AAC9E,SAAS,WAAW,wBAAwB,wCAAwC;AAGpF,cAAc,aAAa,uCAAuC"}
1
+ {"version":3,"file":"plugins.d.ts","sources":["../plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAGD,SAAS,kBAAkB,uCAAuC;AAClE,SAAS,uBAAuB,wCAAwC;AACxE,SAAS,kBAAkB,kCAAkC;AAC7D,SAAS,gBAAgB,gCAAgC;AAGzD,SAAS,WAAW,mBAAmB,uCAAuC;AAC9E,SAAS,WAAW,wBAAwB,wCAAwC;AAGpF,cAAc,aAAa,uCAAuC"}
@@ -0,0 +1,10 @@
1
+ import type { ToolPlugin } from "../plugin-types.js";
2
+ export interface BashPluginOptions {
3
+ /** Maximum output bytes to return */ maxBytes?: number;
4
+ /** Maximum output lines to return */ maxLines?: number;
5
+ /** Command execution timeout in ms */ timeoutMs?: number;
6
+ }
7
+ /**
8
+ * Create a bash plugin that provides command execution capability
9
+ */ export declare function createBashPlugin(options?: BashPluginOptions): ToolPlugin;
10
+ //# sourceMappingURL=bash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash.d.ts","sources":["../../../src/plugins/bash.ts"],"names":[],"mappings":"AAWA,cAAc,UAAU,6BAA6B;AASrD,iBAAiB;EACf,mCAAmC,GACnC,WAAW,MAAM;EACjB,mCAAmC,GACnC,WAAW,MAAM;EACjB,oCAAoC,GACpC,YAAY,MAAM;;AA8FpB;;CAEC,GACD,OAAO,iBAAS,iBAAiB,UAAS,iBAAsB,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sources":["../../../src/plugins/skills.ts"],"names":[],"mappings":"AAOA,cAAmC,UAAU,6BAA6B;UAWhE;EACR,mCAAmC,GACnC,OAAO,MAAM;;AAoGf;;CAEC,GACD,OAAO,iBAAS,mBAAmB,SAAS,mBAAmB,GAAG;AA8IlE;;;;;;;;;;;;;CAaC,GACD,OAAO,iBAAS,aAAa,QAAQ,OAAO,MAAM,EAAE,MAAM,CAAC,GAAG;AAM9D,eAAe,mBAAmB"}
1
+ {"version":3,"file":"skills.d.ts","sources":["../../../src/plugins/skills.ts"],"names":[],"mappings":"AAOA,cAAmC,UAAU,6BAA6B;UAWhE;EACR,mCAAmC,GACnC,OAAO,MAAM;;AAoGf;;CAEC,GACD,OAAO,iBAAS,mBAAmB,SAAS,mBAAmB,GAAG;AAkJlE;;;;;;;;;;;;;CAaC,GACD,OAAO,iBAAS,aAAa,QAAQ,OAAO,MAAM,EAAE,MAAM,CAAC,GAAG;AAM9D,eAAe,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sources":["../../../../src/utils/common/mcp.ts"],"names":[],"mappings":"AAKA,cAA+B,UAAU,iCAAiC;AAwL1E,OAAO,iBAAe,mBACpB,WAAW,UAAU,EACrB,YAAY;EACV,QAAQ,MAAM;EACd,MAAM,GAAG;EACT,SAAS,MAAM;EACf,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,QAAQ,MAAM;MACV,OAAO,GACZ,QAAQ,OAAO,MAAM,EAAE,GAAG"}
1
+ {"version":3,"file":"mcp.d.ts","sources":["../../../../src/utils/common/mcp.ts"],"names":[],"mappings":"AAKA,cAA+B,UAAU,iCAAiC;AAiG1E,OAAO,iBAAe,mBACpB,WAAW,UAAU,EACrB,YAAY;EACV,QAAQ,MAAM;EACd,MAAM,GAAG;EACT,SAAS,MAAM;EACf,mBAAmB,MAAM;EACzB,kBAAkB,MAAM;EACxB,QAAQ,MAAM;MACV,OAAO,GACZ,QAAQ,OAAO,MAAM,EAAE,GAAG"}