appium-mcp 1.88.5 → 1.89.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.89.0](https://github.com/appium/appium-mcp/compare/v1.88.5...v1.89.0) (2026-07-26)
2
+
3
+ ### Features
4
+
5
+ * add payload-free tool result size telemetry ([#476](https://github.com/appium/appium-mcp/issues/476)) ([ca0262d](https://github.com/appium/appium-mcp/commit/ca0262d38a18747f08c1f08c94099c7b82150272))
6
+
1
7
  ## [1.88.5](https://github.com/appium/appium-mcp/compare/v1.88.4...v1.88.5) (2026-07-26)
2
8
 
3
9
  ### Performance Improvements
package/README.md CHANGED
@@ -189,6 +189,8 @@ OTEL_TRACES_SAMPLER=parentbased_always_on
189
189
 
190
190
  When enabled, appium-mcp creates spans for MCP tool calls, prompt loads, resource reads, and resource template reads. Error status is recorded for thrown operation errors and MCP tool results marked with `isError`. Span attributes intentionally avoid raw screenshots, XML page source, prompts, credentials, and other high-cardinality or sensitive payloads.
191
191
 
192
+ Tool spans include payload-free result-size attributes: `mcp.tool.result.content_count`, `content_types`, `text_chars`, `resource_count`, `resource_text_chars`, `image_count`, `audio_count`, `base64_chars`, and `base64_bytes_estimate` (all prefixed with `mcp.tool.result.`). `content_types` contains only known MCP types or `other`; payload values, resource URIs, MIME types, and unknown type strings are never recorded. Sizes are counted directly from known result fields without serializing or copying the complete result.
193
+
192
194
  For local trace inspection, use the Jaeger setup in `tools/telemetry`:
193
195
 
194
196
  ```bash
@@ -25,6 +25,14 @@ export declare function installTelemetryWrappers(server: FastMCP): void;
25
25
  * @returns A new tool definition with telemetry spans around the execute function.
26
26
  */
27
27
  export declare function wrapToolWithTelemetry(toolDef: ToolDef): ToolDef;
28
+ /**
29
+ * Builds payload-free size attributes for a tool result.
30
+ *
31
+ * This intentionally counts known MCP content fields directly instead of
32
+ * serializing the whole result. Large page sources, UI HTML, and base64 data
33
+ * therefore are not copied or recorded in telemetry.
34
+ */
35
+ export declare function safeToolResultSizeAttributes(result: unknown): Record<string, number | string[]>;
28
36
  export declare function safeInputValueAttributes(args: unknown): Record<string, string | number | boolean | string[]>;
29
37
  export {};
30
38
  //# sourceMappingURL=wrapOperations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrapOperations.d.ts","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAMrC,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAUjD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwB9D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAoB/D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAmB5G"}
1
+ {"version":3,"file":"wrapOperations.d.ts","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAMrC,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAYjD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwB9D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAsB/D;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAwE/F;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAmB5G"}
@@ -7,6 +7,7 @@
7
7
  import { isSensitiveKey } from '../utils/sensitive.js';
8
8
  import { isArgumentValueTelemetryEnabled, safeAttributeValue, safeInputKeys, safeSessionId } from './attributes.js';
9
9
  import { getActiveSpan, SpanStatusCode, withSpan } from './tracer.js';
10
+ const SAFE_TOOL_CONTENT_TYPES = new Set(['audio', 'image', 'resource', 'resource_link', 'text']);
10
11
  /**
11
12
  * Installs telemetry wrappers on the given FastMCP server instance. This should be
12
13
  * called early in the server setup process, before registering any tools, prompts,
@@ -50,14 +51,88 @@ export function wrapToolWithTelemetry(toolDef) {
50
51
  ...toolDef,
51
52
  execute: async (args, context) => withSpan(`tools/call ${toolName}`, toolAttributes(toolName, args), async () => {
52
53
  const result = await execute(args, context);
54
+ const span = getActiveSpan();
55
+ span?.setAttributes(safeToolResultSizeAttributes(result));
53
56
  if (isErrorResult(result)) {
54
- getActiveSpan()?.setStatus({ code: SpanStatusCode.ERROR });
55
- getActiveSpan()?.setAttribute('mcp.tool.result.is_error', true);
57
+ span?.setStatus({ code: SpanStatusCode.ERROR });
58
+ span?.setAttribute('mcp.tool.result.is_error', true);
56
59
  }
57
60
  return result;
58
61
  }),
59
62
  };
60
63
  }
64
+ /**
65
+ * Builds payload-free size attributes for a tool result.
66
+ *
67
+ * This intentionally counts known MCP content fields directly instead of
68
+ * serializing the whole result. Large page sources, UI HTML, and base64 data
69
+ * therefore are not copied or recorded in telemetry.
70
+ */
71
+ export function safeToolResultSizeAttributes(result) {
72
+ if (typeof result === 'string') {
73
+ return { 'mcp.tool.result.text_chars': result.length };
74
+ }
75
+ if (!isRecord(result) || !Array.isArray(result.content)) {
76
+ return {};
77
+ }
78
+ const contentTypes = new Set();
79
+ let textChars = 0;
80
+ let resourceCount = 0;
81
+ let resourceTextChars = 0;
82
+ let imageCount = 0;
83
+ let audioCount = 0;
84
+ let base64Chars = 0;
85
+ let base64BytesEstimate = 0;
86
+ for (const content of result.content) {
87
+ if (!isRecord(content)) {
88
+ contentTypes.add('other');
89
+ continue;
90
+ }
91
+ const contentType = typeof content.type === 'string' && SAFE_TOOL_CONTENT_TYPES.has(content.type) ? content.type : 'other';
92
+ contentTypes.add(contentType);
93
+ if (contentType === 'text' && typeof content.text === 'string') {
94
+ textChars += content.text.length;
95
+ continue;
96
+ }
97
+ if (contentType === 'image' || contentType === 'audio') {
98
+ if (contentType === 'image') {
99
+ imageCount += 1;
100
+ }
101
+ else {
102
+ audioCount += 1;
103
+ }
104
+ if (typeof content.data === 'string') {
105
+ base64Chars += content.data.length;
106
+ base64BytesEstimate += estimateBase64DecodedBytes(content.data);
107
+ }
108
+ continue;
109
+ }
110
+ if (contentType === 'resource') {
111
+ resourceCount += 1;
112
+ if (isRecord(content.resource)) {
113
+ if (typeof content.resource.text === 'string') {
114
+ resourceTextChars += content.resource.text.length;
115
+ }
116
+ if (typeof content.resource.blob === 'string') {
117
+ base64Chars += content.resource.blob.length;
118
+ base64BytesEstimate += estimateBase64DecodedBytes(content.resource.blob);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ const attributes = {
124
+ 'mcp.tool.result.content_count': result.content.length,
125
+ 'mcp.tool.result.content_types': [...contentTypes].sort(),
126
+ 'mcp.tool.result.text_chars': textChars,
127
+ 'mcp.tool.result.resource_count': resourceCount,
128
+ 'mcp.tool.result.resource_text_chars': resourceTextChars,
129
+ 'mcp.tool.result.image_count': imageCount,
130
+ 'mcp.tool.result.audio_count': audioCount,
131
+ 'mcp.tool.result.base64_chars': base64Chars,
132
+ 'mcp.tool.result.base64_bytes_estimate': base64BytesEstimate,
133
+ };
134
+ return attributes;
135
+ }
61
136
  export function safeInputValueAttributes(args) {
62
137
  if (!isArgumentValueTelemetryEnabled()) {
63
138
  return {};
@@ -150,4 +225,24 @@ function inputKeyAttributes(args) {
150
225
  function isErrorResult(result) {
151
226
  return (!!result && typeof result === 'object' && 'isError' in result && result.isError === true);
152
227
  }
228
+ function isRecord(value) {
229
+ return !!value && typeof value === 'object' && !Array.isArray(value);
230
+ }
231
+ function estimateBase64DecodedBytes(value) {
232
+ const commaIndex = value.indexOf(',');
233
+ const hasDataUriPrefix = commaIndex >= 7 && value.slice(commaIndex - 7, commaIndex).toLowerCase() === ';base64';
234
+ const contentStart = hasDataUriPrefix ? commaIndex + 1 : 0;
235
+ const encodedLength = value.length - contentStart;
236
+ if (encodedLength <= 0) {
237
+ return 0;
238
+ }
239
+ let padding = 0;
240
+ if (value.endsWith('==')) {
241
+ padding = 2;
242
+ }
243
+ else if (value.endsWith('=')) {
244
+ padding = 1;
245
+ }
246
+ return Math.max(0, Math.floor((encodedLength * 3) / 4) - padding);
247
+ }
153
248
  //# sourceMappingURL=wrapOperations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrapOperations.js","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wrapOperations.js","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-mcp",
3
- "version": "1.88.5",
3
+ "version": "1.89.0",
4
4
  "description": "Intelligent MCP server providing AI assistants with powerful tools and resources for Appium mobile automation",
5
5
  "bugs": {
6
6
  "url": "https://github.com/appium/appium-mcp/issues"
package/server.json CHANGED
@@ -3,12 +3,12 @@
3
3
  "name": "io.github.appium/appium-mcp",
4
4
  "title": "MCP Appium - Mobile Development and Automation Server",
5
5
  "description": "MCP server for Appium mobile automation on iOS and Android devices with test creation tools.",
6
- "version": "1.88.5",
6
+ "version": "1.89.0",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "appium-mcp",
11
- "version": "1.88.5",
11
+ "version": "1.89.0",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  }
@@ -21,6 +21,8 @@ type PromptLoad = NonNullable<PromptDef['load']>;
21
21
  type ResourceLoad = NonNullable<ResourceDef['load']>;
22
22
  type ResourceTemplateLoad = NonNullable<ResourceTemplateDef['load']>;
23
23
 
24
+ const SAFE_TOOL_CONTENT_TYPES = new Set(['audio', 'image', 'resource', 'resource_link', 'text']);
25
+
24
26
  /**
25
27
  * Installs telemetry wrappers on the given FastMCP server instance. This should be
26
28
  * called early in the server setup process, before registering any tools, prompts,
@@ -77,15 +79,98 @@ export function wrapToolWithTelemetry(toolDef: ToolDef): ToolDef {
77
79
  execute: async (args, context) =>
78
80
  withSpan(`tools/call ${toolName}`, toolAttributes(toolName, args), async () => {
79
81
  const result = await execute(args, context);
82
+ const span = getActiveSpan();
83
+ span?.setAttributes(safeToolResultSizeAttributes(result));
80
84
  if (isErrorResult(result)) {
81
- getActiveSpan()?.setStatus({code: SpanStatusCode.ERROR});
82
- getActiveSpan()?.setAttribute('mcp.tool.result.is_error', true);
85
+ span?.setStatus({code: SpanStatusCode.ERROR});
86
+ span?.setAttribute('mcp.tool.result.is_error', true);
83
87
  }
84
88
  return result;
85
89
  }),
86
90
  };
87
91
  }
88
92
 
93
+ /**
94
+ * Builds payload-free size attributes for a tool result.
95
+ *
96
+ * This intentionally counts known MCP content fields directly instead of
97
+ * serializing the whole result. Large page sources, UI HTML, and base64 data
98
+ * therefore are not copied or recorded in telemetry.
99
+ */
100
+ export function safeToolResultSizeAttributes(result: unknown): Record<string, number | string[]> {
101
+ if (typeof result === 'string') {
102
+ return {'mcp.tool.result.text_chars': result.length};
103
+ }
104
+ if (!isRecord(result) || !Array.isArray(result.content)) {
105
+ return {};
106
+ }
107
+
108
+ const contentTypes = new Set<string>();
109
+ let textChars = 0;
110
+ let resourceCount = 0;
111
+ let resourceTextChars = 0;
112
+ let imageCount = 0;
113
+ let audioCount = 0;
114
+ let base64Chars = 0;
115
+ let base64BytesEstimate = 0;
116
+
117
+ for (const content of result.content) {
118
+ if (!isRecord(content)) {
119
+ contentTypes.add('other');
120
+ continue;
121
+ }
122
+
123
+ const contentType =
124
+ typeof content.type === 'string' && SAFE_TOOL_CONTENT_TYPES.has(content.type) ? content.type : 'other';
125
+ contentTypes.add(contentType);
126
+
127
+ if (contentType === 'text' && typeof content.text === 'string') {
128
+ textChars += content.text.length;
129
+ continue;
130
+ }
131
+
132
+ if (contentType === 'image' || contentType === 'audio') {
133
+ if (contentType === 'image') {
134
+ imageCount += 1;
135
+ } else {
136
+ audioCount += 1;
137
+ }
138
+ if (typeof content.data === 'string') {
139
+ base64Chars += content.data.length;
140
+ base64BytesEstimate += estimateBase64DecodedBytes(content.data);
141
+ }
142
+ continue;
143
+ }
144
+
145
+ if (contentType === 'resource') {
146
+ resourceCount += 1;
147
+ if (isRecord(content.resource)) {
148
+ if (typeof content.resource.text === 'string') {
149
+ resourceTextChars += content.resource.text.length;
150
+ }
151
+ if (typeof content.resource.blob === 'string') {
152
+ base64Chars += content.resource.blob.length;
153
+ base64BytesEstimate += estimateBase64DecodedBytes(content.resource.blob);
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ const attributes: Record<string, number | string[]> = {
160
+ 'mcp.tool.result.content_count': result.content.length,
161
+ 'mcp.tool.result.content_types': [...contentTypes].sort(),
162
+ 'mcp.tool.result.text_chars': textChars,
163
+ 'mcp.tool.result.resource_count': resourceCount,
164
+ 'mcp.tool.result.resource_text_chars': resourceTextChars,
165
+ 'mcp.tool.result.image_count': imageCount,
166
+ 'mcp.tool.result.audio_count': audioCount,
167
+ 'mcp.tool.result.base64_chars': base64Chars,
168
+ 'mcp.tool.result.base64_bytes_estimate': base64BytesEstimate,
169
+ };
170
+
171
+ return attributes;
172
+ }
173
+
89
174
  export function safeInputValueAttributes(args: unknown): Record<string, string | number | boolean | string[]> {
90
175
  if (!isArgumentValueTelemetryEnabled()) {
91
176
  return {};
@@ -213,3 +298,25 @@ function isErrorResult(result: unknown): boolean {
213
298
  !!result && typeof result === 'object' && 'isError' in result && (result as {isError?: unknown}).isError === true
214
299
  );
215
300
  }
301
+
302
+ function isRecord(value: unknown): value is Record<string, unknown> {
303
+ return !!value && typeof value === 'object' && !Array.isArray(value);
304
+ }
305
+
306
+ function estimateBase64DecodedBytes(value: string): number {
307
+ const commaIndex = value.indexOf(',');
308
+ const hasDataUriPrefix = commaIndex >= 7 && value.slice(commaIndex - 7, commaIndex).toLowerCase() === ';base64';
309
+ const contentStart = hasDataUriPrefix ? commaIndex + 1 : 0;
310
+ const encodedLength = value.length - contentStart;
311
+ if (encodedLength <= 0) {
312
+ return 0;
313
+ }
314
+
315
+ let padding = 0;
316
+ if (value.endsWith('==')) {
317
+ padding = 2;
318
+ } else if (value.endsWith('=')) {
319
+ padding = 1;
320
+ }
321
+ return Math.max(0, Math.floor((encodedLength * 3) / 4) - padding);
322
+ }