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
80 changes: 75 additions & 5 deletions src/graph-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
method?: string;
body?: string;
rawResponse?: boolean;
includeHeaders?: boolean;
accessToken?: string;
refreshToken?: string;

Expand Down Expand Up @@ -88,15 +89,32 @@
}

const text = await response.text();
let result: any;

Check warning on line 92 in src/graph-client.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Unexpected any. Specify a different type

Check warning on line 92 in src/graph-client.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type

if (text === '') {
return { message: 'OK!' };
result = { message: 'OK!' };
} else {
try {
result = JSON.parse(text);
} catch {
result = { message: 'OK!', rawResponse: text };
}
}

try {
return JSON.parse(text);
} catch {
return { message: 'OK!', rawResponse: text };
// If includeHeaders is requested, add response headers to the result
if (options.includeHeaders) {
const etag = response.headers.get('ETag') || response.headers.get('etag');

// Simple approach: just add ETag to the result if it's an object
if (result && typeof result === 'object' && !Array.isArray(result)) {
return {
...result,
_etag: etag || 'no-etag-found',
};
}
}

return result;
} catch (error) {
logger.error('Microsoft Graph API request failed:', error);
throw error;
Expand Down Expand Up @@ -157,6 +175,58 @@
}

formatJsonResponse(data: unknown, rawResponse = false): McpResponse {
// Handle the case where data includes headers metadata
if (data && typeof data === 'object' && '_headers' in data) {
const responseData = data as {
data: unknown;
_headers: Record<string, string>;
_etag?: string;
};

const meta: Record<string, unknown> = {};
if (responseData._etag) {
meta.etag = responseData._etag;
}
if (responseData._headers) {
meta.headers = responseData._headers;
}

if (rawResponse) {
return {
content: [{ type: 'text', text: JSON.stringify(responseData.data) }],
_meta: meta,
};
}

if (responseData.data === null || responseData.data === undefined) {
return {
content: [{ type: 'text', text: JSON.stringify({ success: true }) }],
_meta: meta,
};
}

// Remove OData properties
const removeODataProps = (obj: Record<string, unknown>): void => {
if (typeof obj === 'object' && obj !== null) {
Object.keys(obj).forEach((key) => {
if (key.startsWith('@odata.')) {
delete obj[key];
} else if (typeof obj[key] === 'object') {
removeODataProps(obj[key] as Record<string, unknown>);
}
});
}
};

removeODataProps(responseData.data as Record<string, unknown>);

return {
content: [{ type: 'text', text: JSON.stringify(responseData.data, null, 2) }],
_meta: meta,
};
}

// Original handling for backward compatibility
if (rawResponse) {
return {
content: [{ type: 'text', text: JSON.stringify(data) }],
Expand Down
24 changes: 23 additions & 1 deletion src/graph-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ export function registerGraphTools(
.optional();
}

// Add includeHeaders parameter for all tools to capture ETags and other headers
paramSchema['includeHeaders'] = z
.boolean()
.describe('Include response headers (including ETag) in the response metadata')
.optional();

server.tool(
tool.alias,
tool.description || `Execute ${tool.method.toUpperCase()} request to ${tool.path}`,
Expand All @@ -150,6 +156,11 @@ export function registerGraphTools(
continue;
}

// Skip headers control parameter - it's not part of the Microsoft Graph API
if (paramName === 'includeHeaders') {
continue;
}

// Ok, so, MCP clients (such as claude code) doesn't support $ in parameter names,
// and others might not support __, so we strip them in hack.ts and restore them here
const odataParams = [
Expand Down Expand Up @@ -201,7 +212,13 @@ export function registerGraphTools(
path = `${path}${path.includes('?') ? '&' : '?'}${queryString}`;
}

const options: { method: string; headers: Record<string, string>; body?: string } = {
const options: {
method: string;
headers: Record<string, string>;
body?: string;
rawResponse?: boolean;
includeHeaders?: boolean;
} = {
method: tool.method.toUpperCase(),
headers,
};
Expand All @@ -218,6 +235,11 @@ export function registerGraphTools(
options.rawResponse = true;
}

// Set includeHeaders if requested
if (params.includeHeaders === true) {
options.includeHeaders = true;
}

logger.info(`Making graph request to ${path} with options: ${JSON.stringify(options)}`);
let response = await graphClient.graphRequest(path, options);

Expand Down
Loading