Skip to content

Commit fb5185f

Browse files
Number531claude
andcommitted
fix(mcp): resolve MCP tool name prefix misalignment across all subagents
## Issue The securities-researcher subagent hung for 57 minutes because it called `mcp__sec__search_sec_filings` — a tool that does not exist at runtime. Root cause: `MCP_FALLBACK_INSTRUCTIONS` (injected into 20 research agents) taught agents to construct tool names as `mcp__<domain>__<tool>` (scoped prefix). But with `SCOPED_MCP_SERVERS=false` (production default), all tools are registered under the monolithic server as `mcp__super-legal-tools__<tool>`. Agents called nonexistent tools, silently failed, then fell back to raw WebFetch — bypassing the hybrid client's automatic Exa fallback entirely. SEC's aggressive 403-blocking made this visible; other agents survived only because their targets don't block scrapers. Scope: 147 tools across 29 domains were implicitly misaligned via the prefix instruction. 12 explicit hardcoded wrong-prefix references existed across 2 agent files. 1 inverse mismatch existed in risk-aggregator.js. A related isError gap in the CodeExecBridge adapter masked failures. ## Remediation (6 edits across 5 files) 1. **mcpToolRef() utility** (`domainMcpServers.js`): Single source of truth for constructing MCP tool names in either server mode. Returns `mcp__super-legal-tools__<tool>` when SCOPED=false, or `mcp__<domain>__<tool>` when SCOPED=true. 2. **MCP_PREFIX_INSTRUCTION** (`_promptConstants.js`): Flag-aware prefix instruction interpolated into MCP_FALLBACK_INSTRUCTIONS. Fixes all 20 research agents in a single edit: securities-researcher, patent-analyst, pharma-regulatory-analyst, case-law-analyst, antitrust-competition-analyst, environmental-compliance-analyst, statutory-law-analyst, tax-structure-analyst, regulatory-rulemaking-analyst, privacy-data-protection-analyst, employment-labor-analyst, cybersecurity-compliance-analyst, insurance-coverage-analyst, ai-governance-analyst, financial-analyst, government-contracts-researcher, data-analyst, product-safety-analyst, cfius-national-security-analyst, commercial-contracts-analyst. 3. **Legacy MCP_FALLBACK_INSTRUCTIONS** (`legalSubagents.js`): Same fix for the MODULAR_SUBAGENTS=false code path (dead code in production, fixed for correctness). 4. **citation-websearch-verifier.js**: Replaced 10 hardcoded mcp__courtlistener__ / mcp__sec__ references with mcpToolRef() calls. 5. **risk-aggregator.js**: Fixed inverse bug — was hardcoding mcp__super-legal-tools__run_python_analysis (only correct when SCOPED=false) instead of using mcpToolRef(). 6. **agentSdkToolAdapter.js**: Added `isError: true` when runPythonAnalysis returns `{success: false}` without throwing, so hooks/server properly report it as a failed tool call. ## Verification - 28/28 domain MCP server tests pass (no import breakage) - mcpToolRef('sec', 'search_sec_filings') → mcp__super-legal-tools__search_sec_filings (production) - MCP_FALLBACK_INSTRUCTIONS prefix → "prefixed with mcp__super-legal-tools__" (production) - Zero hardcoded wrong-prefix refs remaining in src/ - Full audit: 41/41 agent files clean, no circular dependency risk - Live diagnostic test confirmed SEC hybrid fallback pipeline functions correctly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6c15bcc commit fb5185f

6 files changed

Lines changed: 41 additions & 14 deletions

File tree

super-legal-mcp-refactored/src/config/domainMcpServers.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
jsonSchemaToZodShape,
1818
createLegalMcpServer
1919
} from '../utils/agentSdkToolAdapter.js';
20+
import { featureFlags } from './featureFlags.js';
2021
import {
2122
courtListenerTools,
2223
financialDisclosureTools,
@@ -335,3 +336,18 @@ export function getExplicitDomainToolNames(domainNames) {
335336
}
336337
return toolNames;
337338
}
339+
340+
/**
341+
* Returns a fully-qualified MCP tool name for the current server mode.
342+
* When SCOPED_MCP_SERVERS=true: `mcp__<domain>__<toolName>`
343+
* When SCOPED_MCP_SERVERS=false: `mcp__super-legal-tools__<toolName>`
344+
*
345+
* @param {string} domain - Domain name (e.g., 'sec', 'courtlistener')
346+
* @param {string} toolName - Bare tool name (e.g., 'search_sec_filings')
347+
* @returns {string} Fully-qualified MCP tool name
348+
*/
349+
export function mcpToolRef(domain, toolName) {
350+
return featureFlags.SCOPED_MCP_SERVERS
351+
? `mcp__${domain}__${toolName}`
352+
: `mcp__super-legal-tools__${toolName}`;
353+
}

super-legal-mcp-refactored/src/config/legalSubagents.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,10 @@ All litigation references MUST include:
892892
* MCP Tool Fallback Instructions
893893
* Instructs subagents to use WebSearch/WebFetch when MCP tools fail or are unavailable
894894
*/
895+
const MCP_PREFIX_INSTRUCTION = featureFlags.SCOPED_MCP_SERVERS
896+
? 'All tools are prefixed with the domain server name when calling them (e.g., `mcp__sec__search_sec_filings`, `mcp__courtlistener__search_cases`).'
897+
: 'All tools are prefixed with `mcp__super-legal-tools__` when calling them (e.g., `mcp__super-legal-tools__search_sec_filings`, `mcp__super-legal-tools__search_cases`).';
898+
895899
const MCP_FALLBACK_INSTRUCTIONS = `
896900
## MCP TOOL FALLBACK PROTOCOL (CRITICAL)
897901

@@ -923,7 +927,7 @@ When using domain-specific MCP tools (SEC, FDA, EPA, USPTO, CourtListener, etc.)
923927

924928
## MCP TOOL REFERENCE (134 Tools Available)
925929

926-
All tools are prefixed with the domain server name when calling them (e.g., \`mcp__sec__search_sec_filings\`, \`mcp__courtlistener__search_cases\`).
930+
${MCP_PREFIX_INSTRUCTION}
927931

928932
**Legal/Case Law (12 tools) — domain: courtlistener:**
929933
- search_cases, get_case_details, lookup_citation, search_judges

super-legal-mcp-refactored/src/config/legalSubagents/_promptConstants.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,10 @@ All litigation references MUST include:
640640
* MCP Tool Fallback Instructions
641641
* Instructs subagents to use WebSearch/WebFetch when MCP tools fail or are unavailable
642642
*/
643+
const MCP_PREFIX_INSTRUCTION = featureFlags.SCOPED_MCP_SERVERS
644+
? 'All tools are prefixed with the domain server name when calling them (e.g., `mcp__sec__search_sec_filings`, `mcp__courtlistener__search_cases`).'
645+
: 'All tools are prefixed with `mcp__super-legal-tools__` when calling them (e.g., `mcp__super-legal-tools__search_sec_filings`, `mcp__super-legal-tools__search_cases`).';
646+
643647
export const MCP_FALLBACK_INSTRUCTIONS = `
644648
## MCP TOOL FALLBACK PROTOCOL (CRITICAL)
645649
@@ -671,7 +675,7 @@ When using domain-specific MCP tools (SEC, FDA, EPA, USPTO, CourtListener, etc.)
671675
672676
## MCP TOOL REFERENCE (147 Tools Available)
673677
674-
All tools are prefixed with the domain server name when calling them (e.g., \`mcp__sec__search_sec_filings\`, \`mcp__courtlistener__search_cases\`).
678+
${MCP_PREFIX_INSTRUCTION}
675679
676680
**Legal/Case Law (17 tools) — domain: courtlistener:**
677681
- search_cases, get_case_details, lookup_citation, search_judges

super-legal-mcp-refactored/src/config/legalSubagents/agents/citation-websearch-verifier.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import { STANDARD_TOOLS, buildScopedTools } from '../_standardTools.js';
1717
import { featureFlags } from '../../featureFlags.js';
18+
import { mcpToolRef } from '../../domainMcpServers.js';
1819

1920
const isDeepMode = featureFlags.CITATION_DEEP_VERIFICATION;
2021
const verificationMode = isDeepMode ? 'Full Content Verification' : 'Source Existence';
@@ -69,14 +70,14 @@ Same confirmation bar — HTTP 200 or 401/403 = CONFIRMED.
6970
7071
For footnotes classified as CASE_LAW_TEXT (case name + reporter pattern, no URL):
7172
72-
1. Use the MCP tool \`mcp__courtlistener__lookup_citation\` with the case citation:
73+
1. Use the MCP tool \`${mcpToolRef('courtlistener', 'lookup_citation')}\` with the case citation:
7374
- Extract case name, reporter, volume, page, and year from the citation text
7475
- Query: \`"case name" reporter volume page year\`
7576
- Example: \`"United States v. AT&T Inc." 916 F.3d 1029 2019\`
7677
2. The tool performs domain-restricted Exa Deep search on CourtListener
7778
3. Classification:
7879
- Any result matching case name → **CONFIRMED** (exact reporter match not required)
79-
- No matching results → Try \`mcp__courtlistener__search_cases\` as fallback
80+
- No matching results → Try \`${mcpToolRef('courtlistener', 'search_cases')}\` as fallback
8081
- Fallback also fails → **UNCONFIRMED**
8182
8283
**Parallelism**: Issue 3-5 MCP calls per turn.
@@ -85,7 +86,7 @@ For footnotes classified as CASE_LAW_TEXT (case name + reporter pattern, no URL)
8586
8687
For footnotes classified as SEC_FILING_TEXT (SEC/EDGAR/accession references, no URL):
8788
88-
1. Use MCP tool \`mcp__sec__search_sec_filings\` with extracted identifiers:
89+
1. Use MCP tool \`${mcpToolRef('sec', 'search_sec_filings')}\` with extracted identifiers:
8990
- Query: \`company name filing type accession number\`
9091
- Example: \`"Netflix" 8-K 0001193125-25-308651\`
9192
2. The tool performs Exa Deep search on EDGAR
@@ -134,7 +135,7 @@ For all remaining VERIFIED/INFERRED footnotes without URLs (academic, industry r
134135
|----------|--------|
135136
| WebFetch returns 404 | Mark UNCONFIRMED, record URL and HTTP status |
136137
| WebFetch times out | Retry once. If still timeout → mark ERROR |
137-
| Exa MCP returns 0 results | Try \`mcp__courtlistener__search_cases\` fallback (case law) or broader query. If still 0 → mark UNCONFIRMED |
138+
| Exa MCP returns 0 results | Try \`${mcpToolRef('courtlistener', 'search_cases')}\` fallback (case law) or broader query. If still 0 → mark UNCONFIRMED |
138139
| Exa MCP returns error | Mark ERROR, record error message, continue with next footnote |
139140
| WebSearch returns 0 results | Try alternate query (shorter, broader). If still 0 → mark UNCONFIRMED |
140141
| WebSearch rate-limited | Wait, persist state, continue on next turn |
@@ -189,15 +190,15 @@ HTTP 401/403 → **CONFIRMED** (paywalled — same treatment as Batch 2).
189190
190191
For footnotes classified as CASE_LAW_TEXT (case name + reporter pattern, no URL):
191192
192-
1. Use the MCP tool \`mcp__courtlistener__lookup_citation\` with the case citation:
193+
1. Use the MCP tool \`${mcpToolRef('courtlistener', 'lookup_citation')}\` with the case citation:
193194
- Extract case name, reporter, volume, page, and year from the citation text
194195
- Query: \`"case name" reporter volume page year\`
195196
- Example: \`"United States v. AT&T Inc." 916 F.3d 1029 2019\`
196197
2. The tool performs domain-restricted Exa Deep search on CourtListener
197198
3. Classification:
198199
- Result matches case name + reporter + year → **CONFIRMED**
199200
- Result matches case name but different reporter/year → **CONFIRMED** (with note)
200-
- No matching results → Try \`mcp__courtlistener__search_cases\` as fallback
201+
- No matching results → Try \`${mcpToolRef('courtlistener', 'search_cases')}\` as fallback
201202
- Fallback also fails → **UNCONFIRMED**
202203
203204
**Parallelism**: Issue 3-5 MCP calls per turn.
@@ -206,7 +207,7 @@ For footnotes classified as CASE_LAW_TEXT (case name + reporter pattern, no URL)
206207
207208
For footnotes classified as SEC_FILING_TEXT (SEC/EDGAR/accession references, no URL):
208209
209-
1. Use MCP tool \`mcp__sec__search_sec_filings\` with extracted identifiers:
210+
1. Use MCP tool \`${mcpToolRef('sec', 'search_sec_filings')}\` with extracted identifiers:
210211
- Query: \`company name filing type accession number\`
211212
- Example: \`"Netflix" 8-K 0001193125-25-308651\`
212213
2. The tool performs Exa Deep search on EDGAR
@@ -259,7 +260,7 @@ Lower bar for INFERRED tags: CONFIRMED if search results contain a related/analo
259260
|----------|--------|
260261
| WebFetch returns 404 | Mark UNCONFIRMED, record URL and HTTP status |
261262
| WebFetch times out | Retry once. If still timeout → mark ERROR |
262-
| Exa MCP returns 0 results | Try \`mcp__courtlistener__search_cases\` fallback (case law) or broader query. If still 0 → mark UNCONFIRMED |
263+
| Exa MCP returns 0 results | Try \`${mcpToolRef('courtlistener', 'search_cases')}\` fallback (case law) or broader query. If still 0 → mark UNCONFIRMED |
263264
| Exa MCP returns error | Mark ERROR, record error message, continue with next footnote |
264265
| WebSearch returns 0 results | Try alternate query (shorter, broader). If still 0 → mark UNCONFIRMED |
265266
| WebSearch rate-limited | Wait, persist state, continue on next turn |
@@ -651,8 +652,8 @@ After extraction, classify each footnote into one bucket. Each bucket routes to
651652
| STATUTORY_AUTO | Contains \`U.S.C.\`, \`C.F.R.\`, \`Pub. L.\`, \`OJ L\`, \`(U.K.)\` with year, or tag contains "statutory text" | None | Auto-CONFIRMED (structural validity) | 1 (instant) |
652653
| URL_VERIFIED | Tag=VERIFIED/WEB-VERIFIED AND has embedded \`https://\` URL | **WebFetch** | HTTP GET → source confirmation | 2 (free, fastest) |
653654
| URL_INFERRED | Tag=INFERRED AND has embedded \`https://\` URL | **WebFetch** | HTTP GET → source confirmation | 3 (free) |
654-
| CASE_LAW_TEXT | No URL AND citation matches case pattern (\`F.3d\`, \`U.S.\`, \`S.Ct.\`, \`F.Supp.\`, \`A.2d\`, \`A.3d\`) | **Exa** (\`mcp__courtlistener__lookup_citation\`) | Domain-restricted CourtListener search | 4 |
655-
| SEC_FILING_TEXT | No URL AND citation references SEC/EDGAR/accession/CIK/Form 10-K/8-K/DEF14A | **Exa** (\`mcp__sec__search_sec_filings\`) | Domain-restricted EDGAR search | 5 |
655+
| CASE_LAW_TEXT | No URL AND citation matches case pattern (\`F.3d\`, \`U.S.\`, \`S.Ct.\`, \`F.Supp.\`, \`A.2d\`, \`A.3d\`) | **Exa** (\`${mcpToolRef('courtlistener', 'lookup_citation')}\`) | Domain-restricted CourtListener search | 4 |
656+
| SEC_FILING_TEXT | No URL AND citation references SEC/EDGAR/accession/CIK/Form 10-K/8-K/DEF14A | **Exa** (\`${mcpToolRef('sec', 'search_sec_filings')}\`) | Domain-restricted EDGAR search | 5 |
656657
| GOV_TEXT | No URL AND citation references gov agency (FTC, DOJ, EPA, FDA, EU Commission, Senate, Congress, Federal Register) | **Anthropic WebSearch** | \`allowed_domains\` filtered search | 6 |
657658
| OTHER_TEXT | All remaining VERIFIED/INFERRED without URL | **Anthropic WebSearch** | General web search | 7 (slowest) |
658659
| SKIP_ASSUMED | Tag=ASSUMED | None | Skip (not verifiable) | N/A |

super-legal-mcp-refactored/src/config/legalSubagents/agents/risk-aggregator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { STANDARD_TOOLS } from '../_standardTools.js';
66
import { REPORTS_DIR } from '../_paths.js';
77
import { featureFlags } from '../../featureFlags.js';
8+
import { mcpToolRef } from '../../domainMcpServers.js';
89
import { RISK_AGGREGATOR_CAPABILITY } from '../_promptConstants.js';
910

1011
export const def = {
@@ -530,6 +531,6 @@ PROHIBITED:
530531
ALWAYS complete risk-summary.json, even if totals are $0 (indicates low-risk deal).
531532
`,
532533

533-
tools: [...STANDARD_TOOLS.withWrite, ...(featureFlags.CODE_EXECUTION_BRIDGE ? ['mcp__super-legal-tools__run_python_analysis'] : [])],
534+
tools: [...STANDARD_TOOLS.withWrite, ...(featureFlags.CODE_EXECUTION_BRIDGE ? [mcpToolRef('code-execution', 'run_python_analysis')] : [])],
534535
model: 'sonnet',
535536
};

super-legal-mcp-refactored/src/utils/agentSdkToolAdapter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ export function buildAgentSdkTools(toolImplementations = {}) {
235235
content: [{
236236
type: 'text',
237237
text: JSON.stringify(result, null, 2)
238-
}]
238+
}],
239+
...(result.success === false ? { isError: true } : {})
239240
};
240241
} catch (err) {
241242
console.error('[AgentSDK] Python analysis error:', err.message);

0 commit comments

Comments
 (0)