Skip to main content
Glama
marketsharp-mcp

marketsharp-mcp

by WYRE-AI

marketsharp-mcp

MCP server for the MarketSharp CRM REST API (https://restapi.marketsharpm.com, API version 2.0). It gives an AI assistant read access to a home-improvement contractor's contacts, inquiries (leads), appointments, jobs, notes and reference lists. Every tool is read-only.

Maintained by WYRE AI. It runs as a stdio server locally or as a stateless Streamable HTTP server behind a gateway.

Credentials

MarketSharp uses application authentication: the server exchanges your Company ID, API key and API secret at POST /token for a short-lived bearer token, caches it per credential set, and refreshes it on expiry or a 401.

Variable

Gateway header

Required

Where to find it

MARKETSHARP_COMPANY_ID

x-marketsharp-company-id

yes

Shown under your username, top right in MarketSharp

MARKETSHARP_API_KEY

x-marketsharp-api-key

yes

Admin → Apps & Add-ons Setup → API maintenance → Create New API Key

MARKETSHARP_API_SECRET

x-marketsharp-api-secret

yes

Issued with the API key. If there's no secret, MarketSharp support has to turn on API access

MARKETSHARP_EMPLOYEE_ID

x-marketsharp-employee-id

no

MarketSharp employee ID. The contact, inquiry and job search tools, and marketsharp_notes_list need it, and act with that employee's permissions

Use a dedicated MarketSharp employee set up for API access rather than a person's login. The server never uses MarketSharp usernames or passwords.

Related MCP server: EODHD MCP Server

Tools (25, all read-only)

Tool

MarketSharp endpoint

marketsharp_companies_list

GET /companies

marketsharp_companies_get

GET /companies/{companyId}

marketsharp_search_global

POST …/search/global (contacts, inquiries, jobs)

marketsharp_contacts_get

GET …/contacts/{contactId}

marketsharp_contacts_search

POST …/contacts/search (QuickFind, needs employee)

marketsharp_contacts_list

POST …/contacts/filter (paged, modified-since / appointment window)

marketsharp_inquiries_get

GET …/inquiries/{inquiryId} (optional isAppointmentRequired; MarketSharp defaults it to true)

marketsharp_inquiries_list

POST …/inquiries/filter (paged; optional isAppointmentRequired, which MarketSharp defaults to true)

marketsharp_inquiries_search

POST …/inquiries/search (QuickFind, needs employee)

marketsharp_inquiries_list_by_contact

GET …/contacts/{contactId}/inquiries (optional isAppointmentRequired)

marketsharp_inquiry_statuses_list

GET …/inquiries/statuses

marketsharp_inquiry_notes_list

GET …/opportunities/{inquiryId}/notes/{Inquiry|Job} (needs View Opportunity Note)

marketsharp_appointments_get

GET …/appointments/{appointmentId}

marketsharp_appointments_list

POST …/appointments/filter (paged)

marketsharp_appointments_list_by_contact

GET …/appointments/contacts/{contactId}

marketsharp_appointment_results_list

GET …/appointments/results/active or resultreasons/active

marketsharp_jobs_get

GET …/jobs/{jobId}

marketsharp_jobs_search

POST …/jobs/search (QuickFind, needs employee)

marketsharp_opportunities_list

POST …/jobs/opportunities (jobs + leads since a date, contract totals; needs View Opportunity)

marketsharp_notes_list

GET …/notes/contact/{contactId} or by note type (needs employee)

marketsharp_activity_types_list

GET …/activities/types

marketsharp_employees_list

GET …/employees (paged)

marketsharp_lead_sources_list

GET …/primaryleadsources[/…/secondaryleadsources]

marketsharp_product_interests_list

GET …/products/interests/{activeOnly} or per inquiry (needs View Product Interest)

marketsharp_divisions_list

GET …/divisions

There are no create, update or delete tools. Searches and filtered lists use POST because that is how MarketSharp exposes them; they don't change data. The client only sends the GET and POST routes these tools use (an allowlist of route templates in src/client.ts). Any other request, including a GET no tool uses, is refused before sign-in.

Running

Requires Node.js 22 or later.

npm ci && npm run build
MARKETSHARP_COMPANY_ID=… MARKETSHARP_API_KEY=… MARKETSHARP_API_SECRET=… node dist/index.js   # stdio

The Docker image defaults to MCP_TRANSPORT=http and AUTH_MODE=gateway. To run the image over stdio with MARKETSHARP_* credentials, pass -e MCP_TRANSPORT=stdio -e AUTH_MODE=env as well; otherwise it fails closed with "credentials are not configured".

HTTP (MCP_TRANSPORT=http, /mcp + /health) refuses to start without a real CONDUIT_S2S_SECRET (at least 32 characters, not a known or placeholder-looking value, not a repeated pattern, at least 8 distinct characters) and requires a valid X-Gateway-S2S header on every /mcp request. /mcp takes POST only (405 with Allow: POST otherwise). MCP_ALLOWED_HOSTS (comma-separated, off when unset) limits the Host headers /mcp accepts. Every response sets Cache-Control: no-store.

  • AUTH_MODE is gateway (the default when unset) or env. Any other value refuses to start. In gateway mode credentials come only from the x-marketsharp-* headers; MARKETSHARP_* env is never read. env is for a single tenant and reads MARKETSHARP_* from the environment; over HTTP it refuses to start unless the company id, API key and API secret are set and the ids look like MarketSharp ids. Stdio uses the environment only when AUTH_MODE is unset or exactly env; any other value (including a typo) is treated as gateway, so env credentials are never used by mistake.

  • Tool tiers: read is the only tier. Conduit's X-Conduit-Tool-Tiers header can only narrow that set for one request. A present but empty header means no tools at all. Unlike the fleet template there is deliberately no MARKETSHARP_TOOL_TIERS variable: with one tier there is nothing to choose, and nothing in the environment can change the tier set.

  • Every request goes to the fixed host https://restapi.marketsharpm.com at exactly the path that was built. Ids in paths must look like MarketSharp ids (1-64 letters, digits or dashes, which covers numbers and GUIDs); anything else is rejected before any request. Redirects are refused (redirect: 'manual', any 3xx is an error), so the bearer token never leaves the host. Response bodies are read with a 4 MiB cap.

  • Tool output, tool errors and log lines are redacted: the API key and secret in use (env or per request), bearer tokens and JWTs become [REDACTED]. In MarketSharp responses, values under credential-named keys (apiKey, authorization, token, accessToken, refreshToken, and any key ending in password or secret) are redacted too. Arguments a tool doesn't declare are dropped before it runs. Vendor errors keep the HTTP status, MarketSharp's message and, when sent, the request id (x-request-id, request-id or x-correlation-id).

  • Page sizes and record counts above a tool's maximum are rejected, not silently lowered, so "fewer rows than rowsPerPage means the last page" stays true. Pages are passed through as given; the server never fetches more than one page per call.

  • MCP_ALLOW_INSECURE_DEV=1 lets HTTP start without a secret, on 127.0.0.1 only, and requires NODE_ENV=development. With any other NODE_ENV the server refuses to start.

  • Tool output is MarketSharp data that other people may have typed (lead forms, notes, names). Every successful result is escaped and wrapped in <untrusted-content nonce="…"> markers behind a notice telling the model not to follow instructions inside it. MarketSharp's own error text is wrapped the same way.

See .env.example. The image's health check probes /health only when MCP_TRANSPORT=http.

Limits and unknowns

  • MarketSharp doesn't publish rate limits. The server retries 429 with Retry-After or exponential backoff (3 tries). A 503 is retried only for GET requests. Searches and filtered lists (which use POST) return it instead.

  • The /token 200 response shape isn't documented. The server accepts access_token or token, top level or inside response, or a bare JWT body (plain or JSON-quoted).

  • HTTP request bodies are capped at 256 KiB (262,144 bytes). Larger bodies, declared or chunked, get a 413.

  • Responses are capped at 200,000 characters; narrow with filters or paging. An oversized list keeps whole records and its paging fields (also when the page sits under response or data), and a note says how many records were left out. On the paged lists the note names the exact pageNumber and rowsPerPage (page and pageSize for employees) that read on from the first record left out; other tools can't page, so it says to narrow with filters. A list whose first record alone is over the cap is cut as text.

  • The QuickFind searches always send numberOfRecords (default 25): it is a non-nullable int32 in the spec, and what MarketSharp does with 0 is undocumented.

  • Not covered: calendar, texting, email, PaySimple, QuickBooks, CompanyCam, automation rules, analytics, estimates (v1 only), and all admin/config writes.

Security notes

  • companyId and employeeId are identifiers, not secrets. The companies tools return them by design.

  • MCP_ALLOWED_HOSTS is opt-in defence in depth behind S2S, not an auth control. It matches the Host header's hostname and ignores the port.

  • The S2S signature covers only the timestamp, the same as the gateway's verifier. Recipient binding comes from the per-vendor subkey.

  • MCP_ALLOW_INSECURE_DEV must never be set in a deployment.

  • A request has at most 4 MarketSharp calls in flight, each counted from the start of the fetch until its response body is fully read. A large JSON-RPC batch queues instead of fanning out. A retry's wait doesn't hold a slot.

Development

npm ci --ignore-scripts
npm run typecheck        # tsc --noEmit
npm run lint             # eslint + destructive-tool warning lint
npm run lint:destructive # destructive-tool warning lint only
npm run build
npm test                 # vitest, no network
npm run smoke            # spawns dist/index.js over stdio, no network

evals/ holds the mcp-assert suite that CI runs against the built server.

Each release publishes ghcr.io/wyre-ai/marketsharp-mcp, and its release notes are on the GitHub Release for that version.

License

Apache-2.0

Available Tools

25 tools
marketsharp_activity_types_listA
Read-onlyIdempotent

List the company's activity types (e.g. Call, Email, Follow Up).

Inputs: includeInactive.

marketsharp_appointment_results_listA
Read-onlyIdempotent

List the company's active appointment results (e.g. Sold, No Sale), or the result reasons.

Inputs: reasons.

marketsharp_appointments_getA
Read-onlyIdempotent

Get one appointment by ID: time, salespeople, type, result and address.

Inputs: appointmentId (required).

marketsharp_appointments_listA
Read-onlyIdempotent

List appointments page by page by appointment date range, change date range, salesperson or inquiry. Use this for "what's on the schedule".

Inputs: inquiryIds, modifiedTo, pageNumber, rowsPerPage, +4 more.

marketsharp_appointments_list_by_contactB
Read-onlyIdempotent

List every appointment for one contact.

Inputs: contactId (required).

marketsharp_companies_getA
Read-onlyIdempotent

Get this connection's MarketSharp company profile (name, address, time zone and settings).

Inputs: no named parameters.

marketsharp_companies_listA
Read-onlyIdempotent

List the MarketSharp companies this API key can access, with their IDs.

Inputs: no named parameters.

marketsharp_contacts_getA
Read-onlyIdempotent

Get one contact (customer or prospect) by contact ID: name, addresses, emails, phone numbers and do-not-contact flags.

Inputs: contactId (required).

marketsharp_contacts_listB
Read-onlyIdempotent

List contacts page by page, optionally only those changed since a date or with appointments in a date range or for one salesperson.

Inputs: pageNumber, rowsPerPage, appointmentTo, modifiedSince, +2 more.

marketsharp_divisions_listC
Read-onlyIdempotent

List the company's divisions.

Inputs: no named parameters.

marketsharp_employees_listB
Read-onlyIdempotent

List employees (salespeople, project managers and staff) with their IDs, page by page.

Inputs: page, search, pageSize, activeOnly.

marketsharp_inquiries_getA
Read-onlyIdempotent

Get one inquiry (lead) by ID: date, status, lead sources, product interests, address and notes.

Inputs: inquiryId (required), appointmentRequired.

marketsharp_inquiries_listB
Read-onlyIdempotent

List inquiries (leads) page by page, optionally only those changed in a date range, with appointments in a date range, or for one salesperson.

Inputs: modifiedTo, pageNumber, rowsPerPage, modifiedFrom, +4 more.

marketsharp_inquiries_list_by_contactB
Read-onlyIdempotent

List every inquiry (lead) for one contact.

Inputs: contactId (required), appointmentRequired.

marketsharp_inquiry_notes_listA
Read-onlyIdempotent

List the notes on an inquiry (lead), or on the job created from it. Needs MarketSharp's View Opportunity Note permission.

Inputs: inquiryId (required), noteType.

marketsharp_inquiry_statuses_listC
Read-onlyIdempotent

List the company's inquiry (lead) statuses.

Inputs: includeInactive.

marketsharp_jobs_getA
Read-onlyIdempotent

Get one job by ID: name, type, status, site address, sale and completion dates, and contract total.

Inputs: jobId (required).

marketsharp_lead_sources_listA
Read-onlyIdempotent

List the company's primary lead sources, or the secondary lead sources under one primary source.

Inputs: includeInactive, primaryLeadSourceId.

marketsharp_notes_listA
Read-onlyIdempotent

List the notes for a contact: all types, or one type such as ContactNote, Appointment, Inquiry or Job. Needs an Employee ID on the connection, and runs with that employee's MarketSharp permissions.

Inputs: contactId (required), noteType.

marketsharp_opportunities_listA
Read-onlyIdempotent

List opportunities (jobs and leads) changed since a date, with job numbers, sale and contract dates, contract totals, contract status and balance due. Returns at most numberOfRecords (max 1000) with no paging. If you get exactly that many, the list may be incomplete: raise numberOfRecords (up to 1000), and say the result may be incomplete if it is already 1000. Needs MarketSharp's View Opportunity permission.

Inputs: fromDate (required), numberOfRecords.

marketsharp_product_interests_listA
Read-onlyIdempotent

List the company's product interests (e.g. Windows, Siding), or the ones attached to one inquiry (lead). Needs MarketSharp's View Product Interest permission.

Inputs: inquiryId, includeInactive.

marketsharp_search_globalA
Read-onlyIdempotent

Search the company for contacts, inquiries (leads) and jobs by name, address, email, phone or job number. Start here when you only have a name or phone number.

Inputs: query (required), type, numberOfRecords.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 25 tools

    Disambiguation4/5

    Most tools map to a distinct resource+action (contacts_get vs contacts_list vs appointments_list_by_contact), and the by_contact variants are clearly separated from the paged listers. The main overlap is the search family: search_global, contacts_search, inquiries_search and jobs_search all do QuickFind-style lookups, though descriptions do steer agents to start with search_global and note the Employee ID requirement on the others. notes_list vs inquiry_notes_list is also a subtle boundary but is explained.

    Naming Consistency4/5

    Nearly everything follows marketsharp_<resource>_<verb> with a shared prefix, which is highly predictable. Minor deviations: search_global is verb-first rather than resource-first, and some resources are singular where others are plural (inquiry_statuses_list, inquiry_notes_list, appointment_results_list vs contacts_list). notes_list is also vaguer than the pattern implies (it is contact-scoped notes).

    Tool Count4/5

    At 25 tools this is at the upper bound, but the domain is broad (contacts, inquiries, appointments, jobs, opportunities, notes) and seven of the tools are small reference-data lookups (statuses, results, activity types, lead sources, product interests, divisions, employees) that genuinely earn their place. It is heavy but not padded.

    Completeness3/5

    The read surface is thorough across every major entity, but the server appears read-only: there is no create, update or delete for contacts, inquiries, appointments, jobs or notes, which is a notable gap for a CRM. Job enumeration is also awkward — there is jobs_get and jobs_search but no jobs_list, so listing jobs depends on opportunities_list, which is change-date driven and capped at 1000 records with no paging.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Read-only MCP server for the Daktela contact center REST API, providing 40 tools to access tickets, calls, emails, chats, contacts, CRM records, campaigns, and real-time agent status.
      45
      1
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables access to financial market data including EOD, intraday, fundamentals, news, and more via 75 read-only MCP tools.
      5
      MIT
    • F
      license
      A
      quality
      C
      maintenance
      Exposes read-only GoCanvas API v3 endpoints as MCP tools, enabling listing and retrieval of forms, submissions, reports, and reference data.
      16
      -
    • A
      license
      A
      quality
      D
      maintenance
      Provides read-only access to the Mailchimp Marketing API v3 via 18 tools covering 103 endpoints for audiences, members, campaigns, reports, automations, templates, landing pages, e-commerce, and account administration.
      18
      99 npm
      MIT