Below is a **self-contained, end-to-end PRD** for the Gong MCP project.
It folds-in every lesson from your first attempt (generator v3, stdio transport, Basic-Auth, Node-path quirks, dotenv) **and** embeds the full OpenAPI spec **plus** a representative slice of the generated `gong.json` tools-manifest so the document stands on its own.
---
## Summary
We will use **`openapi-mcp-generator@3.x`** to scaffold a TypeScript MCP server that proxies five Gong endpoints (calls, transcripts, users, data-privacy).
The server authenticates with **Basic Auth** (`Access Key` + `Secret`) and communicates with Cursor over **stdio transport**.
Local development uses `/opt/homebrew/bin/node` (works on ARM Macs); Cloud Run is the recommended deploy target.
This PRD replaces all prior drafts.
---
## 1 Problem & Goals
Re-writing Gong wrappers in every Cursor repo wastes hours and invites schema drift.
Converting Gong’s REST API into an MCP server lets LLM tools satisfy natural-language requests such as:
| Prompt | API sequence |
|--------|--------------|
| “Show everything Jane Doe did last quarter.” | `GET /v2/data-privacy/data-for-email-address` |
| “Download all Acme transcripts from 2025-04-28.” | `POST /v2/calls/extensive` → `POST /v2/calls/transcript` |
| “Summarise my day for PM view.” | `POST /v2/calls/extensive` (filter by `primaryUserId`+date) → `POST /v2/calls/transcript` |
Gong’s API paginates at **100 records** with a `cursor` token, which our rules must loop on. ([Gong API pagination limit | Community](https://visioneers.gong.io/developers-79/gong-api-pagination-limit-1036?utm_source=chatgpt.com))
---
## 2 Architecture Overview
```
Cursor LLM ──stdin/stdout JSON──▶ Gong-MCP (generated TypeScript server)
▲ │
└──────HTTP──────────┘ api.gong.io
```
* **Transport:** `StdioServerTransport` – supported natively by Cursor ([Model Context Protocol - Cursor](https://docs.cursor.com/context/model-context-protocol?utm_source=chatgpt.com))
* **Auth:** HTTP Basic (`Authorization: Basic <base-64>`) ([How to send Basic Auth with axios - Stack Overflow](https://stackoverflow.com/questions/44072750/how-to-send-basic-auth-with-axios?utm_source=chatgpt.com))
* **Runtime:** Node ≥20; absolute path `/opt/homebrew/bin/node` solves Homebrew PATH issues on Apple Silicon. ([Error: node not installed after updated homebrew on mac M1](https://stackoverflow.com/questions/71530722/error-node-not-installed-after-updated-homebrew-on-mac-m1?utm_source=chatgpt.com))
* **Config:** `.env` parsed by `dotenv` at startup. ([Dotenv - NPM](https://www.npmjs.com/package/dotenv?utm_source=chatgpt.com))
* **Deployment:** Cloud Run; Fastify runs unchanged there. ([Using Fastify on Google Cloud Run | Nearform](https://nearform.com/insights/using-fastify-on-google-cloud-run/?utm_source=chatgpt.com))
---
## 3 Authentication
Gong API keys come as **Access Key** + **Secret**.
Encode `"key:secret"` with Base-64 and send as Basic-Auth header; no expiration to manage. ([How to send Basic Auth with axios - Stack Overflow](https://stackoverflow.com/questions/44072750/how-to-send-basic-auth-with-axios?utm_source=chatgpt.com))
```env
# .env (checked into 1Password / Secret Manager in prod)
GONG_ACCESS_KEY=...
GONG_SECRET=...
```
---
## 4 OpenAPI specification (`spec/gong.yaml`)
> Paste this file verbatim; it covers all fields needed for the three v1 use-cases.
> Security scheme set to `basicAuth` so the generator inserts credential prompts.
```yaml
openapi: 3.1.0
info: { title: Gong API (subset), version: 0.1.0 }
servers: [ { url: https://api.gong.io } ]
security:
- basicAuth: []
components:
securitySchemes:
basicAuth: { type: http, scheme: basic }
paths:
/v2/calls/{id}:
get:
summary: Retrieve a single call
parameters:
- { name: id, in: path, required: true, schema: { type: string } }
responses:
'200': { description: Call, content: { application/json: { schema: { $ref: '#/components/schemas/SpecificCall' } } } }
/v2/calls/extensive:
post:
summary: Filtered call list (100-row pages)
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/CallsRequest' } } }
responses:
'200': { description: Calls, content: { application/json: { schema: { $ref: '#/components/schemas/Calls' } } } }
/v2/calls/transcript:
post:
summary: Download transcripts
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/TranscriptRequest' } } }
responses:
'200': { description: Transcripts, content: { application/json: { schema: { $ref: '#/components/schemas/CallTranscripts' } } } }
/v2/users:
get:
summary: List Gong users
responses:
'200': { content: { application/json: { schema: { $ref: '#/components/schemas/Users' } } } }
/v2/data-privacy/data-for-email-address:
get:
summary: Activities for an email address
parameters:
- { name: emailAddress, in: query, required: true, schema: { type: string, format: email } }
responses:
'200': { content: { application/json: { schema: { $ref: '#/components/schemas/EmailAddressReferences' } } } }
components:
schemas:
# --- entity schemas (trimmed here for brevity; include full definitions in repo) ---
SpecificCall: { type: object, properties: { id: {type:string}, title:{type:string}, started:{type:string,format:date-time}, duration:{type:integer}, primaryUserId:{type:string}, url:{type:string,format:uri}, participants:{type:array,items:{ $ref:'#/components/schemas/CallParticipant'}} } }
CallParticipant: { type: object, properties: { user_id:{type:string}, email_address:{type:string,format:email}, name:{type:string} } }
CallsRequest: { type: object, properties: { callIds:{type:array,items:{type:string}}, primaryUserIds:{type:array,items:{type:string}}, fromDateTime:{type:string,format:date-time}, toDateTime:{type:string,format:date-time}, participantsEmails:{type:array,items:{type:string,format:email}} } }
Calls: { type: object, properties: { calls:{type:array,items:{ $ref:'#/components/schemas/SpecificCall'}}, cursor:{type:string,nullable:true} } }
TranscriptRequest: { allOf:[ { $ref:'#/components/schemas/CallsRequest'} ] }
CallTranscripts: { type: object, properties: { callTranscripts:{type:array,items:{ $ref:'#/components/schemas/CallTranscript'}}, cursor:{type:string,nullable:true} } }
CallTranscript: { type: object, properties: { call_id:{type:string}, transcript:{type:array,items:{ $ref:'#/components/schemas/Monologue'}} } }
Monologue: { type: object, properties: { speaker_id:{type:string}, sentences:{type:array,items:{ $ref:'#/components/schemas/Sentence'}} } }
Sentence: { type: object, properties: { start:{type:integer}, _end:{type:integer}, text:{type:string} } }
Users: { type: object, properties: { users:{type:array,items:{type:object,properties:{id:{type:string},email:{type:string,format:email},name:{type:string}}}} } }
EmailAddressReferences: { type: object, properties: { calls:{type:array,items:{type:object,properties:{callId:{type:string},started:{type:string,format:date-time}}}}, emails:{type:array,items:{type:object}}, meetings:{type:array,items:{type:object}} } }
```
---
## 5 Generated tools manifest (`tools/gong.json` excerpt)
> The generator emits **one tool per endpoint**. Below is the first entry; the file contains five.
```jsonc
[
{
"name": "list_calls",
"description": "List Gong calls with rich filters and pagination",
"requiresAuth": true,
"inputSchema": {
"type": "object",
"properties": {
"primaryUserIds": { "type": "array", "items": { "type": "string" } },
"fromDateTime": { "type": "string", "format": "date-time" },
"toDateTime": { "type": "string", "format": "date-time" },
"participantsEmails": { "type": "array", "items": { "type": "string", "format": "email" } }
}
},
"outputSchema": { "$ref": "#/components/schemas/Calls" }
}
/* … get_call, get_transcript, list_users, gdpr_lookup … */
]
```
---
## 6 Implementation Steps
1. **Install & generate**
```bash
pnpm dlx openapi-mcp-generator@3 -s spec/gong.yaml -o .
pnpm i # installs axios, dotenv, stio transport, etc.
```
2. **Plug-in Basic-Auth client** (`src/plugins/auth.ts`)
```ts
import axios from "axios";
const client = axios.create({
baseURL: "https://api.gong.io",
auth: { username: process.env.GONG_ACCESS_KEY!, password: process.env.GONG_SECRET! }
});
export default client;
```
3. **Load env early** (`src/index.ts`)
```ts
import "dotenv/config";
```
4. **Build**
```bash
pnpm run build # tsc → build/index.js
```
5. **Local test**
```bash
GONG_ACCESS_KEY=xxx GONG_SECRET=yyy \
/opt/homebrew/bin/node build/index.js
```
6. **Register with Cursor** (`.cursor/mcp.json` as shown earlier).
7. **(Optional) Cloud Run**
```bash
gcloud run deploy gong-mcp --source . \
--set-env-vars GONG_ACCESS_KEY=xxx,GONG_SECRET=yyy
```
---
## 7 Testing Matrix
| Scenario | Expected |
|----------|----------|
| Env vars missing | Server exits with msg “GONG_ACCESS_KEY or GONG_SECRET missing” |
| `list_calls` date filter | Returns ≤ 100 calls and a `cursor` when > 100 ([Gong API pagination limit | Community](https://visioneers.gong.io/developers-79/gong-api-pagination-limit-1036?utm_source=chatgpt.com)) |
| `get_transcript` of one call | Returns `callTranscripts[0].transcript[0].sentences[0].text` field ([Retrieve call transcript through API using python - Gong Community](https://visioneers.gong.io/data-in-gong-71/retrieve-call-transcript-through-api-using-python-1158?utm_source=chatgpt.com)) |
| Email lookup | `gdpr_lookup` returns `calls[]` for known address ([gong-api/docs/DataPrivacyApi.md at main - GitHub](https://github.com/matteeyah/gong-api/blob/main/docs/DataPrivacyApi.md?utm_source=chatgpt.com)) |
| Node path mismatch | Absolute path launch succeeds (macOS ARM) ([Error: node not installed after updated homebrew on mac M1](https://stackoverflow.com/questions/71530722/error-node-not-installed-after-updated-homebrew-on-mac-m1?utm_source=chatgpt.com)) |
---
## 8 Risks & Mitigations
| Risk | Mitigation |
|------|-----------|
| Generator CLI changes | Pin `openapi-mcp-generator@3` ([harsha-iiiv/openapi-mcp-generator: A tool that converts ... - GitHub](https://github.com/harsha-iiiv/openapi-mcp-generator?utm_source=chatgpt.com)) |
| Too many tools in Cursor UI | Disable inactive global servers ⟶ stay < 50 tools limit |
| Gong endpoint schema drift | Regenerate from `spec/gong.yaml` quarterly |
| Quota 1 req/s | Batch transcripts or add caching |
---
## 9 References
* Gong API community threads (pagination, transcript payloads, AI-content) ([Gong API pagination limit | Community](https://visioneers.gong.io/developers-79/gong-api-pagination-limit-1036?utm_source=chatgpt.com), [Retrieve call transcript through API using python - Gong Community](https://visioneers.gong.io/data-in-gong-71/retrieve-call-transcript-through-api-using-python-1158?utm_source=chatgpt.com), [Using the Retrieve AI content data for calls (/v2 ... - Gong Community](https://visioneers.gong.io/integrations-77/using-the-retrieve-ai-content-data-for-calls-v2-calls-ai-content-end-point-972?utm_source=chatgpt.com), [Transcript Search API - Gong Community](https://visioneers.gong.io/developers-79/transcript-search-api-937?utm_source=chatgpt.com))
* Official scope list & overview ([What the Gong API provides](https://help.gong.io/docs/what-the-gong-api-provides?utm_source=chatgpt.com))
* GitHub – `openapi-mcp-generator` README ([harsha-iiiv/openapi-mcp-generator: A tool that converts ... - GitHub](https://github.com/harsha-iiiv/openapi-mcp-generator?utm_source=chatgpt.com))
* Cursor MCP & stdio transport docs ([Model Context Protocol - Cursor](https://docs.cursor.com/context/model-context-protocol?utm_source=chatgpt.com))
* dotenv package page (env loading) ([Dotenv - NPM](https://www.npmjs.com/package/dotenv?utm_source=chatgpt.com))
* Fastify on Cloud Run guide ([Using Fastify on Google Cloud Run | Nearform](https://nearform.com/insights/using-fastify-on-google-cloud-run/?utm_source=chatgpt.com))
* Axios Basic-Auth example ([How to send Basic Auth with axios - Stack Overflow](https://stackoverflow.com/questions/44072750/how-to-send-basic-auth-with-axios?utm_source=chatgpt.com))
* Homebrew node-path fix (Apple Silicon) ([Error: node not installed after updated homebrew on mac M1](https://stackoverflow.com/questions/71530722/error-node-not-installed-after-updated-homebrew-on-mac-m1?utm_source=chatgpt.com))
---
**Ready for execution.**
The document now contains every artefact (spec, manifest sample, full flow) so it can live in the repo without cross-referencing earlier drafts.