agent-services-mcp 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gareth1953
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # agent-services-mcp
2
+
3
+ A single **thin MCP (Model Context Protocol) server** that exposes two existing
4
+ services as discoverable tools, so AI agents and MCP-compatible clients can find
5
+ and use them through one connection:
6
+
7
+ - **[provenance-receipts](https://github.com/Gareth1953/provenance-receipts)** — certifies content **origin**;
8
+ returns an Ed25519-signed receipt.
9
+ - **[quality-gate](https://github.com/Gareth1953/quality-gate)** — scores content **quality** against a
10
+ published rubric; returns an Ed25519-signed score receipt.
11
+
12
+ > **It is a thin wrapper.** Every tool forwards an HTTP call to the underlying
13
+ > Worker and returns its response verbatim. It does **not** reimplement signing,
14
+ > scoring, or payment logic — those live in the two services. The honesty about
15
+ > what each service proves carries through to the tool descriptions.
16
+
17
+ ## What the wrapped services prove (and do not)
18
+
19
+ - **Provenance:** proves the content is unmodified (SHA-256 hash) and the receipt
20
+ was issued by the service's key. The `generator_metadata` is **caller-attested**
21
+ — it proves you *claimed* it, not that a specific model ran. Not AI-detection,
22
+ not a truth guarantee.
23
+ - **Quality:** a reproducible score **against the published rubric** (clarity,
24
+ completeness, internal consistency, obvious-error freedom). **Not** absolute
25
+ truth, **not** an external standard, **not** a fact-check. Read the rubric via
26
+ the `get_quality_rubric` tool.
27
+
28
+ ## Tools
29
+
30
+ | Tool | Forwards to | Paid? | Input |
31
+ | -------------------- | ---------------------------------------- | ---------- | ----- |
32
+ | `certify_provenance` | provenance-receipts `POST /v1/certify` | yes (x402) | `content` (string), `generator_metadata` (object, optional) |
33
+ | `verify_provenance` | provenance-receipts `POST /v1/verify` | no | `content` (string), `receipt` (object) |
34
+ | `score_quality` | quality-gate `POST /v1/score` | yes (x402) | `content` (string), `rubric_version` (string, optional), `target_score` (number 0–100, optional) |
35
+ | `verify_quality` | quality-gate `POST /v1/verify` | no | `content` (string), `receipt` (object) |
36
+ | `get_quality_rubric` | quality-gate `GET /v1/rubric` | no | none |
37
+
38
+ Full descriptions and Zod input schemas: [`src/tools.ts`](src/tools.ts). Each
39
+ tool returns the service's raw JSON (or markdown, for the rubric) as text. A
40
+ non-2xx response from a service (including a `402 Payment Required`) is surfaced
41
+ with `isError: true` and the response body preserved, so the caller can react.
42
+
43
+ ## Configuration
44
+
45
+ The two service URLs are environment-configurable (no secrets — just base URLs):
46
+
47
+ | Env var | Live (deployed) | Local dev fallback |
48
+ | ------------------ | -------------------------------------------------------- | ------------------------- |
49
+ | `PROVENANCE_URL` | `https://provenance-receipts.gpmiddleton71.workers.dev` | `http://localhost:8787` |
50
+ | `QUALITY_GATE_URL` | `https://quality-gate.gpmiddleton71.workers.dev` | `http://localhost:8788` |
51
+
52
+ `.env.example` and the client config below point at the **live** deployments. If
53
+ the vars are unset, the server falls back to localhost for local `wrangler dev`
54
+ (both Workers default to `:8787`, so run quality-gate on `:8788` to avoid a clash).
55
+
56
+ > Against the live services, the **paid** tools (`certify_provenance`,
57
+ > `score_quality`) require x402 — this wrapper forwards the request and holds no
58
+ > wallet, so without an `X-PAYMENT` they return a `402` (the payment requirements)
59
+ > surfaced as `isError`. The free tools work as normal.
60
+
61
+ ## Quickstart (local)
62
+
63
+ ```bash
64
+ # 1. Build the MCP server
65
+ npm install
66
+ npm run build # -> dist/index.js
67
+
68
+ # 2. In separate terminals, run the two services (free; payments off)
69
+ # (provenance-receipts) npm run dev # http://localhost:8787
70
+ # (quality-gate) npx wrangler dev --port 8788 # http://localhost:8788
71
+
72
+ # 3a. Smoke-test the free tool paths through an MCP stdio client
73
+ node scripts/test-client.mjs
74
+
75
+ # 3b. (optional, costs ~$0.012) prove the paid score_quality path end-to-end
76
+ node scripts/test-score.mjs
77
+
78
+ # 3c. Smoke-test the wrapper against the LIVE deployed services (free — the
79
+ # paid tools return a forwarded 402; no payment, no scoring call)
80
+ node scripts/test-live.mjs
81
+ ```
82
+
83
+ `scripts/test-client.mjs` exercises the free tools locally; `scripts/test-score.mjs`
84
+ makes one real Anthropic scoring call through `score_quality`;
85
+ `scripts/test-live.mjs` points the wrapper at the deployed workers.dev URLs and
86
+ asserts the free tools work and the paid tools forward the x402 `402`.
87
+
88
+ ## Connecting an MCP client (stdio)
89
+
90
+ This server speaks MCP over **stdio** (stdin/stdout). Any MCP client launches it
91
+ as a subprocess. Example for a Claude Desktop / Claude Code style
92
+ `mcpServers` config:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "agent-services": {
98
+ "command": "node",
99
+ "args": ["C:\\Users\\Gareth\\agent-services-mcp\\dist\\index.js"],
100
+ "env": {
101
+ "PROVENANCE_URL": "https://provenance-receipts.gpmiddleton71.workers.dev",
102
+ "QUALITY_GATE_URL": "https://quality-gate.gpmiddleton71.workers.dev"
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ - Run `npm run build` first so `dist/index.js` exists.
110
+ - The client connects, calls `tools/list` (it will see the 5 tools above), and
111
+ invokes them via `tools/call`.
112
+ - The underlying services must be reachable at the configured URLs when a tool is
113
+ called.
114
+ - Logs go to **stderr**; stdout is reserved for the MCP protocol.
115
+
116
+ Programmatically, connect with the SDK's `Client` + `StdioClientTransport`
117
+ (`command: "node"`, `args: ["dist/index.js"]`) — see `scripts/test-client.mjs`.
118
+
119
+ ## x402 payments (forwarded, not handled here)
120
+
121
+ The paid endpoints (`/v1/certify`, `/v1/score`) are gated by
122
+ [x402](https://github.com/coinbase/x402) on the underlying services. This wrapper
123
+ **forwards** requests and does not hold a wallet. If a service has payments
124
+ enabled and no valid `X-PAYMENT` is supplied, it returns `402` with the payment
125
+ requirements — the wrapper surfaces that as `isError` with the requirements body
126
+ intact. Settling a payment (signing an x402 authorization) is the client's
127
+ responsibility against the underlying service. See each service's `README.md` /
128
+ `docs/API.md` for the x402 details. **Base Sepolia testnet only — no mainnet.**
129
+
130
+ ## Verifying receipts independently
131
+
132
+ The receipts returned by `certify_provenance` and `score_quality` are
133
+ Ed25519-signed and verifiable **without trusting any of these services** — re-hash
134
+ the content and check the signature against the service's public key. Each service
135
+ ships a runnable independent verifier and recipe: see
136
+ [provenance-receipts/docs/VERIFYING.md](https://github.com/Gareth1953/provenance-receipts/blob/main/docs/VERIFYING.md)
137
+ and [quality-gate/docs/VERIFYING.md](https://github.com/Gareth1953/quality-gate/blob/main/docs/VERIFYING.md).
138
+
139
+ ## Build status
140
+
141
+ - [x] **Step 1 — skeleton + tool definitions** (`src/tools.ts`)
142
+ - [x] **Step 2 — tool handlers (HTTP forwarding) + local smoke test**
143
+ - [x] **Step 3 — README: what it is, the tools, and how an MCP client connects**
144
+ - [x] **Live — pointed at the deployed services** (`*.gpmiddleton71.workers.dev`)
145
+ and verified end-to-end via `scripts/test-live.mjs`: free tools work; paid
146
+ tools forward the x402 `402`.
147
+
148
+ All five tool paths verified locally (including one paid `score_quality` call
149
+ end-to-end through the wrapper) and against the live deployments.
150
+
151
+ ## Stack
152
+
153
+ - Official MCP SDK: [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk)
154
+ v1.29.0 (TypeScript), stdio transport, [`zod`](https://www.npmjs.com/package/zod)
155
+ input schemas.
156
+ - Node ESM + TypeScript (`tsc` → `dist/`).
157
+
158
+ ## Project layout
159
+
160
+ ```
161
+ agent-services-mcp/
162
+ ├── src/
163
+ │ ├── index.ts # MCP server: registers tools, forwards HTTP, stdio transport
164
+ │ └── tools.ts # the 5 tool definitions (names, descriptions, Zod schemas)
165
+ ├── scripts/
166
+ │ ├── test-client.mjs # MCP stdio client — free tool smoke test (local)
167
+ │ ├── test-score.mjs # MCP stdio client — one paid score_quality e2e check
168
+ │ └── test-live.mjs # MCP stdio client — against the live deployed services
169
+ ├── package.json
170
+ ├── tsconfig.json
171
+ ├── .gitignore
172
+ └── .env.example # PROVENANCE_URL, QUALITY_GATE_URL
173
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ // agent-services-mcp — MCP server entry point.
3
+ //
4
+ // A thin MCP server (official @modelcontextprotocol/sdk, stdio transport) that
5
+ // exposes provenance-receipts and quality-gate as tools. Each handler forwards
6
+ // an HTTP call to the relevant service; it does NOT reimplement service logic.
7
+ //
8
+ // IMPORTANT: stdout is the MCP protocol channel — all logging goes to stderr.
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { TOOLS } from "./tools.js";
12
+ // Configurable base URLs of the two underlying services (default to local
13
+ // `wrangler dev`; nothing is deployed to a public URL yet). No secrets here.
14
+ export const PROVENANCE_URL = process.env.PROVENANCE_URL ?? "http://localhost:8787";
15
+ export const QUALITY_GATE_URL = process.env.QUALITY_GATE_URL ?? "http://localhost:8788";
16
+ function baseUrlFor(service) {
17
+ return (service === "provenance" ? PROVENANCE_URL : QUALITY_GATE_URL).replace(/\/$/, "");
18
+ }
19
+ // Forward a tool call to its underlying HTTP endpoint and wrap the response as
20
+ // an MCP tool result. The raw response body is returned verbatim (JSON for the
21
+ // API endpoints, markdown for /v1/rubric); a non-2xx status (incl. 402) sets
22
+ // isError so the caller can react, with the body preserved.
23
+ async function forward(tool, args) {
24
+ const url = baseUrlFor(tool.service) + tool.path;
25
+ try {
26
+ const init = {
27
+ method: tool.method,
28
+ signal: AbortSignal.timeout(30_000),
29
+ };
30
+ if (tool.method === "POST") {
31
+ init.headers = { "content-type": "application/json" };
32
+ init.body = JSON.stringify(args ?? {});
33
+ }
34
+ const res = await fetch(url, init);
35
+ const body = await res.text();
36
+ return {
37
+ content: [{ type: "text", text: body }],
38
+ isError: !res.ok,
39
+ };
40
+ }
41
+ catch (err) {
42
+ const message = err instanceof Error ? err.message : String(err);
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: `Failed to reach ${tool.service} service at ${url}: ${message}`,
48
+ },
49
+ ],
50
+ isError: true,
51
+ };
52
+ }
53
+ }
54
+ async function main() {
55
+ const server = new McpServer({
56
+ name: "agent-services-mcp",
57
+ version: "0.1.0",
58
+ });
59
+ for (const tool of TOOLS) {
60
+ server.registerTool(tool.name, {
61
+ title: tool.title,
62
+ description: tool.description,
63
+ inputSchema: tool.inputSchema,
64
+ }, (args) => forward(tool, (args ?? {})));
65
+ }
66
+ const transport = new StdioServerTransport();
67
+ await server.connect(transport);
68
+ console.error(`agent-services-mcp ready — ${TOOLS.length} tools | provenance=${PROVENANCE_URL} quality=${QUALITY_GATE_URL}`);
69
+ }
70
+ main().catch((err) => {
71
+ console.error("agent-services-mcp failed to start:", err);
72
+ process.exit(1);
73
+ });
package/dist/tools.js ADDED
@@ -0,0 +1,139 @@
1
+ // Tool definitions for the agent-services-mcp server.
2
+ //
3
+ // This file is the single source of truth for the tools this MCP server exposes.
4
+ // Each entry maps a tool name -> an HTTP endpoint on one of the two underlying
5
+ // services (provenance-receipts or quality-gate). Handlers that actually forward
6
+ // the HTTP call are implemented in step 2; this file is definitions only.
7
+ //
8
+ // Descriptions are written to be HONEST and precise — they state what each
9
+ // service proves AND what it does not, consistent with the services' own docs.
10
+ import { z } from "zod";
11
+ // A receipt is an arbitrary signed object returned by a service. We keep the
12
+ // schema permissive (pass-through) — the wrapper does not re-validate receipt
13
+ // internals; the underlying /v1/verify endpoint is the authority.
14
+ const receiptShape = z
15
+ .record(z.unknown())
16
+ .describe("A signed receipt object exactly as returned by the service.");
17
+ export const TOOLS = [
18
+ {
19
+ name: "certify_provenance",
20
+ title: "Certify content provenance",
21
+ service: "provenance",
22
+ method: "POST",
23
+ path: "/v1/certify",
24
+ paid: true,
25
+ description: "Certify the ORIGIN of a piece of content via the provenance-receipts service. " +
26
+ "Returns an Ed25519-signed receipt committing to a SHA-256 hash of the content, " +
27
+ "your caller-attested generator_metadata, and a service-set timestamp.\n\n" +
28
+ "PROVES: the content is unmodified (hash) and the receipt was issued by the holder " +
29
+ "of the service's signing key.\n" +
30
+ "DOES NOT PROVE: it does not independently verify generator_metadata — the receipt " +
31
+ "proves you CLAIMED that metadata at certification time, not that a specific model " +
32
+ "actually produced the content. This is proof-of-origin/tamper-evidence, not " +
33
+ "AI-detection and not a truth guarantee.\n\n" +
34
+ "Note: /v1/certify is the paid action; when the underlying service has x402 payments " +
35
+ "enabled it may require a micropayment. This wrapper forwards the request as-is.",
36
+ inputSchema: {
37
+ content: z
38
+ .string()
39
+ .min(1)
40
+ .describe("The exact content to certify. Non-empty."),
41
+ generator_metadata: z
42
+ .record(z.unknown())
43
+ .optional()
44
+ .describe("Optional caller-attested metadata about what generated the content " +
45
+ "(e.g. { model, provider }). Recorded verbatim; the receipt proves you " +
46
+ "claimed it, not that a specific model ran."),
47
+ },
48
+ },
49
+ {
50
+ name: "verify_provenance",
51
+ title: "Verify a provenance receipt",
52
+ service: "provenance",
53
+ method: "POST",
54
+ path: "/v1/verify",
55
+ paid: false,
56
+ description: "Verify a provenance receipt against its content via provenance-receipts. " +
57
+ "Re-hashes the content and checks the Ed25519 signature; returns { valid, details }. " +
58
+ "`valid` is true only if BOTH the content hash matches the receipt AND the signature " +
59
+ "is valid under the service's public key. Free. A 200 response means verification ran " +
60
+ "— always read `valid` (false means the content was modified or the receipt was " +
61
+ "altered/forged).",
62
+ inputSchema: {
63
+ content: z
64
+ .string()
65
+ .describe("The content to check against the receipt."),
66
+ receipt: receiptShape.describe("A provenance receipt as returned by certify_provenance."),
67
+ },
68
+ },
69
+ {
70
+ name: "score_quality",
71
+ title: "Score content against the published quality rubric",
72
+ service: "quality",
73
+ method: "POST",
74
+ path: "/v1/score",
75
+ paid: true,
76
+ description: "Score content against the quality-gate service's PUBLISHED rubric and get an " +
77
+ "Ed25519-signed score receipt. The rubric scores four dimensions — clarity, " +
78
+ "completeness, internal consistency, and obvious-error freedom (0–25 each, summing " +
79
+ "to 0–100) — plus flags from a closed vocabulary. Read the exact rubric with " +
80
+ "get_quality_rubric.\n\n" +
81
+ "WHAT THIS IS: a reproducible score AGAINST OUR PUBLISHED RUBRIC (vX).\n" +
82
+ "WHAT IT IS NOT: a measure of absolute truth, an external/third-party standard, or a " +
83
+ "fact-check — 'obvious-error freedom' catches errors evident from the text or common " +
84
+ "knowledge, not external verification.\n\n" +
85
+ "Optional target_score (0–100) enables 'no pass, no pay': if the score is below your " +
86
+ "target, no receipt is issued and (when payments are on) no charge is made — the " +
87
+ "response returns the failing breakdown with receipt: null.\n\n" +
88
+ "Note: /v1/score is the paid action (one Claude API scoring pass per call) and may " +
89
+ "require an x402 micropayment when the underlying service has payments enabled.",
90
+ inputSchema: {
91
+ content: z
92
+ .string()
93
+ .min(1)
94
+ .describe("The content to score. Non-empty."),
95
+ rubric_version: z
96
+ .string()
97
+ .optional()
98
+ .describe("Optional rubric version to score against; must match the service's current " +
99
+ "version (e.g. \"v1\") if provided."),
100
+ target_score: z
101
+ .number()
102
+ .min(0)
103
+ .max(100)
104
+ .optional()
105
+ .describe("Optional 0–100 threshold. Enables 'no pass, no pay': below this, no receipt " +
106
+ "is issued and no charge is made."),
107
+ },
108
+ },
109
+ {
110
+ name: "verify_quality",
111
+ title: "Verify a quality score receipt",
112
+ service: "quality",
113
+ method: "POST",
114
+ path: "/v1/verify",
115
+ paid: false,
116
+ description: "Verify a quality score receipt against its content via quality-gate. Re-hashes the " +
117
+ "content and checks the Ed25519 signature, which covers the score itself — so a " +
118
+ "forged or altered score fails. Returns { valid, details }. Free. A 200 response " +
119
+ "means verification ran — always read `valid`.",
120
+ inputSchema: {
121
+ content: z
122
+ .string()
123
+ .describe("The content the score receipt was issued for."),
124
+ receipt: receiptShape.describe("A quality score receipt as returned by score_quality."),
125
+ },
126
+ },
127
+ {
128
+ name: "get_quality_rubric",
129
+ title: "Get the published quality rubric",
130
+ service: "quality",
131
+ method: "GET",
132
+ path: "/v1/rubric",
133
+ paid: false,
134
+ description: "Fetch the published Quality Gate rubric (markdown) that score_quality grades " +
135
+ "against, along with its version. Read this to understand exactly what a quality " +
136
+ "score means and does not mean. Free; takes no input.",
137
+ inputSchema: {},
138
+ },
139
+ ];
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "agent-services-mcp",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "description": "Thin MCP server exposing provenance-receipts and quality-gate as discoverable tools. Wraps the existing Workers over HTTP; does not reimplement their logic.",
7
+ "mcpName": "io.github.Gareth1953/agent-services-mcp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Gareth1953/agent-services-mcp.git"
11
+ },
12
+ "bin": { "agent-services-mcp": "dist/index.js" },
13
+ "files": ["dist", "README.md"],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsx src/index.ts",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "dependencies": {
22
+ "@modelcontextprotocol/sdk": "^1.29.0",
23
+ "zod": "^3.25.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.0.0",
27
+ "tsx": "^4.19.0",
28
+ "typescript": "^5.5.0"
29
+ }
30
+ }