From 3cc50c40b0cad13fa2c590c771eb6a4e631bacfd Mon Sep 17 00:00:00 2001 From: Kenny Derek Date: Tue, 4 Nov 2025 16:14:38 -0500 Subject: [PATCH 1/2] docs --- fern/apis/mcp-tools/generators.yml | 3 + fern/apis/mcp-tools/openapi.json | 209 +++++++++++++++++++++ fern/products/docs/docs.yml | 9 +- fern/products/docs/pages/ai/mcp-server.mdx | 15 +- 4 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 fern/apis/mcp-tools/generators.yml create mode 100644 fern/apis/mcp-tools/openapi.json diff --git a/fern/apis/mcp-tools/generators.yml b/fern/apis/mcp-tools/generators.yml new file mode 100644 index 000000000..d207022e6 --- /dev/null +++ b/fern/apis/mcp-tools/generators.yml @@ -0,0 +1,3 @@ +api: + specs: + - openapi: openapi.json \ No newline at end of file diff --git a/fern/apis/mcp-tools/openapi.json b/fern/apis/mcp-tools/openapi.json new file mode 100644 index 000000000..0c35c0858 --- /dev/null +++ b/fern/apis/mcp-tools/openapi.json @@ -0,0 +1,209 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Fern Docs MCP API", + "version": "1.0.0", + "description": "API endpoints for MCP agents to authenticate and search Fern documentation sites." + }, + "servers": [ + { + "url": "https://docs.example.com", + "description": "Your documentation domain" + } + ], + "components": { + "securitySchemes": { + "FernApiKey": { + "type": "apiKey", + "in": "header", + "name": "FERN_API_KEY", + "description": "Fern API key, from `fern generate token`." + }, + "FernToken": { + "type": "apiKey", + "in": "header", + "name": "FERN_TOKEN", + "description": "JWT token for this user." + } + } + }, + "paths": { + "/api/fern-docs/get-jwt": { + "get": { + "summary": "JWT from Fern API key", + "description": "Get a JWT to access protected documentation endpoints, optionally scoped to specific roles.", + "operationId": "getJwt", + "security": [ + { + "FernApiKey": [] + } + ], + "parameters": [ + { + "name": "ROLES", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Comma-separated list of roles (e.g., \"botanist,seedling\"). Sets roles for returned JWT if provided, otherwise, roles are empty." + }, + { + "name": "x-fern-host", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Domain (required on preview URLs)" + } + ], + "responses": { + "200": { + "description": "Successfully generated JWT", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "fern_token": { + "type": "string", + "description": "JWT token for authenticating subsequent requests" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Roles included in the JWT" + } + }, + "required": ["fern_token", "roles"] + } + } + } + }, + "400": { + "description": "Bad request (local preview, self-hosted, or SSO environment)", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized (missing or invalid API key)", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } + } + } + }, + "403": { + "description": "Forbidden (API key does not belong to this domain's Fern organization)", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/api/fern-docs/search/v2/key": { + "get": { + "summary": "Algolia search credentials", + "description": "Get Algolia search credentials for querying documentation.", + "operationId": "getSearchKey", + "security": [ + { + "FernApiKey": [] + }, + { + "FernToken": [] + } + ], + "parameters": [ + { + "name": "ROLES", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Comma-separated list of roles (only with FERN_API_KEY). Sets roles for returned search key if provided, otherwise, roles are empty." + }, + { + "name": "x-fern-host", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "description": "Documentation domain (required on preview URLs)" + } + ], + "responses": { + "200": { + "description": "Successfully retrieved search credentials", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "appId": { + "type": "string", + "description": "Algolia application ID" + }, + "apiKey": { + "type": "string", + "description": "Short-lived Algolia search API key" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Roles included in the search key (only with FERN_API_KEY auth)" + } + }, + "required": ["appId", "apiKey"] + } + } + } + }, + "400": { + "description": "Bad request (local preview or unsupported preview URL)" + }, + "401": { + "description": "Unauthorized (invalid Fern API key)" + }, + "403": { + "description": "Forbidden (API key does not belong to this domain's Fern organization)" + } + } + } + } + } + } \ No newline at end of file diff --git a/fern/products/docs/docs.yml b/fern/products/docs/docs.yml index c46be377c..596ee952d 100644 --- a/fern/products/docs/docs.yml +++ b/fern/products/docs/docs.yml @@ -128,8 +128,13 @@ navigation: contents: - link: Ask Fern href: /learn/ask-fern/getting-started/what-is-ask-fern - - page: MCP server for your site - path: ./pages/ai/mcp-server.mdx + - section: MCP server for your site + contents: + - page: Overview + path: ./pages/ai/mcp-server.mdx + - api: API Reference + paginated: false + api-name: mcp-tools slug: mcp-server - page: Fern Scribe (coming soon) path: ./pages/ai/scribe.mdx diff --git a/fern/products/docs/pages/ai/mcp-server.mdx b/fern/products/docs/pages/ai/mcp-server.mdx index 9fd3c1200..23f424801 100644 --- a/fern/products/docs/pages/ai/mcp-server.mdx +++ b/fern/products/docs/pages/ai/mcp-server.mdx @@ -17,4 +17,17 @@ Developers can access your MCP server by visiting `your-documentation-site.com/m **Looking for Fern's own MCP server?** If you want to connect your AI client to Fern's MCP server for help with Ask Fern, Docs, and SDKs, see the [fern-mcp-server repository](https://github.com/fern-api/fern-mcp-server). - \ No newline at end of file + + +## Additional AI Tooling + +LLMs can access your documentation programmatically, even for authenticated sites. When content is fetched this way, it's served as clean markdown without HTML markup, making it token efficient for LLMs to process. + +```bash +curl -X GET https://docs.example.com/platform/overview \ + -H 'Accept: text/plain' \ + -H 'x-fern-host: docs.example.com' \ + -H "FERN_TOKEN:${JWT}" +``` + +For sites with authentication, LLMs can obtain a JWT by calling the [`/api/fern-docs/get-jwt`](/learn/docs/ai-features/mcp-server/api-reference/get-jwt) endpoint with a Fern API key. This JWT can then be used to access protected documentation content and search functionality. \ No newline at end of file From f84fbd626b0d8805b4d7acfba700b54e74fb666b Mon Sep 17 00:00:00 2001 From: Devin Logan Date: Wed, 5 Nov 2025 12:00:27 -0500 Subject: [PATCH 2/2] clean up tooling update, add changelog entry --- fern/products/docs/pages/ai/mcp-server.mdx | 16 ++++++++-------- .../products/docs/pages/changelog/2025-11-05.mdx | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 fern/products/docs/pages/changelog/2025-11-05.mdx diff --git a/fern/products/docs/pages/ai/mcp-server.mdx b/fern/products/docs/pages/ai/mcp-server.mdx index 23f424801..672e070c1 100644 --- a/fern/products/docs/pages/ai/mcp-server.mdx +++ b/fern/products/docs/pages/ai/mcp-server.mdx @@ -19,15 +19,15 @@ Developers can access your MCP server by visiting `your-documentation-site.com/m **Looking for Fern's own MCP server?** If you want to connect your AI client to Fern's MCP server for help with Ask Fern, Docs, and SDKs, see the [fern-mcp-server repository](https://github.com/fern-api/fern-mcp-server). -## Additional AI Tooling +## Direct API access for AI agents -LLMs can access your documentation programmatically, even for authenticated sites. When content is fetched this way, it's served as clean markdown without HTML markup, making it token efficient for LLMs to process. +AI agents can access your documentation, including authenticated documentation, directly via HTTP. Fern serves content as clean Markdown for token efficiency. -```bash -curl -X GET https://docs.example.com/platform/overview \ +For authenticated sites, agents obtain a JWT via [`/api/fern-docs/get-jwt`](/learn/docs/ai-features/mcp-server/api-reference/get-jwt) with a Fern API key to access the protected documentation content and search functionality. + +```bash Access protected content +curl https://docs.example.com/platform/overview \ -H 'Accept: text/plain' \ -H 'x-fern-host: docs.example.com' \ - -H "FERN_TOKEN:${JWT}" -``` - -For sites with authentication, LLMs can obtain a JWT by calling the [`/api/fern-docs/get-jwt`](/learn/docs/ai-features/mcp-server/api-reference/get-jwt) endpoint with a Fern API key. This JWT can then be used to access protected documentation content and search functionality. \ No newline at end of file + -H 'FERN_TOKEN: your-jwt-here' +``` diff --git a/fern/products/docs/pages/changelog/2025-11-05.mdx b/fern/products/docs/pages/changelog/2025-11-05.mdx new file mode 100644 index 000000000..d4f1dc037 --- /dev/null +++ b/fern/products/docs/pages/changelog/2025-11-05.mdx @@ -0,0 +1,16 @@ +## HTTP API access for AI agents + +AI agents can now access authenticated documentation directly via HTTP API. This enables custom integrations and AI tools to retrieve protected content programmatically. + +Agents obtain a JWT via the `/api/fern-docs/get-jwt` endpoint with a Fern API key, then use it to access protected documentation: + +```bash +curl https://docs.example.com/platform/overview \ + -H 'Accept: text/plain' \ + -H 'x-fern-host: docs.example.com' \ + -H 'FERN_TOKEN: your-jwt-here' +``` + +Content is served as clean Markdown for token-efficient processing. + +Learn more in the [MCP server documentation](/learn/docs/ai-features/mcp-server).