> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mka1.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Use MCP tools

> Connect an MCP server to the MKA1 API Responses resource, limit allowed tools, and optionally require end-user approval.

Use MCP tools when you want the MKA1 API to call tools from an external MCP server during a response.
Define the MCP server in `tools`.
Limit which tools the model can call with `allowed_tools`.
Use `require_approval` when you want your app to pause and ask the end user before the tool runs.

Use `X-On-Behalf-Of` for the MKA1 API end user.
Pass upstream MCP server credentials in the MCP tool definition.

## Call an MCP tool directly

Set `require_approval` to `'never'` when the tool can run immediately.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm responses create --body '{
    "model": "auto",
    "instructions": "You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.",
    "input": "List my most recent Linear issue assigned to me.",
    "store": true,
    "stream": false,
    "tools": [
      {
        "type": "mcp",
        "server_label": "Linear MCP",
        "server_description": "Access Linear issues through MCP.",
        "server_url": "https://mcp.linear.app/mcp",
        "allowed_tools": ["issues.list"],
        "headers": {
          "Authorization": "Bearer <linear-api-key>"
        },
        "require_approval": "never"
      }
    ]
  }' \
    -H 'X-On-Behalf-Of: <end-user-id>'
  ```

  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1';
  import type * as components from '@meetkai/mka1/models/components';

  const mka1 = new SDK({
    bearerAuth: `Bearer ${YOUR_API_KEY}`,
  });

  const response = await mka1.llm.responses.create(
    {
      model: 'auto',
      instructions:
        'You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.',
      input: 'List my most recent Linear issue assigned to me.',
      store: true,
      stream: false,
      tools: [
        {
          type: 'mcp',
          serverLabel: 'Linear MCP',
          serverDescription: 'Access Linear issues through MCP.',
          serverUrl: 'https://mcp.linear.app/mcp',
          allowedTools: ['issues.list'],
          headers: {
            Authorization: `Bearer ${process.env.LINEAR_API_KEY}`,
          },
          requireApproval: 'never',
        },
      ],
    },
    { headers: { 'X-On-Behalf-Of': '<end-user-id>' } },
  );

  // MCP tool calls appear as function_call items in the output
  const mcpCalls = response.output.filter(
    (item): item is components.MCPToolCall => item.type === 'mcp_call',
  );

  const assistantText = response.output
    .filter(
      (item): item is components.OutputMessage =>
        item.type === 'message' && item.role === 'assistant',
    )
    .flatMap(item =>
      item.content.flatMap(content =>
        content.type === 'output_text' ? [content.text] : [],
      ),
    )
    .join('\n\n')
    .trim();

  console.log(assistantText);
  ```

  ```ts OpenAI SDK theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    apiKey: '<mka1-api-key>',
    baseURL: 'https://apigw.mka1.com/api/v1/llm/',
    defaultHeaders: { 'X-On-Behalf-Of': '<end-user-id>' },
  });

  const response = await openai.responses.create({
    model: 'auto',
    instructions:
      'You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.',
    input: 'List my most recent Linear issue assigned to me.',
    store: true,
    stream: false,
    tools: [
      {
        type: 'mcp',
        server_label: 'Linear MCP',
        server_description: 'Access Linear issues through MCP.',
        server_url: 'https://mcp.linear.app/mcp',
        allowed_tools: ['issues.list'],
        headers: {
          Authorization: `Bearer ${process.env.LINEAR_API_KEY}`,
        },
        require_approval: 'never',
      },
    ],
  });

  // MCP tool calls appear as function_call items in the output
  const functionCall = response.output.find((item) => item.type === 'function_call');
  const message = response.output.find((item) => item.type === 'message');
  console.log(response.output_text);
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  var response = await sdk.Llm.Responses.CreateAsync(
      new ResponsesCreateRequest()
      {
          Model = "auto",
          Input = ResponsesCreateRequestInput.CreateStr(
              "List my most recent Linear issue assigned to me."
          ),
      }
  );

  // MCP tools require a running MCP server endpoint.
  // The SDK constructs the same request shape as the TypeScript and curl examples.
  Console.WriteLine(response);
  ```

  ```python Python SDK theme={null}
  from mka1 import SDK
  import os

  sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")

  response = sdk.llm.responses.create(
      model="auto",
      instructions="You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.",
      input="List my most recent Linear issue assigned to me.",
      store=True,
      stream=False,
      tools=[
          {
              "type": "mcp",
              "server_label": "Linear MCP",
              "server_description": "Access Linear issues through MCP.",
              "server_url": "https://mcp.linear.app/mcp",
              "allowed_tools": ["issues.list"],
              "headers": {
                  "Authorization": f"Bearer {os.environ['LINEAR_API_KEY']}",
              },
              "require_approval": "never",
          },
      ],
  )

  print(response.output_text)
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/responses \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data '{
      "model": "auto",
      "instructions": "You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.",
      "input": "List my most recent Linear issue assigned to me.",
      "store": true,
      "stream": false,
      "tools": [
        {
          "type": "mcp",
          "server_label": "Linear MCP",
          "server_description": "Access Linear issues through MCP.",
          "server_url": "https://mcp.linear.app/mcp",
          "allowed_tools": ["issues.list"],
          "headers": {
            "Authorization": "Bearer <linear-api-key>"
          },
          "require_approval": "never"
        }
      ]
    }'
  ```
</CodeGroup>

This is the simplest flow.
The model calls the allowed MCP tool and returns the final assistant message in one request.

The response `output` array contains:

1. `function_call` — the model's call to the MCP-discovered tool
2. `function_call_output` — the data returned by the MCP server
3. `message` — the model's text response summarizing the results

## Require end-user approval

Set `require_approval` to `'always'` when your app should stop and wait for an approval decision.
In this flow, create the response in background mode, poll it, and look for an `mcp_approval_request` item in `output`.

<CodeGroup>
  ```bash CLI theme={null}
  # Step 1: Create a background response with approval required
  mka1 llm responses create --body '{
    "model": "auto",
    "instructions": "You are a project management assistant with access to Linear via MCP.",
    "input": "List my most recent Linear issue assigned to me.",
    "background": true,
    "store": true,
    "stream": false,
    "tools": [
      {
        "type": "mcp",
        "server_label": "Linear MCP",
        "server_url": "https://mcp.linear.app/mcp",
        "allowed_tools": ["issues.list"],
        "headers": { "Authorization": "Bearer <linear-api-key>" },
        "require_approval": "always"
      }
    ]
  }'

  # Step 2: Poll the response by id until an mcp_approval_request appears
  mka1 llm responses get --response-id <response-id>

  # Step 3: Send approval to continue
  mka1 llm responses create --body '{
    "model": "auto",
    "previous_response_id": "<response-id>",
    "input": [
      {
        "type": "mcp_approval_response",
        "approval_request_id": "<approval-request-id>",
        "approve": true
      }
    ],
    "store": true,
    "stream": false
  }'
  ```

  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1';
  import type * as components from '@meetkai/mka1/models/components';

  const mka1 = new SDK({
    bearerAuth: `Bearer ${YOUR_API_KEY}`,
  });

  // Step 1: Create a background response with approval required
  let pendingResponse = await mka1.llm.responses.create(
    {
      model: 'auto',
      instructions:
        'You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.',
      input: 'List my most recent Linear issue assigned to me.',
      background: true,
      store: true,
      stream: false,
      tools: [
        {
          type: 'mcp',
          serverLabel: 'Linear MCP',
          serverDescription: 'Access Linear issues through MCP.',
          serverUrl: 'https://mcp.linear.app/mcp',
          allowedTools: ['issues.list'],
          headers: {
            Authorization: `Bearer ${process.env.LINEAR_API_KEY}`,
          },
          requireApproval: 'always',
        },
      ],
    },
    { headers: { 'X-On-Behalf-Of': '<end-user-id>' } },
  );

  // Step 2: Poll until an approval request appears
  let approvalRequest: components.MCPApprovalRequest | undefined;

  while (
    pendingResponse.status === 'queued' ||
    pendingResponse.status === 'in_progress'
  ) {
    approvalRequest = pendingResponse.output.find(
      (item): item is components.MCPApprovalRequest =>
        item.type === 'mcp_approval_request',
    );

    if (approvalRequest) break;

    await new Promise(resolve => setTimeout(resolve, 1000));

    pendingResponse = await mka1.llm.responses.get(
      { responseId: pendingResponse.id },
      { headers: { 'X-On-Behalf-Of': '<end-user-id>' } },
    );
  }

  if (!approvalRequest) {
    throw new Error(`No approval request found. Response ended with ${pendingResponse.status}.`);
  }

  // Step 3: Show the user what the model wants to do
  console.log('Server:', approvalRequest.serverLabel);
  console.log('Tool:', approvalRequest.name);
  console.log('Arguments:', approvalRequest.arguments);

  // Step 4: Send the approval (or denial) to continue
  const approve = true; // Replace with your UI decision

  const continuedResponse = await mka1.llm.responses.create(
    {
      model: 'auto',
      previousResponseId: pendingResponse.id,
      input: [
        {
          type: 'mcp_approval_response',
          approvalRequestId: approvalRequest.id,
          approve,
        },
      ],
      store: true,
      stream: false,
    },
    { headers: { 'X-On-Behalf-Of': '<end-user-id>' } },
  );

  const assistantText = continuedResponse.output
    .filter(
      (item): item is components.OutputMessage =>
        item.type === 'message' && item.role === 'assistant',
    )
    .flatMap(item =>
      item.content.flatMap(content =>
        content.type === 'output_text' ? [content.text] : [],
      ),
    )
    .join('\n\n')
    .trim();

  console.log(assistantText);
  ```

  ```ts OpenAI SDK theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    apiKey: '<mka1-api-key>',
    baseURL: 'https://apigw.mka1.com/api/v1/llm/',
    defaultHeaders: { 'X-On-Behalf-Of': '<end-user-id>' },
  });

  // Step 1: Create a background response with approval required
  let pendingResponse = await openai.responses.create({
    model: 'auto',
    instructions:
      'You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.',
    input: 'List my most recent Linear issue assigned to me.',
    background: true,
    store: true,
    stream: false,
    tools: [
      {
        type: 'mcp',
        server_label: 'Linear MCP',
        server_description: 'Access Linear issues through MCP.',
        server_url: 'https://mcp.linear.app/mcp',
        allowed_tools: ['issues.list'],
        headers: {
          Authorization: `Bearer ${process.env.LINEAR_API_KEY}`,
        },
        require_approval: 'always',
      },
    ],
  });

  // Step 2: Poll until an approval request appears
  let approvalRequest;

  while (
    pendingResponse.status === 'queued' ||
    pendingResponse.status === 'in_progress'
  ) {
    approvalRequest = pendingResponse.output.find(
      (item) => item.type === 'mcp_approval_request',
    );

    if (approvalRequest) break;

    await new Promise(resolve => setTimeout(resolve, 1000));
    pendingResponse = await openai.responses.retrieve(pendingResponse.id);
  }

  if (!approvalRequest) {
    throw new Error(`No approval request found. Response ended with ${pendingResponse.status}.`);
  }

  // Step 3: Show the user what the model wants to do
  console.log('Server:', approvalRequest.server_label);
  console.log('Tool:', approvalRequest.name);
  console.log('Arguments:', approvalRequest.arguments);

  // Step 4: Send the approval (or denial) to continue
  const approve = true; // Replace with your UI decision

  const continuedResponse = await openai.responses.create({
    model: 'auto',
    previous_response_id: pendingResponse.id,
    input: [
      {
        type: 'mcp_approval_response',
        approval_request_id: approvalRequest.id,
        approve,
      },
    ],
    store: true,
    stream: false,
  });

  console.log(continuedResponse.output_text);
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  // MCP approval workflow requires a running MCP server.
  // The SDK can construct background requests with MCP tools.
  var request = new ResponsesCreateRequest()
  {
      Model = "auto",
      Input = ResponsesCreateRequestInput.CreateStr("Run the approved tool."),
      Background = true,
  };

  Console.WriteLine(request);
  ```

  ```python Python SDK theme={null}
  import os
  import time

  # Step 1: Create a background response with approval required
  pending = sdk.llm.responses.create(
      model="auto",
      instructions="You are a project management assistant with access to Linear via MCP. Use Linear tools when the user asks about tasks, bugs, or projects. Keep the final answer terse.",
      input="List my most recent Linear issue assigned to me.",
      background=True,
      store=True,
      stream=False,
      tools=[
          {
              "type": "mcp",
              "server_label": "Linear MCP",
              "server_description": "Access Linear issues through MCP.",
              "server_url": "https://mcp.linear.app/mcp",
              "allowed_tools": ["issues.list"],
              "headers": {
                  "Authorization": f"Bearer {os.environ['LINEAR_API_KEY']}",
              },
              "require_approval": "always",
          },
      ],
  )

  # Step 2: Poll until an approval request appears
  approval_request = None
  while pending.status in ("queued", "in_progress"):
      for item in pending.output:
          if item.type == "mcp_approval_request":
              approval_request = item
              break
      if approval_request:
          break
      time.sleep(1)
      pending = sdk.llm.responses.get(response_id=pending.id)

  # Step 3: Show the user what the model wants to do
  print("Server:", approval_request.server_label)
  print("Tool:", approval_request.name)
  print("Arguments:", approval_request.arguments)

  # Step 4: Send the approval (or denial) to continue
  continued = sdk.llm.responses.create(
      model="auto",
      previous_response_id=pending.id,
      input=[
          {
              "type": "mcp_approval_response",
              "approval_request_id": approval_request.id,
              "approve": True,
          },
      ],
      store=True,
      stream=False,
  )

  print(continued.output_text)
  ```

  ```bash bash theme={null}
  # Step 1: Create background response with approval required
  RESPONSE=$(curl -s https://apigw.mka1.com/api/v1/llm/responses \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data '{
      "model": "auto",
      "instructions": "You are a project management assistant with access to Linear via MCP.",
      "input": "List my most recent Linear issue assigned to me.",
      "background": true,
      "store": true,
      "stream": false,
      "tools": [
        {
          "type": "mcp",
          "server_label": "Linear MCP",
          "server_url": "https://mcp.linear.app/mcp",
          "allowed_tools": ["issues.list"],
          "headers": { "Authorization": "Bearer <linear-api-key>" },
          "require_approval": "always"
        }
      ]
    }')

  RESPONSE_ID=$(echo "$RESPONSE" | jq -r '.id')

  # Step 2: Poll until approval request appears
  while true; do
    RESPONSE=$(curl -s "https://apigw.mka1.com/api/v1/llm/responses/$RESPONSE_ID" \
      --header 'Authorization: Bearer <mka1-api-key>' \
      --header 'X-On-Behalf-Of: <end-user-id>')

    APPROVAL_ID=$(echo "$RESPONSE" | jq -r '.output[] | select(.type == "mcp_approval_request") | .id')
    if [ -n "$APPROVAL_ID" ] && [ "$APPROVAL_ID" != "null" ]; then break; fi
    sleep 1
  done

  # Step 3: Send approval to continue
  curl https://apigw.mka1.com/api/v1/llm/responses \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data "{
      \"model\": \"auto\",
      \"previous_response_id\": \"$RESPONSE_ID\",
      \"input\": [
        {
          \"type\": \"mcp_approval_response\",
          \"approval_request_id\": \"$APPROVAL_ID\",
          \"approve\": true
        }
      ],
      \"store\": true,
      \"stream\": false
    }"
  ```
</CodeGroup>

If the end user denies the tool call, send `approve: false`.
You can also include a `reason` field in the `mcp_approval_response` item.

For approval UIs, show:

* `server_label` — which MCP server is being used
* `name` — which tool the model wants to call
* `arguments` — what arguments it plans to send

## MCP tool definition reference

| Parameter            | Type                    | Default    | Description                                                   |
| -------------------- | ----------------------- | ---------- | ------------------------------------------------------------- |
| `type`               | `"mcp"`                 | —          | Required. Identifies this as an MCP tool.                     |
| `server_label`       | string                  | —          | Required. Display name for the MCP server.                    |
| `server_url`         | string                  | —          | URL of the MCP server endpoint.                               |
| `server_description` | string                  | —          | Optional description of the server's purpose.                 |
| `allowed_tools`      | string\[]               | —          | Limit which tools the model can call.                         |
| `headers`            | object                  | —          | Headers to pass to the MCP server (e.g. auth tokens).         |
| `require_approval`   | `"always"` \| `"never"` | `"always"` | Whether to pause for end-user approval before calling.        |
| `connector_id`       | string                  | —          | Use a preconfigured connector instead of a custom server URL. |

Credentials passed in `headers` are automatically masked in stored responses and streaming events.

## Next steps

* See [generate a response](/docs/generate-a-response) for the base Responses flow
* See [background responses](/docs/background-responses) for polling and streaming patterns
* See [conversations](/docs/conversations) if you want to keep the same end user in a longer thread
* Review the [API reference](/api-reference/introduction) for the full Responses schema
