Same MCP Server, Different AI Can Give Different Answer and Claude is Strong in Finance

Using the same MCP server I created across different AI platforms, such as a coding assistant and Claude Desktop, I’ve noticed that the responses vary significantly, particularly when it comes to “finding alpha signals using RBICS data.” Claude consistently provides stronger answers to finance-related questions.

The primary reason for this difference lies in the MCP server’s _instruction, which guides its usage and limits its ability to generalize to broader inquiries. In contrast, Claude Desktop does not adhere to these instructions, allowing for greater flexibility. Additionally, Claude’s model is specifically trained on larger, more comprehensive financial datasets, enhancing its performance in financial contexts.

With that in mind, I’ve also explored the skills available in Claude Desktop to further enhance its financial capabilities.

The MCP server provides the tools (data access). The skill provides the workflow knowledge (how to use those tools for sector rotation analysis). Every user connects to the same MCP server but installs the skill locally on their Claude Desktop.

The skill is also where you can put the richer financial analysis context — the sector rotation theory, EPS momentum frameworks, signal interpretation guidance — that was missing when I answered the same question in Cascade. Claude Desktop would read the skill instructions and offer that analytical depth automatically.

SurfaceHow
claude.ai webCustomize → Skills → upload ZIP
Claude DesktopSettings → upload ZIP (same mechanism)
Claude Code (CLI)Drop folder in ~/.claude/skills/

for example, here is a rbics-alpha-signals / SKILL.md, save it as rbics_alpha-signals.zip to upload to settings in claude desktop:


name: rbics-alpha-signals
description: >
Systematic alpha signal generation using FactSet RBICS classification, supply chain
relationships, and revenue segment data. Use this skill whenever the user asks about
alpha signals, factor research, RBICS-based screening, revenue mix shift, supply chain
contagion, sector reclassification, pure-play concentration, or wants to backtest a
fundamental signal derived from RBICS data. Triggers on phrases like “find alpha using
RBICS”, “revenue mix shift signal”, “supply chain ripple”, “which companies increased
exposure to [sector]”, “backtest RBICS signal”, or “pure-play factor”. Also trigger

when the user asks to screen or rank companies by their L6 revenue exposure over time.

RBICS alpha signal framework

Four signal families, all executable with the factset-index MCP toolset.


Signal 1 — Supply chain ripple beta

Idea: A bellwether company’s supply chain partners have revenue exposure to that company.
Revenue exposure % ≈ earnings-surprise beta proxy.

Workflow:

  1. Resolve seed entity ID via SYM_V1.SYM_ENTITY (search by name):
   SELECT FACTSET_ENTITY_ID, ENTITY_PROPER_NAME, ENTITY_TYPE
   FROM SYM_V1.SYM_ENTITY
   WHERE UPPER(ENTITY_PROPER_NAME) LIKE '%<NAME>%' AND ISO_COUNTRY = '<CC>'
  1. Call get_entity_supply_chain(entity_ids=[seed], as_of_date=date) — returns all
    CUSTOMER, SUPPLIER, COMPETITOR, PARTNER records.
  2. Filter for records where revenue_pct IS NOT NULL. These are the actionable signals.
  3. Rank by revenue_pct descending — high values = high earnings-beta exposure.

Key fields: rel_type (CUSTOMER/SUPPLIER), revenue_pct, direction
(Direct = seed→entity, Reverse = entity→seed).

Trading implication: Long high-exposure suppliers before bellwether earnings;
hedge or reduce on miss risk. Expected price move ≈ revenue_pct × bellwether EPS surprise.


Signal 2 — Pure-play concentration

Idea: Companies with ≥80% revenue from a single L6 code have amplified sector beta.
Rank by concentration for a long/short factor.

Workflow:

  1. validate_rbics_codes(search_terms=["<sector>"]) to find the right L6 codes.
  2. classify_by_rbics(l6_codes=[...], as_of_date=date, min_revenue_pct=0.10)
    returns per-segment revenue_pct breakdown.
  3. Aggregate in Python: for each entity, sum revenue_pct across segments mapping to
    the target L6(s), capped at 100%.
  4. Sort descending. Entities >80% = pure-plays (amplified beta). <30% = sector participants.

Signal 3 — Sector reclassification

Idea: Companies whose primary RBICS L6 focus code changes between two dates are
pivoting business models — often 6–12 months before market recognition.

Workflow:

WITH t1 AS (
SELECT FACTSET_ENTITY_ID, L6_ID AS l6_old
FROM RBICS_V1.RBICS_ENTITY_FOCUS
WHERE START_DATE <= '<t1>' AND (END_DATE > '<t1>' OR END_DATE IS NULL)
),
t2 AS (
SELECT FACTSET_ENTITY_ID, L6_ID AS l6_new
FROM RBICS_V1.RBICS_ENTITY_FOCUS
WHERE START_DATE <= '<t2>' AND (END_DATE > '<t2>' OR END_DATE IS NULL)
)
SELECT t1.FACTSET_ENTITY_ID, t1.l6_old, t2.l6_new
FROM t1 JOIN t2 USING (FACTSET_ENTITY_ID)
WHERE t1.l6_old <> t2.l6_new

Join with SYM_V1.SYM_ENTITY to resolve names. Filter for reclassifications INTO
high-momentum L6 codes (see known codes below).


Signal 4 — Revenue mix shift (primary demonstrated signal)

Idea: Companies whose L6 revenue share in a target sector increases between two fiscal
year snapshots are pivoting toward that sector before price momentum reflects it.

Step-by-step workflow

Step 1: Identify target L6 codes

validate_rbics_codes(search_terms=["<sector>"], as_of_date="<t2>")

For AI semiconductors the confirmed codes are:

  • 551020303010 — Other Processor Semiconductors (GPUs, AI chips, NPUs)
  • 551020302510 — Microprocessor (MPU) Semiconductors (CPUs with AI)
  • 551020351010 — Other Programmable Logic and ASIC Semiconductors (AI ASICs)
  • 551020351510 — Programmable Logic Device Semiconductors (FPGAs for AI)

Step 2: Two-snapshot classify

Run classify_by_rbics at both dates with min_revenue_pct=0.10.
If the L6 set is large (>10 codes) and times out, narrow with screen_by_country_rbics
first or pass an artifact_id from a prior get_base_universe call.

snap_t1 = classify_by_rbics(l6_codes=[...], as_of_date="<t1>", min_revenue_pct=0.10)
snap_t2 = classify_by_rbics(l6_codes=[...], as_of_date="<t2>", min_revenue_pct=0.10)

Each returns details list with factset_entity_id, l6_id, revenue_pct, bus_seg_name.

Step 3: Aggregate and compute Δ in Python

Use bash_tool to run this locally — do not try to do this in SQL or mentally.

def aggregate(details):
agg = {}
for row in details:
eid = row["factset_entity_id"]
pct = row["revenue_pct"] or 0
if pct > 0:
agg[eid] = agg.get(eid, 0) + pct
return {k: min(v, 100.0) for k, v in agg.items()}
exp_t1 = aggregate(snap_t1["details"])
exp_t2 = aggregate(snap_t2["details"])
all_ids = set(exp_t1) | set(exp_t2)
results = [
{"entity_id": eid,
"exp_t1": round(exp_t1.get(eid, 0), 1),
"exp_t2": round(exp_t2.get(eid, 0), 1),
"delta": round(exp_t2.get(eid, 0) - exp_t1.get(eid, 0), 1)}
for eid in all_ids
]
results.sort(key=lambda x: x["delta"], reverse=True)
  • High-Δ (top quartile, especially [NEW] entrants): long signal
  • Low-Δ / declining (entities that dropped out): short / avoid signal

Step 4: Resolve entity names

SELECT FACTSET_ENTITY_ID, ENTITY_PROPER_NAME, ISO_COUNTRY, ENTITY_TYPE
FROM SYM_V1.SYM_ENTITY
WHERE FACTSET_ENTITY_ID IN ('<id1>', '<id2>', ...)

Filter to ENTITY_TYPE = 'PUB' for publicly traded names only.

Step 5: Backtest

high_delta_ids = [top N public entity IDs]
n = len(high_delta_ids)
weight = round(1.0 / n, 4)
constituents = [
{"date": "<t2_plus_1day>", "entity_id": eid, "weight": weight}
for eid in high_delta_ids
]
result = run_backtest(
constituents=constituents,
start_date="<forward_start>",
end_date="<forward_end>",
currency="USD"
)

Run the same for the low-Δ group and compute spread.
For official deliverables use run_pa_backtest (PA Engine) — may timeout; retry
with pa_prepare_holdings → pa_submit_backtest → pa_get_backtest_result async flow.

Step 6: Visualize

Build an interactive Chart.js widget showing:

  • KPI row: cumulative return (high-Δ), cumulative return (low-Δ), spread, max drawdown
  • Cumulative indexed line chart (both portfolios)
  • Monthly returns grouped bar chart
  • Entity table with country flags, Δ value, and sector badges

Key caveats and refinements

Liquidity filter: Apply screen_liquidity on the universe before the backtest
to remove micro-caps with thin trading volumes. Without this, returns are inflated
by small/mid-cap EM names that aren’t investable at scale.

Data staleness: revenue_pct in RBICS lags fiscal year reporting by 1–4 months.
Use period_end_date to confirm the observation date per entity — some may have
stale data (2-3 year old fiscal years) that weakens the signal.

Reclassification artifact: Entities appearing in low-Δ because their
revenue_pct dropped may actually be RBICS-reclassified into a new L6 (Signal 3
in parallel). Cross-check with RBICS_ENTITY_FOCUS before interpreting as fundamental
deterioration.

Entity type PVT: Private entities appear in the RBICS universe but can’t be
included in run_backtest. Always filter to ENTITY_TYPE = 'PUB'.

Cap at 100%: Multiple segments may map to the same L6 — always cap aggregated
revenue_pct at 100 to avoid double-counting.


Defaults to confirm with user

Before running, confirm:

  • Target sector / L6 codes (or ask to search by keyword)
  • Snapshot dates t1 and t2 (suggest: 2 fiscal years apart)
  • Forward backtest window (suggest: 12 months after t2)
  • Country filter if desired (use screen_by_country_rbics for scoped runs)
  • Min revenue threshold (default 10%; lower = more entities, noisier signal)
  • Number of top/bottom entities for backtest (default: top 15 / bottom 10)

Leave a Reply