# Reported issues for akb

Pod holds 13 of 13 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [akb](/mcp/akb).

## Most discussed

### Extend AKB table DDL tools with declarative unique keys, indexes, and check constraints

## Background

AKB currently keeps schema changes behind dedicated DDL tools:

- `akb_create_table`
- `akb_alter_table`
- `akb_drop_table`

This boundary should remain. `akb_sql` should stay DML/query-oriented and should not become an arbitrary DDL execution surface.

However, the current DDL tools only expose basic column operations. They do not allow callers to define common database-level invariants such as uniqueness, lookup indexes, or simple check constraints. This makes clients implement 

[Read the thread](https://github.com/dnotitia/akb/issues/215) · 2026-06-16 · closed · 2 comments

### Extend AKB table DDL tools with declarative check_constraints (#215 follow-up, PR 2 of 2)

## Summary

Follow-up to #215. The declarative `unique_keys` + `indexes` half shipped in #220 (PR 1 of 2). This issue tracks the remaining **`check_constraints`** (PR 2 of 2) — split out deliberately because it is the raw-SQL-injection-heaviest surface and warrants its own focused review.

## Scope (from #215)

Add declarative `check_constraints` to `akb_create_table` (create-time) and `akb_alter_table` (`add_check_constraints` / `drop_check_constraints`). **Declarative rule DSL — NOT raw SQL.**

[Read the thread](https://github.com/dnotitia/akb/issues/223) · 2026-06-17 · closed · 1 comment

### akb_search에서 특정 리소스 URI 목록으로 검색 범위 제한 지원

## 요약

`akb_search`에서 특정 AKB 리소스 URI 목록으로 검색 범위를 제한할 수 있으면 좋겠습니다.

현재는 `vault`, `collection`, `type`, `tags`, `include_archived` 기준으로만 검색 범위를 좁힐 수 있고, 이미 알고 있는 특정 문서/리소스 집합 안에서만 하이브리드 검색을 수행하는 방법이 없습니다.

## 현재 동작

`akb_search`는 검색 결과로 canonical `akb://...` URI를 반환하지만, 검색 입력에서는 URI 목록을 검색 범위로 받을 수 없습니다.

따라서 특정 리소스 집합 안에서만 검색하려면 각 URI를 개별적으로 읽은 뒤 호출하는 쪽에서 직접 처리해야 합니다. 이 방식은 AKB의 chunk-level retrieval, BM25/dense fusion, ranking, score를 활용하지 못합니다.

## 원하는 동작

`akb_search`가 선택적으로 URI 기반 검색 범위 필터를 받

[Read the thread](https://github.com/dnotitia/akb/issues/159) · 2026-06-08 · closed · external user · 1 comment

### Backend image does not install application package for isolated CLI use

The backend Dockerfile installs the project before copying the declared Hatch packages into the image layer:

1. `COPY pyproject.toml .`
2. `RUN pip install --no-cache-dir .`
3. `COPY . .`

This installs dependencies and distribution metadata, but the application packages are absent when the wheel is built. The resulting image works for the default server command only because `WORKDIR` is `/app` and the later source copy is importable from the current directory. An isolated invocation such as `p

[Read the thread](https://github.com/dnotitia/akb/issues/422) · 2026-08-26 · closed · 0 comments

### grep replace rewrites documents outside the requested collection (unanchored, unescaped LIKE)

## Summary

`grep(replace=…)` rewrites documents the caller never scoped. The `collection` filter is compiled as an unanchored, unescaped `LIKE`, so a rewrite scoped to one collection also rewrites every sibling collection whose path shares that prefix — each with its own git commit and re-index.

This fires on ordinary values, not adversarial ones: `collection='core'` also matches `core-extra/…`.

## Where

`backend/app/services/search_service.py:1170` (and the sibling at `:461`):

```sql
d.pat

[Read the thread](https://github.com/dnotitia/akb/issues/338) · 2026-08-07 · open · 0 comments

### akb_grep(replace=…) rewrites only the first `limit` matching documents, contradicting the documented "every matching document" behaviour

## Summary

A find-and-replace across a scope with more matches than `limit` silently rewrites only the first `limit` documents and commits them, while the tool schema and help promise it rewrites *every* matching document. `limit` is documented as an output-only knob and is hard-capped at 50.

## Where

`SearchService.grep` computes `matched_docs` (all matches) then slices `result_docs = matched_docs[:limit]` (`backend/app/services/search_service.py:995-998`); the replace loop iterates only `re

[Read the thread](https://github.com/dnotitia/akb/issues/315) · 2026-07-28 · closed · 0 comments

### bug(mcp): access-gated tools return ForbiddenError as code=internal instead of permission_denied

## Summary

A non-admin (or otherwise unauthorized) caller hitting any **access-gated MCP tool** is correctly *rejected*, but the failure is surfaced under the generic **`code=internal`** envelope instead of a stable permission code (e.g. `permission_denied` / HTTP 403). Clients can't reliably distinguish "you lack the role" (a 4xx the caller should handle) from "the server crashed" (a 5xx).

## Impact

- Misleading error taxonomy for every admin/writer-gated MCP tool: `akb_alter_table`, `akb_dr

[Read the thread](https://github.com/dnotitia/akb/issues/221) · 2026-06-17 · closed · 0 comments

### Add a file replace/update API with an optimistic-concurrency precondition (expected hash/version)

## Current

File tools are create / read / delete only: `akb_put_file`, `akb_get_file`, `akb_delete_file`. There is no file update/replace operation, and no optimistic-concurrency precondition for files.

Documents already have both: `akb_update` accepts `expected_content_hash` and `expected_commit` and rejects with 409 when the current state has moved.

So files are the missing half of the symmetric document/file contract — a consumer can protect a document against out-of-band edits, but cannot

[Read the thread](https://github.com/dnotitia/akb/issues/177) · 2026-06-09 · closed · 0 comments

## Most recent

### akb_put: support an optional slug to set the document path/uri filename over MCP

## Current

`akb_put` (MCP) derives the document path filename from the title: `file_path = {collection}/{slugify(title)}.md`. An MCP caller cannot set the path filename independently of the title — passing `slug` is rejected outright:

    {"error": "Unknown argument 'slug' for akb_put", "code": "unknown_argument"}

The capability already exists below the MCP layer. `DocumentPutRequest` (`backend/app/models/document.py`) has a `slug` field, and `document_service` honors it:

    slug = (req.slu

[Read the thread](https://github.com/dnotitia/akb/issues/176) · 2026-06-09 · closed · 0 comments

### akb_browse: vault-root response emits an empty-name collection marker

## Observed

\`akb_browse(vault="kisa-poc-2")\` returns, as its first item:

\`\`\`json
{ "type": "collection", "name": "", "path": "", "uri": null }
\`\`\`

It's not a real collection — \`path\` and \`name\` are both empty, \`uri\` is null, clicking it leads nowhere meaningful.

## Why it matters

Every browse-rendering client has to special-case this row out of the response, otherwise it renders as an empty folder at the top of the vault listing (which is what was happening in our AKB Browser 

[Read the thread](https://github.com/dnotitia/akb/issues/82) · 2026-05-26 · closed · 0 comments

### akb_browse: vault-root response hides root-level documents (and depth=2 over-broadens)

## Observed (verified live on \`kisa-poc-2\`)

\`akb_browse(vault="kisa-poc-2")\` — i.e. the default \`depth=1\`:

| | total | types |
|---|---|---|
| default | 46 | collection: 5, file: 41 |
| \`depth=2\` | **88** | collection: 5, **document: 42**, file: 41 |
| \`content_type="documents"\` | 5 | collection-only (collections that *contain* documents — not the documents themselves) |

The vault holds 8 items at its root (5 collections + 3 root-level \`*.md\` documents) and 43 inside sub-collectio

[Read the thread](https://github.com/dnotitia/akb/issues/81) · 2026-05-26 · closed · 0 comments

### search_service.search lacks the same defense-in-depth user_id guard as .grep

## Summary

`SearchService.grep()` raises \`ValidationError(\"vault or user_id required\")\` when both \`vault\` and \`user_id\` are None (search_service.py:520), so a caller that forgets to forward \`user_id\` fails closed. **`SearchService.search()` has no equivalent guard.**

PR #67 (which closed #66) makes the MCP path always forward \`user_id\` from the handler, so the leak is gone today. But the service-layer is now fragile to the **next** caller — a new REST endpoint, an internal worker, 

[Read the thread](https://github.com/dnotitia/akb/issues/70) · 2026-05-21 · closed · 0 comments

### MCP akb_search omits user_id / vault read check, risking unauthorized search results

## Summary
The MCP `akb_search` handler currently calls `SearchService.search()` without passing the authenticated `uid`, and it also does not perform a vault-level read check when a `vault` argument is supplied.

## Evidence
In `backend/mcp_server/server.py`:

```py
@_h("akb_search")
async def _handle_search(args: dict, uid: str, user: _MCPUser) -> dict:
    result = await search_service.search(
        query=args["query"],
        vault=args.get("vault"),
        collection=args.get("collectio

[Read the thread](https://github.com/dnotitia/akb/issues/66) · 2026-05-21 · closed · outside contributor · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/dnotitia/akb/issues).
