fabric-vibecoding-settings 0.2__tar.gz → 0.4__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.
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/.gitignore +5 -1
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/PKG-INFO +3 -4
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/CLAUDE.md +2 -2
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/AGENTS.md +2 -2
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/config.toml +14 -1
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/setup/setup.ps1 +29 -13
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/setup/setup.sh +31 -14
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/runtime_cli.py +1 -1
- fabric_vibecoding_settings-0.4/cli/tools/auth/refresh.py +333 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/pyproject.toml +3 -6
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/Dockerfile +10 -4
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/__init__.py +1 -1
- fabric_vibecoding_settings-0.4/server/__main__.py +50 -0
- fabric_vibecoding_settings-0.4/server/api/__init__.py +0 -0
- fabric_vibecoding_settings-0.4/server/api/routes.py +810 -0
- fabric_vibecoding_settings-0.4/server/app.py +277 -0
- fabric_vibecoding_settings-0.4/server/auth/__init__.py +36 -0
- fabric_vibecoding_settings-0.4/server/auth/middleware.py +288 -0
- fabric_vibecoding_settings-0.4/server/auth/repository.py +228 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/builders/build-graph.py +2 -2
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/builders/graph_build/visualize.py +2 -2
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/builder.py +7 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/schema.py +5 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/search.py +17 -8
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/writes.py +23 -6
- fabric_vibecoding_settings-0.4/server/managed/.gitignore +2 -0
- fabric_vibecoding_settings-0.4/server/managed/content/memory/.gitkeep +0 -0
- fabric_vibecoding_settings-0.4/server/managed/content/rules/.gitkeep +0 -0
- fabric_vibecoding_settings-0.4/server/managed/content/skill-fixes/.gitkeep +0 -0
- fabric_vibecoding_settings-0.4/server/managed/skills/.gitkeep +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/data/mock-data-generator.py +108 -48
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/data/tools.py +12 -15
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/graph/tools.py +1 -1
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/_validation/install_package.py +2 -1
- fabric_vibecoding_settings-0.4/tests/test_admin_api.py +411 -0
- fabric_vibecoding_settings-0.4/tests/test_api_key_repository.py +162 -0
- fabric_vibecoding_settings-0.4/tests/test_auth_refresh.py +246 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_graph_search.py +2 -2
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_graph_visualize.py +8 -7
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_layout.py +6 -1
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_local_helper_wrappers.py +2 -4
- fabric_vibecoding_settings-0.4/tests/test_mock_data_generator.py +174 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_server_auth.py +82 -11
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_skill_split_coverage.py +1 -1
- fabric_vibecoding_settings-0.2/cli/tools/auth/refresh.py +0 -237
- fabric_vibecoding_settings-0.2/server/__main__.py +0 -29
- fabric_vibecoding_settings-0.2/server/app.py +0 -67
- fabric_vibecoding_settings-0.2/server/auth/__init__.py +0 -48
- fabric_vibecoding_settings-0.2/server/auth/middleware.py +0 -151
- fabric_vibecoding_settings-0.2/server/auth/repository.py +0 -267
- fabric_vibecoding_settings-0.2/server/docker-compose.yml +0 -53
- fabric_vibecoding_settings-0.2/tests/test_api_key_repository.py +0 -233
- fabric_vibecoding_settings-0.2/tests/test_auth_refresh.py +0 -136
- fabric_vibecoding_settings-0.2/tests/test_mock_data_generator.py +0 -158
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/LICENSE +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/README.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/agents/developer.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/agents/operator.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/agents/orchestrator.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/agents/tester.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/settings.local.json +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/agents/developer.toml +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/agents/operator.toml +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/agents/orchestrator.toml +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/agents/tester.toml +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/shared/.env.example +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/shared/.gitignore.fragment +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/shared/scaffold/data/sandbox/.gitkeep +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/shared/scaffold/workspace/.gitkeep +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/__main__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/cli.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/commands/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/commands/_common.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/commands/check.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/commands/install.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/commands/refresh.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/bootstrap.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/files.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/gitignore.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/markers.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/paths.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/profiles.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/core/version_check.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/src/fabric_skills_settings/logging_config.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lakehouse/list-tables.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lint/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lint/__main__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lint/core.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lint/de_faker_seed.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/lint/sec_no_hardcoded_secrets.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/notebook/build.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/notebook/deploy.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/notebook/smoke-test.ps1 +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/notebook/smoke-test.sh +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/pipeline/manage.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/precommit/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/precommit/pre-commit-check.ps1 +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/precommit/pre-commit-check.sh +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/workspace/init.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/workspace/pick.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/workspace/switch.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/tools/workspace/transfer.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/.dockerignore +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/audit.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/auth/tokens.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/builders/build-agent-capability-graph.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/builders/graph_build/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/builders/graph_build/agent_capabilities.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/diagnostics/smoke-test-diagnostics.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/entry.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/indexes/skills-index.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/integrations/rtk.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/layout/directory-layout.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/layout/tool-layout.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/rules/data-engineering.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/rules/fabric-platform.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/rules/notebook-authoring.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/rules/security.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/semantic/semantic-models.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/session/operating-rules.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/session/session-start.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/workflow/notebook-workflow.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/workflow/pipeline-structure.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/content/workflow/workspace-management.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/extract.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/lock.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/graph/store.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/caveman/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-ingest/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-model/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/diagnosing-opaque-failures.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/full-example.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/mlflow-platform-limits.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/must.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/prefer-avoid.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-notebook-loop/sections/the-loop.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-ops/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-pipeline/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-transform/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/fabric-validate/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/git-commit/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/grill-me/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/mock-data/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/prd/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/rtk/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/skills/semantic-model/SKILL.md +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/data/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/graph/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/semantic_model/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/semantic_model/inspect.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/semantic_model/tools.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/utils/script_runner.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/validate/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/validate/pipeline-lineage.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/server/tools/validate/tools.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/_validation/__init__.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/_validation/agent_guidance.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/conftest.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_agent_capability_graph.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_agent_guidance.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_build.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_extract.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_graph_builder.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_graph_store.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_install_package.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_lint.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_manage.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_mcp_wrappers.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_profile_entrypoints.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_semantic_model_inspect.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_validator_profile_minimal.py +0 -0
- {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/tests/test_version_check.py +0 -0
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
logs/
|
|
14
14
|
src/
|
|
15
15
|
!cli/src/
|
|
16
|
+
!frontend/src/
|
|
16
17
|
fabric_notebooks/
|
|
17
18
|
_delta_log/
|
|
18
19
|
*.checkpoint.parquet
|
|
@@ -60,4 +61,7 @@ Thumbs.db
|
|
|
60
61
|
.vscode/
|
|
61
62
|
.idea/
|
|
62
63
|
*.swp
|
|
63
|
-
.pytest_cache/
|
|
64
|
+
.pytest_cache/
|
|
65
|
+
.agents
|
|
66
|
+
.claude
|
|
67
|
+
skills-lock.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fabric-vibecoding-settings
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4
|
|
4
4
|
Summary: Install Microsoft Fabric agent profiles (Claude Code and Codex) into a target repository.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,13 +13,12 @@ Requires-Dist: rank-bm25>=0.2.2; extra == 'graph'
|
|
|
13
13
|
Provides-Extra: server
|
|
14
14
|
Requires-Dist: mcp>=1.0; extra == 'server'
|
|
15
15
|
Requires-Dist: networkx>=3.2; extra == 'server'
|
|
16
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'server'
|
|
16
17
|
Requires-Dist: pydantic>=2.7; extra == 'server'
|
|
17
18
|
Requires-Dist: pyjwt>=2.8; extra == 'server'
|
|
18
19
|
Requires-Dist: rank-bm25>=0.2.2; extra == 'server'
|
|
20
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'server'
|
|
19
21
|
Requires-Dist: uvicorn>=0.30; extra == 'server'
|
|
20
|
-
Provides-Extra: server-azure
|
|
21
|
-
Requires-Dist: azure-identity>=1.16; extra == 'server-azure'
|
|
22
|
-
Requires-Dist: azure-storage-blob>=12.19; extra == 'server-azure'
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
|
|
25
24
|
# Fabric Agent Pack
|
{fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/claude/CLAUDE.md
RENAMED
|
@@ -12,8 +12,8 @@ directly; use the graph.
|
|
|
12
12
|
## How to work
|
|
13
13
|
|
|
14
14
|
The `fabric-server` MCP is a separate process — a Docker container the
|
|
15
|
-
human starts with `docker compose up` from the source repo
|
|
16
|
-
|
|
15
|
+
human starts with `docker compose up` from the source repo root
|
|
16
|
+
before opening Claude. The project MCP config generated by
|
|
17
17
|
`fabric-vibe setup` points clients to its Fabric and graph tools. If
|
|
18
18
|
`tools/list` returns nothing the container probably isn't running.
|
|
19
19
|
|
{fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/AGENTS.md
RENAMED
|
@@ -12,8 +12,8 @@ directly; use the graph.
|
|
|
12
12
|
## How to work
|
|
13
13
|
|
|
14
14
|
The `fabric-server` MCP is a separate process — a Docker container the
|
|
15
|
-
human starts with `docker compose up` from the source repo
|
|
16
|
-
|
|
15
|
+
human starts with `docker compose up` from the source repo root
|
|
16
|
+
before opening Codex. The project MCP config generated by
|
|
17
17
|
`fabric-vibe setup` points clients to its Fabric and graph tools. If
|
|
18
18
|
`tools/list` returns nothing the container probably isn't running.
|
|
19
19
|
|
{fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.4}/cli/profiles/codex/config.toml
RENAMED
|
@@ -15,7 +15,20 @@ ignore_default_excludes = false
|
|
|
15
15
|
# Single HTTP MCP server, served by the local Docker container
|
|
16
16
|
# (`docker compose up` from the source repo's server/ directory).
|
|
17
17
|
# The target bootstrap patches this URL for the local Fabric MCP server.
|
|
18
|
-
[mcp_servers.
|
|
18
|
+
[mcp_servers.fabric_server]
|
|
19
19
|
url = "http://127.0.0.1:8000/mcp"
|
|
20
20
|
startup_timeout_ms = 20000
|
|
21
21
|
tool_timeout_ms = 120000
|
|
22
|
+
|
|
23
|
+
[mcp_servers.fabric_server.tools.graph_get_entry]
|
|
24
|
+
approval_mode = "approve"
|
|
25
|
+
|
|
26
|
+
[mcp_servers.fabric_server.tools.graph_get_linked]
|
|
27
|
+
approval_mode = "approve"
|
|
28
|
+
|
|
29
|
+
[mcp_servers.fabric_server.tools.graph_get_node]
|
|
30
|
+
approval_mode = "approve"
|
|
31
|
+
|
|
32
|
+
[mcp_servers.fabric_server.tools.graph_search]
|
|
33
|
+
approval_mode = "approve"
|
|
34
|
+
|
|
@@ -14,12 +14,11 @@
|
|
|
14
14
|
# 5. Prompts for FABRIC_CLIENT_SECRET and persists to the user's OS env
|
|
15
15
|
# (NOT .env - secrets stay in the OS env).
|
|
16
16
|
# 6. Prompts for MCP_SERVER_URL.
|
|
17
|
-
# 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.
|
|
17
|
+
# 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric_server]
|
|
18
18
|
# url (if installed).
|
|
19
|
-
# 8. Prompts for
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# into the MCP client headers.
|
|
19
|
+
# 8. Prompts for FABRIC_MCP_AUTH_URL (auth service base URL; defaults to
|
|
20
|
+
# MCP_SERVER_URL/api/auth) and FABRIC_MCP_API_KEY (from server admin).
|
|
21
|
+
# Runs fabric-vibe auth refresh to obtain a short-lived JWT.
|
|
23
22
|
# 9. Verifies SPN auth by calling `fab api workspaces`.
|
|
24
23
|
# 10. Runs fabric-vibe workspace init to populate workspaces.json.
|
|
25
24
|
# 11. Prompts to select the active workspace.
|
|
@@ -179,11 +178,28 @@ if ($env:MCP_SERVER_URL) {
|
|
|
179
178
|
if (-not $mcpServerUrl) { $mcpServerUrl = "http://127.0.0.1:8000" }
|
|
180
179
|
}
|
|
181
180
|
|
|
181
|
+
# -- MCP auth URL ------------------------------------------------------------
|
|
182
|
+
# Base URL for the auth service (without /login or /refresh). This varies by
|
|
183
|
+
# deployment — e.g. a reverse proxy may expose it at /server/auth rather than
|
|
184
|
+
# /api/auth. fabric-vibe auth refresh appends /login or /refresh as needed.
|
|
185
|
+
Write-Host ""
|
|
186
|
+
Write-Host "-- MCP auth URL"
|
|
187
|
+
$defaultAuthUrl = "$($mcpServerUrl.TrimEnd('/'))/api/auth"
|
|
188
|
+
if ($env:FABRIC_MCP_AUTH_URL) {
|
|
189
|
+
Write-Host " FABRIC_MCP_AUTH_URL already set - keeping $($env:FABRIC_MCP_AUTH_URL)"
|
|
190
|
+
} else {
|
|
191
|
+
$authUrl = Read-Host " FABRIC_MCP_AUTH_URL [$defaultAuthUrl]"
|
|
192
|
+
if (-not $authUrl) { $authUrl = $defaultAuthUrl }
|
|
193
|
+
[System.Environment]::SetEnvironmentVariable("FABRIC_MCP_AUTH_URL", $authUrl, "User")
|
|
194
|
+
$env:FABRIC_MCP_AUTH_URL = $authUrl
|
|
195
|
+
$Actions.Add("FABRIC_MCP_AUTH_URL persisted to OS user environment (registry)")
|
|
196
|
+
}
|
|
197
|
+
|
|
182
198
|
# -- MCP API key -------------------------------------------------------------
|
|
183
199
|
# The MCP server validates this key and issues a short-lived JWT. fabric-vibe
|
|
184
|
-
# auth refresh reads FABRIC_MCP_API_KEY, calls /
|
|
185
|
-
# into the MCP client
|
|
186
|
-
# (registry), not to .env, so it is never committed
|
|
200
|
+
# auth refresh reads FABRIC_MCP_API_KEY, calls FABRIC_MCP_AUTH_URL/login, and
|
|
201
|
+
# injects the JWT into the MCP client auth config. The key is persisted to the OS
|
|
202
|
+
# user environment (registry), not to .env, so it is never committed.
|
|
187
203
|
Write-Host ""
|
|
188
204
|
Write-Host "-- MCP API key"
|
|
189
205
|
if ($env:FABRIC_MCP_API_KEY) {
|
|
@@ -198,22 +214,22 @@ if ($env:FABRIC_MCP_API_KEY) {
|
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
# -- MCP client config (.mcp.json) -------------------------------------------
|
|
201
|
-
# Write url only; fabric-vibe auth refresh calls /
|
|
202
|
-
# and writes the returned JWT into the MCP client
|
|
217
|
+
# Write url only; fabric-vibe auth refresh calls FABRIC_MCP_AUTH_URL/login with
|
|
218
|
+
# FABRIC_MCP_API_KEY and writes the returned JWT into the MCP client auth config below.
|
|
203
219
|
$McpJson = Join-Path $ProjectRoot ".mcp.json"
|
|
204
220
|
$mcpUrl = "$($mcpServerUrl.TrimEnd('/'))/mcp"
|
|
205
221
|
$mcpDoc = [ordered]@{ mcpServers = [ordered]@{ "fabric-server" = [ordered]@{ type = "http"; url = $mcpUrl } } }
|
|
206
222
|
$mcpDoc | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $McpJson -Encoding utf8
|
|
207
223
|
$Actions.Add(".mcp.json written ($mcpUrl)")
|
|
208
224
|
|
|
209
|
-
# Keep Codex's MCP config url aligned (auth
|
|
225
|
+
# Keep Codex's MCP config url aligned (auth config is written by fabric-vibe auth refresh).
|
|
210
226
|
$CodexConfig = Join-Path $ProjectRoot ".codex/config.toml"
|
|
211
227
|
if (Test-Path -LiteralPath $CodexConfig) {
|
|
212
228
|
$lines = [System.IO.File]::ReadAllLines($CodexConfig)
|
|
213
229
|
$out = [System.Collections.Generic.List[string]]::new()
|
|
214
230
|
$inFabric = $false
|
|
215
231
|
foreach ($line in $lines) {
|
|
216
|
-
if ($line -match '^\[mcp_servers\.fabric-server\]') { $inFabric = $true; $out.Add(
|
|
232
|
+
if ($line -match '^\[mcp_servers\.(?:"fabric-server"|fabric-server|fabric_server)\]') { $inFabric = $true; $out.Add("[mcp_servers.fabric_server]"); continue }
|
|
217
233
|
if ($line -match '^\[') { $inFabric = $false }
|
|
218
234
|
if ($inFabric -and $line -match '^\s*url\s*=') { $out.Add("url = `"$mcpUrl`""); continue }
|
|
219
235
|
$out.Add($line)
|
|
@@ -227,7 +243,7 @@ Write-Host ""
|
|
|
227
243
|
Write-Host "-- MCP client token"
|
|
228
244
|
fabric-vibe auth refresh
|
|
229
245
|
if ($LASTEXITCODE -ne 0) { Write-Warning "MCP token refresh failed; run 'fabric-vibe auth refresh' manually." }
|
|
230
|
-
else { $Actions.Add("MCP token written to MCP client
|
|
246
|
+
else { $Actions.Add("MCP token written to MCP client auth config") }
|
|
231
247
|
|
|
232
248
|
# -- Authenticate ------------------------------------------------------------
|
|
233
249
|
# Use explicit SPN login. fab does NOT pick up AZURE_* env vars implicitly —
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
# Scope: configure the user's laptop so Claude/Codex can talk to the Fabric
|
|
5
5
|
# MCP server AND drive the local Fabric CLI (fab) for notebook / pipeline /
|
|
6
6
|
# lakehouse / workspace work. The MCP server itself lives in Docker — start
|
|
7
|
-
# it separately with `docker compose up --build` from the source repo
|
|
8
|
-
# `server/` directory.
|
|
7
|
+
# it separately with `docker compose up --build` from the source repo root.
|
|
9
8
|
#
|
|
10
9
|
# This script:
|
|
11
10
|
# 1. Verifies uv is installed.
|
|
@@ -16,10 +15,11 @@
|
|
|
16
15
|
# profile (NOT .env — secrets stay in the OS env).
|
|
17
16
|
# 6. Prompts for MCP_SERVER_URL and the FABRIC_MCP_API_KEY (from server admin).
|
|
18
17
|
# The API key is persisted to the shell profile (chmod 600), not .env.
|
|
19
|
-
# 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.
|
|
18
|
+
# 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric_server]
|
|
20
19
|
# url (if installed).
|
|
21
|
-
# 8.
|
|
22
|
-
#
|
|
20
|
+
# 8. Prompts for FABRIC_MCP_AUTH_URL (auth service base URL; defaults to
|
|
21
|
+
# MCP_SERVER_URL/api/auth) and FABRIC_MCP_API_KEY (from server admin).
|
|
22
|
+
# Runs fabric-vibe auth refresh to obtain a short-lived JWT.
|
|
23
23
|
# 9. Verifies SPN auth by calling `fab api workspaces`.
|
|
24
24
|
# 10. Runs fabric-vibe workspace init to populate workspaces.json.
|
|
25
25
|
# 11. Prompts to select the active workspace.
|
|
@@ -205,11 +205,28 @@ else
|
|
|
205
205
|
mcp_server_url="${mcp_server_url:-http://127.0.0.1:8000}"
|
|
206
206
|
fi
|
|
207
207
|
|
|
208
|
+
# ── MCP auth URL ─────────────────────────────────────────────────────────────
|
|
209
|
+
# Base URL for the auth service (without /login or /refresh). This varies by
|
|
210
|
+
# deployment — e.g. a reverse proxy may expose it at /server/auth rather than
|
|
211
|
+
# /api/auth. fabric-vibe auth refresh appends /login or /refresh as needed.
|
|
212
|
+
echo ""
|
|
213
|
+
echo "-- MCP auth URL"
|
|
214
|
+
_default_auth_url="${mcp_server_url%/}/api/auth"
|
|
215
|
+
if [[ -n "${FABRIC_MCP_AUTH_URL:-}" ]]; then
|
|
216
|
+
echo " FABRIC_MCP_AUTH_URL already set — keeping ${FABRIC_MCP_AUTH_URL}"
|
|
217
|
+
else
|
|
218
|
+
read -rp " FABRIC_MCP_AUTH_URL [${_default_auth_url}]: " FABRIC_MCP_AUTH_URL
|
|
219
|
+
FABRIC_MCP_AUTH_URL="${FABRIC_MCP_AUTH_URL:-${_default_auth_url}}"
|
|
220
|
+
export FABRIC_MCP_AUTH_URL
|
|
221
|
+
persist_secret "FABRIC_MCP_AUTH_URL" "${FABRIC_MCP_AUTH_URL}"
|
|
222
|
+
actions+=("FABRIC_MCP_AUTH_URL persisted to shell profile")
|
|
223
|
+
fi
|
|
224
|
+
|
|
208
225
|
# ── MCP API key ───────────────────────────────────────────────────────────────
|
|
209
226
|
# The MCP server validates this key and issues a short-lived JWT. fabric-vibe
|
|
210
|
-
# auth refresh reads FABRIC_MCP_API_KEY, calls /
|
|
211
|
-
# into the MCP client
|
|
212
|
-
# 600), not to .env, so it is never committed
|
|
227
|
+
# auth refresh reads FABRIC_MCP_API_KEY, calls FABRIC_MCP_AUTH_URL/login, and
|
|
228
|
+
# injects the JWT into the MCP client auth config. The key is persisted to the
|
|
229
|
+
# shell profile (chmod 600), not to .env, so it is never committed.
|
|
213
230
|
echo ""
|
|
214
231
|
echo "-- MCP API key"
|
|
215
232
|
if [[ -n "${FABRIC_MCP_API_KEY:-}" ]]; then
|
|
@@ -224,8 +241,8 @@ else
|
|
|
224
241
|
fi
|
|
225
242
|
|
|
226
243
|
# ── MCP client config (.mcp.json) ─────────────────────────────────────────────
|
|
227
|
-
# Write url only; fabric-vibe auth refresh calls /
|
|
228
|
-
# and writes the returned JWT into the MCP client
|
|
244
|
+
# Write url only; fabric-vibe auth refresh calls FABRIC_MCP_AUTH_URL/login with
|
|
245
|
+
# FABRIC_MCP_API_KEY and writes the returned JWT into the MCP client auth config below.
|
|
229
246
|
MCP_JSON="${PROJECT_ROOT}/.mcp.json"
|
|
230
247
|
mcp_url="${mcp_server_url%/}/mcp"
|
|
231
248
|
cat > "$MCP_JSON" <<EOF
|
|
@@ -240,12 +257,12 @@ cat > "$MCP_JSON" <<EOF
|
|
|
240
257
|
EOF
|
|
241
258
|
actions+=(".mcp.json written (${mcp_url})")
|
|
242
259
|
|
|
243
|
-
# Keep Codex's MCP config url aligned (auth
|
|
260
|
+
# Keep Codex's MCP config url aligned (auth config written by fabric-vibe auth refresh).
|
|
244
261
|
CODEX_CONFIG="${PROJECT_ROOT}/.codex/config.toml"
|
|
245
262
|
if [[ -f "$CODEX_CONFIG" ]]; then
|
|
246
263
|
_codex_tmp="$(mktemp)"
|
|
247
264
|
awk -v url="$mcp_url" '
|
|
248
|
-
/^\[mcp_servers\.fabric-server\]/ { print; in_section=1; next }
|
|
265
|
+
/^\[mcp_servers\.("fabric-server"|fabric-server|fabric_server)\]/ { print "[mcp_servers.fabric_server]"; in_section=1; next }
|
|
249
266
|
/^\[/ { in_section=0 }
|
|
250
267
|
in_section && /^[[:space:]]*url[[:space:]]*=/ { print "url = \"" url "\""; next }
|
|
251
268
|
{ print }
|
|
@@ -257,7 +274,7 @@ fi
|
|
|
257
274
|
echo ""
|
|
258
275
|
echo "-- MCP client token"
|
|
259
276
|
if fabric-vibe auth refresh; then
|
|
260
|
-
actions+=("MCP token written to MCP client
|
|
277
|
+
actions+=("MCP token written to MCP client auth config")
|
|
261
278
|
else
|
|
262
279
|
echo " MCP token refresh failed; run 'fabric-vibe auth refresh' manually." >&2
|
|
263
280
|
fi
|
|
@@ -304,6 +321,6 @@ echo "Setup complete."
|
|
|
304
321
|
for a in "${actions[@]}"; do echo "- $a"; done
|
|
305
322
|
echo ""
|
|
306
323
|
echo "Next: start the Fabric MCP server."
|
|
307
|
-
echo " cd <fabric-vibecoding-settings
|
|
324
|
+
echo " cd <fabric-vibecoding-settings>"
|
|
308
325
|
echo " docker compose up --build"
|
|
309
326
|
echo "Then open Claude Code (or Codex) in this project."
|
|
@@ -198,7 +198,7 @@ def precommit(ctx: typer.Context) -> None:
|
|
|
198
198
|
|
|
199
199
|
@auth_app.command("refresh", context_settings=_PASSTHROUGH_CONTEXT)
|
|
200
200
|
def auth_refresh(ctx: typer.Context) -> None:
|
|
201
|
-
"""
|
|
201
|
+
"""Exchange FABRIC_MCP_API_KEY for a short-lived JWT and inject it into MCP client configs."""
|
|
202
202
|
script = _require_tool_script(Path("auth/refresh.py"))
|
|
203
203
|
raise typer.Exit(code=_dispatch_python(script, ctx.args))
|
|
204
204
|
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Fetch a short-lived JWT from the MCP server using an API key.
|
|
3
|
+
|
|
4
|
+
Saves the JWT into the Claude and Codex MCP client configuration files so
|
|
5
|
+
Claude Code and Codex pick it up on next session load. Each run mints a fresh
|
|
6
|
+
token so client configs recover from server restarts, signing-secret rotation,
|
|
7
|
+
or server-side token-store resets.
|
|
8
|
+
|
|
9
|
+
The API key is read from FABRIC_MCP_API_KEY. If that variable is not set,
|
|
10
|
+
the command prompts for it interactively (input is hidden).
|
|
11
|
+
|
|
12
|
+
The auth service base URL is read from FABRIC_MCP_AUTH_URL (e.g.
|
|
13
|
+
https://host/server/auth). If not set, it defaults to
|
|
14
|
+
{MCP_SERVER_URL}/api/auth. Pass --auth-url to override at runtime.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
fabric-vibe auth refresh [--server-url https://host:port]
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import base64
|
|
23
|
+
import getpass
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import re
|
|
27
|
+
import sys
|
|
28
|
+
import time
|
|
29
|
+
import urllib.error
|
|
30
|
+
import urllib.parse
|
|
31
|
+
import urllib.request
|
|
32
|
+
from datetime import datetime
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
ROOT = Path.cwd()
|
|
36
|
+
|
|
37
|
+
_STATE_DIR = Path.home() / ".fabric-vibecoding"
|
|
38
|
+
_TOKEN_FILE = _STATE_DIR / "mcp-token.json"
|
|
39
|
+
_REFRESH_MARGIN = 300 # refresh when fewer than 5 min remain
|
|
40
|
+
_CODEX_MCP_SECTION = "mcp_servers.fabric_server"
|
|
41
|
+
_CODEX_MCP_HEADERS_SECTION = "mcp_servers.fabric_server.http_headers"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ── JWT expiry helper (no external deps) ─────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
def _jwt_expiry(token: str) -> float | None:
|
|
47
|
+
"""Extract the exp claim from a JWT payload without verifying the signature."""
|
|
48
|
+
try:
|
|
49
|
+
parts = token.split(".")
|
|
50
|
+
if len(parts) != 3:
|
|
51
|
+
return None
|
|
52
|
+
padding = "=" * (-len(parts[1]) % 4)
|
|
53
|
+
payload = json.loads(base64.urlsafe_b64decode(parts[1] + padding).decode("utf-8"))
|
|
54
|
+
return float(payload["exp"])
|
|
55
|
+
except Exception:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ── Persisted token ───────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def _load_saved_token() -> tuple[str, float] | tuple[None, None]:
|
|
62
|
+
if not _TOKEN_FILE.exists():
|
|
63
|
+
return None, None
|
|
64
|
+
try:
|
|
65
|
+
data = json.loads(_TOKEN_FILE.read_text(encoding="utf-8"))
|
|
66
|
+
return data.get("token"), float(data.get("expires_at", 0))
|
|
67
|
+
except Exception:
|
|
68
|
+
return None, None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _save_token(token: str, expires_at: float) -> None:
|
|
72
|
+
_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
_TOKEN_FILE.write_text(
|
|
74
|
+
json.dumps({"token": token, "expires_at": expires_at}), encoding="utf-8"
|
|
75
|
+
)
|
|
76
|
+
try:
|
|
77
|
+
os.chmod(_TOKEN_FILE, 0o600)
|
|
78
|
+
except OSError:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ── URL resolution ────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
def _resolve_server_url(argv: list[str], root: Path) -> str:
|
|
85
|
+
"""Resolve MCP server base URL from --server-url arg, env, or .mcp.json."""
|
|
86
|
+
if "--server-url" in argv:
|
|
87
|
+
idx = argv.index("--server-url")
|
|
88
|
+
if idx + 1 < len(argv):
|
|
89
|
+
return argv[idx + 1].rstrip("/")
|
|
90
|
+
url = os.environ.get("MCP_SERVER_URL", "").strip().rstrip("/")
|
|
91
|
+
if url:
|
|
92
|
+
return url
|
|
93
|
+
mcp_json = root / ".mcp.json"
|
|
94
|
+
if mcp_json.exists():
|
|
95
|
+
try:
|
|
96
|
+
doc = json.loads(mcp_json.read_text(encoding="utf-8-sig"))
|
|
97
|
+
raw = doc.get("mcpServers", {}).get("fabric-server", {}).get("url", "")
|
|
98
|
+
if raw:
|
|
99
|
+
return raw.rstrip("/").removesuffix("/mcp")
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
return "http://127.0.0.1:8000"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _resolve_auth_base_url(argv: list[str], server_url: str) -> str:
|
|
106
|
+
"""Resolve auth service base URL.
|
|
107
|
+
|
|
108
|
+
Priority: --auth-url flag > FABRIC_MCP_AUTH_URL env var > {server_url}/api/auth.
|
|
109
|
+
|
|
110
|
+
The base URL is the path up to but not including /login or /refresh,
|
|
111
|
+
e.g. https://host/server/auth or https://host/api/auth.
|
|
112
|
+
"""
|
|
113
|
+
if "--auth-url" in argv:
|
|
114
|
+
idx = argv.index("--auth-url")
|
|
115
|
+
if idx + 1 < len(argv):
|
|
116
|
+
return argv[idx + 1].rstrip("/")
|
|
117
|
+
url = os.environ.get("FABRIC_MCP_AUTH_URL", "").strip().rstrip("/")
|
|
118
|
+
if url:
|
|
119
|
+
return url
|
|
120
|
+
return f"{server_url}/api/auth"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ── API key resolution ────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
def _prompt_api_key() -> str | None:
|
|
126
|
+
"""Return the API key from env or an interactive prompt. Returns None to skip."""
|
|
127
|
+
key = os.environ.get("FABRIC_MCP_API_KEY", "").strip()
|
|
128
|
+
if key:
|
|
129
|
+
return key
|
|
130
|
+
print(" FABRIC_MCP_API_KEY is not set.")
|
|
131
|
+
try:
|
|
132
|
+
key = getpass.getpass(" Enter your MCP API key (empty to skip): ").strip()
|
|
133
|
+
except (EOFError, KeyboardInterrupt):
|
|
134
|
+
print()
|
|
135
|
+
return None
|
|
136
|
+
return key or None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ── Token fetch / refresh ─────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
def _post(url: str, body: bytes, headers: dict[str, str]) -> tuple[int, dict]:
|
|
142
|
+
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
|
|
143
|
+
try:
|
|
144
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
145
|
+
return resp.status, json.loads(resp.read().decode("utf-8"))
|
|
146
|
+
except urllib.error.HTTPError as exc:
|
|
147
|
+
try:
|
|
148
|
+
return exc.code, json.loads(exc.read().decode("utf-8"))
|
|
149
|
+
except Exception:
|
|
150
|
+
return exc.code, {"error": exc.reason}
|
|
151
|
+
except Exception as exc:
|
|
152
|
+
raise SystemExit(f"Cannot reach MCP server at {url}: {exc}") from exc
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _discover_token_endpoint(server_url: str) -> str | None:
|
|
156
|
+
"""Fetch /.well-known/oauth-authorization-server and return the token_endpoint.
|
|
157
|
+
|
|
158
|
+
Returns None if the endpoint is unreachable or the server has no OAuth2 AS.
|
|
159
|
+
"""
|
|
160
|
+
discovery_url = f"{server_url}/.well-known/oauth-authorization-server"
|
|
161
|
+
req = urllib.request.Request(discovery_url, method="GET")
|
|
162
|
+
try:
|
|
163
|
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
164
|
+
if resp.status == 200:
|
|
165
|
+
meta = json.loads(resp.read().decode("utf-8"))
|
|
166
|
+
return meta.get("token_endpoint") or None
|
|
167
|
+
except Exception:
|
|
168
|
+
pass
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _fetch_token(server_url: str, api_key: str) -> tuple[str, float]:
|
|
173
|
+
"""Exchange an API key for a JWT.
|
|
174
|
+
|
|
175
|
+
Discovers the token endpoint from the server's OAuth2 AS metadata
|
|
176
|
+
(``/.well-known/oauth-authorization-server``) and uses the standard
|
|
177
|
+
client_credentials grant. Falls back to the legacy JSON endpoint
|
|
178
|
+
(``{server_url}/api/auth/login``) for servers that pre-date OAuth2 support.
|
|
179
|
+
"""
|
|
180
|
+
# ── OAuth2 client credentials — endpoint discovered at runtime ────────────
|
|
181
|
+
oauth_url = _discover_token_endpoint(server_url)
|
|
182
|
+
if oauth_url:
|
|
183
|
+
form_body = urllib.parse.urlencode({
|
|
184
|
+
"grant_type": "client_credentials",
|
|
185
|
+
"client_id": api_key,
|
|
186
|
+
}).encode("ascii")
|
|
187
|
+
status, body = _post(oauth_url, form_body, {"Content-Type": "application/x-www-form-urlencoded"})
|
|
188
|
+
if status == 200:
|
|
189
|
+
token = body.get("access_token", "")
|
|
190
|
+
if token:
|
|
191
|
+
expires_in = body.get("expires_in", 3600)
|
|
192
|
+
expires_at = body.get("expires_at") or (time.time() + expires_in)
|
|
193
|
+
return token, float(expires_at)
|
|
194
|
+
if status not in (404, 405):
|
|
195
|
+
raise SystemExit(f"OAuth2 token request failed ({status}): {body.get('error', body)}")
|
|
196
|
+
|
|
197
|
+
# ── Legacy JSON endpoint (backward compat) ────────────────────────────────
|
|
198
|
+
legacy_url = f"{server_url}/api/auth/login"
|
|
199
|
+
status, body = _post(
|
|
200
|
+
legacy_url,
|
|
201
|
+
json.dumps({"api_key": api_key}).encode("utf-8"),
|
|
202
|
+
{"Content-Type": "application/json"},
|
|
203
|
+
)
|
|
204
|
+
if status == 404:
|
|
205
|
+
raise SystemExit(
|
|
206
|
+
f"No auth endpoint found at {server_url}.\n"
|
|
207
|
+
"Check FABRIC_MCP_API_KEY and MCP_SERVER_URL."
|
|
208
|
+
)
|
|
209
|
+
if status != 200:
|
|
210
|
+
raise SystemExit(f"Login failed ({status}): {body.get('error', body)}")
|
|
211
|
+
token = body.get("token", "")
|
|
212
|
+
if not token:
|
|
213
|
+
raise SystemExit(f"Server returned no token: {body}")
|
|
214
|
+
expires_at = body.get("expires_at") or _jwt_expiry(token) or (time.time() + 3600)
|
|
215
|
+
return token, float(expires_at)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _refresh_token(auth_base_url: str, current_token: str) -> tuple[str, float] | tuple[None, None]:
|
|
219
|
+
"""POST to {auth_base_url}/refresh. Returns (new_token, expires_at) or (None, None)."""
|
|
220
|
+
status, body = _post(
|
|
221
|
+
f"{auth_base_url}/refresh",
|
|
222
|
+
b"",
|
|
223
|
+
{"Content-Type": "application/json", "Authorization": f"Bearer {current_token}"},
|
|
224
|
+
)
|
|
225
|
+
if status != 200:
|
|
226
|
+
return None, None
|
|
227
|
+
token = body.get("token", "")
|
|
228
|
+
if not token:
|
|
229
|
+
return None, None
|
|
230
|
+
expires_at = body.get("expires_at") or _jwt_expiry(token) or (time.time() + 3600)
|
|
231
|
+
return token, float(expires_at)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ── Config injection ──────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
def update_mcp_json(root: Path, token: str) -> None:
|
|
237
|
+
mcp_json = root / ".mcp.json"
|
|
238
|
+
if not mcp_json.exists():
|
|
239
|
+
print(f" .mcp.json not found at {mcp_json}; skipping", file=sys.stderr)
|
|
240
|
+
return
|
|
241
|
+
doc = json.loads(mcp_json.read_text(encoding="utf-8-sig"))
|
|
242
|
+
(
|
|
243
|
+
doc.setdefault("mcpServers", {})
|
|
244
|
+
.setdefault("fabric-server", {})
|
|
245
|
+
.setdefault("headers", {})["Authorization"]
|
|
246
|
+
) = f"Bearer {token}"
|
|
247
|
+
mcp_json.write_text(json.dumps(doc, indent=2) + "\n", encoding="utf-8")
|
|
248
|
+
print(" Updated .mcp.json")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _normalize_codex_mcp_section_names(text: str) -> str:
|
|
252
|
+
"""Migrate old hyphenated Codex MCP ids to the stable tool namespace id."""
|
|
253
|
+
text = re.sub(
|
|
254
|
+
r'^\[mcp_servers\.(?:"fabric-server"|fabric-server)\]$',
|
|
255
|
+
f"[{_CODEX_MCP_SECTION}]",
|
|
256
|
+
text,
|
|
257
|
+
flags=re.MULTILINE,
|
|
258
|
+
)
|
|
259
|
+
return re.sub(
|
|
260
|
+
r'^\[mcp_servers\.(?:"fabric-server"|fabric-server|fabric_server)\.(?:headers|http_headers)\]$',
|
|
261
|
+
f"[{_CODEX_MCP_HEADERS_SECTION}]",
|
|
262
|
+
text,
|
|
263
|
+
flags=re.MULTILINE,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _upsert_toml_section_key(text: str, section: str, key: str, value: str) -> str:
|
|
268
|
+
header = f"[{section}]"
|
|
269
|
+
key_line = f'{key} = "{value}"'
|
|
270
|
+
section_re = re.compile(
|
|
271
|
+
rf"(^\[{re.escape(section)}\]\r?\n)(.*?)(?=^\[|\Z)",
|
|
272
|
+
flags=re.MULTILINE | re.DOTALL,
|
|
273
|
+
)
|
|
274
|
+
match = section_re.search(text)
|
|
275
|
+
if not match:
|
|
276
|
+
return text.rstrip() + f"\n\n{header}\n{key_line}\n"
|
|
277
|
+
|
|
278
|
+
body = match.group(2)
|
|
279
|
+
if re.search(rf"^\s*{re.escape(key)}\s*=", body, flags=re.MULTILINE):
|
|
280
|
+
body = re.sub(
|
|
281
|
+
rf"^\s*{re.escape(key)}\s*=.*$",
|
|
282
|
+
key_line,
|
|
283
|
+
body,
|
|
284
|
+
flags=re.MULTILINE,
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
body = body.rstrip() + f"\n{key_line}\n"
|
|
288
|
+
return text[: match.start(2)] + body + text[match.end(2):]
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def update_codex_config(root: Path, token: str) -> None:
|
|
292
|
+
config = root / ".codex" / "config.toml"
|
|
293
|
+
if not config.exists():
|
|
294
|
+
return
|
|
295
|
+
text = _normalize_codex_mcp_section_names(config.read_text(encoding="utf-8"))
|
|
296
|
+
text = _upsert_toml_section_key(
|
|
297
|
+
text,
|
|
298
|
+
_CODEX_MCP_HEADERS_SECTION,
|
|
299
|
+
"Authorization",
|
|
300
|
+
f"Bearer {token}",
|
|
301
|
+
)
|
|
302
|
+
config.write_text(text, encoding="utf-8")
|
|
303
|
+
print(" Updated .codex/config.toml")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
def main(argv: list[str] | None = None) -> int:
|
|
309
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
310
|
+
|
|
311
|
+
api_key = _prompt_api_key()
|
|
312
|
+
if not api_key:
|
|
313
|
+
print(" No API key provided — skipping auth.")
|
|
314
|
+
return 0
|
|
315
|
+
|
|
316
|
+
server_url = _resolve_server_url(argv, ROOT)
|
|
317
|
+
# Always mint a fresh token. A cached JWT can be timestamp-valid but still
|
|
318
|
+
# rejected after a server restart, signing-secret rotation, or JTI-store reset.
|
|
319
|
+
# _fetch_token tries OAuth2 client_credentials first, legacy JSON second.
|
|
320
|
+
token, expires_at = _fetch_token(server_url, api_key)
|
|
321
|
+
|
|
322
|
+
_save_token(token, expires_at)
|
|
323
|
+
update_mcp_json(ROOT, token)
|
|
324
|
+
update_codex_config(ROOT, token)
|
|
325
|
+
|
|
326
|
+
expiry_str = datetime.fromtimestamp(expires_at).strftime("%Y-%m-%d %H:%M:%S")
|
|
327
|
+
print(f"\nMCP token written to client config. Expires: {expiry_str}")
|
|
328
|
+
print("Reload your Claude Code / Codex session to pick up the new token.")
|
|
329
|
+
return 0
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
if __name__ == "__main__":
|
|
333
|
+
sys.exit(main())
|
|
@@ -19,7 +19,7 @@ fabric-vibecoding-agents = "fabric_skills_settings.cli:app"
|
|
|
19
19
|
fabric-vibe = "fabric_skills_settings.runtime_cli:app"
|
|
20
20
|
|
|
21
21
|
[dependency-groups]
|
|
22
|
-
dev = ["pytest>=8", "Faker>=26", "mimesis>=18", "scikit-learn>=1.5", "pandas>=2", "networkx>=3.2", "rank_bm25>=0.2.2", "PyJWT>=2.8", "mcp>=1.0", "uvicorn>=0.30"]
|
|
22
|
+
dev = ["pytest>=8", "Faker>=26", "mimesis>=18", "scikit-learn>=1.5", "pandas>=2", "networkx>=3.2", "rank_bm25>=0.2.2", "PyJWT>=2.8", "mcp>=1.0", "uvicorn>=0.30", "sqlalchemy>=2.0", "psycopg2-binary>=2.9"]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
25
|
graph = ["networkx>=3.2", "rank_bm25>=0.2.2"]
|
|
@@ -30,11 +30,8 @@ server = [
|
|
|
30
30
|
"networkx>=3.2",
|
|
31
31
|
"rank_bm25>=0.2.2",
|
|
32
32
|
"PyJWT>=2.8",
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
server-azure = [
|
|
36
|
-
"azure-storage-blob>=12.19",
|
|
37
|
-
"azure-identity>=1.16",
|
|
33
|
+
"sqlalchemy>=2.0",
|
|
34
|
+
"psycopg2-binary>=2.9",
|
|
38
35
|
]
|
|
39
36
|
|
|
40
37
|
[tool.pytest.ini_options]
|