834 lines
32 KiB
Markdown
834 lines
32 KiB
Markdown
# adapter-1c-mcp
|
|
|
|
Thin MCP proxy for the 1C REST adapter.
|
|
|
|
Target host:
|
|
|
|
```text
|
|
docker.cin.su
|
|
```
|
|
|
|
Runtime URL:
|
|
|
|
```text
|
|
http://docker.cin.su:8021
|
|
```
|
|
|
|
MCP endpoint for Codex:
|
|
|
|
```text
|
|
http://docker.cin.su:8021/mcp
|
|
```
|
|
|
|
The server responds with MCP protocol `2025-06-18` and returns
|
|
`Mcp-Session-Id` during `initialize`.
|
|
|
|
Legacy SSE endpoint:
|
|
|
|
```text
|
|
http://docker.cin.su:8021/sse
|
|
```
|
|
|
|
Direct JSON-RPC endpoint for smoke tests:
|
|
|
|
```text
|
|
http://docker.cin.su:8021/mcp
|
|
```
|
|
|
|
Codex config:
|
|
|
|
```toml
|
|
[mcp_servers.adapter-1c-mcp]
|
|
enabled = true
|
|
url = "http://docker.cin.su:8021/mcp"
|
|
```
|
|
|
|
Already running Codex Desktop windows can keep the MCP tool list from their
|
|
startup time. Restart Codex Desktop, or start a fresh session, after changing
|
|
MCP server configuration.
|
|
|
|
## How The Adapter URL Is Passed
|
|
|
|
The MCP proxy does not hard-code the 1C adapter address. Pass it with:
|
|
|
|
```text
|
|
ONEC_ADAPTER_URL=http://docker-gpu.cin.su:8011
|
|
```
|
|
|
|
Optional adapter bearer token:
|
|
|
|
```text
|
|
ONEC_ADAPTER_TOKEN=
|
|
```
|
|
|
|
Timeout for adapter HTTP calls:
|
|
|
|
```text
|
|
ONEC_ADAPTER_TIMEOUT_SECONDS=4
|
|
```
|
|
|
|
These variables are configured in:
|
|
|
|
```text
|
|
core/deploy/docker/adapter-1c-mcp/.env.example
|
|
```
|
|
|
|
For real deployment, create a non-committed `.env` next to the compose file and set the actual adapter URL/token there.
|
|
|
|
## Deploy
|
|
|
|
Deploy both REST adapter and MCP proxy, then run live verification when a test
|
|
base is available:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context>
|
|
```
|
|
|
|
Multiple test bases can be verified in one deploy:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-1>,<base-id-2>
|
|
```
|
|
|
|
Duplicate base ids are rejected before verification starts, so persisted
|
|
reports are not overwritten by an accidental repeated base value.
|
|
|
|
To make post-deploy checks deterministic, pass an explicit metadata object
|
|
selector. This is optional; without it the smoke discovers a module-capable
|
|
object automatically.
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-ObjectGuid <metadata-object-guid> `
|
|
-ObjectKind <metadata-kind>
|
|
```
|
|
|
|
Saved-state preparation defaults to `ConfigSave` for the base configuration
|
|
save layer. Use `-SavedStateTable ConfigCASSave` when the strict smoke should
|
|
target an extension/CAS save layer instead:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-SavedStateTable ConfigCASSave
|
|
```
|
|
|
|
The deploy script prints Docker container summaries for both services, then the
|
|
verify script prints `/health` status and `contract_version` before running the
|
|
REST and MCP selector-chain smoke tests. The selector-chain smoke also checks,
|
|
when a routine name is available from module metadata, that
|
|
`metadata.resolve_overrides` returns `write_plan_evidence.next_resolution` for
|
|
`metadata.saved_state.modules.search` with the same `base_id` and routine query,
|
|
plus a public object `ref`/`kind`/`name` and semantic
|
|
`layer=base_saved_state|extension_saved_state`; public follow-ups never require
|
|
`ConfigSave`, `ConfigCASSave`, or `owner_guid`. The smoke then follows that
|
|
resolver. If a saved-state stream exposes `write_plan_target`,
|
|
the smoke composes it with `write_plan_evidence.target` and checks the resulting
|
|
read-only `metadata.write.plan`; when the test base has no saved-state stream,
|
|
that last composition step records `skipped_no_saved_state_target`.
|
|
These selector-chain reports are written under
|
|
`reports/1c-sql/<base-id>/selector-chain-rest-smoke.json` and
|
|
`reports/1c-sql/<base-id>/selector-chain-mcp-smoke.json`. Each report includes
|
|
a top-level `coverage` block summarizing whether override evidence was found,
|
|
whether the saved-state resolver ran, whether a `write_plan_target` was
|
|
available, and which steps were skipped. The verify script validates that both
|
|
selector-chain report files are written, parse as JSON, pass, and include the
|
|
expected `coverage` sections.
|
|
Use `-RequireSelectorChainWritePlanComposition` when the test base is expected
|
|
to contain a matching saved-state stream and the selector-chain smoke must fail
|
|
instead of accepting `skipped_no_saved_state_target`.
|
|
Verification also runs the read-only write-plan safety smoke through both REST
|
|
and MCP `onec_request`; the MCP smoke also checks that live methods without
|
|
`payload.base_id` are stopped by `adapter_1c_mcp_policy.v1` with
|
|
`base_id_required`, and that low-level `storage.*`/`query.*` fallback calls are
|
|
blocked as `diagnostic_method` unless diagnostics are explicitly requested. The
|
|
verify script validates the written write-plan safety reports after each smoke.
|
|
Then verification runs REST saved-state form/module write-loop smoke tests
|
|
through `metadata.write`; if the test base has no pending
|
|
`ConfigSave`/`ConfigCASSave` rows, those saved-state checks record
|
|
`skipped_no_saved_state` and pass by default. The saved-state report files are
|
|
also parsed and checked after the smoke commands. Before those smoke tests,
|
|
verification generates read-only `saved-state-strict-readiness.json` and
|
|
`saved-state-copy-plan.json` reports for the selected `-SavedStateTable`. The
|
|
copy plan is generated through `scripts/plan_1c_saved_state_copy.py` using the
|
|
same object selector arguments, so the later persisted-report check has fresh
|
|
copy-preparation evidence. At the end, verification runs
|
|
`scripts/check_1c_verify_reports.py` against the persisted report set, so the
|
|
same report validation can be repeated offline without calling the adapter.
|
|
The offline validator checks that selector-chain coverage includes
|
|
`metadata.resolve_overrides` evidence, the expected
|
|
`metadata.saved_state.modules.search` next step, saved-state resolution, and
|
|
attempted `metadata.write.plan` composition. It also verifies write-plan safety
|
|
outcomes: effective form writes must route to `metadata.write.plan`,
|
|
uncontrolled replacement must be blocked, controlled replacement must be
|
|
planned through `metadata.module.write_apply`, drift must be blocked, and MCP
|
|
policy checks must block missing `base_id` and diagnostic fallback.
|
|
Saved-state write smoke reports are checked in both modes: empty-state skips
|
|
must include successful preflight counts and `skipped=true`; real
|
|
write-and-rollback runs must include allowed write plans, expected apply
|
|
methods, and rollback evidence. The same persisted report validator also checks
|
|
`saved-state-strict-readiness.json` and `saved-state-copy-plan.json` when
|
|
saved-state write smoke reports are enabled. Readiness must match the requested
|
|
`base_id` and selected save-layer table; in strict mode it must be `ready=true`.
|
|
The copy plan must match the requested `base_id`, resolve a concrete object,
|
|
list active source rows from the matching storage family, be `plan_ready`, and
|
|
show clear target collision status for `ConfigSave`/`ConfigCASSave`. The family
|
|
must match the target save layer: `Config -> ConfigSave` for base changes and
|
|
`ConfigCAS -> ConfigCASSave` for extension/CAS changes. The validator also
|
|
checks that readiness, copy-plan target table, saved-state form smoke table, and
|
|
saved-state module smoke `module_ref` table are the same.
|
|
`scripts/check_powershell_scripts.py` also guards the verify/deploy wiring for
|
|
these strict flags and asserts that `check_1c_verify_reports.py --self-test`
|
|
covers selector-chain, safety, saved-state, and copy-plan failure classes.
|
|
Persisted report validation also pins the expected JSON `schema` value for each
|
|
report type, so stale or unrelated report files fail before their contents are
|
|
trusted.
|
|
The same validator checks report identity: each persisted JSON must match the
|
|
requested `base_id`, and REST/MCP reports must match their expected
|
|
`transport`. When verification provides expected endpoints, REST/MCP reports
|
|
must also match the configured `endpoint_url`.
|
|
|
|
To require an actual saved-state write-and-rollback instead of allowing the
|
|
empty-state skip:
|
|
|
|
```powershell
|
|
python scripts\check_1c_saved_state_strict_readiness.py `
|
|
--base-id <base-id-from-project-context> `
|
|
--saved-state-table ConfigSave `
|
|
--report reports\1c-sql\<base-id>\saved-state-strict-readiness.json `
|
|
--json
|
|
```
|
|
|
|
This readiness check is read-only. It checks REST adapter health, row counts in
|
|
the selected save-layer table, and saved-state form/module discovery. Use
|
|
`--saved-state-table ConfigSave` for the base save layer or
|
|
`--saved-state-table ConfigCASSave` for the CAS/extension save layer. If it
|
|
reports `blocked_no_saved_state_rows`, there are no unactivated Configurator
|
|
changes in the selected save layer. To prepare a strict write test, copy the
|
|
target object from the main configuration storage (`Config`/`ConfigCAS`) into
|
|
the selected save layer through the approved saved-state workflow, then rerun
|
|
readiness before enabling strict mode.
|
|
|
|
To produce a read-only copy preparation plan for a concrete object:
|
|
|
|
```powershell
|
|
python scripts\plan_1c_saved_state_copy.py `
|
|
--base-id <base-id-from-project-context> `
|
|
--kind <metadata-kind> `
|
|
--name <metadata-object-name> `
|
|
--target-table ConfigSave `
|
|
--report reports\1c-sql\<base-id>\saved-state-copy-plan.json `
|
|
--json
|
|
```
|
|
|
|
The copy plan does not write SQL. It lists active storage rows for the selected
|
|
object only from the matching source family, checks whether the intended
|
|
`ConfigSave`/`ConfigCASSave` target already contains the same `FileName` values,
|
|
and records the intended save-layer target. `--target-table ConfigSave` plans
|
|
from `Config`; `--target-table ConfigCASSave` plans from `ConfigCAS`. Normal
|
|
verification regenerates this file automatically before saved-state smoke tests
|
|
and uses `-SavedStateTable` to pick the same target table for the copy plan,
|
|
form smoke, and module smoke. Run the command manually when choosing a target
|
|
object for strict readiness or when preparing evidence without the full
|
|
deploy/verify flow. The offline persisted-report validator checks this file by
|
|
default, so regenerate the plan whenever the chosen object, target table, or
|
|
base changes.
|
|
|
|
To generate the reviewed SQL preparation script from that plan without
|
|
executing SQL:
|
|
|
|
```powershell
|
|
python scripts\prepare_1c_saved_state_copy_sql.py `
|
|
--plan reports\1c-sql\<base-id>\saved-state-copy-plan.json `
|
|
--expected-base-id <base-id-from-project-context> `
|
|
--expected-target-table ConfigSave `
|
|
--sql-out reports\1c-sql\<base-id>\prepare-saved-state-copy.sql `
|
|
--report reports\1c-sql\<base-id>\prepare-saved-state-copy-sql.json `
|
|
--json
|
|
```
|
|
|
|
This generator does not call the adapter and does not write SQL. It refuses
|
|
plans that are not `plan_ready`, have target collisions, or mix storage
|
|
families. The generated SQL is a manual review artifact: it starts a
|
|
transaction, rechecks that the save-layer target has no planned `FileName`
|
|
values, copies only the planned rows with `INSERT ... SELECT`, verifies the
|
|
copied row count, and commits only if those guards pass.
|
|
|
|
To execute the reviewed preparation SQL, use the explicit SQL-write gate. This
|
|
is not run by deploy/verify:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\execute_1c_saved_state_copy_sql.ps1 `
|
|
-Server <sql-server> `
|
|
-Database <sql-database> `
|
|
-User <sql-user> `
|
|
-Password <sql-password> `
|
|
-ExpectedBaseId <base-id-from-project-context> `
|
|
-ExpectedTargetTable ConfigSave `
|
|
-IUnderstandThisWritesToSql
|
|
```
|
|
|
|
The executor validates the generated SQL report, checks that the SQL file still
|
|
looks like the reviewed saved-state copy artifact, records the SQL SHA1, runs
|
|
the SQL through `System.Data.SqlClient`, and immediately calls the read-only
|
|
post-copy verifier. Credentials can also be supplied with `ONEC_SQL_SERVER`,
|
|
`ONEC_SQL_DATABASE`, `ONEC_SQL_USER`, and `ONEC_SQL_PASSWORD`. Do not commit
|
|
credentials or execution reports containing environment-specific connection
|
|
details.
|
|
|
|
After the SQL preparation has been executed, verify the saved-state copy
|
|
read-only before enabling strict write smoke:
|
|
|
|
```powershell
|
|
python scripts\verify_1c_saved_state_copy.py `
|
|
--plan reports\1c-sql\<base-id>\saved-state-copy-plan.json `
|
|
--expected-base-id <base-id-from-project-context> `
|
|
--expected-target-table ConfigSave `
|
|
--report reports\1c-sql\<base-id>\saved-state-copy-verify.json `
|
|
--require-ready `
|
|
--json
|
|
```
|
|
|
|
Before the SQL preparation is executed, this verifier is expected to report
|
|
`blocked_missing_target_rows`. After preparation it must report `ready=true` by
|
|
comparing the save-layer `FileName`, `PartNo`, byte sizes, and `BinarySHA1`
|
|
values against the reviewed active-source rows from the copy plan.
|
|
|
|
Also generate the guarded cleanup script before executing the preparation SQL,
|
|
so there is a reviewed way to remove the prepared working-copy rows later:
|
|
|
|
```powershell
|
|
python scripts\prepare_1c_saved_state_cleanup_sql.py `
|
|
--plan reports\1c-sql\<base-id>\saved-state-copy-plan.json `
|
|
--expected-base-id <base-id-from-project-context> `
|
|
--expected-target-table ConfigSave `
|
|
--sql-out reports\1c-sql\<base-id>\cleanup-saved-state-copy.sql `
|
|
--report reports\1c-sql\<base-id>\cleanup-saved-state-copy-sql.json `
|
|
--json
|
|
```
|
|
|
|
The cleanup SQL deletes only the planned `FileName`/`PartNo` rows from the
|
|
selected save layer, and only when their current `BinarySHA1` still matches the
|
|
reviewed copy plan. It rolls back if the rows are missing, extra/mismatched, or
|
|
if the deleted row count differs from the plan.
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-RequireSavedStateWriteSmoke
|
|
```
|
|
|
|
To require selector-chain evidence to resolve all the way to a concrete
|
|
read-only `metadata.write.plan` composition:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-RequireSelectorChainWritePlanComposition
|
|
```
|
|
|
|
To skip saved-state write-loop checks entirely:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-SkipSavedStateWriteSmoke
|
|
```
|
|
|
|
To skip only the read-only write-plan safety smoke:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-BaseId <base-id-from-project-context> `
|
|
-SkipWritePlanSafetySmoke
|
|
```
|
|
|
|
To validate the latest persisted reports without contacting REST or MCP:
|
|
|
|
```powershell
|
|
python scripts\check_1c_verify_reports.py --base-id <base-id-from-project-context>
|
|
```
|
|
|
|
Add `--rest-adapter-url <url> --mcp-url <url>` when the persisted REST/MCP
|
|
reports should be pinned to specific endpoints.
|
|
Add `--saved-state-table ConfigSave` or `--saved-state-table ConfigCASSave`
|
|
when the persisted saved-state reports must be pinned to a specific save-layer
|
|
table.
|
|
Add `--max-report-age-seconds <seconds>` when the persisted reports must also
|
|
be fresh enough for a deployment gate.
|
|
Use `--skip-saved-state-copy-plan` only when intentionally validating an older
|
|
report bundle that does not include `saved-state-copy-plan.json`.
|
|
|
|
To run the offline validator's synthetic soft/strict self-test:
|
|
|
|
```powershell
|
|
python scripts\check_1c_verify_reports.py --self-test
|
|
```
|
|
|
|
To run the whole offline/static verification-stack preflight:
|
|
|
|
```powershell
|
|
python scripts\check_1c_adapter_verification_stack.py --base-id <base-id-from-project-context>
|
|
```
|
|
|
|
The verification-stack preflight passes the default REST/MCP endpoint URLs to
|
|
the persisted report validator. Override them with `--rest-adapter-url` and
|
|
`--mcp-url` for non-default deployments.
|
|
It also passes `--saved-state-table`, defaulting to `ConfigSave`, so
|
|
persisted copy-plan/form/module reports must match the intended save-layer
|
|
table.
|
|
Pass multiple base ids after `--base-id` to validate several persisted report
|
|
directories in one run.
|
|
Use `--max-report-age-seconds` to reject stale report files during the
|
|
verification-stack preflight.
|
|
|
|
To save a machine-readable verification-stack summary:
|
|
|
|
```powershell
|
|
python scripts\check_1c_adapter_verification_stack.py `
|
|
--base-id <base-id-from-project-context> `
|
|
--json `
|
|
--report reports\1c-sql\<base-id>\adapter-verification-stack-check.json
|
|
```
|
|
|
|
The saved stack report keeps per-command return codes, compact parsed summaries
|
|
for JSON-producing checks, and truncated stdout/stderr tails for diagnostics.
|
|
For persisted verify-report checks, the parsed summary also includes per-base
|
|
REST/MCP selector-chain composition status, write-plan safety check counts, and
|
|
saved-state write smoke status, including the saved-state copy-plan target,
|
|
source-row count, and target collision status.
|
|
|
|
To deploy without live verification:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\deploy_1c_adapter_stack.ps1 `
|
|
-SkipVerify
|
|
```
|
|
|
|
Low-level MCP-only deploy:
|
|
|
|
```powershell
|
|
docker --host ssh://docker.cin.su compose `
|
|
--env-file core\deploy\docker\adapter-1c-mcp\.env.example `
|
|
-f core\deploy\docker\adapter-1c-mcp\compose.yaml `
|
|
up -d --build
|
|
```
|
|
|
|
Container name:
|
|
|
|
```text
|
|
adapter-1c-mcp
|
|
```
|
|
|
|
## Health
|
|
|
|
```powershell
|
|
Invoke-RestMethod http://docker.cin.su:8021/health
|
|
docker --host ssh://docker.cin.su ps --filter name=adapter-1c-mcp
|
|
docker --host ssh://docker.cin.su logs --tail 80 adapter-1c-mcp
|
|
```
|
|
|
|
Smoke test MCP initialize:
|
|
|
|
```powershell
|
|
$body = @{
|
|
jsonrpc = "2.0"
|
|
id = 1
|
|
method = "initialize"
|
|
params = @{
|
|
protocolVersion = "2025-06-18"
|
|
capabilities = @{}
|
|
clientInfo = @{ name = "smoke"; version = "1" }
|
|
}
|
|
} | ConvertTo-Json -Depth 10
|
|
|
|
Invoke-WebRequest `
|
|
-Uri "http://docker.cin.su:8021/mcp" `
|
|
-Method Post `
|
|
-ContentType "application/json" `
|
|
-Headers @{ Accept = "application/json, text/event-stream" } `
|
|
-Body $body
|
|
```
|
|
|
|
## Tools
|
|
|
|
The MCP proxy exposes a small stable tool surface:
|
|
|
|
```text
|
|
onec_health
|
|
onec_help
|
|
onec_request
|
|
onec_job_get
|
|
onec_job_cancel
|
|
access_role_users
|
|
access_role_profiles
|
|
access_role_audit_export
|
|
access_role_audit_analyze
|
|
access_user_explain
|
|
access_users_search
|
|
access_object_explain
|
|
access_keys_query
|
|
access_object_keys_resolve
|
|
access_object_roles
|
|
access_object_subjects
|
|
access_rls_discover
|
|
```
|
|
|
|
Use `onec_request(method, payload)` for uncommon or newly added adapter methods
|
|
until a dedicated alias is useful enough to keep stable.
|
|
The `access_*` tools are lightweight aliases for common access-audit workflows;
|
|
they only forward to REST adapter methods such as `access.role.users` and keep
|
|
all BSP/access-rights logic in the REST adapter.
|
|
|
|
For object access checks, pass object names/public refs first; the REST adapter
|
|
resolves GUIDs, SQL numbers, BSP identifiers, synonyms, and readable role-rights
|
|
names internally. Example:
|
|
|
|
```powershell
|
|
python scripts\smoke_1c_access_object.py `
|
|
--base-id upo_test `
|
|
--ref "РегистрСведений.УОП_АктуальныеСпецификации" `
|
|
--action write
|
|
```
|
|
|
|
Use `access_object_roles` or `access_object_subjects` for the question "which
|
|
roles/users can read or write this metadata object". Do not depend on
|
|
`metadata.objects.list kind=Role`: some live bases do not expose roles as regular
|
|
metadata objects through that route.
|
|
|
|
For BSP data-access keys, keep the metadata object and concrete data record
|
|
separate:
|
|
|
|
```json
|
|
{
|
|
"base_id": "upo_test",
|
|
"kind": "object",
|
|
"object_ref": "Справочники.Номенклатура",
|
|
"record_ref": "00112233445566778899AABBCCDDEEFF"
|
|
}
|
|
```
|
|
|
|
Here `kind=object` is the `access_keys_query` area, not the metadata kind.
|
|
`object_ref` is resolved to the internal BSP SQL number and `record_ref` to the
|
|
record id. The same public pair works with `access_object_keys_resolve` and
|
|
`access_object_explain`; do not invent `object_sql_number` in MCP code.
|
|
|
|
For BSP role-right checks, `action=write` means add or modify rights. Read-only
|
|
roles must not be returned for `write`; query `action=read` separately when you
|
|
need visibility roles. Returned permissions keep `source_fields` from
|
|
`ПраваРолей` so suspicious mappings can be verified against the live base.
|
|
|
|
`access.snapshot.extract` also supports this access graph and defaults to the
|
|
BSP extractor preset when `queries` are not supplied. Passing `preset=bsp`
|
|
explicitly is still fine, but the old "No extractor queries were provided"
|
|
discovery response should not appear for a normal BSP base.
|
|
|
|
When `access_object_subjects` is called with `include_access_key_scope=true`,
|
|
the regular `limit` only trims returned roles/profiles/groups/users. Scope
|
|
diagnostics use separate controls: `access_key_scope_subject_limit` limits how
|
|
many matched groups/users are checked, and `access_key_scope_limit` limits rows
|
|
read from each BSP access-key extractor. Check `access_key_scope.coverage` and
|
|
`access_key_scope.truncated` before treating the scope sample as complete.
|
|
|
|
For local audit artifacts, use the workspace script:
|
|
|
|
```powershell
|
|
python scripts\export_1c_access_role_audit.py `
|
|
--base-id upo_test `
|
|
--role "запись изменение номенклатура поставщиков" `
|
|
--user-threshold 30
|
|
```
|
|
|
|
It writes CSV, JSON export, analysis JSON, and a summary file under
|
|
`reports/1c-access/<base_id>/`. By default it also writes a self-contained
|
|
HTML report with findings, counters, artifact links, and a filterable user table;
|
|
pass `--no-html` to skip it.
|
|
|
|
Architecture contract:
|
|
|
|
- The REST adapter owns 1C behavior, payload validation, decoding, owner
|
|
resolution, RAG/tool-facing data shape, and method documentation through
|
|
`help.methods`.
|
|
- MCP is a transport proxy: JSON-RPC/MCP framing, adapter URL/token handling,
|
|
generic `onec_request`, job polling/cancellation helpers, and lightweight
|
|
agent-safety checks such as requiring `base_id` for live database methods.
|
|
- Do not add method-specific 1C business logic to MCP. Put it in the adapter,
|
|
expose it through `/rpc`, and make it discoverable via `help.methods`.
|
|
- Do not register an MCP unified/composite handler with the same name as an
|
|
adapter method; `onec_request(method=...)` must call the adapter method, not
|
|
shadow it inside MCP.
|
|
- When adding a new adapter method, verify it through MCP with
|
|
`onec_request({"method": "...", "payload": {...}})`. MCP should not require a
|
|
schema edit for ordinary adapter growth.
|
|
- Run `python scripts/check_1c_mcp_adapter_contract.py` before release; it
|
|
verifies that `onec_request` stays generic and that every method listed by the
|
|
REST adapter is forwarded through `/rpc`.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"method": "metadata.object.get",
|
|
"payload": {
|
|
"base_id": "<base_id-from-project-context>",
|
|
"kind": "document",
|
|
"name": "ПриходнаяНакладная",
|
|
"view": "merged"
|
|
}
|
|
}
|
|
```
|
|
|
|
Do not copy placeholder or sample `base_id` values from documentation into real
|
|
requests. For live database methods, get `base_id` from the current project,
|
|
user request, environment, or another authoritative context.
|
|
|
|
## Agent Request Policy
|
|
|
|
The MCP proxy blocks live database methods without `payload.base_id` before
|
|
calling the REST adapter. This is intentional: agents must not probe metadata,
|
|
modules, extensions, queries, storage, or code without a concrete target base.
|
|
|
|
Affected method families include:
|
|
|
|
```text
|
|
metadata.*
|
|
modules.*
|
|
code.*
|
|
templates.*
|
|
diagnostics.*
|
|
extensions.*
|
|
query.*
|
|
storage.*
|
|
schema.*
|
|
codec.*
|
|
```
|
|
|
|
Allowed without `base_id`:
|
|
|
|
```text
|
|
onec_help / help.methods
|
|
onec_health without a base for generic service health
|
|
adapter.job.get / adapter.job.cancel
|
|
```
|
|
|
|
Agent rules:
|
|
|
|
- If `base_id` is unknown, ask for it or obtain it from project context.
|
|
- For object-scoped calls, use one of the public selector shapes supported by
|
|
the adapter: `ref`, `kind` + `name`, `guid`, or MCP-friendly
|
|
`object_type`/`object_name`/`object_guid`. Do not add MCP-side conditions for
|
|
concrete object names.
|
|
- If a prior result contains `module_ref`, `module_id`, GUID, or read selector,
|
|
use direct read methods before global search.
|
|
- If a search result contains `read_selector.method`, call that method with the
|
|
selector payload. `code.search` selectors point to `code.read`; `modules.search`
|
|
selectors point to `modules.read`.
|
|
- If a result contains `related_selectors`, prefer those payloads for the next
|
|
object-scoped call. They preserve `kind`/`name`/`guid` and include `ref` when
|
|
the adapter knows the canonical object kind and name.
|
|
- When `modules.search` or `code.search` resolves a module owner, keep both the
|
|
public owner selector (`ref`, `kind`, `name`, `guid`) and the opaque
|
|
`module_ref` in the next read payload. `module_ref` is the direct read handle;
|
|
`ref` makes the owner clear to the agent and later calls.
|
|
- When narrowing `code.search` to one module, pass `module_ordinal` together
|
|
with the object selector. The adapter response should remain a public
|
|
`onec_code_search.v1` object with `item.read_selector`, not a low-level tuple
|
|
or storage result.
|
|
- For BSL edits, prefer high-level `metadata.write` with a 1C canonical path,
|
|
`routine_text`, and `routine_operation`. Do not ask the user for SQL/save
|
|
gates: the adapter prepares saved-state when needed, writes only to the
|
|
saved-state working layer, and does not activate changes. Use
|
|
`metadata.form.command_button.write` for the complete form command + visible
|
|
button + handler workflow. `code.write` remains a compatibility shortcut for
|
|
simple module edits.
|
|
- For a metadata object's standard `synonym` or `comment`, call generic
|
|
`onec_request` with `method=metadata.object.property.write`, a public
|
|
`ref`/`kind` + `name`, and `allow_saved_state_write=true`. Prefer
|
|
`execution_mode=plan` first. The adapter resolves GUIDs and serialized paths;
|
|
MCP callers must not pass SQL tables or file names. Object rename and adding
|
|
a new synonym locale are intentionally unsupported.
|
|
- For an existing requisite, tabular section, dimension, or resource, add a
|
|
public `member_ref` such as
|
|
`Catalog.Номенклатура.Attribute.Артикул`. When only
|
|
`member_kind` + `member_name` is used and several identities match, preserve
|
|
the adapter's `ambiguous` result and retry with the full member path.
|
|
- To add a requisite, call `metadata.object.member.add` through `onec_request`
|
|
with a full `template_member_ref` from the same object and the new public
|
|
name/synonym. Never invent a GUID, Config table, collection path, or type
|
|
descriptor in MCP code; the new requisite inherits type/settings from the
|
|
named template.
|
|
- The same method adds a tabular-section column when
|
|
`template_member_ref` contains the full
|
|
`...TabularSection.<name>.Attribute.<column>` path. Keep the complete path so
|
|
duplicate checks and generated identity remain scoped to that table part.
|
|
- For unresolved module owners, inspect `diagnostics.owner_resolution` and
|
|
`counts.owner_scan_limit_hit`; narrow by `kind`/`name`/`guid` or increase
|
|
`owner_scan_limit` before falling back to broader searches.
|
|
- Do not treat `not_found` from `metadata.definition.find` or scoped search as
|
|
proof that code is absent in extensions or ConfigCAS.
|
|
- Treat `partial`, `truncated=true`, scan limits, and timeouts as incomplete
|
|
evidence.
|
|
|
|
Offline selector-chain smoke:
|
|
|
|
```powershell
|
|
python scripts\smoke_1c_mcp_selector_chain.py --json --no-report
|
|
```
|
|
|
|
Saved-state BSL write smoke:
|
|
|
|
```powershell
|
|
python scripts\smoke_1c_code_write_saved_state.py `
|
|
--adapter-url http://docker-gpu.cin.su:8011 `
|
|
--base-id <base-id-from-project-context> `
|
|
--extension <extension-name> `
|
|
--object-type CommonForm `
|
|
--object-name <form-name> `
|
|
--routine-name <routine-name> `
|
|
--json
|
|
```
|
|
|
|
This smoke performs idempotent `code.write mode=apply` checks with the current
|
|
routine text, a unique `old`/`new` fragment, and the current full
|
|
`module_text`. It then verifies `code.read state=working`,
|
|
`code.read state=both`, marker-hiding for saved form modules, and saved-state
|
|
form indexing. Every write step must report `write_mode.target=saved_state`
|
|
and `activation_state=not_activated`.
|
|
|
|
Agent working-view report:
|
|
|
|
```powershell
|
|
python scripts\report_1c_agent_working_view.py `
|
|
--adapter-url http://docker-gpu.cin.su:8011 `
|
|
--base-id <base-id-from-project-context> `
|
|
--extension <extension-name> `
|
|
--object-type CommonForm `
|
|
--object-name <form-name> `
|
|
--routine-name <routine-name> `
|
|
--json
|
|
```
|
|
|
|
This read-only report shows the form names that an agent sees in the
|
|
working/save layer and verifies that `code.read state=working` and
|
|
`state=both` prefer `saved_state` when saved code exists.
|
|
|
|
The concise agent-facing coding rules are kept in
|
|
`docs/runbooks/1c-agent-coding-contract.md`.
|
|
|
|
Run this with the contract checks before release. It validates generic agent
|
|
routes through `onec_request` and rejects concrete object names in selector
|
|
examples.
|
|
|
|
Optional live selector-chain smoke against a real adapter/base:
|
|
|
|
```powershell
|
|
python scripts\smoke_1c_mcp_selector_chain.py `
|
|
--live `
|
|
--transport rest `
|
|
--adapter-url http://docker-gpu.cin.su:8011 `
|
|
--base-id <base-id-from-project-context> `
|
|
--json `
|
|
--no-report
|
|
```
|
|
|
|
To verify both deployed layers with one command:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\verify_1c_adapter_deployment.ps1 `
|
|
-BaseId <base-id-from-project-context>
|
|
```
|
|
|
|
By default this also writes
|
|
`reports/1c-sql/<base>/code-write-saved-state-rest-smoke.json` and
|
|
`reports/1c-sql/<base>/code-write-saved-state-mcp-smoke.json` when the default
|
|
saved-state code-write target exists. The REST verification also writes
|
|
`reports/1c-sql/<base>/agent-working-view.json` to pin the save-first working
|
|
view that agents should use for coding. Use
|
|
`-RequireCodeWriteSavedStateSmoke` to make that smoke mandatory, or
|
|
`-SkipCodeWriteSavedStateSmoke` to skip it for bases where the scenario is not
|
|
available.
|
|
|
|
Pass multiple base ids to run the same REST and MCP checks against each base:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass `
|
|
-File scripts\verify_1c_adapter_deployment.ps1 `
|
|
-BaseId <base-id-1>,<base-id-2>
|
|
```
|
|
|
|
Use `-ObjectRef`, or `-ObjectKind` with `-ObjectName`/`-ObjectGuid`, to verify a
|
|
specific module-capable metadata object instead of auto-discovery.
|
|
Use `-SavedStateTable ConfigSave` or `-SavedStateTable ConfigCASSave` to choose
|
|
which save-layer table is used by the copy plan and saved-state write smokes.
|
|
Add `-RequireSelectorChainWritePlanComposition` when the selected base/object
|
|
must have a saved-state stream that lets the selector-chain smoke compose a
|
|
concrete read-only `metadata.write.plan`. The same strict mode also requires
|
|
the write-preflight smoke to discover both an extension form and an extension
|
|
module by public names (`extension/ref/form/member` and
|
|
`extension/ref/form/module/stream_ordinal`), compose allowed plans, and prove
|
|
that the repository and support gates use the same resolved
|
|
`extension:<GUID>` layer. Module search restores the public extension name from
|
|
`ConfigCASSave`; it never returns the storage GUID as the caller-facing
|
|
selector. The smoke then submits each public selector with a deliberately
|
|
different GUID and requires a read-only
|
|
`blocked / extension_selector_conflict` result, so a legacy permissive
|
|
repository profile cannot authorize a mismatched layer. Run both checks
|
|
directly with
|
|
`scripts/smoke_1c_write_preflight.py --require-name-first-extension-form --require-name-first-extension-module`.
|
|
|
|
To exercise the MCP proxy itself, switch transport and URL:
|
|
|
|
```powershell
|
|
python scripts\smoke_1c_mcp_selector_chain.py `
|
|
--live `
|
|
--transport mcp `
|
|
--mcp-url http://docker.cin.su:8021 `
|
|
--base-id <base-id-from-project-context> `
|
|
--json `
|
|
--no-report
|
|
```
|
|
|
|
Live mode discovers a module-capable object from metadata lists and then checks
|
|
`adapter.help.methods -> metadata.definition.find -> related_selectors.modules
|
|
-> metadata.object.modules -> modules.read -> code.search ->
|
|
item.read_selector -> code.read` using the returned selector and a token derived
|
|
from module text or routine metadata. The `adapter.help.methods` step verifies
|
|
live REST `contract_version` and `selector_capabilities`, so stale REST adapter
|
|
images fail before the longer chain. MCP transport also performs `initialize`,
|
|
checks `tools/list` for the same `contract_version` and the generic
|
|
`onec_request` selector schema, keeps `Mcp-Session-Id`, and sends adapter calls
|
|
through `tools/call` + `onec_request`. This catches stale MCP proxy images where
|
|
REST works but agents still see old tool descriptions. Do not commit real base
|
|
ids or captured reports from live runs.
|
|
- Avoid increasing global search limits when a direct selector is already
|
|
available.
|
|
- Do not request `include_storage=true` merely to read a module found by search;
|
|
use the public `read_selector` first.
|
|
|
|
The request is proxied to the REST adapter through `POST /rpc`:
|
|
|
|
```json
|
|
{
|
|
"method": "metadata.object.get",
|
|
"payload": {}
|
|
}
|
|
```
|
|
|
|
The MCP proxy intentionally does not know adapter-specific 1C methods. Add new
|
|
methods in the REST adapter and expose them through `help.methods`; MCP stays
|
|
unchanged unless the MCP transport itself changes.
|