driggsby 0.1.3 → 0.1.5
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/README.md +30 -34
- package/dist/auth/login.js +2 -1
- package/dist/auth/oauth.js +24 -1
- package/dist/broker/daemon.js +4 -3
- package/dist/broker/launch.js +2 -1
- package/dist/broker/remote-mcp.js +10 -2
- package/dist/broker/remote-session.js +12 -5
- package/dist/lib/retry.js +22 -0
- package/dist/lib/user-guidance.js +19 -0
- package/dist/shim/server.js +61 -22
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
# driggsby
|
|
2
2
|
|
|
3
|
-
`driggsby`
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
v
|
|
11
|
-
driggsby mcp-server
|
|
12
|
-
|
|
|
13
|
-
v
|
|
14
|
-
shared local broker
|
|
15
|
-
|
|
|
16
|
-
v
|
|
17
|
-
remote Driggsby MCP
|
|
18
|
-
```
|
|
3
|
+
`driggsby` is the local CLI for connecting AI clients to Driggsby over MCP.
|
|
4
|
+
|
|
5
|
+
## What You Get
|
|
6
|
+
|
|
7
|
+
- browser-based sign-in to Driggsby
|
|
8
|
+
- a local MCP server for tools like Codex and Claude Code
|
|
9
|
+
- access to supported Driggsby tools from your AI client
|
|
19
10
|
|
|
20
11
|
## Install
|
|
21
12
|
|
|
@@ -25,37 +16,42 @@ npm install -g driggsby
|
|
|
25
16
|
|
|
26
17
|
Node `20+` is required.
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
If you prefer not to install globally, you can also use `npx -y driggsby ...`.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
1. Sign in:
|
|
29
24
|
|
|
30
|
-
```
|
|
25
|
+
```bash
|
|
31
26
|
driggsby login
|
|
32
|
-
driggsby status
|
|
33
|
-
driggsby mcp-server
|
|
34
|
-
driggsby logout
|
|
35
|
-
driggsby broker-daemon
|
|
36
27
|
```
|
|
37
28
|
|
|
38
|
-
|
|
29
|
+
2. Add Driggsby as an MCP server in your client:
|
|
39
30
|
|
|
40
31
|
```bash
|
|
41
|
-
driggsby login
|
|
42
32
|
codex mcp add driggsby -- driggsby mcp-server
|
|
43
33
|
```
|
|
44
34
|
|
|
45
|
-
|
|
35
|
+
3. Check broker status any time:
|
|
46
36
|
|
|
47
|
-
|
|
37
|
+
```bash
|
|
38
|
+
driggsby status
|
|
39
|
+
```
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
## Commands
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
driggsby login
|
|
45
|
+
driggsby status
|
|
46
|
+
driggsby mcp-server
|
|
47
|
+
driggsby logout
|
|
48
|
+
driggsby broker-daemon
|
|
49
|
+
```
|
|
53
50
|
|
|
54
|
-
##
|
|
51
|
+
## Learn More
|
|
55
52
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- keeps local broker session material in the platform secret store
|
|
53
|
+
- Product and source: https://github.com/thegoodsoftwareco/driggsby
|
|
54
|
+
- Issues: https://github.com/thegoodsoftwareco/driggsby/issues
|
|
59
55
|
|
|
60
56
|
## License
|
|
61
57
|
|
package/dist/auth/login.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fetchAuthorizationServerMetadata, fetchProtectedResourceMetadata, } fro
|
|
|
6
6
|
import { createDpopProof } from "./dpop.js";
|
|
7
7
|
import { startLoopbackAuthListener } from "./loopback.js";
|
|
8
8
|
import { buildAuthorizationUrl, createOAuthState, exchangeAuthorizationCode, registerBrokerClient, } from "./oauth.js";
|
|
9
|
+
import { buildReauthenticationRequiredMessage } from "../lib/user-guidance.js";
|
|
9
10
|
import { generatePkcePair } from "./pkce.js";
|
|
10
11
|
import { assertBrokerRemoteUrl } from "./url-security.js";
|
|
11
12
|
export async function loginBroker(runtimePaths, dependencies) {
|
|
@@ -114,7 +115,7 @@ function validateRemoteMetadata(authorizationServerMetadata, protectedResourceMe
|
|
|
114
115
|
async function tokenEndpointDpopProof(runtimePaths, secretStore, brokerId, tokenEndpoint) {
|
|
115
116
|
const dpopKeyPair = await readBrokerDpopKeyPair(runtimePaths, secretStore, brokerId);
|
|
116
117
|
if (dpopKeyPair === null) {
|
|
117
|
-
throw new Error("The local broker DPoP key is missing
|
|
118
|
+
throw new Error(buildReauthenticationRequiredMessage("The local broker DPoP key is missing"));
|
|
118
119
|
}
|
|
119
120
|
return await createDpopProof({
|
|
120
121
|
httpMethod: "POST",
|
package/dist/auth/oauth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { buildReauthenticationRequiredMessage } from "../lib/user-guidance.js";
|
|
3
4
|
const registrationResponseSchema = z.object({
|
|
4
5
|
client_id: z.string().min(1),
|
|
5
6
|
scope: z.string().min(1),
|
|
@@ -87,7 +88,11 @@ export async function refreshAccessToken(options, fetchImpl = fetch) {
|
|
|
87
88
|
method: "POST",
|
|
88
89
|
});
|
|
89
90
|
if (!response.ok) {
|
|
90
|
-
|
|
91
|
+
const errorBody = await readErrorBody(response);
|
|
92
|
+
if (isAuthenticationFailure(response.status, errorBody)) {
|
|
93
|
+
throw new Error(buildReauthenticationRequiredMessage("Authentication has expired or the saved broker session is no longer valid"));
|
|
94
|
+
}
|
|
95
|
+
throw new Error("Driggsby could not refresh the broker session with the remote service. Wait a moment and try again.");
|
|
91
96
|
}
|
|
92
97
|
const refreshedTokens = parseTokenExchangeResult(await response.json(), false);
|
|
93
98
|
return {
|
|
@@ -95,6 +100,24 @@ export async function refreshAccessToken(options, fetchImpl = fetch) {
|
|
|
95
100
|
refreshToken: refreshedTokens.refreshToken || options.refreshToken,
|
|
96
101
|
};
|
|
97
102
|
}
|
|
103
|
+
async function readErrorBody(response) {
|
|
104
|
+
try {
|
|
105
|
+
return (await response.text()).toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function isAuthenticationFailure(status, errorBody) {
|
|
112
|
+
return (status === 401 ||
|
|
113
|
+
status === 403 ||
|
|
114
|
+
(status === 400 &&
|
|
115
|
+
(errorBody.includes("invalid_grant") ||
|
|
116
|
+
errorBody.includes("invalid_token") ||
|
|
117
|
+
errorBody.includes("expired") ||
|
|
118
|
+
errorBody.includes("revoked") ||
|
|
119
|
+
errorBody.includes("unauthorized_client"))));
|
|
120
|
+
}
|
|
98
121
|
export function createOAuthState() {
|
|
99
122
|
return randomUUID();
|
|
100
123
|
}
|
package/dist/broker/daemon.js
CHANGED
|
@@ -3,6 +3,7 @@ import { BrokerRemoteSessionManager } from "./remote-session.js";
|
|
|
3
3
|
import { callRemoteTool, listRemoteTools } from "./remote-mcp.js";
|
|
4
4
|
import { KeyringSecretStore } from "./secret-store.js";
|
|
5
5
|
import { LocalBrokerServer } from "./server.js";
|
|
6
|
+
import { buildReauthenticationRequiredMessage } from "../lib/user-guidance.js";
|
|
6
7
|
export async function runBrokerDaemon(runtimePaths) {
|
|
7
8
|
const secretStore = new KeyringSecretStore();
|
|
8
9
|
const metadata = await ensureBrokerInstallation(runtimePaths, secretStore);
|
|
@@ -44,21 +45,21 @@ export async function runBrokerDaemon(runtimePaths) {
|
|
|
44
45
|
async function readRequiredLocalAuthToken(secretStore, brokerId) {
|
|
45
46
|
const localAuthToken = await readBrokerLocalAuthToken(secretStore, brokerId);
|
|
46
47
|
if (localAuthToken === null) {
|
|
47
|
-
throw new Error("The local broker auth state is incomplete
|
|
48
|
+
throw new Error(buildReauthenticationRequiredMessage("The local broker auth state is incomplete"));
|
|
48
49
|
}
|
|
49
50
|
return localAuthToken;
|
|
50
51
|
}
|
|
51
52
|
async function readRequiredPrivateJwk(secretStore, brokerId) {
|
|
52
53
|
const privateJwkRaw = await readBrokerPrivateJwk(secretStore, brokerId);
|
|
53
54
|
if (privateJwkRaw === null) {
|
|
54
|
-
throw new Error("The local broker signing key is missing
|
|
55
|
+
throw new Error(buildReauthenticationRequiredMessage("The local broker signing key is missing"));
|
|
55
56
|
}
|
|
56
57
|
return JSON.parse(privateJwkRaw);
|
|
57
58
|
}
|
|
58
59
|
async function readRequiredDpopKeyPair(runtimePaths, secretStore, brokerId) {
|
|
59
60
|
const dpopKeyPair = await readBrokerDpopKeyPair(runtimePaths, secretStore, brokerId);
|
|
60
61
|
if (dpopKeyPair === null) {
|
|
61
|
-
throw new Error("The local broker DPoP key is missing
|
|
62
|
+
throw new Error(buildReauthenticationRequiredMessage("The local broker DPoP key is missing"));
|
|
62
63
|
}
|
|
63
64
|
return dpopKeyPair;
|
|
64
65
|
}
|
package/dist/broker/launch.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
|
+
import { buildBrokerInvestigationMessage } from "../lib/user-guidance.js";
|
|
3
4
|
import { getBrokerStatus, pingBroker } from "./client.js";
|
|
4
5
|
export async function ensureBrokerRunning(options) {
|
|
5
6
|
if ((await pingBroker({
|
|
@@ -11,7 +12,7 @@ export async function ensureBrokerRunning(options) {
|
|
|
11
12
|
spawnBrokerDaemon(options.entrypointPath);
|
|
12
13
|
const started = await waitForBroker(options.runtimePaths, options.secretStore, 4_000);
|
|
13
14
|
if (!started) {
|
|
14
|
-
throw new Error("The local Driggsby broker did not start cleanly
|
|
15
|
+
throw new Error(buildBrokerInvestigationMessage("The local Driggsby broker did not start cleanly"));
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
export async function waitForBroker(runtimePaths, secretStore, timeoutMs) {
|
|
@@ -3,6 +3,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
3
3
|
import { CallToolResultSchema, ListToolsResultSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { createDpopProof } from "../auth/dpop.js";
|
|
5
5
|
import { assertBrokerRemoteUrl } from "../auth/url-security.js";
|
|
6
|
+
import { buildReauthenticationRequiredMessage } from "../lib/user-guidance.js";
|
|
6
7
|
const BROKER_CLIENT_INFO = {
|
|
7
8
|
name: "driggsby-local-broker",
|
|
8
9
|
version: "0.1.0",
|
|
@@ -94,13 +95,20 @@ function createDpopAuthenticatedFetch(session, dpopKeyPair) {
|
|
|
94
95
|
publicJwk: dpopKeyPair.publicJwk,
|
|
95
96
|
targetUrl: resolveRequestUrl(input),
|
|
96
97
|
}));
|
|
97
|
-
return await fetch(input, {
|
|
98
|
+
return await ensureAuthenticatedRemoteResponse(fetch(input, {
|
|
98
99
|
...init,
|
|
99
100
|
headers,
|
|
100
101
|
method: httpMethod,
|
|
101
|
-
});
|
|
102
|
+
}));
|
|
102
103
|
};
|
|
103
104
|
}
|
|
105
|
+
async function ensureAuthenticatedRemoteResponse(responsePromise) {
|
|
106
|
+
const response = await responsePromise;
|
|
107
|
+
if (response.status === 401 || response.status === 403) {
|
|
108
|
+
throw new Error(buildReauthenticationRequiredMessage("Authentication has expired or the saved broker session is no longer authorized for Driggsby"));
|
|
109
|
+
}
|
|
110
|
+
return response;
|
|
111
|
+
}
|
|
104
112
|
function resolveRequestMethod(input, init) {
|
|
105
113
|
if (init?.method !== undefined) {
|
|
106
114
|
return init.method;
|
|
@@ -2,6 +2,7 @@ import { fetchAuthorizationServerMetadata, } from "../auth/discovery.js";
|
|
|
2
2
|
import { createDpopProof } from "../auth/dpop.js";
|
|
3
3
|
import { refreshAccessToken } from "../auth/oauth.js";
|
|
4
4
|
import { assertBrokerRemoteUrl } from "../auth/url-security.js";
|
|
5
|
+
import { buildReauthenticationRequiredMessage, errorMessageIncludesReauthenticationCommand } from "../lib/user-guidance.js";
|
|
5
6
|
import { readBrokerDpopKeyPair } from "./installation.js";
|
|
6
7
|
import { readBrokerRemoteSession, summarizeBrokerRemoteSession, writeBrokerRemoteSession, } from "./session.js";
|
|
7
8
|
const ACCESS_TOKEN_REFRESH_SKEW_MS = 60_000;
|
|
@@ -20,7 +21,7 @@ export class BrokerRemoteSessionManager {
|
|
|
20
21
|
async ensureFreshSession() {
|
|
21
22
|
const session = await readBrokerRemoteSession(this.secretStore, this.brokerId);
|
|
22
23
|
if (session === null) {
|
|
23
|
-
throw new Error("The local Driggsby broker is not connected
|
|
24
|
+
throw new Error(buildReauthenticationRequiredMessage("The local Driggsby broker is not connected"));
|
|
24
25
|
}
|
|
25
26
|
if (!sessionNeedsRefresh(session)) {
|
|
26
27
|
return session;
|
|
@@ -63,7 +64,7 @@ async function refreshRemoteSession(runtimePaths, secretStore, brokerId, session
|
|
|
63
64
|
authorizationServerMetadata = await fetchAuthorizationServerMetadata(session.issuer, fetchImpl);
|
|
64
65
|
}
|
|
65
66
|
catch {
|
|
66
|
-
throw new Error("Driggsby could not refresh the local broker session right now
|
|
67
|
+
throw new Error("Driggsby could not refresh the local broker session right now because it could not reach the authorization server. Wait a moment and try again.");
|
|
67
68
|
}
|
|
68
69
|
let refreshedTokens;
|
|
69
70
|
try {
|
|
@@ -75,8 +76,8 @@ async function refreshRemoteSession(runtimePaths, secretStore, brokerId, session
|
|
|
75
76
|
resource: session.resource,
|
|
76
77
|
}, fetchImpl);
|
|
77
78
|
}
|
|
78
|
-
catch {
|
|
79
|
-
|
|
79
|
+
catch (error) {
|
|
80
|
+
throwRefreshSessionError(error);
|
|
80
81
|
}
|
|
81
82
|
const refreshedSession = {
|
|
82
83
|
...session,
|
|
@@ -92,7 +93,7 @@ async function refreshRemoteSession(runtimePaths, secretStore, brokerId, session
|
|
|
92
93
|
async function refreshTokenDpopProof(runtimePaths, secretStore, brokerId, tokenEndpoint) {
|
|
93
94
|
const dpopKeyPair = await readBrokerDpopKeyPair(runtimePaths, secretStore, brokerId);
|
|
94
95
|
if (dpopKeyPair === null) {
|
|
95
|
-
throw new Error("The local Driggsby broker key is missing
|
|
96
|
+
throw new Error(buildReauthenticationRequiredMessage("The local Driggsby broker key is missing"));
|
|
96
97
|
}
|
|
97
98
|
return await createDpopProof({
|
|
98
99
|
httpMethod: "POST",
|
|
@@ -101,3 +102,9 @@ async function refreshTokenDpopProof(runtimePaths, secretStore, brokerId, tokenE
|
|
|
101
102
|
targetUrl: tokenEndpoint,
|
|
102
103
|
});
|
|
103
104
|
}
|
|
105
|
+
function throwRefreshSessionError(error) {
|
|
106
|
+
if (errorMessageIncludesReauthenticationCommand(error)) {
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
throw new Error("Driggsby could not refresh the local broker session right now. Wait a moment and try again.");
|
|
110
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
+
export async function retryOperation(operation, options) {
|
|
3
|
+
let lastError;
|
|
4
|
+
const totalAttempts = options.delaysMs.length;
|
|
5
|
+
for (const [index, delayMs] of options.delaysMs.entries()) {
|
|
6
|
+
if (delayMs > 0) {
|
|
7
|
+
await sleep(delayMs);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
return await operation(index + 1, totalAttempts);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
lastError = error;
|
|
14
|
+
if (index === totalAttempts - 1 || !options.shouldRetry(error)) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw lastError instanceof Error
|
|
20
|
+
? lastError
|
|
21
|
+
: new Error("The retry operation did not complete successfully.");
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const DRIGGSBY_LOGIN_COMMAND = "npx driggsby@latest login";
|
|
2
|
+
export const DRIGGSBY_STATUS_COMMAND = "npx driggsby@latest status";
|
|
3
|
+
export function buildReauthenticationRequiredMessage(reason) {
|
|
4
|
+
return `${ensureTrailingPeriod(reason)} Please re-authenticate by running \`${DRIGGSBY_LOGIN_COMMAND}\`.`;
|
|
5
|
+
}
|
|
6
|
+
export function buildBrokerInvestigationMessage(reason) {
|
|
7
|
+
return `${ensureTrailingPeriod(reason)} Investigate broker readiness by running \`${DRIGGSBY_STATUS_COMMAND}\`. If authentication expired, re-authenticate with \`${DRIGGSBY_LOGIN_COMMAND}\`.`;
|
|
8
|
+
}
|
|
9
|
+
export function errorMessageIncludesReauthenticationCommand(error) {
|
|
10
|
+
return (error instanceof Error &&
|
|
11
|
+
error.message.includes(DRIGGSBY_LOGIN_COMMAND));
|
|
12
|
+
}
|
|
13
|
+
export function formatRetryWindow(delaysMs) {
|
|
14
|
+
const totalDelayMs = delaysMs.reduce((sum, delay) => sum + delay, 0);
|
|
15
|
+
return `${(totalDelayMs / 1_000).toFixed(1)} seconds`;
|
|
16
|
+
}
|
|
17
|
+
function ensureTrailingPeriod(value) {
|
|
18
|
+
return /[.!?]$/.test(value) ? value : `${value}.`;
|
|
19
|
+
}
|
package/dist/shim/server.js
CHANGED
|
@@ -6,6 +6,8 @@ import { ensureBrokerRunning } from "../broker/launch.js";
|
|
|
6
6
|
import { callBrokerTool, getBrokerStatus, listBrokerTools, } from "../broker/client.js";
|
|
7
7
|
import { buildBrokerStatus } from "../broker/installation.js";
|
|
8
8
|
import { KeyringSecretStore } from "../broker/secret-store.js";
|
|
9
|
+
import { retryOperation } from "../lib/retry.js";
|
|
10
|
+
import { buildBrokerInvestigationMessage, errorMessageIncludesReauthenticationCommand, formatRetryWindow, } from "../lib/user-guidance.js";
|
|
9
11
|
import { formatStatusText } from "../cli/format.js";
|
|
10
12
|
const LOCAL_STATUS_TOOL = {
|
|
11
13
|
description: "Report readiness and connectivity for the shared local Driggsby broker.",
|
|
@@ -16,6 +18,7 @@ const LOCAL_STATUS_TOOL = {
|
|
|
16
18
|
},
|
|
17
19
|
name: "get_local_broker_status",
|
|
18
20
|
};
|
|
21
|
+
const BROKER_OPERATION_RETRY_DELAYS_MS = [0, 250, 500, 1_000, 2_000];
|
|
19
22
|
export async function runMcpServerCommand(runtimePaths, entrypointPath) {
|
|
20
23
|
const secretStore = new KeyringSecretStore();
|
|
21
24
|
await ensureBrokerRunning({
|
|
@@ -36,7 +39,7 @@ export function createLocalShimServer(runtimePaths, secretStore) {
|
|
|
36
39
|
},
|
|
37
40
|
});
|
|
38
41
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
39
|
-
const remoteTools = await
|
|
42
|
+
const remoteTools = await loadRemoteToolsOrThrow(runtimePaths, secretStore);
|
|
40
43
|
return {
|
|
41
44
|
tools: [LOCAL_STATUS_TOOL, ...remoteTools],
|
|
42
45
|
};
|
|
@@ -45,23 +48,11 @@ export function createLocalShimServer(runtimePaths, secretStore) {
|
|
|
45
48
|
if (request.params.name === LOCAL_STATUS_TOOL.name) {
|
|
46
49
|
return await buildLocalStatusToolResult(runtimePaths, secretStore);
|
|
47
50
|
}
|
|
48
|
-
const remoteTools = await
|
|
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
|
-
}
|
|
51
|
+
const remoteTools = await loadRemoteToolsOrThrow(runtimePaths, secretStore);
|
|
55
52
|
if (!remoteTools.some((tool) => tool.name === request.params.name)) {
|
|
56
53
|
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
54
|
}
|
|
58
|
-
const toolResult = await
|
|
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
|
-
}
|
|
55
|
+
const toolResult = await callBrokerToolOrThrow(runtimePaths, secretStore, request.params.name, asToolArguments(request.params.arguments));
|
|
65
56
|
return toolResult;
|
|
66
57
|
});
|
|
67
58
|
return server;
|
|
@@ -89,15 +80,63 @@ function asToolArguments(argumentsValue) {
|
|
|
89
80
|
}
|
|
90
81
|
return argumentsValue;
|
|
91
82
|
}
|
|
92
|
-
async function
|
|
83
|
+
async function loadRemoteToolsOrThrow(runtimePaths, secretStore) {
|
|
84
|
+
try {
|
|
85
|
+
return await retryOperation(async () => {
|
|
86
|
+
const remoteTools = await listBrokerTools({
|
|
87
|
+
runtimePaths,
|
|
88
|
+
secretStore,
|
|
89
|
+
});
|
|
90
|
+
if (remoteTools === null) {
|
|
91
|
+
throw new Error("The local Driggsby broker is not responding yet.");
|
|
92
|
+
}
|
|
93
|
+
return remoteTools;
|
|
94
|
+
}, {
|
|
95
|
+
delaysMs: BROKER_OPERATION_RETRY_DELAYS_MS,
|
|
96
|
+
shouldRetry: shouldRetryBrokerOperation,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (errorMessageIncludesReauthenticationCommand(error)) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(buildBrokerInvestigationMessage(`The local Driggsby broker could not load the Driggsby tool list after ${BROKER_OPERATION_RETRY_DELAYS_MS.length} attempts over ${formatRetryWindow(BROKER_OPERATION_RETRY_DELAYS_MS)}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function callBrokerToolOrThrow(runtimePaths, secretStore, toolName, args) {
|
|
93
107
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
return await retryOperation(async () => {
|
|
109
|
+
const toolResult = await callBrokerTool({
|
|
110
|
+
runtimePaths,
|
|
111
|
+
secretStore,
|
|
112
|
+
}, toolName, args);
|
|
113
|
+
if (toolResult === null) {
|
|
114
|
+
throw new Error("The local Driggsby broker is not responding yet.");
|
|
115
|
+
}
|
|
116
|
+
return toolResult;
|
|
117
|
+
}, {
|
|
118
|
+
delaysMs: BROKER_OPERATION_RETRY_DELAYS_MS,
|
|
119
|
+
shouldRetry: shouldRetryBrokerOperation,
|
|
97
120
|
});
|
|
98
|
-
return remoteTools ?? [];
|
|
99
121
|
}
|
|
100
|
-
catch {
|
|
101
|
-
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (errorMessageIncludesReauthenticationCommand(error)) {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
if (error instanceof Error && error.message.includes("not available in this session anymore")) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(buildBrokerInvestigationMessage(`The local Driggsby broker could not run \`${toolName}\` after ${BROKER_OPERATION_RETRY_DELAYS_MS.length} attempts over ${formatRetryWindow(BROKER_OPERATION_RETRY_DELAYS_MS)}`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function shouldRetryBrokerOperation(error) {
|
|
133
|
+
if (errorMessageIncludesReauthenticationCommand(error)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (!(error instanceof Error)) {
|
|
137
|
+
return true;
|
|
102
138
|
}
|
|
139
|
+
const message = error.message.toLowerCase();
|
|
140
|
+
return (!message.includes("not available in this session anymore") &&
|
|
141
|
+
!message.includes("invalid tool arguments"));
|
|
103
142
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driggsby",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Local
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Local MCP broker and CLI for connecting AI clients to Driggsby",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|