Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,508 changes: 1,333 additions & 175 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cueapi/mcp",
"version": "0.1.4",
"version": "0.2.0",
"mcpName": "io.github.govindkavaturi-art/cueapi-mcp",
"description": "Official Model Context Protocol (MCP) server for CueAPI — give your AI agent a scheduler and verification gate. Open-source execution accountability primitive for AI agents.",
"type": "module",
Expand Down Expand Up @@ -53,11 +53,17 @@
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"better-sqlite3": "^11.3.0",
"express": "^4.21.0",
"zod": "^3.23.0",
"zod-to-json-schema": "^3.24.0"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.11",
"@types/express": "^5.0.0",
"@types/node": "^22.0.0",
"supertest": "^7.0.0",
"@types/supertest": "^6.0.2",
"tsx": "^4.19.0",
"typescript": "^5.5.0",
"vitest": "^2.0.0"
Expand Down
39 changes: 29 additions & 10 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@
* We deliberately avoid adding the cueapi-sdk as a dependency — MCP servers
* should be tiny and self-contained so they cold-start fast under Claude
* Desktop and other hosts.
*
* 0.2.0 (HTTP transport) changes:
*
* - The constructor's ``apiKey`` is now OPTIONAL. In stdio mode it
* comes from ``CUEAPI_API_KEY`` once at boot and is reused for
* every request. In HTTP mode each incoming ``/mcp`` request
* carries its own bearer token (looked up in the token store from
* the OAuth access_token), so the key is passed on every
* ``request()`` call as the optional ``apiKey`` argument.
*
* - ``request()`` accepts a trailing ``apiKey`` that, when provided,
* overrides the constructor's default. Callers in HTTP mode
* always pass it; callers in stdio mode can omit.
*/

export interface CueAPIClientOptions {
apiKey: string;
apiKey?: string;
baseUrl?: string;
}

Expand All @@ -23,23 +36,29 @@ export class CueAPIError extends Error {
}

export class CueAPIClient {
private readonly apiKey: string;
private readonly defaultApiKey: string | undefined;
private readonly baseUrl: string;

constructor(opts: CueAPIClientOptions) {
if (!opts.apiKey) {
throw new Error("CUEAPI_API_KEY is required");
}
this.apiKey = opts.apiKey;
constructor(opts: CueAPIClientOptions = {}) {
this.defaultApiKey = opts.apiKey;
this.baseUrl = (opts.baseUrl ?? "https://api.cueapi.ai").replace(/\/$/, "");
}

async request<T = unknown>(
method: string,
path: string,
body?: Record<string, unknown> | null,
query?: Record<string, string | number | boolean | undefined>
query?: Record<string, string | number | boolean | undefined>,
apiKey?: string
): Promise<T> {
const effectiveKey = apiKey ?? this.defaultApiKey;
if (!effectiveKey) {
throw new Error(
"No API key available. Provide one to the constructor (stdio mode) " +
"or pass it on the request call (HTTP mode)."
);
}

const qs = query
? "?" +
Object.entries(query)
Expand All @@ -55,10 +74,10 @@ export class CueAPIClient {
const res = await fetch(url, {
method,
headers: {
Authorization: `Bearer ${this.apiKey}`,
Authorization: `Bearer ${effectiveKey}`,
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent": "cueapi-mcp/0.1.0",
"User-Agent": "cueapi-mcp/0.2.0",
},
body: body ? JSON.stringify(body) : undefined,
});
Expand Down
Loading