ivaturia/king-mcp-server
criticalNo description
This MCP server provides an interface for King Insurance to retrieve and create insurance quotes. It exposes tools to look up quotes by ID or by email...
19const env = {
20 KING_CLIENT_ID: process.env.KING_CLIENT_ID,
21 KING_CLIENT_SECRET: process.env.KING_CLIENT_SECRET
22};// Network-exposed MCP; credentials could be leaked via server compromise or debug output.
The OAuth client ID and secret are read from environment variables and passed directly to the API calls without any encryption or secure storage. While environment variables are a standard practice, the credentials are used in plaintext in the code flow.
ImpactIf an attacker gains access to the environment (e.g., via server compromise or debug logs), they could obtain the client credentials and impersonate the server to the King Insurance API, potentially accessing or modifying quotes.
FixUse a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault) to store and retrieve credentials at runtime. Avoid logging or exposing credentials in error messages.
395app.post("/mcp", async (req, res) => {
396 try {
397 const transport = new StreamableHTTPServerTransport({
398 sessionIdGenerator: undefined,
399 enableJsonResponse: true
400 });
401 res.on("close", () => transport.close());
402 await server.connect(transport);
403 await transport.handleRequest(req, res, req.body);
404 } catch (err) { ... }
405});// Network-exposed MCP; any client can access all tools without authentication.
The MCP endpoint at /mcp has no authentication or authorization checks. Any client that can reach the server can invoke all tools (get_by_id, get_by_email_zip, create) without restriction. The server relies on the King Insurance API's OAuth for backend authentication, but the MCP layer itself is open.
ImpactAn attacker with network access to the server can query or create insurance quotes, potentially accessing sensitive customer PII (names, DOB, addresses, email, phone) and creating fraudulent quotes.
FixAdd authentication middleware (e.g., API key, JWT) to the /mcp endpoint. Validate that the caller is authorized to use the MCP server.
199inputSchema: {
200 person: z.object({
201 first_name: z.string().min(1).describe("Customer's first name"),
202 last_name: z.string().min(1).describe("Customer's last name"),
203 dob: z.string().describe("Date of birth"),
204 email: z.string().email().describe("Email address for quote delivery"),
205 phone: z.string().optional().describe("Phone number (optional)"),
206 address1: z.string().optional().describe("Street address (optional)"),
207 ...
208 }),
209 drivers: z.array(z.object({ ... })),
210 vehicles: z.array(z.object({ ... })),
211 bundle: z.object({ ... }).optional()
212} as any// Network-exposed MCP; untrusted users can supply arbitrary data to create quotes.
The create quote tool accepts a wide range of inputs with minimal validation. Fields like dob, address, and driver details are only validated as strings or numbers without format checks (e.g., date format, state abbreviations). The 'as any' cast bypasses TypeScript strictness, potentially allowing unexpected data types.
ImpactAn attacker could inject malicious data (e.g., script tags, SQL injection payloads) into fields that might be stored or processed unsafely by the King Insurance API, leading to data corruption or injection attacks.
FixAdd strict validation for each field (e.g., date regex, state enum, phone regex). Remove 'as any' and use proper Zod schemas.
39inputSchema: {
40 quote_id: z.string().min(1).describe("The unique Quote ID (e.g., QUOTE-1234567890-abc123)")
41}// Network-exposed MCP; untrusted users can supply arbitrary quote_id values.
The quote_id parameter is only validated as a non-empty string. There is no format validation (e.g., regex) to ensure it matches the expected pattern (e.g., QUOTE-...). This could allow injection of unexpected values into the API call.
ImpactAn attacker could pass arbitrary strings as quote_id, potentially causing injection attacks against the King Insurance API if it does not properly sanitize inputs. This could lead to information disclosure or other API-level exploits.
FixAdd a regex pattern to validate the quote_id format (e.g., /^QUOTE-[A-Za-z0-9-]+$/).
116inputSchema: {
117 email: z.string().email().describe("Customer's email address"),
118 zipcode: z.string().min(5).describe("Customer's ZIP or postal code")
119} as any// Network-exposed MCP; untrusted users can supply arbitrary zipcode values.
The email is validated as a valid email format, but zipcode is only checked for minimum length 5. There is no format validation (e.g., US ZIP code pattern) or sanitization. This could allow injection of special characters or unexpected values.
ImpactAn attacker could supply malicious zipcode values (e.g., SQL injection payloads) that might be passed to the King Insurance API, potentially leading to data breaches or API abuse.
FixAdd a regex pattern for US ZIP codes (e.g., /^\d{5}(-\d{4})?$/) and sanitize inputs before passing to the API.
46const tokenResult = await api.getOAuthToken('client_credentials', env.KING_CLIENT_ID, env.KING_CLIENT_SECRET);
47if (!tokenResult.success) {
48 return {
49 content: [{
50 type: "text" as const,
51 text: "Authentication failed. Please check your credentials."
52 }]
53 };
54}// Network-exposed MCP; server logs may be accessible to attackers.
When OAuth token retrieval fails, the error message is generic and does not leak credentials. However, the error handling in the catch block (line 407) logs the error to console.error, which could expose sensitive information if the error object contains request details or stack traces.
ImpactIf an attacker can access server logs, they might obtain information about the internal API calls or credentials if error objects are verbose.
FixEnsure error logging does not include sensitive data. Use structured logging with redaction.