Skip to main content

threadlight-connect

CONNECT-leg: evidence-based swap of a scaffolded mock Foundry tool for a real endpoint. Extracts the contract a tool source actually reads, generates conformance tests, checks conformance vs a captured real sample, and gates mock -> real on conformance AND OBO user-scoped evidence AND required-role revalidation vs current identity. States: mock, real-unverified, real-verified, real-drift. Config writes (SPEC.md, mcp-config.json) require --apply plus a validated --real-endpoint, only once verified; dry run yields a nonempty apply plan, zero writes. Manual handoff — threadlight-auto does not run it. USE FOR: mock-to-real swap, contract extraction, conformance tests, integration_state, real-drift, OBO evidence gate, role revalidation, connect-manifest, publish/republish. DO NOT USE FOR: customer field mapping; OAuth on-behalf-of exchange (owned by `entra-agent-id`); role assignment (`azure-rbac`); red-team scanning (threadlight-redteam); evals (threadlight-evals); AGT governance (threadlight-govern).

Ir a la instalación

Datos de origen

Repositorio
aiappsgbb/threadlight-skills
Última actividad en el origen
19 de agosto de 2026 a las 09:25
Idioma detectado de SKILL.md
inglés
Estrellas
1
Forks
5

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
5 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
threadlight-connect
description
CONNECT-leg: evidence-based swap of a scaffolded mock Foundry tool for a real endpoint. Extracts the contract a tool source actually reads, generates conformance tests, checks conformance vs a captured real sample, and gates mock -> real on conformance AND OBO user-scoped evidence AND required-role revalidation vs current identity. States: mock, real-unverified, real-verified, real-drift. Config writes (SPEC.md, mcp-config.json) require --apply plus a validated --real-endpoint, only once verified; dry run yields a nonempty apply plan, zero writes. Manual handoff — threadlight-auto does not run it. USE FOR: mock-to-real swap, contract extraction, conformance tests, integration_state, real-drift, OBO evidence gate, role revalidation, connect-manifest, publish/republish. DO NOT USE FOR: customer field mapping; OAuth on-behalf-of exchange (owned by `entra-agent-id`); role assignment (`azure-rbac`); red-team scanning (threadlight-redteam); evals (threadlight-evals); AGT governance (threadlight-govern).
metadata
{"version":"0.1.0"}
# Threadlight Connect — swap the mock, prove the real The **CONNECT** leg: the step between "a pilot works against mocked tools" and "a pilot is safe to point at a real endpoint." `threadlight-design` / `threadlight-demo-data-factory` scaffold pilots against **mocked** Foundry tools (a JSON sample file standing in for a real backend). This leg is the one place in the pipeline that turns a mock into a real integration — and it never does so on trust. It is a **manual handoff**: `threadlight-auto` does not run this leg for you, because swapping to a real endpoint is an operator decision that requires operator-supplied evidence. > **Why "evidence-based."** An endpoint that *responds* is not the same as an > endpoint that is *safe to depend on*. This leg only ever calls a swap > `real-verified` after three independent, machine-checked facts hold at > once: (1) the real response **conforms** to the contract the tool source > code actually reads, (2) OBO (on-behalf-of) evidence shows the call was > **user-scoped**, and (3) the **required roles** have been revalidated > against the **current** agent identity — not a stale grant from a previous > publish. No endpoint call is ever verified without evidence: this script > never calls the real endpoint itself; the caller supplies the captured > real response and evidence (e.g. from a manual test call made through > `entra-agent-id`, the catalog skill that owns the actual OBO/OAuth token > exchange). ## What this skill does and does not do - **Does:** extract a data contract, generate conformance tests, check conformance, gate the mock → real state transition on conformance + OBO + role evidence, and — only with `--apply` and only once fully verified — transactionally update `specs/SPEC.md`, the effective MCP config (`infra/mcp-config.json` or the caller's actual equivalent) — pointing `servers/<tool>.url` at the validated `--real-endpoint` — and the `connect-manifest.json` together, rolling back a partially-applied write so the three never diverge. The real endpoint URL is persisted **only** in mcp-config.json; the manifest records just safe `endpoint_configured` / `endpoint_verified` booleans, never the URL. - **Does NOT** map or rename customer-specific fields. Field **mapping** (e.g. `cust_id` → `customer_id`) is explicitly out of scope — this leg proves *conformance* between a mock contract and a real response; it does not transform payloads. Field mapping is a downstream, customer-specific concern left to the operator. - **Does NOT** perform the OBO/OAuth token exchange itself. OBO handling here is scaffolding only: it validates the *shape* of OBO evidence the caller supplies and folds it into the state machine. The actual Entra Agent Identity / on-behalf-of implementation is owned by the upstream `entra-agent-id` catalog skill (see "See also" below) — this leg composes with it, it does not replace it. - **Does NOT** call the real endpoint. No network call is made by `scripts/connect.py`. Every phase operates on evidence the caller already captured (tool source text, a mock sample, a captured real response, OBO evidence, role evidence). ## The state machine Exactly four states — `mock`, `real-unverified`, `real-verified`, `real-drift`: | `target_state` | When | |---|---| | `real-drift` | Field-level conformance failed (missing required field, or a type mismatch) against a captured real sample that **had records to check**. | | `real-unverified` | Conformance could not be verified — either the real response had **no items** (empty/missing `items`: insufficient evidence, never a vacuous pass), or conformance passed but OBO evidence is missing/not user-scoped, or required roles are not revalidated against the **current** agent identity. The latter includes the cases where **no `--current-agent-identity` was supplied at all** and where the revalidation names a **stale/mismatched** identity. | | `real-verified` | Conformance passed against a **non-empty** real sample **and** OBO evidence is present and user-scoped **and** required roles are revalidated against a **supplied** current agent identity that the evidence names exactly. | Required-role revalidation is **never opt-in**: a real apply requires `--current-agent-identity`, and the role evidence must record exactly that identity. Omitting the current identity, or supplying one the evidence does not match, holds the swap at `real-unverified` and edits nothing. `integration_state` is the **persisted** current state (read from a prior `connect-manifest.json`; defaults to `mock`). It only ever advances on a **successful** `--apply` — a failed or unverified transition never edits it, and never edits `SPEC.md` / the MCP config. Publishing or **republishing** always re-runs required-role revalidation against the current agent identity before a swap can be called `real-verified` again — a role grant recorded for a previous identity does not carry over. ## Phases ``` inspect read the tool source + the mock sample contract extract_contract(): fields the source actually READS — never sample keys that are merely present but unread generate-tests write an executable, dependency-free conformance test module into the generated project (written every run) verify check_conformance(): field-level diff against a captured real sample — {field, expected, actual, path} per diff; an empty/missing real response is unevaluated, not a pass; a non-object row is itself a difference at $.items[i] plan build_apply_plan(): file-by-file plan, always computed, read-only, no writes — even in a dry run apply only with --apply AND target_state == real-verified AND a validated --real-endpoint: builds and fully validates the final manifest FIRST, points servers/<tool>.url at the real endpoint, then transactionally updates specs/SPEC.md + the MCP config + connect-manifest.json together (an in-process failure after any write — or a failed post-apply endpoint re-read — rolls every applied file back to its prior bytes/mode) and records only the production changed paths emit on the non-apply / unverified path, write specs/connect-manifest.json — shared envelope, schema-validated, atomic, no credentials/tokens/customer payloads (on a verified apply the manifest is committed inside the transaction above instead) ``` ## Usage ```bash # Dry run: always safe. Computes target_state + a nonempty apply plan; # never touches SPEC.md / mcp-config.json. --evidence-captured-at is optional: # it records WHEN the real evidence was captured as freshness.source_oldest_at # (omit it when unknown — it is then recorded as null, never faked). python3 scripts/connect.py \ --project-root ../my-pilot \ --tool-name returns_get_case \ --tool-source-file tool_source.py \ --sample-file mock_sample.json \ --real-response-file real_response.json \ --obo-evidence-file obo_evidence.json \ --role-evidence-file role_evidence.json \ --evidence-captured-at 2026-08-10T09:00:00+00:00 # Publish: config changes require --apply, and only take effect once fully # verified (conformance + OBO + role revalidation all pass). --real-endpoint is # REQUIRED to apply — it is the real MCP URL that servers/<tool>.url is pointed # at (persisted only in mcp-config.json, never in the connect manifest). python3 scripts/connect.py \ --project-root ../my-pilot \ --tool-name returns_get_case \ --tool-source-file tool_source.py \ --sample-file mock_sample.json \ --real-response-file real_response.json \ --obo-evidence-file obo_evidence.json \ --role-evidence-file role_evidence.json \ --current-agent-identity agent-123 \ --real-endpoint https://api.example.com/mcp \ --apply ``` Evidence file shapes: ```jsonc // obo_evidence.json {"present": true, "user_scoped": true} // role_evidence.json { "revalidated": true, "required_roles": ["Case.Read"], "validated_roles": ["Case.Read", "Case.Write"], "agent_identity": "agent-123" } ``` Evidence that is honestly absent or `false` is a normal `real-unverified` finding. Evidence that is the **wrong shape** raises before anything is written, so a malformed-evidence run never disturbs whatever valid manifest/SPEC/mcp-config already existed. This is strict, with no silent coercion: `required_roles` / `validated_roles` (alias `granted_roles`) must each be a list of **non-empty, unique strings** (never `or []`-coerced from a falsey scalar such as `""`, `0`, `{}`, or `false`); `agent_identity` must be a non-empty string or null; and `present` / `user_scoped` / `revalidated` must be actual booleans (`user_scoped: "true"` as a string is rejected). Note `agent_identity` must equal the `--current-agent-identity` you pass, or the swap stays `real-unverified`. ## The real endpoint — validated, bound, never leaked Publishing a swap requires the real endpoint the runtime will actually call, supplied via `--real-endpoint`. It is **optional for a dry run** (evidence assessment only) but **mandatory to `--apply`** a verified swap: applying without it is a controlled error with nothing written, so `INT-002` can never pass on an integration that was never actually bound. Before any write the endpoint is validated (a bad one raises with nothing written): it must be a non-empty `https` URL — `http` is allowed **only** for `localhost` / `127.0.0.1` — with a hostname, no embedded credentials (`user:pass@host`), no URL fragment, no credential/SAS/token query parameter, no whitespace/control characters, and it must **not** be the scaffolded mock (the same conservative delimited-`mock` marker `threadlight-safe-check` uses — so `mock` / `mocked` / `mockserver` are rejected while a real host that merely contains the substring, like `mockingbird`, is fine). On apply, `servers/<tool>.url` in the MCP config is (re)pointed at the validated endpoint, preserving unrelated servers, unrelated top-level config, and the tool entry's own safe unrelated fields (`type`, `headers`, …) while dropping mock/stdio transport fields mutually exclusive with a real HTTPS binding. A malformed existing `servers` / tool entry fails **closed** (raises, nothing written) rather than being silently overwritten. The predicted binding is checked before the transaction, and after the transaction the MCP config is re-read and its effective endpoint asserted to still equal the validated real endpoint — a mismatch rolls all three files back (or, if rollback also fails, raises the inconsistent-state error) so success is never reported on a divergent binding. The endpoint URL is persisted **only** in `mcp-config.json`. The connect manifest records just the safe booleans `evidence_summary.endpoint_configured` (a validated endpoint was supplied) and `evidence_summary.endpoint_verified` (the binding was persisted by a successful apply) — never the URL itself. `apply` is transactional across `SPEC.md`, the MCP config, **and** `connect-manifest.json`. The final manifest is built and fully schema-validated *before* any file is mutated; then a temp file is staged for all three before any is replaced, and if an in-process write fails after an earlier replace has already landed, every applied file is rolled back to its captured prior bytes/mode — the trio never diverges. The manifest is committed inside this transaction (not written separately afterward), and only the two production config paths are recorded in `changed_paths`. The single case this can't defend against is a hard crash / power loss *between* two individually atomic replaces; if the compensating rollback itself fails, the run raises an error naming the unreconciled path(s) instead of reporting success. Forward writes and rollbacks preserve permissions: an existing destination keeps its exact prior mode, and a brand-new file gets a predictable non-executable mode that honors the process umask (`0o644` under the usual `022`). ## Freshness — `source_oldest_at` reflects the evidence, not the run `freshness.source_oldest_at` records when the **real evidence** was captured, threaded in via `--evidence-captured-at` (an ISO-8601 timestamp). It is never back-filled from the run's own `generated_at`: if the capture time is unknown, the field is `null` rather than a misleadingly fresh value. A malformed `--evidence-captured-at` is rejected up front, before any file is written. ## Conformance — lossless numeric widening only A captured integer satisfies an expected `number`, and an integral float such as `1.0` satisfies an expected `integer` (widening that loses no information). A non-integral float such as `1.5` still drifts against an expected `integer`, and booleans never count as numeric — so `true` never widens into `integer`/`number`. A row that is not a JSON object (a scalar, array, or null) is itself a conformance difference at `$.items[i]` (`expected: object`) and can never vacuously pass, not even against an optional-only contract. The generated conformance test module normalizes the `{"items": [...]}` wrapper and a bare list, and flags non-object rows, exactly like the in-process check — so the pytest scaffold and the runtime check always agree. ## Robust inputs — corrupt state and unparseable arguments A prior `connect-manifest.json` or `mcp-config.json` that exists but is malformed (or is valid JSON that isn't an object) is **never** silently reset to a starting `mock`/`{}`; the run raises a clean error and leaves the bytes on disk untouched for repair. A prior manifest that *is* a JSON object but is **missing** `integration_state`, or carries a value outside the four valid states, is likewise refused rather than quietly treated as `mock` — a corrupt or tampered record must not fabricate a starting state. On an apply path, MCP config validation happens before the generated conformance test is scaffolded, so the CLI's `(nothing written)` report is literal. On the CLI, unparseable input — malformed JSON in a `--*-file` argument or a tool source that isn't valid Python — prints a single-line error (no traceback), returns a stable nonzero exit code, and writes nothing. When a verified apply fails, the CLI reports precisely that `SPEC.md`, `mcp-config.json`, and the connect manifest were left unchanged — it never over-claims that *no* files changed, since the regenerated conformance-test scaffold may have been rewritten. ## Contract extraction — exactly what is read, nothing that merely exists `extract_contract()` AST-walks the tool source and only records fields **actually read**: `row['id']` marks `id` as required; `row.get('status')` marks `status` as optional. A field present in the sample but never read by the source (e.g. an internal-only column) is structurally excluded — it can never leak into the contract, the generated tests, or the manifest. Types and cardinality are inferred **only where evidence exists** in the sample; a field read but absent from the sample gets `type: null` rather than a guess. ## Manifest findings — the stable `INT-001..004` gap-evidence contract The emitted `specs/connect-manifest.json` always carries **exactly four findings, one each, in this order** — the live-leg gap evidence that `threadlight-production-ready` projects 1:1 onto its `INT-001..004` targets. The IDs are never dynamic: field-level conformance detail stays in `conformance.differences`, so a consumer always sees the same four IDs regardless of how a real response diverged. | Finding | Evidence | `pass` | `must-fix` | `not-verified` | |---|---|---|---|---| | `INT-001` | Contract conformance | real sample conforms (evaluated, no differences) | any field-level difference | unevaluated (no real records to check) | | `INT-002` | Runtime mock→real binding (`target_state` + persisted `integration_state`) | `target_state: real-verified` **and** persisted `integration_state: real-verified` after successful `--apply` | `target_state: real-drift` | dry-run `target_state: real-verified` with persisted `integration_state: mock`; `target_state: mock` or `real-unverified` | | `INT-003` | OBO user-scoped identity | OBO present **and** user-scoped | — (no explicit-failure signal in the evidence shape) | OBO absent or not user-scoped | | `INT-004` | Required-role revalidation vs current identity | roles revalidated against the **current** identity | revalidation ran but a required role is missing | never revalidated, no current identity, or a stale/mismatched grant | `status` follows evaluation, not the finding severities: the envelope stays `partial` whenever conformance was unevaluated (nothing real to check) and is `complete` once fully evaluated **even if a finding is `must-fix`** (e.g. a `real-drift` result is a complete-but-failing manifest). Because the consumer downgrades any `pass`/`should-fix` it reads from a `partial`, stale, or `aborted` envelope to `not-verified`, an incomplete leg can never inflate a pillar's readiness — negative (`must-fix`) evidence still dominates. ## Files ``` scripts/connect.py # stdlib implementation + CLI references/data-contract.schema.json # extract_contract() output shape references/connect-manifest.schema.json # connect-manifest.json shape (shared envelope) tests/test_connect.py # pytest suite ``` ## Tests ```bash python3 -m pytest skills/threadlight-connect/tests/ -v ``` ## See also — official Azure Skills Threadlight exists to make Microsoft's own platform **trivial to adopt** — never to replace it. For first-party depth behind the evidence this leg consumes, reach for the official **[Azure Skills](https://github.com/microsoft/azure-skills)** catalog. *Further reading, not a dependency* — Threadlight's guidance stays the source of truth for the pilot flow: - **[`entra-agent-id`](https://github.com/microsoft/azure-skills/blob/main/skills/entra-agent-id/SKILL.md)** — **Entra Agent Identity Blueprints** + the actual OAuth token exchange (OBO / `fmi_path`) this leg's OBO evidence is scaffolded around.
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub