+16
-3
@@ -41,6 +41,10 @@ /** | ||
| /** | ||
| * Resolve the effective config. Throws a friendly error if no token is found | ||
| * anywhere — the caller (server or CLI) decides how to surface it. | ||
| * Resolve config WITHOUT throwing. `token` is `null` when none is configured. | ||
| * | ||
| * The server uses this so it can boot token-less: an MCP client (or an | ||
| * automated registry validator like LobeHub) can complete the `initialize` | ||
| * handshake and see an empty catalog instead of the process crashing. The | ||
| * user gets a clear "run `qlows-mcp login`" hint the moment they call a tool. | ||
| */ | ||
| export function resolveConfig(opts) { | ||
| export function resolveConfigOptional(opts) { | ||
| const stored = readStored(); | ||
@@ -52,2 +56,11 @@ const token = opts?.token ?? process.env.QLOWS_TOKEN ?? stored.token ?? ""; | ||
| DEFAULT_BASE_URL); | ||
| return { token: token || null, baseUrl }; | ||
| } | ||
| /** | ||
| * Resolve the effective config. Throws a friendly error if no token is found | ||
| * anywhere — used by CLI commands (e.g. `test`) that cannot do anything | ||
| * useful without a token. The server uses `resolveConfigOptional` instead. | ||
| */ | ||
| export function resolveConfig(opts) { | ||
| const { token, baseUrl } = resolveConfigOptional(opts); | ||
| if (!token) { | ||
@@ -54,0 +67,0 @@ throw new Error("No qlows token found. Run `qlows-mcp login`, or set the QLOWS_TOKEN " + |
+58
-4
@@ -14,10 +14,55 @@ /** | ||
| import { enrichToolsListResult } from "./annotations.js"; | ||
| import { resolveConfig } from "./config.js"; | ||
| import { resolveConfigOptional } from "./config.js"; | ||
| import { createRemoteClient, explainRemoteError } from "./remote.js"; | ||
| /** MCP protocol version this connector negotiates in token-less mode. */ | ||
| const MCP_PROTOCOL_VERSION = "2025-03-26"; | ||
| /** Bump alongside package.json on release (used only in the local handshake). */ | ||
| const CONNECTOR_VERSION = "1.0.0"; | ||
| function isRequest(msg) { | ||
| return msg.id !== undefined && msg.id !== null && typeof msg.method === "string"; | ||
| } | ||
| /** | ||
| * Local response for token-less mode. Lets an MCP client (or a registry | ||
| * validator like LobeHub) complete the `initialize` handshake and see empty | ||
| * catalogs instead of the process crashing. Anything that would actually | ||
| * touch qlows returns a clean, guiding JSON-RPC error rather than a 401. | ||
| */ | ||
| const NO_TOKEN_HINT = "No qlows token configured. Run `qlows-mcp login` (or set QLOWS_TOKEN), then restart your MCP client."; | ||
| function tokenlessResponse(msg) { | ||
| // Notifications (no id) never get a response. | ||
| if (!isRequest(msg)) | ||
| return null; | ||
| const id = msg.id ?? null; | ||
| const ok = (result) => ({ jsonrpc: "2.0", id, result }); | ||
| switch (msg.method) { | ||
| case "initialize": | ||
| return ok({ | ||
| protocolVersion: MCP_PROTOCOL_VERSION, | ||
| capabilities: { tools: {}, resources: {}, prompts: {} }, | ||
| serverInfo: { name: "qlows", version: CONNECTOR_VERSION }, | ||
| instructions: NO_TOKEN_HINT, | ||
| }); | ||
| case "ping": | ||
| return ok({}); | ||
| // Empty catalogs so client UIs render cleanly instead of erroring. | ||
| case "tools/list": | ||
| return ok({ tools: [] }); | ||
| case "prompts/list": | ||
| return ok({ prompts: [] }); | ||
| case "resources/list": | ||
| return ok({ resources: [] }); | ||
| case "resources/templates/list": | ||
| return ok({ resourceTemplates: [] }); | ||
| default: | ||
| // -32001 (Unauthorized) — the user just needs to add a token. | ||
| return { | ||
| jsonrpc: "2.0", | ||
| id, | ||
| error: { code: -32001, message: NO_TOKEN_HINT }, | ||
| }; | ||
| } | ||
| } | ||
| export async function runServer(opts) { | ||
| const { token, baseUrl } = resolveConfig(opts); | ||
| const remote = createRemoteClient(baseUrl, token); | ||
| const { token, baseUrl } = resolveConfigOptional(opts); | ||
| const remote = token ? createRemoteClient(baseUrl, token) : null; | ||
| const transport = new StdioServerTransport(); | ||
@@ -33,2 +78,9 @@ transport.onmessage = (raw) => { | ||
| async function handleMessage(msg) { | ||
| // Token-less mode: answer the handshake locally, guide everything else. | ||
| if (!remote) { | ||
| const response = tokenlessResponse(msg); | ||
| if (response !== null) | ||
| await transport.send(response); | ||
| return; | ||
| } | ||
| const request = isRequest(msg); | ||
@@ -61,3 +113,5 @@ try { | ||
| await transport.start(); | ||
| console.error(`[qlows-mcp] connected to ${baseUrl} — proxying MCP over stdio.`); | ||
| console.error(remote | ||
| ? `[qlows-mcp] connected to ${baseUrl} — proxying MCP over stdio.` | ||
| : `[qlows-mcp] no token set — running in limited mode. ${NO_TOKEN_HINT}`); | ||
| // Keep the process alive until stdin closes (client disconnects). | ||
@@ -64,0 +118,0 @@ await new Promise((resolve) => { |
+1
-1
| { | ||
| "name": "@qlows/mcp", | ||
| "version": "0.1.0", | ||
| "version": "1.0.0", | ||
| "description": "Local MCP connector for qlows — bring your live RFP/bid deals and the public tender corpus into Claude, Cursor, Windsurf, Cline and any MCP client.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+77
-12
@@ -0,5 +1,15 @@ | ||
| <a href="https://qlows.com?utm_source=github&utm_medium=readme&utm_campaign=mcp"> | ||
| <img src="https://qlows.com/brand/qlows-mark-color.svg" alt="qlows" height="72" /> | ||
| </a> | ||
| # qlows MCP connector | ||
| Bring your live **qlows** RFP/bid deals — and the public tender corpus — into | ||
| Claude, Cursor, Windsurf, Cline, Zed, or any [Model Context | ||
| [](https://www.npmjs.com/package/@qlows/mcp) | ||
| [](https://lobehub.com/mcp/getqlows-qlows-mcp) | ||
| [](https://registry.modelcontextprotocol.io) | ||
| [](./LICENSE) | ||
| Bring your live **[qlows](https://qlows.com)** RFP/bid deals — and the public | ||
| tender corpus across 35 WTO-GPA countries — into Claude, Cursor, Windsurf, | ||
| Cline, Zed, or any [Model Context | ||
| Protocol](https://modelcontextprotocol.io) client. | ||
@@ -14,4 +24,13 @@ | ||
| > **Quotes. Flows. Close.** qlows preps the bid; your AI drafts from real, | ||
| > grounded context. | ||
| > grounded context. Learn more at **https://qlows.com**. | ||
| ## Install | ||
| One-click install for the common clients (or copy the JSON snippet below): | ||
| [](cursor://anysphere.cursor-deeplink/mcp/install?name=qlows&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBxbG93cy9tY3AiXX0=) | ||
| [](vscode:mcp/install?%7B%22name%22%3A%22qlows%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40qlows%2Fmcp%22%5D%7D) | ||
| [](https://lobehub.com/mcp/getqlows-qlows-mcp) | ||
| [](https://qlows.com/docs/mcp) | ||
| --- | ||
@@ -97,7 +116,49 @@ | ||
| The connector discovers tools live from the server, so new qlows tools appear | ||
| without updating this package. | ||
| Plus three **prompts** (slash-commands) — `find_matching_tenders`, | ||
| `draft_rfp_section`, `weekly_pipeline_review` — see [Skills](#skills) below. | ||
| The connector discovers tools **and prompts** live from the server, so new | ||
| qlows capabilities appear without updating this package. | ||
| --- | ||
| ## Skills | ||
| Three things qlows is built to do inside your AI. Each maps to a | ||
| **prompt** (a slash-command your client exposes) plus the read-only tools it | ||
| drives — all grounded in real qlows data, never invented. | ||
| ### 🔎 Find matching tenders | ||
| Prompt: **`find_matching_tenders`** (args: `industry`, optional `country`, | ||
| optional `deadline_within_days`). Drives `search_tenders` and returns each live | ||
| notice with its canonical `qlows_url`. Full context: | ||
| <https://qlows.com/platform/qlows-mcp>. | ||
| > *"Find open zero-trust networking tenders in Germany closing in the next 30 | ||
| > days, with the qlows link for each."* | ||
| ### ✍️ Draft an RFP section, grounded | ||
| Prompt: **`draft_rfp_section`** (args: `deal_id`, `section`). Pulls the deal's | ||
| compliance grid and approved answers via `get_deal_snapshot` + | ||
| `search_compliance_items`, then drafts the section with every claim traced to | ||
| source — no fabricated capabilities, dates, or past performance. | ||
| > *"Draft the Technical Approach for deal `<id>`, grounded in the compliance | ||
| > items and our approved answers."* | ||
| ### 🗂️ Review your pipeline | ||
| Prompt: **`weekly_pipeline_review`** (no args). Uses `list_deals` + | ||
| `get_q_routing_state` to produce a prioritized to-do list: what's due, what's | ||
| blocked, and the next action per deal. | ||
| > *"Review my qlows pipeline and tell me which bids need attention first."* | ||
| The public tender skills work on a free account; the deal skills need a | ||
| personal token. See the [full platform overview](https://qlows.com/platform/qlows-mcp). | ||
| --- | ||
| ## Commands | ||
@@ -128,5 +189,5 @@ | ||
| The connector is a transparent JSON-RPC proxy. It does not implement tools — it | ||
| forwards `initialize`, `tools/list`, `tools/call`, and `resources/*` to the | ||
| qlows server and relays the responses. The token travels in the request to | ||
| qlows over HTTPS; treat it like a password. | ||
| forwards `initialize`, `tools/list`, `tools/call`, `prompts/*`, and | ||
| `resources/*` to the qlows server and relays the responses. The token travels | ||
| in the request to qlows over HTTPS; treat it like a password. | ||
@@ -168,6 +229,6 @@ --- | ||
| - **One-click browser auto-capture** for `login` (loopback callback). | ||
| - **Anthropic Connector Directory** listing — requires OAuth 2.1 + PKCE and | ||
| metadata discovery on the qlows server; tracked as a backend follow-up. This | ||
| connector + the MCP Registry listing are the interim distribution path. | ||
| - **Desktop Extension (`.mcpb`)** bundle for one-click Claude Desktop install. | ||
| - **Desktop Extension (`.mcpb`)** bundle for a true one-click Claude Desktop | ||
| install (the badge above links to setup docs in the meantime). | ||
| - **Anthropic Connector Directory** listing — the OAuth 2.1 + PKCE endpoint is | ||
| live on the qlows server; directory submission is tracked as a follow-up. | ||
@@ -187,4 +248,8 @@ --- | ||
| **##Badges** | ||
| [](https://lobehub.com/mcp/getqlows-qlows-mcp) | ||
| ## License | ||
| MIT |
+3
-3
| { | ||
| "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json", | ||
| "name": "io.github.getqlows/qlows-mcp", | ||
| "description": "Bring your live qlows RFP/bid deals and the public tender corpus into any MCP client. Read deal snapshots, compliance items, Q-routing state, intelligence summaries, competitors, and search public tenders.", | ||
| "version": "0.1.0", | ||
| "description": "Find Tenders in 35 Countries of the WTO-GPA Zone and ground your AI for your RFP responses", | ||
| "version": "1.0.0", | ||
| "websiteUrl": "https://qlows.com", | ||
@@ -16,3 +16,3 @@ "repository": { | ||
| "identifier": "@qlows/mcp", | ||
| "version": "0.1.0", | ||
| "version": "1.0.0", | ||
| "transport": { | ||
@@ -19,0 +19,0 @@ "type": "stdio" |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
32316
23.4%569
13.35%0
-100%251
34.95%