@geeks.ltd/geeks-amp-mcp 1.0.0

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 ADDED
@@ -0,0 +1,69 @@
1
+ # Geeks AMP MCP Server
2
+
3
+ Local MCP server for Cursor that authenticates to your AMP backend via OAuth 2.0 (Authorization Code + PKCE) and proxies tool calls to AMP’s `MCPServer.ashx`.
4
+
5
+ ## Flow
6
+
7
+ 1. **First use** – When you call an AMP tool, the server opens your browser to AMP’s login. After you sign in, AMP redirects to `http://127.0.0.1:5005/callback`. The server exchanges the code for access and refresh tokens and stores them (e.g. in `~/.amp-mcp/credentials.json`).
8
+ 2. **Later uses** – The server reuses the stored access token. When it expires, it uses the refresh token; if refresh fails, it runs the browser flow again.
9
+ 3. **Tools** – `get_projects`, `get_amp_item_with_test_cases`, `get_auth_status`, `audit_tdd_amp` are forwarded to AMP with `Authorization: Bearer <token>`.
10
+
11
+ ## Prerequisites
12
+
13
+ - Node.js 18+
14
+ - AMP running with OAuth client `amp-cursor-mcp` and redirect URI `http://127.0.0.1:5005/callback` (see `Website/web.config`).
15
+
16
+ ## Setup
17
+
18
+ ```bash
19
+ cd geeks-amp-mcp
20
+ npm install
21
+ npm run build
22
+ ```
23
+
24
+ ## Cursor config
25
+
26
+ In `.cursor/mcp.json` (project or global), use the **local server** via npx (no env token). From the repo root (`geeksamp`), run the package with:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "amp": {
32
+ "command": "npx",
33
+ "args": ["./geeks-amp-mcp"],
34
+ "env": {
35
+ "AMP_BASE_URL": "http://localhost:39290",
36
+ "AMP_OAUTH_CLIENT_ID": "amp-cursor-mcp",
37
+ "AMP_CALLBACK_PORT": "5005"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ - **`npx ./geeks-amp-mcp`** – Runs the local package’s bin (uses `geeks-amp-mcp/dist/index.js`). Cursor’s working directory is the workspace root, so the path is relative to that. Restart Cursor after changing MCP config.
45
+ - To use an absolute path instead: `"args": ["C:\\path\\to\\geeksamp\\geeks-amp-mcp"]` with `"command": "npx"`, or `"command": "node"` and `"args": ["C:\\path\\to\\geeksamp\\geeks-amp-mcp\\dist\\index.js"]`.
46
+ - **Plain `npx geeks-amp-mcp`** (no path) only works if the package is installed from npm. If you publish `geeks-amp-mcp` to the registry, users can run `npx geeks-amp-mcp` from any directory and set `AMP_BASE_URL` etc. in env.
47
+
48
+ ## Publishing to npm
49
+
50
+ 1. Create an account at [npmjs.com](https://www.npmjs.com/signup).
51
+ 2. In `geeks-amp-mcp`, run `npm run build`, then `npm login` and `npm publish`.
52
+ 3. If the name `geeks-amp-mcp` is taken, use a scoped name in `package.json` (e.g. `"name": "@your-org/geeks-amp-mcp"`); then publish with `npm publish --access public` so others can install it without a paid plan.
53
+ 4. Update `package.json` `repository.url` (replace `YOUR_ORG` with your GitHub org/user) before publishing.
54
+
55
+ ## Env (optional)
56
+
57
+ | Variable | Default | Description |
58
+ |----------|---------|-------------|
59
+ | `AMP_BASE_URL` | `http://localhost:39290` | AMP base URL |
60
+ | `AMP_OAUTH_CLIENT_ID` | `amp-cursor-mcp` | OAuth client id (must match AMP) |
61
+ | `AMP_CALLBACK_PORT` | `5005` | Callback listener port (must be in AMP redirect URIs) |
62
+ | `AMP_MCP_CREDENTIALS_DIR` | `~/.amp-mcp` | Directory for stored tokens |
63
+
64
+ ## Tools
65
+
66
+ - **get_auth_status** – Returns `Authenticated: YES` or `Authenticated: NO` (no browser).
67
+ - **get_projects** – List AMP projects (optional `searchTerm`, `limit`).
68
+ - **get_amp_item_with_test_cases** – AMP item + test case refs by `ampReference` (e.g. `12345` or `AMP-12345`).
69
+ - **audit_tdd_amp** – Same as above plus auth status message for `/audit-tdd-amp` flows.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Launcher so the MCP server is always run with Node (avoids Windows running .js with WScript).
4
+ * npm/npx runs this as "node bin/geeks-amp-mcp.cjs", which then runs "node dist/index.js".
5
+ */
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+
9
+ const dir = __dirname;
10
+ const script = path.join(dir, "..", "dist", "index.js");
11
+
12
+ const result = spawnSync(process.execPath, [script], {
13
+ stdio: "inherit",
14
+ windowsHide: true,
15
+ cwd: path.join(dir, ".."),
16
+ });
17
+
18
+ process.exitCode = result.status != null ? result.status : 1;
package/dist/auth.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OAuth 2.0 Authorization Code + PKCE for AMP backend.
3
+ * Matches AMP endpoints: GET /api/oauth/authorize, POST /api/oauth/token.
4
+ * Uses http://127.0.0.1:5005/callback (per AMP web.config).
5
+ */
6
+ export interface TokenData {
7
+ access_token: string;
8
+ refresh_token?: string;
9
+ expires_in: number;
10
+ token_type?: string;
11
+ }
12
+ export interface StoredCredentials {
13
+ access_token: string;
14
+ refresh_token?: string;
15
+ expires_at: number;
16
+ }
17
+ export declare function getAmpBaseUrl(): string;
18
+ /**
19
+ * Return a valid access token. If none or expired, try refresh; if that fails, run full OAuth flow (browser).
20
+ */
21
+ export declare function ensureAccessToken(): Promise<string>;
22
+ /**
23
+ * Current auth status without triggering login. For "Authenticated: YES/NO" in Cursor chat.
24
+ */
25
+ export declare function getAuthStatus(): {
26
+ authenticated: boolean;
27
+ message: string;
28
+ };
package/dist/auth.js ADDED
@@ -0,0 +1,211 @@
1
+ /**
2
+ * OAuth 2.0 Authorization Code + PKCE for AMP backend.
3
+ * Matches AMP endpoints: GET /api/oauth/authorize, POST /api/oauth/token.
4
+ * Uses http://127.0.0.1:5005/callback (per AMP web.config).
5
+ */
6
+ import * as crypto from "crypto";
7
+ import * as fs from "fs";
8
+ import * as http from "http";
9
+ import * as os from "os";
10
+ import * as path from "path";
11
+ import open from "open";
12
+ const CALLBACK_PORT = parseInt(process.env.AMP_CALLBACK_PORT ?? "5005", 10);
13
+ const CALLBACK_PATH = "/callback";
14
+ const CLIENT_ID = process.env.AMP_OAUTH_CLIENT_ID ?? "amp-cursor-mcp";
15
+ const SCOPE = process.env.AMP_OAUTH_SCOPE ?? "api.read api.write";
16
+ const REFRESH_BUFFER_MS = 60 * 1000; // treat as expired 1 min early
17
+ function getCredentialsPath() {
18
+ const dir = process.env.AMP_MCP_CREDENTIALS_DIR ?? path.join(os.homedir(), ".amp-mcp");
19
+ try {
20
+ if (!fs.existsSync(dir))
21
+ fs.mkdirSync(dir, { mode: 0o700 });
22
+ }
23
+ catch {
24
+ return path.join(process.cwd(), ".amp-mcp-credentials.json");
25
+ }
26
+ return path.join(dir, "credentials.json");
27
+ }
28
+ function randomBase64Url(bytes) {
29
+ return crypto.randomBytes(bytes).toString("base64url");
30
+ }
31
+ function sha256Base64Url(input) {
32
+ return crypto.createHash("sha256").update(input, "utf8").digest("base64url");
33
+ }
34
+ export function getAmpBaseUrl() {
35
+ const base = process.env.AMP_BASE_URL ?? "http://localhost:39290";
36
+ return base.replace(/\/$/, "");
37
+ }
38
+ function loadCredentials() {
39
+ try {
40
+ const p = getCredentialsPath();
41
+ if (!fs.existsSync(p))
42
+ return null;
43
+ const raw = fs.readFileSync(p, "utf8");
44
+ const data = JSON.parse(raw);
45
+ if (data?.access_token && typeof data.expires_at === "number")
46
+ return data;
47
+ }
48
+ catch {
49
+ // ignore
50
+ }
51
+ return null;
52
+ }
53
+ function saveCredentials(creds) {
54
+ const p = getCredentialsPath();
55
+ try {
56
+ fs.writeFileSync(p, JSON.stringify(creds, null, 0), { mode: 0o600 });
57
+ }
58
+ catch (e) {
59
+ console.error("Failed to save credentials:", e);
60
+ }
61
+ }
62
+ /**
63
+ * Start local HTTP server, open browser to AMP authorize, receive callback, exchange code for tokens.
64
+ */
65
+ function runOAuthFlow() {
66
+ const state = randomBase64Url(24);
67
+ const codeVerifier = randomBase64Url(32);
68
+ const codeChallenge = sha256Base64Url(codeVerifier);
69
+ const redirectUri = `http://127.0.0.1:${CALLBACK_PORT}${CALLBACK_PATH}`;
70
+ const baseUrl = getAmpBaseUrl();
71
+ const authorizeUrl = `${baseUrl}/api/oauth/authorize?` +
72
+ new URLSearchParams({
73
+ client_id: CLIENT_ID,
74
+ response_type: "code",
75
+ redirect_uri: redirectUri,
76
+ scope: SCOPE,
77
+ state,
78
+ code_challenge: codeChallenge,
79
+ code_challenge_method: "S256",
80
+ }).toString();
81
+ return new Promise((resolve, reject) => {
82
+ const server = http.createServer((req, res) => {
83
+ const reqUrl = req.url ?? "/";
84
+ const url = new URL(reqUrl, `http://127.0.0.1:${CALLBACK_PORT}`);
85
+ if (url.pathname !== CALLBACK_PATH) {
86
+ res.writeHead(404);
87
+ res.end("Not found");
88
+ return;
89
+ }
90
+ const code = url.searchParams.get("code");
91
+ const returnedState = url.searchParams.get("state");
92
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
93
+ if (returnedState !== state) {
94
+ res.writeHead(400);
95
+ res.end("<h1>Invalid state</h1><p>You can close this window.</p>");
96
+ server.close();
97
+ reject(new Error("OAuth state mismatch"));
98
+ return;
99
+ }
100
+ if (!code) {
101
+ const err = url.searchParams.get("error") ?? "missing code";
102
+ res.writeHead(400);
103
+ res.end(`<h1>Authorization failed</h1><p>${err}</p><p>You can close this window.</p>`);
104
+ server.close();
105
+ reject(new Error(`OAuth error: ${err}`));
106
+ return;
107
+ }
108
+ res.writeHead(200);
109
+ res.end("<h1>Success</h1><p>You are authenticated. You can close this window and return to Cursor.</p>");
110
+ server.close();
111
+ exchangeCodeForTokens(code, codeVerifier, redirectUri)
112
+ .then(resolve)
113
+ .catch(reject);
114
+ });
115
+ server.listen(CALLBACK_PORT, "127.0.0.1", () => {
116
+ open(authorizeUrl);
117
+ });
118
+ server.on("error", (err) => {
119
+ reject(err);
120
+ });
121
+ });
122
+ }
123
+ function exchangeCodeForTokens(code, codeVerifier, redirectUri) {
124
+ const baseUrl = getAmpBaseUrl();
125
+ const body = new URLSearchParams({
126
+ grant_type: "authorization_code",
127
+ client_id: CLIENT_ID,
128
+ code,
129
+ redirect_uri: redirectUri,
130
+ code_verifier: codeVerifier,
131
+ }).toString();
132
+ return fetch(`${baseUrl}/api/oauth/token`, {
133
+ method: "POST",
134
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
135
+ body,
136
+ })
137
+ .then((r) => {
138
+ if (!r.ok)
139
+ return r.text().then((t) => Promise.reject(new Error(`Token exchange failed: ${r.status} ${t}`)));
140
+ return r.json();
141
+ })
142
+ .then((data) => {
143
+ const expiresAt = Date.now() + (data.expires_in ?? 3600) * 1000;
144
+ saveCredentials({
145
+ access_token: data.access_token,
146
+ refresh_token: data.refresh_token,
147
+ expires_at: expiresAt,
148
+ });
149
+ return data.access_token;
150
+ });
151
+ }
152
+ function refreshAccessToken() {
153
+ const creds = loadCredentials();
154
+ if (!creds?.refresh_token)
155
+ return Promise.reject(new Error("No refresh token"));
156
+ const baseUrl = getAmpBaseUrl();
157
+ const body = new URLSearchParams({
158
+ grant_type: "refresh_token",
159
+ client_id: CLIENT_ID,
160
+ refresh_token: creds.refresh_token,
161
+ }).toString();
162
+ return fetch(`${baseUrl}/api/oauth/token`, {
163
+ method: "POST",
164
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
165
+ body,
166
+ })
167
+ .then((r) => {
168
+ if (!r.ok)
169
+ return r
170
+ .text()
171
+ .then((t) => Promise.reject(new Error(`Refresh failed: ${r.status} ${t}`)));
172
+ return r.json();
173
+ })
174
+ .then((data) => {
175
+ const expiresAt = Date.now() + (data.expires_in ?? 3600) * 1000;
176
+ saveCredentials({
177
+ access_token: data.access_token,
178
+ refresh_token: data.refresh_token ?? creds.refresh_token,
179
+ expires_at: expiresAt,
180
+ });
181
+ return data.access_token;
182
+ });
183
+ }
184
+ /**
185
+ * Return a valid access token. If none or expired, try refresh; if that fails, run full OAuth flow (browser).
186
+ */
187
+ export async function ensureAccessToken() {
188
+ const creds = loadCredentials();
189
+ if (creds && Date.now() < creds.expires_at - REFRESH_BUFFER_MS)
190
+ return creds.access_token;
191
+ if (creds?.refresh_token) {
192
+ try {
193
+ return await refreshAccessToken();
194
+ }
195
+ catch {
196
+ // fall through to full flow
197
+ }
198
+ }
199
+ return runOAuthFlow();
200
+ }
201
+ /**
202
+ * Current auth status without triggering login. For "Authenticated: YES/NO" in Cursor chat.
203
+ */
204
+ export function getAuthStatus() {
205
+ const creds = loadCredentials();
206
+ const valid = !!creds && Date.now() < creds.expires_at - REFRESH_BUFFER_MS;
207
+ return {
208
+ authenticated: valid,
209
+ message: valid ? "Authenticated: YES" : "Authenticated: NO",
210
+ };
211
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Geeks AMP MCP server – local stdio server for Cursor.
3
+ * Performs OAuth 2.0 PKCE with AMP (browser login, callback, token storage/refresh),
4
+ * then proxies tool calls to AMP's MCPServer.ashx with Bearer token.
5
+ */
6
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Geeks AMP MCP server – local stdio server for Cursor.
3
+ * Performs OAuth 2.0 PKCE with AMP (browser login, callback, token storage/refresh),
4
+ * then proxies tool calls to AMP's MCPServer.ashx with Bearer token.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { z } from "zod";
9
+ import { ensureAccessToken, getAuthStatus } from "./auth.js";
10
+ import { callAmpTool } from "./tools.js";
11
+ async function main() {
12
+ const server = new McpServer({
13
+ name: "geeks-amp-mcp",
14
+ version: "1.0.0",
15
+ });
16
+ // get_auth_status – no AMP call; report stored auth state
17
+ server.tool("get_auth_status", "Report whether the MCP server is authenticated to AMP. Returns 'Authenticated: YES' or 'Authenticated: NO' without opening the browser.", {}, async () => {
18
+ const status = getAuthStatus();
19
+ return {
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: JSON.stringify({
24
+ authenticated: status.authenticated,
25
+ message: status.message,
26
+ }),
27
+ },
28
+ ],
29
+ };
30
+ });
31
+ // get_projects – ensure token, then call AMP
32
+ server.tool("get_projects", "Get a list of projects with their ID and Name from AMP.", {
33
+ searchTerm: z.string().optional().describe("Optional search term to filter projects by name or ID"),
34
+ limit: z.number().optional().describe("Maximum number of projects to return (default: 100)"),
35
+ }, async ({ searchTerm, limit, }) => {
36
+ const token = await ensureAccessToken();
37
+ const args = {};
38
+ if (searchTerm !== undefined)
39
+ args.searchTerm = searchTerm;
40
+ if (limit !== undefined)
41
+ args.limit = limit;
42
+ return callAmpTool("get_projects", args, token);
43
+ });
44
+ // get_amp_item_with_test_cases – ensure token, then call AMP
45
+ server.tool("get_amp_item_with_test_cases", "Get AMP (work item) details and all its test case references for a given AMP item. Use the reference number (e.g. 12345) or AMP-12345 format. Returns subject, note, status, progress, project, and a list of test case references (e.g. +AVFD) with the comment they appear in.", {
46
+ ampReference: z.string().describe("AMP item reference number, e.g. '12345' or 'AMP-12345'"),
47
+ }, async ({ ampReference }) => {
48
+ const token = await ensureAccessToken();
49
+ return callAmpTool("get_amp_item_with_test_cases", { ampReference }, token);
50
+ });
51
+ // Optional: audit-tdd-amp – get AMP item + test cases and report auth status (for /audit-tdd-amp in Cursor)
52
+ server.tool("audit_tdd_amp", "Get AMP item and its test case references, and report auth status. Use for /audit-tdd-amp flows in Cursor.", {
53
+ ampReference: z.string().describe("AMP item reference number, e.g. '12345' or 'AMP-12345'"),
54
+ }, async ({ ampReference }) => {
55
+ const token = await ensureAccessToken();
56
+ const result = await callAmpTool("get_amp_item_with_test_cases", { ampReference }, token);
57
+ const status = getAuthStatus();
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: JSON.stringify({
63
+ message: status.message,
64
+ ampItemAndTestCases: result.content[0]?.text,
65
+ }),
66
+ },
67
+ ],
68
+ };
69
+ });
70
+ const transport = new StdioServerTransport();
71
+ await server.connect(transport);
72
+ }
73
+ main().catch((err) => {
74
+ console.error("[geeks-amp-mcp] Startup error:", err?.message ?? err);
75
+ process.exit(1);
76
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tool definitions and AMP MCPServer.ashx proxy.
3
+ * All AMP tools require a valid Bearer token (obtained via auth.ensureAccessToken).
4
+ */
5
+ export interface ToolDefinition {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: {
9
+ type: "object";
10
+ properties: Record<string, {
11
+ type: string;
12
+ description?: string;
13
+ }>;
14
+ required?: string[];
15
+ };
16
+ }
17
+ export declare const TOOL_DEFINITIONS: ToolDefinition[];
18
+ export interface AmpToolResult {
19
+ content: Array<{
20
+ type: "text";
21
+ text: string;
22
+ }>;
23
+ isError?: boolean;
24
+ [key: string]: unknown;
25
+ }
26
+ /**
27
+ * Call AMP's MCPServer.ashx tools/call with the given Bearer token.
28
+ * Returns MCP-style content array; on HTTP or JSON-RPC error returns error content.
29
+ */
30
+ export declare function callAmpTool(toolName: string, args: Record<string, unknown>, accessToken: string): Promise<AmpToolResult>;
package/dist/tools.js ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Tool definitions and AMP MCPServer.ashx proxy.
3
+ * All AMP tools require a valid Bearer token (obtained via auth.ensureAccessToken).
4
+ */
5
+ import { getAmpBaseUrl } from "./auth.js";
6
+ export const TOOL_DEFINITIONS = [
7
+ {
8
+ name: "get_projects",
9
+ description: "Get a list of projects with their ID and Name from AMP.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ searchTerm: {
14
+ type: "string",
15
+ description: "Optional search term to filter projects by name or ID",
16
+ },
17
+ limit: {
18
+ type: "number",
19
+ description: "Maximum number of projects to return (default: 100)",
20
+ },
21
+ },
22
+ },
23
+ },
24
+ {
25
+ name: "get_amp_item_with_test_cases",
26
+ description: "Get AMP (work item) details and all its test case references for a given AMP item. Use the reference number (e.g. 12345) or AMP-12345 format. Returns subject, note, status, progress, project, and a list of test case references (e.g. +AVFD) with the comment they appear in.",
27
+ inputSchema: {
28
+ type: "object",
29
+ properties: {
30
+ ampReference: {
31
+ type: "string",
32
+ description: "AMP item reference number, e.g. '12345' or 'AMP-12345'",
33
+ },
34
+ },
35
+ required: ["ampReference"],
36
+ },
37
+ },
38
+ {
39
+ name: "get_auth_status",
40
+ description: "Report whether the MCP server is authenticated to AMP. Returns 'Authenticated: YES' or 'Authenticated: NO' without opening the browser.",
41
+ inputSchema: {
42
+ type: "object",
43
+ properties: {},
44
+ },
45
+ },
46
+ ];
47
+ /**
48
+ * Call AMP's MCPServer.ashx tools/call with the given Bearer token.
49
+ * Returns MCP-style content array; on HTTP or JSON-RPC error returns error content.
50
+ */
51
+ export async function callAmpTool(toolName, args, accessToken) {
52
+ const baseUrl = getAmpBaseUrl();
53
+ const url = `${baseUrl}/Services/MCPServer.ashx`;
54
+ const body = JSON.stringify({
55
+ jsonrpc: "2.0",
56
+ id: 1,
57
+ method: "tools/call",
58
+ params: { name: toolName, arguments: args },
59
+ });
60
+ const res = await fetch(url, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ Authorization: `Bearer ${accessToken}`,
65
+ },
66
+ body,
67
+ });
68
+ if (!res.ok) {
69
+ const text = await res.text();
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: JSON.stringify({
75
+ error: `AMP request failed: ${res.status}`,
76
+ body: text,
77
+ }),
78
+ },
79
+ ],
80
+ isError: true,
81
+ };
82
+ }
83
+ const json = (await res.json());
84
+ if (json.error) {
85
+ return {
86
+ content: [
87
+ {
88
+ type: "text",
89
+ text: JSON.stringify({
90
+ error: json.error.message ?? json.error,
91
+ }),
92
+ },
93
+ ],
94
+ isError: true,
95
+ };
96
+ }
97
+ const result = json.result;
98
+ if (result?.content && Array.isArray(result.content))
99
+ return { content: result.content };
100
+ return {
101
+ content: [
102
+ { type: "text", text: JSON.stringify(result ?? json) },
103
+ ],
104
+ };
105
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@geeks.ltd/geeks-amp-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Local MCP server for AMP (OAuth 2.0 PKCE, token storage, proxy to AMP MCPServer.ashx)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "geeks-amp-mcp": "./bin/geeks-amp-mcp.cjs"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "start": "node dist/index.js",
21
+ "dev": "node --import tsx src/index.ts"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "model-context-protocol",
29
+ "amp",
30
+ "oauth",
31
+ "cursor"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://bitbucket.org/geeks-ltd/geeks.amp.mcp",
36
+ "directory": "geeks-amp-mcp"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.0.0",
40
+ "open": "^10.0.0",
41
+ "zod": "^3.23.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.0.0",
45
+ "tsx": "^4.0.0",
46
+ "typescript": "^5.0.0"
47
+ }
48
+ }