Describe the bug
Hey,
I've been struggling to understand why copilot was sometimes incorrectly using my tools.
What I discovered is that tools being loaded from my stdio mcp server seem to be getting exposed, differently, to copilot-cli.
For example, I have a tool with this input schema (and confirmed this is what's getting returned from my mcp server by debugging a list tools
{
type: "object",
properties: {
query: {
type: "object",
properties: {
index: {
type: "string",
enum: [ "application-logs", "jaeger-tracing" ],
description: "Target index to query, must be one of the indices returned by elasticsearch_get_index_field_capabilities",
},
size: {
default: 20,
description: "The number of results to return, if you are doing an aggregation query you may want to set this to 0",
type: "number",
minimum: 0,
maximum: 50,
},
query: {
description: "The query body, using Elasticsearch Query DSL.\n\nQuery-shape guardrails:\n- You should always provide a reasonable time range filter in your query to avoid long-running queries.\n- Use exactly one root query type under query (for example bool), and when using bool only place clauses inside must/filter/should/must_not.\n- Keep query shape valid Elasticsearch DSL: one root query type under query, and for bool queries place range/term/match clauses inside must/filter/should/must_not.",
type: "object",
propertyNames: {
type: "string",
},
additionalProperties: {},
},
aggs: {
type: "object",
propertyNames: {
type: "string",
},
additionalProperties: {},
},
sort: {
anyOf: [
{
type: "object",
propertyNames: {
type: "string",
},
additionalProperties: {},
description: "Sort using Elasticsearch object syntax. Example: {\"startTimeMillis\":{\"order\":\"desc\"}} or {\"startTimeMillis\":\"desc\"}.",
},
{
type: "array",
items: {
anyOf: [
{
type: "string",
description: "Sort by a field name string. Example: \"startTimeMillis\" or \"startTimeMillis:desc\". Do not JSON-encode sort objects as strings.",
},
{
type: "object",
propertyNames: {
type: "string",
},
additionalProperties: {},
description: "Sort using Elasticsearch object syntax. Example: {\"startTimeMillis\":{\"order\":\"desc\"}} or {\"startTimeMillis\":\"desc\"}.",
}
],
},
description: "Sort using an ordered list. Example: [\"_score\", {\"startTimeMillis\":{\"order\":\"desc\"}}].",
}
],
description: "Sort order for the results, using Elasticsearch sort syntax. Prefer object/list syntax for explicit ordering and only use fields returned by elasticsearch_get_index_field_capabilities.",
},
},
required: [ "index" ],
description: "Use object-style sort syntax and only fields returned by elasticsearch_get_index_field_capabilities.",
},
environment: {
type: "string",
enum: [ "testing", "dev", "preprod", "prod", "bcp" ],
description: "Target deployment environment to search",
},
},
required: [ "query", "environment" ],
$schema: "http://json-schema.org/draft-07/schema#",
}
This was quite literally the output from:
await client.connect(transport)
const result = await client.listTools()
console.dir(
result.tools.find((item) => item.name === 'elasticsearch_query')
?.inputSchema,
{ depth: null }
)
However what copilot claims it can't see query, and aggs
I asked it to explain, and it said:
Assistant-visible `atai-elasticsearch_query` input shape appears reduced to:
- `query.index`
- `query.size`
- `query.sort`
- `environment`
And notably **missing**:
- `query.query`
- `query.aggs`
Also, `sort` representation appears narrowed/normalized compared with server-advertised `anyOf` object-or-array union.
Further to that it can't see any of the descriptions. So it's like there's some sort of translation of the tools between the mcp list tools response and then what the llm actually sees?
Affected version
1.0.23
Steps to reproduce the behavior
No response
Expected behavior
No response
Additional context
Using GPT-5.3-Codex
I asked copilot to try and debug it in the copilot code, not sure if this helps or not:
● I tracked this through node_modules/@github/copilot/app.js, and the key result is: the local MCP schema is preserved through Copilot’s in-process MCP transforms; the
field loss happens after that layer (in the model-facing tool bridge).
Evidence from code:
1. MCP tools are loaded and normalized via:
- input_schema: pit(a.inputSchema) (and similarly pit(m.inputSchema))
2. Before sending tools to the model, schemas are transformed again:
- input_schema: ZNn(D.input_schema)
I extracted both transform functions (pit/LQe and ZNn/l0e) and then ran your real elasticsearch_query schema through them. Result:
{
"original_query_query": true,
"original_query_aggs": true,
"pit_query_query": true,
"pit_query_aggs": true,
"znn_query_query": true,
"znn_query_aggs": true
}
So query.query and query.aggs survive both Copilot-side schema normalizers in app.js.
What this means
The mismatch you’re seeing is not from your MCP server, and not from pit/ZNn in the local Copilot package.
It’s in the later adapter that exposes tools to this chat model (the atai-* function contract surface), where a reduced/lossy schema is being presented. In short:
tool-bridge/schema-projection bug upstream of what’s in your repo code.
❯ i dont understand, is it a bug? if so in what module? where should i be raising the bug?
● Yes — this is a bug.
It’s most likely not in your MCP server and not in the local @github/copilot MCP ingestion path (pit/ZNn in app.js). The bug appears in the tool exposure bridge that
turns runtime tools into the model-visible functions.* schema (the layer that produced atai-elasticsearch_query for me).
Where to raise it: the Copilot CLI / agent runtime repo (the team that owns model tool-binding/schema projection), not your Autotrader/ai repo and not your MCP tool
module.
In the issue, include:
1. bun run lib/test.ts output showing full elasticsearch_query schema.
2. The model-visible contract showing missing query.query and query.aggs.
3. The finding that pit and ZNn both preserve those fields.
4. Conclusion: loss occurs in post-MCP tool projection to model function schema.
Describe the bug
Hey,
I've been struggling to understand why copilot was sometimes incorrectly using my tools.
What I discovered is that tools being loaded from my stdio mcp server seem to be getting exposed, differently, to copilot-cli.
For example, I have a tool with this input schema (and confirmed this is what's getting returned from my mcp server by debugging a list tools
This was quite literally the output from:
However what copilot claims it can't see
query, andaggsI asked it to explain, and it said:
Further to that it can't see any of the descriptions. So it's like there's some sort of translation of the tools between the mcp list tools response and then what the llm actually sees?
Affected version
1.0.23
Steps to reproduce the behavior
No response
Expected behavior
No response
Additional context
Using
GPT-5.3-CodexI asked copilot to try and debug it in the copilot code, not sure if this helps or not: