qcoder 0.5.0a6__tar.gz → 0.5.0a8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/CHANGELOG.md +10 -0
- {qcoder-0.5.0a6/src/qcoder.egg-info → qcoder-0.5.0a8}/PKG-INFO +6 -2
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/README.md +5 -1
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/pyproject.toml +1 -1
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/__init__.py +1 -1
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/cli.py +12 -3
- qcoder-0.5.0a8/src/qcoder/context_bridge_mcp.py +498 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8/src/qcoder.egg-info}/PKG-INFO +6 -2
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder.egg-info/SOURCES.txt +1 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/LICENSE +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/MANIFEST.in +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/NOTICE +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/setup.cfg +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/__main__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/context.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/qasm2/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/qasm2/adjoint_eligibility.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/qasm2/mirror_build.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/run_config.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/schema.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/core/share_safe.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/context/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/context/bundle.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/context/markdown.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/cirq_intake.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/pennylane_intake.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/qiskit_intake.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/extractor.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/compute_v0.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/glossary_v0.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/schema_v0.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/ir.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/labeling.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/parsers/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/qasm2_regex_parser.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/cut_profile.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/depth.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/entangling_layers.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/gate_set_stats.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/interaction_graph.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/interaction_graph_metrics.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/spans.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/guidance/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/guidance/model_pack.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/guidance/resource.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/guidance/structural_scores.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/profiles/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/profiles/feature_profiles_v0.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/review/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/review/bundle.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/review/counts_v0.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/review/markdown.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/review/qiskit_counts.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/explorer/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/explorer/derived_evidence.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/model_packs/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/model_packs/resource_guidance_local_v0.json +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pipelines/analyze.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pipelines/batch.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pipelines/context.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pipelines/review.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pro_preview/__init__.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pro_preview/client.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pro_preview/config.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pro_preview/errors.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/pro_preview/manifest.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/tools/batch.py +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder.egg-info/dependency_links.txt +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder.egg-info/entry_points.txt +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder.egg-info/requires.txt +0 -0
- {qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## 0.5.0a8
|
|
5
|
+
|
|
6
|
+
- Add `qcoder context-bridge mcp serve`, a stdio Context Bridge MCP adapter for eligible Explorer users.
|
|
7
|
+
- Add `qcoder context-bridge mcp smoke --token-file <path> --json` for sanitized install verification.
|
|
8
|
+
- Expose only the approved Context Bridge tools: guided evidence context, prompt context, Evidence Context Pack, Context Session Card, and Run Readiness Card.
|
|
9
|
+
- Read Context Bridge tokens from a local token file only; token values are not printed and are not required in IDE config.
|
|
10
|
+
- Reject raw QASM, raw counts, paths, unsupported artifact lookup, unknown tools, and non-current-context inputs before forwarding.
|
|
11
|
+
- Preserve qCoder OSS local commands, Explorer evidence commands, and archived Pro shell boundaries.
|
|
12
|
+
|
|
13
|
+
|
|
4
14
|
## 0.5.0a6
|
|
5
15
|
|
|
6
16
|
- Add share-safe / redacted artifact mode for artifacts intended for ChatGPT, Cursor, email, GitHub issues, or support threads.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qcoder
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0a8
|
|
4
4
|
Summary: Quantum circuit analysis and structured feature extraction tools.
|
|
5
5
|
Author-email: Quantum Ready Solutions <support@qcoder.ai>
|
|
6
6
|
Maintainer-email: Quantum Ready Solutions <support@qcoder.ai>
|
|
@@ -41,15 +41,17 @@ The current public local path is **qCoder OSS**. OSS commands run locally and do
|
|
|
41
41
|
- `qcoder context`
|
|
42
42
|
- `qcoder review`
|
|
43
43
|
- `qcoder explorer` (Explorer Beta account-backed status/demo/evidence checks)
|
|
44
|
+
- `qcoder context-bridge` (token-backed Context Bridge MCP adapter for eligible Explorer users)
|
|
44
45
|
- `qcoder student` (temporary compatibility alias for Explorer Beta)
|
|
45
46
|
- `qcoder pro` (archived pilot/bootstrap client contract; non-confidential local plumbing only, not a current public product path)
|
|
46
47
|
|
|
47
48
|
## Current product boundaries
|
|
48
49
|
|
|
49
|
-
Public `qcoder` ships **OSS local commands
|
|
50
|
+
Public `qcoder` ships **OSS local commands**, Explorer Beta compatibility commands, and the Context Bridge adapter command for eligible Explorer users. Pro is not launched and is not a current public product path.
|
|
50
51
|
|
|
51
52
|
- **OSS commands** (`analyze`, `batch`, `context`, `review`) are Apache-2.0, local-first/offline, and useful without an account or token. They do not upload data, call a qCoder hosted service, or run QPU/simulator jobs.
|
|
52
53
|
- **Explorer Beta commands** (`qcoder explorer status`, `qcoder explorer demo`, `qcoder explorer evidence`) are account-backed checks for Explorer Beta status, built-in guided evidence samples, and derived-context guided evidence for user-owned OpenQASM 2 artifacts. The older `qcoder student ...` commands remain available as beta compatibility aliases.
|
|
54
|
+
- **Context Bridge adapter commands** (`qcoder context-bridge mcp serve`, `qcoder context-bridge mcp smoke`) are for eligible Explorer users with support-managed Context Bridge token access. They expose bounded current-evidence context tools to configured MCP clients and read tokens from a local token file.
|
|
53
55
|
- Explorer Beta custom evidence uses locally derived qCoder context/features. The CLI may read QASM locally, but the hosted request must not include raw QASM, raw source text, local paths, operation lists, raw counts, notebooks, prompts, tokens, auth headers, or cookies.
|
|
54
56
|
- Explorer Beta custom evidence is stateless in this v0 slice; it does not create persistent Explorer history.
|
|
55
57
|
- **`qcoder pro` bootstrap/workflow commands** are archived pilot/client-contract surfaces. They are not a Pro purchase path, not a current public signup path, and not generally available hosted Pro.
|
|
@@ -96,6 +98,8 @@ qcoder explorer evidence
|
|
|
96
98
|
qcoder explorer evidence --qasm path/to/circuit.qasm
|
|
97
99
|
qcoder explorer evidence --context-json preflight.context.json
|
|
98
100
|
qcoder explorer evidence --qasm path/to/circuit.qasm --out-json explorer.json --out-md explorer.md --share-safe
|
|
101
|
+
qcoder context-bridge mcp serve --help
|
|
102
|
+
qcoder context-bridge mcp smoke --token-file ~/.qcoder/context-bridge/token.txt --json
|
|
99
103
|
qcoder pro --help
|
|
100
104
|
```
|
|
101
105
|
|
|
@@ -11,15 +11,17 @@ The current public local path is **qCoder OSS**. OSS commands run locally and do
|
|
|
11
11
|
- `qcoder context`
|
|
12
12
|
- `qcoder review`
|
|
13
13
|
- `qcoder explorer` (Explorer Beta account-backed status/demo/evidence checks)
|
|
14
|
+
- `qcoder context-bridge` (token-backed Context Bridge MCP adapter for eligible Explorer users)
|
|
14
15
|
- `qcoder student` (temporary compatibility alias for Explorer Beta)
|
|
15
16
|
- `qcoder pro` (archived pilot/bootstrap client contract; non-confidential local plumbing only, not a current public product path)
|
|
16
17
|
|
|
17
18
|
## Current product boundaries
|
|
18
19
|
|
|
19
|
-
Public `qcoder` ships **OSS local commands
|
|
20
|
+
Public `qcoder` ships **OSS local commands**, Explorer Beta compatibility commands, and the Context Bridge adapter command for eligible Explorer users. Pro is not launched and is not a current public product path.
|
|
20
21
|
|
|
21
22
|
- **OSS commands** (`analyze`, `batch`, `context`, `review`) are Apache-2.0, local-first/offline, and useful without an account or token. They do not upload data, call a qCoder hosted service, or run QPU/simulator jobs.
|
|
22
23
|
- **Explorer Beta commands** (`qcoder explorer status`, `qcoder explorer demo`, `qcoder explorer evidence`) are account-backed checks for Explorer Beta status, built-in guided evidence samples, and derived-context guided evidence for user-owned OpenQASM 2 artifacts. The older `qcoder student ...` commands remain available as beta compatibility aliases.
|
|
24
|
+
- **Context Bridge adapter commands** (`qcoder context-bridge mcp serve`, `qcoder context-bridge mcp smoke`) are for eligible Explorer users with support-managed Context Bridge token access. They expose bounded current-evidence context tools to configured MCP clients and read tokens from a local token file.
|
|
23
25
|
- Explorer Beta custom evidence uses locally derived qCoder context/features. The CLI may read QASM locally, but the hosted request must not include raw QASM, raw source text, local paths, operation lists, raw counts, notebooks, prompts, tokens, auth headers, or cookies.
|
|
24
26
|
- Explorer Beta custom evidence is stateless in this v0 slice; it does not create persistent Explorer history.
|
|
25
27
|
- **`qcoder pro` bootstrap/workflow commands** are archived pilot/client-contract surfaces. They are not a Pro purchase path, not a current public signup path, and not generally available hosted Pro.
|
|
@@ -66,6 +68,8 @@ qcoder explorer evidence
|
|
|
66
68
|
qcoder explorer evidence --qasm path/to/circuit.qasm
|
|
67
69
|
qcoder explorer evidence --context-json preflight.context.json
|
|
68
70
|
qcoder explorer evidence --qasm path/to/circuit.qasm --out-json explorer.json --out-md explorer.md --share-safe
|
|
71
|
+
qcoder context-bridge mcp serve --help
|
|
72
|
+
qcoder context-bridge mcp smoke --token-file ~/.qcoder/context-bridge/token.txt --json
|
|
69
73
|
qcoder pro --help
|
|
70
74
|
```
|
|
71
75
|
|
|
@@ -819,6 +819,12 @@ def _cmd_student(argv: list[str]) -> int:
|
|
|
819
819
|
return _cmd_explorer(argv, compatibility_alias=True)
|
|
820
820
|
|
|
821
821
|
|
|
822
|
+
def _cmd_context_bridge(argv: list[str]) -> int:
|
|
823
|
+
from qcoder.context_bridge_mcp import main as context_bridge_main
|
|
824
|
+
|
|
825
|
+
return context_bridge_main(argv)
|
|
826
|
+
|
|
827
|
+
|
|
822
828
|
def _cmd_pro(argv: list[str]) -> int:
|
|
823
829
|
p = argparse.ArgumentParser(
|
|
824
830
|
prog="qcoder pro",
|
|
@@ -1200,15 +1206,16 @@ def _cmd_pro(argv: list[str]) -> int:
|
|
|
1200
1206
|
|
|
1201
1207
|
def _print_root_help() -> None:
|
|
1202
1208
|
print(
|
|
1203
|
-
"usage: qcoder [--version | -V] [-h] {analyze,batch,context,review,explorer,pro,student} ...\n\n"
|
|
1209
|
+
"usage: qcoder [--version | -V] [-h] {analyze,batch,context,review,explorer,context-bridge,pro,student} ...\n\n"
|
|
1204
1210
|
"Quantum circuit analysis CLI.\n\n"
|
|
1205
1211
|
"positional arguments:\n"
|
|
1206
|
-
" {analyze,batch,context,review,explorer,pro,student} subcommand\n\n"
|
|
1212
|
+
" {analyze,batch,context,review,explorer,context-bridge,pro,student} subcommand\n\n"
|
|
1207
1213
|
" analyze Analyze a QASM file (feature extraction + metadata + run config).\n"
|
|
1208
1214
|
" batch Batch extract a directory to JSONL (requires --out).\n"
|
|
1209
1215
|
" context Build deterministic preflight context artifacts.\n"
|
|
1210
1216
|
" review Build deterministic execution review artifacts from counts.\n"
|
|
1211
1217
|
" explorer Explorer Beta status/demo/evidence checks.\n"
|
|
1218
|
+
" context-bridge Run the Context Bridge MCP adapter for eligible Explorer users.\n"
|
|
1212
1219
|
" pro Archived Pro client-contract shell (not current public product).\n"
|
|
1213
1220
|
" student Compatibility alias for Explorer Beta checks.\n\n"
|
|
1214
1221
|
"Run `qcoder <subcommand> --help` for subcommand options.",
|
|
@@ -1241,13 +1248,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1241
1248
|
return _cmd_review(rest)
|
|
1242
1249
|
if cmd == "explorer":
|
|
1243
1250
|
return _cmd_explorer(rest)
|
|
1251
|
+
if cmd == "context-bridge":
|
|
1252
|
+
return _cmd_context_bridge(rest)
|
|
1244
1253
|
if cmd == "pro":
|
|
1245
1254
|
return _cmd_pro(rest)
|
|
1246
1255
|
if cmd == "student":
|
|
1247
1256
|
return _cmd_student(rest)
|
|
1248
1257
|
|
|
1249
1258
|
print(
|
|
1250
|
-
f"qcoder: unknown subcommand {cmd!r} (expected analyze, batch, context, review, explorer, pro, or student)",
|
|
1259
|
+
f"qcoder: unknown subcommand {cmd!r} (expected analyze, batch, context, review, explorer, context-bridge, pro, or student)",
|
|
1251
1260
|
file=sys.stderr,
|
|
1252
1261
|
)
|
|
1253
1262
|
print("Run `qcoder --help` for usage.", file=sys.stderr)
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import stat
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any, Callable
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://preview-api.qcoder.ai"
|
|
14
|
+
ROUTE_PATH = "/v0/internal/hosted-mcp/context"
|
|
15
|
+
EXPECTED_TOOLS = (
|
|
16
|
+
"get_guided_evidence_context",
|
|
17
|
+
"create_prompt_context",
|
|
18
|
+
"create_evidence_context_pack",
|
|
19
|
+
"create_context_session_card",
|
|
20
|
+
"create_run_readiness_card",
|
|
21
|
+
)
|
|
22
|
+
DEFAULT_ARTIFACT_KIND = "share_safe_evidence_summary"
|
|
23
|
+
MAX_ARTIFACT_TEXT_CHARS = 20_000
|
|
24
|
+
FORBIDDEN_TEXT_MARKERS = (
|
|
25
|
+
"openqasm",
|
|
26
|
+
"qreg ",
|
|
27
|
+
"creg ",
|
|
28
|
+
"counts=",
|
|
29
|
+
'"counts"',
|
|
30
|
+
"'counts'",
|
|
31
|
+
"/home/",
|
|
32
|
+
"\\users\\",
|
|
33
|
+
"c:\\",
|
|
34
|
+
"../",
|
|
35
|
+
"repo_path",
|
|
36
|
+
"file_path",
|
|
37
|
+
"raw_qasm",
|
|
38
|
+
"raw_counts",
|
|
39
|
+
"raw_source",
|
|
40
|
+
"notebook",
|
|
41
|
+
".ipynb",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def default_token_file() -> Path:
|
|
46
|
+
return Path.home() / ".qcoder" / "context-bridge" / "token.txt"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def safe_error(error_category: str, *, status_category: str = "adapter_rejected") -> dict[str, Any]:
|
|
50
|
+
return {
|
|
51
|
+
"ok": False,
|
|
52
|
+
"error_category": error_category,
|
|
53
|
+
"status_category": status_category,
|
|
54
|
+
"retention": "process_and_discard",
|
|
55
|
+
"retained_artifacts": [],
|
|
56
|
+
"token_printed": False,
|
|
57
|
+
"raw_payload_printed": False,
|
|
58
|
+
"raw_response_printed": False,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_token_file(token_file: str | Path) -> tuple[bool, str, str]:
|
|
63
|
+
path = Path(token_file)
|
|
64
|
+
if not path.is_file():
|
|
65
|
+
return False, "token_file_missing", ""
|
|
66
|
+
try:
|
|
67
|
+
mode = stat.S_IMODE(path.stat().st_mode)
|
|
68
|
+
except OSError:
|
|
69
|
+
return False, "token_file_unreadable", ""
|
|
70
|
+
if os.name != "nt" and mode & 0o077:
|
|
71
|
+
return False, "token_file_permissions_unsafe", ""
|
|
72
|
+
try:
|
|
73
|
+
token = path.read_text(encoding="utf-8").strip()
|
|
74
|
+
except OSError:
|
|
75
|
+
return False, "token_file_unreadable", ""
|
|
76
|
+
if not token:
|
|
77
|
+
return False, "token_file_empty", ""
|
|
78
|
+
if "\n" in token or "\r" in token:
|
|
79
|
+
return False, "token_file_malformed", ""
|
|
80
|
+
return True, "ok", token
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_artifact_text(artifact_text: object) -> str:
|
|
84
|
+
if not isinstance(artifact_text, str) or not artifact_text.strip():
|
|
85
|
+
return "artifact_text_missing"
|
|
86
|
+
if len(artifact_text) > MAX_ARTIFACT_TEXT_CHARS:
|
|
87
|
+
return "artifact_text_too_large"
|
|
88
|
+
lowered = artifact_text.lower()
|
|
89
|
+
if any(marker in lowered for marker in FORBIDDEN_TEXT_MARKERS):
|
|
90
|
+
return "forbidden_input_value"
|
|
91
|
+
return "ok"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def decode_json(raw: bytes) -> dict[str, Any]:
|
|
95
|
+
try:
|
|
96
|
+
decoded = json.loads(raw.decode("utf-8"))
|
|
97
|
+
except Exception:
|
|
98
|
+
return {"ok": False, "error_category": "non_json_response"}
|
|
99
|
+
return decoded if isinstance(decoded, dict) else {"ok": False, "error_category": "non_object_response"}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def post_context_bridge(
|
|
103
|
+
*,
|
|
104
|
+
base_url: str,
|
|
105
|
+
token_file: str | Path,
|
|
106
|
+
tool_name: str,
|
|
107
|
+
artifact_text: object,
|
|
108
|
+
artifact_kind: str = DEFAULT_ARTIFACT_KIND,
|
|
109
|
+
client_context: dict[str, Any] | None = None,
|
|
110
|
+
opener: Callable[..., Any] | None = None,
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
if tool_name not in EXPECTED_TOOLS:
|
|
113
|
+
return safe_error("unknown_tool")
|
|
114
|
+
if artifact_kind != DEFAULT_ARTIFACT_KIND:
|
|
115
|
+
return safe_error("unsupported_artifact_kind")
|
|
116
|
+
text_validation = validate_artifact_text(artifact_text)
|
|
117
|
+
if text_validation != "ok":
|
|
118
|
+
return safe_error(text_validation)
|
|
119
|
+
token_ok, token_category, token = validate_token_file(token_file)
|
|
120
|
+
if not token_ok:
|
|
121
|
+
return safe_error(token_category, status_category="auth_preflight_failed")
|
|
122
|
+
|
|
123
|
+
body = {
|
|
124
|
+
"tool_name": tool_name,
|
|
125
|
+
"artifact_kind": artifact_kind,
|
|
126
|
+
"artifact_text": artifact_text,
|
|
127
|
+
"client_context": {
|
|
128
|
+
"client_version": "qcoder-context-bridge-mcp-adapter",
|
|
129
|
+
**(client_context or {}),
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
data = json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
133
|
+
request = urllib.request.Request(
|
|
134
|
+
base_url.rstrip("/") + ROUTE_PATH,
|
|
135
|
+
data=data,
|
|
136
|
+
headers={
|
|
137
|
+
"content-type": "application/json",
|
|
138
|
+
"authorization": f"Bearer {token}",
|
|
139
|
+
},
|
|
140
|
+
method="POST",
|
|
141
|
+
)
|
|
142
|
+
urlopen = opener or urllib.request.urlopen
|
|
143
|
+
try:
|
|
144
|
+
with urlopen(request, timeout=20) as response:
|
|
145
|
+
status = int(response.status)
|
|
146
|
+
payload = decode_json(response.read())
|
|
147
|
+
except urllib.error.HTTPError as exc:
|
|
148
|
+
status = int(exc.code)
|
|
149
|
+
payload = decode_json(exc.read())
|
|
150
|
+
except Exception:
|
|
151
|
+
return safe_error("context_bridge_unreachable", status_category="network_error")
|
|
152
|
+
|
|
153
|
+
payload.setdefault("adapter_status_category", "success_2xx" if 200 <= status < 300 else f"http_{status}")
|
|
154
|
+
payload.setdefault("token_printed", False)
|
|
155
|
+
payload.setdefault("raw_payload_printed", False)
|
|
156
|
+
payload.setdefault("raw_response_printed", False)
|
|
157
|
+
return payload
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def tool_descriptors() -> list[dict[str, Any]]:
|
|
161
|
+
schema = {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"properties": {
|
|
164
|
+
"artifact_text": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"description": "Share-safe current qCoder evidence summary. Raw circuits, counts, paths, notebooks, and source files are rejected.",
|
|
167
|
+
},
|
|
168
|
+
"artifact_kind": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"enum": [DEFAULT_ARTIFACT_KIND],
|
|
171
|
+
"default": DEFAULT_ARTIFACT_KIND,
|
|
172
|
+
},
|
|
173
|
+
"client_context": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"additionalProperties": True,
|
|
176
|
+
"description": "Optional client metadata without secrets, paths, or raw artifacts.",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
"required": ["artifact_text"],
|
|
180
|
+
"additionalProperties": False,
|
|
181
|
+
}
|
|
182
|
+
descriptions = {
|
|
183
|
+
"get_guided_evidence_context": "Create bounded assistant context from share-safe current qCoder evidence.",
|
|
184
|
+
"create_prompt_context": "Create a share-safe prompt context from current qCoder evidence.",
|
|
185
|
+
"create_evidence_context_pack": "Create a current-evidence context packet with evidence limits and next-step framing.",
|
|
186
|
+
"create_context_session_card": "Create a current-session context card without memory or history.",
|
|
187
|
+
"create_run_readiness_card": "Create a bounded readiness card for the next development check.",
|
|
188
|
+
}
|
|
189
|
+
return [
|
|
190
|
+
{"name": name, "description": descriptions[name], "inputSchema": schema}
|
|
191
|
+
for name in EXPECTED_TOOLS
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _jsonrpc_result(message_id: object, result: dict[str, Any]) -> dict[str, Any]:
|
|
196
|
+
return {"jsonrpc": "2.0", "id": message_id, "result": result}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _jsonrpc_error(message_id: object, code: int, message: str) -> dict[str, Any]:
|
|
200
|
+
return {"jsonrpc": "2.0", "id": message_id, "error": {"code": code, "message": message}}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def handle_jsonrpc_message(
|
|
204
|
+
message: dict[str, Any],
|
|
205
|
+
*,
|
|
206
|
+
base_url: str,
|
|
207
|
+
token_file: str | Path,
|
|
208
|
+
opener: Callable[..., Any] | None = None,
|
|
209
|
+
) -> dict[str, Any] | None:
|
|
210
|
+
method = message.get("method")
|
|
211
|
+
message_id = message.get("id")
|
|
212
|
+
if method == "notifications/initialized":
|
|
213
|
+
return None
|
|
214
|
+
if method == "initialize":
|
|
215
|
+
return _jsonrpc_result(
|
|
216
|
+
message_id,
|
|
217
|
+
{
|
|
218
|
+
"protocolVersion": "2024-11-05",
|
|
219
|
+
"capabilities": {"tools": {"listChanged": False}, "resources": {}, "prompts": {}},
|
|
220
|
+
"serverInfo": {"name": "qcoder-context-bridge", "version": "1.0.0"},
|
|
221
|
+
},
|
|
222
|
+
)
|
|
223
|
+
if method == "tools/list":
|
|
224
|
+
return _jsonrpc_result(message_id, {"tools": tool_descriptors()})
|
|
225
|
+
if method == "prompts/list":
|
|
226
|
+
return _jsonrpc_result(message_id, {"prompts": []})
|
|
227
|
+
if method == "resources/list":
|
|
228
|
+
return _jsonrpc_result(message_id, {"resources": []})
|
|
229
|
+
if method == "tools/call":
|
|
230
|
+
params = message.get("params") if isinstance(message.get("params"), dict) else {}
|
|
231
|
+
tool_name = params.get("name")
|
|
232
|
+
arguments = params.get("arguments") if isinstance(params.get("arguments"), dict) else {}
|
|
233
|
+
payload = post_context_bridge(
|
|
234
|
+
base_url=base_url,
|
|
235
|
+
token_file=token_file,
|
|
236
|
+
tool_name=str(tool_name or ""),
|
|
237
|
+
artifact_text=arguments.get("artifact_text"),
|
|
238
|
+
artifact_kind=str(arguments.get("artifact_kind") or DEFAULT_ARTIFACT_KIND),
|
|
239
|
+
client_context=arguments.get("client_context")
|
|
240
|
+
if isinstance(arguments.get("client_context"), dict)
|
|
241
|
+
else None,
|
|
242
|
+
opener=opener,
|
|
243
|
+
)
|
|
244
|
+
return _jsonrpc_result(
|
|
245
|
+
message_id,
|
|
246
|
+
{
|
|
247
|
+
"content": [{"type": "text", "text": json.dumps(payload, sort_keys=True)}],
|
|
248
|
+
"structuredContent": payload,
|
|
249
|
+
"isError": payload.get("ok") is False,
|
|
250
|
+
},
|
|
251
|
+
)
|
|
252
|
+
return _jsonrpc_error(message_id, -32601, "method_not_supported")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def serve_stdio(*, base_url: str, token_file: str | Path) -> int:
|
|
256
|
+
for raw_line in sys.stdin:
|
|
257
|
+
line = raw_line.strip()
|
|
258
|
+
if not line:
|
|
259
|
+
continue
|
|
260
|
+
try:
|
|
261
|
+
message = json.loads(line)
|
|
262
|
+
except json.JSONDecodeError:
|
|
263
|
+
response = _jsonrpc_error(None, -32700, "parse_error")
|
|
264
|
+
else:
|
|
265
|
+
if not isinstance(message, dict):
|
|
266
|
+
response = _jsonrpc_error(None, -32600, "invalid_request")
|
|
267
|
+
else:
|
|
268
|
+
response = handle_jsonrpc_message(message, base_url=base_url, token_file=token_file)
|
|
269
|
+
if response is None:
|
|
270
|
+
continue
|
|
271
|
+
print(json.dumps(response, sort_keys=True), flush=True)
|
|
272
|
+
return 0
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _case_summary(*, payload: dict[str, Any], expected_success: bool) -> dict[str, Any]:
|
|
276
|
+
serialized = json.dumps(payload, sort_keys=True)
|
|
277
|
+
ok_value = payload.get("ok")
|
|
278
|
+
retained = payload.get("retained_artifacts", [])
|
|
279
|
+
status_category = str(payload.get("adapter_status_category") or payload.get("status_category") or "missing")
|
|
280
|
+
success = ok_value is True or status_category == "success_2xx"
|
|
281
|
+
return {
|
|
282
|
+
"expected_outcome_met": success if expected_success else not success,
|
|
283
|
+
"ok_category": "true" if ok_value is True else "false" if ok_value is False else "missing",
|
|
284
|
+
"status_category": status_category,
|
|
285
|
+
"error_category": str(payload.get("error_category") or ""),
|
|
286
|
+
"tool_name_category": payload.get("tool_name")
|
|
287
|
+
if payload.get("tool_name") in EXPECTED_TOOLS
|
|
288
|
+
else "other_or_missing",
|
|
289
|
+
"context_status_category": str(payload.get("context_status") or "missing"),
|
|
290
|
+
"retention_category": str(payload.get("retention") or "missing"),
|
|
291
|
+
"retained_artifacts_empty_or_absent": retained in ([], None),
|
|
292
|
+
"raw_payload_echo_absent": "QCODER_CONTEXT_BRIDGE_SMOKE_MARKER" not in serialized,
|
|
293
|
+
"token_printed": False,
|
|
294
|
+
"raw_response_printed": False,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def run_smoke(*, base_url: str, token_file: str | Path) -> dict[str, Any]:
|
|
299
|
+
token_ok, token_category, _ = validate_token_file(token_file)
|
|
300
|
+
if not token_ok:
|
|
301
|
+
return {
|
|
302
|
+
"ok": False,
|
|
303
|
+
"metadata_only": True,
|
|
304
|
+
"token_file_category": token_category,
|
|
305
|
+
"token_printed": False,
|
|
306
|
+
"raw_token_printed": False,
|
|
307
|
+
"instruction_category": "create_local_chmod_600_token_file",
|
|
308
|
+
}
|
|
309
|
+
safe_text = (
|
|
310
|
+
"Share-safe current qCoder evidence summary. "
|
|
311
|
+
"Small Bell-state style circuit workflow. Evidence summary says the user prepared "
|
|
312
|
+
"a two-qubit entanglement example and wants bounded assistant context. "
|
|
313
|
+
"No raw QASM, no raw counts, no file paths, no backend identifiers, and no source code are included. "
|
|
314
|
+
"QCODER_CONTEXT_BRIDGE_SMOKE_MARKER"
|
|
315
|
+
)
|
|
316
|
+
cases = {
|
|
317
|
+
"guided_context_allowed": _case_summary(
|
|
318
|
+
payload=post_context_bridge(
|
|
319
|
+
base_url=base_url,
|
|
320
|
+
token_file=token_file,
|
|
321
|
+
tool_name="get_guided_evidence_context",
|
|
322
|
+
artifact_text=safe_text,
|
|
323
|
+
),
|
|
324
|
+
expected_success=True,
|
|
325
|
+
),
|
|
326
|
+
"prompt_context_allowed": _case_summary(
|
|
327
|
+
payload=post_context_bridge(
|
|
328
|
+
base_url=base_url,
|
|
329
|
+
token_file=token_file,
|
|
330
|
+
tool_name="create_prompt_context",
|
|
331
|
+
artifact_text=safe_text,
|
|
332
|
+
),
|
|
333
|
+
expected_success=True,
|
|
334
|
+
),
|
|
335
|
+
"evidence_context_pack_allowed": _case_summary(
|
|
336
|
+
payload=post_context_bridge(
|
|
337
|
+
base_url=base_url,
|
|
338
|
+
token_file=token_file,
|
|
339
|
+
tool_name="create_evidence_context_pack",
|
|
340
|
+
artifact_text=safe_text,
|
|
341
|
+
),
|
|
342
|
+
expected_success=True,
|
|
343
|
+
),
|
|
344
|
+
"context_session_card_allowed": _case_summary(
|
|
345
|
+
payload=post_context_bridge(
|
|
346
|
+
base_url=base_url,
|
|
347
|
+
token_file=token_file,
|
|
348
|
+
tool_name="create_context_session_card",
|
|
349
|
+
artifact_text=safe_text,
|
|
350
|
+
),
|
|
351
|
+
expected_success=True,
|
|
352
|
+
),
|
|
353
|
+
"run_readiness_card_allowed": _case_summary(
|
|
354
|
+
payload=post_context_bridge(
|
|
355
|
+
base_url=base_url,
|
|
356
|
+
token_file=token_file,
|
|
357
|
+
tool_name="create_run_readiness_card",
|
|
358
|
+
artifact_text=safe_text,
|
|
359
|
+
),
|
|
360
|
+
expected_success=True,
|
|
361
|
+
),
|
|
362
|
+
"raw_qasm_rejected": _case_summary(
|
|
363
|
+
payload=post_context_bridge(
|
|
364
|
+
base_url=base_url,
|
|
365
|
+
token_file=token_file,
|
|
366
|
+
tool_name="get_guided_evidence_context",
|
|
367
|
+
artifact_text="OPENQASM 2.0; qreg q[1];",
|
|
368
|
+
),
|
|
369
|
+
expected_success=False,
|
|
370
|
+
),
|
|
371
|
+
"repo_path_rejected": _case_summary(
|
|
372
|
+
payload=post_context_bridge(
|
|
373
|
+
base_url=base_url,
|
|
374
|
+
token_file=token_file,
|
|
375
|
+
tool_name="get_guided_evidence_context",
|
|
376
|
+
artifact_text="/home/private/project/source.py",
|
|
377
|
+
),
|
|
378
|
+
expected_success=False,
|
|
379
|
+
),
|
|
380
|
+
"artifact_lookup_rejected": _case_summary(
|
|
381
|
+
payload=post_context_bridge(
|
|
382
|
+
base_url=base_url,
|
|
383
|
+
token_file=token_file,
|
|
384
|
+
tool_name="get_guided_evidence_context",
|
|
385
|
+
artifact_text="artifact lookup request",
|
|
386
|
+
artifact_kind="server_artifact_id",
|
|
387
|
+
),
|
|
388
|
+
expected_success=False,
|
|
389
|
+
),
|
|
390
|
+
"unknown_tool_rejected": _case_summary(
|
|
391
|
+
payload=post_context_bridge(
|
|
392
|
+
base_url=base_url,
|
|
393
|
+
token_file=token_file,
|
|
394
|
+
tool_name="suggest_next_checks",
|
|
395
|
+
artifact_text=safe_text,
|
|
396
|
+
),
|
|
397
|
+
expected_success=False,
|
|
398
|
+
),
|
|
399
|
+
}
|
|
400
|
+
approved = [
|
|
401
|
+
"guided_context_allowed",
|
|
402
|
+
"prompt_context_allowed",
|
|
403
|
+
"evidence_context_pack_allowed",
|
|
404
|
+
"context_session_card_allowed",
|
|
405
|
+
"run_readiness_card_allowed",
|
|
406
|
+
]
|
|
407
|
+
unsafe = ["raw_qasm_rejected", "repo_path_rejected", "artifact_lookup_rejected", "unknown_tool_rejected"]
|
|
408
|
+
result = {
|
|
409
|
+
"ok": True,
|
|
410
|
+
"metadata_only": True,
|
|
411
|
+
"client_category": "qCoder Context Bridge MCP adapter",
|
|
412
|
+
"token_source_category": "local_chmod_600_file",
|
|
413
|
+
"tools_visible": list(EXPECTED_TOOLS),
|
|
414
|
+
"tools_exact": True,
|
|
415
|
+
"approved_tool_calls_passed": all(cases[name]["expected_outcome_met"] for name in approved),
|
|
416
|
+
"unsafe_calls_rejected": all(cases[name]["expected_outcome_met"] for name in unsafe),
|
|
417
|
+
"token_printed": False,
|
|
418
|
+
"raw_payload_echo": "no" if all(case["raw_payload_echo_absent"] for case in cases.values()) else "yes",
|
|
419
|
+
"retention_category": "process_and_discard_or_rejected",
|
|
420
|
+
"retained_artifacts_empty": "yes"
|
|
421
|
+
if all(case["retained_artifacts_empty_or_absent"] for case in cases.values())
|
|
422
|
+
else "no",
|
|
423
|
+
"payment_auth_billing_mutation": "no",
|
|
424
|
+
"public_claim_created": "no",
|
|
425
|
+
"source_modified": "no",
|
|
426
|
+
"cases": cases,
|
|
427
|
+
}
|
|
428
|
+
result["all_expected_outcomes_met"] = (
|
|
429
|
+
result["approved_tool_calls_passed"]
|
|
430
|
+
and result["unsafe_calls_rejected"]
|
|
431
|
+
and result["raw_payload_echo"] == "no"
|
|
432
|
+
and result["retained_artifacts_empty"] == "yes"
|
|
433
|
+
)
|
|
434
|
+
result["ok"] = bool(result["all_expected_outcomes_met"])
|
|
435
|
+
return result
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
439
|
+
parser = argparse.ArgumentParser(
|
|
440
|
+
prog="qcoder context-bridge",
|
|
441
|
+
description="qCoder Context Bridge adapter tools for eligible Explorer users.",
|
|
442
|
+
)
|
|
443
|
+
sub = parser.add_subparsers(dest="context_bridge_command")
|
|
444
|
+
mcp = sub.add_parser("mcp", help="Run or smoke-test the local Context Bridge MCP adapter.")
|
|
445
|
+
mcp_sub = mcp.add_subparsers(dest="mcp_command")
|
|
446
|
+
|
|
447
|
+
serve = mcp_sub.add_parser("serve", help="Run the local stdio MCP adapter.")
|
|
448
|
+
serve.add_argument(
|
|
449
|
+
"--token-file",
|
|
450
|
+
default=os.getenv("QCODER_CONTEXT_BRIDGE_TOKEN_FILE", str(default_token_file())),
|
|
451
|
+
help="Path to a local Context Bridge token file. The token value is never printed.",
|
|
452
|
+
)
|
|
453
|
+
serve.add_argument(
|
|
454
|
+
"--base-url",
|
|
455
|
+
default=os.getenv("QCODER_CONTEXT_BRIDGE_BASE_URL", DEFAULT_BASE_URL),
|
|
456
|
+
help="Context Bridge service base URL.",
|
|
457
|
+
)
|
|
458
|
+
serve.set_defaults(context_bridge_command="mcp", mcp_command="serve")
|
|
459
|
+
|
|
460
|
+
smoke = mcp_sub.add_parser("smoke", help="Run a sanitized adapter install smoke.")
|
|
461
|
+
smoke.add_argument(
|
|
462
|
+
"--token-file",
|
|
463
|
+
default=os.getenv("QCODER_CONTEXT_BRIDGE_TOKEN_FILE", str(default_token_file())),
|
|
464
|
+
help="Path to a local Context Bridge token file. The token value is never printed.",
|
|
465
|
+
)
|
|
466
|
+
smoke.add_argument(
|
|
467
|
+
"--base-url",
|
|
468
|
+
default=os.getenv("QCODER_CONTEXT_BRIDGE_BASE_URL", DEFAULT_BASE_URL),
|
|
469
|
+
help="Context Bridge service base URL.",
|
|
470
|
+
)
|
|
471
|
+
smoke.add_argument("--json", action="store_true", help="Emit sanitized JSON result.")
|
|
472
|
+
smoke.set_defaults(context_bridge_command="mcp", mcp_command="smoke")
|
|
473
|
+
return parser
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def main(argv: list[str] | None = None) -> int:
|
|
477
|
+
parser = build_parser()
|
|
478
|
+
args = parser.parse_args(argv)
|
|
479
|
+
if args.context_bridge_command is None or getattr(args, "mcp_command", None) is None:
|
|
480
|
+
parser.print_help()
|
|
481
|
+
return 0
|
|
482
|
+
if args.mcp_command == "serve":
|
|
483
|
+
return serve_stdio(base_url=args.base_url, token_file=args.token_file)
|
|
484
|
+
if args.mcp_command == "smoke":
|
|
485
|
+
result = run_smoke(base_url=args.base_url, token_file=args.token_file)
|
|
486
|
+
if args.json:
|
|
487
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
488
|
+
else:
|
|
489
|
+
print(f"qCoder Context Bridge adapter smoke: {'PASS' if result.get('ok') else 'CHECK'}")
|
|
490
|
+
print(f" token_file: {result.get('token_file_category', 'present_safe')}")
|
|
491
|
+
print(f" tools_exact: {result.get('tools_exact', False)}")
|
|
492
|
+
print(f" approved_tool_calls: {result.get('approved_tool_calls_passed', False)}")
|
|
493
|
+
print(f" unsafe_calls_rejected: {result.get('unsafe_calls_rejected', False)}")
|
|
494
|
+
print(f" token_printed: {result.get('token_printed', False)}")
|
|
495
|
+
return 0 if result.get("ok") else 1
|
|
496
|
+
parser.print_help()
|
|
497
|
+
return 0
|
|
498
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qcoder
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0a8
|
|
4
4
|
Summary: Quantum circuit analysis and structured feature extraction tools.
|
|
5
5
|
Author-email: Quantum Ready Solutions <support@qcoder.ai>
|
|
6
6
|
Maintainer-email: Quantum Ready Solutions <support@qcoder.ai>
|
|
@@ -41,15 +41,17 @@ The current public local path is **qCoder OSS**. OSS commands run locally and do
|
|
|
41
41
|
- `qcoder context`
|
|
42
42
|
- `qcoder review`
|
|
43
43
|
- `qcoder explorer` (Explorer Beta account-backed status/demo/evidence checks)
|
|
44
|
+
- `qcoder context-bridge` (token-backed Context Bridge MCP adapter for eligible Explorer users)
|
|
44
45
|
- `qcoder student` (temporary compatibility alias for Explorer Beta)
|
|
45
46
|
- `qcoder pro` (archived pilot/bootstrap client contract; non-confidential local plumbing only, not a current public product path)
|
|
46
47
|
|
|
47
48
|
## Current product boundaries
|
|
48
49
|
|
|
49
|
-
Public `qcoder` ships **OSS local commands
|
|
50
|
+
Public `qcoder` ships **OSS local commands**, Explorer Beta compatibility commands, and the Context Bridge adapter command for eligible Explorer users. Pro is not launched and is not a current public product path.
|
|
50
51
|
|
|
51
52
|
- **OSS commands** (`analyze`, `batch`, `context`, `review`) are Apache-2.0, local-first/offline, and useful without an account or token. They do not upload data, call a qCoder hosted service, or run QPU/simulator jobs.
|
|
52
53
|
- **Explorer Beta commands** (`qcoder explorer status`, `qcoder explorer demo`, `qcoder explorer evidence`) are account-backed checks for Explorer Beta status, built-in guided evidence samples, and derived-context guided evidence for user-owned OpenQASM 2 artifacts. The older `qcoder student ...` commands remain available as beta compatibility aliases.
|
|
54
|
+
- **Context Bridge adapter commands** (`qcoder context-bridge mcp serve`, `qcoder context-bridge mcp smoke`) are for eligible Explorer users with support-managed Context Bridge token access. They expose bounded current-evidence context tools to configured MCP clients and read tokens from a local token file.
|
|
53
55
|
- Explorer Beta custom evidence uses locally derived qCoder context/features. The CLI may read QASM locally, but the hosted request must not include raw QASM, raw source text, local paths, operation lists, raw counts, notebooks, prompts, tokens, auth headers, or cookies.
|
|
54
56
|
- Explorer Beta custom evidence is stateless in this v0 slice; it does not create persistent Explorer history.
|
|
55
57
|
- **`qcoder pro` bootstrap/workflow commands** are archived pilot/client-contract surfaces. They are not a Pro purchase path, not a current public signup path, and not generally available hosted Pro.
|
|
@@ -96,6 +98,8 @@ qcoder explorer evidence
|
|
|
96
98
|
qcoder explorer evidence --qasm path/to/circuit.qasm
|
|
97
99
|
qcoder explorer evidence --context-json preflight.context.json
|
|
98
100
|
qcoder explorer evidence --qasm path/to/circuit.qasm --out-json explorer.json --out-md explorer.md --share-safe
|
|
101
|
+
qcoder context-bridge mcp serve --help
|
|
102
|
+
qcoder context-bridge mcp smoke --token-file ~/.qcoder/context-bridge/token.txt --json
|
|
99
103
|
qcoder pro --help
|
|
100
104
|
```
|
|
101
105
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/__init__.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/cirq_intake.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/pennylane_intake.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/adapters/qiskit_intake.py
RENAMED
|
File without changes
|
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/compute_v0.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/glossary_v0.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/features/schema_v0.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/qasm2_regex_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/entangling_layers.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/gate_set_stats.py
RENAMED
|
File without changes
|
{qcoder-0.5.0a6 → qcoder-0.5.0a8}/src/qcoder/engines/feature_extraction/reps/interaction_graph.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|