driggsby 0.0.1 → 0.1.2

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.
@@ -0,0 +1,23 @@
1
+ export function formatStatusText(status) {
2
+ const lines = [
3
+ "Local Driggsby broker",
4
+ `installed: ${status.installed ? "yes" : "no"}`,
5
+ `running: ${status.brokerRunning ? "yes" : "no"}`,
6
+ `connected: ${status.remoteSession ? "yes" : "no"}`,
7
+ `remote mcp ready: ${status.remoteMcpReady ? "yes" : "no"}`,
8
+ `socket: ${status.socketPath}`,
9
+ ];
10
+ if (status.brokerId) {
11
+ lines.push(`broker id: ${status.brokerId}`);
12
+ }
13
+ if (status.dpopThumbprint) {
14
+ lines.push(`dpop thumbprint: ${status.dpopThumbprint}`);
15
+ }
16
+ if (status.remoteSession) {
17
+ lines.push(`server: ${status.remoteSession.issuer}`);
18
+ lines.push(`resource: ${status.remoteSession.resource}`);
19
+ lines.push(`scope: ${status.remoteSession.scope}`);
20
+ lines.push(`access token expires: ${status.remoteSession.accessTokenExpiresAt}`);
21
+ }
22
+ return `${lines.join("\n")}\n`;
23
+ }
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { runBrokerDaemonCommand } from "./cli/commands/broker-daemon.js";
4
+ import { runLoginCommand } from "./cli/commands/login.js";
5
+ import { runLogoutCommand } from "./cli/commands/logout.js";
6
+ import { runStatusCommand } from "./cli/commands/status.js";
7
+ import { runMcpServerCommand } from "./shim/server.js";
8
+ import { resolveRuntimePaths } from "./lib/runtime-paths.js";
9
+ const ENTRYPOINT_PATH = fileURLToPath(import.meta.url);
10
+ async function main() {
11
+ const [, , ...args] = process.argv;
12
+ const command = args[0];
13
+ const runtimePaths = resolveRuntimePaths();
14
+ switch (command) {
15
+ case "login":
16
+ await runLoginCommand(runtimePaths);
17
+ return;
18
+ case "logout":
19
+ await runLogoutCommand(runtimePaths);
20
+ return;
21
+ case "status":
22
+ await runStatusCommand(runtimePaths);
23
+ return;
24
+ case "mcp-server":
25
+ await runMcpServerCommand(runtimePaths, ENTRYPOINT_PATH);
26
+ return;
27
+ case "broker-daemon":
28
+ await runBrokerDaemonCommand(runtimePaths);
29
+ return;
30
+ default:
31
+ process.stderr.write("Usage: driggsby <mcp-server|login|logout|status|broker-daemon>\n");
32
+ process.exitCode = 1;
33
+ }
34
+ }
35
+ void main().catch((error) => {
36
+ const message = error instanceof Error ? error.message : "Driggsby command failed.";
37
+ process.stderr.write(`${message}\n`);
38
+ process.exitCode = 1;
39
+ });
@@ -0,0 +1,36 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ export async function readJsonFile(filePath) {
4
+ try {
5
+ const contents = await fs.readFile(filePath, "utf8");
6
+ return JSON.parse(contents);
7
+ }
8
+ catch (error) {
9
+ if (isNotFoundError(error)) {
10
+ return null;
11
+ }
12
+ throw error;
13
+ }
14
+ }
15
+ export async function writeJsonFile(filePath, value) {
16
+ const tempPath = `${filePath}.${process.pid}.tmp`;
17
+ const contents = `${JSON.stringify(value, null, 2)}\n`;
18
+ await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
19
+ await fs.writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
20
+ await fs.rename(tempPath, filePath);
21
+ }
22
+ export async function removeFileIfPresent(filePath) {
23
+ try {
24
+ await fs.rm(filePath);
25
+ }
26
+ catch (error) {
27
+ if (!isNotFoundError(error)) {
28
+ throw error;
29
+ }
30
+ }
31
+ }
32
+ function isNotFoundError(error) {
33
+ return (error instanceof Error &&
34
+ "code" in error &&
35
+ error.code === "ENOENT");
36
+ }
@@ -0,0 +1,62 @@
1
+ import { createHash } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ export function resolveRuntimePaths(options = {}) {
6
+ const env = options.env ?? process.env;
7
+ const platform = options.platform ?? process.platform;
8
+ const homeDir = options.homeDir ?? os.homedir();
9
+ const username = options.username ?? os.userInfo().username;
10
+ const allowEnvOverrides = options.allowEnvOverrides ?? false;
11
+ const configDir = allowEnvOverrides && env["DRIGGSBY_CONFIG_DIR"]
12
+ ? env["DRIGGSBY_CONFIG_DIR"]
13
+ : defaultConfigDir(platform, homeDir, env);
14
+ const stateDir = allowEnvOverrides && env["DRIGGSBY_STATE_DIR"]
15
+ ? env["DRIGGSBY_STATE_DIR"]
16
+ : defaultStateDir(platform, homeDir, env);
17
+ const pathModule = platform === "win32" ? path.win32 : path;
18
+ return {
19
+ configDir,
20
+ stateDir,
21
+ metadataPath: pathModule.join(configDir, "broker-metadata.json"),
22
+ socketPath: defaultSocketPath(platform, stateDir, username),
23
+ lockPath: pathModule.join(stateDir, "broker.lock"),
24
+ };
25
+ }
26
+ export async function ensureRuntimeDirectories(runtimePaths) {
27
+ await fs.mkdir(runtimePaths.configDir, { recursive: true, mode: 0o700 });
28
+ await fs.mkdir(runtimePaths.stateDir, { recursive: true, mode: 0o700 });
29
+ }
30
+ function defaultConfigDir(platform, homeDir, env) {
31
+ const pathModule = platform === "win32" ? path.win32 : path;
32
+ switch (platform) {
33
+ case "darwin":
34
+ return pathModule.join(homeDir, "Library", "Application Support", "driggsby");
35
+ case "win32":
36
+ return pathModule.join(env["APPDATA"] ?? path.join(homeDir, "AppData", "Roaming"), "Driggsby");
37
+ default:
38
+ return pathModule.join(env["XDG_CONFIG_HOME"] ?? path.join(homeDir, ".config"), "driggsby");
39
+ }
40
+ }
41
+ function defaultStateDir(platform, homeDir, env) {
42
+ const pathModule = platform === "win32" ? path.win32 : path;
43
+ switch (platform) {
44
+ case "darwin":
45
+ return pathModule.join(homeDir, "Library", "Application Support", "driggsby");
46
+ case "win32":
47
+ return pathModule.join(env["LOCALAPPDATA"] ?? path.join(homeDir, "AppData", "Local"), "Driggsby");
48
+ default:
49
+ return pathModule.join(env["XDG_STATE_HOME"] ?? path.join(homeDir, ".local", "state"), "driggsby");
50
+ }
51
+ }
52
+ function defaultSocketPath(platform, stateDir, username) {
53
+ if (platform === "win32") {
54
+ const win32Path = path.win32;
55
+ const userHash = createHash("sha256")
56
+ .update(username)
57
+ .digest("hex")
58
+ .slice(0, 16);
59
+ return win32Path.join("\\\\.\\pipe", `driggsby-broker-${userHash}`);
60
+ }
61
+ return path.join(stateDir, "broker.sock");
62
+ }
@@ -0,0 +1,103 @@
1
+ /* eslint-disable @typescript-eslint/no-deprecated */
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { ensureBrokerRunning } from "../broker/launch.js";
6
+ import { callBrokerTool, getBrokerStatus, listBrokerTools, } from "../broker/client.js";
7
+ import { buildBrokerStatus } from "../broker/installation.js";
8
+ import { KeyringSecretStore } from "../broker/secret-store.js";
9
+ import { formatStatusText } from "../cli/format.js";
10
+ const LOCAL_STATUS_TOOL = {
11
+ description: "Report readiness and connectivity for the shared local Driggsby broker.",
12
+ inputSchema: {
13
+ additionalProperties: false,
14
+ properties: {},
15
+ type: "object",
16
+ },
17
+ name: "get_local_broker_status",
18
+ };
19
+ export async function runMcpServerCommand(runtimePaths, entrypointPath) {
20
+ const secretStore = new KeyringSecretStore();
21
+ await ensureBrokerRunning({
22
+ entrypointPath,
23
+ runtimePaths,
24
+ secretStore,
25
+ });
26
+ const server = createLocalShimServer(runtimePaths, secretStore);
27
+ await server.connect(new StdioServerTransport());
28
+ }
29
+ export function createLocalShimServer(runtimePaths, secretStore) {
30
+ const server = new Server({
31
+ name: "driggsby-local-broker",
32
+ version: "0.1.0",
33
+ }, {
34
+ capabilities: {
35
+ tools: {},
36
+ },
37
+ });
38
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
39
+ const remoteTools = await loadRemoteTools(runtimePaths, secretStore);
40
+ return {
41
+ tools: [LOCAL_STATUS_TOOL, ...remoteTools],
42
+ };
43
+ });
44
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
45
+ if (request.params.name === LOCAL_STATUS_TOOL.name) {
46
+ return await buildLocalStatusToolResult(runtimePaths, secretStore);
47
+ }
48
+ const remoteTools = await listBrokerTools({
49
+ runtimePaths,
50
+ secretStore,
51
+ });
52
+ if (remoteTools === null) {
53
+ throw new Error("The local Driggsby broker could not load the remote Driggsby tools right now. Use `get_local_broker_status` for details, then try again.");
54
+ }
55
+ if (!remoteTools.some((tool) => tool.name === request.params.name)) {
56
+ throw new Error("That Driggsby tool is not available in this session anymore. Ask the client to refresh its tool list and try again.");
57
+ }
58
+ const toolResult = await callBrokerTool({
59
+ runtimePaths,
60
+ secretStore,
61
+ }, request.params.name, asToolArguments(request.params.arguments));
62
+ if (toolResult === null) {
63
+ throw new Error("The local Driggsby broker could not reach Driggsby right now. Use `get_local_broker_status` for details, then try again.");
64
+ }
65
+ return toolResult;
66
+ });
67
+ return server;
68
+ }
69
+ async function buildLocalStatusToolResult(runtimePaths, secretStore) {
70
+ const status = (await getBrokerStatus({ runtimePaths, secretStore })) ??
71
+ (await buildBrokerStatus(runtimePaths, secretStore, false));
72
+ return {
73
+ content: [
74
+ {
75
+ text: formatStatusText(status).trimEnd(),
76
+ type: "text",
77
+ },
78
+ ],
79
+ };
80
+ }
81
+ function asToolArguments(argumentsValue) {
82
+ if (argumentsValue === undefined) {
83
+ return undefined;
84
+ }
85
+ if (typeof argumentsValue !== "object" ||
86
+ argumentsValue === null ||
87
+ Array.isArray(argumentsValue)) {
88
+ throw new Error("Driggsby received invalid tool arguments.");
89
+ }
90
+ return argumentsValue;
91
+ }
92
+ async function loadRemoteTools(runtimePaths, secretStore) {
93
+ try {
94
+ const remoteTools = await listBrokerTools({
95
+ runtimePaths,
96
+ secretStore,
97
+ });
98
+ return remoteTools ?? [];
99
+ }
100
+ catch {
101
+ return [];
102
+ }
103
+ }
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "driggsby",
3
- "version": "0.0.1",
4
- "description": "FinAgent",
5
- "license": "MIT"
3
+ "version": "0.1.2",
4
+ "description": "Local Driggsby MCP broker and CLI",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/thegoodsoftwareco/driggsby.git",
10
+ "directory": "packages/driggsby"
11
+ },
12
+ "homepage": "https://github.com/thegoodsoftwareco/driggsby/tree/main/packages/driggsby#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/thegoodsoftwareco/driggsby/issues"
15
+ },
16
+ "keywords": [
17
+ "driggsby",
18
+ "mcp",
19
+ "broker",
20
+ "cli",
21
+ "finance"
22
+ ],
23
+ "bin": {
24
+ "driggsby": "./dist/index.js"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "scripts": {
35
+ "build": "node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true });\" && tsc -p tsconfig.build.json",
36
+ "lint": "eslint src --max-warnings 0",
37
+ "pack:release": "npm pack --json --ignore-scripts",
38
+ "secrets:check": "node ./scripts/check-publish-surface.mjs",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "test": "tsx --test src/**/*.test.ts",
41
+ "start": "tsx src/index.ts",
42
+ "verify:release": "npm run lint && npm run typecheck && npm test && npm run build"
43
+ },
44
+ "dependencies": {
45
+ "@modelcontextprotocol/sdk": "^1.29.0",
46
+ "@napi-rs/keyring": "^1.2.0",
47
+ "jose": "^6.2.2",
48
+ "zod": "^4.3.6"
49
+ },
50
+ "devDependencies": {
51
+ "@secretlint/secretlint-rule-preset-recommend": "^11.4.1",
52
+ "@eslint/js": "^9.39.4",
53
+ "@types/node": "^24.6.1",
54
+ "eslint": "^9.39.4",
55
+ "secretlint": "^11.4.1",
56
+ "tsx": "^4.21.0",
57
+ "typescript": "^6.0.2",
58
+ "typescript-eslint": "^8.58.0"
59
+ }
6
60
  }
package/pyproject.toml DELETED
@@ -1,11 +0,0 @@
1
- [project]
2
- name = "driggsby"
3
- version = "0.0.1"
4
- description = "FinAgent"
5
- requires-python = ">=3.11"
6
- license = "MIT"
7
- authors = [{ name = "Driggsby" }]
8
-
9
- [build-system]
10
- requires = ["hatchling"]
11
- build-backend = "hatchling.build"
@@ -1,3 +0,0 @@
1
- """Driggsby - Your personal financial agent."""
2
-
3
- __version__ = "0.0.1"