duckpond-mcp-server 0.2.0 → 0.2.3

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,48 @@
1
+ import{a as C}from"./chunk-SU42EK5H.js";import{a as O,b as T,c as $,d as A,e as I,f as P}from"./chunk-43NWQIU3.js";import{b as R}from"./chunk-A3S6D44B.js";import{webcrypto as z}from"crypto";import{FastMCP as E}from"@jordanburke/fastmcp";import{createHash as D,randomBytes as f}from"crypto";import{createRequire as j}from"module";import*as y from"jsonwebtoken";import{URL as N}from"url";globalThis.crypto||(globalThis.crypto=z);var q=j(import.meta.url),v=q("../package.json"),u=R.fastmcp,b=process.env.DUCKPOND_JWT_SECRET||f(32).toString("hex"),S=process.env.DUCKPOND_JWT_EXPIRES_IN?parseInt(process.env.DUCKPOND_JWT_EXPIRES_IN,10):365*24*60*60,_=new Map,m=new Map;function J(t){u("\u{1F680} Initializing FastMCP server...");let i=new C(t.config),h={name:"duckpond",version:v.version,health:{enabled:!0,path:"/health",status:200,message:JSON.stringify({status:"healthy",service:"duckpond-mcp-server",version:v.version,timestamp:new Date().toISOString()})}},s=t.oauth?.enabled||t.basicAuth?new E({...h,oauth:{enabled:!0,authorizationServer:{issuer:t.oauth?.issuer||`http://localhost:${t.port||3e3}`,authorizationEndpoint:`${t.oauth?.issuer||`http://localhost:${t.port||3e3}`}/oauth/authorize`,tokenEndpoint:`${t.oauth?.issuer||`http://localhost:${t.port||3e3}`}/oauth/token`,jwksUri:`${t.oauth?.issuer||`http://localhost:${t.port||3e3}`}/oauth/jwks`,registrationEndpoint:`${t.oauth?.issuer||`http://localhost:${t.port||3e3}`}/oauth/register`,responseTypesSupported:["code"],grantTypesSupported:["authorization_code"],tokenEndpointAuthMethodsSupported:["client_secret_post","client_secret_basic"],codeChallengeMethodsSupported:["S256","plain"]},protectedResource:{resource:process.env.DUCKPOND_OAUTH_RESOURCE||t.oauth?.resource||`${t.oauth?.issuer||`http://localhost:${t.port||3e3}`}/mcp`,authorizationServers:[t.oauth?.issuer||`http://localhost:${t.port||3e3}`]}},authenticate:r=>{let e=r.headers?.authorization,n=t.oauth?.issuer||`http://localhost:${t.port||3e3}`;if(!e)throw t.oauth?.enabled?new Response(JSON.stringify({error:"unauthorized",error_description:"Authorization required. Please authenticate via OAuth."}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":`Bearer realm="MCP", authorization_uri="${n}/oauth/authorize", resource="${n}/.well-known/oauth-protected-resource"`}}):new Response(JSON.stringify({error:"unauthorized",error_description:"Authorization required."}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json"}});if(t.basicAuth&&e.startsWith("Basic ")){let c=Buffer.from(e.slice(6),"base64").toString("utf-8"),[a,d]=c.split(":");if(a===t.basicAuth.username&&d===t.basicAuth.password)return Promise.resolve({userId:t.basicAuth.userId||a,email:t.basicAuth.email||`${a}@example.com`,scope:"read write"});throw new Response(JSON.stringify({error:"unauthorized",error_description:"Invalid username or password"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Basic realm="MCP"'}})}if(t.oauth?.enabled&&e.startsWith("Bearer ")){let c=e.slice(7);try{let a=y.verify(c,b);if(!a.sub||!a.iat||!a.exp)throw new Response(JSON.stringify({error:"invalid_token",error_description:"Invalid token structure"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Invalid token structure"'}});let d=t.oauth?.resource||`${n}/mcp`;if(a.aud&&a.aud!==d)throw new Response(JSON.stringify({error:"invalid_token",error_description:"Token audience mismatch"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Token audience mismatch"'}});return Promise.resolve({userId:a.sub,email:a.email||"",scope:a.scope||"read write"})}catch(a){throw a instanceof Response?a:new Response(JSON.stringify({error:"invalid_token",error_description:"Invalid or expired token"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Invalid or expired token"'}})}}throw new Response(JSON.stringify({error:"unauthorized",error_description:"Invalid authorization header format"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":`Bearer realm="MCP", authorization_uri="${n}/oauth/authorize", resource="${n}/.well-known/oauth-protected-resource"`}})}}):new E(h);return s.addTool({name:"query",description:"Execute a SQL query for a specific user and return results",parameters:O,execute:async r=>{try{let e=await i.query(r.userId,r.sql);return e.success?JSON.stringify({rows:e.data,rowCount:e.data.length,executionTime:e.executionTime},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in query tool:",e);let n=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:n},null,2)}`}}}),s.addTool({name:"execute",description:"Execute SQL statement (DDL/DML) for a specific user without returning results",parameters:T,execute:async r=>{try{let e=await i.execute(r.userId,r.sql);return e.success?JSON.stringify({success:!0,message:"Statement executed successfully",executionTime:e.executionTime},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in execute tool:",e);let n=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:n},null,2)}`}}}),s.addTool({name:"getUserStats",description:"Get statistics about a user's database (memory usage, query count, etc.)",parameters:$,execute:async r=>{try{let e=await i.getUserStats(r.userId);return e.success?JSON.stringify({...e.data,lastAccess:e.data.lastAccess.toISOString()},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in getUserStats tool:",e);let n=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:n},null,2)}`}}}),s.addTool({name:"isAttached",description:"Check if a user's database is currently cached in memory",parameters:A,execute:async r=>{try{let e=i.isAttached(r.userId);return e.success?JSON.stringify({attached:e.data,userId:r.userId},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in isAttached tool:",e);let n=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:n},null,2)}`}}}),s.addTool({name:"detachUser",description:"Manually detach a user's database from the cache to free resources",parameters:I,execute:async r=>{try{let e=await i.detachUser(r.userId);return e.success?JSON.stringify({success:!0,message:`User ${r.userId} detached successfully`},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in detachUser tool:",e);let n=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:n},null,2)}`}}}),s.addTool({name:"listUsers",description:"List all currently cached users and cache statistics",parameters:P,execute:async()=>{try{let r=i.listUsers();return r.success?JSON.stringify(r.data,null,2):`ERROR: ${r.error.message}`}catch(r){u("Error in listUsers tool:",r);let e=r instanceof Error?r.message:String(r);return`ERROR: ${JSON.stringify({error:e},null,2)}`}}}),t.oauth?.enabled&&W(s,t),s.getApp().get("/",r=>{let e=t.oauth?.issuer||`http://localhost:${t.port||3e3}`,n={name:"DuckPond MCP Server",version:v.version,description:"Model Context Protocol server for multi-tenant DuckDB with R2/S3 storage",service:"duckpond-mcp-server",capabilities:{tools:["query","execute","getUserStats","isAttached","detachUser","listUsers"],transports:["stdio","http"],authentication:{oauth:t.oauth?.enabled||!1,basicAuth:!!t.basicAuth}},endpoints:{mcp:`${e}${t.endpoint||"/mcp"}`,health:`${e}/health`,...t.oauth?.enabled&&{oauth:{authorization:`${e}/oauth/authorize`,token:`${e}/oauth/token`,jwks:`${e}/oauth/jwks`,register:`${e}/oauth/register`}}},timestamp:new Date().toISOString()};return r.json(n)}),u("\u2713 FastMCP server created"),{server:s,duckpond:i}}function W(t,i){let h=t.getApp();setInterval(()=>{let s=Date.now();for(let[o,r]of _.entries())s-r.createdAt>6e5&&_.delete(o);for(let[o,r]of m.entries())s-r.createdAt>2592e6&&m.delete(o)},6e4),h.get("/oauth/authorize",s=>{let o=s.req.query(),r=o.response_type,e=o.redirect_uri,n=o.state,c=o.code_challenge,a=o.code_challenge_method,d=o.client_id;if(r!=="code")return s.json({error:"unsupported_response_type",error_description:"Only 'code' response type is supported"},400);if(!e)return s.json({error:"invalid_request",error_description:"redirect_uri is required"},400);if(c&&(!a||!["S256","plain"].includes(a)))return s.json({error:"invalid_request",error_description:"Invalid code_challenge_method. Only 'S256' and 'plain' are supported"},400);let l=`
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <title>OAuth Login - DuckPond MCP Server</title>
6
+ <style>
7
+ body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; padding: 20px; }
8
+ .form-group { margin-bottom: 15px; }
9
+ label { display: block; margin-bottom: 5px; font-weight: bold; }
10
+ input[type="text"], input[type="password"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
11
+ button { width: 100%; padding: 12px; background: #007cba; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
12
+ button:hover { background: #005a87; }
13
+ .app-info { background: #f5f5f5; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div class="app-info">
18
+ <h3>\u{1F510} OAuth Authorization</h3>
19
+ <p><strong>Application:</strong> ${d||"MCP Client"}</p>
20
+ <p><strong>Permissions:</strong> Read and write access to DuckDB databases</p>
21
+ </div>
22
+
23
+ <form method="POST" action="/oauth/authorize">
24
+ <input type="hidden" name="response_type" value="${r}">
25
+ <input type="hidden" name="redirect_uri" value="${e}">
26
+ <input type="hidden" name="state" value="${n||""}">
27
+ <input type="hidden" name="code_challenge" value="${c||""}">
28
+ <input type="hidden" name="code_challenge_method" value="${a||""}">
29
+ <input type="hidden" name="client_id" value="${d||""}">
30
+
31
+ <div class="form-group">
32
+ <label for="username">Username:</label>
33
+ <input type="text" id="username" name="username" required>
34
+ </div>
35
+
36
+ <div class="form-group">
37
+ <label for="password">Password:</label>
38
+ <input type="password" id="password" name="password" required>
39
+ </div>
40
+
41
+ <button type="submit">Authorize Application</button>
42
+ </form>
43
+ </body>
44
+ </html>`;return s.html(l)}),h.post("/oauth/authorize",async s=>{try{let o=await s.req.text(),r=new URLSearchParams(o),e=r.get("username"),n=r.get("password"),c=r.get("redirect_uri"),a=r.get("state"),d=r.get("code_challenge"),l=r.get("code_challenge_method");if(e!==i.oauth?.username||n!==i.oauth?.password)return s.html(`
45
+ <!DOCTYPE html>
46
+ <html><head><title>Login Failed</title><style>body{font-family:Arial;max-width:400px;margin:100px auto;padding:20px;}.error{color:red;background:#fee;padding:10px;border-radius:4px;margin-bottom:15px;}</style></head>
47
+ <body><div class="error">\u274C Invalid username or password</div><a href="javascript:history.back()">\u2190 Try Again</a></body></html>`,401);let w=f(16).toString("hex");_.set(w,{createdAt:Date.now(),redirectUri:c||"",codeChallenge:d||void 0,codeChallengeMethod:l||void 0,userId:i.oauth?.userId||e||"oauth-user"});let g=new N(c||"");return g.searchParams.set("code",w),a&&g.searchParams.set("state",a),s.redirect(g.toString(),302)}catch{return s.json({error:"invalid_request",error_description:"Failed to process authorization request"},400)}}),h.post("/oauth/token",async s=>{let o=await s.req.text(),r=new URLSearchParams(o),e=r.get("grant_type"),n=r.get("code"),c=r.get("redirect_uri"),a=r.get("code_verifier"),d=r.get("refresh_token");if(e==="refresh_token"){if(!d)return s.json({error:"invalid_request",error_description:"refresh_token is required for refresh_token grant type"},400);let p=m.get(d);if(!p)return s.json({error:"invalid_grant",error_description:"Invalid or expired refresh token"},400);m.delete(d);let M={sub:p.userId,email:p.email||"",scope:"read write",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+S,iss:i.oauth?.issuer||`http://localhost:${i.port||3e3}`,aud:i.oauth?.resource||`${i.oauth?.issuer||`http://localhost:${i.port||3e3}`}/mcp`},x=f(32).toString("hex");m.set(x,{createdAt:Date.now(),userId:p.userId,email:p.email});let U=y.sign(M,b);return s.json({access_token:U,token_type:"Bearer",expires_in:S,scope:"read write",refresh_token:x})}if(e!=="authorization_code")return s.json({error:"unsupported_grant_type",error_description:"Only 'authorization_code' and 'refresh_token' grant types are supported"},400);let l=_.get(n||"");if(!l)return s.json({error:"invalid_grant",error_description:"Invalid or expired authorization code"},400);if(l.redirectUri&&l.redirectUri!==c)return s.json({error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(l.codeChallenge){if(!a)return s.json({error:"invalid_grant",error_description:"code_verifier is required when code_challenge was used"},400);let p;if(l.codeChallengeMethod==="S256"?p=D("sha256").update(a).digest().toString("base64url"):p=a,p!==l.codeChallenge)return s.json({error:"invalid_grant",error_description:"Invalid code_verifier"},400)}_.delete(n);let w={sub:l.userId,email:i.oauth?.email||"",scope:"read write",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+S,iss:i.oauth?.issuer||`http://localhost:${i.port||3e3}`,aud:i.oauth?.resource||`${i.oauth?.issuer||`http://localhost:${i.port||3e3}`}/mcp`},g=f(32).toString("hex");m.set(g,{createdAt:Date.now(),userId:l.userId,email:i.oauth?.email});let k=y.sign(w,b);return s.json({access_token:k,token_type:"Bearer",expires_in:S,scope:"read write",refresh_token:g})}),h.get("/oauth/jwks",s=>s.json({keys:[{kty:"oct",use:"sig",kid:"duckpond-hmac-key",alg:"HS256"}]})),h.post("/oauth/register",async s=>{try{let o={};try{let c=await s.req.text();if(c&&c!=="[object Object]")try{o=JSON.parse(c)}catch{o=Object.fromEntries(new URLSearchParams(c))}}catch(c){u("Error parsing request body:",c)}let r=`client-${f(8).toString("hex")}`,e=f(16).toString("hex"),n={client_id:r,client_secret:e,client_id_issued_at:Math.floor(Date.now()/1e3),client_secret_expires_at:0,grant_types:o.grant_types||["authorization_code"],response_types:o.response_types||["code"],redirect_uris:o.redirect_uris||[],token_endpoint_auth_method:o.token_endpoint_auth_method||"client_secret_post"};return o.client_name&&(n.client_name=o.client_name),o.scope&&(n.scope=o.scope),s.json(n,201)}catch(o){return s.json({error:"invalid_client_metadata",error_description:"Invalid client registration request: "+(o instanceof Error?o.message:String(o))},400)}}),u("\u2713 OAuth flow endpoints added")}async function Y(t,i){let{server:h,duckpond:s}=J(t),o=await s.init();if(!o.success)throw new Error(`Failed to initialize DuckPond: ${o.error.message}`);u("DuckPond initialized successfully"),i==="stdio"?(await h.start({transportType:"stdio"}),u("\u2713 FastMCP server running with stdio transport")):(await h.start({transportType:"httpStream",httpStream:{port:t.port||3e3,endpoint:t.endpoint||"/mcp"}}),u(`\u2713 FastMCP server running on http://0.0.0.0:${t.port||3e3}${t.endpoint||"/mcp"}`),u("\u{1F50C} Connect with StreamableHTTPClientTransport")),process.on("SIGINT",async()=>{u("Received SIGINT, closing server..."),await s.close(),process.exit(0)}),process.on("SIGTERM",async()=>{u("Received SIGTERM, closing server..."),await s.close(),process.exit(0)})}export{J as a,Y as b};
48
+ //# sourceMappingURL=chunk-TAP5LWZ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["// Polyfill for Web Crypto API in Node.js environments\nimport { webcrypto } from \"crypto\"\n\nif (!globalThis.crypto) {\n globalThis.crypto = webcrypto as Crypto\n}\n\nimport { FastMCP } from \"@jordanburke/fastmcp\"\nimport { createHash, randomBytes } from \"crypto\"\nimport { createRequire } from \"module\"\n\nconst require = createRequire(import.meta.url)\nconst packageJson = require(\"../package.json\") as { version: string }\nimport * as jwt from \"jsonwebtoken\"\nimport { URL } from \"url\"\nimport { z } from \"zod\"\n\nimport { DuckPondServer, type DuckPondServerConfig } from \"./server-core\"\nimport {\n detachUserSchema,\n executeSchema,\n getUserStatsSchema,\n isAttachedSchema,\n listUsersSchema,\n querySchema,\n} from \"./tools\"\nimport { loggers } from \"./utils/logger\"\n\nconst log = loggers.fastmcp\n\nexport type OAuthConfig = {\n enabled: boolean\n username: string\n password: string\n userId: string\n email?: string\n issuer?: string\n resource?: string\n}\n\nexport type FastMCPServerOptions = {\n config: DuckPondServerConfig\n port?: number\n endpoint?: string\n oauth?: OAuthConfig\n basicAuth?: {\n username: string\n password: string\n userId?: string\n email?: string\n }\n}\n\n// JWT secret for token signing/validation\nconst JWT_SECRET = process.env.DUCKPOND_JWT_SECRET || randomBytes(32).toString(\"hex\")\n\n// JWT token expiration configuration (default: 1 year)\nconst JWT_EXPIRES_IN = process.env.DUCKPOND_JWT_EXPIRES_IN\n ? parseInt(process.env.DUCKPOND_JWT_EXPIRES_IN, 10)\n : 365 * 24 * 60 * 60 // 1 year in seconds\n\n// In-memory stores for OAuth flow\nconst authorizationCodes = new Map<\n string,\n {\n createdAt: number\n redirectUri?: string\n codeChallenge?: string\n codeChallengeMethod?: string\n userId: string\n }\n>()\n\nconst refreshTokens = new Map<\n string,\n {\n createdAt: number\n userId: string\n email?: string\n }\n>()\n\n// AuthSession type for FastMCP authentication\ntype AuthSession = {\n userId: string\n email: string\n scope: string\n [key: string]: unknown // Allow additional properties\n}\n\ntype OAuthClientRegistrationRequest = {\n grant_types?: string[]\n response_types?: string[]\n redirect_uris?: string[]\n token_endpoint_auth_method?: string\n client_name?: string\n scope?: string\n}\n\ntype OAuthClientRegistrationResponse = {\n client_id: string\n client_secret: string\n client_id_issued_at: number\n client_secret_expires_at: number\n grant_types: string[]\n response_types: string[]\n redirect_uris: string[]\n token_endpoint_auth_method: string\n client_name?: string\n scope?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): {\n server: FastMCP\n duckpond: DuckPondServer\n} {\n log(\"🚀 Initializing FastMCP server...\")\n\n // Create DuckPond server instance\n const duckpond = new DuckPondServer(options.config)\n\n // Build server configuration\n const baseConfig = {\n name: \"duckpond\",\n version: packageJson.version as `${number}.${number}.${number}`,\n health: {\n enabled: true,\n path: \"/health\",\n status: 200,\n message: JSON.stringify({\n status: \"healthy\",\n service: \"duckpond-mcp-server\",\n version: packageJson.version,\n timestamp: new Date().toISOString(),\n }),\n },\n }\n\n // Create server with authentication (OAuth, Basic Auth, or none)\n const server =\n options.oauth?.enabled || options.basicAuth\n ? new FastMCP<AuthSession>({\n ...baseConfig,\n oauth: {\n enabled: true,\n authorizationServer: {\n issuer: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n authorizationEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/authorize`,\n tokenEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/token`,\n jwksUri: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/jwks`,\n registrationEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/register`,\n responseTypesSupported: [\"code\"],\n grantTypesSupported: [\"authorization_code\"],\n tokenEndpointAuthMethodsSupported: [\"client_secret_post\", \"client_secret_basic\"],\n codeChallengeMethodsSupported: [\"S256\", \"plain\"],\n },\n protectedResource: {\n resource:\n process.env.DUCKPOND_OAUTH_RESOURCE ||\n options.oauth?.resource ||\n `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n authorizationServers: [options.oauth?.issuer || `http://localhost:${options.port || 3000}`],\n },\n },\n authenticate: (request) => {\n const authHeader = request.headers?.authorization\n const baseUrl = options.oauth?.issuer || `http://localhost:${options.port || 3000}`\n\n // For OAuth-enabled servers, require authentication\n if (!authHeader) {\n if (options.oauth?.enabled) {\n // Return HTTP 401 with WWW-Authenticate header for proper OAuth discovery\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Authorization required. Please authenticate via OAuth.\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", authorization_uri=\"${baseUrl}/oauth/authorize\", resource=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n },\n },\n )\n }\n\n // For non-OAuth servers, also require some form of auth\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Authorization required.\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n },\n )\n }\n\n // Handle Basic Authentication\n if (options.basicAuth && authHeader.startsWith(\"Basic \")) {\n const credentials = Buffer.from(authHeader.slice(6), \"base64\").toString(\"utf-8\")\n const [username, password] = credentials.split(\":\")\n\n if (username === options.basicAuth.username && password === options.basicAuth.password) {\n return Promise.resolve({\n userId: options.basicAuth.userId || username,\n email: options.basicAuth.email || `${username}@example.com`,\n scope: \"read write\",\n })\n } else {\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Invalid username or password\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Basic realm=\"MCP\"`,\n },\n },\n )\n }\n }\n\n // Handle Bearer Token (OAuth) - Validate JWT\n if (options.oauth?.enabled && authHeader.startsWith(\"Bearer \")) {\n const token = authHeader.slice(7) // Remove 'Bearer ' prefix\n\n try {\n // Verify JWT token\n const decoded = jwt.verify(token, JWT_SECRET) as jwt.JwtPayload\n\n if (!decoded.sub || !decoded.iat || !decoded.exp) {\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Invalid token structure\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Invalid token structure\"`,\n },\n },\n )\n }\n\n // Validate audience\n const expectedAudience = options.oauth?.resource || `${baseUrl}/mcp`\n if (decoded.aud && decoded.aud !== expectedAudience) {\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Token audience mismatch\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Token audience mismatch\"`,\n },\n },\n )\n }\n\n // Return user info from JWT claims\n return Promise.resolve({\n userId: decoded.sub,\n email: (decoded.email as string) || \"\",\n scope: (decoded.scope as string) || \"read write\",\n })\n } catch (error) {\n if (error instanceof Response) {\n throw error // Re-throw our custom Response errors\n }\n\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Invalid or expired token\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Invalid or expired token\"`,\n },\n },\n )\n }\n }\n\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Invalid authorization header format\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", authorization_uri=\"${baseUrl}/oauth/authorize\", resource=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n },\n },\n )\n },\n })\n : new FastMCP(baseConfig)\n\n // Add query tool\n server.addTool({\n name: \"query\",\n description: \"Execute a SQL query for a specific user and return results\",\n parameters: querySchema,\n execute: async (args) => {\n try {\n const result = await duckpond.query(args.userId, args.sql)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n rows: result.data,\n rowCount: result.data.length,\n executionTime: result.executionTime,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in query tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add execute tool\n server.addTool({\n name: \"execute\",\n description: \"Execute SQL statement (DDL/DML) for a specific user without returning results\",\n parameters: executeSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.execute(args.userId, args.sql)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n success: true,\n message: \"Statement executed successfully\",\n executionTime: result.executionTime,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in execute tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add getUserStats tool\n server.addTool({\n name: \"getUserStats\",\n description: \"Get statistics about a user's database (memory usage, query count, etc.)\",\n parameters: getUserStatsSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.getUserStats(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n ...result.data,\n lastAccess: result.data.lastAccess.toISOString(),\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in getUserStats tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add isAttached tool\n server.addTool({\n name: \"isAttached\",\n description: \"Check if a user's database is currently cached in memory\",\n parameters: isAttachedSchema,\n execute: async (args) => {\n try {\n const result = duckpond.isAttached(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n attached: result.data,\n userId: args.userId,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in isAttached tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add detachUser tool\n server.addTool({\n name: \"detachUser\",\n description: \"Manually detach a user's database from the cache to free resources\",\n parameters: detachUserSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.detachUser(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n success: true,\n message: `User ${args.userId} detached successfully`,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in detachUser tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add listUsers tool\n server.addTool({\n name: \"listUsers\",\n description: \"List all currently cached users and cache statistics\",\n parameters: listUsersSchema,\n execute: async () => {\n try {\n const result = duckpond.listUsers()\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(result.data, null, 2)\n } catch (error) {\n log(\"Error in listUsers tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add OAuth flow endpoints if OAuth is enabled\n if (options.oauth?.enabled) {\n setupOAuthEndpoints(server, options)\n }\n\n // Add root info endpoint using Hono\n const app = server.getApp()\n app.get(\"/\", (c) => {\n const baseUrl = options.oauth?.issuer || `http://localhost:${options.port || 3000}`\n\n const serverInfo = {\n name: \"DuckPond MCP Server\",\n version: packageJson.version,\n description: \"Model Context Protocol server for multi-tenant DuckDB with R2/S3 storage\",\n service: \"duckpond-mcp-server\",\n capabilities: {\n tools: [\"query\", \"execute\", \"getUserStats\", \"isAttached\", \"detachUser\", \"listUsers\"],\n transports: [\"stdio\", \"http\"],\n authentication: {\n oauth: options.oauth?.enabled || false,\n basicAuth: !!options.basicAuth,\n },\n },\n endpoints: {\n mcp: `${baseUrl}${options.endpoint || \"/mcp\"}`,\n health: `${baseUrl}/health`,\n ...(options.oauth?.enabled && {\n oauth: {\n authorization: `${baseUrl}/oauth/authorize`,\n token: `${baseUrl}/oauth/token`,\n jwks: `${baseUrl}/oauth/jwks`,\n register: `${baseUrl}/oauth/register`,\n },\n }),\n },\n timestamp: new Date().toISOString(),\n }\n\n return c.json(serverInfo)\n })\n\n log(\"✓ FastMCP server created\")\n\n return { server, duckpond }\n}\n\nfunction setupOAuthEndpoints(server: FastMCP, options: FastMCPServerOptions): void {\n const app = server.getApp()\n\n // Clean up old codes and refresh tokens every minute\n setInterval(() => {\n const now = Date.now()\n // Clean authorization codes (10 minutes)\n for (const [code, data] of authorizationCodes.entries()) {\n if (now - data.createdAt > 600000) {\n authorizationCodes.delete(code)\n }\n }\n // Clean refresh tokens (30 days)\n for (const [token, data] of refreshTokens.entries()) {\n if (now - data.createdAt > 2592000000) {\n refreshTokens.delete(token)\n }\n }\n }, 60000)\n\n // OAuth Authorization Endpoint - Login Form\n app.get(\"/oauth/authorize\", (c) => {\n const params = c.req.query()\n const responseType = params.response_type\n const redirectUri = params.redirect_uri\n const state = params.state\n const codeChallenge = params.code_challenge\n const codeChallengeMethod = params.code_challenge_method\n const clientId = params.client_id\n\n if (responseType !== \"code\") {\n return c.json(\n {\n error: \"unsupported_response_type\",\n error_description: \"Only 'code' response type is supported\",\n },\n 400,\n )\n }\n\n if (!redirectUri) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"redirect_uri is required\",\n },\n 400,\n )\n }\n\n // Validate PKCE parameters if present\n if (codeChallenge) {\n if (!codeChallengeMethod || ![\"S256\", \"plain\"].includes(codeChallengeMethod)) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"Invalid code_challenge_method. Only 'S256' and 'plain' are supported\",\n },\n 400,\n )\n }\n }\n\n // Serve login form\n const loginForm = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>OAuth Login - DuckPond MCP Server</title>\n <style>\n body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; padding: 20px; }\n .form-group { margin-bottom: 15px; }\n label { display: block; margin-bottom: 5px; font-weight: bold; }\n input[type=\"text\"], input[type=\"password\"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }\n button { width: 100%; padding: 12px; background: #007cba; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }\n button:hover { background: #005a87; }\n .app-info { background: #f5f5f5; padding: 15px; border-radius: 4px; margin-bottom: 20px; }\n </style>\n</head>\n<body>\n <div class=\"app-info\">\n <h3>🔐 OAuth Authorization</h3>\n <p><strong>Application:</strong> ${clientId || \"MCP Client\"}</p>\n <p><strong>Permissions:</strong> Read and write access to DuckDB databases</p>\n </div>\n\n <form method=\"POST\" action=\"/oauth/authorize\">\n <input type=\"hidden\" name=\"response_type\" value=\"${responseType}\">\n <input type=\"hidden\" name=\"redirect_uri\" value=\"${redirectUri}\">\n <input type=\"hidden\" name=\"state\" value=\"${state || \"\"}\">\n <input type=\"hidden\" name=\"code_challenge\" value=\"${codeChallenge || \"\"}\">\n <input type=\"hidden\" name=\"code_challenge_method\" value=\"${codeChallengeMethod || \"\"}\">\n <input type=\"hidden\" name=\"client_id\" value=\"${clientId || \"\"}\">\n\n <div class=\"form-group\">\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\" required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\" required>\n </div>\n\n <button type=\"submit\">Authorize Application</button>\n </form>\n</body>\n</html>`\n\n return c.html(loginForm)\n })\n\n // OAuth Authorization POST - Process Login\n app.post(\"/oauth/authorize\", async (c) => {\n try {\n const body = await c.req.text()\n const params = new URLSearchParams(body)\n\n const username = params.get(\"username\")\n const password = params.get(\"password\")\n const redirectUri = params.get(\"redirect_uri\")\n const state = params.get(\"state\")\n const codeChallenge = params.get(\"code_challenge\")\n const codeChallengeMethod = params.get(\"code_challenge_method\")\n\n // Validate credentials\n if (username !== options.oauth?.username || password !== options.oauth?.password) {\n const errorForm = `\n<!DOCTYPE html>\n<html><head><title>Login Failed</title><style>body{font-family:Arial;max-width:400px;margin:100px auto;padding:20px;}.error{color:red;background:#fee;padding:10px;border-radius:4px;margin-bottom:15px;}</style></head>\n<body><div class=\"error\">❌ Invalid username or password</div><a href=\"javascript:history.back()\">← Try Again</a></body></html>`\n return c.html(errorForm, 401)\n }\n\n // Generate authorization code\n const code = randomBytes(16).toString(\"hex\")\n authorizationCodes.set(code, {\n createdAt: Date.now(),\n redirectUri: redirectUri || \"\",\n codeChallenge: codeChallenge || undefined,\n codeChallengeMethod: codeChallengeMethod || undefined,\n userId: options.oauth?.userId || username || \"oauth-user\",\n })\n\n // Redirect with authorization code\n const redirectUrl = new URL(redirectUri || \"\")\n redirectUrl.searchParams.set(\"code\", code)\n if (state) {\n redirectUrl.searchParams.set(\"state\", state)\n }\n\n return c.redirect(redirectUrl.toString(), 302)\n } catch {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"Failed to process authorization request\",\n },\n 400,\n )\n }\n })\n\n // OAuth Token Endpoint\n app.post(\"/oauth/token\", async (c) => {\n const body = await c.req.text()\n const params = new URLSearchParams(body)\n const grantType = params.get(\"grant_type\")\n const code = params.get(\"code\")\n const redirectUri = params.get(\"redirect_uri\")\n const codeVerifier = params.get(\"code_verifier\")\n const refreshTokenParam = params.get(\"refresh_token\")\n\n if (grantType === \"refresh_token\") {\n // Handle refresh token flow\n if (!refreshTokenParam) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"refresh_token is required for refresh_token grant type\",\n },\n 400,\n )\n }\n\n const tokenData = refreshTokens.get(refreshTokenParam)\n if (!tokenData) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid or expired refresh token\",\n },\n 400,\n )\n }\n\n // Remove old refresh token (token rotation)\n refreshTokens.delete(refreshTokenParam)\n\n // Generate new JWT access token\n const accessTokenPayload = {\n sub: tokenData.userId,\n email: tokenData.email || \"\",\n scope: \"read write\",\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + JWT_EXPIRES_IN,\n iss: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n aud: options.oauth?.resource || `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n }\n\n // Generate new refresh token\n const newRefreshToken = randomBytes(32).toString(\"hex\")\n refreshTokens.set(newRefreshToken, {\n createdAt: Date.now(),\n userId: tokenData.userId,\n email: tokenData.email,\n })\n\n const accessToken = jwt.sign(accessTokenPayload, JWT_SECRET)\n\n return c.json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: JWT_EXPIRES_IN,\n scope: \"read write\",\n refresh_token: newRefreshToken,\n })\n }\n\n if (grantType !== \"authorization_code\") {\n return c.json(\n {\n error: \"unsupported_grant_type\",\n error_description: \"Only 'authorization_code' and 'refresh_token' grant types are supported\",\n },\n 400,\n )\n }\n\n const codeData = authorizationCodes.get(code || \"\")\n if (!codeData) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid or expired authorization code\",\n },\n 400,\n )\n }\n\n // Validate redirect_uri matches\n if (codeData.redirectUri && codeData.redirectUri !== redirectUri) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"redirect_uri mismatch\",\n },\n 400,\n )\n }\n\n // Validate PKCE if code_challenge was provided\n if (codeData.codeChallenge) {\n if (!codeVerifier) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"code_verifier is required when code_challenge was used\",\n },\n 400,\n )\n }\n\n let expectedChallenge: string\n if (codeData.codeChallengeMethod === \"S256\") {\n expectedChallenge = createHash(\"sha256\").update(codeVerifier).digest().toString(\"base64url\")\n } else {\n // 'plain' method\n expectedChallenge = codeVerifier\n }\n\n if (expectedChallenge !== codeData.codeChallenge) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid code_verifier\",\n },\n 400,\n )\n }\n }\n\n // Remove used code\n authorizationCodes.delete(code!)\n\n // Generate JWT access token\n const accessTokenPayload = {\n sub: codeData.userId,\n email: options.oauth?.email || \"\",\n scope: \"read write\",\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + JWT_EXPIRES_IN,\n iss: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n aud: options.oauth?.resource || `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n }\n\n // Generate refresh token\n const refreshToken = randomBytes(32).toString(\"hex\")\n refreshTokens.set(refreshToken, {\n createdAt: Date.now(),\n userId: codeData.userId,\n email: options.oauth?.email,\n })\n\n const accessToken = jwt.sign(accessTokenPayload, JWT_SECRET)\n\n return c.json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: JWT_EXPIRES_IN,\n scope: \"read write\",\n refresh_token: refreshToken,\n })\n })\n\n // JWKS Endpoint\n app.get(\"/oauth/jwks\", (c) => {\n return c.json({\n keys: [\n {\n kty: \"oct\", // Octet sequence for symmetric keys\n use: \"sig\",\n kid: \"duckpond-hmac-key\",\n alg: \"HS256\",\n },\n ],\n })\n })\n\n // Dynamic Client Registration\n app.post(\"/oauth/register\", async (c) => {\n try {\n let registrationRequest: OAuthClientRegistrationRequest = {}\n\n try {\n const body = await c.req.text()\n if (body && body !== \"[object Object]\") {\n try {\n registrationRequest = JSON.parse(body) as OAuthClientRegistrationRequest\n } catch {\n const formData = Object.fromEntries(new URLSearchParams(body))\n registrationRequest = formData as OAuthClientRegistrationRequest\n }\n }\n } catch (parseError) {\n log(\"Error parsing request body:\", parseError)\n }\n\n const clientId = `client-${randomBytes(8).toString(\"hex\")}`\n const clientSecret = randomBytes(16).toString(\"hex\")\n\n const response: OAuthClientRegistrationResponse = {\n client_id: clientId,\n client_secret: clientSecret,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_secret_expires_at: 0, // Never expires\n grant_types: registrationRequest.grant_types || [\"authorization_code\"],\n response_types: registrationRequest.response_types || [\"code\"],\n redirect_uris: registrationRequest.redirect_uris || [],\n token_endpoint_auth_method: registrationRequest.token_endpoint_auth_method || \"client_secret_post\",\n }\n\n if (registrationRequest.client_name) {\n response.client_name = registrationRequest.client_name\n }\n if (registrationRequest.scope) {\n response.scope = registrationRequest.scope\n }\n\n return c.json(response, 201)\n } catch (error) {\n return c.json(\n {\n error: \"invalid_client_metadata\",\n error_description:\n \"Invalid client registration request: \" + (error instanceof Error ? error.message : String(error)),\n },\n 400,\n )\n }\n })\n\n log(\"✓ OAuth flow endpoints added\")\n}\nexport async function startServer(options: FastMCPServerOptions, transport: \"stdio\" | \"http\"): Promise<void> {\n const { server, duckpond } = createFastMCPServer(options)\n\n // Initialize DuckPond\n const initResult = await duckpond.init()\n if (!initResult.success) {\n throw new Error(`Failed to initialize DuckPond: ${initResult.error.message}`)\n }\n\n log(\"DuckPond initialized successfully\")\n\n // Start the server with appropriate transport\n if (transport === \"stdio\") {\n await server.start({\n transportType: \"stdio\",\n })\n log(\"✓ FastMCP server running with stdio transport\")\n } else {\n await server.start({\n transportType: \"httpStream\",\n httpStream: {\n port: options.port || 3000,\n endpoint: (options.endpoint || \"/mcp\") as `/${string}`,\n },\n })\n log(`✓ FastMCP server running on http://0.0.0.0:${options.port || 3000}${options.endpoint || \"/mcp\"}`)\n log(\"🔌 Connect with StreamableHTTPClientTransport\")\n }\n\n // Handle cleanup on exit\n process.on(\"SIGINT\", async () => {\n log(\"Received SIGINT, closing server...\")\n await duckpond.close()\n process.exit(0)\n })\n\n process.on(\"SIGTERM\", async () => {\n log(\"Received SIGTERM, closing server...\")\n await duckpond.close()\n process.exit(0)\n })\n}\n"],"mappings":"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","names":["webcrypto","FastMCP","createHash","randomBytes","createRequire","jwt","URL","webcrypto","require","createRequire","packageJson","log","loggers","JWT_SECRET","randomBytes","JWT_EXPIRES_IN","authorizationCodes","refreshTokens","createFastMCPServer","options","duckpond","DuckPondServer","baseConfig","server","FastMCP","request","authHeader","baseUrl","credentials","username","password","token","decoded","expectedAudience","error","querySchema","args","result","errorMessage","executeSchema","getUserStatsSchema","isAttachedSchema","detachUserSchema","listUsersSchema","setupOAuthEndpoints","c","serverInfo","app","now","code","data","params","responseType","redirectUri","state","codeChallenge","codeChallengeMethod","clientId","loginForm","body","redirectUrl","URL","grantType","codeVerifier","refreshTokenParam","tokenData","accessTokenPayload","newRefreshToken","accessToken","codeData","expectedChallenge","createHash","refreshToken","registrationRequest","parseError","clientSecret","response","startServer","transport","initResult"]}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{b as a}from"./chunk-3FXN77J7.js";import"./chunk-SU42EK5H.js";import"./chunk-43NWQIU3.js";import{b as D}from"./chunk-A3S6D44B.js";import{webcrypto as i}from"crypto";import{Command as _}from"commander";globalThis.crypto||(globalThis.crypto=i);var t=D.main;function C(){let r={memoryLimit:process.env.DUCKPOND_MEMORY_LIMIT||"4GB",threads:parseInt(process.env.DUCKPOND_THREADS||"4"),maxActiveUsers:parseInt(process.env.DUCKPOND_MAX_ACTIVE_USERS||"10"),evictionTimeout:parseInt(process.env.DUCKPOND_EVICTION_TIMEOUT||"300000"),cacheType:process.env.DUCKPOND_CACHE_TYPE||"disk",strategy:process.env.DUCKPOND_STRATEGY||"parquet",tempDir:process.env.DUCKPOND_TEMP_DIR,cacheDir:process.env.DUCKPOND_CACHE_DIR};return process.env.DUCKPOND_R2_ACCOUNT_ID&&(r.r2={accountId:process.env.DUCKPOND_R2_ACCOUNT_ID,accessKeyId:process.env.DUCKPOND_R2_ACCESS_KEY_ID||"",secretAccessKey:process.env.DUCKPOND_R2_SECRET_ACCESS_KEY||"",bucket:process.env.DUCKPOND_R2_BUCKET||""}),process.env.DUCKPOND_S3_REGION&&(r.s3={region:process.env.DUCKPOND_S3_REGION,accessKeyId:process.env.DUCKPOND_S3_ACCESS_KEY_ID||"",secretAccessKey:process.env.DUCKPOND_S3_SECRET_ACCESS_KEY||"",bucket:process.env.DUCKPOND_S3_BUCKET||""},process.env.DUCKPOND_S3_ENDPOINT&&(r.s3.endpoint=process.env.DUCKPOND_S3_ENDPOINT)),r}var p=new _;p.name("duckpond-mcp-server").description("MCP server for multi-tenant DuckDB management with R2/S3 storage").version("0.1.0").option("-t, --transport <type>","Transport mode: stdio or http","stdio").option("-p, --port <port>","HTTP port (when using http transport)","3000").action(async r=>{try{let e=C();t(`Starting DuckPond MCP Server with ${r.transport} transport`),t("Configuration:",{memoryLimit:e.memoryLimit,threads:e.threads,maxActiveUsers:e.maxActiveUsers,strategy:e.strategy,tempDir:e.tempDir,cacheDir:e.cacheDir,cacheType:e.cacheType,hasR2:!!e.r2,hasS3:!!e.s3});let o;if(process.env.DUCKPOND_OAUTH_ENABLED==="true"){let n=process.env.DUCKPOND_OAUTH_USERNAME,c=process.env.DUCKPOND_OAUTH_PASSWORD;(!n||!c)&&(console.error("\u274C OAuth enabled but DUCKPOND_OAUTH_USERNAME and DUCKPOND_OAUTH_PASSWORD are required"),process.exit(1)),o={enabled:!0,username:n,password:c,userId:process.env.DUCKPOND_OAUTH_USER_ID||n,email:process.env.DUCKPOND_OAUTH_EMAIL,issuer:process.env.DUCKPOND_OAUTH_ISSUER||`http://localhost:${parseInt(r.port)||3e3}`,resource:process.env.DUCKPOND_OAUTH_RESOURCE},console.error("\u{1F510} OAuth enabled with username/password authentication"),console.error(` Username: ${o.username}`),console.error(` User ID: ${o.userId}`),console.error(" \u2713 Login form will be shown at authorization endpoint")}let s;process.env.DUCKPOND_BASIC_AUTH_USERNAME&&process.env.DUCKPOND_BASIC_AUTH_PASSWORD&&(s={username:process.env.DUCKPOND_BASIC_AUTH_USERNAME,password:process.env.DUCKPOND_BASIC_AUTH_PASSWORD,userId:process.env.DUCKPOND_BASIC_AUTH_USER_ID,email:process.env.DUCKPOND_BASIC_AUTH_EMAIL},console.error("\u{1F510} Basic authentication enabled"),console.error(` Username: ${s.username}`),console.error(` User ID: ${s.userId||s.username}`)),r.transport==="stdio"||r.transport==="http"?await a({config:e,port:parseInt(r.port)||3e3,endpoint:"/mcp",oauth:o,basicAuth:s},r.transport==="stdio"?"stdio":"http"):(t(`Unknown transport: ${r.transport}`),process.exit(1))}catch(e){t("Fatal error:",e),console.error("Fatal error:",e),process.exit(1)}});p.parse();
2
+ import{b as i}from"./chunk-TAP5LWZ7.js";import"./chunk-SU42EK5H.js";import"./chunk-43NWQIU3.js";import{b as a}from"./chunk-A3S6D44B.js";import{webcrypto as p}from"crypto";import{Command as _}from"commander";import{createRequire as C}from"module";globalThis.crypto||(globalThis.crypto=p);var U=C(import.meta.url),O=U("../package.json"),t=a.main;function m(){let r={memoryLimit:process.env.DUCKPOND_MEMORY_LIMIT||"4GB",threads:parseInt(process.env.DUCKPOND_THREADS||"4"),maxActiveUsers:parseInt(process.env.DUCKPOND_MAX_ACTIVE_USERS||"10"),evictionTimeout:parseInt(process.env.DUCKPOND_EVICTION_TIMEOUT||"300000"),cacheType:process.env.DUCKPOND_CACHE_TYPE||"disk",strategy:process.env.DUCKPOND_STRATEGY||"parquet",tempDir:process.env.DUCKPOND_TEMP_DIR,cacheDir:process.env.DUCKPOND_CACHE_DIR};return process.env.DUCKPOND_R2_ACCOUNT_ID&&(r.r2={accountId:process.env.DUCKPOND_R2_ACCOUNT_ID,accessKeyId:process.env.DUCKPOND_R2_ACCESS_KEY_ID||"",secretAccessKey:process.env.DUCKPOND_R2_SECRET_ACCESS_KEY||"",bucket:process.env.DUCKPOND_R2_BUCKET||""}),process.env.DUCKPOND_S3_REGION&&(r.s3={region:process.env.DUCKPOND_S3_REGION,accessKeyId:process.env.DUCKPOND_S3_ACCESS_KEY_ID||"",secretAccessKey:process.env.DUCKPOND_S3_SECRET_ACCESS_KEY||"",bucket:process.env.DUCKPOND_S3_BUCKET||""},process.env.DUCKPOND_S3_ENDPOINT&&(r.s3.endpoint=process.env.DUCKPOND_S3_ENDPOINT)),r}var D=new _;D.name("duckpond-mcp-server").description("MCP server for multi-tenant DuckDB management with R2/S3 storage").version(O.version).option("-t, --transport <type>","Transport mode: stdio or http","stdio").option("-p, --port <port>","HTTP port (when using http transport)","3000").action(async r=>{try{let e=m();t(`Starting DuckPond MCP Server with ${r.transport} transport`),t("Configuration:",{memoryLimit:e.memoryLimit,threads:e.threads,maxActiveUsers:e.maxActiveUsers,strategy:e.strategy,tempDir:e.tempDir,cacheDir:e.cacheDir,cacheType:e.cacheType,hasR2:!!e.r2,hasS3:!!e.s3});let o;if(process.env.DUCKPOND_OAUTH_ENABLED==="true"){let n=process.env.DUCKPOND_OAUTH_USERNAME,c=process.env.DUCKPOND_OAUTH_PASSWORD;(!n||!c)&&(console.error("\u274C OAuth enabled but DUCKPOND_OAUTH_USERNAME and DUCKPOND_OAUTH_PASSWORD are required"),process.exit(1)),o={enabled:!0,username:n,password:c,userId:process.env.DUCKPOND_OAUTH_USER_ID||n,email:process.env.DUCKPOND_OAUTH_EMAIL,issuer:process.env.DUCKPOND_OAUTH_ISSUER||`http://localhost:${parseInt(r.port)||3e3}`,resource:process.env.DUCKPOND_OAUTH_RESOURCE},console.error("\u{1F510} OAuth enabled with username/password authentication"),console.error(` Username: ${o.username}`),console.error(` User ID: ${o.userId}`),console.error(" \u2713 Login form will be shown at authorization endpoint")}let s;process.env.DUCKPOND_BASIC_AUTH_USERNAME&&process.env.DUCKPOND_BASIC_AUTH_PASSWORD&&(s={username:process.env.DUCKPOND_BASIC_AUTH_USERNAME,password:process.env.DUCKPOND_BASIC_AUTH_PASSWORD,userId:process.env.DUCKPOND_BASIC_AUTH_USER_ID,email:process.env.DUCKPOND_BASIC_AUTH_EMAIL},console.error("\u{1F510} Basic authentication enabled"),console.error(` Username: ${s.username}`),console.error(` User ID: ${s.userId||s.username}`)),r.transport==="stdio"||r.transport==="http"?await i({config:e,port:parseInt(r.port)||3e3,endpoint:"/mcp",oauth:o,basicAuth:s},r.transport==="stdio"?"stdio":"http"):(t(`Unknown transport: ${r.transport}`),process.exit(1))}catch(e){t("Fatal error:",e),console.error("Fatal error:",e),process.exit(1)}});D.parse();
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Polyfill for Web Crypto API in Node.js environments\nimport { webcrypto } from \"crypto\"\n\nif (!globalThis.crypto) {\n globalThis.crypto = webcrypto as Crypto\n}\n\nimport { Command } from \"commander\"\n\nimport type { OAuthConfig } from \"./server\"\nimport { startServer } from \"./server\"\nimport type { DuckPondServerConfig } from \"./server-core\"\nimport { loggers } from \"./utils/logger\"\n\nconst log = loggers.main\n\n/**\n * Parse environment variables into DuckPond configuration\n */\nfunction getConfigFromEnv(): DuckPondServerConfig {\n const config: DuckPondServerConfig = {\n memoryLimit: process.env.DUCKPOND_MEMORY_LIMIT || \"4GB\",\n threads: parseInt(process.env.DUCKPOND_THREADS || \"4\"),\n maxActiveUsers: parseInt(process.env.DUCKPOND_MAX_ACTIVE_USERS || \"10\"),\n evictionTimeout: parseInt(process.env.DUCKPOND_EVICTION_TIMEOUT || \"300000\"),\n cacheType: (process.env.DUCKPOND_CACHE_TYPE as \"disk\" | \"memory\" | \"noop\") || \"disk\",\n strategy: (process.env.DUCKPOND_STRATEGY as \"parquet\" | \"duckdb\" | \"hybrid\") || \"parquet\",\n tempDir: process.env.DUCKPOND_TEMP_DIR,\n cacheDir: process.env.DUCKPOND_CACHE_DIR,\n }\n\n // R2 configuration\n if (process.env.DUCKPOND_R2_ACCOUNT_ID) {\n config.r2 = {\n accountId: process.env.DUCKPOND_R2_ACCOUNT_ID,\n accessKeyId: process.env.DUCKPOND_R2_ACCESS_KEY_ID || \"\",\n secretAccessKey: process.env.DUCKPOND_R2_SECRET_ACCESS_KEY || \"\",\n bucket: process.env.DUCKPOND_R2_BUCKET || \"\",\n }\n }\n\n // S3 configuration\n if (process.env.DUCKPOND_S3_REGION) {\n config.s3 = {\n region: process.env.DUCKPOND_S3_REGION,\n accessKeyId: process.env.DUCKPOND_S3_ACCESS_KEY_ID || \"\",\n secretAccessKey: process.env.DUCKPOND_S3_SECRET_ACCESS_KEY || \"\",\n bucket: process.env.DUCKPOND_S3_BUCKET || \"\",\n }\n\n if (process.env.DUCKPOND_S3_ENDPOINT) {\n config.s3.endpoint = process.env.DUCKPOND_S3_ENDPOINT\n }\n }\n\n return config\n}\n\n/**\n * Main CLI program\n */\nconst program = new Command()\n\nprogram\n .name(\"duckpond-mcp-server\")\n .description(\"MCP server for multi-tenant DuckDB management with R2/S3 storage\")\n .version(\"0.1.0\")\n .option(\"-t, --transport <type>\", \"Transport mode: stdio or http\", \"stdio\")\n .option(\"-p, --port <port>\", \"HTTP port (when using http transport)\", \"3000\")\n .action(async (options) => {\n try {\n const config = getConfigFromEnv()\n\n log(`Starting DuckPond MCP Server with ${options.transport} transport`)\n log(\"Configuration:\", {\n memoryLimit: config.memoryLimit,\n threads: config.threads,\n maxActiveUsers: config.maxActiveUsers,\n strategy: config.strategy,\n tempDir: config.tempDir,\n cacheDir: config.cacheDir,\n cacheType: config.cacheType,\n hasR2: !!config.r2,\n hasS3: !!config.s3,\n })\n\n // Load OAuth configuration from environment variables (for HTTP transport)\n let oauthConfig: OAuthConfig | undefined\n if (process.env.DUCKPOND_OAUTH_ENABLED === \"true\") {\n const username = process.env.DUCKPOND_OAUTH_USERNAME\n const password = process.env.DUCKPOND_OAUTH_PASSWORD\n\n if (!username || !password) {\n console.error(\"❌ OAuth enabled but DUCKPOND_OAUTH_USERNAME and DUCKPOND_OAUTH_PASSWORD are required\")\n process.exit(1)\n }\n\n oauthConfig = {\n enabled: true,\n username,\n password,\n userId: process.env.DUCKPOND_OAUTH_USER_ID || username,\n email: process.env.DUCKPOND_OAUTH_EMAIL,\n issuer: process.env.DUCKPOND_OAUTH_ISSUER || `http://localhost:${parseInt(options.port) || 3000}`,\n resource: process.env.DUCKPOND_OAUTH_RESOURCE,\n }\n\n console.error(\"🔐 OAuth enabled with username/password authentication\")\n console.error(` Username: ${oauthConfig.username}`)\n console.error(` User ID: ${oauthConfig.userId}`)\n console.error(\" ✓ Login form will be shown at authorization endpoint\")\n }\n\n // Load Basic Auth configuration from environment variables (for HTTP transport)\n let basicAuthConfig: { username: string; password: string; userId?: string; email?: string } | undefined\n if (process.env.DUCKPOND_BASIC_AUTH_USERNAME && process.env.DUCKPOND_BASIC_AUTH_PASSWORD) {\n basicAuthConfig = {\n username: process.env.DUCKPOND_BASIC_AUTH_USERNAME,\n password: process.env.DUCKPOND_BASIC_AUTH_PASSWORD,\n userId: process.env.DUCKPOND_BASIC_AUTH_USER_ID,\n email: process.env.DUCKPOND_BASIC_AUTH_EMAIL,\n }\n\n console.error(\"🔐 Basic authentication enabled\")\n console.error(` Username: ${basicAuthConfig.username}`)\n console.error(` User ID: ${basicAuthConfig.userId || basicAuthConfig.username}`)\n }\n\n // Start unified FastMCP server with appropriate transport\n if (options.transport === \"stdio\" || options.transport === \"http\") {\n await startServer(\n {\n config,\n port: parseInt(options.port) || 3000,\n endpoint: \"/mcp\",\n oauth: oauthConfig,\n basicAuth: basicAuthConfig,\n },\n options.transport === \"stdio\" ? \"stdio\" : \"http\",\n )\n } else {\n log(`Unknown transport: ${options.transport}`)\n process.exit(1)\n }\n } catch (error) {\n log(\"Fatal error:\", error)\n console.error(\"Fatal error:\", error)\n process.exit(1)\n }\n })\n\nprogram.parse()\n"],"mappings":"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","names":["webcrypto","Command","webcrypto","log","loggers","getConfigFromEnv","config","program","Command","options","oauthConfig","username","password","basicAuthConfig","startServer","error"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Polyfill for Web Crypto API in Node.js environments\nimport { webcrypto } from \"crypto\"\n\nif (!globalThis.crypto) {\n globalThis.crypto = webcrypto as Crypto\n}\n\nimport { Command } from \"commander\"\nimport { createRequire } from \"module\"\n\nimport type { OAuthConfig } from \"./server\"\n\nconst require = createRequire(import.meta.url)\nconst packageJson = require(\"../package.json\") as { version: string }\nimport { startServer } from \"./server\"\nimport type { DuckPondServerConfig } from \"./server-core\"\nimport { loggers } from \"./utils/logger\"\n\nconst log = loggers.main\n\n/**\n * Parse environment variables into DuckPond configuration\n */\nfunction getConfigFromEnv(): DuckPondServerConfig {\n const config: DuckPondServerConfig = {\n memoryLimit: process.env.DUCKPOND_MEMORY_LIMIT || \"4GB\",\n threads: parseInt(process.env.DUCKPOND_THREADS || \"4\"),\n maxActiveUsers: parseInt(process.env.DUCKPOND_MAX_ACTIVE_USERS || \"10\"),\n evictionTimeout: parseInt(process.env.DUCKPOND_EVICTION_TIMEOUT || \"300000\"),\n cacheType: (process.env.DUCKPOND_CACHE_TYPE as \"disk\" | \"memory\" | \"noop\") || \"disk\",\n strategy: (process.env.DUCKPOND_STRATEGY as \"parquet\" | \"duckdb\" | \"hybrid\") || \"parquet\",\n tempDir: process.env.DUCKPOND_TEMP_DIR,\n cacheDir: process.env.DUCKPOND_CACHE_DIR,\n }\n\n // R2 configuration\n if (process.env.DUCKPOND_R2_ACCOUNT_ID) {\n config.r2 = {\n accountId: process.env.DUCKPOND_R2_ACCOUNT_ID,\n accessKeyId: process.env.DUCKPOND_R2_ACCESS_KEY_ID || \"\",\n secretAccessKey: process.env.DUCKPOND_R2_SECRET_ACCESS_KEY || \"\",\n bucket: process.env.DUCKPOND_R2_BUCKET || \"\",\n }\n }\n\n // S3 configuration\n if (process.env.DUCKPOND_S3_REGION) {\n config.s3 = {\n region: process.env.DUCKPOND_S3_REGION,\n accessKeyId: process.env.DUCKPOND_S3_ACCESS_KEY_ID || \"\",\n secretAccessKey: process.env.DUCKPOND_S3_SECRET_ACCESS_KEY || \"\",\n bucket: process.env.DUCKPOND_S3_BUCKET || \"\",\n }\n\n if (process.env.DUCKPOND_S3_ENDPOINT) {\n config.s3.endpoint = process.env.DUCKPOND_S3_ENDPOINT\n }\n }\n\n return config\n}\n\n/**\n * Main CLI program\n */\nconst program = new Command()\n\nprogram\n .name(\"duckpond-mcp-server\")\n .description(\"MCP server for multi-tenant DuckDB management with R2/S3 storage\")\n .version(packageJson.version)\n .option(\"-t, --transport <type>\", \"Transport mode: stdio or http\", \"stdio\")\n .option(\"-p, --port <port>\", \"HTTP port (when using http transport)\", \"3000\")\n .action(async (options) => {\n try {\n const config = getConfigFromEnv()\n\n log(`Starting DuckPond MCP Server with ${options.transport} transport`)\n log(\"Configuration:\", {\n memoryLimit: config.memoryLimit,\n threads: config.threads,\n maxActiveUsers: config.maxActiveUsers,\n strategy: config.strategy,\n tempDir: config.tempDir,\n cacheDir: config.cacheDir,\n cacheType: config.cacheType,\n hasR2: !!config.r2,\n hasS3: !!config.s3,\n })\n\n // Load OAuth configuration from environment variables (for HTTP transport)\n let oauthConfig: OAuthConfig | undefined\n if (process.env.DUCKPOND_OAUTH_ENABLED === \"true\") {\n const username = process.env.DUCKPOND_OAUTH_USERNAME\n const password = process.env.DUCKPOND_OAUTH_PASSWORD\n\n if (!username || !password) {\n console.error(\"❌ OAuth enabled but DUCKPOND_OAUTH_USERNAME and DUCKPOND_OAUTH_PASSWORD are required\")\n process.exit(1)\n }\n\n oauthConfig = {\n enabled: true,\n username,\n password,\n userId: process.env.DUCKPOND_OAUTH_USER_ID || username,\n email: process.env.DUCKPOND_OAUTH_EMAIL,\n issuer: process.env.DUCKPOND_OAUTH_ISSUER || `http://localhost:${parseInt(options.port) || 3000}`,\n resource: process.env.DUCKPOND_OAUTH_RESOURCE,\n }\n\n console.error(\"🔐 OAuth enabled with username/password authentication\")\n console.error(` Username: ${oauthConfig.username}`)\n console.error(` User ID: ${oauthConfig.userId}`)\n console.error(\" ✓ Login form will be shown at authorization endpoint\")\n }\n\n // Load Basic Auth configuration from environment variables (for HTTP transport)\n let basicAuthConfig: { username: string; password: string; userId?: string; email?: string } | undefined\n if (process.env.DUCKPOND_BASIC_AUTH_USERNAME && process.env.DUCKPOND_BASIC_AUTH_PASSWORD) {\n basicAuthConfig = {\n username: process.env.DUCKPOND_BASIC_AUTH_USERNAME,\n password: process.env.DUCKPOND_BASIC_AUTH_PASSWORD,\n userId: process.env.DUCKPOND_BASIC_AUTH_USER_ID,\n email: process.env.DUCKPOND_BASIC_AUTH_EMAIL,\n }\n\n console.error(\"🔐 Basic authentication enabled\")\n console.error(` Username: ${basicAuthConfig.username}`)\n console.error(` User ID: ${basicAuthConfig.userId || basicAuthConfig.username}`)\n }\n\n // Start unified FastMCP server with appropriate transport\n if (options.transport === \"stdio\" || options.transport === \"http\") {\n await startServer(\n {\n config,\n port: parseInt(options.port) || 3000,\n endpoint: \"/mcp\",\n oauth: oauthConfig,\n basicAuth: basicAuthConfig,\n },\n options.transport === \"stdio\" ? \"stdio\" : \"http\",\n )\n } else {\n log(`Unknown transport: ${options.transport}`)\n process.exit(1)\n }\n } catch (error) {\n log(\"Fatal error:\", error)\n console.error(\"Fatal error:\", error)\n process.exit(1)\n }\n })\n\nprogram.parse()\n"],"mappings":"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","names":["webcrypto","Command","createRequire","webcrypto","require","createRequire","packageJson","log","loggers","getConfigFromEnv","config","program","Command","options","oauthConfig","username","password","basicAuthConfig","startServer","error"]}
package/dist/server.js CHANGED
@@ -1,2 +1,2 @@
1
- import{a,b}from"./chunk-3FXN77J7.js";import"./chunk-SU42EK5H.js";import"./chunk-43NWQIU3.js";import"./chunk-A3S6D44B.js";export{a as createFastMCPServer,b as startServer};
1
+ import{a,b}from"./chunk-TAP5LWZ7.js";import"./chunk-SU42EK5H.js";import"./chunk-43NWQIU3.js";import"./chunk-A3S6D44B.js";export{a as createFastMCPServer,b as startServer};
2
2
  //# sourceMappingURL=server.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "duckpond-mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "MCP server for multi-tenant DuckDB management with R2/S3 storage",
5
5
  "keywords": [
6
6
  "mcp",
@@ -26,38 +26,38 @@
26
26
  "duckpond-mcp-server": "./dist/index.js"
27
27
  },
28
28
  "dependencies": {
29
- "@jordanburke/fastmcp": "3.21.0",
30
- "@types/express": "^5.0.3",
29
+ "@jordanburke/fastmcp": "3.26.700",
30
+ "@types/express": "^5.0.6",
31
31
  "@types/jsonwebtoken": "^9.0.10",
32
- "commander": "^12.1.0",
32
+ "commander": "^14.0.2",
33
33
  "debug": "^4.4.3",
34
- "duckpond": "^0.2.0",
35
- "express": "^5.1.0",
36
- "jsonwebtoken": "^9.0.2",
37
- "zod": "^3.24.1"
34
+ "duckpond": "^0.2.1",
35
+ "express": "^5.2.1",
36
+ "jsonwebtoken": "^9.0.3",
37
+ "zod": "^3.25.76"
38
38
  },
39
39
  "devDependencies": {
40
- "@eslint/eslintrc": "^3.3.1",
41
- "@eslint/js": "^9.38.0",
42
- "@types/node": "^22.18.11",
43
- "@typescript-eslint/eslint-plugin": "^8.46.1",
44
- "@typescript-eslint/parser": "^8.46.1",
45
- "@vitest/coverage-v8": "3.2.4",
46
- "@vitest/ui": "^3.2.4",
40
+ "@eslint/eslintrc": "^3.3.3",
41
+ "@eslint/js": "^9.39.2",
42
+ "@types/node": "~24.10.4",
43
+ "@typescript-eslint/eslint-plugin": "^8.51.0",
44
+ "@typescript-eslint/parser": "^8.51.0",
45
+ "@vitest/coverage-v8": "4.0.16",
46
+ "@vitest/ui": "^4.0.16",
47
47
  "cross-env": "^10.1.0",
48
- "eslint": "^9.38.0",
48
+ "eslint": "^9.39.2",
49
49
  "eslint-config-prettier": "^10.1.8",
50
50
  "eslint-plugin-import": "^2.32.0",
51
51
  "eslint-plugin-prettier": "^5.5.4",
52
52
  "eslint-plugin-simple-import-sort": "^12.1.1",
53
- "globals": "^16.4.0",
54
- "prettier": "^3.6.2",
55
- "rimraf": "^6.0.1",
53
+ "globals": "^16.5.0",
54
+ "prettier": "^3.7.4",
55
+ "rimraf": "^6.1.2",
56
56
  "ts-node": "^10.9.2",
57
- "tsup": "^8.5.0",
58
- "tsx": "^4.19.2",
57
+ "tsup": "^8.5.1",
58
+ "tsx": "^4.21.0",
59
59
  "typescript": "^5.9.3",
60
- "vitest": "^3.2.4"
60
+ "vitest": "^4.0.16"
61
61
  },
62
62
  "main": "./dist/index.js",
63
63
  "module": "./dist/index.js",
@@ -1,48 +0,0 @@
1
- import{a as R}from"./chunk-SU42EK5H.js";import{a as C,b as O,c as T,d as A,e as $,f as I}from"./chunk-43NWQIU3.js";import{b as x}from"./chunk-A3S6D44B.js";import{webcrypto as U}from"crypto";import{FastMCP as P}from"@jordanburke/fastmcp";import{createHash as z,randomBytes as f}from"crypto";import*as y from"jsonwebtoken";import{URL as D}from"url";globalThis.crypto||(globalThis.crypto=U);var u=x.fastmcp,v=process.env.DUCKPOND_JWT_SECRET||f(32).toString("hex"),S=process.env.DUCKPOND_JWT_EXPIRES_IN?parseInt(process.env.DUCKPOND_JWT_EXPIRES_IN,10):365*24*60*60,_=new Map,m=new Map;function j(r){u("\u{1F680} Initializing FastMCP server...");let i=new R(r.config),h={name:"duckpond",version:"0.1.0",health:{enabled:!0,path:"/health",status:200,message:JSON.stringify({status:"healthy",service:"duckpond-mcp-server",version:"0.1.0",timestamp:new Date().toISOString()})}},s=r.oauth?.enabled||r.basicAuth?new P({...h,oauth:{enabled:!0,authorizationServer:{issuer:r.oauth?.issuer||`http://localhost:${r.port||3e3}`,authorizationEndpoint:`${r.oauth?.issuer||`http://localhost:${r.port||3e3}`}/oauth/authorize`,tokenEndpoint:`${r.oauth?.issuer||`http://localhost:${r.port||3e3}`}/oauth/token`,jwksUri:`${r.oauth?.issuer||`http://localhost:${r.port||3e3}`}/oauth/jwks`,registrationEndpoint:`${r.oauth?.issuer||`http://localhost:${r.port||3e3}`}/oauth/register`,responseTypesSupported:["code"],grantTypesSupported:["authorization_code"],tokenEndpointAuthMethodsSupported:["client_secret_post","client_secret_basic"],codeChallengeMethodsSupported:["S256","plain"]},protectedResource:{resource:process.env.DUCKPOND_OAUTH_RESOURCE||r.oauth?.resource||`${r.oauth?.issuer||`http://localhost:${r.port||3e3}`}/mcp`,authorizationServers:[r.oauth?.issuer||`http://localhost:${r.port||3e3}`]}},authenticate:t=>{let e=t.headers?.authorization,a=r.oauth?.issuer||`http://localhost:${r.port||3e3}`;if(!e)throw r.oauth?.enabled?new Response(JSON.stringify({error:"unauthorized",error_description:"Authorization required. Please authenticate via OAuth."}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":`Bearer realm="MCP", authorization_uri="${a}/oauth/authorize", resource="${a}/.well-known/oauth-protected-resource"`}}):new Response(JSON.stringify({error:"unauthorized",error_description:"Authorization required."}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json"}});if(r.basicAuth&&e.startsWith("Basic ")){let c=Buffer.from(e.slice(6),"base64").toString("utf-8"),[n,d]=c.split(":");if(n===r.basicAuth.username&&d===r.basicAuth.password)return Promise.resolve({userId:r.basicAuth.userId||n,email:r.basicAuth.email||`${n}@example.com`,scope:"read write"});throw new Response(JSON.stringify({error:"unauthorized",error_description:"Invalid username or password"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Basic realm="MCP"'}})}if(r.oauth?.enabled&&e.startsWith("Bearer ")){let c=e.slice(7);try{let n=y.verify(c,v);if(!n.sub||!n.iat||!n.exp)throw new Response(JSON.stringify({error:"invalid_token",error_description:"Invalid token structure"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Invalid token structure"'}});let d=r.oauth?.resource||`${a}/mcp`;if(n.aud&&n.aud!==d)throw new Response(JSON.stringify({error:"invalid_token",error_description:"Token audience mismatch"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Token audience mismatch"'}});return Promise.resolve({userId:n.sub,email:n.email||"",scope:n.scope||"read write"})}catch(n){throw n instanceof Response?n:new Response(JSON.stringify({error:"invalid_token",error_description:"Invalid or expired token"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP", error="invalid_token", error_description="Invalid or expired token"'}})}}throw new Response(JSON.stringify({error:"unauthorized",error_description:"Invalid authorization header format"}),{status:401,statusText:"Unauthorized",headers:{"Content-Type":"application/json","WWW-Authenticate":`Bearer realm="MCP", authorization_uri="${a}/oauth/authorize", resource="${a}/.well-known/oauth-protected-resource"`}})}}):new P(h);return s.addTool({name:"query",description:"Execute a SQL query for a specific user and return results",parameters:C,execute:async t=>{try{let e=await i.query(t.userId,t.sql);return e.success?JSON.stringify({rows:e.data,rowCount:e.data.length,executionTime:e.executionTime},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in query tool:",e);let a=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:a},null,2)}`}}}),s.addTool({name:"execute",description:"Execute SQL statement (DDL/DML) for a specific user without returning results",parameters:O,execute:async t=>{try{let e=await i.execute(t.userId,t.sql);return e.success?JSON.stringify({success:!0,message:"Statement executed successfully",executionTime:e.executionTime},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in execute tool:",e);let a=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:a},null,2)}`}}}),s.addTool({name:"getUserStats",description:"Get statistics about a user's database (memory usage, query count, etc.)",parameters:T,execute:async t=>{try{let e=await i.getUserStats(t.userId);return e.success?JSON.stringify({...e.data,lastAccess:e.data.lastAccess.toISOString()},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in getUserStats tool:",e);let a=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:a},null,2)}`}}}),s.addTool({name:"isAttached",description:"Check if a user's database is currently cached in memory",parameters:A,execute:async t=>{try{let e=i.isAttached(t.userId);return e.success?JSON.stringify({attached:e.data,userId:t.userId},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in isAttached tool:",e);let a=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:a},null,2)}`}}}),s.addTool({name:"detachUser",description:"Manually detach a user's database from the cache to free resources",parameters:$,execute:async t=>{try{let e=await i.detachUser(t.userId);return e.success?JSON.stringify({success:!0,message:`User ${t.userId} detached successfully`},null,2):`ERROR: ${e.error.message}`}catch(e){u("Error in detachUser tool:",e);let a=e instanceof Error?e.message:String(e);return`ERROR: ${JSON.stringify({error:a},null,2)}`}}}),s.addTool({name:"listUsers",description:"List all currently cached users and cache statistics",parameters:I,execute:async()=>{try{let t=i.listUsers();return t.success?JSON.stringify(t.data,null,2):`ERROR: ${t.error.message}`}catch(t){u("Error in listUsers tool:",t);let e=t instanceof Error?t.message:String(t);return`ERROR: ${JSON.stringify({error:e},null,2)}`}}}),r.oauth?.enabled&&q(s,r),s.getApp().get("/",t=>{let e=r.oauth?.issuer||`http://localhost:${r.port||3e3}`,a={name:"DuckPond MCP Server",version:"0.1.0",description:"Model Context Protocol server for multi-tenant DuckDB with R2/S3 storage",service:"duckpond-mcp-server",capabilities:{tools:["query","execute","getUserStats","isAttached","detachUser","listUsers"],transports:["stdio","http"],authentication:{oauth:r.oauth?.enabled||!1,basicAuth:!!r.basicAuth}},endpoints:{mcp:`${e}${r.endpoint||"/mcp"}`,health:`${e}/health`,...r.oauth?.enabled&&{oauth:{authorization:`${e}/oauth/authorize`,token:`${e}/oauth/token`,jwks:`${e}/oauth/jwks`,register:`${e}/oauth/register`}}},timestamp:new Date().toISOString()};return t.json(a)}),u("\u2713 FastMCP server created"),{server:s,duckpond:i}}function q(r,i){let h=r.getApp();setInterval(()=>{let s=Date.now();for(let[o,t]of _.entries())s-t.createdAt>6e5&&_.delete(o);for(let[o,t]of m.entries())s-t.createdAt>2592e6&&m.delete(o)},6e4),h.get("/oauth/authorize",s=>{let o=s.req.query(),t=o.response_type,e=o.redirect_uri,a=o.state,c=o.code_challenge,n=o.code_challenge_method,d=o.client_id;if(t!=="code")return s.json({error:"unsupported_response_type",error_description:"Only 'code' response type is supported"},400);if(!e)return s.json({error:"invalid_request",error_description:"redirect_uri is required"},400);if(c&&(!n||!["S256","plain"].includes(n)))return s.json({error:"invalid_request",error_description:"Invalid code_challenge_method. Only 'S256' and 'plain' are supported"},400);let l=`
2
- <!DOCTYPE html>
3
- <html>
4
- <head>
5
- <title>OAuth Login - DuckPond MCP Server</title>
6
- <style>
7
- body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; padding: 20px; }
8
- .form-group { margin-bottom: 15px; }
9
- label { display: block; margin-bottom: 5px; font-weight: bold; }
10
- input[type="text"], input[type="password"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
11
- button { width: 100%; padding: 12px; background: #007cba; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
12
- button:hover { background: #005a87; }
13
- .app-info { background: #f5f5f5; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
14
- </style>
15
- </head>
16
- <body>
17
- <div class="app-info">
18
- <h3>\u{1F510} OAuth Authorization</h3>
19
- <p><strong>Application:</strong> ${d||"MCP Client"}</p>
20
- <p><strong>Permissions:</strong> Read and write access to DuckDB databases</p>
21
- </div>
22
-
23
- <form method="POST" action="/oauth/authorize">
24
- <input type="hidden" name="response_type" value="${t}">
25
- <input type="hidden" name="redirect_uri" value="${e}">
26
- <input type="hidden" name="state" value="${a||""}">
27
- <input type="hidden" name="code_challenge" value="${c||""}">
28
- <input type="hidden" name="code_challenge_method" value="${n||""}">
29
- <input type="hidden" name="client_id" value="${d||""}">
30
-
31
- <div class="form-group">
32
- <label for="username">Username:</label>
33
- <input type="text" id="username" name="username" required>
34
- </div>
35
-
36
- <div class="form-group">
37
- <label for="password">Password:</label>
38
- <input type="password" id="password" name="password" required>
39
- </div>
40
-
41
- <button type="submit">Authorize Application</button>
42
- </form>
43
- </body>
44
- </html>`;return s.html(l)}),h.post("/oauth/authorize",async s=>{try{let o=await s.req.text(),t=new URLSearchParams(o),e=t.get("username"),a=t.get("password"),c=t.get("redirect_uri"),n=t.get("state"),d=t.get("code_challenge"),l=t.get("code_challenge_method");if(e!==i.oauth?.username||a!==i.oauth?.password)return s.html(`
45
- <!DOCTYPE html>
46
- <html><head><title>Login Failed</title><style>body{font-family:Arial;max-width:400px;margin:100px auto;padding:20px;}.error{color:red;background:#fee;padding:10px;border-radius:4px;margin-bottom:15px;}</style></head>
47
- <body><div class="error">\u274C Invalid username or password</div><a href="javascript:history.back()">\u2190 Try Again</a></body></html>`,401);let w=f(16).toString("hex");_.set(w,{createdAt:Date.now(),redirectUri:c||"",codeChallenge:d||void 0,codeChallengeMethod:l||void 0,userId:i.oauth?.userId||e||"oauth-user"});let g=new D(c||"");return g.searchParams.set("code",w),n&&g.searchParams.set("state",n),s.redirect(g.toString(),302)}catch{return s.json({error:"invalid_request",error_description:"Failed to process authorization request"},400)}}),h.post("/oauth/token",async s=>{let o=await s.req.text(),t=new URLSearchParams(o),e=t.get("grant_type"),a=t.get("code"),c=t.get("redirect_uri"),n=t.get("code_verifier"),d=t.get("refresh_token");if(e==="refresh_token"){if(!d)return s.json({error:"invalid_request",error_description:"refresh_token is required for refresh_token grant type"},400);let p=m.get(d);if(!p)return s.json({error:"invalid_grant",error_description:"Invalid or expired refresh token"},400);m.delete(d);let E={sub:p.userId,email:p.email||"",scope:"read write",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+S,iss:i.oauth?.issuer||`http://localhost:${i.port||3e3}`,aud:i.oauth?.resource||`${i.oauth?.issuer||`http://localhost:${i.port||3e3}`}/mcp`},k=f(32).toString("hex");m.set(k,{createdAt:Date.now(),userId:p.userId,email:p.email});let M=y.sign(E,v);return s.json({access_token:M,token_type:"Bearer",expires_in:S,scope:"read write",refresh_token:k})}if(e!=="authorization_code")return s.json({error:"unsupported_grant_type",error_description:"Only 'authorization_code' and 'refresh_token' grant types are supported"},400);let l=_.get(a||"");if(!l)return s.json({error:"invalid_grant",error_description:"Invalid or expired authorization code"},400);if(l.redirectUri&&l.redirectUri!==c)return s.json({error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(l.codeChallenge){if(!n)return s.json({error:"invalid_grant",error_description:"code_verifier is required when code_challenge was used"},400);let p;if(l.codeChallengeMethod==="S256"?p=z("sha256").update(n).digest().toString("base64url"):p=n,p!==l.codeChallenge)return s.json({error:"invalid_grant",error_description:"Invalid code_verifier"},400)}_.delete(a);let w={sub:l.userId,email:i.oauth?.email||"",scope:"read write",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+S,iss:i.oauth?.issuer||`http://localhost:${i.port||3e3}`,aud:i.oauth?.resource||`${i.oauth?.issuer||`http://localhost:${i.port||3e3}`}/mcp`},g=f(32).toString("hex");m.set(g,{createdAt:Date.now(),userId:l.userId,email:i.oauth?.email});let b=y.sign(w,v);return s.json({access_token:b,token_type:"Bearer",expires_in:S,scope:"read write",refresh_token:g})}),h.get("/oauth/jwks",s=>s.json({keys:[{kty:"oct",use:"sig",kid:"duckpond-hmac-key",alg:"HS256"}]})),h.post("/oauth/register",async s=>{try{let o={};try{let c=await s.req.text();if(c&&c!=="[object Object]")try{o=JSON.parse(c)}catch{o=Object.fromEntries(new URLSearchParams(c))}}catch(c){u("Error parsing request body:",c)}let t=`client-${f(8).toString("hex")}`,e=f(16).toString("hex"),a={client_id:t,client_secret:e,client_id_issued_at:Math.floor(Date.now()/1e3),client_secret_expires_at:0,grant_types:o.grant_types||["authorization_code"],response_types:o.response_types||["code"],redirect_uris:o.redirect_uris||[],token_endpoint_auth_method:o.token_endpoint_auth_method||"client_secret_post"};return o.client_name&&(a.client_name=o.client_name),o.scope&&(a.scope=o.scope),s.json(a,201)}catch(o){return s.json({error:"invalid_client_metadata",error_description:"Invalid client registration request: "+(o instanceof Error?o.message:String(o))},400)}}),u("\u2713 OAuth flow endpoints added")}async function H(r,i){let{server:h,duckpond:s}=j(r),o=await s.init();if(!o.success)throw new Error(`Failed to initialize DuckPond: ${o.error.message}`);u("DuckPond initialized successfully"),i==="stdio"?(await h.start({transportType:"stdio"}),u("\u2713 FastMCP server running with stdio transport")):(await h.start({transportType:"httpStream",httpStream:{port:r.port||3e3,endpoint:r.endpoint||"/mcp"}}),u(`\u2713 FastMCP server running on http://0.0.0.0:${r.port||3e3}${r.endpoint||"/mcp"}`),u("\u{1F50C} Connect with StreamableHTTPClientTransport")),process.on("SIGINT",async()=>{u("Received SIGINT, closing server..."),await s.close(),process.exit(0)}),process.on("SIGTERM",async()=>{u("Received SIGTERM, closing server..."),await s.close(),process.exit(0)})}export{j as a,H as b};
48
- //# sourceMappingURL=chunk-3FXN77J7.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["// Polyfill for Web Crypto API in Node.js environments\nimport { webcrypto } from \"crypto\"\n\nif (!globalThis.crypto) {\n globalThis.crypto = webcrypto as Crypto\n}\n\nimport { FastMCP } from \"@jordanburke/fastmcp\"\nimport { createHash, randomBytes } from \"crypto\"\nimport * as jwt from \"jsonwebtoken\"\nimport { URL } from \"url\"\nimport { z } from \"zod\"\n\nimport { DuckPondServer, type DuckPondServerConfig } from \"./server-core\"\nimport {\n detachUserSchema,\n executeSchema,\n getUserStatsSchema,\n isAttachedSchema,\n listUsersSchema,\n querySchema,\n} from \"./tools\"\nimport { loggers } from \"./utils/logger\"\n\nconst log = loggers.fastmcp\n\nexport type OAuthConfig = {\n enabled: boolean\n username: string\n password: string\n userId: string\n email?: string\n issuer?: string\n resource?: string\n}\n\nexport type FastMCPServerOptions = {\n config: DuckPondServerConfig\n port?: number\n endpoint?: string\n oauth?: OAuthConfig\n basicAuth?: {\n username: string\n password: string\n userId?: string\n email?: string\n }\n}\n\n// JWT secret for token signing/validation\nconst JWT_SECRET = process.env.DUCKPOND_JWT_SECRET || randomBytes(32).toString(\"hex\")\n\n// JWT token expiration configuration (default: 1 year)\nconst JWT_EXPIRES_IN = process.env.DUCKPOND_JWT_EXPIRES_IN\n ? parseInt(process.env.DUCKPOND_JWT_EXPIRES_IN, 10)\n : 365 * 24 * 60 * 60 // 1 year in seconds\n\n// In-memory stores for OAuth flow\nconst authorizationCodes = new Map<\n string,\n {\n createdAt: number\n redirectUri?: string\n codeChallenge?: string\n codeChallengeMethod?: string\n userId: string\n }\n>()\n\nconst refreshTokens = new Map<\n string,\n {\n createdAt: number\n userId: string\n email?: string\n }\n>()\n\n// AuthSession type for FastMCP authentication\ntype AuthSession = {\n userId: string\n email: string\n scope: string\n [key: string]: unknown // Allow additional properties\n}\n\ntype OAuthClientRegistrationRequest = {\n grant_types?: string[]\n response_types?: string[]\n redirect_uris?: string[]\n token_endpoint_auth_method?: string\n client_name?: string\n scope?: string\n}\n\ntype OAuthClientRegistrationResponse = {\n client_id: string\n client_secret: string\n client_id_issued_at: number\n client_secret_expires_at: number\n grant_types: string[]\n response_types: string[]\n redirect_uris: string[]\n token_endpoint_auth_method: string\n client_name?: string\n scope?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): {\n server: FastMCP\n duckpond: DuckPondServer\n} {\n log(\"🚀 Initializing FastMCP server...\")\n\n // Create DuckPond server instance\n const duckpond = new DuckPondServer(options.config)\n\n // Build server configuration\n const baseConfig = {\n name: \"duckpond\",\n version: \"0.1.0\" as const,\n health: {\n enabled: true,\n path: \"/health\",\n status: 200,\n message: JSON.stringify({\n status: \"healthy\",\n service: \"duckpond-mcp-server\",\n version: \"0.1.0\",\n timestamp: new Date().toISOString(),\n }),\n },\n }\n\n // Create server with authentication (OAuth, Basic Auth, or none)\n const server =\n options.oauth?.enabled || options.basicAuth\n ? new FastMCP<AuthSession>({\n ...baseConfig,\n oauth: {\n enabled: true,\n authorizationServer: {\n issuer: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n authorizationEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/authorize`,\n tokenEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/token`,\n jwksUri: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/jwks`,\n registrationEndpoint: `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/oauth/register`,\n responseTypesSupported: [\"code\"],\n grantTypesSupported: [\"authorization_code\"],\n tokenEndpointAuthMethodsSupported: [\"client_secret_post\", \"client_secret_basic\"],\n codeChallengeMethodsSupported: [\"S256\", \"plain\"],\n },\n protectedResource: {\n resource:\n process.env.DUCKPOND_OAUTH_RESOURCE ||\n options.oauth?.resource ||\n `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n authorizationServers: [options.oauth?.issuer || `http://localhost:${options.port || 3000}`],\n },\n },\n authenticate: (request) => {\n const authHeader = request.headers?.authorization\n const baseUrl = options.oauth?.issuer || `http://localhost:${options.port || 3000}`\n\n // For OAuth-enabled servers, require authentication\n if (!authHeader) {\n if (options.oauth?.enabled) {\n // Return HTTP 401 with WWW-Authenticate header for proper OAuth discovery\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Authorization required. Please authenticate via OAuth.\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", authorization_uri=\"${baseUrl}/oauth/authorize\", resource=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n },\n },\n )\n }\n\n // For non-OAuth servers, also require some form of auth\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Authorization required.\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n },\n )\n }\n\n // Handle Basic Authentication\n if (options.basicAuth && authHeader.startsWith(\"Basic \")) {\n const credentials = Buffer.from(authHeader.slice(6), \"base64\").toString(\"utf-8\")\n const [username, password] = credentials.split(\":\")\n\n if (username === options.basicAuth.username && password === options.basicAuth.password) {\n return Promise.resolve({\n userId: options.basicAuth.userId || username,\n email: options.basicAuth.email || `${username}@example.com`,\n scope: \"read write\",\n })\n } else {\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Invalid username or password\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Basic realm=\"MCP\"`,\n },\n },\n )\n }\n }\n\n // Handle Bearer Token (OAuth) - Validate JWT\n if (options.oauth?.enabled && authHeader.startsWith(\"Bearer \")) {\n const token = authHeader.slice(7) // Remove 'Bearer ' prefix\n\n try {\n // Verify JWT token\n const decoded = jwt.verify(token, JWT_SECRET) as jwt.JwtPayload\n\n if (!decoded.sub || !decoded.iat || !decoded.exp) {\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Invalid token structure\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Invalid token structure\"`,\n },\n },\n )\n }\n\n // Validate audience\n const expectedAudience = options.oauth?.resource || `${baseUrl}/mcp`\n if (decoded.aud && decoded.aud !== expectedAudience) {\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Token audience mismatch\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Token audience mismatch\"`,\n },\n },\n )\n }\n\n // Return user info from JWT claims\n return Promise.resolve({\n userId: decoded.sub,\n email: (decoded.email as string) || \"\",\n scope: (decoded.scope as string) || \"read write\",\n })\n } catch (error) {\n if (error instanceof Response) {\n throw error // Re-throw our custom Response errors\n }\n\n throw new Response(\n JSON.stringify({\n error: \"invalid_token\",\n error_description: \"Invalid or expired token\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", error=\"invalid_token\", error_description=\"Invalid or expired token\"`,\n },\n },\n )\n }\n }\n\n throw new Response(\n JSON.stringify({\n error: \"unauthorized\",\n error_description: \"Invalid authorization header format\",\n }),\n {\n status: 401,\n statusText: \"Unauthorized\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"WWW-Authenticate\": `Bearer realm=\"MCP\", authorization_uri=\"${baseUrl}/oauth/authorize\", resource=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n },\n },\n )\n },\n })\n : new FastMCP(baseConfig)\n\n // Add query tool\n server.addTool({\n name: \"query\",\n description: \"Execute a SQL query for a specific user and return results\",\n parameters: querySchema,\n execute: async (args) => {\n try {\n const result = await duckpond.query(args.userId, args.sql)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n rows: result.data,\n rowCount: result.data.length,\n executionTime: result.executionTime,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in query tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add execute tool\n server.addTool({\n name: \"execute\",\n description: \"Execute SQL statement (DDL/DML) for a specific user without returning results\",\n parameters: executeSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.execute(args.userId, args.sql)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n success: true,\n message: \"Statement executed successfully\",\n executionTime: result.executionTime,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in execute tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add getUserStats tool\n server.addTool({\n name: \"getUserStats\",\n description: \"Get statistics about a user's database (memory usage, query count, etc.)\",\n parameters: getUserStatsSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.getUserStats(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n ...result.data,\n lastAccess: result.data.lastAccess.toISOString(),\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in getUserStats tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add isAttached tool\n server.addTool({\n name: \"isAttached\",\n description: \"Check if a user's database is currently cached in memory\",\n parameters: isAttachedSchema,\n execute: async (args) => {\n try {\n const result = duckpond.isAttached(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n attached: result.data,\n userId: args.userId,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in isAttached tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add detachUser tool\n server.addTool({\n name: \"detachUser\",\n description: \"Manually detach a user's database from the cache to free resources\",\n parameters: detachUserSchema,\n execute: async (args) => {\n try {\n const result = await duckpond.detachUser(args.userId)\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(\n {\n success: true,\n message: `User ${args.userId} detached successfully`,\n },\n null,\n 2,\n )\n } catch (error) {\n log(\"Error in detachUser tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add listUsers tool\n server.addTool({\n name: \"listUsers\",\n description: \"List all currently cached users and cache statistics\",\n parameters: listUsersSchema,\n execute: async () => {\n try {\n const result = duckpond.listUsers()\n\n if (!result.success) {\n return `ERROR: ${result.error.message}`\n }\n\n return JSON.stringify(result.data, null, 2)\n } catch (error) {\n log(\"Error in listUsers tool:\", error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n return `ERROR: ${JSON.stringify({ error: errorMessage }, null, 2)}`\n }\n },\n })\n\n // Add OAuth flow endpoints if OAuth is enabled\n if (options.oauth?.enabled) {\n setupOAuthEndpoints(server, options)\n }\n\n // Add root info endpoint using Hono\n const app = server.getApp()\n app.get(\"/\", (c) => {\n const baseUrl = options.oauth?.issuer || `http://localhost:${options.port || 3000}`\n\n const serverInfo = {\n name: \"DuckPond MCP Server\",\n version: \"0.1.0\",\n description: \"Model Context Protocol server for multi-tenant DuckDB with R2/S3 storage\",\n service: \"duckpond-mcp-server\",\n capabilities: {\n tools: [\"query\", \"execute\", \"getUserStats\", \"isAttached\", \"detachUser\", \"listUsers\"],\n transports: [\"stdio\", \"http\"],\n authentication: {\n oauth: options.oauth?.enabled || false,\n basicAuth: !!options.basicAuth,\n },\n },\n endpoints: {\n mcp: `${baseUrl}${options.endpoint || \"/mcp\"}`,\n health: `${baseUrl}/health`,\n ...(options.oauth?.enabled && {\n oauth: {\n authorization: `${baseUrl}/oauth/authorize`,\n token: `${baseUrl}/oauth/token`,\n jwks: `${baseUrl}/oauth/jwks`,\n register: `${baseUrl}/oauth/register`,\n },\n }),\n },\n timestamp: new Date().toISOString(),\n }\n\n return c.json(serverInfo)\n })\n\n log(\"✓ FastMCP server created\")\n\n return { server, duckpond }\n}\n\nfunction setupOAuthEndpoints(server: FastMCP, options: FastMCPServerOptions): void {\n const app = server.getApp()\n\n // Clean up old codes and refresh tokens every minute\n setInterval(() => {\n const now = Date.now()\n // Clean authorization codes (10 minutes)\n for (const [code, data] of authorizationCodes.entries()) {\n if (now - data.createdAt > 600000) {\n authorizationCodes.delete(code)\n }\n }\n // Clean refresh tokens (30 days)\n for (const [token, data] of refreshTokens.entries()) {\n if (now - data.createdAt > 2592000000) {\n refreshTokens.delete(token)\n }\n }\n }, 60000)\n\n // OAuth Authorization Endpoint - Login Form\n app.get(\"/oauth/authorize\", (c) => {\n const params = c.req.query()\n const responseType = params.response_type\n const redirectUri = params.redirect_uri\n const state = params.state\n const codeChallenge = params.code_challenge\n const codeChallengeMethod = params.code_challenge_method\n const clientId = params.client_id\n\n if (responseType !== \"code\") {\n return c.json(\n {\n error: \"unsupported_response_type\",\n error_description: \"Only 'code' response type is supported\",\n },\n 400,\n )\n }\n\n if (!redirectUri) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"redirect_uri is required\",\n },\n 400,\n )\n }\n\n // Validate PKCE parameters if present\n if (codeChallenge) {\n if (!codeChallengeMethod || ![\"S256\", \"plain\"].includes(codeChallengeMethod)) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"Invalid code_challenge_method. Only 'S256' and 'plain' are supported\",\n },\n 400,\n )\n }\n }\n\n // Serve login form\n const loginForm = `\n<!DOCTYPE html>\n<html>\n<head>\n <title>OAuth Login - DuckPond MCP Server</title>\n <style>\n body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; padding: 20px; }\n .form-group { margin-bottom: 15px; }\n label { display: block; margin-bottom: 5px; font-weight: bold; }\n input[type=\"text\"], input[type=\"password\"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }\n button { width: 100%; padding: 12px; background: #007cba; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }\n button:hover { background: #005a87; }\n .app-info { background: #f5f5f5; padding: 15px; border-radius: 4px; margin-bottom: 20px; }\n </style>\n</head>\n<body>\n <div class=\"app-info\">\n <h3>🔐 OAuth Authorization</h3>\n <p><strong>Application:</strong> ${clientId || \"MCP Client\"}</p>\n <p><strong>Permissions:</strong> Read and write access to DuckDB databases</p>\n </div>\n\n <form method=\"POST\" action=\"/oauth/authorize\">\n <input type=\"hidden\" name=\"response_type\" value=\"${responseType}\">\n <input type=\"hidden\" name=\"redirect_uri\" value=\"${redirectUri}\">\n <input type=\"hidden\" name=\"state\" value=\"${state || \"\"}\">\n <input type=\"hidden\" name=\"code_challenge\" value=\"${codeChallenge || \"\"}\">\n <input type=\"hidden\" name=\"code_challenge_method\" value=\"${codeChallengeMethod || \"\"}\">\n <input type=\"hidden\" name=\"client_id\" value=\"${clientId || \"\"}\">\n\n <div class=\"form-group\">\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\" required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\" required>\n </div>\n\n <button type=\"submit\">Authorize Application</button>\n </form>\n</body>\n</html>`\n\n return c.html(loginForm)\n })\n\n // OAuth Authorization POST - Process Login\n app.post(\"/oauth/authorize\", async (c) => {\n try {\n const body = await c.req.text()\n const params = new URLSearchParams(body)\n\n const username = params.get(\"username\")\n const password = params.get(\"password\")\n const redirectUri = params.get(\"redirect_uri\")\n const state = params.get(\"state\")\n const codeChallenge = params.get(\"code_challenge\")\n const codeChallengeMethod = params.get(\"code_challenge_method\")\n\n // Validate credentials\n if (username !== options.oauth?.username || password !== options.oauth?.password) {\n const errorForm = `\n<!DOCTYPE html>\n<html><head><title>Login Failed</title><style>body{font-family:Arial;max-width:400px;margin:100px auto;padding:20px;}.error{color:red;background:#fee;padding:10px;border-radius:4px;margin-bottom:15px;}</style></head>\n<body><div class=\"error\">❌ Invalid username or password</div><a href=\"javascript:history.back()\">← Try Again</a></body></html>`\n return c.html(errorForm, 401)\n }\n\n // Generate authorization code\n const code = randomBytes(16).toString(\"hex\")\n authorizationCodes.set(code, {\n createdAt: Date.now(),\n redirectUri: redirectUri || \"\",\n codeChallenge: codeChallenge || undefined,\n codeChallengeMethod: codeChallengeMethod || undefined,\n userId: options.oauth?.userId || username || \"oauth-user\",\n })\n\n // Redirect with authorization code\n const redirectUrl = new URL(redirectUri || \"\")\n redirectUrl.searchParams.set(\"code\", code)\n if (state) {\n redirectUrl.searchParams.set(\"state\", state)\n }\n\n return c.redirect(redirectUrl.toString(), 302)\n } catch {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"Failed to process authorization request\",\n },\n 400,\n )\n }\n })\n\n // OAuth Token Endpoint\n app.post(\"/oauth/token\", async (c) => {\n const body = await c.req.text()\n const params = new URLSearchParams(body)\n const grantType = params.get(\"grant_type\")\n const code = params.get(\"code\")\n const redirectUri = params.get(\"redirect_uri\")\n const codeVerifier = params.get(\"code_verifier\")\n const refreshTokenParam = params.get(\"refresh_token\")\n\n if (grantType === \"refresh_token\") {\n // Handle refresh token flow\n if (!refreshTokenParam) {\n return c.json(\n {\n error: \"invalid_request\",\n error_description: \"refresh_token is required for refresh_token grant type\",\n },\n 400,\n )\n }\n\n const tokenData = refreshTokens.get(refreshTokenParam)\n if (!tokenData) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid or expired refresh token\",\n },\n 400,\n )\n }\n\n // Remove old refresh token (token rotation)\n refreshTokens.delete(refreshTokenParam)\n\n // Generate new JWT access token\n const accessTokenPayload = {\n sub: tokenData.userId,\n email: tokenData.email || \"\",\n scope: \"read write\",\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + JWT_EXPIRES_IN,\n iss: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n aud: options.oauth?.resource || `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n }\n\n // Generate new refresh token\n const newRefreshToken = randomBytes(32).toString(\"hex\")\n refreshTokens.set(newRefreshToken, {\n createdAt: Date.now(),\n userId: tokenData.userId,\n email: tokenData.email,\n })\n\n const accessToken = jwt.sign(accessTokenPayload, JWT_SECRET)\n\n return c.json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: JWT_EXPIRES_IN,\n scope: \"read write\",\n refresh_token: newRefreshToken,\n })\n }\n\n if (grantType !== \"authorization_code\") {\n return c.json(\n {\n error: \"unsupported_grant_type\",\n error_description: \"Only 'authorization_code' and 'refresh_token' grant types are supported\",\n },\n 400,\n )\n }\n\n const codeData = authorizationCodes.get(code || \"\")\n if (!codeData) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid or expired authorization code\",\n },\n 400,\n )\n }\n\n // Validate redirect_uri matches\n if (codeData.redirectUri && codeData.redirectUri !== redirectUri) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"redirect_uri mismatch\",\n },\n 400,\n )\n }\n\n // Validate PKCE if code_challenge was provided\n if (codeData.codeChallenge) {\n if (!codeVerifier) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"code_verifier is required when code_challenge was used\",\n },\n 400,\n )\n }\n\n let expectedChallenge: string\n if (codeData.codeChallengeMethod === \"S256\") {\n expectedChallenge = createHash(\"sha256\").update(codeVerifier).digest().toString(\"base64url\")\n } else {\n // 'plain' method\n expectedChallenge = codeVerifier\n }\n\n if (expectedChallenge !== codeData.codeChallenge) {\n return c.json(\n {\n error: \"invalid_grant\",\n error_description: \"Invalid code_verifier\",\n },\n 400,\n )\n }\n }\n\n // Remove used code\n authorizationCodes.delete(code!)\n\n // Generate JWT access token\n const accessTokenPayload = {\n sub: codeData.userId,\n email: options.oauth?.email || \"\",\n scope: \"read write\",\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + JWT_EXPIRES_IN,\n iss: options.oauth?.issuer || `http://localhost:${options.port || 3000}`,\n aud: options.oauth?.resource || `${options.oauth?.issuer || `http://localhost:${options.port || 3000}`}/mcp`,\n }\n\n // Generate refresh token\n const refreshToken = randomBytes(32).toString(\"hex\")\n refreshTokens.set(refreshToken, {\n createdAt: Date.now(),\n userId: codeData.userId,\n email: options.oauth?.email,\n })\n\n const accessToken = jwt.sign(accessTokenPayload, JWT_SECRET)\n\n return c.json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: JWT_EXPIRES_IN,\n scope: \"read write\",\n refresh_token: refreshToken,\n })\n })\n\n // JWKS Endpoint\n app.get(\"/oauth/jwks\", (c) => {\n return c.json({\n keys: [\n {\n kty: \"oct\", // Octet sequence for symmetric keys\n use: \"sig\",\n kid: \"duckpond-hmac-key\",\n alg: \"HS256\",\n },\n ],\n })\n })\n\n // Dynamic Client Registration\n app.post(\"/oauth/register\", async (c) => {\n try {\n let registrationRequest: OAuthClientRegistrationRequest = {}\n\n try {\n const body = await c.req.text()\n if (body && body !== \"[object Object]\") {\n try {\n registrationRequest = JSON.parse(body) as OAuthClientRegistrationRequest\n } catch {\n const formData = Object.fromEntries(new URLSearchParams(body))\n registrationRequest = formData as OAuthClientRegistrationRequest\n }\n }\n } catch (parseError) {\n log(\"Error parsing request body:\", parseError)\n }\n\n const clientId = `client-${randomBytes(8).toString(\"hex\")}`\n const clientSecret = randomBytes(16).toString(\"hex\")\n\n const response: OAuthClientRegistrationResponse = {\n client_id: clientId,\n client_secret: clientSecret,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_secret_expires_at: 0, // Never expires\n grant_types: registrationRequest.grant_types || [\"authorization_code\"],\n response_types: registrationRequest.response_types || [\"code\"],\n redirect_uris: registrationRequest.redirect_uris || [],\n token_endpoint_auth_method: registrationRequest.token_endpoint_auth_method || \"client_secret_post\",\n }\n\n if (registrationRequest.client_name) {\n response.client_name = registrationRequest.client_name\n }\n if (registrationRequest.scope) {\n response.scope = registrationRequest.scope\n }\n\n return c.json(response, 201)\n } catch (error) {\n return c.json(\n {\n error: \"invalid_client_metadata\",\n error_description:\n \"Invalid client registration request: \" + (error instanceof Error ? error.message : String(error)),\n },\n 400,\n )\n }\n })\n\n log(\"✓ OAuth flow endpoints added\")\n}\nexport async function startServer(options: FastMCPServerOptions, transport: \"stdio\" | \"http\"): Promise<void> {\n const { server, duckpond } = createFastMCPServer(options)\n\n // Initialize DuckPond\n const initResult = await duckpond.init()\n if (!initResult.success) {\n throw new Error(`Failed to initialize DuckPond: ${initResult.error.message}`)\n }\n\n log(\"DuckPond initialized successfully\")\n\n // Start the server with appropriate transport\n if (transport === \"stdio\") {\n await server.start({\n transportType: \"stdio\",\n })\n log(\"✓ FastMCP server running with stdio transport\")\n } else {\n await server.start({\n transportType: \"httpStream\",\n httpStream: {\n port: options.port || 3000,\n endpoint: (options.endpoint || \"/mcp\") as `/${string}`,\n },\n })\n log(`✓ FastMCP server running on http://0.0.0.0:${options.port || 3000}${options.endpoint || \"/mcp\"}`)\n log(\"🔌 Connect with StreamableHTTPClientTransport\")\n }\n\n // Handle cleanup on exit\n process.on(\"SIGINT\", async () => {\n log(\"Received SIGINT, closing server...\")\n await duckpond.close()\n process.exit(0)\n })\n\n process.on(\"SIGTERM\", async () => {\n log(\"Received SIGTERM, closing server...\")\n await duckpond.close()\n process.exit(0)\n })\n}\n"],"mappings":"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","names":["webcrypto","FastMCP","createHash","randomBytes","jwt","URL","webcrypto","log","loggers","JWT_SECRET","randomBytes","JWT_EXPIRES_IN","authorizationCodes","refreshTokens","createFastMCPServer","options","duckpond","DuckPondServer","baseConfig","server","FastMCP","request","authHeader","baseUrl","credentials","username","password","token","decoded","expectedAudience","error","querySchema","args","result","errorMessage","executeSchema","getUserStatsSchema","isAttachedSchema","detachUserSchema","listUsersSchema","setupOAuthEndpoints","c","serverInfo","app","now","code","data","params","responseType","redirectUri","state","codeChallenge","codeChallengeMethod","clientId","loginForm","body","redirectUrl","URL","grantType","codeVerifier","refreshTokenParam","tokenData","accessTokenPayload","newRefreshToken","accessToken","codeData","expectedChallenge","createHash","refreshToken","registrationRequest","parseError","clientSecret","response","startServer","transport","initResult"]}