Object Metadata & Tag Search Tools
Status: ✅ Implemented in v0.6.0 Issue: #15Commit:
0f722dc—feat(tools): add object metadata and tag toolsBranch:feature/cloud-native-v1
Problem
Cloud objects are more than just bytes — they carry metadata (Content-Type, Cache-Control, custom headers) and tags (key-value pairs for classification, cost allocation, lifecycle management). The current toolset treats objects as opaque files, missing these cloud-native capabilities.
Use cases:
- "Find all objects tagged
environment=production" - "Show me the metadata for this config file"
- "Tag all CSV files under
data/withdepartment=analytics"
Design
New Tools
1. get_object_metadata
typescript
server.registerTool("get_object_metadata", {
description: "Get cloud-native metadata and tags for an object (content-type, custom headers, S3 tags, etc.).",
inputSchema: z.object({
path: z.string(),
}),
});Returns:
json
{
"key": "data/report.csv",
"size": 45231,
"lastModified": "2024-01-15T10:00:00Z",
"contentType": "text/csv",
"metadata": { "x-amz-meta-author": "alice", "x-amz-meta-version": "3" },
"tags": { "department": "analytics", "environment": "production" }
}2. set_object_tags
typescript
server.registerTool("set_object_tags", {
description: "Set or update tags on a cloud object. Replaces all existing tags.",
inputSchema: z.object({
path: z.string(),
tags: z.record(z.string(), z.string()).describe("Key-value tag pairs"),
}),
});3. search_by_tag
typescript
server.registerTool("search_by_tag", {
description: "Find objects matching tag filters under a path.",
inputSchema: z.object({
path: z.string(),
tags: z.record(z.string(), z.string()).describe("Tag key-value pairs to match (AND logic)"),
max_objects: z.number().int().positive().default(100),
}),
});Provider Interface Extension
typescript
export interface StorageProvider {
// ... existing methods ...
/** Get object metadata and tags. */
getObjectMetadata?(root: ParsedRoot, key: string): Promise<ObjectMetadata>;
/** Set tags on an object. */
setObjectTags?(root: ParsedRoot, key: string, tags: Record<string, string>): Promise<void>;
/** Get tags for an object. */
getObjectTags?(root: ParsedRoot, key: string): Promise<Record<string, string>>;
}
export interface ObjectMetadata extends ObjectInfo {
metadata: Record<string, string>; // custom metadata headers
tags: Record<string, string>; // object tags
}Provider Support
| Provider | Metadata | Tags |
|---|---|---|
| S3 | ✅ HeadObject → Metadata | ✅ GetObjectTagging / PutObjectTagging |
| Azure | ✅ getProperties() → metadata | ✅ getTags() / setTags() |
| GCS | ✅ file.metadata | ✅ Via metadata.labels (GCS uses labels not tags) |
| Memory | ✅ In-memory map | ✅ In-memory map |
| SQLite | ✅ JSON column | ✅ JSON column |
Search Strategy
search_by_tag lists objects under the prefix, then fetches tags for each and filters client-side. This is O(n) in objects but necessary since most cloud providers don't support server-side tag-based listing (except S3 Inventory, which is async).
Cap at max_objects to bound the cost.
Implementation Plan
- Extend
StorageProviderinterface with optionalgetObjectMetadata,setObjectTags,getObjectTags. - Implement in S3Provider (HeadObject + GetObjectTagging/PutObjectTagging).
- Implement in MemoryProvider (in-memory maps).
- Create
src/tools/metadata.tswith handlers. - Register tools, add to scope map (
get_object_metadata→ READ,set_object_tags→ WRITE,search_by_tag→ SEARCH). - Unit tests with mock provider.
- Integration tests with MinIO.
Acceptance Criteria
- [x]
get_object_metadatareturns metadata + tags for S3/Azure/GCS objects - [x]
set_object_tagssets tags on objects - [x]
search_by_tagfinds objects matching tag filters - [x] Unsupported providers return clear error messages
- [x] Memory/SQLite providers support metadata and tags
- [x] Proper OAuth scope enforcement
- [x] Unit tests with mock provider
