@goke/mcp 0.0.4
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 +217 -0
- package/dist/auth.d.ts +21 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +122 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +324 -0
- package/dist/local-callback-server.d.ts +14 -0
- package/dist/local-callback-server.d.ts.map +1 -0
- package/dist/local-callback-server.js +162 -0
- package/dist/oauth-provider.d.ts +63 -0
- package/dist/oauth-provider.d.ts.map +1 -0
- package/dist/oauth-provider.js +96 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +45 -0
- package/src/auth.ts +138 -0
- package/src/index.ts +465 -0
- package/src/local-callback-server.ts +185 -0
- package/src/oauth-provider.ts +134 -0
- package/src/types.ts +87 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { OAuthTokens, OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
/**
|
|
3
|
+
* Persisted OAuth state for file-based storage
|
|
4
|
+
*/
|
|
5
|
+
export interface McpOAuthState {
|
|
6
|
+
tokens?: OAuthTokens;
|
|
7
|
+
clientInformation?: OAuthClientInformation;
|
|
8
|
+
codeVerifier?: string;
|
|
9
|
+
serverUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* OAuth configuration for addMcpCommands
|
|
13
|
+
*/
|
|
14
|
+
export interface McpOAuthConfig {
|
|
15
|
+
/** Client name shown during OAuth consent screen */
|
|
16
|
+
clientName: string;
|
|
17
|
+
/**
|
|
18
|
+
* Load persisted OAuth state from storage (e.g., config file)
|
|
19
|
+
*/
|
|
20
|
+
load: () => McpOAuthState | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Save OAuth state to storage. Called after successful auth or token refresh.
|
|
23
|
+
* Pass undefined to clear the state (logout).
|
|
24
|
+
*/
|
|
25
|
+
save: (state: McpOAuthState | undefined) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Called with the authorization URL. Default behavior opens the browser.
|
|
28
|
+
* Override to customize (e.g., just print the URL).
|
|
29
|
+
*/
|
|
30
|
+
onAuthUrl?: (url: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Called on successful authentication
|
|
33
|
+
*/
|
|
34
|
+
onAuthSuccess?: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Called on authentication error
|
|
37
|
+
*/
|
|
38
|
+
onAuthError?: (error: string) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Result of startOAuthFlow
|
|
42
|
+
*/
|
|
43
|
+
export interface OAuthFlowResult {
|
|
44
|
+
success: boolean;
|
|
45
|
+
state?: McpOAuthState;
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Options for starting OAuth flow
|
|
50
|
+
*/
|
|
51
|
+
export interface StartOAuthFlowOptions {
|
|
52
|
+
serverUrl: string;
|
|
53
|
+
clientName: string;
|
|
54
|
+
/** Existing OAuth state (for re-auth scenarios) */
|
|
55
|
+
existingState?: McpOAuthState;
|
|
56
|
+
/** Called with auth URL, default opens browser */
|
|
57
|
+
onAuthUrl?: (url: string) => void;
|
|
58
|
+
/** Timeout in ms waiting for callback, default 5 minutes */
|
|
59
|
+
timeout?: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Options for the local callback server
|
|
63
|
+
*/
|
|
64
|
+
export interface CallbackServerOptions {
|
|
65
|
+
/** Called when server starts with the redirect URI */
|
|
66
|
+
onReady?: (redirectUri: string) => void;
|
|
67
|
+
/** Timeout in ms, default 5 minutes */
|
|
68
|
+
timeout?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Result from callback server
|
|
72
|
+
*/
|
|
73
|
+
export interface CallbackResult {
|
|
74
|
+
code: string;
|
|
75
|
+
state: string;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AAEpG;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,IAAI,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IAEtC;;;OAGG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,KAAK,IAAI,CAAC;IAEjD;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAE3B;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kDAAkD;IAClD,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goke/mcp",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Dynamically generate CLI commands from MCP server tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./src/index.js": {
|
|
14
|
+
"types": "./src/index.ts",
|
|
15
|
+
"import": "./src/index.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"cli",
|
|
25
|
+
"goke"
|
|
26
|
+
],
|
|
27
|
+
"author": "Tommaso De Rossi, morse <beats.by.morse@gmail.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
|
+
"js-yaml": "^4.1.1"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"goke": "*"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/js-yaml": "^4.0.9",
|
|
38
|
+
"@types/node": "^22.19.7",
|
|
39
|
+
"goke": "6.1.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"watch": "tsc -w"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
|
+
import { FileOAuthProvider } from "./oauth-provider.js";
|
|
3
|
+
import { startCallbackServer } from "./local-callback-server.js";
|
|
4
|
+
import type { McpOAuthState, OAuthFlowResult, StartOAuthFlowOptions } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Open a URL in the default browser.
|
|
8
|
+
* Uses platform-specific commands.
|
|
9
|
+
*/
|
|
10
|
+
async function openBrowser(url: string): Promise<void> {
|
|
11
|
+
const { exec } = await import("node:child_process");
|
|
12
|
+
const { promisify } = await import("node:util");
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
|
|
15
|
+
const platform = process.platform;
|
|
16
|
+
const command = (() => {
|
|
17
|
+
if (platform === "darwin") {
|
|
18
|
+
return `open "${url}"`;
|
|
19
|
+
}
|
|
20
|
+
if (platform === "win32") {
|
|
21
|
+
return `start "" "${url}"`;
|
|
22
|
+
}
|
|
23
|
+
// Linux and others
|
|
24
|
+
return `xdg-open "${url}"`;
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
await execAsync(command);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Start the OAuth flow for an MCP server.
|
|
32
|
+
* This is an internal function - consumers should not call this directly.
|
|
33
|
+
* It is automatically triggered by addMcpCommands when a 401 error occurs.
|
|
34
|
+
*
|
|
35
|
+
* This function:
|
|
36
|
+
* 1. Starts a local callback server on a random port
|
|
37
|
+
* 2. Initiates OAuth with the MCP server
|
|
38
|
+
* 3. Opens the browser for user authorization
|
|
39
|
+
* 4. Waits for the callback with the authorization code
|
|
40
|
+
* 5. Exchanges the code for tokens
|
|
41
|
+
* 6. Returns the OAuth state for persistence
|
|
42
|
+
*/
|
|
43
|
+
export async function startOAuthFlow(options: StartOAuthFlowOptions): Promise<OAuthFlowResult> {
|
|
44
|
+
const { serverUrl, clientName, existingState, onAuthUrl, timeout } = options;
|
|
45
|
+
|
|
46
|
+
// Start local callback server on random port
|
|
47
|
+
const callbackServer = await startCallbackServer({ timeout });
|
|
48
|
+
const { redirectUri, waitForCallback, close } = callbackServer;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Create OAuth provider with the dynamic redirect URI
|
|
52
|
+
const oauthProvider = new FileOAuthProvider({
|
|
53
|
+
serverUrl,
|
|
54
|
+
redirectUri,
|
|
55
|
+
clientName,
|
|
56
|
+
tokens: existingState?.tokens,
|
|
57
|
+
clientInformation: existingState?.clientInformation,
|
|
58
|
+
codeVerifier: existingState?.codeVerifier,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Start the OAuth flow - this will trigger dynamic client registration
|
|
62
|
+
// and set the authorization URL on the provider
|
|
63
|
+
const authResult = await auth(oauthProvider, { serverUrl });
|
|
64
|
+
|
|
65
|
+
if (authResult !== "REDIRECT") {
|
|
66
|
+
// Auth succeeded without redirect (had valid tokens)
|
|
67
|
+
close();
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
state: oauthProvider.getState(),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get the authorization URL
|
|
75
|
+
const authUrl = oauthProvider.redirectStartAuthUrl;
|
|
76
|
+
if (!authUrl) {
|
|
77
|
+
close();
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: "No authorization URL returned from OAuth flow",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Open browser or call custom handler
|
|
85
|
+
const authUrlString = authUrl.toString();
|
|
86
|
+
if (onAuthUrl) {
|
|
87
|
+
onAuthUrl(authUrlString);
|
|
88
|
+
} else {
|
|
89
|
+
await openBrowser(authUrlString);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Wait for the callback
|
|
93
|
+
const callback = await waitForCallback();
|
|
94
|
+
|
|
95
|
+
// Complete the OAuth flow by exchanging the code for tokens
|
|
96
|
+
const finalResult = await auth(oauthProvider, {
|
|
97
|
+
serverUrl,
|
|
98
|
+
authorizationCode: callback.code,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (finalResult === "REDIRECT") {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: "Unexpected redirect after code exchange",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
state: oauthProvider.getState(),
|
|
111
|
+
};
|
|
112
|
+
} catch (err) {
|
|
113
|
+
close();
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: err instanceof Error ? err.message : String(err),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if an error indicates authentication is required.
|
|
123
|
+
* Internal function used by addMcpCommands.
|
|
124
|
+
*/
|
|
125
|
+
export function isAuthRequiredError(err: unknown): boolean {
|
|
126
|
+
if (!(err instanceof Error)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const message = err.message.toLowerCase();
|
|
130
|
+
return (
|
|
131
|
+
message.includes("401") ||
|
|
132
|
+
message.includes("unauthorized") ||
|
|
133
|
+
message.includes("authentication required") ||
|
|
134
|
+
message.includes("not authenticated") ||
|
|
135
|
+
message.includes("invalid_token") ||
|
|
136
|
+
message.includes("missing or invalid access token")
|
|
137
|
+
);
|
|
138
|
+
}
|