npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nquandt/azure-ai-sdk

v0.5.16

Published

Vercel AI SDK custom provider for Azure AI Foundry using Azure Entra identity authentication

Readme

@nquandt/azure-ai-sdk

A Vercel AI SDK custom provider for Azure AI Foundry that authenticates using Azure Entra identity — no API keys required.

Works with any Azure-hosted chat model: GPT-4o, GPT-5, Claude (Anthropic), DeepSeek, Kimi, Llama, Phi, Cohere, and others.

npm JSR License: MIT


Installation

npm install @nquandt/azure-ai-sdk

Or from JSR:

npx jsr add @nquandt/azure-ai-sdk

Prerequisites

  • An Azure AI Foundry resource or Azure OpenAI resource
  • A deployed model (e.g. gpt-4o, DeepSeek-R1)
  • One of the following authentication methods (resolved automatically by DefaultAzureCredential):
    • az login for local development
    • Environment variables (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET) for a service principal
    • Managed identity or workload identity for Azure-hosted compute

Quick start

import { createAzureFoundry } from '@nquandt/azure-ai-sdk';
import { generateText } from 'ai';

const foundry = createAzureFoundry({
  endpoint: 'https://my-resource.cognitiveservices.azure.com',
});

const { text } = await generateText({
  model: foundry('gpt-4o'),
  prompt: 'Explain quantum entanglement in one paragraph.',
});

console.log(text);

Endpoint formats

Three endpoint styles are supported and detected automatically from the hostname:

| Endpoint format | URL called | Model location | |---|---|---| | https://<resource>.cognitiveservices.azure.com | /openai/deployments/{model}/chat/completions?api-version=... | URL path | | https://<resource>.openai.azure.com/openai/v1 | {endpoint}/chat/completions | Request body | | https://<resource>.services.ai.azure.com/models | {endpoint}/chat/completions | Request body |

You can also supply resourceName (and optionally projectId) to have the endpoint constructed automatically — see Configuration options.

When routing through a gateway (e.g. APIM) whose hostname does not match any of the above, use the endpointStyle option to override detection. See Using with APIM below.


Authentication

By default the provider uses DefaultAzureCredential from @azure/identity, which tries the following in order:

  1. Environment variables — AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
  2. Workload identity (Kubernetes federated credentials)
  3. Managed identity (Azure-hosted compute)
  4. Azure CLI (az login)
  5. Azure PowerShell
  6. VS Code account

For local development, az login is all you need.

Custom credential

Credential types are re-exported from the package so you don't need a direct @azure/identity dependency:

import { createAzureFoundry, ManagedIdentityCredential } from '@nquandt/azure-ai-sdk';

const foundry = createAzureFoundry({
  endpoint: 'https://my-resource.cognitiveservices.azure.com',
  credential: new ManagedIdentityCredential('<client-id>'),
});

Service principal (CI / GitHub Actions)

Set these environment variables — DefaultAzureCredential picks them up automatically:

AZURE_TENANT_ID=<tenant-id>
AZURE_CLIENT_ID=<app-registration-client-id>
AZURE_CLIENT_SECRET=<client-secret>

Configuration options

createAzureFoundry({
  // Endpoint — one of the three forms below.
  // Can also be set via the AZURE_AI_FOUNDRY_ENDPOINT environment variable.
  endpoint: 'https://my-resource.cognitiveservices.azure.com',

  // Alternatively, supply resource + project to have the URL built for you.
  // Constructs: https://{resourceName}.services.ai.azure.com/api/projects/{projectId}
  // Can also be set via AZURE_FOUNDRY_RESOURCE / AZURE_FOUNDRY_PROJECT env vars.
  resourceName: 'my-resource',
  projectId: 'my-project',

  // Optional. Controls URL construction. Values: 'auto' | 'cognitive-services' | 'foundry'.
  // Defaults to 'auto' — infers style from the hostname.
  // Set explicitly when routing through a gateway whose hostname is not recognized.
  endpointStyle: 'auto',

  // Optional. API version query param (cognitive-services style only).
  // Defaults to '2024-10-21'.
  apiVersion: '2024-10-21',

  // Optional. Custom TokenCredential. Defaults to DefaultAzureCredential.
  credential: new ManagedIdentityCredential(),

  // Optional. OAuth2 scope. Defaults to 'https://cognitiveservices.azure.com/.default'.
  // For APIM: set to 'api://<apim-app-client-id>/.default'.
  scope: 'https://cognitiveservices.azure.com/.default',

  // Optional. Direct API key — used as the Bearer token, bypassing Entra entirely.
  // Convenient for local testing. For production use DefaultAzureCredential instead.
  // Can also be set via AZURE_FOUNDRY_API_KEY.
  apiKey: process.env.AZURE_FOUNDRY_API_KEY,

  // Optional. APIM subscription key sent *alongside* the Entra bearer token.
  // Only needed when your APIM policy requires a subscription key in addition to a JWT.
  subscriptionKey: process.env.APIM_SUBSCRIPTION_KEY,

  // Optional. Extra headers sent with every request.
  headers: { 'x-custom-header': 'value' },
});

Per-model settings

const model = foundry('gpt-4o', {
  maxTokens: 1024,
  temperature: 0.7,
  topP: 0.95,
});

Usage examples

Generate text

import { createAzureFoundry } from '@nquandt/azure-ai-sdk';
import { generateText } from 'ai';

const foundry = createAzureFoundry({
  endpoint: process.env.AZURE_AI_FOUNDRY_ENDPOINT,
});

const { text, usage } = await generateText({
  model: foundry('gpt-4o'),
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is the capital of France?' },
  ],
});

Stream text

import { createAzureFoundry } from '@nquandt/azure-ai-sdk';
import { streamText } from 'ai';

const foundry = createAzureFoundry({
  endpoint: process.env.AZURE_AI_FOUNDRY_ENDPOINT,
});

const result = streamText({
  model: foundry('gpt-4o'),
  prompt: 'Write a haiku about mountains.',
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

Tool calling

import { createAzureFoundry } from '@nquandt/azure-ai-sdk';
import { generateText, tool } from 'ai';
import { z } from 'zod';

const foundry = createAzureFoundry({
  endpoint: process.env.AZURE_AI_FOUNDRY_ENDPOINT,
});

const { text } = await generateText({
  model: foundry('gpt-4o'),
  tools: {
    getWeather: tool({
      description: 'Get current weather for a city',
      parameters: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ temperature: 22, condition: 'sunny', city }),
    }),
  },
  prompt: 'What is the weather in London?',
});

AI Foundry inference endpoint (serverless models)

const foundry = createAzureFoundry({
  endpoint: 'https://my-project.services.ai.azure.com/models',
});

const { text } = await generateText({
  model: foundry('DeepSeek-R1'),
  prompt: 'Solve: what is 17 * 23?',
});

Environment variables

All endpoint and authentication options can be set via environment variables:

# Direct endpoint URL
AZURE_AI_FOUNDRY_ENDPOINT=https://my-resource.cognitiveservices.azure.com

# Or, resource + project name (constructs services.ai.azure.com URL automatically)
AZURE_FOUNDRY_RESOURCE=my-resource
AZURE_FOUNDRY_PROJECT=my-project

# Optional API key (bypasses Entra auth — for testing only)
AZURE_FOUNDRY_API_KEY=<key>
// All options read from environment automatically
const foundry = createAzureFoundry({});

Using with Azure API Management (APIM)

When Azure AI models are exposed through an APIM gateway rather than accessed directly, three things need to be configured differently from a standard Foundry/Cognitive Services connection.

1. Endpoint style

APIM hostnames (e.g. my-org.azure-api.net) are not automatically recognized as Cognitive Services endpoints. If your APIM routes to an Azure OpenAI backend, set endpointStyle: 'cognitive-services' explicitly so requests are sent to /openai/deployments/{model}/chat/completions?api-version=...:

const foundry = createAzureFoundry({
  endpoint: 'https://my-org.azure-api.net',
  endpointStyle: 'cognitive-services',
});

Note: Do not include /openai in the endpoint value. The provider appends /openai/deployments/{model}/... automatically. If a trailing /openai segment is present (a common copy-paste from APIM or Azure portal URLs), the provider strips it silently so the correct path is produced either way.

2. OAuth scope

APIM validates tokens against its own Entra app registration, not the Cognitive Services resource. A token obtained for the default scope (https://cognitiveservices.azure.com/.default) will be rejected by APIM's JWT validation policy. Set scope to your APIM app registration's audience:

const foundry = createAzureFoundry({
  endpoint: 'https://my-org.azure-api.net',
  endpointStyle: 'cognitive-services',
  scope: 'api://<apim-app-client-id>/.default',
});

Your APIM instance must have a validate-jwt policy configured to accept tokens from this audience and a backend policy to forward requests to the underlying Azure OpenAI / Foundry resource.

3. Subscription key (if required by your APIM policy)

Entra bearer token authentication is the primary mechanism — subscriptionKey is only needed when your APIM product policy additionally requires a subscription key alongside the token. Most well-configured APIM deployments validate the Entra JWT alone and do not require a subscription key at all.

If your specific APIM policy does require one:

const foundry = createAzureFoundry({
  endpoint: 'https://my-org.azure-api.net',
  endpointStyle: 'cognitive-services',
  scope: 'api://<apim-app-client-id>/.default',
  subscriptionKey: process.env.APIM_SUBSCRIPTION_KEY,  // supplement to Entra auth, not a replacement
});

The subscriptionKey value is sent as both Ocp-Apim-Subscription-Key (APIM) and api-key (Azure OpenAI / Cognitive Services compatibility) headers alongside the Entra bearer token. Values in headers take precedence if the same key appears in both.

OpenCode config example

{
  "provider": {
    "azure-foundry": {
      "npm": "@nquandt/azure-ai-sdk",
      "name": "Azure AI Foundry (APIM)",
      "options": {
        "endpoint": "https://my-org.azure-api.net",
        "endpointStyle": "cognitive-services",
        "scope": "api://<apim-app-client-id>/.default",
        "subscriptionKey": "<apim-subscription-key>"
      },
      "models": {
        "gpt-4o": {
          "name": "GPT-4o",
          "limit": { "context": 128000, "output": 16384 }
        }
      }
    }
  }
}

Integration tests

Integration tests in test/chat.test.ts are skipped automatically unless the required environment variables are set:

cp .env.example .env
# Fill in the values for the models you want to test, then:
az login          # or set AZURE_FOUNDRY_API_KEY to skip Entra auth
npm test

Three separate test suites exist — each is skipped independently if its env vars are absent:

| Suite | Env vars required | |---|---| | GPT-5.4-nano | AZURE_AI_FOUNDRY_ENDPOINT, AZURE_FOUNDRY_MODEL | | Kimi-K2.5 | AZURE_FOUNDRY_KIMI_ENDPOINT, AZURE_FOUNDRY_KIMI_MODEL | | Claude Sonnet 4.6 | AZURE_FOUNDRY_CLAUDE_ENDPOINT, AZURE_FOUNDRY_CLAUDE_MODEL |

The unit tests (provider, generate, stream, adapters) run entirely with in-memory mocks and require no Azure access.


Further reading


License

MIT