# How to Integrate External HTTP Services via AntSK's API Plugin System

> Integrate external HTTP services with AntSK by loading API definitions, wrapping REST calls with RestSharp, and exposing them as semantic kernel functions within the AntSKFunctions plugin.

- Repository: [AIDotNet/antsk](https://github.com/aidotnet/antsk)
- Tags: how-to-guide
- Published: 2026-02-24

---

**AntSK integrates external HTTP services by loading API definitions from its database, wrapping REST calls using RestSharp, and exposing them as semantic-kernel functions under the `AntSKFunctions` plugin.**

AntSK is an open-source AI application framework that bridges large language models with external data sources and services. Understanding the mechanism for integrating external HTTP services via AntSK's API plugin system is essential for developers who want to extend their AI applications with real-time data from REST endpoints, webhooks, or third-party APIs.

## Architecture Overview

The integration follows a three-stage pipeline that transforms static API configurations into executable kernel functions. When an application initializes, the `KernelService` checks the `ApiFunctionList` property of the `Apps` entity—a comma-separated string of database IDs. For each ID, the system retrieves the corresponding API definition from the `Apis` table and generates a wrapped function.

The wrapper uses **RestSharp** to handle HTTP semantics, including header injection, query string construction for GET requests, and JSON body serialization for POST requests. Each generated function accepts a single `jsonbody` parameter that carries dynamic inputs, allowing the LLM to supply structured data at runtime.

## Core Implementation in KernelService.cs

The primary orchestration logic resides in [`src/AntSK.Domain/Domain/Service/KernelService.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs). This file contains the `ImportFunctionsByApp` entry point and the private `ImportApiFunction` method that performs the heavy lifting.

### Loading API Definitions

The process begins when `ImportFunctionsByApp` detects that the `AntSKFunctions` plugin has not yet been registered. It initializes a `List<KernelFunction>` and delegates to `ImportApiFunction`, which parses the `app.ApiFunctionList` CSV and queries the repository:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs#L81-L88
private void ImportApiFunction(Apps app, List<KernelFunction> functions)
{
    if (!string.IsNullOrWhiteSpace(app.ApiFunctionList))
    {
        var apiIdList = app.ApiFunctionList.Split(",");
        var apiList = _apis_Repositories.GetList(p => apiIdList.Contains(p.Id));
        // ... function generation logic
    }
}

```

### Creating Kernel Functions for GET Requests

For each API configured with `HttpMethodType.Get`, the system constructs a `KernelFunction` that expects a `jsonbody` string. This string is deserialized into a `Dictionary<string, string>` to populate the query string:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs#L96-L124
case HttpMethodType.Get:
    var getParams = new List<KernelParameterMetadata>() {
        new KernelParameterMetadata("jsonbody") {
            ParameterType = typeof(string),
            Description = $"背景文档:{Environment.NewLine}{api.InputPrompt}{Environment.NewLine}提取出对应的json格式字符串，参考如下格式:{Environment.NewLine}{api.Query}"
        }
    };
    functions.Add(_kernel.CreateFunctionFromMethod((string jsonbody) =>
    {
        try
        {
            var queryString = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonbody);
            RestClient client = new RestClient();
            RestRequest request = new RestRequest(api.Url, Method.Get);
            // headers
            foreach (var header in api.Header?.Split("\n") ?? Array.Empty<string>())
            {
                var parts = header.Split(":");
                if (parts.Length == 2) request.AddHeader(parts[0], parts[1]);
            }
            // query string
            foreach (var q in queryString)
                request.AddQueryParameter(q.Key, q.Value);
            var result = client.Execute(request);
            return result.Content;
        }
        catch (Exception ex) { return "调用失败：" + ex.Message; }
    }, api.Name, api.Describe, getParams, returnType));
    break;

```

### Creating Kernel Functions for POST Requests

POST requests follow a similar pattern but pass the `jsonbody` directly as the request body rather than parsing it into query parameters:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs#L132-L160
case HttpMethodType.Post:
    var postParams = new List<KernelParameterMetadata>() {
        new KernelParameterMetadata("jsonbody") {
            ParameterType = typeof(string),
            Description = $"背景文档:{Environment.NewLine}{api.InputPrompt}{Environment.NewLine}提取出对应的json格式字符串，参考如下格式:{Environment.NewLine}{api.JsonBody}"
        }
    };
    functions.Add(_kernel.CreateFunctionFromMethod((string jsonBody) =>
    {
        try
        {
            RestClient client = new RestClient();
            RestRequest request = new RestRequest(api.Url, Method.Post);
            foreach (var header in api.Header?.Split("\n") ?? Array.Empty<string>())
            {
                var parts = header.Split(":");
                if (parts.Length == 2) request.AddHeader(parts[0], parts[1]);
            }
            request.AddJsonBody(jsonBody);
            var result = client.Execute(request);
            return result.Content;
        }
        catch (Exception ex) { return "调用失败：" + ex.Message; }
    }, api.Name, api.Describe, postParams, returnType));
    break;

```

### Registering the Plugin

Once all API functions are generated, they are collected into a single plugin named `AntSKFunctions` and imported into the kernel:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs#L166-L174
_kernel.ImportPluginFromFunctions("AntSKFunctions", functions);

```

This registration happens inside `ImportFunctionsByApp`, which serves as the public entry point:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs#L59-L74
public void ImportFunctionsByApp(Apps app, Kernel _kernel)
{
    if (_kernel.Plugins.Any(p => p.Name == "AntSKFunctions")) return;

    var functions = new List<KernelFunction>();
    ImportApiFunction(app, functions);          // external HTTP services
    ImportNativeFunction(app, functions);       // local .NET functions
    _kernel.ImportPluginFromFunctions("AntSKFunctions", functions);
}

```

## Data Model and Configuration

### The Apis Entity

The schema for external HTTP services is defined in [`src/AntSK.Domain/Repositories/AI/Api/Apis.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Repositories/AI/Api/Apis.cs). This entity captures everything needed to construct a REST request:

```csharp
// https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Repositories/AI/Api/Apis.cs#L10-L33
public partial class Apis {
    public string Id { get; set; }
    public string Name { get; set; }
    public string Describe { get; set; }
    public string Url { get; set; }
    public HttpMethodType Method { get; set; }
    public string? Header { get; set; }
    public string? Query { get; set; }
    public string? JsonBody { get; set; }
    public string InputPrompt { get; set; }
    public string OutputPrompt { get; set; }
}

```

Key fields include:
- **Name**: Becomes the function name in the kernel.
- **Url**: The endpoint address.
- **Method**: GET or POST (enum `HttpMethodType`).
- **Header**: Newline-separated key-value pairs (e.g., `Authorization:Bearer token\nAccept:application/json`).
- **Query**: Template for GET query parameters.
- **JsonBody**: Template for POST request bodies.
- **InputPrompt**: Instructions shown to the LLM for constructing the `jsonbody` argument.
- **OutputPrompt**: Description of what the function returns.

### App Configuration

Applications enable specific APIs by listing their IDs in the `ApiFunctionList` column of the `Apps` table. This is a comma-separated string parsed during kernel initialization. For example, an app with `ApiFunctionList = "api-123,api-456"` will expose two external HTTP services as kernel functions.

## Practical Usage Examples

### C# Client Integration

To consume an external HTTP service from your own code after the kernel is configured:

```csharp
// Assume kernel already built and app loaded
var kernel = ...;
kernel.ImportFunctionsByApp(app, kernel); // registers API plugins

// Invoke the external service from code
var result = await kernel.InvokeAsync(
    kernel.Plugins.GetFunction("AntSKFunctions", "WeatherLookup"),
    new() { ["jsonbody"] = "{\"city\":\"Beijing\"}" });
Console.WriteLine(result.GetValue<string>());

```

This pattern is useful when you need to programmatically trigger external APIs without going through the LLM.

### Prompt-Based Invocation

In a chat interface or prompt template, the LLM automatically discovers and calls the registered functions:

```

User: 现在帮我查询北京的天气。
Assistant: (calls AntSKFunctions.WeatherLookup with {"city":"北京"} internally)
Assistant: 北京今天晴，最高温度 28°C，最低温度 18°C。

```

The LLM receives the **InputPrompt** from the function metadata, which instructs it to extract parameters into the required JSON format. The wrapper then deserializes this JSON, constructs the HTTP request using **RestSharp**, and returns the raw response content to the LLM as a string.

## Key Source Files

| File | Role |
|------|------|
| [`src/AntSK.Domain/Domain/Service/KernelService.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/KernelService.cs) | Contains `ImportFunctionsByApp` and `ImportApiFunction`, the core orchestration logic for converting HTTP definitions into kernel functions. |
| [`src/AntSK.Domain/Repositories/AI/Api/Apis.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Repositories/AI/Api/Apis.cs) | Defines the `Apis` entity that stores endpoint metadata (URL, headers, method, prompts). |
| [`src/AntSK.Domain/Repositories/AI/Apps/Apps.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Repositories/AI/Apps/Apps.cs) | Stores the `ApiFunctionList` CSV that links applications to their permitted external APIs. |
| [`src/AntSK.Domain/Domain/Service/OpenApiService.cs`](https://github.com/aidotnet/antsk/blob/main/src/AntSK.Domain/Domain/Service/OpenApiService.cs) | Exposes the HTTP-based chat API that triggers the kernel and its registered plugins. |
| `src/AntSK/Pages/Plugin/ApiPage/` | Razor components providing the UI for managing API plugin records. |

## Summary

- **AntSK** bridges external REST APIs and LLMs by storing API definitions in the `Apis` table and exposing them as semantic-kernel functions.
- The **`ImportApiFunction`** method in [`KernelService.cs`](https://github.com/aidotnet/antsk/blob/main/KernelService.cs) dynamically generates wrappers using **RestSharp** for GET and POST requests, handling headers, query strings, and JSON bodies.
- Applications enable specific APIs via the **`ApiFunctionList`** CSV column in the `Apps` entity, allowing fine-grained control over which external services an AI agent can access.
- At runtime, the LLM invokes these functions by supplying a `jsonbody` parameter, which the wrapper deserializes to construct the actual HTTP request, returning the response content to the model.

## Frequently Asked Questions

### How does AntSK handle authentication headers for external APIs?

AntSK stores headers as newline-separated key-value pairs in the `Header` field of the `Apis` entity (e.g., `Authorization:Bearer token\nAccept:application/json`). When generating the kernel function, the wrapper splits this string by newlines and colons, adding each pair to the `RestRequest` via `AddHeader`. This allows secure credential injection without hardcoding secrets in the function logic.

### Can AntSK integrate with APIs that require complex query parameters?

Yes. For GET requests, AntSK expects the LLM to provide a JSON object via the `jsonbody` parameter. The wrapper deserializes this into a `Dictionary<string, string>` and iterates over it, calling `AddQueryParameter` for each key-value pair. This mechanism supports dynamic query construction based on user input while maintaining type safety through JSON validation.

### What happens when an external API call fails?

The generated wrapper includes comprehensive exception handling within the lambda function. If the `RestClient` throws an exception or the request returns an error status, the catch block returns a formatted error string prefixed with "调用失败：" (Call failed:). This error message is passed back to the LLM, allowing the agent to report the failure to the user or attempt recovery logic.

### Is there a limit to how many external APIs can be attached to one application?

There is no hardcoded limit within the `ImportApiFunction` logic. The system parses the `ApiFunctionList` CSV and iterates over every matching ID in the database. However, practical limits depend on the kernel's plugin capacity and memory constraints. Each API generates one `KernelFunction`, so applications can theoretically expose dozens of external services as long as the `ApiFunctionList` string remains manageable and function names remain unique within the `AntSKFunctions` plugin.