@f5-sales-demo/xcsh 21.50.0 → 22.0.1

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.
Files changed (124) hide show
  1. package/README.md +0 -3
  2. package/package.json +8 -8
  3. package/src/browser/headless-bridge.ts +0 -2
  4. package/src/browser/host-profiles.ts +1 -1
  5. package/src/capability/extension.ts +2 -4
  6. package/src/capability/types.ts +6 -6
  7. package/src/cli/args.ts +0 -11
  8. package/src/cli/flag-spec.ts +0 -2
  9. package/src/commit/agentic/agent.ts +0 -1
  10. package/src/config/settings-schema.ts +0 -55
  11. package/src/cursor.ts +5 -26
  12. package/src/discovery/builtin.ts +1 -130
  13. package/src/discovery/claude-plugins.ts +1 -88
  14. package/src/discovery/claude.ts +0 -80
  15. package/src/discovery/codex.ts +11 -148
  16. package/src/discovery/cursor.ts +1 -84
  17. package/src/discovery/gemini.ts +0 -89
  18. package/src/discovery/helpers.ts +1 -1
  19. package/src/discovery/index.ts +0 -4
  20. package/src/discovery/opencode.ts +1 -106
  21. package/src/discovery/windsurf.ts +1 -82
  22. package/src/extensibility/custom-tools/types.ts +0 -4
  23. package/src/extensibility/extensions/types.ts +0 -4
  24. package/src/extensibility/plugins/marketplace/builtin-marketplace-snapshot.ts +1 -1
  25. package/src/extensibility/plugins/marketplace/builtin-marketplace.provenance.json +1 -1
  26. package/src/extensibility/plugins/marketplace/types.ts +0 -1
  27. package/src/internal-urls/api-catalog-discovery.ts +6 -5
  28. package/src/internal-urls/api-catalog-qmd-index.generated.ts +2 -2
  29. package/src/internal-urls/build-info.generated.ts +8 -8
  30. package/src/internal-urls/docs-index.generated.ts +13 -15
  31. package/src/internal-urls/index.ts +1 -2
  32. package/src/internal-urls/router.ts +1 -1
  33. package/src/internal-urls/types.ts +2 -2
  34. package/src/lifecycle/native-acceptance-driver.ts +0 -1
  35. package/src/lifecycle/native-acceptance.ts +1 -2
  36. package/src/locales/ar.json +1 -65
  37. package/src/locales/de.json +1 -65
  38. package/src/locales/en.json +1 -99
  39. package/src/locales/es.json +1 -65
  40. package/src/locales/fr.json +1 -65
  41. package/src/locales/hi.json +1 -65
  42. package/src/locales/it.json +1 -65
  43. package/src/locales/ja.json +1 -65
  44. package/src/locales/ko.json +1 -65
  45. package/src/locales/pt-br.json +1 -65
  46. package/src/locales/th.json +1 -65
  47. package/src/locales/zh-cn.json +1 -65
  48. package/src/locales/zh-tw.json +1 -65
  49. package/src/main.ts +3 -20
  50. package/src/modes/acp/acp-agent.ts +14 -146
  51. package/src/modes/components/agent-dashboard.ts +0 -1
  52. package/src/modes/components/extensions/extension-dashboard.ts +1 -2
  53. package/src/modes/components/extensions/state-manager.ts +0 -46
  54. package/src/modes/components/extensions/types.ts +0 -1
  55. package/src/modes/components/settings-defs.ts +1 -2
  56. package/src/modes/controllers/command-controller.ts +0 -14
  57. package/src/modes/controllers/selector-controller.ts +0 -8
  58. package/src/modes/interactive-mode.ts +3 -20
  59. package/src/modes/theme/theme.ts +0 -6
  60. package/src/modes/types.ts +0 -6
  61. package/src/prompts/internal-urls/about.md +1 -1
  62. package/src/prompts/system/progressive-system-prompt.md +1 -1
  63. package/src/prompts/system/system-prompt.md +0 -9
  64. package/src/prompts/tools/search-tool-bm25-progressive.md +3 -6
  65. package/src/qmd-smoke.ts +189 -24
  66. package/src/remote-control/interactions.ts +1 -1
  67. package/src/remote-control/protocol-methods.json +0 -10
  68. package/src/remote-control/router.ts +1 -2
  69. package/src/remote-control/voice-timeline.ts +2 -2
  70. package/src/sdk.ts +10 -252
  71. package/src/session/agent-session.ts +23 -321
  72. package/src/session/session-manager.ts +0 -40
  73. package/src/slash-commands/builtin-registry.ts +3 -85
  74. package/src/system-prompt.ts +0 -9
  75. package/src/task/executor.ts +0 -116
  76. package/src/task/index.ts +0 -4
  77. package/src/{mcp → tools}/discoverable-tool-metadata.ts +14 -84
  78. package/src/tools/index.ts +7 -28
  79. package/src/tools/path-utils.ts +2 -10
  80. package/src/tools/read.ts +2 -2
  81. package/src/tools/search-tool-bm25.ts +28 -72
  82. package/src/tools/vim.ts +1 -1
  83. package/src/web/search/index.ts +2 -5
  84. package/src/web/search/provider.ts +0 -3
  85. package/src/web/search/providers/exa.ts +4 -84
  86. package/src/web/search/types.ts +0 -2
  87. package/src/capability/mcp.ts +0 -74
  88. package/src/config/mcp-schema.json +0 -230
  89. package/src/discovery/mcp-json.ts +0 -171
  90. package/src/discovery/vscode.ts +0 -105
  91. package/src/exa/factory.ts +0 -61
  92. package/src/exa/index.ts +0 -27
  93. package/src/exa/mcp-client.ts +0 -293
  94. package/src/exa/render.ts +0 -232
  95. package/src/exa/researcher.ts +0 -47
  96. package/src/exa/search.ts +0 -86
  97. package/src/exa/types.ts +0 -166
  98. package/src/exa/websets.ts +0 -247
  99. package/src/internal-urls/mcp-protocol.ts +0 -156
  100. package/src/mcp/client.ts +0 -482
  101. package/src/mcp/config-writer.ts +0 -225
  102. package/src/mcp/config.ts +0 -365
  103. package/src/mcp/index.ts +0 -29
  104. package/src/mcp/json-rpc.ts +0 -84
  105. package/src/mcp/loader.ts +0 -124
  106. package/src/mcp/manager.ts +0 -1173
  107. package/src/mcp/oauth-discovery.ts +0 -349
  108. package/src/mcp/oauth-flow.ts +0 -398
  109. package/src/mcp/policy.ts +0 -24
  110. package/src/mcp/render.ts +0 -161
  111. package/src/mcp/runtime-controller.ts +0 -95
  112. package/src/mcp/smithery-auth.ts +0 -105
  113. package/src/mcp/smithery-connect.ts +0 -145
  114. package/src/mcp/smithery-registry.ts +0 -477
  115. package/src/mcp/tool-bridge.ts +0 -449
  116. package/src/mcp/tool-cache.ts +0 -117
  117. package/src/mcp/transports/http.ts +0 -475
  118. package/src/mcp/transports/index.ts +0 -6
  119. package/src/mcp/transports/stdio.ts +0 -325
  120. package/src/mcp/types.ts +0 -428
  121. package/src/modes/components/mcp-add-wizard.ts +0 -1346
  122. package/src/modes/controllers/mcp-command-controller.ts +0 -2502
  123. package/src/prompts/tools/search-tool-bm25.md +0 -38
  124. package/src/web/search/providers/zai.ts +0 -311
package/README.md CHANGED
@@ -10,9 +10,6 @@ For installation, setup, provider configuration, model roles, slash commands, an
10
10
  Package-specific references:
11
11
 
12
12
  - [CHANGELOG](./CHANGELOG.md)
13
- - [MCP configuration guide](../../docs/mcp-config.md)
14
- - [MCP runtime lifecycle](../../docs/mcp-runtime-lifecycle.md)
15
- - [MCP server/tool authoring](../../docs/mcp-server-tool-authoring.md)
16
13
  - [Remote-control implementation](./src/remote-control/IMPLEMENTATION.md)
17
14
  - [Remote-control acceptance](./src/remote-control/ACCEPTANCE.md)
18
15
  - [CONTRIBUTING](../../CONTRIBUTING.md)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "21.50.0",
4
+ "version": "22.0.1",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -67,13 +67,13 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@agentclientprotocol/sdk": "1.4.0",
70
- "@f5-sales-demo/pi-agent-core": "21.50.0",
71
- "@f5-sales-demo/pi-ai": "21.50.0",
72
- "@f5-sales-demo/pi-natives": "21.50.0",
73
- "@f5-sales-demo/pi-resource-management": "21.50.0",
74
- "@f5-sales-demo/pi-tui": "21.50.0",
75
- "@f5-sales-demo/pi-utils": "21.50.0",
76
- "@f5-sales-demo/xcsh-stats": "21.50.0",
70
+ "@f5-sales-demo/pi-agent-core": "22.0.1",
71
+ "@f5-sales-demo/pi-ai": "22.0.1",
72
+ "@f5-sales-demo/pi-natives": "22.0.1",
73
+ "@f5-sales-demo/pi-resource-management": "22.0.1",
74
+ "@f5-sales-demo/pi-tui": "22.0.1",
75
+ "@f5-sales-demo/pi-utils": "22.0.1",
76
+ "@f5-sales-demo/xcsh-stats": "22.0.1",
77
77
  "@mozilla/readability": "^0.6",
78
78
  "@resvg/resvg-wasm": "2.6.2",
79
79
  "@sinclair/typebox": "0.34.52",
@@ -180,7 +180,6 @@ export async function startHeadlessChatBridge(
180
180
  hasUI: false,
181
181
  toolNames: [...new Set([...BROWSER_TOOL_NAMES, ...EXTENSION_AGENT_TOOL_NAMES])],
182
182
  customTools: createExtensionBridgeTools(bridge),
183
- enableMCP: false,
184
183
  enableLsp: false,
185
184
  disableExtensionDiscovery: true,
186
185
  ...(process.env.XCSH_BENCH_EXTENSION
@@ -201,7 +200,6 @@ export async function startHeadlessChatBridge(
201
200
  sessionManager: SessionManager.inMemory(cwd),
202
201
  toolNames: [...OFFICE_TOOL_NAMES],
203
202
  customTools: [],
204
- enableMCP: false,
205
203
  enableLsp: false,
206
204
  disableExtensionDiscovery: true,
207
205
  bundledExtensions: ["sandbox-guard"],
@@ -102,7 +102,7 @@ SAFETY — NEVER DO THESE:
102
102
  const OFFICE_NATIVE_TOOLS_NOTE = `
103
103
  NATIVE TOOLS: Beyond the document host tools, you have xcsh's full local toolset — \`bash\` (run shell commands, including CLIs like \`az\`, \`gh\`, \`terraform\`, \`git\` when installed and authenticated), file tools (\`read\`/\`write\`/\`edit\`), and \`grep\` — plus any skills available in this workspace. Reach for them when the task genuinely needs them (pull live data with a CLI, read a local file the user points you at). Prefer the document host tools for document work. Your file tools and shell are confined to the folder xcsh was launched from.
104
104
 
105
- This pane runs NO MCP servers and exposes NO plugin-provided TOOLS — the tools listed above (plus the document host tools) are everything you can call. To use a cloud or SCM CLI, invoke it directly with \`bash\` (e.g. \`az account show\`, \`gh repo view\`) rather than hunting for an MCP server that would provide it. The user sees your narration, so don't describe a missing MCP tool as a failure — just use the CLI.
105
+ This pane exposes no plugin-provided tools — the tools listed above (plus the document host tools) are everything you can call. To use a cloud or SCM CLI, invoke it directly with \`bash\` (e.g. \`az account show\`, \`gh repo view\`).
106
106
 
107
107
  Plugin RESOURCES are a different thing and they ARE available to you: an installed plugin's skills, slash commands, schemas, templates and engines. Read them through \`xcsh://plugin/<name>\` (summary), \`xcsh://plugin/<name>/<key>\` (a declared resource such as \`schema\`), or \`xcsh://plugin/<name>/file/<path>\` (any file in the plugin), and run a plugin's engine with \`bash\` (e.g. \`bun xcsh://plugin/<name>/file/engine/cli.ts <command>\`). When a plugin declares a deterministic engine, use it — never recompute by hand what the engine computes.
108
108
 
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Extensions Capability
3
3
  *
4
- * Gemini-style extensions that provide MCP servers, tools, and context.
4
+ * Gemini-style extensions that provide tools and context.
5
5
  */
6
6
  import { defineCapability } from ".";
7
- import type { MCPServer } from "./mcp";
8
7
  import type { SourceMeta } from "./types";
9
8
 
10
9
  /**
@@ -13,7 +12,6 @@ import type { SourceMeta } from "./types";
13
12
  export interface ExtensionManifest {
14
13
  name?: string;
15
14
  description?: string;
16
- mcpServers?: Record<string, Omit<MCPServer, "name" | "_source">>;
17
15
  tools?: unknown[];
18
16
  context?: unknown;
19
17
  }
@@ -37,7 +35,7 @@ export interface Extension {
37
35
  export const extensionCapability = defineCapability<Extension>({
38
36
  id: "extensions",
39
37
  displayName: "Extensions",
40
- description: "Gemini-style extensions providing MCP servers, tools, and context",
38
+ description: "Gemini-style extensions providing tools and context",
41
39
  key: ext => ext.name,
42
40
  validate: ext => {
43
41
  if (!ext.name) return "Missing extension name";
@@ -2,8 +2,8 @@
2
2
  * Core types for the capability-based config discovery system.
3
3
  *
4
4
  * This architecture inverts control: instead of callers knowing about paths like
5
- * `.xcsh`, `.codex`, `.gemini`, they simply ask for `load("mcps")` and get back
6
- * a unified array of MCP servers.
5
+ * `.xcsh`, `.codex`, `.gemini`, they simply ask for a capability and get back
6
+ * a unified array of items.
7
7
  */
8
8
 
9
9
  /**
@@ -33,7 +33,7 @@ export interface LoadResult<T> {
33
33
  * A provider that can load items for a capability.
34
34
  */
35
35
  export interface Provider<T> {
36
- /** Unique provider ID (e.g., "xcsh", "mcp-json", "agents-md") */
36
+ /** Unique provider ID (e.g., "xcsh", "codex", "agents-md") */
37
37
  id: string;
38
38
 
39
39
  /** Human-readable name for UI display (e.g., "xcsh", "OpenAI Codex") */
@@ -47,7 +47,7 @@ export interface Provider<T> {
47
47
  * Suggested ranges:
48
48
  * 100+ : Primary providers (xcsh, pi)
49
49
  * 50-99: Tool-specific providers (claude, codex, gemini)
50
- * 1-49 : Shared standards (mcp-json, agents-md)
50
+ * 1-49 : Shared standards (agents-md)
51
51
  */
52
52
  priority: number;
53
53
 
@@ -110,10 +110,10 @@ export interface CapabilityResult<T> {
110
110
  * Definition of a capability.
111
111
  */
112
112
  export interface Capability<T> {
113
- /** Capability ID (e.g., "mcps", "skills", "context-files") */
113
+ /** Capability ID (e.g., "skills", "context-files") */
114
114
  id: string;
115
115
 
116
- /** Human-readable name for UI display (e.g., "MCP Servers", "Skills") */
116
+ /** Human-readable name for UI display (e.g., "Skills", "Context Files") */
117
117
  displayName: string;
118
118
 
119
119
  /** Short description for settings/status UI */
package/src/cli/args.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import { type Effort, THINKING_EFFORTS } from "@f5-sales-demo/pi-ai";
5
5
  import { APP_NAME, CONFIG_DIR_NAME, logger } from "@f5-sales-demo/pi-utils";
6
- import { CliUsageError } from "@f5-sales-demo/pi-utils/cli";
7
6
  import chalk from "chalk";
8
7
  import { parseEffort } from "../thinking";
9
8
  import {
@@ -50,8 +49,6 @@ export interface Args {
50
49
  models?: string[];
51
50
  tools?: string[];
52
51
  noTools?: boolean;
53
- mcp?: boolean;
54
- noMcp?: boolean;
55
52
  noLsp?: boolean;
56
53
  noPty?: boolean;
57
54
  hooks?: string[];
@@ -161,12 +158,6 @@ const APPLY: Record<LaunchFlagName, (result: Args, value: string | true) => void
161
158
  "no-tools": r => {
162
159
  r.noTools = true;
163
160
  },
164
- mcp: r => {
165
- r.mcp = true;
166
- },
167
- "no-mcp": r => {
168
- r.noMcp = true;
169
- },
170
161
  "no-lsp": r => {
171
162
  r.noLsp = true;
172
163
  },
@@ -404,8 +395,6 @@ export function parseArgs(args: string[], extensionFlags?: ExtensionFlagRegistry
404
395
  result.unrecognizedFlags.push({ token, name: name ?? token.replace(/^-+/, "") });
405
396
  }
406
397
 
407
- if (result.mcp && result.noMcp) throw new CliUsageError("--mcp cannot be combined with --no-mcp");
408
- if (result.mcp && result.noTools) throw new CliUsageError("--mcp cannot be combined with --no-tools");
409
398
  return result;
410
399
  }
411
400
 
@@ -79,8 +79,6 @@ export const LAUNCH_FLAGS = defineFlags({
79
79
  "provider-session-id": { arity: "value", description: "Resume a provider-side session", hidden: true },
80
80
  models: { arity: "value", description: "Comma-separated model patterns for Ctrl+P cycling" },
81
81
  "no-tools": { arity: "boolean", description: "Disable all built-in tools" },
82
- mcp: { arity: "boolean", description: "Enable MCP servers for this run" },
83
- "no-mcp": { arity: "boolean", description: "Disable MCP server discovery and tools" },
84
82
  "no-lsp": { arity: "boolean", description: "Disable LSP tools, formatting, and diagnostics" },
85
83
  "no-pty": { arity: "boolean", description: "Disable PTY-based interactive bash execution" },
86
84
  tools: { arity: "value", description: "Comma-separated list of tools to enable (default: all)" },
@@ -63,7 +63,6 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise<Co
63
63
  systemPrompt,
64
64
  customTools: tools,
65
65
  enableLsp: false,
66
- enableMCP: false,
67
66
  hasUI: false,
68
67
  spawns,
69
68
  toolNames: ["__none__"],
@@ -267,7 +267,6 @@ export const SETTINGS_SCHEMA = {
267
267
  "cline",
268
268
  "github",
269
269
  "vscode",
270
- "mcp-json",
271
270
  "ssh-json",
272
271
  ] as string[],
273
272
  },
@@ -1774,60 +1773,6 @@ export const SETTINGS_SCHEMA = {
1774
1773
  },
1775
1774
  },
1776
1775
 
1777
- // MCP
1778
- "mcp.enabled": {
1779
- type: "boolean",
1780
- default: false,
1781
- scope: "user",
1782
- ui: { tab: "tools", label: "MCP", description: "Enable MCP servers for new sessions" },
1783
- },
1784
-
1785
- "mcp.enableProjectConfig": {
1786
- type: "boolean",
1787
- default: true,
1788
- ui: { tab: "tools", label: "MCP Project Config", description: "Load .mcp.json/mcp.json from project root" },
1789
- },
1790
-
1791
- "mcp.discoveryMode": {
1792
- type: "boolean",
1793
- default: false,
1794
- ui: {
1795
- tab: "tools",
1796
- label: "MCP Tool Discovery",
1797
- description: "Hide MCP tools by default and expose them through a tool discovery tool",
1798
- },
1799
- },
1800
-
1801
- "mcp.discoveryDefaultServers": {
1802
- type: "array",
1803
- default: [] as string[],
1804
- ui: {
1805
- tab: "tools",
1806
- label: "MCP Discovery Default Servers",
1807
- description: "Keep MCP tools from these servers visible while discovery mode hides other MCP tools",
1808
- },
1809
- },
1810
-
1811
- "mcp.notifications": {
1812
- type: "boolean",
1813
- default: false,
1814
- ui: {
1815
- tab: "tools",
1816
- label: "MCP Update Injection",
1817
- description: "Inject MCP resource updates into the agent conversation",
1818
- },
1819
- },
1820
-
1821
- "mcp.notificationDebounceMs": {
1822
- type: "number",
1823
- default: 500,
1824
- ui: {
1825
- tab: "tools",
1826
- label: "MCP Notification Debounce",
1827
- description: "Debounce window for MCP resource update notifications before injecting into conversation",
1828
- },
1829
- },
1830
-
1831
1776
  // ────────────────────────────────────────────────────────────────────────
1832
1777
  // Tasks
1833
1778
  // ────────────────────────────────────────────────────────────────────────
package/src/cursor.ts CHANGED
@@ -8,8 +8,8 @@ import type {
8
8
  AgentToolUpdateCallback,
9
9
  } from "@f5-sales-demo/pi-agent-core";
10
10
  import type {
11
- CursorMcpCall,
12
11
  CursorShellStreamCallbacks,
12
+ CursorToolCall,
13
13
  CursorExecHandlers as ICursorExecHandlers,
14
14
  ToolResultMessage,
15
15
  } from "@f5-sales-demo/pi-ai";
@@ -145,7 +145,7 @@ function decodeToolCallId(toolCallId?: string): string {
145
145
  return toolCallId && toolCallId.length > 0 ? toolCallId : randomUUID();
146
146
  }
147
147
 
148
- function decodeMcpArgs(rawArgs: Record<string, Uint8Array>): Record<string, unknown> {
148
+ function decodeGenericToolArgs(rawArgs: Record<string, Uint8Array>): Record<string, unknown> {
149
149
  const decoded: Record<string, unknown> = {};
150
150
  for (const [key, value] of Object.entries(rawArgs)) {
151
151
  const text = new TextDecoder().decode(value);
@@ -158,11 +158,6 @@ function decodeMcpArgs(rawArgs: Record<string, Uint8Array>): Record<string, unkn
158
158
  return decoded;
159
159
  }
160
160
 
161
- function formatMcpToolErrorMessage(toolName: string, availableTools: string[]): string {
162
- const list = availableTools.length > 0 ? availableTools.join(", ") : "none";
163
- return `MCP tool "${toolName}" not found. Available tools: ${list}`;
164
- }
165
-
166
161
  export class CursorExecHandlers implements ICursorExecHandlers {
167
162
  constructor(private options: CursorExecBridgeOptions) {}
168
163
 
@@ -298,26 +293,10 @@ export class CursorExecHandlers implements ICursorExecHandlers {
298
293
  return toolResultMessage;
299
294
  }
300
295
 
301
- async mcp(call: CursorMcpCall) {
296
+ async tool(call: CursorToolCall) {
302
297
  const toolName = call.toolName || call.name;
303
298
  const toolCallId = decodeToolCallId(call.toolCallId);
304
- const tool = this.options.tools.get(toolName);
305
- if (!tool) {
306
- const availableTools = Array.from(this.options.tools.keys()).filter(name => name.startsWith("mcp_"));
307
- const message = formatMcpToolErrorMessage(toolName, availableTools);
308
- const toolResult: ToolResultMessage = {
309
- role: "toolResult",
310
- toolCallId,
311
- toolName,
312
- content: [{ type: "text", text: message }],
313
- isError: true,
314
- timestamp: Date.now(),
315
- };
316
- return toolResult;
317
- }
318
-
319
- const args = Object.keys(call.args ?? {}).length > 0 ? call.args : decodeMcpArgs(call.rawArgs ?? {});
320
- const toolResultMessage = await executeTool(this.options, toolName, toolCallId, args);
321
- return toolResultMessage;
299
+ const args = Object.keys(call.args).length > 0 ? call.args : decodeGenericToolArgs(call.rawArgs);
300
+ return executeTool(this.options, toolName, toolCallId, args);
322
301
  }
323
302
  }
@@ -4,7 +4,7 @@
4
4
  * Primary provider for OMP native configs. Supports all capabilities.
5
5
  */
6
6
  import * as path from "node:path";
7
- import { logger, parseFrontmatter, tryParseJson } from "@f5-sales-demo/pi-utils";
7
+ import { parseFrontmatter, tryParseJson } from "@f5-sales-demo/pi-utils";
8
8
  import { registerProvider } from "../capability";
9
9
  import { type ContextFile, contextFileCapability } from "../capability/context-file";
10
10
  import { type Extension, type ExtensionManifest, extensionCapability } from "../capability/extension";
@@ -12,7 +12,6 @@ import { type ExtensionModule, extensionModuleCapability } from "../capability/e
12
12
  import { readDirEntries, readFile } from "../capability/fs";
13
13
  import { type Hook, hookCapability } from "../capability/hook";
14
14
  import { type Instruction, instructionCapability } from "../capability/instruction";
15
- import { type MCPServer, mcpCapability } from "../capability/mcp";
16
15
  import { type Prompt, promptCapability } from "../capability/prompt";
17
16
  import { type Rule, ruleCapability } from "../capability/rule";
18
17
  import { type Settings, settingsCapability } from "../capability/settings";
@@ -26,7 +25,6 @@ import {
26
25
  buildRuleFromMarkdown,
27
26
  createSourceMeta,
28
27
  discoverExtensionModulePaths,
29
- expandEnvVarsDeep,
30
28
  getExtensionNameFromPath,
31
29
  loadFilesFromDir,
32
30
  SOURCE_PATHS,
@@ -93,133 +91,6 @@ async function findNearestProjectConfigDir(
93
91
  return null;
94
92
  }
95
93
 
96
- // MCP
97
- async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>> {
98
- const items: MCPServer[] = [];
99
- const warnings: string[] = [];
100
-
101
- const parseMcpServers = (content: string, path: string, level: "user" | "project"): MCPServer[] => {
102
- const result: MCPServer[] = [];
103
- const data = tryParseJson<{ mcpServers?: Record<string, unknown> }>(content);
104
- if (!data?.mcpServers) return result;
105
-
106
- const expanded = expandEnvVarsDeep(data.mcpServers);
107
- for (const [serverName, config] of Object.entries(expanded)) {
108
- const serverConfig = config as Record<string, unknown>;
109
-
110
- // Validate enabled: coerce string "true"/"false", warn on other types
111
- let enabled: boolean | undefined;
112
- if (serverConfig.enabled === undefined || serverConfig.enabled === null) {
113
- enabled = undefined;
114
- } else if (typeof serverConfig.enabled === "boolean") {
115
- enabled = serverConfig.enabled;
116
- } else if (typeof serverConfig.enabled === "string") {
117
- const lower = serverConfig.enabled.toLowerCase();
118
- if (lower === "false" || lower === "0") enabled = false;
119
- else if (lower === "true" || lower === "1") enabled = true;
120
- else {
121
- logger.warn(`MCP server "${serverName}": invalid enabled value "${serverConfig.enabled}", ignoring`);
122
- enabled = undefined;
123
- }
124
- } else {
125
- logger.warn(`MCP server "${serverName}": invalid enabled type ${typeof serverConfig.enabled}, ignoring`);
126
- enabled = undefined;
127
- }
128
-
129
- // Validate timeout: coerce numeric strings, warn on invalid
130
- let timeout: number | undefined;
131
- if (serverConfig.timeout === undefined || serverConfig.timeout === null) {
132
- timeout = undefined;
133
- } else if (typeof serverConfig.timeout === "number") {
134
- if (Number.isFinite(serverConfig.timeout) && serverConfig.timeout > 0) {
135
- timeout = serverConfig.timeout;
136
- } else {
137
- logger.warn(`MCP server "${serverName}": invalid timeout ${serverConfig.timeout}, ignoring`);
138
- timeout = undefined;
139
- }
140
- } else if (typeof serverConfig.timeout === "string") {
141
- const parsed = Number(serverConfig.timeout);
142
- if (Number.isFinite(parsed) && parsed > 0) {
143
- timeout = parsed;
144
- } else {
145
- logger.warn(`MCP server "${serverName}": invalid timeout "${serverConfig.timeout}", ignoring`);
146
- timeout = undefined;
147
- }
148
- } else {
149
- logger.warn(`MCP server "${serverName}": invalid timeout type ${typeof serverConfig.timeout}, ignoring`);
150
- timeout = undefined;
151
- }
152
-
153
- result.push({
154
- name: serverName,
155
- enabled,
156
- timeout,
157
- command: serverConfig.command as string | undefined,
158
- args: serverConfig.args as string[] | undefined,
159
- env: serverConfig.env as Record<string, string> | undefined,
160
- cwd: serverConfig.cwd as string | undefined,
161
- url: serverConfig.url as string | undefined,
162
- headers: serverConfig.headers as Record<string, string> | undefined,
163
- auth: serverConfig.auth as
164
- | {
165
- type: "oauth" | "apikey";
166
- credentialId?: string;
167
- tokenUrl?: string;
168
- clientId?: string;
169
- clientSecret?: string;
170
- }
171
- | undefined,
172
- oauth: serverConfig.oauth as
173
- | {
174
- clientId?: string;
175
- clientSecret?: string;
176
- redirectUri?: string;
177
- callbackPort?: number;
178
- callbackPath?: string;
179
- }
180
- | undefined,
181
- transport: serverConfig.type as "stdio" | "sse" | "http" | undefined,
182
- _source: createSourceMeta(PROVIDER_ID, path, level),
183
- });
184
- }
185
- return result;
186
- };
187
-
188
- const paths = [
189
- { path: path.join(ctx.cwd, PATHS.projectDir, "mcp.json"), level: "project" as const },
190
- { path: path.join(ctx.cwd, PATHS.projectDir, ".mcp.json"), level: "project" as const },
191
- { path: path.join(ctx.home, PATHS.userAgent, "mcp.json"), level: "user" as const },
192
- { path: path.join(ctx.home, PATHS.userAgent, ".mcp.json"), level: "user" as const },
193
- ];
194
-
195
- const contents = await Promise.allSettled(
196
- paths.map(async p => {
197
- const content = await readFile(p.path);
198
- if (content) {
199
- return { path: p.path, content, level: p.level };
200
- }
201
- return null;
202
- }),
203
- );
204
-
205
- for (const result of contents) {
206
- if (result.status === "fulfilled" && result.value) {
207
- const { path, content, level } = result.value;
208
- items.push(...parseMcpServers(content, path, level));
209
- }
210
- }
211
-
212
- return { items, warnings };
213
- }
214
-
215
- registerProvider<MCPServer>(mcpCapability.id, {
216
- id: PROVIDER_ID,
217
- displayName: SOURCE_LABEL,
218
- description: DESCRIPTION,
219
- priority: PRIORITY,
220
- load: loadMCPServers,
221
- });
222
-
223
94
  // System Prompt (SYSTEM.md)
224
95
  async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemPrompt>> {
225
96
  const items: SystemPrompt[] = [];
@@ -5,25 +5,13 @@
5
5
  * Priority: 70 (below claude.ts at 80, so user overrides in .xcsh/ take precedence)
6
6
  */
7
7
  import * as path from "node:path";
8
- import { logger } from "@f5-sales-demo/pi-utils";
9
8
  import { registerProvider } from "../capability";
10
- import { readFile } from "../capability/fs";
11
9
  import { type Hook, hookCapability } from "../capability/hook";
12
- import { type MCPServer, mcpCapability } from "../capability/mcp";
13
10
  import { type Skill, skillCapability } from "../capability/skill";
14
11
  import { type SlashCommand, slashCommandCapability } from "../capability/slash-command";
15
12
  import { type CustomTool, toolCapability } from "../capability/tool";
16
13
  import type { LoadContext, LoadResult } from "../capability/types";
17
- import {
18
- createSourceMeta,
19
- listXcshPluginRoots,
20
- loadFilesFromDir,
21
- scanSkillsFromDir,
22
- scopeToLevel,
23
- type XcshPluginRoot,
24
- } from "./helpers";
25
-
26
- import { substitutePluginRoot } from "./substitute-plugin-root";
14
+ import { listXcshPluginRoots, loadFilesFromDir, scanSkillsFromDir, scopeToLevel, type XcshPluginRoot } from "./helpers";
27
15
 
28
16
  const PROVIDER_ID = "xcsh-plugins";
29
17
  const SOURCE_LABEL = "xcsh Marketplace";
@@ -186,73 +174,6 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
186
174
  return { items, warnings };
187
175
  }
188
176
 
189
- // =============================================================================
190
- // MCP Servers
191
- // =============================================================================
192
-
193
- async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>> {
194
- const items: MCPServer[] = [];
195
- const warnings: string[] = [];
196
-
197
- const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
198
- warnings.push(...rootWarnings);
199
-
200
- for (const root of roots) {
201
- const mcpPath = path.join(root.path, ".mcp.json");
202
- const raw = await readFile(mcpPath);
203
- if (raw === null) continue; // file absent — skip silently
204
-
205
- let parsed: unknown;
206
- try {
207
- parsed = JSON.parse(raw);
208
- } catch {
209
- warnings.push(`[claude-plugins] Invalid JSON in ${mcpPath}`);
210
- logger.warn(`[claude-plugins] Invalid JSON in ${mcpPath}`);
211
- continue;
212
- }
213
-
214
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
215
- const config = parsed as { mcpServers?: Record<string, unknown> };
216
- if (!config.mcpServers || typeof config.mcpServers !== "object") continue;
217
-
218
- for (const [serverName, serverCfg] of Object.entries(config.mcpServers)) {
219
- if (!serverCfg || typeof serverCfg !== "object" || Array.isArray(serverCfg)) continue;
220
- const raw = serverCfg as {
221
- enabled?: boolean;
222
- timeout?: number;
223
- command?: string;
224
- args?: string[];
225
- env?: Record<string, string>;
226
- cwd?: string;
227
- url?: string;
228
- headers?: Record<string, string>;
229
- auth?: MCPServer["auth"];
230
- oauth?: MCPServer["oauth"];
231
- type?: string;
232
- };
233
- const namespacedName = root.plugin ? `${root.plugin}:${serverName}` : serverName;
234
- const server: MCPServer = {
235
- name: namespacedName,
236
- ...(raw.enabled !== undefined && { enabled: raw.enabled }),
237
- ...(raw.timeout !== undefined && { timeout: raw.timeout }),
238
- ...(raw.command !== undefined && { command: substitutePluginRoot(raw.command, root.path) }),
239
- ...(raw.args !== undefined && { args: substitutePluginRoot(raw.args, root.path) }),
240
- ...(raw.env !== undefined && { env: substitutePluginRoot(raw.env, root.path) }),
241
- ...(raw.cwd !== undefined && { cwd: substitutePluginRoot(raw.cwd, root.path) }),
242
- ...(raw.url !== undefined && { url: raw.url }),
243
- ...(raw.headers !== undefined && { headers: raw.headers }),
244
- ...(raw.auth !== undefined && { auth: raw.auth }),
245
- ...(raw.oauth !== undefined && { oauth: raw.oauth }),
246
- ...(raw.type !== undefined && { transport: raw.type as MCPServer["transport"] }),
247
- _source: createSourceMeta(PROVIDER_ID, mcpPath, scopeToLevel(root.scope)),
248
- };
249
- items.push(server);
250
- }
251
- }
252
-
253
- return { items, warnings };
254
- }
255
-
256
177
  // =============================================================================
257
178
  // Provider Registration
258
179
  // =============================================================================
@@ -288,11 +209,3 @@ registerProvider<CustomTool>(toolCapability.id, {
288
209
  priority: PRIORITY,
289
210
  load: loadTools,
290
211
  });
291
-
292
- registerProvider<MCPServer>(mcpCapability.id, {
293
- id: PROVIDER_ID,
294
- displayName: SOURCE_LABEL,
295
- description: "Load MCP servers from marketplace plugin .mcp.json files",
296
- priority: PRIORITY,
297
- load: loadMCPServers,
298
- });