Metadata filtering
Every artifact, memory, and blueprint carries metadata: free-form key/value pairs you (or your AI) attach when the resource is created or imported. Metadata filtering turns that into a query surface, so a team that stamps env: prod or scope: backend onto its resources can ask for them back.
The same filter works in three places:
- the REST API, through a single
metadataquery parameter - the web app, through the metadata filter control on the Artifacts, Memory, and Blueprints list pages
- the MCP server, so a connected AI tool can filter without going through the UI
The metadata query parameter
Section titled “The metadata query parameter”metadata takes a JSON object mapping each key to an array of accepted values, URL-encoded.
GET /api/v1/{team_id}/artifacts?metadata={"env":["prod"]}URL-encoded, which is what an HTTP client actually sends:
GET /api/v1/{team_id}/artifacts?metadata=%7B%22env%22%3A%5B%22prod%22%5D%7DIt is accepted on:
| Endpoint | Resource |
|---|---|
GET /api/v1/{team_id}/artifacts | artifacts |
GET /api/v1/{team_id}/artifacts/{project_id} | artifacts in one project |
GET /api/v1/{team_id}/memories | memories |
GET /api/v1/{team_id}/blueprints | blueprints |
GET /api/v1/{team_id}/blueprints/{project_id} | blueprints in one project |
Keys are ANDed, values are ORed
Section titled “Keys are ANDed, values are ORed”Within a key, any listed value matches. Across keys, every key must match.
{"env": ["prod", "staging"], "team": ["backend"]}That reads as: env is prod or staging, and team is backend.
Matching inside arrays
Section titled “Matching inside arrays”If a resource stores a metadata value as an array, the filter matches any element of it. A resource with {"tags": ["api", "billing"]} is returned by {"tags": ["billing"]}. You do not need to know whether a value was stored as a single string or a list.
Matching non-string values
Section titled “Matching non-string values”Metadata values are compared as they are stored. A filter value that unambiguously reads as a number or a boolean also matches a resource that stored it as one, so {"version": ["3"]} matches both {"version": "3"} and {"version": 3}, and {"active": ["true"]} matches both "true" and true.
Filtering on presence alone
Section titled “Filtering on presence alone”An empty array means “this key exists, whatever its value”:
{"env": []}That returns every resource carrying an env key. It is the right filter when you want to find resources that have been tagged at all, rather than tagged with something specific.
Limits
Section titled “Limits”A filter is rejected with 400 if it exceeds any of these:
| Limit | Value |
|---|---|
| Keys per filter | 10 |
| Values per key | 25 |
| Key length | 255 characters |
| Value length | 512 characters |
Malformed JSON is also rejected with 400. The error names the limit that was violated, so a client can correct it.
Discovering which keys and values exist
Section titled “Discovering which keys and values exist”Filtering is only useful if you know what to filter on. Two catalog endpoints report the metadata a team is actually using.
List the distinct keys on a resource type:
GET /api/v1/{team_id}/metadata/keys?resource_type=artifactsList the distinct values of one key:
GET /api/v1/{team_id}/metadata/values?resource_type=artifacts&key=envresource_type is one of artifacts, memories, or blueprints. Both accept an optional project_id to narrow to a single project, an optional q substring filter, and an optional limit (default 100, max 500). Both return a truncated flag reporting that more entries exist than were returned.
Results are scoped to your team. One team never sees another team’s metadata, even when both use the same key.
In the web app you do not call these directly: the metadata filter control on each list page reads them as you type.
Over MCP
Section titled “Over MCP”An AI tool connected to the MCP server has the same two capabilities.
Filter a listing by passing metadata to vibexp_io_list_resources. Because MCP is JSON based, it takes the object directly rather than a URL-encoded string:
{ "team_id": "your-team", "resource_type": "artifact", "metadata": {"env": ["prod", "staging"], "team": ["backend"]}}The semantics are identical to the REST parameter, including the empty-array key-exists form and the limits above.
Discover keys and values with vibexp_io_list_resource_metadata. Omit key to list the distinct metadata keys on a resource type, or supply key to list that key’s distinct values:
{ "team_id": "your-team", "resource_type": "artifact", "key": "env"}This is what lets an agent build a filter from real keys instead of guessing at them.
Removed in this release
Section titled “Removed in this release”Three earlier query parameters were removed and no longer filter anything. Requests carrying them still succeed, so an old client or a bookmarked URL degrades to an unfiltered list rather than an error. Nothing is silently mis-filtered, but nothing is filtered either.
| Removed | Was used on | Replacement |
|---|---|---|
metadata_<key>=value | artifacts, blueprints | metadata={"<key>":["value"]} |
metadata_key + metadata_value | memories | metadata={"<key>":["value"]} |
The endpoint GET /api/v1/{team_id}/memories/search was also removed. It filtered on a single metadata pair, which GET /api/v1/{team_id}/memories now does through metadata, with more expressive semantics.
If you have scripts or integrations using the old parameters, translate them to metadata before upgrading. The new form is strictly more capable: it filters on several keys at once, accepts several values per key, matches inside arrays, and is served by an index, so it is faster on large teams.