Changelog

Stay up to date with all the latest features, improvements, and bug fixes.


Truncated Reads and a Sharper MCP access_denied

Two Flux API refinements: capping long text in list responses, and an MCP error hint that finally points agents at the right fix.

Flux API

  • truncate_text on list and search. Add truncate_text=<n> to the query string on List Resources or Search to cap every text field to n characters — handy when a page of long documents is far larger than the caller needs. Opt-in and off by default: without it, responses are byte-for-byte unchanged. Only text-typed fields are affected (identifiers, keys, and _sys are never touched; raw=true ignores it), and every shortened field is flagged in the record's _sys.truncated array (field path, locale, original length) so a fragment is never mistaken for a whole document. Result count, ordering, relevance, and cursors are computed on the full data and are unchanged.

MCP

  • access_denied now returns a cause-specific hint. A get_record against a collection connected without get_one used to return access_denied with the generic unknown_resource hint ("reuse the exact resource_id") — sending the agent to re-confirm a resource_id that was already correct. The hint now names the actual remedy: use query_records/search_records when the method isn't enabled for the collection, or a different key/role when the key lacks access. error_code and message are unchanged, and the unknown_resource hint is unchanged.

Cross-Parent (Unscoped) Reads for Strict Collections

A strict-reference collection can now be read across its parents — a portal front page can list every listing, not the listings of one agency — without loosening the scoped route or changing how anything is written.

Flux API

  • New read addresses per collection. Alongside the scoped route, a strict-reference collection can publish additional read-only addresses that drop one or more ancestor keys. Two forms: fully-flat / unscoped (/realty/accounts/listings/photos — every resource across all parents) and partially-flat (/realty/accounts/{account_key}/listings/photos — everything under one higher ancestor). They serve List Resources, Search, Get Resource and Schema; there is no create/update/delete on a flat address. Strictly additive — the scoped route is unchanged. See Cross-Parent Reads.
  • On a public API, a fully-flat address is an anonymous, cross-tenant read of the whole collection — a deliberate exposure, opt-in only. On a key-authenticated API the address changes the scope, never the permission check.
  • /_router lists the cross-parent addresses actually being served.

Management API

  • New fields on the collection connection: unscoped_levels (which levels to publish) plus the unscoped_ancestors chain it publishes against, and a read-only flat_routes describing every publishable address and whether it is enabled. See the Collection Connection Object.

Dashboard

  • Enabled cross-parent addresses are listed among the collection's endpoints; an Add a cross-parent address control publishes one, asking for confirmation before widening exposure. A single click removes one.

CORS for Public Flux APIs: Call Search from the Browser

Public Flux APIs can now be called directly from browser JavaScript — a static page gets typed reads and hybrid search with no server of its own.

Management API

  • New field on the API object: cors_origins (array of origins, default [] = off). Set explicit origins or ["*"] for a public API — anonymous reads are already open server-side, so wildcard CORS adds no new exposure. See APIs.

Flux API

  • With cors_origins configured, cross-origin GET reads and POST search work from browsers. Preflight OPTIONS requests are answered immediately and are never billed. Error responses (402, rate limiting) carry CORS headers, so client code receives the machine-readable body instead of an opaque error. Retry-After is exposed to JavaScript.
  • Browser CORS covers public, key-free traffic only: Authorization is not an allowed browser header, writes still require an authenticated key, and /{api_prefix}/_mcp is not covered — MCP clients are not browsers.

REST Writes on the Flux API

The write capability added with the MCP write tools is now available over plain REST — same pipeline, same permissions, same billing, two transports.

Flux API

  • New endpoints: POST /{api_prefix}/{collection_path}/ (create + publish) and PUT .../{resource_key}/ (full-document replace → new published revision). No PATCH, no delete.
  • Same double opt-in as MCP: the key's role needs create/update grants, the collection connection must allow the method. Writes always require an authenticated key — public APIs accept anonymous reads only.
  • /_router now advertises write routes to keys that hold the grants.
  • Billing: one weighted Write per call, never a retrieval; failed calls free.

Structural Limit Scopes Clarified

Plan quotas now live where the resources live. Per organization: projects and members. Per project: environments (production included; protection settings never affect the count). Per environment: collections, components, Flux APIs, locales, API keys, and custom roles — each environment gets the full allowance, so adding an environment never eats into another one's capacity. Plan numbers are unchanged; only the scopes are clarified. See Limits.

Management API

  • GET /organizations/{org_key}/usage/ now reports only org-scoped structural entities (projects, resources, users) alongside storage and billing usage. environments, collections, and components left the response — they are enforced per project / per environment at create time and no longer have an org-wide meaning.
  • Exceeding a scoped quota returns the same 403 plan_limit_exceeded with the affected detail.entity.

MCP Write Tools: Agents Can Now Save What They Learn

The per-API MCP server gains two opt-in write tools — create_record and update_record. An agent connected over MCP can now persist knowledge through the same connector it reads from: the natural fit for agent-memory setups, where the agent reads the whole knowledge base and writes what it learns into a dedicated collection.

Flux API

  • Two new tools on POST /{api_prefix}/_mcp: create_record (create + publish a record) and update_record (replace a record with a new published revision). Every write runs the full publish pipeline — schema validation, revisions, vectorization — exactly like a Management API write. There is deliberately no delete tool. See Write tools.
  • Double opt-in permissions. Write access requires both the key's role (create/update grants on the API) and the collection connection (create/update in its allowed_methods, off by default). Keys without write grants don't even see the write tools in tools/list. The typical setup — read ten collections, write into one — is one API with writes enabled on a single collection.
  • discover_resources now annotates each resource with writable (subset of create, update), so agents know where they may write without probing.
  • New tool-level errors: collection_not_writable, content_validation_failed (carries the standard content validation payload with json_path items), external_id_conflict.

Billing

  • A write-tool tools/call meters as a weighted Writemax(1, ceil(embedded_tokens / 500)), same formula as REST writes — and is never additionally counted as a Retrieval. The result reports the charge in write_units. Failed calls remain free.

Dashboard

  • Collection connections in the Flux API editor gain Create / Update toggles (off by default).
  • Flux Access roles gain create / update grants for the MCP write tools.

Documentation

  • New guide: Multi-Tenant Agent Memory — per-user agent memory with enforced tenant scoping (strict reference), scoped MCP writes, and one-call tenant deletion with vector cleanup.

Built-in MCP Server & Per-API Agent Visibility

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_resources, describe_resource, get_record, query_records, search_records) scoped to the connected collections of the prefix. The search_records tool 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_required controls anonymous access, per-collection allowed_methods controls which tools are available per collection, Flux API key roles apply identically.

Management API

  • New fields on the API object (returned by GET, accepted by POST and PATCH):
    • mcp_enabled (boolean, default true) — when false, the /{api_prefix}/_mcp endpoint returns 404
    • router_introspection_enabled (boolean, default true) — when false, the /{api_prefix}/_router endpoint returns 404
  • Both fields are independent policy switches. The data routes (/{collection}, /{collection}/{key}, /{collection}/_search) are never affected.
  • Both fields use strict JSON-boolean validation — strings like "true", integers, and null are rejected with 422.
  • 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 collection edit). No data migration is required.

Search Metadata & Default Threshold

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.

LangChain Integration 0.3.1

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

Vector Search in Python & TypeScript SDKs

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.models module: SearchMode, VectorSearch, VectorFieldSearch, VectorBoostConfig, HybridConfig, SearchRequest with cross-field validation
  • Convenience methods on FluxClient / AsyncFluxClient with **extra_body pass-through for where, 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, SearchRequest with buildSearchBody() runtime validation
  • Convenience methods on FluxClient with 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,
});

Vector Fields & Custom Vector Search

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_search is supported with search_mode="vector" and search_mode="vector_boosted"
  • vector_field_search is not supported with search_mode="text" or search_mode="hybrid"
  • vector_field_search and vector_search are mutually exclusive
  • Distance metric for vector field search: cosine only

TypeScript SDK 0.2.0

Support for Flux API introspection and route-level descriptions on API collection connections.

New: Flux introspection methods

  • FluxClient.getRouter() — calls GET /{api_prefix}/_router
  • FluxClient.getSchema(folderPath) — calls GET /{api_prefix}/{collection_path}/_schema
const router = await fluxClient.getRouter();
const schema = await fluxClient.getSchema('content/articles');

Updated: API collection connection methods

  • ManagementClient.addApiFolder() and updateApiFolder() now accept optional route descriptions:
    • descriptionGetOne
    • descriptionGetMany
    • descriptionSearch
    • descriptionSchema
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

  • APIFolderSummary now includes all description_* fields from Management API responses

Python SDK 0.4.0

Support for Flux API introspection and route-level descriptions on API collection connections.

New: Flux introspection methods

  • FluxClient.get_router() / AsyncFluxClient.get_router() — calls GET /{api_prefix}/_router
  • FluxClient.get_schema(folder_path) / AsyncFluxClient.get_schema(folder_path) — calls GET /{api_prefix}/{collection_path}/_schema
router = flux_client.get_router()
schema = flux_client.get_schema("content/articles")

Updated: API collection connection methods

  • ManagementClient.add_api_folder() and update_api_folder() now accept optional route descriptions:
    • description_get_one
    • description_get_many
    • description_search
    • description_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

  • APIFolderSummary now includes all description_* fields from Management API responses

Flux API Introspection Updates

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}/{collection_path}/_schema — returns live JSON Schema and route metadata for the target collection path
  • /_schema now includes searchable metadata:
    • searchable_fields — fields available for filtering/search
    • non_searchable_fields — fields present in schema but not indexed for search

Management API

  • Collection-to-API connection supports per-action route descriptions:
    • description_get_many
    • description_get_one
    • description_search
    • description_schema
  • These descriptions are used by Flux /_router for each action route

LangChain.js Integration 0.1.0

Initial release of the official LangChain.js integration for FoxNose.

Features

  • FoxNoseRetriever — LangChain BaseRetriever backed by FoxNose Flux search with text, vector, hybrid, and vector-boosted modes
  • FoxNoseLoaderBaseDocumentLoader with cursor-based pagination and loadLazy() async generator for memory-efficient bulk loading
  • createFoxNoseTool — factory that wraps retrieval as a DynamicStructuredTool for LLM agents
  • Flexible content mapping: single field, multiple fields, or custom mapper
  • Metadata control: whitelist, blacklist, system metadata toggle
  • Structured filtering via where parameter
  • 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

TypeScript SDK 0.1.0

Initial release of the official FoxNose TypeScript SDK.

Features

  • ManagementClient — full coverage of Management API v1: collections, 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 maxConcurrency and 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

Resource External IDs & Upsert API

New external_id field on resources enables idempotent syncing from external systems.

Upsert by external_id

  • New endpoint: PUT /v1/:env/collections/:collection/resources/?external_id=<value>
  • Creates a new resource if the external_id doesn't exist in the collection, or creates a new revision if it does
  • Returns 201 Created, 200 OK, or 202 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 POST creation or PUT upsert
  • POST returns 409 external_id_conflict if the ID is already taken in the collection
  • Returned in the resource object as external_id (null when not set)

Improved error handling

  • Non-GET requests (POST, PUT, PATCH, DELETE) to URLs without a trailing slash now return 422 trailing_slash_required instead of 500
  • GET requests without a trailing slash continue to redirect via 301

Endpoints

  • PUT /v1/:env/collections/:collection/resources/?external_id=<value>Upsert Resource
  • POST /v1/:env/collections/:collection/resources/Create Resource (now accepts optional external_id)

Python SDK 0.3.0

Full support for external_id and upsert operations, including concurrent batch upserts.

New: upsert_resource()

  • Creates a resource if the external_id doesn't exist, or creates a new revision if it does
  • Uses PUT /collections/:collection/resources/?external_id=<value> under the hood
collection = client.get_collection_by_path("content/blog-posts")

resource = client.upsert_resource(
    collection,
    {"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=True stops on first error; fail_fast=False (default) processes all items
  • Optional on_progress callback: (completed_count, total_count)
from foxnose_sdk import BatchUpsertItem

collection = client.get_collection_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(
    collection,
    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_id parameter to assign an external ID during creation
collection = client.get_collection_by_path("content/blog-posts")

resource = client.create_resource(
    collection,
    {"title": "New Post"},
    external_id="post-99",
)

New model field

  • ResourceSummary.external_idstr | None, populated from API responses

New types

  • BatchUpsertItem — input container with external_id, payload, and optional component
  • BatchUpsertResult — output with succeeded, failed lists and total, success_count, failure_count, has_failures properties
  • BatchItemError — error details with index, external_id, and exception

Async support

All new methods are available on AsyncManagementClient with identical signatures.

LangChain Integration 0.2.0

Two new components that extend the LangChain integration beyond retrieval.

FoxNoseLoader

  • Bulk document loading from FoxNose collections with cursor-based pagination
  • Memory-efficient lazy_load() for large collections
  • Async support via alazy_load()
  • Filtering, sorting, and configurable batch size

create_foxnose_tool

  • Factory function that wraps FoxNoseRetriever as 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",
)

Events API

Audit trail for all content changes — now accessible via API and dashboard.

Features

  • Environment Events — track changes to collections, 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

LangChain Integration 0.1.0

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"])

Python SDK 0.2.0

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 FolderSummary or ResourceSummary directly 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
collection = client.get_collection("my-folder")
resources = client.list_resources(collection.key)

# With 0.2.0 — pass the object directly
collection = client.get_collection("my-folder")
resources = client.list_resources(collection)

Python SDK 0.1.0

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()

Was this page helpful?