Free MCP tools reference

Auto-generated from the real MCP tool registry — the exact tools/list response a free-tier client receives (silex-archaeology 0.12.12). 14 tools. Drift between this page and the registry fails the site build (--check).

create_workspace

Create a workspace and trigger the ingestion scan in background.

Quando usar: primeiro passo pra analisar um repositório novo (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: get_workspace_stats (poll até scan_status=completed); se não souber o workspace_id, list_workspaces primeiro.

Two modes, both returning immediately after the workspace is registered (BEFORE scan completes). In both cases the scan (chunking, Neo4j/SQLite mirror, Qdrant, communities, semantic labeling) runs in a daemon thread — check progress with get_workspace_stats(workspace_id).

FRESH-FREE-1 (#1737): terminado o scan, o índice ACOMPANHA os seus commits sem você instalar nada — cada chamada de tool compara o HEAD que o índice cobre com o HEAD atual (mais o git status) e re-ingere só os arquivos tocados; toda resposta carrega index_age dizendo quantos commits o repo andou desde o scan e se aquela chamada refrescou. O hook git (silex-fresh-hook-install) continua valendo como OTIMIZAÇÃO (deixa o índice pronto no próprio commit, poupando o diff na chamada), nunca como requisito; rescan_workspace(workspace_id) é o caminho manual.

Mode git (default) — multi-repo CLONE: Clones each repo (reusing existing clones for the same URL+branch) and registers one Repo per spec. Wraps the same engine as POST /api/workspaces.

Mode local (EXT-ZEROCONF-2 #1040) — attach IN-PLACE, no clone/copy: Registers local_path as the workspace source via create_workspace_with_source and scans the directory WHERE IT IS (zero clone, zero copy). This is the Jornada B path: the "Analisar este repositório" button in the Extension analyses the repo the user already has open. repos is IGNORED in this mode.

Parameter Type Default Required
name string — yes
repos array | null None no
description null | string None no
source_type string 'git' no
local_path null | string None no
include_ignored boolean False no
install_claude_read_hook boolean False no

Example call:

{
  "tool": "create_workspace",
  "arguments": {
    "name": "<name>"
  }
}

generate_domain_report

Generate a human-readable Markdown domain report for sharing with QA, functional analysts and tech leads.

Quando usar: fechamento do fluxo de exploração — consolidar entidades/rules/FSMs/workflows num relatório compartilhável (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: nenhum obrigatório; para enriquecer com LLM primeiro, confira silex.llm_provider.status e rode de novo com enrich_with_llm=True.

Runs the full extraction pipeline, then renders a report with: - Summary (entities, rules, state machines, workflows) - Entity table with fields, relationships, PKs - State machine diagrams with transitions - Business rules table (with 🔧 heuristic / 🤖 llm badges) - Enums table - Workflow summaries - Gap analysis with severity emojis - Recommended next steps

Parameter Type Default Required
workspace_id string — yes
domain_id string 'auto' no
output_dir string '' no
enrich_with_llm boolean False no
llm_provider string 'azure' no
language string 'en' no
force_inline boolean False no

Example call:

{
  "tool": "generate_domain_report",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

get_code_communities

Hierarchical code communities (Leiden) with members + labels — tier-aware, filtrável por arquivo e com orçamento.

Quando usar: descobrir A COMUNIDADE DE UM ARQUIVO antes de ler o código — get_code_communities(workspace_id, member_filter="Api/services/x.py") devolve, numa chamada barata, a comunidade que contém o arquivo e o caminho dela até a raiz (ancestors); o id que volta é o community_id do silex.community.code_pack. Sem member_filter, é o mapa navegável do codebase depois do scan (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: silex.community.code_pack (código da comunidade) ou get_structural_findings (achados sem LLM).

Orçamento por default (COMMUNITIES-PAGE-1 #1690): o mapa inteiro do workspace do próprio Silex mede 1.183.055 chars — retorno que o cliente MCP recusa. Agora a resposta respeita max_total_chars (default ~40k, mesma folga do code_pack) medido no envelope como você o recebe: se não couber, vem truncated: true com next_cursor opaco e continuation_hint — chame de novo com cursor=<next_cursor> (MESMOS parâmetros) até não vir mais cursor; a união das páginas reconstrói o mapa completo, sem perda nem duplicata. Comunidade grande demais pra uma página só pagina por dentro: members parcial vem com members_truncated: true e members_total. max_total_chars=0 devolve tudo de uma vez (ilimitado) — e sem member_filter/level o campo communities é byte-idêntico à lista histórica desta tool. O index_age anexado entra na conta do orçamento (reservado antes da paginação — FRESH-FREE-2 #1748).

member_filter: arquivo(s) ou membro(s) — um valor, lista JSON ou separados por vírgula. Casa por igualdade case-insensitive OU por sufixo com separador (o mirror guarda path absoluto, você informa o relativo — /, \, caixa e prefixo .//.\ são normalizados dos dois lados antes de comparar, PACK-FILTER-WINPATH-1 #1722/PACK-FILTER-WINPATH-2 #1727: relativo, relativo com \, relativo com .//.\ inicial — o que o tab-complete do PowerShell produz — e absoluto casam o MESMO membro; / inicial e ../ NÃO são tratados como ./), a mesma tolerância do code_pack, em QUALQUER nível (prefere o nível 0, o escopo mais coeso). Valor sem casamento sai em filter_unmatched com lista vazia — ausência honesta, não erro. Nome puro (sem separador) que casa mais de um ARQUIVO distinto sai em filter_ambiguous ([{"value": ..., "matched": [...]}]) — as comunidades dos DOIS (ou mais) arquivos são servidas, nunca uma escolhida a dedo em silêncio; dois membros do MESMO arquivo não são ambíguos entre si, e a contagem é a MESMA do code_pack (#1727). level: -1 (default) = todos; 1 = super-comunidades, 0 = subs.

J1-MAPA-1 (#1077): fonte do MAPA navegável do Domain Explorer no free-tier. Delega ao leitor canônico único fetch_communities_with_members (DOGFOOD-2 #678), que resolve o tier: no free lê .silex/workspace.db (SQLite), no pro/enterprise lê Postgres — SHAPE IDÊNTICO nos dois. Cada comunidade preserva a forma que generate_domain_jsons anexa no campo communities: id, label, level, parent_id, algorithm, cohesion, bridge_nodes, members (+ member_count e, com filtro, ancestors). Ordem estável (supers por size desc; cada sub logo após o pai). communities: [] quando o workspace não tem community detection rodada — o consumidor trata como "sem mapa" (orientação #1058), nunca mapa fantasma. Schema community_map/v1.

Parameter Type Default Required
workspace_id string — yes
member_filter string '' no
level integer -1 no
max_total_chars integer 40000 no
cursor string '' no

Example call:

{
  "tool": "get_code_communities",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

get_rules_by_ids

Batch-fetch rules from the materialized business_logic.json.

Quando usar: resolver linked_rule_ids (de um step de workflow ou de silex.business_logic.submit) em rule dicts completos, num round-trip (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: generate_domain_report pra visão consolidada do domínio.

Sprint 2026-04-17. Used by the Workflow Viewer to resolve step.linked_rule_ids into full rule dicts in a single round-trip (avoids N calls to get_operation_detail per workflow).

Parameter Type Default Required
workspace_id string — yes
rule_ids array — yes

Example call:

{
  "tool": "get_rules_by_ids",
  "arguments": {
    "workspace_id": "<workspace_id>",
    "rule_ids": []
  }
}

get_structural_findings

Structural findings — pontes, hubs e áreas órfãs do grafo local (sem LLM).

Quando usar: depois do mapa (get_code_communities), pra achados estruturais objetivos (pontes/hubs/órfãos) sem gastar LLM (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: silex.community.code_pack pra aprofundar numa comunidade específica.

Orçamento por default (FINDINGS-BUDGET-1 #1723): no campo (FIPE, 2026-09-09, repo TS de 325 arquivos) esta tool devolveu 1,86 MB e o cliente MCP recusou. Agora a resposta respeita max_total_chars (default ~40k, mesma folga do code_pack e do mapa) medido no envelope JSON como você o recebe: se não couber, vem truncated: true com next_cursor opaco e continuation_hint — chame de novo com cursor=<next_cursor> (MESMOS parâmetros, inclusive max_total_chars) até não vir mais cursor; a união das páginas reconstrói os achados completos, sem perda nem duplicata. O corte é sempre em fronteira de ITEM (nunca achado pela metade) e percorre as famílias na ordem do envelope. Toda família declara o conjunto inteiro em <familia>_total (nunca omitido, mesmo quando a lista vem parcial ou vazia nesta página) e <familia>_capped diz se a página cortou — nunca truncamento silencioso. Um achado sozinho maior que o orçamento (uma família de clone com 150 membros, p.ex.) vem INTEIRO e a página declara budget_exceeded: true — nunca achado pela metade, nunca página vazia. max_total_chars=0 devolve tudo de uma vez (ilimitado); cursor de outra sequência (outro focus_stale, outro max_total_chars, outro workspace) vira invalid_cursor honesto, nunca página misturada. Schema structural_findings/v1.

Agregados também paginam (FINDINGS-BUDGET-2 #1750): no campo (3º teste, 0.12.4) o portal-relatorios tinha 488 comunidades, e risk_markers.by_community inteiro em toda página passava do orçamento sozinho — a paginação degenerava a 1 item/página. Agora by_community/top_files são posições do MESMO cursor (logo depois dos markers): numa sequência paginada, toda página declara dentro de risk_markers o by_community_total/by_community_capped e o top_files_total/top_files_capped (o agregado da página pode vir parcial ou vazio), e a união das páginas reconstrói os agregados inteiros. Resposta que cabe numa página só (e max_total_chars=0) volta byte-idêntica, sem esses aditivos. O index_age anexado entra na conta do orçamento. Os achados são computados UMA vez por scan e reusados enquanto você pagina (cache por mtime do mirror no tier free: mirror mudou → recomputa) — paginar não custa mais um cômputo inteiro por página.

ACHADOS-1 (#1114): a fonte da seção Achados do Domain Explorer (irmã do mapa J1-MAPA-1 #1077). Fina por cima do service compute_structural_findings, que deriva os três achados dos MESMOS dados do mapa (comunidades de código + arestas code_references) via os leitores canônicos tier-aware — free lê o espelho SQLite, pro/enterprise Postgres/ Neo4j. NÃO recomputa comunidades nem escreve no grafo.

Shape: {workspace_id, bridges, hubs, orphan_areas, hardcoded_secrets, shared_literals, shared_literals_total, shared_literals_capped, clone_families, clone_families_total, clone_families_capped, dead_code_candidates, risk_markers, config_usage, state_contradictions, empty_catches, empty_catches_total, nesting_hotspots, nesting_hotspots_total, oversized_units, oversized_units_total} (com focus_stale=True, adiciona focus_stale_applied/focus_stale_file_count). hardcoded_secrets (SENSOR-SECRET-1 #1200) é aditivo: connection string Password=/Pwd= ou api key/token/secret literal em código de aplicação, com âncora file/line e valor SEMPRE mascarado (masked_value, nunca o segredo em claro). shared_literals (SENSOR-LIT-1 #1201) é aditivo: literal distintivo (≥6 chars, não-rota/SQL/path/formato) compartilhado entre arquivos de ≥2 comunidades ou 2 linguagens vira achado shared_literal com âncoras de TODOS os arquivos envolvidos (padrão golden: FP_events PHP×JS do SuiteCRM) — literal trivial/único/de alta frequência nunca vira achado. Cap de 100 literais (os que cruzam mais comunidades/arquivos primeiro) declarado via shared_literals_total/shared_literals_capped — nunca truncamento silencioso. clone_families (SENSOR-CLONE-1 #1202) é aditivo: bloco de código idêntico (normalizado, janela de ≥5 linhas significativas) repetido em ≥2 arquivos de aplicação vira UMA família clone_family com TODOS os membros ancorados (file/line_start/line_end por membro) — nunca N achados soltos pro mesmo clone. Cap de 100 famílias (as maiores primeiro) declarado via clone_families_total/clone_families_capped — nunca truncamento silencioso. dead_code_candidates (SENSOR-DEAD-1 #1203) é aditivo: símbolo de aplicação (chunk com method) sem referência textual fora da própria definição vira candidato dead_code_candidate com âncora e confidence (alta privado/interno, baixa público/protegido — reflection/DI podem chamar sem referência estática, NUNCA um veredito). Exclusões estruturadas (entry point, handler DOM *_handler_L<linha>, action de *Controller, override/interface, teste/vendor) nunca viram achado — controller action pública NUNCA é flagada (anti-aceite: falso-positivo mata a confiança no achado). risk_markers (SENSOR-MARK-1 #1204) é aditivo: {markers, total, by_community, top_files, top_files_total, top_files_capped} — marcador TODO/HACK/FIXME em contexto de comentário (//, #, /*, <!--, ' só em VB) vira achado marker_kind/file/line/text ancorado, agregado por comunidade e por top arquivos (cap declarado, nunca truncamento silencioso). Marcador dentro de STRING literal (sem delimitador de comentário na linha) nunca conta. age_days sai SEMPRE None aqui — a leitura tier-aware não expõe root de disco resolvível pra git blame; só o extractor de business_logic.rules (pipeline real, com context.workspace_path) calcula a idade, best-effort, nunca quebra sem git. config_usage (SENSOR-CONF-1 #1205) é aditivo: {declared_unused, used_undeclared, diagnostics} — chave declarada em appsettings*.json/web.config sem NENHUMA leitura no código vira declared_unused (âncora na declaração, severidade baixa); chave lida (Configuration["K"]/AppSettings["K"]/GetValue<T>("K")/ GetSection("K")/GetConnectionString("K")) e ausente de TODA config do repo vira used_undeclared (âncora na leitura, severidade média — risco de runtime); declarada E lida não vira achado. Chave passada como variável/concatenação (não literal) nunca vira candidato — sai como diagnóstico dynamic_key (nunca silêncio). Limitação: este consumidor só lê chunks tier-aware (sem disco) — web.config não é chunkeado pelo ingestor hoje, então só surge aqui se presente como chunk; o extractor real (business_logic.rules, com context.workspace_path) lê ambos direto do disco, sem essa limitação. state_contradictions (CONTRA-1 #1221) é aditivo: literal de estado numa condition de regra (ex.: status != 11) cujo campo resolve pra um enum do domínio (ex.: Situacao {1..9}) mas NÃO está entre os valores válidos vira achado state_not_in_domain com âncora dos DOIS lados (client_anchor da condition + domain_anchor da declaração do enum) e mensagem legível. Reusa o parser de enum de numeric_state_catalog (não re-parseia) — enum [Flags]/aberto tem domínio aberto e nunca contradiz; campo que não resolve pra enum conhecido não vira achado (conservador, zero falso positivo); literal dentro dos valores válidos não vira achado. empty_catches/nesting_hotspots/oversized_units (SILEX-GUARD-1 #1537) são aditivos — erros recorrentes de LLM que hoje só aparecem no gate do Sonar, ancorados file/line: catch/except vazio ou genérico-sem-tratamento (empty|generic_only; catch com log/throw/rethrow nunca conta); aninhamento por método acima do limiar (default 5, threshold sempre explícito no finding); arquivo/classe acima do limiar de LOC (defaults 400/200, derivados das âncoras dos chunks). Positioning: o agente entrega → o guardrail (Silex) valida → o Sonar confirma. config_usage também declara o corte de CADA sub-lista (declared_unused_total/used_undeclared_total/diagnostics_total) e risk_markers mantém total/top_files_total do conjunto COMPLETO mesmo quando os markers vêm paginados (by_community/ top_files paginam com _total/_capped próprios — #1750). focus_stale (SILEX-GUARD-1 #1537, integração FRESH-1 #1535) é o modo delta: com focus_stale=True, TODOS os kinds ancorados por arquivo são filtrados à interseção com o conjunto stale corrente do mirror (invalidate_paths) — responde "o que ESTE change trouxe", não "o que o codebase inteiro tem". Mirror ausente/sem stale devolve tudo, NUNCA erro. Default False preserva o contrato byte-idêntico. Grafo/chunks sem dado → os grupos vêm vazios (o consumidor renderiza o estado honesto, nunca achado inventado).

Parameter Type Default Required
workspace_id string — yes
focus_stale boolean False no
max_total_chars integer 40000 no
cursor string '' no

Example call:

{
  "tool": "get_structural_findings",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

get_workspace_stats

Get overview stats for a workspace: classes, edges, communities, languages.

language (SCAN-IGNORE-1 #1329, mesmo contrato stateless de generate_domain_report): idioma da linha honesta vendor_generated — "en" (default) ou "pt-br". Idioma desconhecido devolve erro estruturado (normalize_language), nunca fallback silencioso.

Quando usar: poll depois de create_workspace até scan_status virar completed (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: get_code_communities (mapa) quando o scan terminar.

Tier-aware (STATS-TIER-1 #1057). Este é o elo do poll do botão de 1 clique: o free-tier não tem Neo4j instalado, então o ramo pago (que consulta o grafo) importava neo4j incondicionalmente e o poll morria com No module named 'neo4j' mesmo com o scan já completo.

O shape de resposta é IDÊNTICO entre tiers: {workspace_id, classes, edges, communities, languages, scan_status, heartbeat_age_seconds, stale_file_count, last_invalidation_at, scan_phase, extraction_status, vendor_generated_excluded, ignored, purged, next_action}.

ignored (SCAN-GITIGNORE-1 #1724): {count, by_rule, sample, disabled, git_available, fallback, warnings, traversed} — contagem honesta de TUDO que o scan pulou, nunca silêncio. by_rule quebra por origem: root_gitignore/nested_gitignore/info_exclude/global_excludes (o git é a autoridade quando o diretório é um checkout git — .gitignore da raiz E aninhado, .git/info/exclude, core.excludesFile; sem git, PathSpec só da raiz), builtin_dirs (poda estrutural: node_modules, dist, venv, obj…), self_artifact (.silex//.claude/ — depósito do próprio Silex), vendor (vendor/generated/minified), submodule, nested_git, fallback e unknown. sample traz até 10 paths.

disabled: true quando o workspace foi criado com include_ignored=True (opt-out): vale SÓ pro mecanismo de gitignore — os filtros incondicionais (self_artifact, builtin_dirs) continuam podando e contando, então count segue maior que zero. warnings/ fallback declaram degradação (git indisponível; raiz do workspace dentro de subárvore que o repo pai ignora por inteiro) e traversed lista o que entrou no mirror por FORA da autoridade do git (sub-repo aninhado percorrido). None até o primeiro scan pós-#1724 completar.

purged (RESCAN-PURGE-1 #1745): {files, chunks, references} — o que o ÚLTIMO scan completo tirou do mirror: arquivo apagado/renomeado ou que a travessia atual não visita mais (.gitignore novo, filtro), com as arestas dele. Comparação de path normalizada (/ × \, caixa da unidade no Windows); o que é gravado não muda. null quando o último completo não rodou o purge (tier pesado, 0 chunks) ou antes deste campo existir. Nunca silêncio sobre o que SAIU do índice.

J1-DONE-1 (#1069): scan_status (registry de workspaces, mesma coluna nos dois tiers — scanning/completed/error/pending) é o sinal de "pipeline COMPLETO" que o poll do botão de 1 clique passou a exigir; ``classes

0`` só prova o fim do chunking (existe minutos antes de linking/Leiden/ comunidades). Campo aditivo — leitor antigo ignora.

J1-RESCAN-1 (#1071): heartbeat_age_seconds (segundos desde o updated_at do registry, null quando indisponível) expõe a idade do heartbeat da #1072. É a fonte que o botão usa pra distinguir scanning vivo (idade pequena) de scanning zumbi (idade estagnada — só sobra em kill -9), sem hardcodar heurística sem lastro no registry. Campo aditivo.

SCAN-PROGRESS-1 (#1172): scan_phase responde ONDE o scan está — não só vivo×travado (isso já era scan_status+heartbeat_age_seconds). Mother-smoke rodada 2 (2026-07-08): braço B esperou ~35min com o stats "congelado" (embedding em CPU) sem saber em qual etapa. Shape: {stage, label, index, total, result} — stage/index/total do vocabulário e catálogo REAIS do motor (Api.ingestao.pipeline. STAGE_LABELS/STAGES, 1-based); result traz os contadores da fase CONCLUÍDA (null enquanto ela está em andamento). null até a 1ª transição de fase de um scan, ou em registries anteriores a esta coluna. Campo aditivo.

SILEX-EXTRACT-PHASE-1 (#1377): extraction_status fecha a ponta que a SCAN-PROGRESS-1 (#1172) deixou declarada — depois do scan completo (fase 7/7 semantic_labeling), os extractors rodam em background e o stats congelava em "7/7" enquanto o produto ainda trabalhava. Campo ADITIVO ("running"/"done"/"error"; null = workspace que nunca rodou extractors, ou registry anterior à coluna) derivado dos eventos de extraction que o trigger_extractors JÁ emite — nunca um segundo catálogo de fases: scan_phase permanece EXATAMENTE as 7 fases do pipeline (contrato #1172/j2-21). Com scan_phase 7/7, é este campo que distingue "parado" de "trabalho em voo". Campo aditivo.

FRESH-FREE-3 (#1747): esta chamada é SEM escopo (não recebe member_filter), então index_age.touched_in_scope lista os arquivos de CÓDIGO (filtrados por EXTENSION_MAP, nunca binário sem parser pdf/docx/xlsx) que diferem do último scan no workspace INTEIRO — mas é uma LISTA truncada a MAX_TOUCHED_IN_SCOPE (20) paths, enquanto index_age.touched_total é o NÚMERO completo e nunca trunca: os dois só coincidem quando cabem no teto (FRESH-FREE-2 #1748). touched_total é a mesma contagem que code_pack/entity.pack reportam pro MESMO workspace, mesmo quando o escopo deles é mais estreito — ver a nota em silex.community.code_pack.

Parameter Type Default Required
workspace_id string — yes
language string 'en' no

Example call:

{
  "tool": "get_workspace_stats",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

list_workspaces

List all registered workspaces, most recently updated first.

Quando usar: descobrir workspaces já existentes antes de repetir create_workspace (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: get_workspace_stats(workspace_id) com o id escolhido.

Reads the local registry in-process (same tier-aware path as create_workspace — no HTTP, no running API required). Read-only — no side effects. Use this before create_workspace or any workspace-scoped tool to let the caller pick an existing workspace by name instead of requiring a raw UUID.

No parameters.

Example call:

{
  "tool": "list_workspaces",
  "arguments": {}
}

rescan_workspace

Atualiza o índice do workspace com o que mudou no código — sem re-escanear tudo.

Quando usar: o mapa/pack respondeu com index_age.commits_behind > 0 (ou com um aviso de índice sem base), você acabou de commitar/trocar de branch, ou quer forçar a atualização antes de ler o código (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: silex.community.code_pack / get_workspace_stats — a resposta já vem do índice novo.

FRESH-FREE-1 (#1737): o default é INCREMENTAL. Compara o HEAD que o índice cobre (scanned_head) com o HEAD atual — git diff --name-status <scanned_head>..HEAD + git status --porcelain (não commitado e não rastreado) — e re-ingere SÓ os arquivos tocados, sincronamente (o campo mediu 93 s de rescan completo contra dezenas de ms do incremental). Arquivo que o scan ignora (.silex/, vendor/generated) nunca entra. No caminho incremental o scan_status do workspace não é tocado: ele não é um scan novo, é o índice acompanhando o repo.

Cai no rescan COMPLETO (comportamento histórico, thread em background) quando não há como fazer incremental honesto: full=True explícito, repo sem git, workspace escaneado antes desta coluna existir (sem scanned_head — o completo estabelece a base), ÍNDICE NÃO SAUDÁVEL (scan_status diferente de completed, ou completo sem comunidades), ou DELTA GRANDE (mais arquivos tocados do que o refresh por chamada ingere sem segurar a resposta — canonical_fresh.MAX_DELTA_FILES). Adivinhar que o índice está fresco seria pior que o silêncio que esta tool existe pra matar — e um delta aplicado sobre índice quebrado nunca o conserta (é o caminho de recuperação da #1071, preservado aqui).

Recupera também o que o diff NÃO enxerga: path marcado stale que ninguém re-ingeriu (por invalidate_paths, ou por um hook cujo rescan parcial estourou o timeout) entra no trabalho desta chamada mesmo com o git em dia — antes, o incremental respondia completed sem ingerir nada. Stale pendente acima do teto cai no completo, como o delta grande; o que sobrar volta declarado em stale_file_count com next_action pro full.

J1-RESCAN-1 (#1071): o caminho completo recupera workspace com scan incompleto (morreu no meio → error; completou sem comunidades; ou scanning zumbi — heartbeat estagnado só sobra em kill -9) SEM criar nada novo: re-roda o MESMO run_scan_pipeline (via _run_scan_tracked, com a morte-honesta e o heartbeat da #1072) sobre os Repo que já existem. NÃO chama create_workspace — nenhuma linha nova em workspaces nem em repos.

Recusa (idempotente, sem empilhar scans — anti-loop) quando já há um scan em voo SAUDÁVEL: scan_status='scanning' com heartbeat fresco (heartbeat_age_seconds <= SCAN_STALE_THRESHOLD_S). O zumbi (heartbeat estagnado) NÃO é saudável — esse re-dispara.

SILEX-FRESH-1 (#1535): paths (opcional) engata o rescan PARCIAL — re-ingest apenas da interseção stale ∩ paths no mirror, sem churn global (chunks intocados mantêm id). Tem precedência sobre o incremental.

FRESH-FREE-3 (#1747): index_age.touched_total (dentro do bloco que esta tool também carrega) é a contagem do workspace INTEIRO — arquivos de código (EXTENSION_MAP) que diferem do último scan, nunca um binário sem parser (pdf/docx/xlsx). No modo incremental, o refresh desta CHAMADA AVANÇA a base (scanned_head) e ingere o delta; touched_total e touched_in_scope continuam declarando o que difere da base NOVA — o que segue tocado e ainda não commitado (working tree sujo) reaparece IDÊNTICO na chamada seguinte, sem que o refresh desta chamada desligue esse sinal (touched ESTÁVEL, FRESH-FREE-2 #1748) — ver touched_total × touched_in_scope em Api.services.canonical_fresh (docstring do módulo).

Parameter Type Default Required
workspace_id string — yes
paths array | null None no
full boolean False no

Example call:

{
  "tool": "rescan_workspace",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

silex.business_logic.submit

Persiste rules extraídas pelo LLM do cliente (com provenance) (#675).

Quando usar: depois de silex.community.code_pack — submeter as rules que o LLM do cliente extraiu do código lido (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: silex.contract.fact.generate pra consolidar o escopo, ou generate_domain_report pra visão completa do domínio.

Fecha o loop da arqueologia client-orchestrated (ONBOARD-6): recebe as rules que o LLM do cliente extraiu do code_pack, valida a evidência e as persiste no business_logic.json materializado — shape consumido por silex.contract.fact.generate (passa a montar o fact_contract com rules reais, não fixture).

Trava não-negociável: rule sem evidência é alucinação. Cada rule exige provenance — class_name (símbolo), source_file (arquivo) e line_start ou evidence (localização/trecho). Rule sem isso é rejeitada (erro estruturado) e nada é persistido (atômico). As rules entram como proposed (camada 1.5 — fato candidato, nunca ground-truth silencioso); promover é review humano. Merge idempotente: re-submeter a mesma rule não duplica.

A âncora é VERIFICADA contra o código real (ANCORA-SUBMIT-1 #1684), não só exigida: quando o workspace tem mirror (o workspace.db do scan free-tier), cada rule é conferida contra os chunks daquele workspace — source_file precisa existir, line_start..line_end precisa cair DENTRO de um chunk daquele arquivo (fora do chunk é fora, sem tolerância de "linhas perto") e evidence, quando é a única localização, precisa aparecer no código do chunk (comparação ignora espaços). Códigos de rejeição: anchor_file_not_in_workspace (arquivo desconhecido), anchor_file_ambiguous (caminho relativo que casa com 2+ arquivos — mande o caminho completo), anchor_outside_chunks (a resposta lista as known_ranges do arquivo — método + linhas — pra você corrigir) e evidence_not_in_file. Rule verificada é persistida com provenance.anchor {verified, chunk_id, method, file}. Sem mirror o submit aceita como antes e diz na cara: report.anchor_verification = "skipped_no_mirror" + aviso em warnings — nunca bloqueio, nunca silêncio.

Parameter Type Default Required
workspace_id string — yes
rules_json string — yes
artifact_root string '' no
db_path string '' no

Example call:

{
  "tool": "silex.business_logic.submit",
  "arguments": {
    "workspace_id": "<workspace_id>",
    "rules_json": "<rules_json>"
  }
}

silex.community.code_pack

Devolve o pack de leitura de uma comunidade — código + evidência (#675).

Quando usar: depois de escolher uma comunidade (via get_code_communities/get_structural_findings) pra ler o código-fonte e extrair rules com o LLM do cliente (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: silex.business_logic.submit com as rules extraídas.

Substrato da arqueologia client-orchestrated (ONBOARD-6): o server não roda LLM. Dado um escopo coeso (comunidade/package), esta tool resolve os membros (Postgres) e devolve o código dos chunks (do .silex/workspace.db free-tier, ONBOARD-2) + evidência estrutural (arquivo/classe/método/linhas/labels + arestas :REFERENCES intra-escopo), num formato enxuto pro LLM do cliente (claude-code) interpretar e extrair as rules — que voltam por silex.business_logic.submit.

Camada 1 (fato, fiel ao código). Read-only — não muta nada. Happy path free-tier: zero chave, zero GPU.

Orçamento por default (PACK-BUDGET-1 #1325): comece SEM parâmetros — o pack já respeita um orçamento default (~40k chars) que cabe no teto de resultado do seu cliente MCP. Se houver mais código do que cabe, o retorno vem truncated: true com um next_cursor opaco e uma continuation_hint textual: chame de novo com cursor=<next_cursor> (mesmos parâmetros) pra pegar a próxima página. Pagine até o retorno vir sem next_cursor — a concatenação reconstrói o pack inteiro, byte a byte, sem perda. Corte é SEMPRE em fronteira de chunk (nunca código pela metade). Poder avançado: max_total_chars=0 devolve tudo de uma vez (ilimitado); max_chunk_chars/max_chunks_per_member são knobs finos ortogonais ao orçamento.

O orçamento vale pro ENVELOPE inteiro (PACK-EDGES-BUDGET-1 #1686): as edges e o scope.classes paginam JUNTO com os membros — cada membro carrega as arestas que ele origina e a sua entrada em scope.classes, então uma comunidade grande não estoura mais o teto pelo lado das arestas (medido no próprio Silex: 102.882 chars com max_total_chars=6000). Nada é descartado pra caber: paginar até esgotar reconstrói membros, chunks, arestas e classes sem perda nem duplicata. metrics.edges_total_in_scope é o total do escopo e metrics.edges_in_page o que veio nesta página; scope.class_count continua o total de membros do território.

Alvo alcançável (mesma história): order="relevance" (default) ordena os membros por grau intra-escopo desc (desempate alfabético) — o hub da comunidade vem na página 1 — e, desde a PACK-CHUNK-RANK-1 (#1698), vale também DENTRO de cada membro: os chunks saem por grau do SÍMBOLO desc (in + out nas code_references, desempate por arquivo/linha), então os hubs do arquivo abrem o membro mesmo quando você não sabe o nome de nada (medido no Silex: assemble_code_pack era o 23º dos 29 chunks do seu membro-módulo). Desde a PACK-MEMBER-RANK-1 (#1713) a mesma palavra vale para o símbolo PROMOVIDO a membro próprio (uma função de módulo que o mapa reconhece pelo nome): se o arquivo de origem dele também é membro deste pack, ele não espera mais o membro-módulo inteiro — entra na lista de chunks do módulo, na posição que o grau dele manda, com promoted_member dizendo de qual membro veio (medido no Silex: _community_code_pack, grau 71, saiu da página 17 de 18 para a 1). Ele continua listado em scope.classes, as arestas dele continuam as mesmas (metrics.members_folded conta as dobras), e member_filter pelo nome dele continua levando ao código. order="name" mantém a ordem alfabética entre os membros e a de LINHA dentro deles, sem dobra nenhuma — pra quem quer ler o arquivo de cima a baixo. member_filter aponta direto pro alvo: lista JSON (["Api/services/pedido.py", "Pedido"]) ou valores separados por vírgula, casando por igualdade case-insensitive com o nome do membro OU por sufixo de arquivo ("pedido.py" casa /repo/Api/services/pedido.py) — relativo, relativo com \ (mirror Windows), relativo com prefixo .//.\ (tab-complete do PowerShell) e absoluto casam o MESMO membro (PACK-FILTER-WINPATH-1 #1722/PACK-FILTER-WINPATH-2 #1727: separador, caixa e prefixo relativo normalizados dos dois lados antes de comparar; / inicial e ../ NÃO são tratados como ./). Nome puro (sem separador, ex: "pedido.py") que casa mais de um ARQUIVO distinto do escopo serve OS DOIS (nunca escolhe um a dedo) e DECLARA o casamento múltiplo em filter_ambiguous ([{"value": ..., "matched": [...]}]) — dois membros do MESMO arquivo não são ambíguos entre si; nunca resolvido em silêncio; informe o relativo com subdiretório pra desambiguar. Valor sem casamento algum sai em filter_unmatched; filtro que não casa nada devolve members: [] + gap filter_without_match (ausência honesta, não erro). O cursor é selado com esses parâmetros: reapresentá-lo com order/ member_filter/knobs diferentes devolve invalid_cursor em vez de misturar páginas de sequências distintas.

O SÍMBOLO na página 1 (PACK-SYMBOL-FILTER-1 #1697): o member_filter leva ao arquivo, mas dentro dele os chunks saem em ordem de linha — num arquivo grande o que você nomeou cai na 17ª página. symbol_filter põe o alvo na frente do membro dele, em três formas: (1) nome — "assemble_code_pack", "Pedido" ou qualificado "Pedido.confirmar" (igualdade case-insensitive com o método/classe do chunk; NUNCA substring: "pack" não casa assemble_code_pack); (2) linha — "contract_tools.py:1742" (sufixo de arquivo, mesma tolerância do member_filter, + a linha); (3) faixa — "contract_tools.py:1742-1905" (todo chunk cujo intervalo intersecta a faixa). Nas formas (2) e (3) o <arquivo> casa com a MESMA tolerância do member_filter — separador (/ ou \), caixa e prefixo .//.\ normalizados dos dois lados, só em fronteira de separador (PACK-SYMBOL-RANGE-WINPATH-1 #1867). Vários valores: lista JSON ou separados por vírgula. É REORDENAÇÃO, não recorte: o resto do membro segue atrás e paginar até esgotar continua reconstruindo o pack inteiro. Valor sem casamento sai em symbol_filter_unmatched; nenhum casando vira gap symbol_without_match (ausência honesta, não erro) — sintaxe malformada ("arquivo.py:", faixa invertida) é que vira invalid_arguments. Também faz parte do selo do cursor. Símbolo que está no ARQUIVO filtrado mas cujo nó mora em outra comunidade (promovido pelo mapa) vem servido do mesmo jeito, dobrado no membro-módulo, com o gap symbol_served_from_other_member (ref/member/community_id da origem — null quando o catálogo não resolve) declarando de onde ele veio (#1828); origem em mais de uma comunidade (homônimo) vem community_id/label null + candidates: [{community_id, label, level}] ordenados por community_id — ambiguidade se declara, nunca se resolve por posição de lista (#1843). E o alvo que casou mas não coube nesta página vem como gap symbol_matched_beyond_budget com o next_cursor que o alcança — página sem o alvo nunca é página calada.

Chunk pedido maior que a página (PACK-BIG-CLASS-CHUNK-1 #1861): o chunk que casa o filtro EXPLÍCITO (member_filter resolvido em membro, ou symbol_filter) e cujo CÓDIGO sozinho é maior que o max_total_chars da página não cabe em página nenhuma: ele não é servido (serviria acima do teto) nem cortado pela metade (o corte é sempre em fronteira de chunk) e sai DECLARADO no gap chunk_exceeds_page_budget {member, symbol, chunk_type, line_start, line_end, chunk_chars, page_budget, min_max_total_chars} — uma vez por chunk, na página que abre o membro dele, mesmo quando outros chunks entraram. O continuation_hint nomeia a saída (max_total_chars>=N, estimativa que já inclui o overhead do envelope desta página, ou max_total_chars=0) e metrics.chunks_exceeding_budget conta os declarados. É o oposto do silêncio que havia: member_filter="QueueItem" com max_total_chars=4000 devolvia members: [] + gaps: [], e com 8000 servia to_dict/load engolindo o chunk class de 12.925 chars. Sem filtro nenhum (leitura exploratória) nada muda: a unidade isolada maior que o orçamento segue emitida inteira.

SÓ o trecho (symbol_only, PACK-RANGE-ONLY-1 #1835): symbol_only=true (com symbol_filter) serve APENAS os chunks que casaram — nos membros servidos, com o god symbol inteiro (cabeçalho + blocos), sem o resto do arquivo; nada casou → members: [] + gap symbol_without_match, e metrics.chunks_omitted_by_symbol_only diz quantas views ficaram de fora. Sem o knob nada muda (o symbol_filter prioriza, não recorta); symbol_only=true sem symbol_filter → invalid_arguments. Faz parte do selo do cursor.

Sem package_id/community_id (MCP-INSTRUCTIONS-1 #1689): member_filter sozinho já delimita o escopo — casa os valores contra os membros das comunidades do workspace em QUALQUER nível (preferindo o nível 0; COMMUNITIES-PAGE-1 #1690) e resolve pra UMA delas, com scope.level reportando o nível usado. Sem casamento algum → member_filter_unmatched; casamento espalhado por mais de uma comunidade → member_filter_ambiguous (com candidates — reapresente com package_id/community_id explícito, ou filtre por um arquivo/classe só). Casa por MEMBRO (classe ou módulo-arquivo, MAPA-PROCEDURAL-1 #1677): módulo procedural resolve direto; classe dentro de um arquivo com outras classes pede package_id/community_id explícito (via get_code_communities) ou silex.entity.pack(workspace_id, entity_name).

Truncamento consciente de chunk (PACK-CHUNK-TRUNC-1 #1338): quando max_chunk_chars corta o código de um chunk, o corte recua pra a última quebra de linha completa dentro do limite — nunca no meio de uma linha/método. O chunk truncado vem com truncated: true + chunk_handle opaco + continuation_hint: chame de novo só com chunk_handle=<handle> (mesmos workspace_id/db_path; não precisa repetir package_id/community_id) pra receber o RESTO do código desse chunk, byte a byte, numa resposta chunk_continuation.

FRESH-FREE-3 (#1747): index_age.touched_total (bloco aditivo no envelope) é a contagem do workspace INTEIRO — arquivos de código, filtrados por EXTENSION_MAP (binário sem parser como pdf/docx/ xlsx nunca conta) — NUNCA o escopo desta chamada: um member_filter estreito que não casa com nada tocado ainda assim reporta o total certo, em vez de 0 escondendo que algo mudou em outro lugar do workspace. index_age.touched_in_scope é o subconjunto que casa com os membros DESTE pack, uma LISTA truncada a MAX_TOUCHED_IN_SCOPE (20) paths — touched_total é um NÚMERO e nunca trunca, então os dois só batem quando o total cabe no teto e tudo que mudou está neste pack. O conjunto é estável: um arquivo editado e não commitado segue em touched_in_scope na chamada que o ingere (refreshed_now: true) e nas seguintes (FRESH-FREE-2 #1748). O index_age entra na conta do max_total_chars: é reservado antes da paginação, nunca anexado por fora do orçamento.

Parameter Type Default Required
workspace_id string — yes
package_id string '' no
community_id string '' no
db_path string '' no
max_chunk_chars integer 0 no
max_chunks_per_member integer 0 no
max_total_chars integer 40000 no
cursor string '' no
chunk_handle string '' no
order string 'relevance' no
member_filter string '' no
symbol_filter string '' no
symbol_only boolean False no

Example call:

{
  "tool": "silex.community.code_pack",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

silex.community.label_submit

Persiste nomes de comunidade dados pelo SEU LLM (com proveniência) (#1619).

Quando usar: depois de get_code_communities + silex.community.code_pack — o label heurístico não diz o negócio? Leia o pack, dê o nome e submeta aqui (kit de onboarding — no tier free é o ÚNICO caminho de nomeação: o motor não usa provider próprio, label_communities não se aplica). Próximo passo: get_code_communities de novo pra confirmar o nome na leitura.

Fecha o loop da nomeação client-orchestrated (COMMUNITY-LABEL-CLIENT-1, mesmo padrão do garçom de silex.business_logic.submit): o LLM do cliente lê o código, propõe o nome, o Silex persiste — com trava doutrinária idêntica à de rules: label sem evidência é alucinação. Cada entrada exige provenance — âncora do que foi lido (classes do pack, source_file e/ou evidence). Lote com QUALQUER entrada sem proveniência é rejeitado por inteiro (erro estruturado) e nada é persistido, nem parcialmente. Upsert idempotente por comunidade; fonte registrada client_orchestrated; o nome sobrevive a re-scan via cache lookup-only.

Parameter Type Default Required
workspace_id string — yes
labels_json string — yes
db_path string '' no

Example call:

{
  "tool": "silex.community.label_submit",
  "arguments": {
    "workspace_id": "<workspace_id>",
    "labels_json": "<labels_json>"
  }
}

silex.contract.fact.generate

Gera o contrato de fato (fact_contract/v1) de um escopo coeso.

Quando usar: depois de silex.business_logic.submit (ou de rules heurísticas já materializadas), pra consolidar o escopo num contrato versionado (kit de onboarding — MCP-KIT-1 #1136). Próximo passo: nenhum obrigatório — o fact_contract é o artefato de saída deste ramo do fluxo.

Coleta as rules já extraídas (com provenance de símbolo) de uma comunidade/package materializada e as serializa num artefato versionado com status: proposed. É agregação/serialização sobre o business_logic.json materializado — não nova extração.

Camada 1 (fato, fiel ao código). Propor ledger candidato é MH-3; review/promoção proposed→active é MH-4 — fora desta tool.

Parameter Type Default Required
workspace_id string — yes
package_id string '' no
community_id string '' no
artifact_root string '' no
use_cache boolean True no

Example call:

{
  "tool": "silex.contract.fact.generate",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

silex.entity.pack

Devolve o agregado inteiro (raiz + partes) em 1 chamada (#1141).

Quando usar: pra responder "me explica o agregado X" — a pergunta nº1 da arqueologia. Prefira esta tool ao silex.community.code_pack quando você já sabe o NOME da entidade: o Leiden particiona por acoplamento, não por agregado, então o pack por comunidade quase nunca entrega o agregado inteiro (medido na cobaia eShop: a comunidade da raiz Order tem 7 membros, enquanto OrderItem, Address, Buyer e OrderStatus — o mesmo agregado — moram em 4 comunidades DIFERENTES). Esta tool ATRAVESSA comunidades. Próximo passo: silex.business_logic.submit com as rules extraídas.

Camada 1 (fato, fiel ao código). Read-only — não muta nada. Roda no free-tier puro: só o .silex/workspace.db (sem Postgres/Neo4j).

Raiz determinística, nunca chute silencioso: quando o nome casa com mais de uma classe (ex: o agregado Order de Ordering.Domain × o DTO homônimo do ClientApp), a escolha usa sinais MEDIDOS do free-tier — rules ancoradas, nº de chunks, chars de código — e as candidatas rejeitadas voltam DECLARADAS em candidates com o motivo. Empate em todos os sinais → diagnostic entity_ambiguous com as candidatas, nunca um chute.

Toda âncora existe: todo item traz arquivo:linha de um chunk real. Classe relacionada sem código (tipo de framework) é excluída e contada em metrics.unanchored_refs_excluded — nunca âncora inventada.

Hop-do-repositório + fronteira declarada (PACK-FRONTEIRA-1 #1396): em DDD a coreografia de MUTAÇÃO do agregado vive atrás da interface do repositório (medido no eShop: 15 classes seguram IOrderRepository e ZERO delas aponta pra Order) — o 1-hop puro não a contém. Quando as arestas a 1 hop da raiz contêm interface(s) de repositório (heurística DECLARADA no retorno: sufixo Repository no nome do vizinho, cobrindo o padrão exato I<raiz>Repository), os CALLERS dessa interface entram em mutators_via_repository (top-N por weight, teto = used_by_cap, corte contado em metrics.mutators_via_repository_omitted — nunca silencioso; âncora real obrigatória; quem já tem aresta direta com a raiz fica em uses/used_by, nunca duplicado). É SÓ o hop da interface de repositório — 2-hops genérico continua fora (Pro/Neo4j). E o retorno declara a FRONTEIRA em texto legível (boundary.statement + boundary.not_covered): o que o pack NÃO cobre (mediator/DI sem aresta estrutural, refs sem âncora) — used_by_omitted: 0 deixa de ser o único sinal de completude.

summary_first (default): devolve só o MAPA do agregado (nomes + âncoras + comunidades), barato. Peça o código depois com summary_first=False. O orçamento, o next_cursor e o chunk_handle são os MESMOS do silex.community.code_pack (PACK-BUDGET-1 #1325 / PACK-CHUNK-TRUNC-1 #1338): o payload INTEIRO respeita max_total_chars (default ~40k; 0 = ilimitado) e paginar até esgotar reconstrói o pack byte a byte.

Parameter Type Default Required
workspace_id string — yes
entity_name string '' no
summary_first boolean True no
db_path string '' no
used_by_cap integer 25 no
max_chunk_chars integer 0 no
max_chunks_per_member integer 0 no
max_total_chars integer 40000 no
cursor string '' no
chunk_handle string '' no

Example call:

{
  "tool": "silex.entity.pack",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}

silex.repo.map

Mapa do repositório — ASSINATURAS ranqueadas, nunca corpo (#1769).

Quando usar: antes de pedir código, pra saber ONDE está o que importa pra tarefa — passe em mentions os arquivos/símbolos que a conversa já citou (["Api/fresh/hook.py", "PedidoService"]) e o mapa volta com eles e a vizinhança deles no topo. Próximo passo: silex.community.code_pack(workspace_id, member_filter=[<arquivos do topo do mapa>]) pra ler o código de verdade. member_filter é alias de mentions (o nome que o code_pack usa), mesma forma; com os dois vale a união, mentions primeiro, sem duplicata. Forma inválida (42, item não-string) → invalid_arguments com param.

Cada arquivo traz rank, score, a community de menor nível do seu membro principal (o de mais símbolos no arquivo; null sem detecção) e symbols — chunk_id, name, kind, line_start/line_end, signature e signature_source: metadata (gravada pelo parser na ingestão), ast (projeção tree-sitter do código já no índice — TS/JS/C#) ou unavailable (signature: null; nunca a primeira linha do código fingindo de assinatura).

Ranking: PageRank dirigido ponderado (code_references.weight) sobre os MEMBROS do mapa — a mesma identidade das comunidades e dos achados; nome externo (get/str) não é nó, e o mesmo par gravado com o UUID com e sem hífen conta uma vez (ranking.duplicate_pairs_collapsed). Personalização: damping 0.85, teleporte 50% uniforme + 50% nos membros mencionados (ranking.personalization.applied); sem mentions o ranking é uniforme. Com menção, o que você mencionou é rank 1..k NA ORDEM em que mencionou (ranking.personalization.mentions_pinned) e as arestas incidentes aos mencionados entram com peso ×10, percorríveis nos dois sentidos (mention_edge_boost) — quem chama e quem é chamado pelo mencionado vem antes dos hubs globais. Grafo sem arestas ou sem convergência → ranking.method = "in_degree" com reason, inventário completo (grau zero incluído). Menção sem casamento sai em mentions_unmatched; homônimo — path que casa mais de um arquivo, ou nome que casa símbolos DIFERENTES em arquivos diferentes (main em N módulos) — sai em mentions_ambiguous (matched com os 3 primeiros arquivos + matched_total) e não personaliza nenhum: informe o path com diretório. Classe parcial (Form.cs + Form.Designer.cs) é um símbolo só e personaliza.

Orçamento: max_total_chars (default 8000) vale pro envelope INTEIRO medido como você recebe (index_age e cursor incluídos); símbolos nunca são cortados. Mais do que cabe → truncated: true + next_cursor: chame de novo com cursor=<next_cursor> e as MESMAS mentions (o orçamento pode mudar entre páginas); a união das páginas é o mapa inteiro. 0 = ilimitado. Orçamento menor que o envelope com 1 símbolo (ou que o envelope do workspace sem símbolo) → erro budget_too_small com min_total_chars. Cursor de outro workspace/mentions → invalid_cursor; índice alterado desde a página anterior → snapshot_changed (recomece sem cursor).

Parameter Type Default Required
workspace_id string — yes
mentions array | null | string None no
max_total_chars integer 8000 no
cursor string '' no
member_filter array | null | string None no

Example call:

{
  "tool": "silex.repo.map",
  "arguments": {
    "workspace_id": "<workspace_id>"
  }
}