fa-mcp-sdk 0.12.15 → 0.12.19

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.
@@ -58,7 +58,7 @@
58
58
  "dependencies": {
59
59
  "@modelcontextprotocol/sdk": "^1.29.0",
60
60
  "dotenv": "^17.4.1",
61
- "fa-mcp-sdk": "^0.12.15"
61
+ "fa-mcp-sdk": "^0.12.19"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/express": "^5.0.6",
@@ -1 +1 @@
1
- {"version":3,"file":"home-api.d.ts","sourceRoot":"","sources":["../../../src/core/web/home-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA6B5C,wBAAsB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAkHhF"}
1
+ {"version":3,"file":"home-api.d.ts","sourceRoot":"","sources":["../../../src/core/web/home-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA6B5C,wBAAsB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4IhF"}
@@ -39,7 +39,29 @@ export async function handleHomeInfo(_req, res) {
39
39
  const { httpComponents } = global.__MCP_PROJECT_DATA__;
40
40
  const toolsOrFn = global.__MCP_PROJECT_DATA__.tools;
41
41
  let tools = typeof toolsOrFn === 'function' ? await toolsOrFn(httpArgs) : toolsOrFn;
42
- const { getConsulUIAddress = (_s) => '' } = getProjectData();
42
+ const { getConsulUIAddress = (_s) => '', toolPrompt } = getProjectData();
43
+ // Honestly probe every tool for a non-empty tool-specific prompt. The `tool_prompt` prompt is
44
+ // advertised over MCP unconditionally, but on the home page it should appear only when at least
45
+ // one tool actually returns a non-empty prompt. We collect the names of such tools so the viewer
46
+ // can offer a dropdown, and drop `tool_prompt` from the home prompt list when none qualify.
47
+ const toolPromptTools = [];
48
+ let homePrompts = prompts;
49
+ if (typeof toolPrompt === 'function') {
50
+ for (const tool of tools) {
51
+ try {
52
+ const text = await toolPrompt({ method: 'prompts/get', params: { name: 'tool_prompt', arguments: { tool: tool.name } } }, { tool: tool.name });
53
+ if (typeof text === 'string' && text.trim()) {
54
+ toolPromptTools.push(tool.name);
55
+ }
56
+ }
57
+ catch {
58
+ // A tool whose prompt resolver throws is treated as having no prompt.
59
+ }
60
+ }
61
+ }
62
+ if (toolPromptTools.length === 0) {
63
+ homePrompts = prompts.filter((p) => p.name !== 'tool_prompt');
64
+ }
43
65
  // Build footer HTML
44
66
  const footerParts = [];
45
67
  if (repo) {
@@ -111,10 +133,11 @@ export async function handleHomeInfo(_req, res) {
111
133
  logoSvg,
112
134
  toolsCount: tools.length,
113
135
  resourcesCount: resources.length,
114
- promptsCount: prompts.length,
136
+ promptsCount: homePrompts.length,
115
137
  tools,
116
138
  resources,
117
- prompts,
139
+ prompts: homePrompts,
140
+ toolPromptTools,
118
141
  db,
119
142
  openAPI: !!httpComponents?.apiRouter,
120
143
  consul,
@@ -1 +1 @@
1
- {"version":3,"file":"home-api.js","sourceRoot":"","sources":["../../../src/core/web/home-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"home-api.js","sourceRoot":"","sources":["../../../src/core/web/home-api.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,9 @@
2
2
  let toolsData = [];
3
3
  let resourcesData = [];
4
4
  let promptsData = [];
5
+ // Names of tools that expose a non-empty `tool_prompt`, computed honestly on the server by probing
6
+ // every tool. Drives the dropdown shown for the `tool_prompt` row in the prompts modal.
7
+ let toolPromptTools = [];
5
8
 
6
9
  // Parse an /mcp response. The Streamable HTTP transport may answer either as plain JSON or as a
7
10
  // Server-Sent Events stream ("event: message\ndata: {...}"), depending on negotiation. Handle both so
@@ -187,6 +190,7 @@ async function loadPageData() {
187
190
  toolsData = data.tools || [];
188
191
  resourcesData = data.resources || [];
189
192
  promptsData = data.prompts || [];
193
+ toolPromptTools = data.toolPromptTools || [];
190
194
 
191
195
  // Render page info
192
196
  renderPageInfo(data);
@@ -300,14 +304,19 @@ function generatePromptsTableRows(prompts) {
300
304
  return '<tr><td colspan="2" class="loading-cell">No prompts available</td></tr>';
301
305
  }
302
306
  return prompts
303
- .map(
304
- (prompt, index) =>
305
- `<tr>
307
+ .map((prompt, index) => {
308
+ // The `tool_prompt` row gets a tool picker instead of a static "prompt" link: choosing a tool
309
+ // fetches that tool's prompt. The list of tools is restricted to those with a non-empty prompt.
310
+ const actions =
311
+ prompt.name === 'tool_prompt'
312
+ ? `<span class="tool-prompt-actions"><a class="detail-link" id="prompts-toggle-details-${index}" onclick="togglePromptDetails('prompts', ${index}, 'details')">details</a> / <select class="tool-prompt-select" id="tool-prompt-select-${index}" onchange="loadToolPrompt(${index}, this.value)"><option value="">— select tool —</option>${toolPromptTools.map((name) => `<option value="${escapeAttr(name)}">${escapeHtml(name)}</option>`).join('')}</select></span>`
313
+ : `<a class="detail-link" id="prompts-toggle-details-${index}" onclick="togglePromptDetails('prompts', ${index}, 'details')">details</a>
314
+ /
315
+ <a class="detail-link" id="prompts-toggle-prompt-${index}" onclick="togglePromptDetails('prompts', ${index}, 'prompt')">prompt</a>`;
316
+ return `<tr>
306
317
  <td><code>${prompt.name}</code></td>
307
318
  <td>
308
- <a class="detail-link" id="prompts-toggle-details-${index}" onclick="togglePromptDetails('prompts', ${index}, 'details')">details</a>
309
- /
310
- <a class="detail-link" id="prompts-toggle-prompt-${index}" onclick="togglePromptDetails('prompts', ${index}, 'prompt')">prompt</a>
319
+ ${actions}
311
320
  </td>
312
321
  </tr>
313
322
  <tr id="prompts-detail-${index}" class="detail-row" style="display: none;">
@@ -318,8 +327,8 @@ function generatePromptsTableRows(prompts) {
318
327
  <div class="prompt-content" style="display: none;"></div>
319
328
  </div>
320
329
  </td>
321
- </tr>`,
322
- )
330
+ </tr>`;
331
+ })
323
332
  .join('');
324
333
  }
325
334
 
@@ -391,7 +400,10 @@ async function togglePromptDetails(sectionName, index, displayType) {
391
400
  // Show the detail row with loading state
392
401
  detailRow.style.display = 'table-row';
393
402
  currentToggleLink.textContent = 'hide';
394
- otherToggleLink.textContent = displayType === 'details' ? 'prompt' : 'details';
403
+ // The `tool_prompt` row has no separate "prompt" link (it uses a dropdown), so guard against null.
404
+ if (otherToggleLink) {
405
+ otherToggleLink.textContent = displayType === 'details' ? 'prompt' : 'details';
406
+ }
395
407
  loadingSpinner.style.display = 'block';
396
408
  jsonContent.style.display = 'none';
397
409
  promptContent.style.display = 'none';
@@ -465,7 +477,89 @@ async function togglePromptDetails(sectionName, index, displayType) {
465
477
  // Hide the detail row
466
478
  detailRow.style.display = 'none';
467
479
  toggleLinkDetails.textContent = 'details';
468
- toggleLinkPrompt.textContent = 'prompt';
480
+ if (toggleLinkPrompt) {
481
+ toggleLinkPrompt.textContent = 'prompt';
482
+ }
483
+ }
484
+ }
485
+
486
+ // Fetch and display the prompt of a specific tool via the `tool_prompt` MCP prompt. Triggered by the
487
+ // tool dropdown in the prompts modal; passes the selected tool name in the `tool` argument.
488
+ // eslint-disable-next-line unused-imports/no-unused-vars
489
+ async function loadToolPrompt(index, toolName) {
490
+ const detailRow = document.getElementById('prompts-detail-' + index);
491
+ const loadingSpinner = detailRow.querySelector('.loading-spinner');
492
+ const jsonContent = detailRow.querySelector('.json-content');
493
+ const promptContent = detailRow.querySelector('.prompt-content');
494
+ const toggleLinkDetails = document.getElementById('prompts-toggle-details-' + index);
495
+
496
+ // Empty selection collapses the detail row and restores the "details" link label.
497
+ if (!toolName) {
498
+ detailRow.style.display = 'none';
499
+ if (toggleLinkDetails) {
500
+ toggleLinkDetails.textContent = 'details';
501
+ }
502
+ return;
503
+ }
504
+
505
+ detailRow.style.display = 'table-row';
506
+ if (toggleLinkDetails) {
507
+ toggleLinkDetails.textContent = 'details';
508
+ }
509
+ loadingSpinner.style.display = 'block';
510
+ jsonContent.style.display = 'none';
511
+ promptContent.style.display = 'none';
512
+
513
+ try {
514
+ const response = await fetch('/mcp', {
515
+ method: 'POST',
516
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' },
517
+ body: JSON.stringify({
518
+ jsonrpc: '2.0',
519
+ id: Date.now(),
520
+ method: 'prompts/get',
521
+ params: { name: 'tool_prompt', arguments: { tool: toolName } },
522
+ }),
523
+ });
524
+
525
+ if (!response.ok) {
526
+ let errorData = '';
527
+ try {
528
+ errorData = await response.text();
529
+ } catch {
530
+ //
531
+ }
532
+ errorData = [response.statusText || '', errorData].join('. ');
533
+ throw new Error('HTTP ' + response.status + (errorData ? ': ' + errorData : ''));
534
+ }
535
+
536
+ const result = await parseMcpJsonResponse(response);
537
+ const messages = result.result?.messages || [];
538
+ let promptText = '';
539
+
540
+ messages.forEach((msg, i) => {
541
+ if (i > 0) {
542
+ promptText += '\n\n---\n\n';
543
+ }
544
+ promptText += 'Role: ' + msg.role + '\n\n';
545
+ if (typeof msg.content === 'string') {
546
+ promptText += msg.content;
547
+ } else if (msg.content?.text) {
548
+ promptText += msg.content.text;
549
+ } else {
550
+ promptText += JSON.stringify(msg.content, null, 2);
551
+ }
552
+ });
553
+
554
+ loadingSpinner.style.display = 'none';
555
+ promptContent.style.display = 'block';
556
+ promptContent.innerHTML = '<pre class="json-content">' + escapeHtml(promptText) + '</pre>';
557
+ addCopyButton(promptContent.querySelector('.json-content'));
558
+ } catch (error) {
559
+ loadingSpinner.style.display = 'none';
560
+ promptContent.style.display = 'block';
561
+ promptContent.innerHTML =
562
+ '<div class="error-message">Failed to load tool prompt: ' + escapeHtml(error.message) + '</div>';
469
563
  }
470
564
  }
471
565
 
@@ -625,6 +719,11 @@ function escapeHtml(text) {
625
719
  return div.innerHTML;
626
720
  }
627
721
 
722
+ // Escape a string for safe use inside a double-quoted HTML attribute (e.g. <option value="...">).
723
+ function escapeAttr(text) {
724
+ return escapeHtml(text).replace(/"/g, '&quot;');
725
+ }
726
+
628
727
  // Copy to clipboard functionality
629
728
  function addCopyButton(contentElement) {
630
729
  if (!contentElement || contentElement.hasAttribute('data-copy-added')) {
@@ -273,6 +273,17 @@ select {
273
273
  padding: 3px 6px;
274
274
  }
275
275
 
276
+ .tool-prompt-actions {
277
+ display: inline-flex;
278
+ align-items: center;
279
+ gap: 6px;
280
+ white-space: nowrap;
281
+ }
282
+
283
+ .tool-prompt-select {
284
+ vertical-align: middle;
285
+ }
286
+
276
287
  input:focus, select:focus, textarea:focus {
277
288
  outline: none;
278
289
  border-color: #0065ff;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fa-mcp-sdk",
3
3
  "productName": "FA MCP SDK",
4
- "version": "0.12.15",
4
+ "version": "0.12.19",
5
5
  "description": "Core infrastructure and templates for building Model Context Protocol (MCP) servers with TypeScript",
6
6
  "type": "module",
7
7
  "main": "dist/core/index.js",
package/scripts/fcp.js CHANGED
@@ -1,16 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Atomically save content to a file.
4
+ * The sanctioned write/delete channel for files under .claude/ (and any other path).
5
+ *
6
+ * Direct Write/Edit on .claude/** is denied by settings.json, and direct shell modification of .claude/
7
+ * (rm, mv, cp, output redirection) is blocked by the harness. This script is the ONE allowed way to create,
8
+ * overwrite or delete such files, because it runs as `node` (an allowlisted command). See the /edit-claude-files
9
+ * skill for the full protocol.
5
10
  *
6
11
  * Usage:
7
- * node scripts/fcp.js <filePath> <contentFilePath>
12
+ * node scripts/fcp.js <filePath> <contentFilePath> # create/overwrite <filePath> with the contents of the temp file
13
+ * node scripts/fcp.js --rm <path> [<path> ...] # delete the given path(s) (files or directories, recursive)
8
14
  *
9
15
  * <filePath> — destination path (absolute or relative to project root)
10
16
  * <contentFilePath> — path to a temp file whose contents will be written to <filePath>
11
17
  *
12
- * The script reads the content from <contentFilePath> and writes it to <filePath>,
13
- * creating parent directories if needed.
18
+ * Write mode reads the content from <contentFilePath> and writes it to <filePath>, creating parent directories if
19
+ * needed. Delete mode (--rm / --delete) removes each target path; missing paths are reported, not an error.
14
20
  */
15
21
 
16
22
  import fs from 'fs';
@@ -22,10 +28,31 @@ const __filename = fileURLToPath(import.meta.url);
22
28
  const __dirname = dirname(__filename);
23
29
  const projectRoot = path.resolve(__dirname, '..');
24
30
 
25
- const [, , rawTarget, rawSource] = process.argv;
31
+ const argv = process.argv.slice(2);
32
+
33
+ // Delete mode: `--rm` / `--delete` followed by one or more paths to remove.
34
+ if (argv[0] === '--rm' || argv[0] === '--delete') {
35
+ const targets = argv.slice(1);
36
+ if (!targets.length) {
37
+ console.error('Usage: node scripts/fcp.js --rm <path> [<path> ...]');
38
+ process.exit(1);
39
+ }
40
+ for (const raw of targets) {
41
+ const target = path.isAbsolute(raw) ? raw : path.resolve(projectRoot, raw);
42
+ if (fs.existsSync(target)) {
43
+ fs.rmSync(target, { recursive: true, force: true });
44
+ console.log(`Deleted: ${target}`);
45
+ } else {
46
+ console.log(`Already absent: ${target}`);
47
+ }
48
+ }
49
+ process.exit(0);
50
+ }
51
+
52
+ const [rawTarget, rawSource] = argv;
26
53
 
27
54
  if (!rawTarget || !rawSource) {
28
- console.error('Usage: node scripts/fcp.js <filePath> <contentFilePath>');
55
+ console.error('Usage: node scripts/fcp.js <filePath> <contentFilePath> | node scripts/fcp.js --rm <path> [...]');
29
56
  process.exit(1);
30
57
  }
31
58
 
@@ -0,0 +1,33 @@
1
+ import { TPromptContentFunction } from '../../core/index.js';
2
+
3
+ /**
4
+ * Tool-specific prompts served by the built-in `tool_prompt` prompt.
5
+ *
6
+ * The `tool_prompt` prompt is always advertised over MCP, but it returns a non-empty string only
7
+ * for the tools listed here. Clients pass the tool name in the required `tool` argument; the home
8
+ * page catalog viewer additionally shows a dropdown of the tools that have a non-empty prompt.
9
+ *
10
+ * Add an entry keyed by the MCP tool name to attach usage instructions to that tool.
11
+ */
12
+ const TOOL_PROMPTS: Record<string, string> = {
13
+ example_tool: `You are using the "example_tool" tool, which processes a text input and returns the result.
14
+
15
+ - Pass the text to process in the required "query" field.
16
+ - Keep the input concise; send one logical request at a time.
17
+ - Use the returned text as the processed output — do not re-process it again unless the user asks.`,
18
+
19
+ example_search: `You are using the "example_search" tool, which performs a search with pagination and filtering.
20
+
21
+ - Put the search text in the required "query" field.
22
+ - Use "limit" (1-100, default 20) to cap the number of results; request only as many as you need.
23
+ - Use "threshold" (0-1) to drop low-similarity matches when precision matters more than recall.
24
+ - Read results from the "results" array; "total" reports how many matches exist overall.`,
25
+ };
26
+
27
+ export const toolPrompt: TPromptContentFunction = (_request, args) => {
28
+ const tool = args?.tool;
29
+ if (!tool) {
30
+ return '';
31
+ }
32
+ return TOOL_PROMPTS[tool] ?? '';
33
+ };
@@ -6,6 +6,7 @@ import { customResources } from './custom-resources.js';
6
6
  import { AGENT_BRIEF } from './prompts/agent-brief.js';
7
7
  import { AGENT_PROMPT } from './prompts/agent-prompt.js';
8
8
  import { customPrompts } from './prompts/custom-prompts.js';
9
+ import { toolPrompt } from './prompts/tool-prompts.js';
9
10
  import { handleToolCall } from './tools/handle-tool-call.js';
10
11
  import { tools } from './tools/tools.js';
11
12
 
@@ -27,6 +28,7 @@ const startProject = async (): Promise<void> => {
27
28
  // Prompts
28
29
  agentBrief: AGENT_BRIEF,
29
30
  agentPrompt: AGENT_PROMPT,
31
+ toolPrompt,
30
32
  customPrompts,
31
33
  usedHttpHeaders: [
32
34
  { name: 'Authorization', description: 'JWT Token issued on request' },
@@ -101,9 +101,8 @@ export const tools: Tool[] = [
101
101
  // work can exceed the 30s tool timeout or you want a cancellable, pollable operation.
102
102
  name: 'example_long_task',
103
103
  title: 'Example: long-running task',
104
- description:
105
- 'Example long-running tool that emits progress and supports cancellation. ' +
106
- 'Demonstrates task-augmented execution (§8.7) — call it with a `task` param to run it as a task.',
104
+ description: `Example long-running tool that emits progress and supports cancellation.
105
+ Demonstrates task-augmented execution call it with a 'task' param to run it as a task.`,
107
106
  inputSchema: {
108
107
  $schema: JSON_SCHEMA_2020_12,
109
108
  type: 'object',