Skip to content

Add Search v2 endpoint documentation#180

Open
dodeja wants to merge 2 commits into
mainfrom
docs/search-v2-endpoint
Open

Add Search v2 endpoint documentation#180
dodeja wants to merge 2 commits into
mainfrom
docs/search-v2-endpoint

Conversation

@dodeja
Copy link
Copy Markdown
Contributor

@dodeja dodeja commented Mar 3, 2026

Documents the GET /search endpoint in the API reference docs.

Changes

  • openapi.json — Added /search GET endpoint with full schema, parameters, response examples, error codes
  • search.mdx — New MDX page for the Search endpoint
  • docs.json — Added Search navigation group

Details

  • Query parameter: query (required, string)
  • Max 25 results (no pagination)
  • Searches: Shipments, Containers, Tracking Requests
  • Full-text search via PostgreSQL websearch_to_tsquery + ILIKE fallback

Linear: DEV-8837

Created by Trin 🥷

Greptile Summary

This PR documents the GET /search endpoint in the API reference, adding its OpenAPI spec entry, a Mintlify MDX page, and navigation support. It also ships a substantial OAuth 2.1 authentication layer for the hosted MCP endpoint: a new workos-jwt.ts module for local RS256 JWT verification (with JWKS caching), a remote fallback via an internal principal endpoint, RFC-compliant WWW-Authenticate challenge headers on all 401 responses, and updated MCP documentation covering the new dual-mode auth flow.

Key observations:

  • The new /search OpenAPI path is well-structured and consistent with existing JSON:API response shapes, but the "Search" tag is absent from the x-tagGroups array, which will leave the endpoint ungrouped in tools like Redoc or Stoplight.
  • fetchJwks in packages/mcp/src/auth/workos-jwt.ts lacks a request timeout, unlike verifyViaInternalPrincipal which enforces a 2-second abort. A slow or unreachable JWKS endpoint could stall the serverless function.
  • The 401 response on /search is documented with a description only; adding a minimal body schema would keep it consistent with the 400 entry.
  • The sdks/typescript-sdk/src/client.ts change correctly handles Bearer-prefixed tokens passed as apiToken, which is needed for OAuth tokens forwarded from the MCP layer.

The documentation additions are accurate and the MCP OAuth implementation is logically sound with good test coverage and proper fallback handling. Two small gaps should be addressed: the missing request timeout on fetchJwks is a real reliability risk in a serverless environment, and the "Search" tag omission from x-tagGroups is a documentation completeness issue.

Confidence Score: 4/5

  • Safe to merge with minor follow-up: address the missing JWKS fetch timeout and add the Search tag to x-tagGroups before significant production OAuth traffic.
  • The documentation additions are accurate and well-structured. The MCP OAuth implementation is logically sound with good test coverage and proper fallback handling. Two small gaps prevent a perfect score: the missing request timeout on fetchJwks is a real reliability risk in a serverless environment, and the "Search" tag omission from x-tagGroups is a documentation completeness issue that affects endpoint discoverability in OpenAPI tools.
  • packages/mcp/src/auth/workos-jwt.ts (missing fetch timeout in fetchJwks) and docs/openapi.json (Search tag absent from x-tagGroups, 401 response missing body schema)

Sequence Diagram

sequenceDiagram
    participant Client
    participant MCP as api/mcp.ts
    participant WorkOS as workos-jwt.ts
    participant Internal as Internal Principal API
    participant T49API as Terminal49 API

    Client->>MCP: POST /mcp with Authorization Bearer token
    MCP->>MCP: extractAuthorizationToken → scheme detection

    alt No token present
        MCP-->>Client: 401 with WWW-Authenticate challenge
    else Token has three dot-segments (JWT-like bearer)
        MCP->>WorkOS: verifyWorkosJwt(token)
        WorkOS->>WorkOS: fetchJwks cached 10 minutes
        WorkOS->>WorkOS: verifySignature + validClaims
        alt Local verification succeeds
            WorkOS-->>MCP: VerifiedPayload authSource oauth_local
            MCP->>T49API: Forward with Bearer token
            T49API-->>MCP: API response
            MCP-->>Client: 200 MCP response
        else Local verification returns null
            WorkOS-->>MCP: null
            MCP->>Internal: POST token_principal with 2s timeout
            alt Remote verification active
                Internal-->>MCP: active true with user and account ids
                MCP->>T49API: Forward with Bearer token
                T49API-->>MCP: API response
                MCP-->>Client: 200 MCP response
            else Remote verification fails
                Internal-->>MCP: error or inactive
                MCP-->>Client: 401 with WWW-Authenticate challenge
            end
        end
    else Legacy token scheme or no dots
        MCP->>MCP: isMatchingClientSecret check
        alt Secret matches
            MCP->>T49API: Forward with configured API token
            T49API-->>MCP: API response
            MCP-->>Client: 200 MCP response
        else Mismatch
            MCP-->>Client: 401 with WWW-Authenticate challenge
        end
    end
Loading

Last reviewed commit: 66f98f3

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview, Comment Mar 3, 2026 7:08pm

Request Review

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

17 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Comment thread docs/openapi.json
"example": {
"data": [
{
"id": "abc12345-6789-0def-ghij-klmnopqrstuv",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example id values contain non-hexadecimal characters (letters g through s). Valid UUIDs use only hex digits [0-9a-f], but these examples use invalid characters. This may confuse API consumers reading the documentation.

Replace with valid UUID examples using only hexadecimal characters [0-9a-f]. Real Terminal49 resource IDs follow the standard 8-4-4-4-12 hex format.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/openapi.json
Line: 7165

Comment:
The example `id` values contain non-hexadecimal characters (letters g through s). Valid UUIDs use only hex digits [0-9a-f], but these examples use invalid characters. This may confuse API consumers reading the documentation. 

Replace with valid UUID examples using only hexadecimal characters [0-9a-f]. Real Terminal49 resource IDs follow the standard 8-4-4-4-12 hex format.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 3, 2026

Additional Comments (2)

docs/docs.json
The MCP navigation references "mcp/project-overview", but no corresponding docs/mcp/project-overview.mdx file exists. This will cause a broken navigation link or build failure in Mintlify.

Either create the missing docs/mcp/project-overview.mdx file or remove this entry from the pages array.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/docs.json
Line: 204

Comment:
The MCP navigation references `"mcp/project-overview"`, but no corresponding `docs/mcp/project-overview.mdx` file exists. This will cause a broken navigation link or build failure in Mintlify.

Either create the missing `docs/mcp/project-overview.mdx` file or remove this entry from the pages array.

How can I resolve this? If you propose a fix, please make it concise.

sdks/typescript-sdk/src/client.ts
The search method accepts an empty query string and forwards it to the API, which returns a 400 Bad Request. Unlike other methods in the class (e.g., inferTrackingNumber on line 290-293), this lacks early validation and causes an unnecessary network round-trip.

Add a guard at the start of the method:

if (!query || query.trim() === '') {
  throw new ValidationError('query is required (/query)');
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdks/typescript-sdk/src/client.ts
Line: 205-208

Comment:
The `search` method accepts an empty query string and forwards it to the API, which returns a `400 Bad Request`. Unlike other methods in the class (e.g., `inferTrackingNumber` on line 290-293), this lacks early validation and causes an unnecessary network round-trip.

Add a guard at the start of the method:
```typescript
if (!query || query.trim() === '') {
  throw new ValidationError('query is required (/query)');
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +125 to +128
const response = await fetch(jwksUrl, {
method: 'GET',
headers: { Accept: 'application/json' },
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchJwks has no request timeout

The fetch call here has no AbortController or timeout, while the sibling verifyViaInternalPrincipal consistently uses a 2-second abort timeout. In a serverless/Vercel environment, if the WorkOS JWKS endpoint is slow or temporarily unreachable, this call will block the function until the platform-level timeout (potentially tens of seconds), rejecting every OAuth token during that window.

Consider adding the same pattern used in verifyViaInternalPrincipal:

Suggested change
const response = await fetch(jwksUrl, {
method: 'GET',
headers: { Accept: 'application/json' },
});
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), 3000);
let response: Response;
try {
response = await fetch(jwksUrl, {
method: 'GET',
headers: { Accept: 'application/json' },
signal: abortController.signal,
});
} catch {
return [];
} finally {
clearTimeout(timeout);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/mcp/src/auth/workos-jwt.ts
Line: 125-128

Comment:
`fetchJwks` has no request timeout

The `fetch` call here has no `AbortController` or timeout, while the sibling `verifyViaInternalPrincipal` consistently uses a 2-second abort timeout. In a serverless/Vercel environment, if the WorkOS JWKS endpoint is slow or temporarily unreachable, this call will block the function until the platform-level timeout (potentially tens of seconds), rejecting every OAuth token during that window.

Consider adding the same pattern used in `verifyViaInternalPrincipal`:

```suggestion
  const abortController = new AbortController();
  const timeout = setTimeout(() => abortController.abort(), 3000);

  let response: Response;
  try {
    response = await fetch(jwksUrl, {
      method: 'GET',
      headers: { Accept: 'application/json' },
      signal: abortController.signal,
    });
  } catch {
    return [];
  } finally {
    clearTimeout(timeout);
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread docs/openapi.json
Comment on lines +8517 to +8519
"401": {
"description": "Unauthorized \u2014 missing or invalid API token"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

401 response missing body schema

The 400 response is fully documented with a JSON:API error schema and example, but the 401 response only carries a description string with no content entry. API consumers generating client code or running type-safe validation will see an untyped 401. For consistency with the rest of the spec, consider adding a minimal schema:

"401": {
  "description": "Unauthorized — missing or invalid API token",
  "content": {
    "application/vnd.api+json": {
      "schema": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": { "type": "object", "properties": { "detail": { "type": "string" } } }
          }
        }
      }
    }
  }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/openapi.json
Line: 8517-8519

Comment:
`401` response missing body schema

The `400` response is fully documented with a JSON:API error schema and example, but the `401` response only carries a description string with no `content` entry. API consumers generating client code or running type-safe validation will see an untyped `401`. For consistency with the rest of the spec, consider adding a minimal schema:

```json
"401": {
  "description": "Unauthorized — missing or invalid API token",
  "content": {
    "application/vnd.api+json": {
      "schema": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": { "type": "object", "properties": { "detail": { "type": "string" } } }
          }
        }
      }
    }
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 3, 2026

Additional Comments (1)

docs/openapi.json
"Search" tag not added to x-tagGroups

The new /search endpoint uses the tag "Search", but the x-tagGroups array only lists the tags "Shipments", "Containers", "Custom Field Definitions", "Custom Field Options", "Custom Fields", "Tracking Requests", "Webhooks", "Webhook Notifications", and "Metro Areas". Tools that respect x-tagGroups (e.g., Redoc, Stoplight) will not surface the Search endpoint under any named group — it would appear ungrouped or be hidden entirely.

Add "Search" to the "End Points" group:

  "x-tagGroups": [
    {
      "name": "End Points",
      "tags": [
        "Shipments",
        "Containers",
        "Custom Field Definitions",
        "Custom Field Options",
        "Custom Fields",
        "Tracking Requests",
        "Search",
        "Webhooks",
        "Webhook Notifications",
        "Metro Areas"
      ]
    },
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/openapi.json
Line: 8524-8537

Comment:
`"Search"` tag not added to `x-tagGroups`

The new `/search` endpoint uses the tag `"Search"`, but the `x-tagGroups` array only lists the tags `"Shipments"`, `"Containers"`, `"Custom Field Definitions"`, `"Custom Field Options"`, `"Custom Fields"`, `"Tracking Requests"`, `"Webhooks"`, `"Webhook Notifications"`, and `"Metro Areas"`. Tools that respect `x-tagGroups` (e.g., Redoc, Stoplight) will not surface the `Search` endpoint under any named group — it would appear ungrouped or be hidden entirely.

Add `"Search"` to the `"End Points"` group:

```suggestion
  "x-tagGroups": [
    {
      "name": "End Points",
      "tags": [
        "Shipments",
        "Containers",
        "Custom Field Definitions",
        "Custom Field Options",
        "Custom Fields",
        "Tracking Requests",
        "Search",
        "Webhooks",
        "Webhook Notifications",
        "Metro Areas"
      ]
    },
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant