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

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. 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:

// 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:

// 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:

// 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:

// 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:

// 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. This entity captures everything needed to construct a REST request:

// 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:

// 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 Contains ImportFunctionsByApp and ImportApiFunction, the core orchestration logic for converting HTTP definitions into kernel functions.
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 Stores the ApiFunctionList CSV that links applications to their permitted external APIs.
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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →