MCP App Store

Overview

4Degrees brings your relationship intelligence and deal flow into ChatGPT. Ask about your contacts, companies, and deals in plain English — "who's my warmest path to a CTO at Stripe?", "show me Series B deals by stage", "what needs my attention today?". You can also update your CRM — add tags, log notes, and move deal stages — with a preview-and-confirm step on every change and one-tap undo. Scoped per-teammate via OAuth to the data and permissions your seat already has. Designed for private-markets investors, M&A teams, and search funds.

Tools

act

ChatGPT
Perform a verb-style write operation (snooze, complete, undo, …). Unlike `mutate (which uses a dotted resource.action key), act accepts a plain verb string and resolves it to an ActionSpec via the action registry. Verb lookup order: 1. Universal scope — built-in verbs such as undo that apply across resources. 2. Resource-scoped fallback — verbs registered for a specific target_type (e.g., snooze for reminder). Most verbs follow the same two-call confirmation flow as mutate: 1. Call act without a confirmation_token — the server returns a preview and a confirmation_token. 2. Call act again with the same arguments PLUS the confirmation_token to commit the write. Verbs with min_confirmation: none execute in a single call. Args: verb: The verb to execute, e.g. "snooze", "complete", "undo". payload: Verb-specific payload dict. Schema is validated server-side; an invalid payload returns a validation.schema error. Defaults to {} for verbs with an empty payload schema. target_type: Resource type of the target, e.g. "reminder". Required for resource-scoped verbs when the universal lookup would miss. target_id: Integer ID of the target record. confirmation_token: Token returned by the preview call. Supply this to commit the write after reviewing the preview. idempotency_key: Client-generated unique key (UUID recommended). Required for programmatic clients on create/update/delete verbs. auto_confirm: If True, programmatic clients may skip the confirmation step for single_call_token verbs. Has no effect on preview_required / human_required verbs. Returns: One of: - {confirmation_required: true, confirmation_token, preview, expires_at, fingerprint} — preview step, no write occurred. - {request_id, result, audit_url} — write committed. - {replayed: true, original_request_id, result} — idempotency replay (same result as first execution). - {error: {code, message, http_status, ...}} — error envelope. code="action.unknown_verb"` means the verb is not (yet) registered.

act

ChatGPT
Perform a verb-style write operation (snooze, complete, undo, …). Unlike `mutate (which uses a dotted resource.action key), act accepts a plain verb string and resolves it to an ActionSpec via the action registry. Verb lookup order: 1. Universal scope — built-in verbs such as undo that apply across resources. 2. Resource-scoped fallback — verbs registered for a specific target_type (e.g., snooze for reminder). Most verbs follow the same two-call confirmation flow as mutate: 1. Call act without a confirmation_token — the server returns a preview and a confirmation_token. 2. Call act again with the same arguments PLUS the confirmation_token to commit the write. Verbs with min_confirmation: none execute in a single call. Args: verb: The verb to execute, e.g. "snooze", "complete", "undo". payload: Verb-specific payload dict. Schema is validated server-side; an invalid payload returns a validation.schema error. Defaults to {} for verbs with an empty payload schema. target_type: Resource type of the target, e.g. "reminder". Required for resource-scoped verbs when the universal lookup would miss. target_id: Integer ID of the target record. confirmation_token: Token returned by the preview call. Supply this to commit the write after reviewing the preview. idempotency_key: Client-generated unique key (UUID recommended). Required for programmatic clients on create/update/delete verbs. auto_confirm: If True, programmatic clients may skip the confirmation step for single_call_token verbs. Has no effect on preview_required / human_required verbs. Returns: One of: - {confirmation_required: true, confirmation_token, preview, expires_at, fingerprint} — preview step, no write occurred. - {request_id, result, audit_url} — write committed. - {replayed: true, original_request_id, result} — idempotency replay (same result as first execution). - {error: {code, message, http_status, ...}} — error envelope. code="action.unknown_verb"` means the verb is not (yet) registered.

bulk_mutate

ChatGPT
Apply one write action to multiple CRM targets in a single call. For small batches (≤ 50 rows, no external side effects), the call executes inline and returns per-row results synchronously. For larger batches or actions with external side effects, the call enqueues an async job and returns immediately with a `job_id. Poll GET /mcp/jobs/{job_id} to check progress. Each target receives its own mcp_write_request journal entry so that every row is individually auditable and idempotent. Hard limits: - Maximum 5 000 rows per call (bulk.size_exceeded if exceeded). - partial_failure_policy="all_or_nothing" is forbidden for > 20 rows. - The write:bulk scope is required in addition to the per-action scope. Args: action_key: Dotted action identifier applied to every target, e.g. contact.add_tag.v1. targets: List of {type, id} dicts identifying the records to act on. Example:: [{"type": "contact", "id": 42}, {"type": "contact", "id": 99}] payload: Action payload applied to every row (same for all targets). Per-target payload customisation is not supported in Wave 1. idempotency_key: Client-generated unique key for the bulk envelope. Per-row keys are derived as {idempotency_key}:{target_type}:{target_id} so individual rows remain idempotent on retry. confirmation_token: Required when the inline batch requires re-confirmation (> ASYNC_THRESHOLD rows that are still eligible for inline execution). partial_failure_policy: How to handle per-row failures. One of: - "continue" (default) — continue processing remaining rows after a failure. - "abort_on_first" — stop at the first failed row; return results up to that point. - "all_or_nothing" — all rows in a single transaction; forbidden for > 20 rows. auto_confirm: If True, programmatic clients may skip the confirmation step for eligible action tiers. Has no effect on async-path jobs (those were already confirmed at enqueue time). Returns: Inline path (synchronous): {total, succeeded, failed, rows: [{target, status, request_id?, error?, message?}, ...]} Async path (job enqueued): {job_id, state: "pending", total_rows} Error: {error: {code, message, http_status, ...}}`

bulk_mutate

ChatGPT
Apply one write action to multiple CRM targets in a single call. For small batches (≤ 50 rows, no external side effects), the call executes inline and returns per-row results synchronously. For larger batches or actions with external side effects, the call enqueues an async job and returns immediately with a `job_id. Poll GET /mcp/jobs/{job_id} to check progress. Each target receives its own mcp_write_request journal entry so that every row is individually auditable and idempotent. Hard limits: - Maximum 5 000 rows per call (bulk.size_exceeded if exceeded). - partial_failure_policy="all_or_nothing" is forbidden for > 20 rows. - The write:bulk scope is required in addition to the per-action scope. Args: action_key: Dotted action identifier applied to every target, e.g. contact.add_tag.v1. targets: List of {type, id} dicts identifying the records to act on. Example:: [{"type": "contact", "id": 42}, {"type": "contact", "id": 99}] payload: Action payload applied to every row (same for all targets). Per-target payload customisation is not supported in Wave 1. idempotency_key: Client-generated unique key for the bulk envelope. Per-row keys are derived as {idempotency_key}:{target_type}:{target_id} so individual rows remain idempotent on retry. confirmation_token: Required when the inline batch requires re-confirmation (> ASYNC_THRESHOLD rows that are still eligible for inline execution). partial_failure_policy: How to handle per-row failures. One of: - "continue" (default) — continue processing remaining rows after a failure. - "abort_on_first" — stop at the first failed row; return results up to that point. - "all_or_nothing" — all rows in a single transaction; forbidden for > 20 rows. auto_confirm: If True, programmatic clients may skip the confirmation step for eligible action tiers. Has no effect on async-path jobs (those were already confirmed at enqueue time). Returns: Inline path (synchronous): {total, succeeded, failed, rows: [{target, status, request_id?, error?, message?}, ...]} Async path (job enqueued): {job_id, state: "pending", total_rows} Error: {error: {code, message, http_status, ...}}`

compare

ChatGPT
Compare and aggregate CRM data — counts, averages, totals grouped by a field. Performs server-side aggregation so you don't need to fetch raw records. Args: resource: What to aggregate — 'contacts', 'deals'. metric: What to measure — 'count'. group_by: How to group — 'pipeline', 'stage', 'owner'. filters: Optional filters (same syntax as query). format: Output format — 'csv' (default), 'markdown', 'json'. Returns: Aggregated results. Much more compact than fetching all records. Examples: compare("deals", metric="count", group_by="pipeline") compare("deals", metric="count", group_by="stage") compare("contacts", metric="count", group_by="owner")

compare

ChatGPT
Compare and aggregate CRM data — counts, averages, totals grouped by a field. Performs server-side aggregation so you don't need to fetch raw records. Args: resource: What to aggregate — 'contacts', 'deals'. metric: What to measure — 'count'. group_by: How to group — 'pipeline', 'stage', 'owner'. filters: Optional filters (same syntax as query). format: Output format — 'csv' (default), 'markdown', 'json'. Returns: Aggregated results. Much more compact than fetching all records. Examples: compare("deals", metric="count", group_by="pipeline") compare("deals", metric="count", group_by="stage") compare("contacts", metric="count", group_by="owner")

discover

ChatGPT
Discover available CRM resources, fields, and query syntax. Call with no arguments to list all resources. Call with a resource name for detailed field info, your org's custom fields, pipelines, and example queries. Args: resource: Optional — 'contacts', 'companies', 'deals', 'interactions', 'custom_fields', 'team', 'pipelines', 'tags'. Returns: Resource descriptions with example query() calls. Includes your org's custom fields and pipeline names.

discover

ChatGPT
Discover available CRM resources, fields, and query syntax. Call with no arguments to list all resources. Call with a resource name for detailed field info, your org's custom fields, pipelines, and example queries. Args: resource: Optional — 'contacts', 'companies', 'deals', 'interactions', 'custom_fields', 'team', 'pipelines', 'tags'. Returns: Resource descriptions with example query() calls. Includes your org's custom fields and pipeline names.

inferred_paths

ChatGPT
Find inferred warm introduction paths to a company via shared past employment. Identifies connectors in your network who worked alongside people at the target company at a shared past employer, even if no direct interaction is recorded. Args: company: The target company name to find warm paths to. Returns: Ranked list of inferred paths with connector, confidence, shared context, and a recommended introduction ask. Returns a friendly message when no paths are found. Examples: inferred_paths("Acme Corp") inferred_paths("Middesk")

inferred_paths

ChatGPT
Find inferred warm introduction paths to a company via shared past employment. Identifies connectors in your network who worked alongside people at the target company at a shared past employer, even if no direct interaction is recorded. Args: company: The target company name to find warm paths to. Returns: Ranked list of inferred paths with connector, confidence, shared context, and a recommended introduction ask. Returns a friendly message when no paths are found. Examples: inferred_paths("Acme Corp") inferred_paths("Middesk")

mutate

ChatGPT
Perform a write action on a CRM resource. Most actions require a two-call confirmation flow: 1. Call `mutate without a confirmation_token — the server returns a preview and a confirmation_token. 2. Call mutate again with the same arguments PLUS the confirmation_token to commit the write. Actions with min_confirmation: none execute in a single call. Programmatic clients (API key / OAuth) may pass auto_confirm=True to skip confirmation for single_call_token actions. Confirmation tier per action (B-15 — discoverable): +-----------------------------------------+------------------------+ | Tier | Actions | +-----------------------------------------+------------------------+ | NONE (single-call, no token needed) | contact.add_tag, | | | contact.remove_tag, | | | contact.add_note, | ← executes immediately | | contact.add_interaction| | | contact.update_*, | | | company.add_tag, | | | company.remove_tag, | | | company.add_note, | | | company.update_note, | | | company.update.v1, | | | deal.add_tag, | | | deal.remove_tag, | | | deal.move_stage, | | | deal.update.v1, | | | deal.add_note, | | | deal.update_note, | | | deal.add_contact, | | | deal.remove_contact, | | | reminder.* (act verbs) | +-----------------------------------------+------------------------+ | SINGLE_CALL_TOKEN (preview + token) | contact.create, | ← returns confirmation_token | | contact.add_email, | ← first call = preview only | | contact.add_phone, | ← second call with token commits | | contact.add_address, | | | contact.add_job, | | | contact.reactivate, | | | contact.remove_email, | | | contact.remove_phone, | | | contact.remove_address,| | | contact.remove_job, | | | contact.remove_interaction, | | | company.create, | | | company.remove_note, | | | deal.create, | | | deal.remove_note, | | | deal.update_record | +-----------------------------------------+------------------------+ | BULK_CLEAR_ESCALATION (dynamic) | *.set_custom_field | ← NONE when <5 clears | | (company/deal) | ← SINGLE_CALL_TOKEN when ≥5 clears +-----------------------------------------+------------------------+ NONE-tier actions do NOT need auto_confirm=True — they always execute on the first call. Only SINGLE_CALL_TOKEN actions need auto_confirm=True (or a confirmation_token from the preview response) to commit. Custom-field payload contract (B-10 — 4D-5250): All *.set_custom_field.v1 actions use ONE payload shape: {<entity>_id, fields: [{custom_field_id: int, value: ...}, ...]} - Only custom_field_id (int) is accepted — field_name / name / id strings are rejected with validation.schema. Enumerate IDs via query("custom_fields", filters={"entity_type":"<entity>"}). - Per-kind value shape: | Kind | Accepted value | |------------------|------------------------------------------------------| | Text / Long Text | str | | URL | str (must be a URL the server accepts) | | Number / Currency| int OR float OR decimal-shaped string | | Date | ISO date "YYYY-MM-DD" OR epoch int | | Select | option_id (int) OR label (str) | | Multi Select | list[int|str] — option_ids OR labels | | Contact Reference| contact_id (int) OR numeric string | | Formula | REJECTED — validation.field_kind_readonly (B-25) | - To clear a field: pass null (or "" / "none" / [] per kind — server normalises). - Max 50 entries per batch, no duplicate custom_field_id. - Idempotency rule: B-1/B-2 (4D-5250) — repeating the same call with the same idempotency_key returns {"replayed": true, "original_request_id": <uuid>, "result": <cached>}. For OAuth clients (Claude Desktop) replay is Valkey-only (24h TTL); PAT clients additionally have a DB backstop. Args: action_key: Dotted action identifier, e.g. contact.add_note.v1. Omit the version suffix to use the latest version. payload: Action-specific payload dict. Schema is validated server-side; an invalid payload returns a validation.schema error. target_type: Resource type of the target, e.g. contact`. Required for actions that operate on a specific record. target_id: Integer ID of the t…

mutate

ChatGPT
Perform a write action on a CRM resource. Most actions require a two-call confirmation flow: 1. Call `mutate without a confirmation_token — the server returns a preview and a confirmation_token. 2. Call mutate again with the same arguments PLUS the confirmation_token to commit the write. Actions with min_confirmation: none execute in a single call. Programmatic clients (API key / OAuth) may pass auto_confirm=True to skip confirmation for single_call_token actions. Confirmation tier per action (B-15 — discoverable): +-----------------------------------------+------------------------+ | Tier | Actions | +-----------------------------------------+------------------------+ | NONE (single-call, no token needed) | contact.add_tag, | | | contact.remove_tag, | | | contact.add_note, | ← executes immediately | | contact.add_interaction| | | contact.update_*, | | | company.add_tag, | | | company.remove_tag, | | | company.add_note, | | | company.update_note, | | | company.update.v1, | | | deal.add_tag, | | | deal.remove_tag, | | | deal.move_stage, | | | deal.update.v1, | | | deal.add_note, | | | deal.update_note, | | | deal.add_contact, | | | deal.remove_contact, | | | reminder.* (act verbs) | +-----------------------------------------+------------------------+ | SINGLE_CALL_TOKEN (preview + token) | contact.create, | ← returns confirmation_token | | contact.add_email, | ← first call = preview only | | contact.add_phone, | ← second call with token commits | | contact.add_address, | | | contact.add_job, | | | contact.reactivate, | | | contact.remove_email, | | | contact.remove_phone, | | | contact.remove_address,| | | contact.remove_job, | | | contact.remove_interaction, | | | company.create, | | | company.remove_note, | | | deal.create, | | | deal.remove_note, | | | deal.update_record | +-----------------------------------------+------------------------+ | BULK_CLEAR_ESCALATION (dynamic) | *.set_custom_field | ← NONE when <5 clears | | (company/deal) | ← SINGLE_CALL_TOKEN when ≥5 clears +-----------------------------------------+------------------------+ NONE-tier actions do NOT need auto_confirm=True — they always execute on the first call. Only SINGLE_CALL_TOKEN actions need auto_confirm=True (or a confirmation_token from the preview response) to commit. Custom-field payload contract (B-10 — 4D-5250): All *.set_custom_field.v1 actions use ONE payload shape: {<entity>_id, fields: [{custom_field_id: int, value: ...}, ...]} - Only custom_field_id (int) is accepted — field_name / name / id strings are rejected with validation.schema. Enumerate IDs via query("custom_fields", filters={"entity_type":"<entity>"}). - Per-kind value shape: | Kind | Accepted value | |------------------|------------------------------------------------------| | Text / Long Text | str | | URL | str (must be a URL the server accepts) | | Number / Currency| int OR float OR decimal-shaped string | | Date | ISO date "YYYY-MM-DD" OR epoch int | | Select | option_id (int) OR label (str) | | Multi Select | list[int|str] — option_ids OR labels | | Contact Reference| contact_id (int) OR numeric string | | Formula | REJECTED — validation.field_kind_readonly (B-25) | - To clear a field: pass null (or "" / "none" / [] per kind — server normalises). - Max 50 entries per batch, no duplicate custom_field_id. - Idempotency rule: B-1/B-2 (4D-5250) — repeating the same call with the same idempotency_key returns {"replayed": true, "original_request_id": <uuid>, "result": <cached>}. For OAuth clients (Claude Desktop) replay is Valkey-only (24h TTL); PAT clients additionally have a DB backstop. Args: action_key: Dotted action identifier, e.g. contact.add_note.v1. Omit the version suffix to use the latest version. payload: Action-specific payload dict. Schema is validated server-side; an invalid payload returns a validation.schema error. target_type: Resource type of the target, e.g. contact`. Required for actions that operate on a specific record. target_id: Integer ID of the t…

query

ChatGPT
Query CRM data with optional filters, field selection, and format control. Use discover() first to learn available resources and fields. Args: resource: What to query — 'contacts', 'companies', 'deals', 'interactions', 'custom_fields', 'team', 'pipelines', 'tags'. filters: Field-value pairs to filter results. Use {"id": "87"} for a specific record, {"name": "Jason"} for search. fields: Comma-separated field names to return (e.g. "name,email,company"). Default: all fields. sort: Sort field with optional 'Desc' suffix (e.g. "name", "strengthDesc"). limit: Max records (1-100, default 20). offset: Skip N records for pagination (default 0). format: Output format — 'csv' (default, most compact), 'markdown', 'json', 'summary'. Returns: Formatted CRM data. CSV is most token-efficient for follow-up queries. Examples: query("contacts", filters={"name": "Jason"}) query("contacts", filters={"id": "87"}, fields="name,email,company,strength") query("deals", filters={"name": "Acme"}, format="markdown") query("team") query("pipelines") query("tags") query("custom_fields", filters={"entity_type": "deal"})

query

ChatGPT
Query CRM data with optional filters, field selection, and format control. Use discover() first to learn available resources and fields. Args: resource: What to query — 'contacts', 'companies', 'deals', 'interactions', 'custom_fields', 'team', 'pipelines', 'tags'. filters: Field-value pairs to filter results. Use {"id": "87"} for a specific record, {"name": "Jason"} for search. fields: Comma-separated field names to return (e.g. "name,email,company"). Default: all fields. sort: Sort field with optional 'Desc' suffix (e.g. "name", "strengthDesc"). limit: Max records (1-100, default 20). offset: Skip N records for pagination (default 0). format: Output format — 'csv' (default, most compact), 'markdown', 'json', 'summary'. Returns: Formatted CRM data. CSV is most token-efficient for follow-up queries. Examples: query("contacts", filters={"name": "Jason"}) query("contacts", filters={"id": "87"}, fields="name,email,company,strength") query("deals", filters={"name": "Acme"}, format="markdown") query("team") query("pipelines") query("tags") query("custom_fields", filters={"entity_type": "deal"})

Capabilities

Writes

App Stats

14

Tools

ChatGPT

Platforms

Works with

ChatGPT

Data refreshed daily