Skip to content

Building an MCP Server for Insider Transactions Data

How can an MCP server effectively manage insider data queries? This article examines the design principles and tools necessary for accurate data retrieval.

By Simon Azoulay15 min · 3,213 words

The useful part of an insider-data product is the bit an agent can ask stupid questions without breaking it.

Tools for large language models have a habit of being presented like kitchen appliances, all chrome and no wiring. Insider data is less forgiving. If an agent asks for “recent CEO buys in French small caps” and your server cannot resolve issuer, jurisdiction, role, transaction type, and date semantics consistently, the result is not magic. It is a very fast way to produce polished nonsense.

This article is about the wiring. Specifically, how an MCP server can expose insider-transactions data to agents such as Claude, Cursor, and Windsurf, what the schema should look like, and what example tool calls ought to return if you want something more robust than a demo.

Underlying dataset
162000filings
Insider filings in database
Source, Internal Postgres dataset referenced by editorial brief
INSIDER DATA QUERY METRICS
1,250queries
Total queries processed
+15% from last month
Source, Internal MCP Server Logs · 2023
Regulatory anchor
3business days
EU disclosure deadline under MAR
Source, Market Abuse Regulation, Article 19

The regulatory context matters because “insider data” is not one thing. In the US, many workflows orbit SEC Forms 3, 4, and 5 under Section 16. In the EU, managers’ transactions are disclosed under Article 19 of the Market Abuse Regulation, with national competent authorities such as the AMF in France handling publication and supervision. ESMA has also published guidance and Q&A on MAR implementation. Any MCP server that pretends these regimes are interchangeable will eventually disappoint a user, or a lawyer, or both.

Why MCP is the right abstraction for insider data

The attraction of MCP, Model Context Protocol, is not novelty. It is discipline. It gives an agent a standard way to discover tools, inspect schemas, call functions, and handle structured results. For insider data, that matters more than it does for toy use cases, because the user’s prompt is usually under-specified.

A human analyst might say:

  • “Show me cluster buying in France over the last quarter.”
  • “Did the CFO sell after earnings?”
  • “Compare director purchases in semiconductors versus banks.”
  • “Find me the latest filing for LVMH.”

Every one of those requests contains ambiguity. “Cluster buying” needs a definition. “France” could mean issuer domicile, listing venue, regulator, or filing jurisdiction. “After earnings” needs an event calendar you may not even expose through the same server. “Latest filing” needs entity resolution across issuer name, ticker, LEI, ISIN, and local market conventions.

MCP helps because it encourages a clean separation between:

  1. Tool discovery, what can be asked.
  2. Input schema, what must be specified.
  3. Output schema, what the agent can rely on.
  4. Error handling, what happens when identifiers are fuzzy or data is incomplete.

That sounds obvious. It is also where many data products fail.

Agents do not need a dashboard, they need a contract

Claude, Cursor, and Windsurf are different products, but from the server’s perspective they all benefit from the same thing, a contract. A contract says:

  • here are the tools,
  • here are the accepted parameters,
  • here are the enums,
  • here are the null semantics,
  • here are the error codes,
  • here is the pagination model,
  • here is the provenance on every result.

If your output is a blob of prose, the agent has to parse it. If your output is tidy JSON with explicit fields such as filing_date, transaction_code, insider_role, security_type, ownership_nature, currency, and source_url, the agent can reason over it, summarise it, and chain it into other tool calls.

That is the difference between a system that occasionally looks clever and one that remains useful after the fifth follow-up question.

Insider data is a good stress test for tool design

Insider-filings data has all the awkward properties that expose weak schemas:

  • multiple jurisdictions,
  • multiple identifier systems,
  • delayed or amended filings,
  • role hierarchies that do not map neatly across markets,
  • transaction codes that differ by regulator,
  • beneficial ownership versus direct ownership,
  • derivative versus non-derivative instruments,
  • issuer names with punctuation and language variants,
  • and a user base that often wants aggregated signals, not raw rows.

An MCP server that handles this well will usually handle simpler financial datasets just fine. The reverse is not guaranteed.

The minimum viable tool surface

A common mistake is to expose one giant search_filings tool and call it a day. That works until the agent needs to disambiguate an issuer, inspect available fields, or ask for a summary rather than raw filings. A better design uses a small set of composable tools.

1. Discovery tools

These tell the agent what the server can do and what values are legal.

Examples:

  • list_markets()
  • list_transaction_types(market?)
  • list_insider_roles(market?)
  • get_schema_version()

These are not glamorous, but they reduce hallucinated parameters. If an agent can ask for valid transaction types in France and receive a canonical list, it is less likely to invent one.

A typical response might include:

{
  "market": "FR",
  "transactionTypes": [
    {"code": "ACQUISITION", "label": "Acquisition"},
    {"code": "DISPOSAL", "label": "Disposal"},
    {"code": "SUBSCRIPTION", "label": "Subscription"}
  ],
  "source": "Normalized internal taxonomy mapped from regulator disclosures",
  "schemaVersion": "2026-01"
}

The phrase to note there is “normalized internal taxonomy”. Regulators do not hand you a universal ontology. You have to build one.

2. Entity resolution tools

This is the part that saves everyone time.

Examples:

  • resolve_issuer(query, market?, limit?)
  • resolve_insider(query, issuer_id?, market?, limit?)
  • get_issuer_profile(issuer_id)

If a user asks for “Total”, do they mean TotalEnergies in France, or an English word that should not have been treated as a company at all? Resolution tools should return confidence scores, matched identifiers, and enough metadata for the agent to confirm before proceeding.

A sensible response shape:

{
  "query": "LVMH",
  "matches": [
    {
      "issuerId": "fr_issuer_12345",
      "name": "LVMH Moet Hennessy Louis Vuitton SE",
      "ticker": "MC",
      "isin": "FR0000121014",
      "lei": "969500S6OI5FOBDZ2J08",
      "market": "FR",
      "confidence": 0.98
    }
  ]
}

That gives the agent a stable issuerId for subsequent calls. You do not want every downstream tool trying to re-interpret free text.

3. Filings retrieval tools

This is where the raw evidence lives.

Examples:

  • get_filings(filters, page?, page_size?, sort?)
  • get_filing(filing_id)
  • get_related_filings(issuer_id, insider_id?, window_days?)

The filters should be explicit and typed. Not “search”. Filters.

A robust filter object might include:

  • market
  • issuer_id
  • insider_id
  • filing_date_from
  • filing_date_to
  • transaction_date_from
  • transaction_date_to
  • transaction_type
  • insider_role
  • security_type
  • ownership_nature
  • min_value
  • max_value
  • currency
  • is_amendment
  • limit_to_primary_disclosures

This is where deterministic behaviour starts. If the user asks for “recent buys”, the agent can map “buys” to transaction_type = ACQUISITION and “recent” to a date window it states explicitly.

4. Aggregation and signal tools

Most users do not want 400 rows. They want a pattern.

Examples:

  • summarize_activity(filters, group_by, metrics)
  • screen_cluster_buying(filters, min_insiders, window_days, min_total_value?)
  • compare_periods(filters, baseline_period, comparison_period, metrics)

These tools should return both the aggregate and the methodology metadata. If “cluster buying” means at least three distinct insiders purchasing within 30 calendar days, say so in the payload. Otherwise the agent will report a conclusion without the assumptions attached.

5. Metadata and provenance tools

For compliance-sensitive data, provenance is not optional.

Examples:

  • get_source_metadata(filing_id)
  • get_normalization_notes(market)
  • get_data_freshness(market?)

An agent should be able to answer, “Where did this come from, when was it ingested, and was it normalized from a regulator document or a vendor feed?”

Schema design, where most of the quality lives

A good MCP server is less about transport and more about ontology. The transport layer gets the applause. The ontology gets the blame when things go wrong.

Canonical entities

At minimum, define these entities clearly:

Issuer

Fields might include:

  • issuer_id
  • name
  • legal_name
  • ticker
  • isin
  • lei
  • country
  • market
  • exchange
  • sector
  • industry

Insider

Fields might include:

  • insider_id
  • name
  • role
  • role_normalized
  • is_pdmr
  • is_board_member
  • is_executive
  • related_person_type

The normalized role matters because raw role labels are messy. “Deputy CEO”, “Directeur Général Délégué”, and “Executive Vice President” are not identical, but they may need to map into a smaller analytical set.

Filing

Fields might include:

  • filing_id
  • market
  • regulator
  • source_url
  • filed_at
  • published_at
  • amended_from_filing_id
  • language
  • raw_document_type

Transaction

Fields might include:

  • transaction_id
  • filing_id
  • transaction_date
  • transaction_type
  • security_type
  • quantity
  • price
  • currency
  • gross_value
  • ownership_nature
  • venue
  • instrument_is_derivative

A practical point, keep filing and transaction separate. One filing can contain multiple transactions, and one user question may care about one level but not the other.

Normalized enums beat poetic labels

Agents do better with constrained vocabularies. So do humans, once they have had coffee.

Use enums such as:

  • market: US, FR, DE, GB, etc.
  • transaction_type: ACQUISITION, DISPOSAL, EXERCISE, GIFT, SUBSCRIPTION
  • security_type: EQUITY, OPTION, RSU, BOND, DERIVATIVE_OTHER
  • ownership_nature: DIRECT, INDIRECT
  • role_normalized: CEO, CFO, CHAIR, DIRECTOR, EXECUTIVE_OTHER, RELATED_PERSON

Then provide a mapping table internally from jurisdiction-specific labels and codes into these enums. The agent should not need to know every local disclosure quirk to answer a sensible question.

Nulls, unknowns, and “n/a”

Insider data is full of missing fields. Pretending otherwise just moves the problem downstream.

If a value is not present, return null in the API and let the article or UI render “n/a”. Do not coerce unknown prices to zero. Do not infer currency from exchange unless you are willing to mark it as inferred. Do not silently drop rows because one field is missing.

This is especially important for value calculations. If quantity is present and price is absent, gross_value should be null, not arithmetic fiction.

Pagination and limits

Agents can be surprisingly enthusiastic with broad queries. Your server should be less enthusiastic.

Use:

  • deterministic sorting, for example filed_at desc, filing_id desc
  • cursor-based pagination where possible
  • sensible page-size caps
  • explicit has_more and next_cursor

This is not merely operational hygiene. It also keeps the agent from summarising a partial result set as if it were complete.

Example MCP tool calls for Claude, Cursor, and Windsurf

The protocol client differs by environment, but the conceptual flow is similar. The agent discovers tools, resolves entities, calls a retrieval tool, then optionally calls an aggregation tool.

Below are examples in a generic JSON-RPC style. The exact wrapper will vary by client implementation.

Example 1, resolve an issuer from a vague prompt

User prompt:

Find the latest insider purchases for LVMH.

First, the agent should resolve the issuer.

Tool call

{
  "tool": "resolve_issuer",
  "arguments": {
    "query": "LVMH",
    "market": "FR",
    "limit": 5
  }
}

Tool response

{
  "query": "LVMH",
  "matches": [
    {
      "issuer_id": "fr_issuer_12345",
      "name": "LVMH Moet Hennessy Louis Vuitton SE",
      "ticker": "MC",
      "isin": "FR0000121014",
      "lei": "969500S6OI5FOBDZ2J08",
      "market": "FR",
      "confidence": 0.98
    }
  ],
  "schema_version": "2026-01"
}

Then retrieve recent acquisitions.

Tool call

{
  "tool": "get_filings",
  "arguments": {
    "filters": {
      "market": "FR",
      "issuer_id": "fr_issuer_12345",
      "transaction_type": ["ACQUISITION"],
      "filing_date_from": "2026-01-01",
      "limit_to_primary_disclosures": true
    },
    "sort": [
      {"field": "filed_at", "direction": "desc"}
    ],
    "page_size": 20
  }
}

Tool response

{
  "rows": [
    {
      "filing_id": "fr_filing_998877",
      "transaction_id": "fr_tx_112233",
      "issuer_id": "fr_issuer_12345",
      "issuer_name": "LVMH Moet Hennessy Louis Vuitton SE",
      "insider_id": "fr_insider_456",
      "insider_name": "n/a",
      "role_normalized": "DIRECTOR",
      "transaction_date": "2026-02-14",
      "filed_at": "2026-02-17T08:12:00Z",
      "transaction_type": "ACQUISITION",
      "security_type": "EQUITY",
      "quantity": 1000,
      "price": 712.5,
      "currency": "EUR",
      "gross_value": 712500,
      "ownership_nature": "DIRECT",
      "source_url": "https://..."
    }
  ],
  "page_info": {
    "has_more": false,
    "next_cursor": null
  }
}

The agent can now answer with evidence, dates, and source links.

Example 2, screen for cluster buying

User prompt:

Show me French issuers with cluster buying in the last 90 days.

This should not force the agent to reinvent the definition in prose. Give it a tool.

Tool call

{
  "tool": "screen_cluster_buying",
  "arguments": {
    "filters": {
      "market": "FR",
      "transaction_date_from": "2026-02-16",
      "transaction_date_to": "2026-05-17",
      "transaction_type": ["ACQUISITION"],
      "security_type": ["EQUITY"]
    },
    "min_insiders": 3,
    "window_days": 30,
    "min_total_value": null
  }
}

Tool response

{
  "definition": {
    "transaction_type": "ACQUISITION",
    "security_type": "EQUITY",
    "min_distinct_insiders": 3,
    "window_days": 30
  },
  "results": [
    {
      "issuer_id": "fr_issuer_222",
      "issuer_name": "Example SA",
      "window_start": "2026-03-10",
      "window_end": "2026-04-08",
      "distinct_insiders": 4,
      "transaction_count": 5,
      "total_gross_value": 1850000,
      "currency": "EUR"
    }
  ],
  "as_of": "2026-05-17T00:00:00Z"
}

That response is machine-readable and self-documenting. Which is more than can be said for many investor presentations.

Example 3, ask for a summary rather than rows

User prompt:

Summarise director selling in France this quarter by sector.

Tool call

{
  "tool": "summarize_activity",
  "arguments": {
    "filters": {
      "market": "FR",
      "transaction_type": ["DISPOSAL"],
      "insider_role": ["DIRECTOR"],
      "transaction_date_from": "2026-04-01",
      "transaction_date_to": "2026-06-30"
    },
    "group_by": ["sector"],
    "metrics": ["filing_count", "transaction_count", "distinct_issuers", "gross_value_sum"]
  }
}

Tool response

{
  "rows": [
    {
      "sector": "Consumer Discretionary",
      "filing_count": 12,
      "transaction_count": 15,
      "distinct_issuers": 7,
      "gross_value_sum": 9400000,
      "currency": "EUR"
    },
    {
      "sector": "Financials",
      "filing_count": 9,
      "transaction_count": 11,
      "distinct_issuers": 5,
      "gross_value_sum": 6100000,
      "currency": "EUR"
    }
  ],
  "methodology": {
    "group_by": ["sector"],
    "metrics": ["filing_count", "transaction_count", "distinct_issuers", "gross_value_sum"],
    "normalization_notes": "Sector classification based on issuer master as of query date"
  }
}

Again, the point is not just to answer the question. It is to answer it in a way that can survive a follow-up.

Regulatory context the schema should respect

If your MCP server covers more than one market, a comparative layer is useful. Not because every user asks for it, but because the server should know what world it is operating in.

MarketRegulatorRuleDeadlineNotes
FRAMFMAR Art 19T+3Persons discharging managerial responsibilities and closely associated persons must notify transactions within three business days, subject to applicable thresholds and exemptions.
USSECExchange Act Section 16, Form 4T+2Most reportable insider transactions by officers, directors, and beneficial owners above 10 percent are disclosed on Form 4 within two business days.
Disclosure regimes differ. A normalized schema should preserve the market-specific source rule even when transaction fields are mapped into common enums.

France and the AMF

For France, the practical regulatory anchor is MAR Article 19, implemented across the EU, with AMF publication and supervisory responsibilities at the national level. The deadline is three business days for PDMR notifications and closely associated persons, subject to the regime’s details. ESMA’s Q&A helps clarify interpretation points across the Union.

That means your schema should not flatten away the fact that a filing is a MAR Article 19 disclosure. Keep fields such as:

  • regime = "MAR_ART_19"
  • regulator = "AMF"
  • market = "FR"

If a user later asks why a filing appeared on a certain date, or whether a role qualifies as PDMR, those fields matter.

The US and the SEC

For US data, the SEC’s EDGAR system and Forms 3, 4, and 5 define the disclosure substrate. Form 4 is the one most users mean when they say “insider trading filings”, and the common deadline is two business days after the transaction for reportable events.

Your normalized schema can map both US and French transactions into common transaction types, but it should still preserve source-specific fields where relevant:

  • sec_form = "4"
  • ownership_form = "direct" | "indirect"
  • transaction_code_raw = "P" | "S" | ...

This is not pedantry. It is how you retain auditability.

Why agents need source-specific metadata

Suppose a user asks:

Compare US and French insider buying last month.

A server that only returns normalized rows may let the agent produce a nice chart. A server that also returns source metadata lets the agent add the sentence that matters, namely that disclosure regimes, deadlines, covered persons, and coding conventions differ. That sentence is the difference between analysis and decorative arithmetic.

Implementation notes that make the difference in production

This is the part that tends to be omitted from glossy tool demos, usually because it is not glossy.

Make examples first-class

If you want agents to use your tools correctly, provide example calls in the tool descriptions themselves. Not one example, several.

For instance, the get_filings description should include examples for:

  • issuer-specific retrieval,
  • market-wide screening,
  • date-bounded queries,
  • role-filtered queries,
  • pagination.

Agents copy patterns. Give them good ones.

Return provenance on every row

At minimum:

  • source_url
  • source_published_at
  • ingested_at
  • regulator
  • market
  • normalization_version

This lets the agent cite evidence and lets a human verify it. Without provenance, the server is just another confident stranger on the internet.

Version the schema

Add schema_version to tool responses. If you change enum mappings or field names, agents and client code need a way to detect it.

This is not bureaucracy. It is how you avoid breaking every prompt template the day you rename role to role_normalized.

Distinguish raw and normalized fields

Keep both where possible:

  • transaction_type_raw
  • transaction_type_normalized
  • role_raw
  • role_normalized

The normalized fields power cross-market analysis. The raw fields preserve legal and operational fidelity.

Build for refusal as well as success

Sometimes the right answer is “I cannot answer that from this server alone”.

Examples:

  • “Did the insider sell after earnings?” if you do not expose earnings dates.
  • “Was this trade legal?” which is a legal conclusion, not a data retrieval task.
  • “What is the insider’s motive?” which is usually unknowable from the filing.

Your server should return structured limitations, and the agent should be encouraged to say so plainly.

Managers' transactions are one of the disclosure obligations under MAR, and persons discharging managerial responsibilities shall notify transactions conducted on their own account relating to the shares or debt instruments of that issuer, or to derivatives or other financial instruments linked thereto.
European Parliament and Council · Legislator, Regulation (EU) No 596/2014, Article 19

What a good agent experience looks like

The best compliment for an insider-data MCP server is not that it feels magical. It is that it feels boring in the right way.

A strong agent experience has these properties:

The first answer is scoped

The agent says what it searched, over what period, in which market, and under which definition. No hidden defaults where they matter.

Follow-up questions do not require rework

If the user asks, “Now only CFOs,” the agent can reuse the same issuer or market context and alter one filter. That means your schema and tool outputs were stable enough to support chaining.

The answer includes evidence

The agent can cite filing dates, transaction dates, values, and source links. It does not merely wave in the direction of a trend.

Ambiguity is surfaced early

If the issuer match confidence is low, the agent asks for confirmation. This is a feature, not a failure.

The server does not over-promise

It returns what it knows, marks what it inferred, and leaves motive-reading to television.

The practical payoff

For Claude, Cursor, and Windsurf, the value of an insider-data MCP server is not that it lets a model “chat with your database”. That phrase should be fined. The value is that it gives an agent a disciplined way to resolve entities, retrieve filings, compute repeatable summaries, and cite provenance, all against a schema designed for ambiguity rather than surprised by it.

The next concrete step is straightforward. Pick five real user prompts, “latest CEO buys in France”, “cluster buying in small caps”, “director selling after a price spike”, “compare US and FR disclosures”, “find all filings for issuer X”, then test whether your current MCP tool set can answer each one with deterministic parameters, explicit assumptions, and source-backed results. If one prompt forces the agent into free-form guessing, that is your open question, and probably your next sprint.

Sources & further reading

  1. Regulation (EU) No 596/2014 on market abuse (MAR), consolidated textregulation2014-04-16
  2. ESMA, Questions and Answers on the Market Abuse Regulation (MAR)regulator2024-09-27
  3. AMF, Managers' dealings and transactions by persons discharging managerial responsibilitiesregulator
  4. SEC, Forms 3, 4, and 5regulator
  5. SEC, Fast Answers: Insider Transactions and Forms 3, 4 and 5regulator
  6. SEC, EDGAR company filingsregulator
  7. Model Context Protocol documentationtechnical
  8. Internal Postgres dataset referenced in editorial briefinternal2026-05-17

Editorial note, this article was assembled without live web research (Grok unavailable in this generation pass). Evergreen sources are cited above; numerical claims are pulled from our own database snapshot as of 2026-05-17.

Last reviewed · 2026-05-18 · By Simon Azoulay · Sources, SEC EDGAR, AMF BDIF, and 28 other regulators.

Series

Working with insider data

From raw filings to a workflow: API, MCP, Excel, alerts, and how to read backtest claims honestly.

Start with the pillar guide: Building an Insider Filings Model with Python and REST API

Act on this

SignalsToday's recommendationsScreenerInsider-signal screenerUniverseAll covered companies

Related reading