Add dedicated tracing for MCP resources (#11569)

* Initial plan

* feat: Add separate tracing for MCP resources

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
This commit is contained in:
Copilot
2026-02-25 14:40:08 -05:00
committed by GitHub
parent 38920cd1fe
commit 6708f5dbc2
4 changed files with 41 additions and 8 deletions
+2 -1
View File
@@ -17,6 +17,7 @@ import {
buildAPIContext,
pathToUrl,
withTracing,
withResourceTracing,
} from "./util";
/**
@@ -128,7 +129,7 @@ export function collectionTools(server: McpServer, scopes: string[]) {
"Fetches the details of a collection by its ID, including its document structure.",
mimeType: "application/json",
},
withTracing("get_collection", async (uri, variables, extra) => {
withResourceTracing("get_collection", async (uri, variables, extra) => {
try {
const { id } = variables;
const user = getActorFromContext(extra);
+2 -1
View File
@@ -25,6 +25,7 @@ import {
getActorFromContext,
pathToUrl,
withTracing,
withResourceTracing,
} from "./util";
import { TextEditMode } from "@shared/types";
@@ -45,7 +46,7 @@ export function documentTools(server: McpServer, scopes: string[]) {
description: "Fetches the content of a document by its ID.",
mimeType: "text/markdown",
},
withTracing("get_document", async (uri, variables, extra) => {
withResourceTracing("get_document", async (uri, variables, extra) => {
try {
const { id } = variables;
const user = getActorFromContext(extra);
+2 -2
View File
@@ -11,7 +11,7 @@ import { User, Team } from "@server/models";
import { authorize, can } from "@server/policies";
import { presentUser } from "@server/presenters";
import AuthenticationHelper from "@shared/helpers/AuthenticationHelper";
import { error, success, getActorFromContext, withTracing } from "./util";
import { error, success, getActorFromContext, withTracing, withResourceTracing } from "./util";
/**
* Resolves a user identifier to a User model instance. Accepts special
@@ -50,7 +50,7 @@ export function userTools(server: McpServer, scopes: string[]) {
'Fetches a user by their ID. Use "current_user" as the ID to get the currently authenticated user.',
mimeType: "application/json",
},
withTracing("get_user", async (uri, variables, extra) => {
withResourceTracing("get_user", async (uri, variables, extra) => {
try {
const { id } = variables;
const actor = getActorFromContext(extra);
+35 -4
View File
@@ -76,11 +76,11 @@ export function error(err: unknown): CallToolResult {
}
/**
* Wraps an MCP tool or resource handler with Datadog tracing. Each invocation
* creates a span under the `outline-mcp` service with the tool name as the
* resource, and tags it with the acting user and team IDs.
* Wraps an MCP tool handler with Datadog tracing. Each invocation creates a
* span under the `outline-mcp` service with the tool name as the resource,
* and tags it with the acting user and team IDs.
*
* @param toolName - the name of the MCP tool or resource being traced.
* @param toolName - the name of the MCP tool being traced.
* @param handler - the handler function to wrap.
* @returns the wrapped handler with tracing enabled.
*/
@@ -106,6 +106,37 @@ export function withTracing<F extends (...args: any[]) => any>(
} as F);
}
/**
* Wraps an MCP resource handler with Datadog tracing. Each invocation creates
* a span under the `outline-mcp` service with the resource name, and tags it
* with the acting user and team IDs.
*
* @param resourceName - the name of the MCP resource being traced.
* @param handler - the handler function to wrap.
* @returns the wrapped handler with tracing enabled.
*/
export function withResourceTracing<F extends (...args: any[]) => any>(
resourceName: string,
handler: F
): F {
return traceFunction({
serviceName: "mcp",
spanName: "resource",
resourceName: resourceName,
})(function tracedHandler(this: any, ...args: any[]) {
const context = args[args.length - 1];
const user = getActorFromContext(context);
if (user) {
addTags({
"mcp.resource": resourceName,
"request.userId": user.id,
"request.teamId": user.teamId,
});
}
return handler.apply(this, args);
} as F);
}
/**
* Utility function to construct a URL by joining a team URL with a path segment.
*