GovConDash MCP / Developers

Developer documentation

How a custom client discovers, authenticates with and calls the GovConDash MCP server. Off-the-shelf clients need none of this; their steps are in the connection guides.

Included with Pro and Max. Every tool reads under your own account. See plans

Integration

The authentication contract

The endpoint

The server speaks MCP over Streamable HTTP, stateless: send JSON-RPC as a POST to the URL below with Accept: application/json, text/event-stream. A GET answers 405. Every request carries a bearer credential; there is no anonymous tool call.

https://govcondash.ai/api/mcp

Discovery

A request with no bearer, or an invalid one, answers 401 with an RFC 9728 challenge naming the protected-resource metadata document:

response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://govcondash.ai/.well-known/oauth-protected-resource/api/mcp"

That document (served at the path-suffixed location above and at the root /.well-known/oauth-protected-resource) names the authorization server. Its resource equals the server URL exactly as a reader enters it.

https://govcondash.ai/.well-known/oauth-protected-resource/api/mcp
{
  "resource": "https://govcondash.ai/api/mcp",
  "authorization_servers": [
    "https://govcondash.ai"
  ],
  "scopes_supported": [
    "read"
  ],
  "bearer_methods_supported": [
    "header"
  ],
  "resource_documentation": "https://govcondash.ai/mcp/developers"
}

The RFC 8414 authorization server metadata:

https://govcondash.ai/.well-known/oauth-authorization-server
{
  "issuer": "https://govcondash.ai",
  "authorization_endpoint": "https://govcondash.ai/mcp/authorize",
  "token_endpoint": "https://govcondash.ai/api/mcp/oauth/token",
  "registration_endpoint": "https://govcondash.ai/api/mcp/oauth/register",
  "revocation_endpoint": "https://govcondash.ai/api/mcp/oauth/revoke",
  "response_types_supported": [
    "code"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ],
  "token_endpoint_auth_methods_supported": [
    "none"
  ],
  "scopes_supported": [
    "read",
    "offline_access"
  ],
  "client_id_metadata_document_supported": true
}

Client registration

Clients are public: there is no client secret, and token_endpoint_auth_method is always none. Register one of two ways. With dynamic client registration (RFC 7591), POST your metadata and keep the returned client_id. Each redirect URI must be absolute https, or a loopback http://localhost or http://127.0.0.1 URI, where any port matches.

bash
curl -s -X POST https://govcondash.ai/api/mcp/oauth/register \
  -H "Content-Type: application/json" \
  -d '{"client_name":"My Agent","redirect_uris":["http://127.0.0.1/callback"]}'

Or use a client ID metadata document: pass an https URL as client_id. The server fetches it, requires its client_id field to equal that URL, and reads the redirect URIs and client name from it.

Authorization and consent

Send the reader's browser to the authorization endpoint with PKCE. Only S256 is accepted; the verifier is 43 to 128 characters.

GET
https://govcondash.ai/mcp/authorize?response_type=code
  &client_id=<client_id>
  &redirect_uri=http://127.0.0.1:53682/callback
  &code_challenge=<base64url(sha256(code_verifier))>
  &code_challenge_method=S256
  &scope=read%20offline_access
  &resource=https%3A%2F%2Fgovcondash.ai%2Fapi%2Fmcp
  &state=<random>

The consent page asks the reader to sign in to GovConDash, then shows your client's name and the full redirect host (with a warning when it is a loopback address), the plan the connection runs under, and Allow and Deny. A Pro or Max account that chooses Allow is redirected to your redirect_uri with code and state; the code is single use and lives ten minutes. A Free account is shown the upgrade instead of Allow and no code is issued; Deny returns error=access_denied.

Token endpoint

The token endpoint takes application/x-www-form-urlencoded only. Exchange the code with the verifier:

bash
curl -s -X POST https://govcondash.ai/api/mcp/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d code=<code> \
  -d code_verifier=<code_verifier> \
  -d client_id=<client_id> \
  -d redirect_uri=http://127.0.0.1:53682/callback
200 response
{
  "access_token": "<opaque>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque, rotates on every use>",
  "scope": "read offline_access"
}

Access tokens live one hour. Refresh tokens live 30 days and rotate on every use: the response carries a new refresh token and the old one is revoked. A refresh token presented twice answers invalid_grant and revokes the pair, so store the newest one. Errors are RFC 6749 bodies (invalid_request, invalid_grant, invalid_client, unsupported_grant_type). Revoke a token with RFC 7009 at https://govcondash.ai/api/mcp/oauth/revoke.

bash
curl -s -X POST https://govcondash.ai/api/mcp/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=refresh_token \
  -d refresh_token=<refresh_token> \
  -d client_id=<client_id>

Agent keys

A client that cannot run OAuth sends an agent key as a fixed header. A Pro or Max reader mints one on the Connected agents tab of the Account page; it starts with gcd_mcp_, is shown once, has no expiry and stops working the moment it is revoked. A dashboard session token is never accepted as an MCP bearer.

bash
curl -s -X POST https://govcondash.ai/api/mcp \
  -H "Authorization: Bearer gcd_mcp_example_key_not_real" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Result envelope

Every tool answers one JSON envelope, as text content and as structuredContent. A served call carries its sources, and every source has a URL: the public decision page, the official document, or the dashboard deep link. A corpus that is not ready answers { "available": false, "note": ... } inside data, never an error.

served
{
  "ok": true,
  "data": { "...": "the tool's rows" },
  "sources": [
    { "n": 1, "label": "FAR 31.205-6", "url": "https://www.ecfr.gov/...", "kind": "official" }
  ],
  "truncated": false
}

JSON-RPC has no 429, so a refusal is a tool result with isError: true and a structured body:

refused
{
  "ok": false,
  "code": "rate_limited",
  "message": "Too many tool calls this minute.",
  "retry_after_seconds": 12
}
codeMeaning
rate_limitedMore than 60 tool calls in a minute across the account's credentials. Wait retry_after_seconds.
daily_capThe plan's daily tool-call allowance is used up. It resets at 00:00 UTC.
upgradeThe account is not on Pro or Max (or the plan has lapsed). The body carries upgrade_url. tools/list still answers.
invalid_argsAn argument failed validation. allowed lists the accepted keys or values where that helps.
unavailableThe server is temporarily unable to serve the call. Retry later.

Limits

  • 60 tool calls a minute per account, across every credential it holds.
  • A daily tool-call allowance of 500 on Pro and 1,250 on Max, reset at 00:00 UTC.
  • One result is capped at 60,000 characters; a longer one is cut with truncated: true. Page long texts with offset and max_chars.
  • The plan is read on every call, so a lapsed plan answers the upgrade refusal at once.

search and fetch

For ChatGPT's connectors and deep research the server exposes the two tools that contract requires, under those exact names. search takes a query and answers { "results": [{ "id", "title", "url" }] } across every corpus at once; each id is typed corpus:ref, for example asbca:3849 or far:31.205-6. fetch takes that id and answers { "id", "title", "text", "url", "metadata" }. Every other tool is named govcondash_*; see the tool reference.

Your account's data

Most tools read public and derived data. A few read your own account, always for the signed-in user and never for an argument: govcondash_get_my_company (your company of interest), govcondash_list_my_partner_pipeline (the partners saved on the Teaming tab) and govcondash_list_my_agencies (the Agency Watchlist and the buyers your awards came from). Both lists are saved per company of interest and answer for the company active on your account. The notes you typed on a saved partner or buyer are left out unless the call passes include_notes: true, so they reach your agent only when you ask for them. Every tool is read-only: nothing can add to, change or delete a list.

Local developer server

The GovConDash repository also keeps a local, stdio MCP server (its mcp-server/ folder and README) for people developing the platform itself, reading a development database directly. It is not a subscriber product; subscribers connect to the hosted server above. Questions about an integration: support@govcondash.ai.