feat(signer): MCP server with get_address, decode, sign, get_spend_limits

stdio transport only - no HTTP listener, no Provider, no outbound network.
Wires together decode.ts/policy.ts/sign.ts/audit.ts/keysource.ts (Tasks 2-6)
behind four tools: vector_signer_get_address, vector_signer_decode_transaction,
vector_signer_sign, vector_signer_get_spend_limits.

Two corrections needed beyond the brief's given code to reach its own stated
"pass 9, fail 0" outcome, both verified necessary by reproducing the failure
first:

- packages/signer/tsup.config.ts: target bumped es2020 -> es2022. index.ts's
  top-level `await server.connect(...)` is ES2022 syntax; esbuild hard-errors
  ("Top-level await is not available in the configured target environment")
  under the package's original es2020 target rather than attempting an unsafe
  downlevel transform. Scoped to the signer package only - the builder never
  uses top-level await and its own tsup.config.ts is untouched.
- tools.ts's decode_transaction response text: "(not signed)" and "would be
  signed" both contain the substring "signed", which collides with this same
  smoke test's own `!/signed/i.test(text)` check on every call, including the
  refused case. Reworded to "(read-only)" / "would be ALLOWED" - same meaning,
  no collision. Fixed the response text rather than loosening the test, which
  stays exactly as given.

Brief's own smoke test (9 tests) passes verbatim. Workspace-wide: typecheck 0,
test:unit 180/180 (no regressions), builder test:smoke still 26/23/0.

The audit-append-failure handling required by the task (a produced signature
must never be released if its audit record fails to write) is not yet in this
commit - see the follow-up commit.
This commit is contained in:
FilipBl4gojevic
2026-07-28 16:22:19 +02:00
parent 79004b90f3
commit b0faac8c12
7 changed files with 350 additions and 3 deletions

View File

@@ -1,3 +1,19 @@
// Local signing companion for the Vector MCP server.
// Speaks stdio only: no HTTP listener, no outbound network. See test/boundary.test.ts.
console.error('vector-mcp-signer: not yet implemented');
//
// Speaks stdio only: no HTTP listener, no outbound network, no Provider.
// The signing key never leaves this process. See test/boundary.test.ts.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { loadConfig } from './config.js';
import { registerSignerTools } from './tools.js';
const config = loadConfig();
const server = new McpServer({ name: 'vector-mcp-signer', version: '0.1.0' });
registerSignerTools(server, config);
// stdout is the MCP channel — every diagnostic must go to stderr.
console.error(`vector-mcp-signer ready. Key source: ${config.keySource.describe()}`);
console.error(`Audit log: ${config.auditLogPath}`);
await server.connect(new StdioServerTransport());