Skip to main content
Glama
prd_v3.md8.85 kB
Below is a **fully-revised, self-contained PRD (v 1.2)** that rolls in every fix we just discovered: * **Basic-Auth done once** via the OpenAPI security scheme (no hand-rolled header code). * **Correct request wrappers** (`filter`, `contentSelector`) for **both** POST endpoints. * **All five original endpoints** plus the new **Briefs API** (`/v2/askanything/generate-brief`). * A **single source-of-truth OpenAPI spec** you can paste into `spec/gong.yaml`. * A miniature **smoke-test harness** to run *before* regenerating the MCP. Copy this document into `docs/gong-mcp.md` and throw away older drafts. --- ## ✨ Summary * Scaffold with **`openapi-mcp-generator@3.x`** → TypeScript MCP server. * Auth = **HTTP Basic** (`Access Key : Secret`) pulled from `.env`. * Transport = **stdio** (Cursor default). * Local Node path = `/opt/homebrew/bin/node` (stable on ARM Macs). * Six endpoints: 1. `GET /v2/calls/{id}` 2. `POST /v2/calls/extensive` 3. `POST /v2/calls/transcript` 4. `GET /v2/users` 5. `GET /v2/data-privacy/data-for-email-address` 6. `GET /v2/askanything/generate-brief` ⬅ new --- ## 1 Problem & Goals Same as v 1.1, plus: “Generate a brief for a deal or account” via the new endpoint. --- ## 2 Architecture (unchanged) ``` Cursor LLM ⇄ Gong-MCP (stdio) ⇄ https://api.gong.io ``` --- ## 3 Authentication (unchanged) `.env` ```env GONG_ACCESS_KEY=... GONG_SECRET=... ``` --- ## 4 OpenAPI spec `spec/gong.yaml` Paste **verbatim**. ```yaml openapi: 3.1.0 info: { title: Gong API (subset), version: 1.2.0 } servers: [ { url: https://api.gong.io } ] security: [ { basicAuth: [] } ] components: securitySchemes: basicAuth: { type: http, scheme: basic } # ---------- shared request pieces ---------- schemas: CallsFilter: type: object properties: fromDateTime: { type: string, format: date-time } toDateTime: { type: string, format: date-time } callIds: { type: array, items: { type: string } } primaryUserIds: { type: array, items: { type: string } } participantsEmails: type: array items: { type: string, format: email } ContentSelector: type: object properties: context: { type: string, enum: [None, Extended] } contextTiming: { type: array, items: { type: string, enum: [Now, TimeOfCall] } } exposedFields: { type: object, additionalProperties: { type: object } } CallsExtensiveRequest: type: object required: [ filter ] properties: filter: { $ref: '#/components/schemas/CallsFilter' } contentSelector: { $ref: '#/components/schemas/ContentSelector' } cursor: { type: string } TranscriptRequest: type: object required: [ filter ] properties: filter: { $ref: '#/components/schemas/CallsFilter' } cursor: { type: string } # ---------- brief endpoint ---------- BriefRequestParams: type: object required: - workspace-id - brief-name - entity-type - crm-entity-id - period-type properties: workspace-id: { type: string } brief-name: { type: string } entity-type: { type: string, enum: [Deal, Account] } crm-entity-id: { type: string } period-type: type: string enum: [LAST_7DAYS, LAST_30DAYS, LAST_90DAYS, LAST_90_DAYS_SINCE_LAST_ACTIVITY, LAST_YEAR_SINCE_LAST_ACTIVITY, LAST_YEAR, THIS_WEEK, THIS_MONTH, THIS_YEAR, THIS_QUARTER, CUSTOM_RANGE] from-date-time: { type: string, format: date-time } to-date-time: { type: string, format: date-time } # ---------- trimmed responses ---------- SpecificCall: { type: object, additionalProperties: true } Calls: { type: object, additionalProperties: true } CallTranscripts: { type: object, additionalProperties: true } Users: { type: object, additionalProperties: true } EmailAddressReferences:{ type: object, additionalProperties: true } BriefResponse: { type: object, additionalProperties: true } paths: /v2/calls/{id}: get: summary: Retrieve a single call parameters: - { name: id, in: path, required: true, schema: { type: string } } responses: '200': { content: { application/json: { schema: { $ref: '#/components/schemas/SpecificCall' } } } } /v2/calls/extensive: post: summary: Filtered call list requestBody: required: true content: application/json: { schema: { $ref: '#/components/schemas/CallsExtensiveRequest' } } responses: '200': { 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': { content: { application/json: { schema: { $ref: '#/components/schemas/CallTranscripts' } } } } /v2/users: get: summary: List Gong users parameters: [ { name: cursor, in: query, schema: { type: string } } ] responses: '200': { content: { application/json: { schema: { $ref: '#/components/schemas/Users' } } } } /v2/data-privacy/data-for-email-address: get: summary: Activities for an email parameters: - { name: emailAddress, in: query, required: true, schema: { type: string, format: email } } - { name: cursor, in: query, schema: { type: string } } responses: '200': { content: { application/json: { schema: { $ref: '#/components/schemas/EmailAddressReferences' } } } } /v2/askanything/generate-brief: get: summary: Generate account/deal brief parameters: - { name: workspace-id, in: query, required: true, schema: { type: string } } - { name: brief-name, in: query, required: true, schema: { type: string } } - { name: entity-type, in: query, required: true, schema: { type: string, enum: [Deal, Account] } } - { name: crm-entity-id, in: query, required: true, schema: { type: string } } - { name: period-type, in: query, required: true, schema: { type: string } } - { name: from-date-time, in: query, schema: { type: string, format: date-time } } - { name: to-date-time, in: query, schema: { type: string, format: date-time } } responses: '200': { content: { application/json: { schema: { $ref: '#/components/schemas/BriefResponse' } } } } ``` --- ## 5 Implementation Steps (delta) *Delete* the custom header logic. Replace with: ```ts const client = axios.create({ baseURL: 'https://api.gong.io', auth: { username: process.env.GONG_ACCESS_KEY!, password: process.env.GONG_SECRET! } }); ``` Generator will mirror that for every call. --- ## 6 Pre-MCP Smoke Tests (`tests/gongSmoke.test.ts`) ```ts import axios from 'axios'; import dotenv from 'dotenv'; dotenv.config(); const api = axios.create({ baseURL: 'https://api.gong.io', auth: { username: process.env.GONG_ACCESS_KEY!, password: process.env.GONG_SECRET! } }); test('auth works', async () => { const { status } = await api.get('/v2/users?limit=1'); expect(status).toBe(200); }); test('extensive wrapper', async () => { const { status } = await api.post('/v2/calls/extensive', { filter: { fromDateTime: '2025-04-30T00:00:00Z', toDateTime: '2025-04-30T23:59:59Z' } }); expect(status).toBe(200); }); test('brief endpoint', async () => { const { status } = await api.get('/v2/askanything/generate-brief', { params: { 'workspace-id': '123', 'brief-name': 'Churn risk signals', 'entity-type': 'Deal', 'crm-entity-id': '006Pc0000093OGjIAA', 'period-type': 'LAST_90DAYS' } }); // Gong returns 202 if async; accept either expect([200, 202]).toContain(status); }); ``` Run `npm t` before every MCP build. --- ## 7 Testing Matrix (add brief) | Scenario | Expected | |----------|----------| | brief request | 200/202 and JSON with `summaryBullets` array | --- ## 8 Risks & Mitigations (unchanged + brief rate-limit) * Gong brief endpoint is compute-heavy → 429 if > 5 req/min → add retry logic later. --- **Done.** This PRD now contains: * Correct wrapper schemas * Built-in Basic-Auth * All six endpoints (calls, transcripts, users, GDPR, brief) * Smoke-test harness to catch 401/400 *before* MCP generation. Regenerate (`openapi-mcp-generator@3 …`), rebuild, run the tests—then reload your MCP in Cursor.

Latest Blog Posts

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/MaPa07/gong-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server