mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user