Clearinghouse
Provides tools to search and retrieve Salesforce opportunities, deal status, recent activity, and run coverage checks on open deals.
Provides tools to retrieve recent messages from Slack channels mapped to deals and identify external participants.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ClearinghouseWhat's the deal status for Acme Corp?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Clearinghouse
Single-auth GTM deal-intelligence MCP server for 1upHealth. One connector in
Claude (https://mcp.1uphealth.com/mcp), one "Continue with Google" sign-in,
and every deal answer the company already has in Gong, Salesforce, and Slack —
without anyone ever connecting those three systems themselves. The user's token
stops at this server; downstream systems are reached with org credentials that
never leave GCP.
PRD (v1.0 draft, 2026-08-05): the Claude artifact "Clearinghouse — PRD".
Status — Weeks 1–4 slice
OAuth 2.1 resource server — RFC 9728 metadata at
/.well-known/oauth-protected-resource, the 401 +WWW-Authenticate: resource_metadatahandshake Claude requires, JWT verification against the rented authorization server via JWKS.find_deal— fuzzy deal name → real Salesforce opportunities.deal_status— the Salesforce picture of one deal. Itscoveragefield points at the two tools below rather than returning a silently thin answer.deal_channel_activity— recent messages in the one Slack channel mapped to a deal (Slack_Channel_Id__con the Opportunity). No workspace-wide search; Slack Connect guests are flaggedexternal, not hidden.call_details— recent Gong calls on a deal: when, how long, who was on them, and Gong's brief only once Decision D is answered (see below).recent_activity— no deal Id required: the most recently modified Salesforce opportunities in a window, each annotated with how many Slack messages and Gong calls landed on it in the same window. For "what should I catch up on."coverage_check— bulk data-hygiene sweep across open deals: which ones have no Slack channel mapped, no next step, or no Gong call on file.deal_status's owncoveragefield answers this one deal at a time; this is the many-deals-at-once version.pipeline_snapshot— the read-only, Claude-facing sibling ofpipeline-pulse: same fiction detection (ghost_expansion,renewal_blindspot,stale_momentum), answered inline instead of on a schedule, and it never proposes or writes anything to Planhat.Roster gate — Git-backed
roster.json, deny by default, denial audited.Audit — every tool call logged as one JSON line (actor, tool, args, systems, bytes, latency) for the Cloud Logging → BigQuery sink.
Injection guards — typed enumerated inputs, one escape path for SOQL, Salesforce Ids validated by shape, LIMIT capped server-side; free-text fields (deal descriptions, Slack messages, Gong call titles and briefs) returned inside a labeled external-data envelope.
pipeline-pulseroutine — a service-to-service job (not a Claude tool) that cross-references Salesforce opportunities against Planhat companies to flag "fictions": pipeline data that looks fine but isn't (ghost_expansion,renewal_blindspot,stale_momentum). See below./dashboard— a browser UI behind the same roster gate as/mcp, for people who want the data directly instead of asking Claude: deal lookup, a pipeline-pulse review board, and roster/audit admin. See below.
Not yet built (Week 5+): the Gong nightly index that replaces the window scan, per-person budgets, directory sync.
Related MCP server: Salesforce MCP Server
Run locally
npm install
cp .env.example .env # defaults: AUTH_MODE=dev, SF_MODE=mock, PLANHAT_MODE=mock
npm run dev
npm testSmoke-test with MCP Inspector: npx @modelcontextprotocol/inspector, connect
to http://localhost:8080/mcp (streamable HTTP). In dev mode identity comes
from DEV_USER_EMAIL, which must be on roster.json. Dev mode refuses to
start when NODE_ENV=production.
Launch gates → where they live
Gate | What | Where |
01 Fake-"Claude" phishing | DCR off, one pinned client, one redirect URI ( | Auth-vendor dashboard — two settings, do this the day the tenant exists |
02 Lookalike connector URL | Host on | Decision A + Claude admin console |
03 Gong tenant walk |
|
|
04 PHI on sales calls |
| Decision D — |
05 Salesforce signing key | JWT bearer as one pre-authorized integration user ( | Code + Salesforce setup runbook below |
06 Roster misconfiguration | Git-backed |
|
Salesforce live setup (Gate 05 runbook)
Create integration user
clearinghouse@1uphealth.comwith a read-only profile scoped to Opportunity + Account.In Cloud Shell (so the key never touches a laptop):
openssl req -x509 -newkey rsa:2048 -nodes -keyout sf.key -out sf.crt -days 730Connected app: upload
sf.crt, enable OAuth, scopesapi; Admin approved users are pre-authorized; assign only the integration user's profile; set Relax IP restrictions = Enforce, allowlist the Cloud Run static egress IP (via Serverless VPC connector + Cloud NAT).Put
sf.keyin Secret Manager asSF_PRIVATE_KEY; delete the local copy.Set
SF_MODE=live,SF_CLIENT_ID(consumer key),SF_USERNAME.
Gong live setup (Gates 03 + 04 runbook)
Gong admin → API key for this service only; store both halves in Secret Manager as
GONG_ACCESS_KEY/GONG_ACCESS_KEY_SECRET.Leave
GONG_CONTENT=metadata. In this mode the content selector sent to Gong omitscontent.briefentirely, so no spoken content reaches this process, its memory, or its logs — the gate is the request, not a filter.Before flipping to
GONG_CONTENT=summaries: confirm the BAA and retention terms covering call recordings (Decision D), then setGONG_PHI_REVIEW_SIGNED_OFF=true. Live Gong refuses to boot with summaries on while that is false — deliberately two hands, not one flag.Verify the response shape first.
src/gong/live.tsfollows Gong's documented/v2/calls/extensivepayload (metaData,parties[].affiliation,context[].objects[]) but has never run against 1upHealth's tenant. Check one real call beforeGONG_MODE=live.GONG_LOOKBACK_DAYS(default 180) bounds the window scan. This is the direct-read stand-in for the PRD's nightly index; when call volume makes the scan slow, the index lands behind the sameGongClientinterface.
Pipeline-pulse routine (Planhat)
POST /routines/pipeline-pulse is a separate, service-to-service path — not
part of the /mcp connector Claude talks to, and not reachable by an end-user
Claude session. It's meant to be triggered by Cloud Scheduler, not a person.
What it does: pulls Salesforce opportunities and Planhat companies, then runs three deterministic detectors over them —
ghost_expansion— a Planhat expansion signal with no real health behind it (seesrc/fictions/rules/ghostExpansion.ts)renewal_blindspot— a Planhat renewal coming due (or overdue) with no open Salesforce opportunity anywhere near that datestale_momentum— a late-stage opportunity that's gone quiet in both Salesforce and Planhat
High-confidence findings can propose a [DRAFT] Planhat project as a
follow-up; everything else surfaces as a Slack nudge (suggestedAction).
ROUTINES_DRY_RUN=true by default means the routine always computes and
returns what it would create in Planhat without ever calling the write
method — the preview response and the real write share the same code path
(toProjectDraft in src/routines/pipelinePulse.ts), so the preview can't
drift from reality.
Before setting PLANHAT_MODE=live or ROUTINES_DRY_RUN=false:
src/planhat/live.ts's field mapping (RawCompany) is a best-effort guess at Planhat's schema — a real-tenant lookup was started but never finished. Verify it against one real company record first.Generate a Planhat API token scoped to this service, store it as
PLANHAT_API_TOKEN(Secret Manager in Cloud Run, never a file in the repo).Generate a random secret ≥16 chars for
ROUTINES_SHARED_SECRET; whoever calls the route (Cloud Scheduler) sends it asx-routines-secret. Unset = the route always 403s, independent of whether the rest of the server is configured.Only after (1) is confirmed against real data, flip
ROUTINES_DRY_RUN=false— until then, run it in dry-run and read theproposedProjectsit returns.
Local dashboard
/dashboard is a second, human-facing surface — same server, same
bearerAuth + roster gate as /mcp, just a browser tab instead of a Claude
conversation. For anyone who wants to look something up directly, or for
demoing/debugging without going through Claude at all. Vanilla JS, no build
step, no separate deploy.
Five tabs:
Deal lookup — search by name (
find_deal), pick a result, see the same combined Salesforce + Gong + Slack picturedeal_status/deal_channel_activity/call_detailsgive Claude.Recent activity —
recent_activityas a form: an owner filter and a day count instead of a deal Id.Coverage check —
coverage_checkas a form: which open deals are missing a Slack channel, a next step, or a Gong call.Pipeline-pulse — a "Run pipeline-pulse (dry run)" button that lists the fictions it finds and the Planhat projects it would propose. This button always forces
dryRun: true, regardless ofROUTINES_DRY_RUNin config — a person clicking a button in a browser should never be the thing that writes to Planhat; only the Cloud Scheduler-triggered/routines/pipeline-pulsepath can do that, and only once (1) in that section's runbook is done. (No separate tab forpipeline_snapshot— same underlying scan, so this tab already covers it;pipeline_snapshotexists as its own MCP tool because Claude needs a read-only-only version with no proposed-projects section at all.)Admin & audit — the roster's members, and a live tail of the most recent audit events (in-memory, capped, lost on restart — the durable trail is still Cloud Logging → BigQuery; this is a convenience view, not a second source of truth).
Free text from external systems (deal descriptions, Slack messages, Gong call
titles) is written into the page with textContent, never innerHTML — same
untrusted-data handling as everywhere else in this codebase, just for a human
reader instead of an LLM.
Auth vendor setup (Decision C — vendor-agnostic checklist)
Whichever of WorkOS AuthKit / Auth0 / Stytch / Descope is chosen, on
auth.1uphealth.com (CNAME so the vendor stays swappable):
Federate to Google Workspace; restrict to the 1uphealth.com hosted domain.
Disable dynamic client registration (Gate 01).
Register exactly one client, redirect URI
https://claude.ai/api/mcp/auth_callback, PKCE required.Access tokens: JWT, audience =
https://mcp.1uphealth.com/mcp, includeemail. Long-lived rotating refresh tokens with reuse detection — Claude's background refresh is unreliable, and a dead refresh token must return exactlyinvalid_grantor users get wedged.Do not put a WAF in front of the IdP that blocks Anthropic egress (
160.79.104.0/21must reach both this server and the AS).Fill
AUTH_ISSUER+AUTH_JWKS_URL, setAUTH_MODE=oauth.
Deploy (Cloud Run)
Stateless by construction: one MCP server instance per request, JSON responses, no sessions — safe under autoscaling.
Public ingress; OAuth enforced in-app (Claude cannot mint Google IAM tokens, so Cloud Run IAM protection is unusable).
roster.jsonships in the image; the deploy pipeline is the only writer (Gate 06). CI syncs it from the repo — or from the restricted Google group if Decision E lands that way.
Open decisions (PRD §08)
Decision | Recommendation | Where it lands here | |
A | Domain |
|
|
B | One access tier or two | One tier for v1, stated at consent | roster stays one flat list |
C | Auth vendor | Any of the four; keep DNS ours |
|
D | PHI / BAA gate | Confirm Claude-plan retention terms + GCP BAA; only decision that can change the product |
|
E | Roster ownership | One restricted Google group, synced by CI | replaces hand-edits to |
Deliberately not building
No workspace-wide Slack search, no stored Slack messages, no write tools in
the Claude-facing connector, ever, no admin tools inside the connector (PRD
§06). The one write path in this repo — pipeline-pulse creating draft
Planhat projects — is a separate service-to-service routine Claude can never
reach, dry-run by default, and still gated on an unverified field mapping
(see above).
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceAn MCP server implementation that integrates Claude with Salesforce, enabling natural language interactions with Salesforce data and metadata for querying, modifying, and managing objects and records.6151,909166MIT
- AlicenseAqualityCmaintenanceAn MCP server implementation that integrates Claude with Salesforce, enabling natural language interactions with Salesforce data and metadata for querying, modifying, and managing objects and records.75014MIT
- AlicenseAqualityDmaintenanceAn MCP server implementation that integrates Claude with Salesforce, enabling natural language interactions with Salesforce data and metadata.850MIT
- AlicenseAqualityBmaintenanceOne MCP server for the SaaS back office. Stripe, HubSpot, and Google Sheets exposed as typed, read-only-by-default tools for Claude and any MCP client.1MIT
Related MCP Connectors
MCP server giving Claude AI access to 22+ NYC public-record databases for real estate due diligence
Surface customer & prospect context from Slack, email, transcripts and tickets in any MCP client.
MCP server for LeadDelta — manage LinkedIn connections and CRM data via AI assistants.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jodipatton/clearinghouse'
If you have feedback or need assistance with the MCP directory API, please join our Discord server