diff --git a/.env.example b/.env.example index d3ecaa93..48a1ce6f 100644 --- a/.env.example +++ b/.env.example @@ -75,6 +75,12 @@ FALKORDB_URL=redis://localhost:6379/0 # REQUIRED - change to your FalkorDB URL # GOOGLE_TAG_MANAGER_ID=GTM-XXXXXXX # ----------------------------- +# Optional MCP (Model Context Protocol) settings +# ----------------------------- +# Control QueryWeaver's built-in MCP endpoints (default: enabled) +# Set to "true" to disable mounting the MCP HTTP surface without editing code +# DISABLE_MCP=false + # Notes # ----------------------------- # - Keep secrets out of source control. Use your local `.env` (ignored by git) or a secrets manager in production. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b11dd69e..289c91e8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -318,6 +318,45 @@ Before submitting any changes, run these validation steps: - `tests/e2e/README.md`: Comprehensive E2E testing documentation - `setup_e2e_tests.sh`: Automated test environment setup script +### MCP (Model Context Protocol) + +QueryWeaver optionally exposes an MCP HTTP surface (mounted at `/mcp`) to allow external MCP clients to call QueryWeaver's Text2SQL operations. Key points for coding agents and reviewers: + +- Runtime toggle: the built-in MCP endpoints can be disabled with the env var `DISABLE_MCP=true`. Default behavior is enabled. +- Client config: consumers typically use an `mcp.json` (or client-specific config) that points to the MCP URL, for example: + +```json +{ + "servers": { + "queryweaver": { + "type": "http", + "url": "http://127.0.0.1:5000/mcp", + "headers": { + "Authorization": "Bearer your_token_here" + } + } + }, + "inputs": [] +} +``` + +- Tools and examples: projects like GitMCP show common client configurations for Cursor, VSCode, and other MCP-capable tools; use those patterns for guidance when writing docs or adding examples in this repo. +- Security: avoid embedding bearer tokens in repo files. Prefer runtime injection via env files or secret managers. If you need to demonstrate a token in tests, use mocked tokens and don't commit them. + +Example: generate `mcp.json` from an environment token (pseudo): + +```bash +export MQW_TOKEN="secret-token" +cat > mcp.json <= '3.9'", - "version": "==1.6.2" + "version": "==1.6.3" }, "certifi": { "hashes": [ @@ -400,6 +400,15 @@ "markers": "python_version >= '3.8'", "version": "==0.116.1" }, + "fastapi-mcp": { + "hashes": [ + "sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec", + "sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==0.4.0" + }, "filelock": { "hashes": [ "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", @@ -536,17 +545,17 @@ }, "hf-xet": { "hashes": [ - "sha256:09e86514c3c4284ed8a57d6b0f3d089f9836a0af0a1ceb3c9dd664f1f3eaefef", - "sha256:25b9d43333bbef39aeae1616789ec329c21401a7fe30969d538791076227b591", - "sha256:3d5f82e533fc51c7daad0f9b655d9c7811b5308e5890236828bd1dd3ed8fea74", - "sha256:4171f31d87b13da4af1ed86c98cf763292e4720c088b4957cf9d564f92904ca9", - "sha256:4a9b99ab721d385b83f4fc8ee4e0366b0b59dce03b5888a86029cc0ca634efbf", - "sha256:62a0043e441753bbc446dcb5a3fe40a4d03f5fb9f13589ef1df9ab19252beb53", - "sha256:8e2dba5896bca3ab61d0bef4f01a1647004de59640701b37e37eaa57087bbd9d", - "sha256:bfe5700bc729be3d33d4e9a9b5cc17a951bf8c7ada7ba0c9198a6ab2053b7453" + "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127", + "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea", + "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790", + "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a", + "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160", + "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c", + "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95", + "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803" ], "markers": "python_version >= '3.8'", - "version": "==1.1.8" + "version": "==1.1.9" }, "httpcore": { "hashes": [ @@ -564,6 +573,14 @@ "markers": "python_version >= '3.8'", "version": "==0.28.1" }, + "httpx-sse": { + "hashes": [ + "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", + "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37" + ], + "markers": "python_version >= '3.9'", + "version": "==0.4.1" + }, "huggingface-hub": { "hashes": [ "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", @@ -715,6 +732,14 @@ "markers": "python_version not in '2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7' and python_version >= '3.8'", "version": "==1.76.0" }, + "markdown-it-py": { + "hashes": [ + "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", + "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" + ], + "markers": "python_version >= '3.10'", + "version": "==4.0.0" + }, "markupsafe": { "hashes": [ "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", @@ -782,6 +807,22 @@ "markers": "python_version >= '3.9'", "version": "==3.0.2" }, + "mcp": { + "hashes": [ + "sha256:165306a8fd7991dc80334edd2de07798175a56461043b7ae907b279794a834c5", + "sha256:c314e7c8bd477a23ba3ef472ee5a32880316c42d03e06dcfa31a1cc7a73b65df" + ], + "markers": "python_version >= '3.10'", + "version": "==1.13.1" + }, + "mdurl": { + "hashes": [ + "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", + "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" + ], + "markers": "python_version >= '3.7'", + "version": "==0.1.2" + }, "multidict": { "hashes": [ "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", @@ -1214,6 +1255,22 @@ "markers": "python_version >= '3.9'", "version": "==2.33.2" }, + "pydantic-settings": { + "hashes": [ + "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", + "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796" + ], + "markers": "python_version >= '3.9'", + "version": "==2.10.1" + }, + "pygments": { + "hashes": [ + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.19.2" + }, "pyjwt": { "hashes": [ "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", @@ -1333,96 +1390,96 @@ }, "regex": { "hashes": [ - "sha256:0200a5150c4cf61e407038f4b4d5cdad13e86345dac29ff9dab3d75d905cf130", - "sha256:02e5860a250cd350c4933cf376c3bc9cb28948e2c96a8bc042aee7b985cfa26f", - "sha256:075641c94126b064c65ab86e7e71fc3d63e7ff1bea1fb794f0773c97cdad3a03", - "sha256:0a5966220b9a1a88691282b7e4350e9599cf65780ca60d914a798cb791aa1177", - "sha256:0b85241d3cfb9f8a13cefdfbd58a2843f208f2ed2c88181bf84e22e0c7fc066d", - "sha256:1a764a83128af9c1a54be81485b34dca488cbcacefe1e1d543ef11fbace191e1", - "sha256:1e4f4f62599b8142362f164ce776f19d79bdd21273e86920a7b604a4275b4f59", - "sha256:20ff8433fa45e131f7316594efe24d4679c5449c0ca69d91c2f9d21846fdf064", - "sha256:24257953d5c1d6d3c129ab03414c07fc1a47833c9165d49b954190b2b7f21a1a", - "sha256:2d15a9da5fad793e35fb7be74eec450d968e05d2e294f3e0e77ab03fa7234a83", - "sha256:3157aa512b9e606586900888cd469a444f9b898ecb7f8931996cb715f77477f0", - "sha256:32b9f9bcf0f605eb094b08e8da72e44badabb63dde6b83bd530580b488d1c6da", - "sha256:33be70d75fa05a904ee0dc43b650844e067d14c849df7e82ad673541cd465b5f", - "sha256:35e43ebf5b18cd751ea81455b19acfdec402e82fe0dc6143edfae4c5c4b3909a", - "sha256:37555e4ae0b93358fa7c2d240a4291d4a4227cc7c607d8f85596cdb08ec0a083", - "sha256:3b836eb4a95526b263c2a3359308600bd95ce7848ebd3c29af0c37c4f9627cd3", - "sha256:4494f8fd95a77eb434039ad8460e64d57baa0434f1395b7da44015bef650d0e4", - "sha256:469142fb94a869beb25b5f18ea87646d21def10fbacb0bcb749224f3509476f0", - "sha256:48fb045bbd4aab2418dc1ba2088a5e32de4bfe64e1457b948bb328a8dc2f1c2e", - "sha256:4913f52fbc7a744aaebf53acd8d3dc1b519e46ba481d4d7596de3c862e011ada", - "sha256:4b7dc33b9b48fb37ead12ffc7bdb846ac72f99a80373c4da48f64b373a7abeae", - "sha256:4b8c4d39f451e64809912c82392933d80fe2e4a87eeef8859fcc5380d0173c64", - "sha256:4f42b522259c66e918a0121a12429b2abcf696c6f967fa37bdc7b72e61469f98", - "sha256:4fef81b2f7ea6a2029161ed6dea9ae13834c28eb5a95b8771828194a026621e4", - "sha256:524c868ba527eab4e8744a9287809579f54ae8c62fbf07d62aacd89f6026b282", - "sha256:57d25b6732ea93eeb1d090e8399b6235ca84a651b52d52d272ed37d3d2efa0f1", - "sha256:5d7de1ceed5a5f84f342ba4a9f4ae589524adf9744b2ee61b5da884b5b659834", - "sha256:6164b1d99dee1dfad33f301f174d8139d4368a9fb50bf0a3603b2eaf579963ad", - "sha256:656433e5b7dccc9bc0da6312da8eb897b81f5e560321ec413500e5367fcd5d47", - "sha256:69c593ff5a24c0d5c1112b0df9b09eae42b33c014bdca7022d6523b210b69f72", - "sha256:69ed3bc611540f2ea70a4080f853741ec698be556b1df404599f8724690edbcd", - "sha256:6c053f9647e3421dd2f5dff8172eb7b4eec129df9d1d2f7133a4386319b47435", - "sha256:6cef962d7834437fe8d3da6f9bfc6f93f20f218266dcefec0560ed7765f5fe01", - "sha256:70645cad3407d103d1dbcb4841839d2946f7d36cf38acbd40120fee1682151e5", - "sha256:716a47515ba1d03f8e8a61c5013041c8c90f2e21f055203498105d7571b44531", - "sha256:72a26dcc6a59c057b292f39d41465d8233a10fd69121fa24f8f43ec6294e5415", - "sha256:7373afae7cfb716e3b8e15d0184510d518f9d21471f2d62918dbece85f2c588f", - "sha256:739a74970e736df0773788377969c9fea3876c2fc13d0563f98e5503e5185f46", - "sha256:7bf1c5503a9f2cbd2f52d7e260acb3131b07b6273c470abb78568174fe6bde3f", - "sha256:7f7211a746aced993bef487de69307a38c5ddd79257d7be83f7b202cb59ddb50", - "sha256:8283afe7042d8270cecf27cca558873168e771183d4d593e3c5fe5f12402212a", - "sha256:85c3a958ef8b3d5079c763477e1f09e89d13ad22198a37e9d7b26b4b17438b33", - "sha256:89c9504fc96268e8e74b0283e548f53a80c421182a2007e3365805b74ceef936", - "sha256:95b4639c77d414efa93c8de14ce3f7965a94d007e068a94f9d4997bb9bd9c81f", - "sha256:95d538b10eb4621350a54bf14600cc80b514211d91a019dc74b8e23d2159ace5", - "sha256:96bbae4c616726f4661fe7bcad5952e10d25d3c51ddc388189d8864fbc1b3c68", - "sha256:98d0ce170fcde1a03b5df19c5650db22ab58af375aaa6ff07978a85c9f250f0e", - "sha256:9960d162f3fecf6af252534a1ae337e9c2e20d74469fed782903b24e2cc9d3d7", - "sha256:9a9ab52a466a9b4b91564437b36417b76033e8778e5af8f36be835d8cb370d62", - "sha256:9d644de5520441e5f7e2db63aec2748948cc39ed4d7a87fd5db578ea4043d997", - "sha256:9ead9765217afd04a86822dfcd4ed2747dfe426e887da413b15ff0ac2457e21a", - "sha256:9feab78a1ffa4f2b1e27b1bcdaad36f48c2fed4870264ce32f52a393db093c78", - "sha256:a16dd56bbcb7d10e62861c3cd000290ddff28ea142ffb5eb3470f183628011ac", - "sha256:a664291c31cae9c4a30589bd8bc2ebb56ef880c9c6264cb7643633831e606a4d", - "sha256:aaef1f056d96a0a5d53ad47d019d5b4c66fe4be2da87016e0d43b7242599ffc7", - "sha256:baf2fe122a3db1c0b9f161aa44463d8f7e33eeeda47bb0309923deb743a18276", - "sha256:bca11d3c38a47c621769433c47f364b44e8043e0de8e482c5968b20ab90a3986", - "sha256:c1844be23cd40135b3a5a4dd298e1e0c0cb36757364dd6cdc6025770363e06c1", - "sha256:c3c9740a77aeef3f5e3aaab92403946a8d34437db930a0280e7e81ddcada61f5", - "sha256:c436fd1e95c04c19039668cfb548450a37c13f051e8659f40aed426e36b3765f", - "sha256:c7f663ccc4093877f55b51477522abd7299a14c5bb7626c5238599db6a0cb95d", - "sha256:c83aec91af9c6fbf7c743274fd952272403ad9a9db05fe9bfc9df8d12b45f176", - "sha256:cbe1698e5b80298dbce8df4d8d1182279fbdaf1044e864cbc9d53c20e4a2be77", - "sha256:cbfaa401d77334613cf434f723c7e8ba585df162be76474bccc53ae4e5520b3a", - "sha256:d03c6f9dcd562c56527c42b8530aad93193e0b3254a588be1f2ed378cdfdea1b", - "sha256:d428fc7731dcbb4e2ffe43aeb8f90775ad155e7db4347a639768bc6cd2df881a", - "sha256:d5273fddf7a3e602695c92716c420c377599ed3c853ea669c1fe26218867002f", - "sha256:d600e58ee6d036081c89696d2bdd55d507498a7180df2e19945c6642fac59588", - "sha256:d72765a4bff8c43711d5b0f5b452991a9947853dfa471972169b3cc0ba1d0751", - "sha256:d856164d25e2b3b07b779bfed813eb4b6b6ce73c2fd818d46f47c1eb5cd79bd6", - "sha256:da304313761b8500b8e175eb2040c4394a875837d5635f6256d6fa0377ad32c8", - "sha256:da7507d083ee33ccea1310447410c27ca11fb9ef18c95899ca57ff60a7e4d8f1", - "sha256:dde35e2afbbe2272f8abee3b9fe6772d9b5a07d82607b5788e8508974059925c", - "sha256:e154a7ee7fa18333ad90b20e16ef84daaeac61877c8ef942ec8dfa50dc38b7a1", - "sha256:e4636a7f3b65a5f340ed9ddf53585c42e3ff37101d383ed321bfe5660481744b", - "sha256:e91eb2c62c39705e17b4d42d4b86c4e86c884c0d15d9c5a47d0835f8387add8e", - "sha256:ea74cf81fe61a7e9d77989050d0089a927ab758c29dac4e8e1b6c06fccf3ebf0", - "sha256:ee38926f31f1aa61b0232a3a11b83461f7807661c062df9eb88769d86e6195c3", - "sha256:efac4db9e044d47fd3b6b0d40b6708f4dfa2d8131a5ac1d604064147c0f552fd", - "sha256:f14b36e6d4d07f1a5060f28ef3b3561c5d95eb0651741474ce4c0a4c56ba8719", - "sha256:f3e5c1e0925e77ec46ddc736b756a6da50d4df4ee3f69536ffb2373460e2dafd", - "sha256:f3f6e8e7af516a7549412ce57613e859c3be27d55341a894aacaa11703a4c31a", - "sha256:f7f3071b5faa605b0ea51ec4bb3ea7257277446b053f4fd3ad02b1dcb4e64353", - "sha256:f978ddfb6216028c8f1d6b0f7ef779949498b64117fc35a939022f67f810bdcb", - "sha256:fa1cdfb8db96ef20137de5587954c812821966c3e8b48ffc871e22d7ec0a4938", - "sha256:fb31080f2bd0681484b275461b202b5ad182f52c9ec606052020fe13eb13a72f", - "sha256:fd5edc3f453de727af267c7909d083e19f6426fc9dd149e332b6034f2a5611e6" + "sha256:008947a7fa92f4cb3b28201c9aa7becc0a44c31a7c2fcb934356e1877baccc09", + "sha256:090d20a6f308c1cd3c33824e892666089d9719ff88e139d4b63623e881d3945c", + "sha256:0b4d8a7f75da748a2d0c045600259f1899c9dd8dd9d3da1daa50bf534c3fa5ba", + "sha256:0c42fbffe25ac6291f8dd00176d1916165550aa649d14e9c4668d6a3d6a5c900", + "sha256:0c460628f6098cf8916b2d62fb39a37a39e49cca0279ac301ff9d94f7e75033e", + "sha256:0d4b71791975fc203e0e6c50db974abb23a8df30729c1ac4fd68c9f2bb8c9358", + "sha256:119a0e930916bb26fe028ef5098c6cad66d7a298560cacbc6942e834580dfba5", + "sha256:145fb4ca5a85e26c330b464fc71bbe0e92523ec5d295c6de9a1e31b06ebccf25", + "sha256:156f711019968ffb3512723a38b06d94d379675c296bdb6104d1abb6e57374c6", + "sha256:15869e4f36de7091342e1dae90216aafa3746e3a069f30b34503a36931036f95", + "sha256:16b5ca6570c71b1ee61dd30f24a1944eb82a372364e37f58f9b9731636cc6ba9", + "sha256:1915dfda52bd4d466f3a66b66988db1f647ee1d9c605858640ceeb779cffd908", + "sha256:1dcec2448ed0062f63e82ca02d1d05f74d4127cb6a9d76a73df60e81298d380b", + "sha256:2206d3a30469e8fc8848139884168127f456efbaca8ae14809c26b98d2be15c6", + "sha256:284fcd2dcb613e8b89b22a30cf42998c9a73ee360b8a24db8457d24f5c42282e", + "sha256:2dadb4ecaad42562771697685a381e3f723bd4d522e357c07ae4a541ebf5753c", + "sha256:305577fab545e64fb84d9a24269aa3132dbe05e1d7fa74b3614e93ec598fe6e6", + "sha256:33a26d4b2dc639868d73b9ec4ff8a89eb295797170125e4d4810ad23228f93c8", + "sha256:348cbcdf2d9dd0d09f05a78218776a33779e95aa57d553065a00429a96c553d3", + "sha256:394c492c398a9f9e17545e19f770c58b97e65963eedaa25bb879e80a03e2b327", + "sha256:3f747541fd1ad1dcf859ce221749a5d26d7dbe6d928efdd407c97a2d27c8f434", + "sha256:40eeff06bbcfa69201b60488f3f3aa38ad3c92c7c0ab2cfc7c9599abfdf24262", + "sha256:421b6ccd037ad551e1ef1bc31debc3a914b579c27c0807f35c85f13b0eccbff3", + "sha256:4561aeb36b0bf3bb44826e4b61a80c6ace0d8839bf4914d78f061f9ba61444b4", + "sha256:470138c8882d66493969f45fad2f8e20f35e381b9f96a37f59a5ac786e653cf6", + "sha256:4d6dbdfdb4de3a77d1b2f9ec6bded2e056081407923d69236e13457924cf5fd7", + "sha256:50628bc413193041838001b3926570629369d675b92badd6962c402aa09ed4c4", + "sha256:54018e66344d60b214f4aa151c046e0fa528221656f4f7eba5a787ccc7057312", + "sha256:5421a2d2026e8189500f12375cfd80a9a1914466d446edd28b37eb33c1953b39", + "sha256:590de47e6c390a42e6bfb1bdbe2148456827a6b28464c6e387f51b4bbe1f83e2", + "sha256:5ba4f8b0d5b88c33fe4060e6def58001fd8334b03c7ce2126964fa8851ab5d1b", + "sha256:5cd74545c32e0da0d489c2293101a82f4a1b88050c235e45509e4123017673b2", + "sha256:62141843d1ec079cd66604424af566e542e7e072b2d9e37165d414d2e6e271dd", + "sha256:630d5c7e0a490db2fee3c7b282c8db973abcbb036a6e4e6dc06c4270965852be", + "sha256:731ddb27a0900fa227dfba976b4efccec8c1c6fba147829bb52e71d49e91a5d7", + "sha256:7b4a3dc155984f09a55c64b90923cb136cd0dad21ca0168aba2382d90ea4c546", + "sha256:86e7ee69fdc9daf6aa98693b0db27a76e3d960c80d87c695af262c2608ccfc6a", + "sha256:8dad3ce46390fe3d81ae1c131e29179f010925fa164e15b918fb037effdb7ad9", + "sha256:8decb26f271b989d612c5d99db5f8f741dcd63ece51c59029840070f5f9778bf", + "sha256:8eaf3ea6631f804efcf0f5bd0e4ab62ba984fd9b70e3aef44b05cc6b951cc728", + "sha256:9082c0db8d43c696fac70b5b0592934f21533940f0118239b5c32fa23e51ed1a", + "sha256:90c37a24d9a809ff1898e74f3318a4e21f8bb3db9975a560fa3722e42c370285", + "sha256:93e077d1fbd24033fa427eab43d80ad47e449d25700cda78e8cac821a30090bf", + "sha256:96adc63fd63c05e2feb9c6b8a7212e2b9f52ccb1fa1f18eaed4f9e0ac2cbd186", + "sha256:97b98ea38fc3c1034f3d7bd30288d2c5b3be8cdcd69e2061d1c86cb14644a27b", + "sha256:9b3535b9a69a818735ebac392876dae4b215fe28c13b145353a2dac468ebae16", + "sha256:a367dbb66842a08744f49c64ba1aab23e4cbcc924bae8ef40870f2c51d6cb240", + "sha256:a848368797515bc141d3fad5fd2d81bf9e8a6a22d9ac1a4be4690dd22e997854", + "sha256:aef62e0b08b0e3c2616783a9f75a02f001254695a0a1d28b829dc9fb6a3603e4", + "sha256:b394b5157701b22cf63699c792bfeed65fbfeacbd94fea717a9e2036a51148ab", + "sha256:b839268539b44a965f3ed680fda6270337a05bd425cc80542e0c6808efdc9a7e", + "sha256:ba1deae2ceaa0b181ac9fd4cb8f04d6ba1494f3c8d053c8999f7c0dadb93497b", + "sha256:bc94bccb0482a1eceb34961e3c46e25a3746633fa19f93c93a42ff4b231ee6c3", + "sha256:c03308757831a8d89e7c007abb75d1d4c9fbca003b5fb32755d4475914535f08", + "sha256:c15d361fe9800bf38ef69c2e0c4b8b961ae4ce2f076fcf4f28e1fc9ea127f55a", + "sha256:cd7c1821eff911917c476d41030b422791ce282c23ee9e1b8f7681fd0993f1e4", + "sha256:ceeeaab602978c8eac3b25b8707f21a69c0bcd179d9af72519da93ef3966158f", + "sha256:d0ffe4a3257a235f9d39b99c6f1bc53c7a4b11f28565726b1aa00a5787950d60", + "sha256:d1f3498dcc96266b8db76512ffb2432bab2587df5e8ebfdceba5e737378e2bd1", + "sha256:d41726de2040c2a487bbac70fdd6e3ff2f1aa47dc91f0a29f6955a6dfa0f06b6", + "sha256:d41a71342819bdfe87c701f073a14ea4bd3f847333d696c7344e9ff3412b7f70", + "sha256:d7a9bc68610d22735b6ac01a3c3ef5b03d9303a18bd3e2249340213389f273dc", + "sha256:d82fb8a97e5ed8f1d3ed7f8e0e7fe1760faa95846c0d38b314284dfdbe86b229", + "sha256:d8cb77df92d1a204a0c218d93c5fb14945e2a7b40da2d9f15b05c9ddae393b43", + "sha256:d92379e53d782bdb773988687300e3bccb91ad38157b754b04b1857aaeea16a3", + "sha256:d93801012bb23901df403ae0adf528abfd50041c9e1136a303937d45c14466e0", + "sha256:db8b0e05af08ff38d78544950e844b5f159032b66dedda19b3f9b17297248be7", + "sha256:dc12259599d953bc25bc01f19b056b9115a96cd3cfe05f154d4570c9649800b0", + "sha256:dc8c7fc96c9eb18b6690c96ec9c8fb63ea2fa78c6df4258fd76b59d4fbf46645", + "sha256:dd23006c90d9ff0c2e4e5f3eaf8233dcefe45684f2acb330869ec5c2aa02b1fb", + "sha256:dd61f18dc4446bc3a2904559a61f32e98091cef7fb796e06fa35b9bfefe4c0c5", + "sha256:dd7df4ae4ea0efe0d378535e9825bd20e3be8d57eb3d55291d8094d61c9ccd9e", + "sha256:decd84f195c08b3d9d0297a7e310379aae13ca7e166473534508c81b95c74bba", + "sha256:df8deeb34e06c8ba196beabbcf2810d5ecd8cf71cfe69899e93806244610f7ae", + "sha256:e2ef0087ad6949918836f215480a9331f6c59ad54912a9a412f08ab1c9ccbc98", + "sha256:e3948db57ebe3c4bfb7e05765411ce6186820cafa27e5c737d72dbc5249010b3", + "sha256:e785e40f7edfc19ff0b81b27f25eefdb0251cfd2ac4a9fa1eea03f5129e93758", + "sha256:e78ab1b3e68b890d7ebd69218cfbfe4a09dc00b8a47be8648510b81b932d55ff", + "sha256:e8f709146e0f3dafdb4315884de1490ab59f1b93ecf7f9c6c8b0f655f437e593", + "sha256:ea197ac22396faf5e70c87836bb89f94ed5b500e1b407646a4e5f393239611f1", + "sha256:ebaf81f7344dbf1a2b383e35923648de8f78fb262cf04154c82853887ac3e684", + "sha256:eed02e5c39f91268ea4ddf68ee19eed189d57c605530b7d32960f54325c52e7a", + "sha256:f21b416be10a8348a7313ba8c610569a1ab4bf8ec70731750540842a4551cd3d", + "sha256:f89e5beb3012d3c36c526fd4af163ada24011a0b417378f726b17c2fb382a35d", + "sha256:fadf22d84901f1b6cc6b27439d98688a33cefb83e70c885791c2c27524907ed4", + "sha256:fbabdb18fdd1fc4b0740f4e6b3070d7f41f98a88b8c38cf1962b6dcb3e745e56", + "sha256:fd347592a4811ba1d246f99fb53db82a1898a5aebb511281ac0c2d81632e1789" ], "markers": "python_version >= '3.9'", - "version": "==2025.7.34" + "version": "==2025.8.29" }, "requests": { "hashes": [ @@ -1432,6 +1489,14 @@ "markers": "python_version >= '3.9'", "version": "==2.32.5" }, + "rich": { + "hashes": [ + "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", + "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==14.1.0" + }, "rpds-py": { "hashes": [ "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", @@ -1593,6 +1658,14 @@ "markers": "python_version >= '3.9'", "version": "==0.27.1" }, + "shellingham": { + "hashes": [ + "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", + "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de" + ], + "markers": "python_version >= '3.7'", + "version": "==1.5.4" + }, "six": { "hashes": [ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", @@ -1609,6 +1682,14 @@ "markers": "python_version >= '3.7'", "version": "==1.3.1" }, + "sse-starlette": { + "hashes": [ + "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", + "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a" + ], + "markers": "python_version >= '3.9'", + "version": "==3.0.2" + }, "starlette": { "hashes": [ "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", @@ -1656,24 +1737,62 @@ }, "tokenizers": { "hashes": [ - "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", - "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", - "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", - "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", - "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", - "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", - "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", - "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", - "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", - "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", - "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", - "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", - "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", - "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", - "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880" + "sha256:1626cb186e143720c62c6c6b5371e62bbc10af60481388c0da89bc903f37ea0c", + "sha256:2e33b98525be8453f355927f3cab312c36cd3e44f4d7e9e97da2fa94d0a49dcb", + "sha256:4136e1558a9ef2e2f1de1555dcd573e1cbc4a320c1a06c4107a3d46dc8ac6e4b", + "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79", + "sha256:76cf6757c73a10ef10bf06fa937c0ec7393d90432f543f49adc8cab3fb6f26cb", + "sha256:790bad50a1b59d4c21592f9c3cf5e5cf9c3c7ce7e1a23a739f13e01fb1be377a", + "sha256:8337ca75d0731fc4860e6204cc24bb36a67d9736142aa06ed320943b50b1e7ed", + "sha256:a89264e26f63c449d8cded9061adea7b5de53ba2346fc7e87311f7e4117c1cc8", + "sha256:c78174859eeaee96021f248a56c801e36bfb6bd5b067f2e95aa82445ca324f00", + "sha256:cdf5954de3962a5fd9781dc12048d24a1a6f1f5df038c6e95db328cd22964206", + "sha256:da589a61cbfea18ae267723d6b029b84598dc8ca78db9951d8f5beff72d8507c", + "sha256:dbf9d6851bddae3e046fedfb166f47743c1c7bd11c640f0691dd35ef0bcad3be", + "sha256:ea8562fa7498850d02a16178105b58803ea825b50dc9094d60549a7ed63654bb", + "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484", + "sha256:ec5b71f668a8076802b0241a42387d48289f25435b86b769ae1837cad4172a17" ], "markers": "python_version >= '3.9'", - "version": "==0.21.4" + "version": "==0.22.0" + }, + "tomli": { + "hashes": [ + "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", + "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", + "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", + "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", + "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", + "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", + "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", + "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", + "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", + "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", + "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", + "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", + "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", + "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", + "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", + "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", + "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", + "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", + "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", + "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", + "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", + "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", + "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", + "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", + "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", + "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", + "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", + "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", + "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", + "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", + "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", + "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "tqdm": { "hashes": [ @@ -1684,6 +1803,14 @@ "markers": "python_version >= '3.7'", "version": "==4.67.1" }, + "typer": { + "hashes": [ + "sha256:90ee01cb02d9b8395ae21ee3368421faf21fa138cb2a541ed369c08cec5237c9", + "sha256:d358c65a464a7a90f338e3bb7ff0c74ac081449e53884b12ba658cbd72990614" + ], + "markers": "python_version >= '3.7'", + "version": "==0.16.1" + }, "typing-extensions": { "hashes": [ "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", diff --git a/README.md b/README.md index 3db4d693..037cc3a5 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,51 @@ QueryWeaver supports Google and GitHub OAuth. Create OAuth credentials for each - Google: set authorized origin and callback `http://localhost:5000/login/google/authorized` - GitHub: set homepage and callback `http://localhost:5000/login/github/authorized` +## MCP server: host or connect (optional) + +QueryWeaver includes optional support for the Model Context Protocol (MCP). You can either have QueryWeaver expose an MCP-compatible HTTP surface (so other services can call QueryWeaver as an MCP server), or configure QueryWeaver to call an external MCP server for model/context services. + +What QueryWeaver provides +- The app registers MCP operations focused on Text2SQL flows: + - `list_databases` + - `connect_database` + - `database_schema` + - `query_database` + +- To disable the built-in MCP endpoints set `DISABLE_MCP=true` in your `.env` or environment (default: MCP enabled). +- Configuration + +- `DISABLE_MCP` — disable QueryWeaver's built-in MCP HTTP surface. Set to `true` to disable. Default: `false` (MCP enabled). + +Examples + +Disable the built-in MCP when running with Docker: + +```bash +docker run -p 5000:5000 -it --env DISABLE_MCP=true falkordb/queryweaver +``` +Calling the built-in MCP endpoints (example) +- The MCP surface is exposed as HTTP endpoints. + + +### Server Configuration +Below is a minimal example `mcp.json` client configuration that targets a local QueryWeaver instance exposing the MCP HTTP surface at `/mcp`. + +```json +{ + "servers": { + "queryweaver": { + "type": "http", + "url": "http://127.0.0.1:5000/mcp", + "headers": { + "Authorization": "Bearer your_token_here" + } + } + }, + "inputs": [] +} +``` + ## Testing > Quick note: many tests require FalkorDB to be available. Use the included helper to run a test DB in Docker if needed. diff --git a/api/agents/relevancy_agent.py b/api/agents/relevancy_agent.py index 699aedfb..91e4fa2d 100644 --- a/api/agents/relevancy_agent.py +++ b/api/agents/relevancy_agent.py @@ -58,7 +58,7 @@ class RelevancyAgent(BaseAgent): # pylint: disable=too-few-public-methods """Agent for determining relevancy of queries to database schema.""" - def __init__(self, queries_history: list, result_history: list): + def __init__(self, queries_history: list[str], result_history: list[str]): """Initialize the relevancy agent with query and result history.""" if result_history is None: self.messages = [] diff --git a/api/app_factory.py b/api/app_factory.py index f066fafd..030d8e93 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -7,6 +7,7 @@ from fastapi import FastAPI, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse from fastapi.staticfiles import StaticFiles +from fastapi_mcp import FastApiMCP from starlette.middleware.sessions import SessionMiddleware from starlette.middleware.base import BaseHTTPMiddleware @@ -51,10 +52,9 @@ def create_app(): "Text2SQL with " "Graph-Powered Schema Understanding" ), - ) - - app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*") + ) + app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*") # Add session middleware with explicit settings to ensure OAuth state persists app.add_middleware( @@ -81,9 +81,27 @@ def create_app(): app.include_router(auth_router) app.include_router(graphs_router, prefix="/graphs") app.include_router(database_router) + # app.include_router(mcp_router, prefix="/mcp") setup_oauth_handlers(app, app.state.oauth) + # Control MCP endpoints via environment variable DISABLE_MCP + # Default: MCP is enabled unless DISABLE_MCP is set to true + disable_mcp = os.getenv("DISABLE_MCP", "false").lower() in ("1", "true", "yes") + if disable_mcp: + logging.info("MCP endpoints disabled via DISABLE_MCP environment variable") + else: + mcp = FastApiMCP(app, + name="queryweaver", + description="QueryWeaver MCP API, provides Text2SQL capabilities", + include_operations=["list_databases", + "connect_database", + "database_schema", + "query_database"] + ) + + mcp.mount_http() + @app.exception_handler(Exception) async def handle_oauth_error(request: Request, exc: Exception): """Handle OAuth-related errors gracefully""" diff --git a/api/auth/user_management.py b/api/auth/user_management.py index b49106da..c1280ab3 100644 --- a/api/auth/user_management.py +++ b/api/auth/user_management.py @@ -234,11 +234,26 @@ async def validate_user(request: Request) -> Tuple[Optional[Dict[str, Any]], boo Includes refresh handling for Google. """ try: - # token might be in the URL if not in the cookie for API access + # token might be in the cookie or in the URL (api_token) for API access api_token = request.cookies.get("api_token") if not api_token: api_token = request.query_params.get("api_token") + # If still not found, also accept Authorization: Bearer + if not api_token: + auth_header = ( + request.headers.get("authorization") + or request.headers.get("Authorization") + ) + if auth_header: + try: + parts = auth_header.split(None, 1) + if len(parts) == 2 and parts[0].lower() == "bearer": + api_token = parts[1].strip() + except Exception: + # If parsing fails, ignore and continue (will return unauthenticated) + api_token = None + if api_token: db_info = await _get_user_info(api_token) diff --git a/api/routes/database.py b/api/routes/database.py index 9e8f61e2..d6a17547 100644 --- a/api/routes/database.py +++ b/api/routes/database.py @@ -21,7 +21,7 @@ class DatabaseConnectionRequest(BaseModel): url: str -@database_router.post("/database") +@database_router.post("/database", operation_id="connect_database") @token_required async def connect_database(request: Request, db_request: DatabaseConnectionRequest): """ diff --git a/api/routes/graphs.py b/api/routes/graphs.py index ebb108a9..3a673002 100644 --- a/api/routes/graphs.py +++ b/api/routes/graphs.py @@ -41,8 +41,8 @@ class ChatRequest(BaseModel): Args: BaseModel (_type_): _description_ """ - chat: list - result: list = None + chat: list[str] + result: list[str] = None instructions: str = None @@ -105,11 +105,11 @@ def _graph_name(request: Request, graph_id:str) -> str: return f"{request.state.user_id}_{graph_id}" -@graphs_router.get("") +@graphs_router.get("", operation_id="list_databases") @token_required async def list_graphs(request: Request): """ - This route is used to list all the graphs that are available in the database. + This route is used to list all the graphs (databases names) that are available in the database. """ user_id = request.state.user_id user_graphs = await db.list_graphs() @@ -118,15 +118,17 @@ async def list_graphs(request: Request): for graph in user_graphs if graph.startswith(f"{user_id}_")] return JSONResponse(content=filtered_graphs) - -@graphs_router.get("/{graph_id}/data") +@graphs_router.get("/{graph_id}/data", operation_id="database_schema") @token_required async def get_graph_data(request: Request, graph_id: str): - """Return all nodes and edges for the specified graph (namespaced to the user). + """Return all nodes and edges for the specified database schema (namespaced to the user). This endpoint returns a JSON object with two keys: `nodes` and `edges`. Nodes contain a minimal set of properties (id, name, labels, props). Edges contain source and target node names (or internal ids), type and props. + + args: + graph_id (str): The ID of the graph to query (the database name). """ namespaced = _graph_name(request, graph_id) try: @@ -274,11 +276,15 @@ async def load_graph(request: Request, data: GraphData = None, file: UploadFile raise HTTPException(status_code=400, detail="Failed to load graph data") -@graphs_router.post("/{graph_id}") +@graphs_router.post("/{graph_id}", operation_id="query_database") @token_required async def query_graph(request: Request, graph_id: str, chat_data: ChatRequest): """ - text2sql + Query the Database with the given graph_id and chat_data. + + Args: + graph_id (str): The ID of the graph to query. + chat_data (ChatRequest): The chat data containing user queries and context. """ graph_id = _graph_name(request, graph_id)