Changelog
Stay up to date with all the latest features, improvements, and bug fixes.
Every Flux API prefix now exposes a built-in Model Context Protocol server alongside its REST endpoints, and two new governance toggles let you control which APIs are discoverable by AI agents. Together they make Flux APIs directly usable by Claude, ChatGPT, the OpenAI Responses API, Cursor, and any MCP-compatible client — without writing a custom tool server or maintaining a second access-control surface.
Flux API
- New endpoint:
GET | POST | DELETE /{api_prefix}/_mcp— per-API MCP server over Streamable HTTP (2025-03-26). Publishes a fixed five-tool catalog (discover,get_schema,get_one,list,search) scoped to the connected folders of the prefix. Thesearchtool exposes hybrid retrieval (vector + full-text + structured filters) as a single call. See MCP Server reference. - Same auth and permissions as the REST surface:
is_auth_requiredcontrols anonymous access, per-folderallowed_methodscontrols which tools are available per folder, Flux API key roles apply identically.
Management API
- New fields on the API object (returned by
GET, accepted byPOSTandPATCH):mcp_enabled(boolean, defaulttrue) — whenfalse, the/{api_prefix}/_mcpendpoint returns404router_introspection_enabled(boolean, defaulttrue) — whenfalse, the/{api_prefix}/_routerendpoint returns404
- Both fields are independent policy switches. The data routes (
/{folder},/{folder}/{key},/{folder}/_search) are never affected. - Both fields use strict JSON-boolean validation — strings like
"true", integers, andnullare rejected with422. PATCH /v1/{env}/api/{api}/supports single-field updates:{"mcp_enabled": false}works without re-sending the entire API object.- Both fields are captured in the API delete-event audit snapshot.
Documentation
- New concept page: Agent-Native Flux APIs — what "agent-native" means here, the three introspection endpoints, the differentiators vs. hand-written MCP servers, and a per-prefix governance model.
- New guides: Connect Claude and Connect ChatGPT and OpenAI.
- LLM Integrations now lists "MCP Server Connection" as a fourth integration pattern alongside Direct API, LangChain Retriever, and Agent Tool.
Rollout note
Router items written before this release lack the agent_features block in DynamoDB; the Flux API treats a missing block as defaults-on. Items refresh automatically on the next router-build trigger (any API or folder edit). No data migration is required.
The Flux _search endpoint now returns a metadata object in every response, showing the effective parameters that were used — including server-side defaults.
"metadata": {
"search_mode": "vector",
"limit": 100,
"vector_search_enabled": true,
"top_k": 10,
"similarity_threshold": 0.4
}
The metadata shape varies by search mode. See the Search API reference for the full list of fields per mode.
Fixed a bug where vector search modes did not pass limit to the Flux API, causing the top_k parameter to have no effect on the number of returned documents.
The package now conforms to LangChain integration standards and passes the official RetrieversIntegrationTests suite.
pip install langchain-foxnose==0.3.1
Both official SDKs now have typed models and convenience methods for all Flux vector search modes: vector_search, vector_field_search, hybrid_search, and boosted_search.
Python SDK 0.5.0
- New
flux.modelsmodule:SearchMode,VectorSearch,VectorFieldSearch,VectorBoostConfig,HybridConfig,SearchRequestwith cross-field validation - Convenience methods on
FluxClient/AsyncFluxClientwith**extra_bodypass-through forwhere,sort, etc.
results = client.hybrid_search(
"articles",
query="ML applications",
find_text={"query": "machine learning"},
vector_weight=0.7,
text_weight=0.3,
)
TypeScript SDK 0.3.0
- New types:
SearchMode,VectorSearch,VectorFieldSearch,VectorBoostConfig,HybridConfig,SearchRequestwithbuildSearchBody()runtime validation - Convenience methods on
FluxClientwith extra parameter forwarding via rest spread
const results = await client.hybridSearch('articles', {
query: 'ML applications',
findText: { query: 'machine learning' },
vectorWeight: 0.7,
textWeight: 0.3,
});
FoxNose now supports a full workflow for custom embeddings: you can define vector fields in Management API, store model-generated vectors directly in resources, and search them in Flux with vector_field_search.
This unlocks semantic experiences beyond classic text search, including voice/speaker matching, image similarity, recommendation and ranking scenarios, while keeping search behavior explicit and predictable in vector and vector_boosted modes.
For custom vector search, the distance metric is cosine only.
Management API
- New field type:
vector - Supported dimensions:
256,384,768,1024,1536
Flux API
- New search payload:
vector_field_search - Supported parameters:
field,query_vector,top_k,similarity_threshold vector_field_searchis supported withsearch_mode="vector"andsearch_mode="vector_boosted"vector_field_searchis not supported withsearch_mode="text"orsearch_mode="hybrid"vector_field_searchandvector_searchare mutually exclusive- Distance metric for vector field search: cosine only
Support for Flux API introspection and route-level descriptions on API folder connections.
New: Flux introspection methods
FluxClient.getRouter()— callsGET /{api_prefix}/_routerFluxClient.getSchema(folderPath)— callsGET /{api_prefix}/{folder_path}/_schema
const router = await fluxClient.getRouter();
const schema = await fluxClient.getSchema('content/articles');
Updated: API folder connection methods
ManagementClient.addApiFolder()andupdateApiFolder()now accept optional route descriptions:descriptionGetOnedescriptionGetManydescriptionSearchdescriptionSchema
const connection = await managementClient.addApiFolder(apiKey, folderKey, {
allowedMethods: ['get_many', 'get_one'],
descriptionGetOne: 'Get one article by key',
descriptionGetMany: 'List published articles',
descriptionSearch: 'Search published articles',
descriptionSchema: 'Read article schema',
});
Updated model
APIFolderSummarynow includes alldescription_*fields from Management API responses
Support for Flux API introspection and route-level descriptions on API folder connections.
New: Flux introspection methods
FluxClient.get_router()/AsyncFluxClient.get_router()— callsGET /{api_prefix}/_routerFluxClient.get_schema(folder_path)/AsyncFluxClient.get_schema(folder_path)— callsGET /{api_prefix}/{folder_path}/_schema
router = flux_client.get_router()
schema = flux_client.get_schema("content/articles")
Updated: API folder connection methods
ManagementClient.add_api_folder()andupdate_api_folder()now accept optional route descriptions:description_get_onedescription_get_manydescription_searchdescription_schema
- Same signature updates are available on
AsyncManagementClient
connection = management_client.add_api_folder(
api_key,
folder_key,
allowed_methods=["get_many", "get_one"],
description_get_one="Get one article by key",
description_get_many="List published articles",
description_search="Search published articles",
description_schema="Read article schema",
)
Updated model
APIFolderSummarynow includes alldescription_*fields from Management API responses
New introspection capabilities for Flux APIs to improve runtime discovery for users, MCP servers, and LLM agents.
Flux API
- New endpoint:
GET /{api_prefix}/_router— returns a flat route catalog with method, path, action, scope, path params, query params, request body contract, and response shape - New endpoint:
GET /{api_prefix}/{folder_path}/_schema— returns live JSON Schema and route metadata for the target folder path /_schemanow includes searchable metadata:searchable_fields— fields available for filtering/searchnon_searchable_fields— fields present in schema but not indexed for search
Management API
- Folder-to-API connection supports per-action route descriptions:
description_get_manydescription_get_onedescription_searchdescription_schema
- These descriptions are used by Flux
/_routerfor each action route
Initial release of the official LangChain.js integration for FoxNose.
Features
- FoxNoseRetriever — LangChain
BaseRetrieverbacked by FoxNose Flux search with text, vector, hybrid, and vector-boosted modes - FoxNoseLoader —
BaseDocumentLoaderwith cursor-based pagination andloadLazy()async generator for memory-efficient bulk loading - createFoxNoseTool — factory that wraps retrieval as a
DynamicStructuredToolfor LLM agents - Flexible content mapping: single field, multiple fields, or custom mapper
- Metadata control: whitelist, blacklist, system metadata toggle
- Structured filtering via
whereparameter - Dual output: ESM and CommonJS with full TypeScript declarations
Installation
npm install @foxnose/langchain @foxnose/sdk @langchain/core
Quick Start
import { FluxClient, SimpleKeyAuth } from '@foxnose/sdk';
import { FoxNoseRetriever } from '@foxnose/langchain';
const client = new FluxClient({
baseUrl: 'https://your-env.fxns.io',
apiPrefix: 'content',
auth: new SimpleKeyAuth('YOUR_PUBLIC_KEY', 'YOUR_SECRET_KEY'),
});
const retriever = new FoxNoseRetriever({
client,
folderPath: 'knowledge-base',
pageContentField: 'body',
searchMode: 'hybrid',
});
const docs = await retriever.invoke('how to build AI applications');
Links
Initial release of the official FoxNose TypeScript SDK.
Features
- ManagementClient — full coverage of Management API v1: folders, components, resources, revisions, roles, API keys, environments, projects, and more
- FluxClient — content delivery: list resources, get resource, search with text/vector/hybrid modes
- Four auth strategies — Anonymous, JWT, Simple key, and Secure (ECDSA P-256)
- Automatic retries — exponential backoff with jitter and Retry-After support
- Batch upsert — concurrent resource upserts with configurable
maxConcurrencyand progress callback - Zero dependencies — built on native
fetch(Node 18+) - Dual output — ESM and CommonJS with full TypeScript declarations
Installation
npm install @foxnose/sdk
Quick Start
import { FluxClient, SimpleKeyAuth } from '@foxnose/sdk';
const client = new FluxClient({
baseUrl: 'https://your-env.fxns.io',
apiPrefix: 'content',
auth: new SimpleKeyAuth('YOUR_PUBLIC_KEY', 'YOUR_SECRET_KEY'),
});
const resources = await client.listResources('articles');
const results = await client.search('articles', {
search_mode: 'hybrid',
vector_search: { query: 'how to build AI applications', top_k: 5 },
});
client.close();
Links
New external_id field on resources enables idempotent syncing from external systems.
Upsert by external_id
- New endpoint:
PUT /v1/:env/folders/:folder/resources/?external_id=<value> - Creates a new resource if the
external_iddoesn't exist in the folder, or creates a new revision if it does - Returns
201 Created,200 OK, or202 Accepted(async mode) - Supports all standard resource options:
name,mode,resource_owner,validate_data,component
external_id on resources
- Optional identifier — allowed characters:
a-z A-Z 0-9 - _ / ., max 255 characters - Can be set during
POSTcreation orPUTupsert POSTreturns409 external_id_conflictif the ID is already taken in the folder- Returned in the resource object as
external_id(nullwhen not set)
Improved error handling
- Non-GET requests (POST, PUT, PATCH, DELETE) to URLs without a trailing slash now return
422 trailing_slash_requiredinstead of500 - GET requests without a trailing slash continue to redirect via
301
Endpoints
PUT /v1/:env/folders/:folder/resources/?external_id=<value>— Upsert ResourcePOST /v1/:env/folders/:folder/resources/— Create Resource (now accepts optionalexternal_id)
Full support for external_id and upsert operations, including concurrent batch upserts.
New: upsert_resource()
- Creates a resource if the
external_iddoesn't exist, or creates a new revision if it does - Uses
PUT /folders/:folder/resources/?external_id=<value>under the hood
folder = client.get_folder_by_path("content/blog-posts")
resource = client.upsert_resource(
folder,
{"title": "Hello World", "body": "..."},
external_id="post-42",
)
New: batch_upsert_resources()
- Upserts multiple resources in parallel with configurable concurrency
- Collects successes and failures into a
BatchUpsertResult fail_fast=Truestops on first error;fail_fast=False(default) processes all items- Optional
on_progresscallback:(completed_count, total_count)
from foxnose_sdk import BatchUpsertItem
folder = client.get_folder_by_path("content/articles")
items = [
BatchUpsertItem(external_id=f"article-{i}", payload={"title": f"Article {i}"})
for i in range(100)
]
result = client.batch_upsert_resources(
folder,
items,
max_concurrency=10,
on_progress=lambda done, total: print(f"{done}/{total}"),
)
print(f"OK: {result.success_count}, Failed: {result.failure_count}")
Updated: create_resource()
- New optional
external_idparameter to assign an external ID during creation
folder = client.get_folder_by_path("content/blog-posts")
resource = client.create_resource(
folder,
{"title": "New Post"},
external_id="post-99",
)
New model field
ResourceSummary.external_id—str | None, populated from API responses
New types
BatchUpsertItem— input container withexternal_id,payload, and optionalcomponentBatchUpsertResult— output withsucceeded,failedlists andtotal,success_count,failure_count,has_failurespropertiesBatchItemError— error details withindex,external_id, andexception
Async support
All new methods are available on AsyncManagementClient with identical signatures.
Two new components that extend the LangChain integration beyond retrieval.
FoxNoseLoader
- Bulk document loading from FoxNose folders with cursor-based pagination
- Memory-efficient
lazy_load()for large folders - Async support via
alazy_load() - Filtering, sorting, and configurable batch size
create_foxnose_tool
- Factory function that wraps
FoxNoseRetrieveras a LangChain tool for LLM agents - Compatible with any LangChain agent framework (langgraph, etc.)
- Customizable tool name and description
- Two response formats: plain string or
(str, list[Document])tuple
from langchain_foxnose import FoxNoseLoader, create_foxnose_tool
# Bulk-load all documents from a folder
loader = FoxNoseLoader(
client=client,
folder_path="knowledge-base",
page_content_field="body",
)
docs = loader.load()
# Create a search tool for agents
tool = create_foxnose_tool(
client=client,
folder_path="knowledge-base",
page_content_field="body",
search_mode="hybrid",
)
Audit trail for all content changes — now accessible via API and dashboard.
Features
- Environment Events — track changes to folders, resources, revisions, components, versions, APIs, roles, and API keys
- Global Events — track changes to users and projects at the organization level
- Rich filtering by entity type, event type, time range, and related entities
- Actor information including user details or API key metadata
- Detailed metadata for each event type with contextual information
Dashboard
- Activity Log section added to environment dashboard — view and filter environment events
- Activity Log section added to organization dashboard — view and filter global events
Endpoints
GET /v1/:env/events/— List environment eventsGET /v1/global-events/— List global events
Initial release of the official LangChain integration for FoxNose.
Features
- FoxNoseRetriever — LangChain BaseRetriever backed by FoxNose Flux search
- Support for all search modes: text, vector, hybrid, and vector-boosted
- Flexible content mapping: single field, multiple fields, or custom mapper
- Metadata control: whitelist, blacklist, system metadata toggle
- Native async support via AsyncFluxClient
- Structured filtering via where parameter
- Convenience from_client_params() constructor
Installation
pip install langchain-foxnose
Quick Start
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_foxnose import FoxNoseRetriever
retriever = FoxNoseRetriever.from_client_params(
base_url="https://7c9h4pwu.fxns.io",
api_prefix="content",
public_key="YOUR_PUBLIC_KEY",
secret_key="YOUR_SECRET_KEY",
folder="articles",
search_mode="hybrid",
content_field="body",
)
qa = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o"),
retriever=retriever,
)
answer = qa.invoke("What are the best practices for API design?")
print(answer["result"])
Management client methods now accept either string keys or corresponding model objects (e.g. FolderSummary, ResourceSummary) wherever a *_key parameter is used. This eliminates the need to manually extract .key from objects returned by the API.
Added
- Model objects as identifiers — pass objects like
FolderSummaryorResourceSummarydirectly instead of extracting.key _resolve_key()helper for extracting string keys from model objects- 13 type aliases for method parameters:
FolderRef,ResourceRef,RevisionRef,ComponentRef,SchemaVersionRef,OrgRef,ProjectRef,EnvironmentRef,ManagementRoleRef,FluxRoleRef,ManagementAPIKeyRef,FluxAPIKeyRef,APIRef
from foxnose_sdk.management import ManagementClient
from foxnose_sdk.auth import SimpleKeyAuth
client = ManagementClient(
base_url="https://api.foxnose.net",
auth=SimpleKeyAuth("YOUR_PUBLIC_KEY", "YOUR_SECRET_KEY"),
)
# Before 0.2.0 — manually extract .key
folder = client.get_folder("my-folder")
resources = client.list_resources(folder.key)
# With 0.2.0 — pass the object directly
folder = client.get_folder("my-folder")
resources = client.list_resources(folder)
Initial release of the official FoxNose Python SDK.
Features
- Full support for Flux API v1
- Sync and async clients
- Simple and Secure authentication methods
- Type hints for all methods and responses
- Built-in error handling
Installation
pip install foxnose-sdk
Quick Start
from foxnose_sdk.flux import FluxClient
from foxnose_sdk.auth import SimpleKeyAuth
client = FluxClient(
base_url="https://7c9h4pwu.fxns.io",
api_prefix="content",
auth=SimpleKeyAuth("YOUR_PUBLIC_KEY", "YOUR_SECRET_KEY"),
)
# List resources
data = client.list_resources("articles")
for article in data["results"]:
print(f"- {article['data']['title']}")
# Search with filters
data = client.search("articles", body={
"where": {
"$": {
"all_of": [
{"status__eq": "published"},
{"category__in": ["tech", "science"]}
]
}
},
"limit": 10
})
# Vector search
data = client.search("articles", body={
"search_mode": "vector",
"vector_search": {
"query": "how to build AI applications",
"top_k": 5,
"similarity_threshold": 0.7
}
})
client.close()