See your agent call HubSpotwithout touching anything real.

Point your agent at a faithful mock of any MCP server — or bring your own. Watch every call, inject any failure, never touch real data.

observing

Every tool call your agent makes, captured the moment it happens.

agent → mockd → mock

Drops into any MCP client you already run

ClaudeOpenAICursorVS CodeLangChainVercel AI SDKn8nZedReplitClaudeOpenAICursorVS CodeLangChainVercel AI SDKn8nZedReplitClaudeOpenAICursorVS CodeLangChainVercel AI SDKn8nZedReplitClaudeOpenAICursorVS CodeLangChainVercel AI SDKn8nZedReplit
Inside a trace

Read the exact request, response and latency of every call

Every MCP tool call your agent makes — the precise request, response, status and timing — including the failures you inject and the retries that recover.

Open the full trace viewer
sessionses_h7Qf…3a1
  • crm.contacts.searchquery: "acme"200142ms
    crm.contacts.search
    ok · 200
    started 12:04:01.221 · took 142 ms · 3 fields

    Full-text search across CRM contacts; returns matching records.

    From the tool catalog — what the agent read.

    Agent inputrequired 1/1 · 0 unknown
    I'm calling crm.contacts.search with 3 values:
    querystringrequired

    Search term — e.g. a company name or email address.

    "acme"
    propertiesarrayoptional

    Which contact fields to include in the result.

    emailcompanylifecyclestage
    limitnumberoptional

    Maximum number of matches.

    10
    Exactly the data the agent sent (JSON)
    {
      "query": "acme",
      "properties": ["email", "company", "lifecyclestage"],
      "limit": 10
    }
    Server response
    what the tool returned
    200 ok
    {
      "total": 2,
      "results": [
        { "id": "5512", "company": "Acme Inc.", "email": "ops@acme.io" },
        { "id": "5513", "company": "Acme Labs", "email": "hi@acmelabs.dev" }
      ]
    }
  • crm.contacts.getid: 551220088ms
    crm.contacts.get
    ok · 200
    started 12:04:01.402 · took 88 ms · 2 fields

    Loads a single contact by its unique ID.

    From the tool catalog — what the agent read.

    Agent inputrequired 1/1 · 0 unknown
    I'm calling crm.contacts.get with 2 values:
    contactIdstringrequired

    Unique ID of the contact.

    "5512"
    propertiesarrayoptional

    Which fields to load.

    emailcompanyownerlifecyclestage
    Exactly the data the agent sent (JSON)
    {
      "contactId": "5512",
      "properties": ["email", "company", "owner", "lifecyclestage"]
    }
    Server response
    what the tool returned
    200 ok
    {
      "id": "5512",
      "company": "Acme Inc.",
      "email": "ops@acme.io",
      "owner": "rep_42",
      "lifecyclestage": "opportunity"
    }
  • crm.deals.createname: "Acme – Q3 Expansion"201196ms
    crm.deals.create
    ok · 201
    started 12:04:01.690 · took 196 ms · 4 fields

    Creates a new deal in the CRM pipeline.

    From the tool catalog — what the agent read.

    Agent inputrequired 1/1 · 0 unknown
    I'm calling crm.deals.create with 4 values:
    dealnamestringrequired

    Display name of the deal.

    "Acme – Q3 Expansion"
    amountnumberoptional

    Deal value in the account's currency.

    48000
    pipelinestringoptional

    ID of the pipeline the deal sits in.

    "default"
    dealstagestringoptional

    Current stage in the sales process.

    "qualifiedtobuy"
    Exactly the data the agent sent (JSON)
    {
      "dealname": "Acme – Q3 Expansion",
      "amount": 48000,
      "pipeline": "default",
      "dealstage": "qualifiedtobuy"
    }
    Server response
    what the tool returned
    201 ok
    {
      "id": "deal_9981",
      "dealname": "Acme – Q3 Expansion",
      "amount": 48000,
      "createdAt": "2026-06-19T12:04:01Z"
    }
  • crm.associations.createdeal_9981 ↔ 551220074ms
    crm.associations.create
    ok · 200
    started 12:04:01.871 · took 74 ms · 3 fields

    Links two CRM records together, e.g. a deal with a contact.

    From the tool catalog — what the agent read.

    Agent inputrequired 3/3 · 0 unknown
    I'm calling crm.associations.create with 3 values:
    fromobjectrequired

    Record the association starts from — type (e.g. "deal") and id.

    {object with 2 fieldstype:"deal"id:"deal_9981"}
    toobjectrequired

    Record being linked to — also type and id.

    {object with 2 fieldstype:"contact"id:"5512"}
    associationTypestringrequired

    Type of link (e.g. deal_to_contact).

    "deal_to_contact"
    Exactly the data the agent sent (JSON)
    {
      "from": { "type": "deal", "id": "deal_9981" },
      "to": { "type": "contact", "id": "5512" },
      "associationType": "deal_to_contact"
    }
    Server response
    what the tool returned
    200 ok
    { "status": "ASSOCIATED", "from": "deal_9981", "to": "5512" }
  • crm.notes.createinjectedrate limit — injected42931ms
    crm.notes.create
    429 errorinjected: 429 burst
    started 12:04:02.005 · took 31 ms · 2 fields

    Creates a note and attaches it to one or more CRM records.

    From the tool catalog — what the agent read.

    Agent inputrequired 1/1 · 0 unknown
    I'm calling crm.notes.create with 2 values:
    bodystringrequired

    Text content of the note.

    "Logged Q3 expansion deal from agent."
    associationsarrayoptional

    IDs of the records the note is attached to.

    deal_9981
    Exactly the data the agent sent (JSON)
    {
      "body": "Logged Q3 expansion deal from agent.",
      "associations": ["deal_9981"]
    }
    Server response
    what the tool returned
    429 error

    Rate limited — injected on purpose by the Mockd scenario 429 burst". Your agent waited 250 ms and the retry came back with 201 — from the mock, not the real tool.

    {
      "error": "RATE_LIMITED",
      "message": "Too many requests. Injected by Mockd scenario 'burst'.",
      "retryAfterMs": 250
    }
  • crm.notes.create↻ retry okretried after 250ms backoff201110ms
    crm.notes.create
    ok · 201
    started 12:04:02.379 · took 110 ms · 2 fields

    Creates a note and attaches it to one or more CRM records.

    From the tool catalog — what the agent read.

    Agent inputrequired 1/1 · 0 unknown
    I'm calling crm.notes.create with 2 values:
    bodystringrequired

    Text content of the note.

    "Logged Q3 expansion deal from agent."
    associationsarrayoptional

    IDs of the records the note is attached to.

    deal_9981
    Exactly the data the agent sent (JSON)
    {
      "body": "Logged Q3 expansion deal from agent.",
      "associations": ["deal_9981"]
    }
    Server response
    what the tool returned
    201 ok
    { "id": "note_4471", "createdAt": "2026-06-19T12:04:02Z" }
6 calls1 injectedtotal 641ms
One hub, every tool

Point your agent at Mockd, reach the whole ecosystem

Your agent connects to a single Mockd endpoint. Behind it sits a faithful mock of whatever you want to test — popular SaaS servers from the catalog, or your own custom MCP. No accounts, no keys, no data at risk.

  • One mock endpoint stands in for every tool your agent calls
  • Swap providers without touching a single line of agent code
  • From HubSpot to your own internal server — same trace, same loop
Browse the catalog
Faithful mocks
Bring your own MCP

Paste a config. Watch the first trace.

Grab a ready-made config, drop it into your agent, and see the trace roll in — no signups for services you're only evaluating.

Start testing — free
claude_desktop_config.jsonjson
{
  "mcpServers": {
    "hubspot": {
      "url": "mock.mock-d.com/hubspot/mcp",
      "headers": { "x-mockd-key": "mk_test_8f2c…" }
    }
  }
}
FAQ

Questions, answered

Still curious? The docs go deeper on transports, scenarios and the trace API.

What exactly is a 'mock MCP server'?

It's a server that speaks the Model Context Protocol and exposes the same tools — same names, same descriptions, same schemas — as the real thing, but answers with synthetic, deterministic data instead of calling a live API. Your agent can't tell the difference; you get total control and full visibility.

Do I have to change my agent's code?

No. You paste a connection config into whatever MCP client you already use — Claude, Cursor, the OpenAI Agents SDK, LangGraph, and so on. Mockd looks like any other MCP server to your agent.

How is this different from just logging tool calls myself?

Two things: the mock (you don't need the real service or its data) and the viewer (a structured, shareable trace of every call). Together they turn 'integrate, then hope' into 'test, observe, then integrate'.

Can I test my own internal MCP server?

Yes — that's Bring Your Own MCP. Point Mockd at your server's URL and it renders the tool descriptions, proxies the calls, and records a full trace so you can tune prompts against your own tools.

Is the mocked data realistic?

Yes. Mocks return shaped, plausible records (real field names, IDs, relationships) so the model behaves the way it would in production — without any production data being involved.

Which servers can I mock today?

HubSpot is live now. Salesforce, Slack, Stripe, Notion, GitHub and more are next. Bring Your Own MCP works with anything that speaks the protocol.