@tuppence/mcp 0.0.0-stage → 0.1.1

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.
Files changed (3) hide show
  1. package/README.md +31 -2
  2. package/dist/cli.js +68 -0
  3. package/package.json +35 -3
package/README.md CHANGED
@@ -1,3 +1,32 @@
1
- # Temporary Holding Version
1
+ # @tuppence/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Tuppence's MCP tools for clients that only speak **stdio**. Tuppence's MCP server is remote
4
+ (`https://api.tuppence.ai/mcp`); this is a small local server that forwards to it with your key,
5
+ so the tools are exactly the remote ones, scoped to what that key may do.
6
+
7
+ Clients that support remote (HTTP) MCP servers don't need this: point them at
8
+ `https://api.tuppence.ai/mcp` with an `Authorization: Bearer <key>` header.
9
+
10
+ ## Claude Code
11
+
12
+ ```sh
13
+ claude mcp add tuppence --env TUPPENCE_API_KEY=sk_test_… -- npx -y @tuppence/mcp
14
+ ```
15
+
16
+ ## Claude Desktop / Cursor (`mcpServers` config)
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "tuppence": {
22
+ "command": "npx",
23
+ "args": ["-y", "@tuppence/mcp"],
24
+ "env": { "TUPPENCE_API_KEY": "sk_test_…" }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ Use a **restricted key** (`rk_…`) with only the scopes the agent needs: it will only see the
31
+ tools those scopes allow. Prefer `TUPPENCE_API_KEY` to `--api-key`: command lines are visible to
32
+ other processes. `TUPPENCE_API_URL` (or `--api-url`) points it somewhere other than production.
package/dist/cli.js ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `npx @tuppence/mcp` — Tuppence's MCP server for clients that only speak stdio (plan 11-07).
4
+ *
5
+ * Tuppence's MCP server is remote (`POST https://api.tuppence.ai/mcp`, streamable HTTP). This
6
+ * is a local stdio server that forwards `tools/list` and `tools/call` to it, authenticated with
7
+ * your API key — so the tools, their scopes and their behaviour are exactly the remote ones.
8
+ *
9
+ * TUPPENCE_API_KEY=sk_test_… npx @tuppence/mcp
10
+ *
11
+ * Prefer the environment variable to `--api-key`: command lines are visible to every process
12
+ * on the machine.
13
+ *
14
+ * Tuppence Connect: `TUPPENCE_ACCOUNT=acct_…` (or `--account`) makes every tool act as that one
15
+ * of the key's connected accounts — the `Tuppence-Account` header on the connection.
16
+ */
17
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
18
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
22
+ const VERSION = "0.1.1";
23
+ function option(name) {
24
+ const at = process.argv.indexOf(`--${name}`);
25
+ if (at >= 0)
26
+ return process.argv[at + 1];
27
+ const inline = process.argv.find((a) => a.startsWith(`--${name}=`));
28
+ return inline?.slice(name.length + 3);
29
+ }
30
+ /** Everything goes to stderr: stdout is the protocol. */
31
+ const fail = (message) => {
32
+ process.stderr.write(`tuppence-mcp: ${message}\n`);
33
+ process.exit(1);
34
+ };
35
+ const apiKey = process.env.TUPPENCE_API_KEY ?? option("api-key");
36
+ const apiUrl = (process.env.TUPPENCE_API_URL ??
37
+ option("api-url") ??
38
+ "https://api.tuppence.ai").replace(/\/+$/, "");
39
+ if (!apiKey)
40
+ fail("set TUPPENCE_API_KEY (or pass --api-key) to a Tuppence secret or restricted key.");
41
+ if (!/^(sk|rk)_(test|live)_/.test(apiKey)) {
42
+ fail("that is not a Tuppence secret or restricted key (sk_… / rk_…).");
43
+ }
44
+ const account = process.env.TUPPENCE_ACCOUNT ?? option("account");
45
+ if (account !== undefined && !/^acct_\w+$/.test(account)) {
46
+ fail('TUPPENCE_ACCOUNT (or --account) is a connected account id: "acct_…".');
47
+ }
48
+ const remote = new Client({ name: "tuppence-mcp-stdio", version: VERSION });
49
+ await remote
50
+ .connect(new StreamableHTTPClientTransport(new URL(`${apiUrl}/mcp`), {
51
+ requestInit: {
52
+ headers: {
53
+ Authorization: `Bearer ${apiKey}`,
54
+ ...(account ? { "Tuppence-Account": account } : {}),
55
+ },
56
+ },
57
+ }))
58
+ .catch((error) => fail(`could not reach ${apiUrl}/mcp: ${error.message}`));
59
+ const local = new Server({ name: "tuppence", version: VERSION }, {
60
+ capabilities: { tools: {} },
61
+ instructions: "Tuppence: take payments, create payment links, refund, read balances and meter usage. Tools act as the API key this server was started with.",
62
+ });
63
+ local.setRequestHandler(ListToolsRequestSchema, (request) => remote.listTools(request.params));
64
+ local.setRequestHandler(CallToolRequestSchema, (request) => remote.callTool(request.params));
65
+ await local.connect(new StdioServerTransport());
66
+ process.stdin.on("close", () => {
67
+ void remote.close().finally(() => process.exit(0));
68
+ });
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@tuppence/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Tuppence's MCP server for clients that only speak stdio: a local proxy to the remote server.",
5
+ "license": "MIT",
6
+ "homepage": "https://docs.tuppence.ai",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ZenningAI/tuppence.git",
10
+ "directory": "packages/mcp"
11
+ },
12
+ "bugs": "https://github.com/ZenningAI/tuppence/issues",
13
+ "type": "module",
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "bin": {
18
+ "tuppence-mcp": "./dist/cli.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "1.32.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.20.4",
29
+ "typescript": "^5.9.3"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit"
37
+ }
6
38
  }