@ragieai/skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ # Ragie MCP Server
2
+
3
+ The Ragie MCP server exposes a `retrieve` tool scoped to a specific partition. Use it to search your knowledge base interactively from Claude Code — without writing code.
4
+
5
+ ## URL Pattern
6
+
7
+ Each partition gets its own endpoint:
8
+
9
+ ```
10
+ https://api.ragie.ai/mcp/{partition}
11
+ ```
12
+
13
+ Examples:
14
+ - `https://api.ragie.ai/mcp/debug` → `debug` partition
15
+ - `https://api.ragie.ai/mcp/production` → `production` partition
16
+
17
+ ## Configuration
18
+
19
+ ### Via this plugin (recommended)
20
+
21
+ Set environment variables before starting Claude Code:
22
+
23
+ ```bash
24
+ export RAGIE_API_KEY=ragie_...
25
+ export RAGIE_PARTITION=your-partition-name
26
+ ```
27
+
28
+ The plugin's `.mcp.json` handles the rest automatically.
29
+
30
+ ### Manual project config
31
+
32
+ Add to your project's `.mcp.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "ragie": {
38
+ "type": "http",
39
+ "url": "https://api.ragie.ai/mcp/your-partition",
40
+ "headers": {
41
+ "Authorization": "Bearer ${RAGIE_API_KEY}"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Multiple partitions
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "ragie-main": {
54
+ "type": "http",
55
+ "url": "https://api.ragie.ai/mcp/main",
56
+ "headers": { "Authorization": "Bearer ${RAGIE_API_KEY}" }
57
+ },
58
+ "ragie-debug": {
59
+ "type": "http",
60
+ "url": "https://api.ragie.ai/mcp/debug",
61
+ "headers": { "Authorization": "Bearer ${RAGIE_API_KEY}" }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## The `retrieve` Tool
68
+
69
+ Once the MCP server is connected, Claude can call `retrieve` directly.
70
+
71
+ Example prompts:
72
+ - *"Search my Ragie knowledge base for rate limit documentation"*
73
+ - *"What does Ragie have indexed about authentication?"*
74
+ - *"Retrieve the top 5 chunks about error handling and show me the scores"*
75
+
76
+ ## When to Use MCP vs SDK
77
+
78
+ | Situation | Use |
79
+ |-----------|-----|
80
+ | Testing retrieval quality during development | MCP |
81
+ | Exploring what's indexed in a partition | MCP |
82
+ | Debugging poor search results | MCP |
83
+ | Production application code | SDK (`client.retrievals.retrieve()`) |
84
+ | Ingesting documents | SDK (MCP only exposes `retrieve`) |
@@ -0,0 +1,149 @@
1
+ # Ragie Metadata Filtering
2
+
3
+ Metadata is arbitrary key-value pairs attached to documents at ingest time. Use them to filter retrieval to a relevant subset of documents.
4
+
5
+ > Python user? See `references/python.md` for Python equivalents.
6
+
7
+ ## Attaching Metadata on Ingest
8
+
9
+ ```typescript
10
+ await client.documents.createRaw({
11
+ content: contentBuffer,
12
+ contentType: "application/pdf",
13
+ name: "API Docs v3",
14
+ metadata: {
15
+ product: "api",
16
+ version: 3, // numbers are allowed
17
+ active: true, // booleans are allowed
18
+ tags: ["public", "stable"], // list of strings is allowed
19
+ },
20
+ });
21
+ ```
22
+
23
+ Metadata values can be: **strings**, **numbers** (stored as 64-bit float), **booleans**, or **lists of strings**. Keys set to `null` in a patch operation are deleted.
24
+
25
+ ## Filtering at Retrieval
26
+
27
+ ### Simple equality (shorthand)
28
+
29
+ ```typescript
30
+ const results = await client.retrievals.retrieve({
31
+ query: "rate limits",
32
+ filter: {
33
+ product: "api",
34
+ version: 3,
35
+ },
36
+ });
37
+ ```
38
+
39
+ All keys in a plain object filter must match — it's an implicit `$and` of equality checks.
40
+
41
+ ### Filter operators
42
+
43
+ For range, set membership, and logical combinations use explicit operators:
44
+
45
+ ```typescript
46
+ // Greater than / less than
47
+ filter: { year: { $gt: 2022 } }
48
+ filter: { score: { $gte: 0.8, $lte: 1.0 } }
49
+
50
+ // Not equal
51
+ filter: { status: { $ne: "draft" } }
52
+
53
+ // In / not in a set
54
+ filter: { lang: { $in: ["en", "fr"] } }
55
+ filter: { env: { $nin: ["test", "staging"] } }
56
+
57
+ // Logical OR
58
+ filter: {
59
+ $or: [
60
+ { product: "api" },
61
+ { product: "dashboard" }
62
+ ]
63
+ }
64
+
65
+ // Logical AND with sub-conditions
66
+ filter: {
67
+ $and: [
68
+ { product: "api" },
69
+ { version: { $gte: 2 } }
70
+ ]
71
+ }
72
+ ```
73
+
74
+ | Operator | Purpose | Supported types |
75
+ |----------|---------|-----------------|
76
+ | `$eq` | Equal | number, string, boolean |
77
+ | `$ne` | Not equal | number, string, boolean |
78
+ | `$gt` | Greater than | number only |
79
+ | `$gte` | Greater than or equal | number only |
80
+ | `$lt` | Less than | number only |
81
+ | `$lte` | Less than or equal | number only |
82
+ | `$in` | Value in array | string or number |
83
+ | `$nin` | Value not in array | string or number |
84
+ | `$and` | Logical AND | compound |
85
+ | `$or` | Logical OR | compound |
86
+
87
+ ## Updating Metadata
88
+
89
+ ```typescript
90
+ // Partial update — only specified keys are changed; keys set to null are deleted
91
+ await client.documents.patchMetadata({
92
+ documentId: docId,
93
+ patchDocumentMetadataParams: { metadata: { reviewed: "true", version: 4 } },
94
+ });
95
+ ```
96
+
97
+ ## Common Patterns
98
+
99
+ ### Product + version scoping
100
+
101
+ ```typescript
102
+ // Tag by product and version on ingest
103
+ await client.documents.createDocumentFromUrl({
104
+ url,
105
+ metadata: { product: "dashboard", version: 4 },
106
+ });
107
+
108
+ // Query only that version's docs
109
+ const results = await client.retrievals.retrieve({
110
+ query: "usage metrics",
111
+ filter: { product: "dashboard", version: 4 },
112
+ });
113
+ ```
114
+
115
+ ### Language filtering
116
+
117
+ ```typescript
118
+ metadata: { lang: "fr" }
119
+ filter: { lang: "fr" }
120
+ ```
121
+
122
+ ### Environment separation
123
+
124
+ ```typescript
125
+ metadata: { env: "staging" }
126
+ filter: { env: "staging" }
127
+ ```
128
+
129
+ ### Date range filtering
130
+
131
+ ```typescript
132
+ // Store dates as Unix timestamps (numbers)
133
+ metadata: { published_at: 1704067200 }
134
+
135
+ // Query documents published after 2024-01-01
136
+ filter: { published_at: { $gt: 1704067200 } }
137
+ ```
138
+
139
+ ## Metadata vs Partitions
140
+
141
+ Metadata filtering and partitions serve different purposes — see `partitions.md` for the comparison.
142
+
143
+ ## Gotchas
144
+
145
+ - Metadata values may be strings, numbers, booleans, or lists of strings. Numbers do **not** need to be stringified — use native numbers for range operators to work correctly.
146
+ - Filtering on a key that doesn't exist on a document excludes that document from results.
147
+ - Reserved keys (will cause a 422 error): `document_id`, `document_type`, `document_source`, `document_name`, `document_uploaded_at`. Keys beginning with `_` are also reserved.
148
+ - Metadata filtering is a **pre-filter**: Ragie guarantees `top_k` results if they exist after filtering.
149
+ - Up to 1000 total metadata values per document (each item in an array counts toward the total).
@@ -0,0 +1,85 @@
1
+ # Ragie Partitions
2
+
3
+ Partitions are logical namespaces within a single Ragie account. Use them to isolate documents by tenant, environment, project, or any other boundary.
4
+
5
+ > Python user? See `references/python.md` for Python equivalents.
6
+
7
+ ## Basic Usage
8
+
9
+ ```typescript
10
+ // Ingest into a partition
11
+ await client.documents.createDocumentFromUrl({
12
+ url: "https://example.com/doc",
13
+ partition: "tenant-42",
14
+ });
15
+
16
+ // Retrieve from that partition only
17
+ const results = await client.retrievals.retrieve({
18
+ query: "pricing",
19
+ partition: "tenant-42",
20
+ });
21
+ ```
22
+
23
+ ## Multi-Tenant Pattern
24
+
25
+ ```typescript
26
+ function ingestForTenant(client: Ragie, tenantId: string, url: string) {
27
+ return client.documents.createDocumentFromUrl({
28
+ url,
29
+ partition: `tenant-${tenantId}`,
30
+ });
31
+ }
32
+
33
+ function retrieveForTenant(client: Ragie, tenantId: string, query: string) {
34
+ return client.retrievals.retrieve({
35
+ query,
36
+ partition: `tenant-${tenantId}`,
37
+ rerank: true,
38
+ });
39
+ }
40
+ ```
41
+
42
+ ## Partition Management
43
+
44
+ ```typescript
45
+ // List all partitions (returns a PageIterator — async iterable)
46
+ for await (const page of client.partitions.list()) {
47
+ for (const partition of page.result.partitions) {
48
+ console.log(partition.id, partition.name);
49
+ }
50
+ }
51
+
52
+ // Create a partition explicitly
53
+ await client.partitions.create({ name: "tenant-42", description: "optional" });
54
+ // Note: partitions are also created implicitly on first document ingest
55
+
56
+ // Get partition details and usage metrics (document count, pages processed)
57
+ const detail = await client.partitions.get({ partitionId: "tenant-42" });
58
+
59
+ // Set page limits (triggers webhook when limit is exceeded)
60
+ await client.partitions.setLimits({
61
+ partitionId: "tenant-42",
62
+ partitionLimitParams: { pagesHostedLimitMonthly: 1000 },
63
+ });
64
+
65
+ // Delete a partition and all its documents
66
+ await client.partitions.delete({ partitionId: "tenant-42" });
67
+ ```
68
+
69
+ ## Partitions vs Metadata Filters
70
+
71
+ | | Partitions | Metadata filters |
72
+ |-|------------|-----------------|
73
+ | Isolation | Hard — separate index | Soft — same index, filtered at query time |
74
+ | Use for | Multi-tenancy, environments | Document categories, versions, tags |
75
+ | Performance | Fastest (no cross-partition scan) | Slightly slower on large corpora |
76
+ | Deletion | Delete whole partition at once | Must delete documents individually |
77
+
78
+ Use partitions for tenant isolation. Use metadata filters for sub-categorization within a tenant. See `metadata-filtering.md`.
79
+
80
+ ## Gotchas
81
+
82
+ - Omitting `partition` on ingest places the document in the default partition.
83
+ - Omitting `partition` on retrieval searches **only** the default partition — not all partitions.
84
+ - Partition names are case-sensitive. `Tenant-42` and `tenant-42` are different partitions.
85
+ - Deleting a partition is irreversible and deletes all documents within it.
@@ -0,0 +1,232 @@
1
+ # Ragie Python SDK
2
+
3
+ The Python SDK mirrors the TypeScript SDK conceptually — same methods, snake_case naming.
4
+
5
+ ```bash
6
+ pip install ragie
7
+ ```
8
+
9
+ ```python
10
+ import os
11
+ from ragie import Ragie
12
+
13
+ client = Ragie(auth=os.environ["RAGIE_API_KEY"])
14
+ ```
15
+
16
+ ## Ingestion
17
+
18
+ The Python SDK has three distinct methods depending on the source. Using the wrong one is a common source of errors.
19
+
20
+ | Source | Method | Request class |
21
+ |--------|--------|---------------|
22
+ | File upload (all file types) | `documents.create()` | `ragie.CreateDocumentParams` + `ragie.File` |
23
+ | In-memory data (text/JSON) | `documents.create_raw()` | `ragie.CreateDocumentRawParams` |
24
+ | URL | `documents.create_document_from_url()` | `ragie.CreateDocumentFromURLParams` |
25
+
26
+ **Prefer `documents.create()`** when uploading files from disk, as it supports all file types including binary formats. **Prefer `create_raw()`** when your data is already in memory as a string or dict — it is simpler and avoids unnecessary file wrapping, but only handles text and JSON.
27
+
28
+ ### From a file
29
+
30
+ Use `documents.create()` with `ragie.File`. This is the only method that supports all file types including binary formats (PDF, DOCX, images, etc.).
31
+
32
+ ```python
33
+ import ragie
34
+
35
+ with open("doc.pdf", "rb") as f:
36
+ doc = client.documents.create(
37
+ request=ragie.CreateDocumentParams(
38
+ file=ragie.File(
39
+ file_name="doc.pdf",
40
+ content=f.read(),
41
+ content_type="application/pdf",
42
+ ),
43
+ name="Q4 Report",
44
+ partition="tenant-42",
45
+ metadata={"type": "report", "year": "2024"},
46
+ )
47
+ )
48
+ ```
49
+
50
+ ### From a URL
51
+
52
+ ```python
53
+ import ragie
54
+
55
+ doc = client.documents.create_document_from_url(
56
+ request=ragie.CreateDocumentFromURLParams(
57
+ url="https://example.com/report.pdf",
58
+ name="Q4 Report",
59
+ partition="tenant-42",
60
+ metadata={"type": "report", "year": "2024"},
61
+ )
62
+ )
63
+ ```
64
+
65
+ ### From in-memory data (raw text or JSON)
66
+
67
+ **Preferred when your data is already in memory** (e.g., scraped content, generated text, API responses). Accepts strings and dicts — not bytes.
68
+
69
+ ```python
70
+ import ragie
71
+
72
+ doc = client.documents.create_raw(
73
+ request=ragie.CreateDocumentRawParams(
74
+ data="Your text content here...", # str or dict
75
+ name="my-note",
76
+ partition="tenant-42",
77
+ )
78
+ )
79
+ ```
80
+
81
+ ## Polling for Readiness
82
+
83
+ `documents.get()` returns `DocumentGet`, which is a **different type** from the `Document` returned by `create()` and `create_document_from_url()`. Both have `.status` and `.id`, but annotate them separately if you need type safety.
84
+
85
+ ```python
86
+ import time
87
+ from ragie.models import DocumentGet
88
+
89
+ def wait_for_ready(client, doc_id: str, timeout: int = 120) -> None:
90
+ start = time.time()
91
+ while time.time() - start < timeout:
92
+ doc: DocumentGet = client.documents.get(document_id=doc_id)
93
+ if doc.status == "ready":
94
+ return
95
+ if doc.status == "failed":
96
+ raise RuntimeError(f"Document {doc_id} failed")
97
+ time.sleep(3)
98
+ raise TimeoutError(f"Document {doc_id} not ready after {timeout}s")
99
+ ```
100
+
101
+ ## Retrieval
102
+
103
+ ```python
104
+ import ragie
105
+
106
+ results = client.retrievals.retrieve(
107
+ request=ragie.RetrieveParams(
108
+ query="your question",
109
+ top_k=8,
110
+ rerank=True,
111
+ partition="tenant-42",
112
+ filter={"product": "api", "version": "v3"},
113
+ )
114
+ )
115
+
116
+ for chunk in results.scored_chunks:
117
+ print(chunk.text, chunk.score)
118
+ # also: chunk.document_id, chunk.document_name, chunk.document_metadata
119
+ ```
120
+
121
+ ## Document Management
122
+
123
+ ```python
124
+ import ragie
125
+ from ragie.models import DocumentGet
126
+
127
+ # Get a document — returns DocumentGet, not Document
128
+ doc: DocumentGet = client.documents.get(document_id=doc_id)
129
+
130
+ # List documents — use ListDocumentsRequest, not keyword args
131
+ # .result is a DocumentList object — access .result.documents for the list
132
+ page = client.documents.list(
133
+ request=ragie.ListDocumentsRequest(partition="tenant-42", page_size=50)
134
+ )
135
+ docs = page.result.documents
136
+
137
+ # Paginate
138
+ while page is not None:
139
+ for doc in page.result.documents:
140
+ print(doc.id, doc.name)
141
+ page = page.next() # .next() returns the next page or None
142
+
143
+ # Update metadata (partial update — keyword args are required)
144
+ client.documents.patch_metadata(
145
+ document_id=doc_id,
146
+ patch_document_metadata_params=ragie.PatchDocumentMetadataParams(
147
+ metadata={"reviewed": "true", "version": 4}
148
+ ),
149
+ )
150
+
151
+ # Delete a document (keyword args are required)
152
+ client.documents.delete(document_id=doc_id)
153
+ ```
154
+
155
+ ## Bulk Ingestion (asyncio)
156
+
157
+ The Python SDK has no `AsyncRagie` class. Use `async with Ragie(...) as client:` and call the `_async`-suffixed method variants.
158
+
159
+ ```python
160
+ import asyncio
161
+ import os
162
+ import ragie
163
+ from ragie import Ragie
164
+
165
+ async def bulk_ingest(urls: list[str], partition: str):
166
+ async with Ragie(auth=os.environ["RAGIE_API_KEY"]) as client:
167
+ tasks = [
168
+ client.documents.create_document_from_url_async(
169
+ request=ragie.CreateDocumentFromURLParams(url=url, partition=partition)
170
+ )
171
+ for url in urls
172
+ ]
173
+ return await asyncio.gather(*tasks)
174
+ ```
175
+
176
+ ## RAG Response
177
+
178
+ ```python
179
+ import anthropic
180
+ import ragie
181
+
182
+ ragie_client = Ragie(auth=os.environ["RAGIE_API_KEY"])
183
+ claude = anthropic.Anthropic()
184
+
185
+ def answer(question: str) -> str:
186
+ chunks = ragie_client.retrievals.retrieve(
187
+ request=ragie.RetrieveParams(query=question, rerank=True, top_k=6)
188
+ )
189
+ context = "\n\n".join(c.text for c in chunks.scored_chunks)
190
+ msg = claude.messages.create(
191
+ model="claude-sonnet-4-6",
192
+ max_tokens=1024,
193
+ messages=[{"role": "user", "content": f"Context:\n{context}\n\nQuestion: {question}"}],
194
+ )
195
+ return msg.content[0].text
196
+
197
+ def stream_answer(question: str) -> None:
198
+ chunks = ragie_client.retrievals.retrieve(
199
+ request=ragie.RetrieveParams(query=question, rerank=True, top_k=6)
200
+ )
201
+ context = "\n\n".join(c.text for c in chunks.scored_chunks)
202
+ with claude.messages.stream(
203
+ model="claude-sonnet-4-6",
204
+ max_tokens=1024,
205
+ messages=[{"role": "user", "content": f"Context:\n{context}\n\nQuestion: {question}"}],
206
+ ) as stream:
207
+ for text in stream.text_stream:
208
+ print(text, end="", flush=True)
209
+ ```
210
+
211
+ ## Naming Differences vs TypeScript
212
+
213
+ | TypeScript | Python |
214
+ |------------|--------|
215
+ | `createDocumentFromUrl()` | `create_document_from_url()` |
216
+ | `createRaw()` | `create_raw()` |
217
+ | `topK` | `top_k` |
218
+ | `scoredChunks` | `scored_chunks` |
219
+ | `documentId` | `document_id` |
220
+ | `documentName` | `document_name` |
221
+ | `contentType` | `content_type` |
222
+
223
+ ## Gotchas
224
+
225
+ - **`ragie.File`, not `ragie.FileUpload`** — the file wrapper class is `ragie.File`. `FileUpload` does not exist.
226
+ - **`ragie.ListDocumentsRequest`, not `ragie.ListDocumentsParams`** — always use the `Request` suffix for list operations.
227
+ - **Prefer `create_raw()` for in-memory data** — it's simpler when you already have a string or dict. **Prefer `create()` for file uploads** — it supports all file types. `create_raw()` only handles text and JSON; binary files (PDF, DOCX, etc.) must use `create()` with `ragie.File`.
228
+ - **`documents.list()` response requires `.result.documents`** — `.result` is a `DocumentList` object, not a list. Access `.result.documents` to get the actual `List[Document]`. Iterating `.result` directly yields Pydantic field tuples, not documents.
229
+ - **`documents.get()` returns `DocumentGet`, not `Document`** — these are distinct types. Import `from ragie.models import DocumentGet` and annotate accordingly. Do not assign a `DocumentGet` to a variable typed as `Document`.
230
+ - **Pagination via `.next()`** — call `page.next()` to get the next `ListDocumentsResponse`, or `None` if there are no more pages.
231
+ - **Keyword-only arguments** — `delete`, `patch_metadata`, and similar methods use keyword-only args (`*` in signature). Always pass `document_id=doc_id`, never positionally.
232
+ - **No `AsyncRagie` class** — there is only `Ragie`. For async usage, open it as a context manager (`async with Ragie(...) as client:`) and call `_async`-suffixed methods: `create_async()`, `create_document_from_url_async()`, `create_raw_async()`, etc.
@@ -0,0 +1,69 @@
1
+ # Ragie Quickstart
2
+
3
+ > Python user? See `references/python.md` for Python equivalents.
4
+
5
+ ## Get an API Key
6
+
7
+ Sign up at [ragie.ai](https://ragie.ai) and copy the API key from the dashboard.
8
+
9
+ ## Install the SDK
10
+
11
+ ```bash
12
+ npm install ragie
13
+ ```
14
+
15
+ ## Ingest a Document
16
+
17
+ ```typescript
18
+ import { Ragie } from "ragie";
19
+ import { openAsBlob } from "fs";
20
+
21
+ const client = new Ragie({ auth: process.env.RAGIE_API_KEY });
22
+
23
+ // From a file (supports all file types: PDF, DOCX, images, …)
24
+ const doc = await client.documents.create({
25
+ file: await openAsBlob("report.pdf"),
26
+ name: "report.pdf",
27
+ });
28
+
29
+ // From in-memory data (preferred when data is already a string/object)
30
+ const doc2 = await client.documents.createRaw({
31
+ data: "Your text content here...",
32
+ name: "my-note",
33
+ });
34
+
35
+ console.log(doc.id, doc.status); // status: "pending" → "ready"
36
+ // Use createRaw() for in-memory text/JSON; use create() for file uploads (all file types supported)
37
+ ```
38
+
39
+ See `ingestion.md` for URL ingestion, polling, webhooks, and bulk patterns.
40
+
41
+ ## Retrieve (Search)
42
+
43
+ ```typescript
44
+ const results = await client.retrievals.retrieve({
45
+ query: "What are the key findings?",
46
+ rerank: true,
47
+ });
48
+ for (const chunk of results.scoredChunks) {
49
+ console.log(chunk.text, chunk.score);
50
+ }
51
+ ```
52
+
53
+ ## Environment Setup
54
+
55
+ Always load the API key from the environment:
56
+
57
+ ```bash
58
+ export RAGIE_API_KEY=ragie_...
59
+ ```
60
+
61
+ ```typescript
62
+ import { Ragie } from "ragie";
63
+ const client = new Ragie({ auth: process.env.RAGIE_API_KEY });
64
+ ```
65
+
66
+ ## Gotchas
67
+
68
+ - Documents process **asynchronously** — `status` starts as `pending`, transitions to `ready`. Don't query before it's ready. See `ingestion.md` for polling/webhook patterns.
69
+ - `rerank: true` significantly improves result quality. Always enable it for generation use cases unless latency is critical.