docs, site: Integrating your app or agent with magpie, at docs/integrating.md and usemagpie.ai/docs/integrate (#918, Magixyne)

A developer of an AI app asked how to work with magpie and whether an app can
register itself after one confirmation. The page says what exists: the
gateway (find it via /api/hello and settings.json's port, the OpenAI,
Anthropic, Responses and Gemini endpoints, /v1/models' fields), how a key
magpie-<id> or the User-Agent's first word names the app in Usage, a row on
the Agents page by an adapter PR in internal/agent (config-edit, Cindy-style
own import link confirmed in the app, or a local API) with what the PR needs,
what plugins can and can't do, and what isn't there: no link or API registers
an app or installs a plugin, and magpie://import adds providers only. English
and Chinese pages, linked from every docs page's nav and footer.
TestThirdPartyAppIsCountedAsItself pins the attribution the page promises.
go vet and the gateway tests pass; site node tests pass; pages checked in
headless Chromium (no errors, TOC, no horizontal scroll at 390px).
This commit is contained in:
yetone committed 2026-10-05 22:46:35 +08:00
1 parent 47f0824c6b
commit df95ac6bc2
17 files changed
+578

No files matched your search

+145
View File
@@ -0,0 +1,145 @@
# Integrating your app or agent with magpie
For developers of an AI app, agent or client who want it to work well with
magpie. There are three levels, from no work on magpie's side to a row of
your own on the Agents page. The web version is
<https://usemagpie.ai/docs/integrate>.
| You want | Route | Change to magpie |
| ----------------------------------------------------- | ------------------------------ | ---------------- |
| Your app's requests served by magpie, counted under its own name in Usage | [Talk to the gateway](#1-talk-to-the-gateway) | none |
| A row on the Agents page: detected, its model picked there, put back on disconnect | [An agent adapter](#2-a-row-on-the-agents-page) | a pull request |
| A new model source (a subscription, a vendor), or rewriting requests | [A plugin](#3-plugins) | none |
## 1. Talk to the gateway
magpie runs a local LLM gateway. Every model the user has in magpie
(subscriptions, API keys, local models, routing groups) is served there
under one id each, so your app needs one provider entry, not one per
vendor.
**Find it.** The gateway listens on `127.0.0.1:3425`, or on the port in
`port` of magpie's `settings.json` (`~/.config/magpie/settings.json`, the
same path under the user's folder on Windows; `$XDG_CONFIG_HOME/magpie`
when that is set). `GET /api/hello` answers
`{"name":"magpie","version":"…"}` when it is magpie. Read that file, never
write it. magpie must be running (the app or `magpie serve`). An app that
finds nothing there should offer its own providers as usual.
**Endpoints.** Requests go straight through when the vendor speaks your
API, and are translated otherwise, streaming, tool calls, images and
reasoning included:
| Path | API |
| ---- | --- |
| `/v1/chat/completions` | OpenAI Chat Completions |
| `/v1/responses` | OpenAI Responses |
| `/v1/messages`, `/v1/messages/count_tokens` | Anthropic Messages |
| `/v1beta/models/{model}:generateContent` (and `:streamGenerateContent`) | Google Gemini |
| `/v1/models` | the catalog |
So the base URL is `http://127.0.0.1:<port>/v1` for an OpenAI client and
`http://127.0.0.1:<port>` for an Anthropic or Gemini one.
**The key names your app.** On this machine the gateway takes any key.
Send `magpie-<your-app-id>` (`Authorization: Bearer magpie-acme`, or
`x-api-key`) and every call is counted as `acme` in Usage, the Requests
log and the routing rules that match on agents. Without it, magpie counts
the call by the first word of your `User-Agent` (`Acme/1.4 (darwin)` is
`Acme`); an SDK's own User-Agent (`OpenAI/JS …`, `ai-sdk/…`) would count it
as that SDK. From another computer (Settings → Share on local network) the
key must be one of the user's gateway keys, and the User-Agent names your
app.
**Models.** `GET /v1/models` lists what the user has turned on: `id`
(`provider/model`, or a routing group's name, which you send as it is),
`display_name`, `owned_by` (the provider), `reasoning` and
`supported_reasoning_levels` (`[{"effort":"low"},…]`), `context_window`
and `max_output_tokens` when known, `modalities` when magpie knows whether
it takes images. List them from there rather than shipping a list: the
user adds and removes providers in magpie. Reasoning is asked in your
API's own field (`reasoning_effort`, `reasoning.effort`, Anthropic's
`thinking` or `output_config.effort`). `?format=text` gives the ids one a
line.
**Optional.** Send `X-Magpie-Session: <id>` with a conversation's requests
and `GET /v1/magpie/route?session=<id>` says which model a routing group
picked for the turn and the fallbacks it tried, before the first token
(long-poll with `after=<seq>&wait=<s>`). `GET /v1/magpie/quotas` lists the
user's subscription allowances. Both answer only this machine, or another
with a gateway key. [Connecting anything else](reference.md#connecting-anything-else)
has the details.
**Keep the user's providers.** Add magpie as one more provider (named
Magpie) beside the ones the user set up in your app, rather than replacing
them, so turning magpie off is picking another provider.
## 2. A row on the Agents page
The agents magpie lists (Claude Code, Codex, OpenCode, dsh, Alma, Cindy…)
are adapters in [`internal/agent`](../internal/agent), one file each,
listed in [`agents.go`](../internal/agent/agents.go). A row gets your
app's name and icon, shows when your app is installed, lets the user pick
its model (and reasoning) from magpie's catalog, follows the catalog as
providers change, and on disconnect puts back what your app had before.
There is no runtime registration: a row is added by a pull request to
this repository, and ships in the next release.
An adapter is an [`Agent`](../internal/agent/agent.go) value. What your
app offers decides its shape:
- **magpie edits your config file** (most adapters; [`fx.go`](../internal/agent/fx.go),
[`empryo.go`](../internal/agent/empryo.go)). `Dir`, `Path` and `Bin` find
the app; each `Field` (`model`, `effort`, …) has `Get`, `Set` and
`Options`. Connecting adds a `magpie` provider at the gateway, keyed
`magpie-<id>`, and sets your model to one of magpie's; `Set("")` (the
agent's default) or `Unwire` takes magpie's entries out and restores
what was there. Only the keys magpie sets are touched; the user's other
settings, providers and comments stay. `Notice` says when your app must
be restarted to see a change; `Sync` rewrites a model list your app keeps
in a file rather than asking `/v1/models`.
- **Your app takes a provider only through its own import link, which the
user confirms in your app** ([`cindy.go`](../internal/agent/cindy.go)).
`Import` returns the link (`yourapp://provider/import?…` carrying the
gateway's URL, the key `magpie-<id>` and the endpoints), the row's button
opens it, and the user says yes once, in your app. `Added` reads,
read-only, whether your app has magpie already. magpie writes nothing of
yours. This suits an app that keeps its providers in a database or
encrypted.
- **Your app has a local API while it runs** (Alma, at `localhost:23001`):
magpie adds itself as a provider and sets the default model through it.
A pull request needs: the adapter and its registration, `UA` (what your
User-Agent begins with, lower-case) so Usage knows your calls, an icon in
`internal/gui/assets/icons`, tests that run under a temporary `HOME`
(connect, switch, disconnect restoring the original file), and a row in
the [Agents table](reference.md#agents). What helps us most is a stable,
documented config format or import link, and where your app keeps it on
each system.
## 3. Plugins
A plugin is an npm package magpie runs; the user installs it from
Settings → Plugins or with `magpie plugin add <package>`. There are two
kinds ([plugin guide](https://usemagpie.ai/docs/plugins)):
- **Provider plugins**, OpenCode's `auth` hook or a pi package: a new
source of models, such as a subscription magpie doesn't sign in to
itself. They sign in, list models and make requests.
- **Gateway middleware**: rewrites requests and replies as they pass
(`onRequest`, `onEvent`, `onResponse`).
A plugin can't add a row to the Agents page or write another app's
config, so it is not how an app registers itself.
## What isn't there
- No API or link lets another app register itself as an agent, or install
a plugin, without the user: plugins are installed in magpie by the user,
and agent rows come from adapters in magpie's source.
- `magpie://import` links add a *provider* (a source of models, with its
key), not an agent or an app; see [Import links](reference.md#import-links).
Questions, or an adapter you'd like to discuss first: open an issue at
<https://github.com/yetone/magpie/issues> or ask on
[Discord](https://discord.gg/vGSnD3ZKQF).
+3
View File
@@ -708,6 +708,9 @@ deployments, set `MAGPIE_PUBLIC_URL=https://magpie.example.com` to the base
URL shown in the console and CLI, including connection examples. Local
agent configs still use the local gateway address.
Building an app or agent that should use magpie, or get a row on the
Agents page: see [Integrating your app or agent](integrating.md).
A reverse proxy must enforce authentication itself, or you must enable
Settings → Share on local network and use an enabled gateway key
(Gateway → Gateway keys) for external clients. A public URL with no port of
+55
View File
@@ -0,0 +1,55 @@
package gateway
import (
"encoding/json"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/yetone/magpie/internal/provider"
"github.com/yetone/magpie/internal/usage"
)
// What docs/integrating.md promises an app magpie has no adapter for (#918):
// /api/hello finds the gateway, and its calls are counted as its own in
// Usage, by the key it sends (magpie-<app>) or else by its User-Agent.
func TestThirdPartyAppIsCountedAsItself(t *testing.T) {
setHome(t, t.TempDir())
rec := httptest.NewRecorder()
New().Handler().ServeHTTP(rec, httptest.NewRequest("GET", "/api/hello", nil))
var hello struct{ Name string }
if rec.Code != 200 || json.Unmarshal(rec.Body.Bytes(), &hello) != nil || hello.Name != "magpie" {
t.Fatalf("GET /api/hello: %d %s", rec.Code, rec.Body.String())
}
f := &fake{t: t, reply: sse(
`data: {"id":"c1","choices":[{"delta":{"content":"OK"},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":2}}`,
`data: [DONE]`)}
setup(t, provider.Chat, f)
body := `{"model":"fake/m1","stream":true,"messages":[{"role":"user","content":"hi"}]}`
for i, c := range []struct{ key, ua, want string }{
{TokenFor("my-app"), "node-fetch/1.0", "my-app"},
{Token, "MyApp/1.2 (darwin)", "MyApp"},
} {
rec := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer "+c.key)
req.Header.Set("User-Agent", c.ua)
New().Handler().ServeHTTP(rec, req)
if rec.Code != 200 || !strings.Contains(rec.Body.String(), "OK") {
t.Fatalf("%+v: %d %s", c, rec.Code, rec.Body.String())
}
var got []usage.Record
for range 100 {
if got = usage.Load(time.Time{}); len(got) > i {
break
}
time.Sleep(20 * time.Millisecond)
}
if len(got) <= i || got[i].Agent != c.want {
t.Fatalf("%+v: records %+v, want the last %s's", c, got, c.want)
}
}
}
+1
View File
@@ -47,6 +47,7 @@
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
<div class="langs">
+177
View File
@@ -0,0 +1,177 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Integrate your app · magpie docs</title>
<meta name="description" content="How an AI app, agent or client works with magpie: the local gateway, its endpoints and models, being counted as your app, a row on the Agents page, and plugins.">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#fafafa">
<script>
(() => {
let mode = "system";
try { mode = localStorage.getItem("theme") || "system"; } catch {}
const q = matchMedia("(prefers-color-scheme: dark)");
const apply = () => {
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
document.documentElement.dataset.theme = t;
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
};
q.addEventListener("change", () => mode === "system" && apply());
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
window.themeMode = () => mode;
apply();
})();
</script>
<script>try{if(/Linux/.test(navigator.userAgent)&&!/Android/.test(navigator.userAgent)||localStorage.getItem("omarchy")||/[?&]omarchy=/.test(location.search))document.write('<script src="/omarchy.js"><\/script>')}catch(e){}</script>
<link rel="icon" href="/favicon.png" type="image/png">
<link rel="apple-touch-icon" href="/img/icon-256.png">
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/integrate">
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/integrate">
<link rel="stylesheet" href="/docs.css">
<meta property="og:title" content="Integrate your app">
<meta property="og:description" content="Use magpie's gateway from your app, or get a row on its Agents page.">
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
<meta property="og:url" content="https://usemagpie.ai/docs/integrate">
<meta name="twitter:card" content="summary_large_image">
</head>
<body>
<nav><div class="wrap">
<a class="brand" href="/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>Docs</small></a>
<div class="links">
<a href="/">Home</a>
<a href="/docs/start">Get started</a>
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
<div class="langs">
<button class="icon-btn lang" type="button" aria-label="Language" title="Language" aria-haspopup="true" aria-expanded="false" aria-controls="langs"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M1.75 8h12.5M8 1.75c1.7 1.75 2.55 3.83 2.55 6.25S9.7 12.5 8 14.25M8 1.75C6.3 3.5 5.45 5.58 5.45 8S6.3 12.5 8 14.25"/></svg></button>
<div class="lang-menu" id="langs" hidden>
<a href="/docs/integrate" hreflang="en" lang="en">English</a>
<a href="/docs/zh/integrate" hreflang="zh" lang="zh">中文</a>
</div>
</div>
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
<a class="btn small" href="/#get">Download</a>
</div></nav>
<main class="wrap doc">
<aside class="toc">
<p>Integrate an app</p>
<a href="#overview">Overview</a>
<a href="#gateway">Talk to the gateway</a>
<a href="#agent">A row on the Agents page</a>
<a href="#plugins">Plugins</a>
<a href="#not">What isn’t there</a>
</aside>
<article>
<p class="eyebrow" id="overview">Docs · For developers</p>
<h1>Integrate your app</h1>
<p class="lede">For developers of an AI app, agent or client who want it to work well with magpie. There are three levels, from no work on magpie’s side to a row of your own on the Agents page.</p>
<div class="table-wrap"><table>
<tr><th>You want</th><th>Route</th><th>Change to magpie</th></tr>
<tr><td>Your app’s requests served by magpie, counted under its own name in Usage</td><td><a href="#gateway">Talk to the gateway</a></td><td>none</td></tr>
<tr><td>A row on the Agents page: detected, its model picked there, put back on disconnect</td><td><a href="#agent">An agent adapter</a></td><td>a pull request</td></tr>
<tr><td>A new model source (a subscription, a vendor), or rewriting requests</td><td><a href="#plugins">A plugin</a></td><td>none</td></tr>
</table></div>
<h2 id="gateway"><a class="anchor" href="#gateway">1. Talk to the gateway</a></h2>
<p>magpie runs a local LLM gateway. Every model the user has in magpie (subscriptions, API keys, local models, routing groups) is served there under one id each, so your app needs one provider entry, not one per vendor.</p>
<h3>Find it</h3>
<p>The gateway listens on <code>127.0.0.1:3425</code>, or on the port in <code>port</code> of magpie’s <code>settings.json</code> (<code>~/.config/magpie/settings.json</code>, the same path under the user’s folder on Windows; <code>$XDG_CONFIG_HOME/magpie</code> when that is set). <code>GET /api/hello</code> answers <code>{"name":"magpie","version":"…"}</code> when it is magpie. Read that file, never write it. magpie must be running (the app or <code>magpie serve</code>); an app that finds nothing there offers its own providers as usual.</p>
<div class="code">
<span class="lang">Shell</span>
<button class="copy" data-copy="ex-hello">Copy</button>
<pre id="ex-hello">curl -s http://127.0.0.1:3425/api/hello
curl -s http://127.0.0.1:3425/v1/models -H "Authorization: Bearer magpie-acme"</pre>
</div>
<h3>Endpoints</h3>
<p>Requests go straight through when the vendor speaks your API, and are translated otherwise, streaming, tool calls, images and reasoning included. The base URL is <code>http://127.0.0.1:&lt;port&gt;/v1</code> for an OpenAI client and <code>http://127.0.0.1:&lt;port&gt;</code> for an Anthropic or Gemini one.</p>
<div class="table-wrap"><table>
<tr><th>Path</th><th>API</th></tr>
<tr><td><code>/v1/chat/completions</code></td><td>OpenAI Chat Completions</td></tr>
<tr><td><code>/v1/responses</code></td><td>OpenAI Responses</td></tr>
<tr><td><code>/v1/messages</code>, <code>/v1/messages/count_tokens</code></td><td>Anthropic Messages</td></tr>
<tr><td><code>/v1beta/models/{model}:generateContent</code> (and <code>:streamGenerateContent</code>)</td><td>Google Gemini</td></tr>
<tr><td><code>/v1/models</code></td><td>the catalog</td></tr>
</table></div>
<h3>The key names your app</h3>
<p>On this machine the gateway takes any key. Send <code>magpie-&lt;your-app-id&gt;</code> (<code>Authorization: Bearer magpie-acme</code>, or <code>x-api-key</code>) and every call is counted as <code>acme</code> in Usage, the Requests log and the routing rules that match on agents. Without it, magpie counts the call by the first word of your <code>User-Agent</code> (<code>Acme/1.4 (darwin)</code> is <code>Acme</code>); an SDK’s own User-Agent (<code>OpenAI/JS …</code>, <code>ai-sdk/…</code>) would count it as that SDK. From another computer (<span class="ui">Settings</span> → <span class="ui">Share on local network</span>) the key must be one of the user’s gateway keys, and the User-Agent names your app.</p>
<h3>Models</h3>
<p><code>GET /v1/models</code> lists what the user has turned on: <code>id</code> (<code>provider/model</code>, or a routing group’s name, sent as it is), <code>display_name</code>, <code>owned_by</code> (the provider), <code>reasoning</code> and <code>supported_reasoning_levels</code> (<code>[{"effort":"low"},…]</code>), <code>context_window</code> and <code>max_output_tokens</code> when known, and <code>modalities</code> when magpie knows whether it takes images. List them from there rather than shipping a list: the user adds and removes providers in magpie. Reasoning is asked in your API’s own field (<code>reasoning_effort</code>, <code>reasoning.effort</code>, Anthropic’s <code>thinking</code> or <code>output_config.effort</code>). <code>?format=text</code> gives the ids one a line.</p>
<h3>Optional</h3>
<p>Send <code>X-Magpie-Session: &lt;id&gt;</code> with a conversation’s requests and <code>GET /v1/magpie/route?session=&lt;id&gt;</code> says which model a routing group picked for the turn and the fallbacks it tried, before the first token (long-poll with <code>after=&lt;seq&gt;&amp;wait=&lt;s&gt;</code>). <code>GET /v1/magpie/quotas</code> lists the user’s subscription allowances. Both answer only this machine, or another with a gateway key. The <a href="https://github.com/yetone/magpie/blob/main/docs/reference.md#connecting-anything-else">reference</a> has the details.</p>
<div class="callout"><b>Keep the user’s providers.</b> Add magpie as one more provider (named Magpie) beside the ones the user set up in your app, rather than replacing them, so turning magpie off is picking another provider.</div>
<h2 id="agent"><a class="anchor" href="#agent">2. A row on the Agents page</a></h2>
<p>The agents magpie lists (Claude Code, Codex, OpenCode, dsh, Alma, Cindy…) are adapters in <a href="https://github.com/yetone/magpie/tree/main/internal/agent"><code>internal/agent</code></a>, one file each. A row gets your app’s name and icon, shows when your app is installed, lets the user pick its model (and reasoning) from magpie’s catalog, follows the catalog as providers change, and on disconnect puts back what your app had before. There is no runtime registration: a row is added by a pull request to magpie, and ships in the next release.</p>
<p>An adapter is an <a href="https://github.com/yetone/magpie/blob/main/internal/agent/agent.go"><code>Agent</code></a> value. What your app offers decides its shape:</p>
<ul>
<li><b>magpie edits your config file</b> (most adapters; <a href="https://github.com/yetone/magpie/blob/main/internal/agent/fx.go"><code>fx.go</code></a>, <a href="https://github.com/yetone/magpie/blob/main/internal/agent/empryo.go"><code>empryo.go</code></a>). <code>Dir</code>, <code>Path</code> and <code>Bin</code> find the app; each <code>Field</code> (<code>model</code>, <code>effort</code>…) has <code>Get</code>, <code>Set</code> and <code>Options</code>. Connecting adds a <code>magpie</code> provider at the gateway, keyed <code>magpie-&lt;id&gt;</code>, and sets your model to one of magpie’s; the agent’s default or <code>Unwire</code> takes magpie’s entries out and restores what was there. Only the keys magpie sets are touched. <code>Notice</code> says when your app must be restarted; <code>Sync</code> rewrites a model list your app keeps in a file.</li>
<li><b>Your app takes a provider only through its own import link, confirmed in your app</b> (<a href="https://github.com/yetone/magpie/blob/main/internal/agent/cindy.go"><code>cindy.go</code></a>). <code>Import</code> returns the link (<code>yourapp://provider/import?…</code> with the gateway’s URL, the key <code>magpie-&lt;id&gt;</code> and the endpoints), the row’s button opens it, and the user says yes once, in your app. <code>Added</code> reads, read-only, whether your app has magpie already. magpie writes nothing of yours. This suits an app that keeps its providers in a database or encrypted.</li>
<li><b>Your app has a local API while it runs</b> (Alma): magpie adds itself as a provider and sets the default model through it.</li>
</ul>
<p>A pull request needs the adapter and its entry in <code>agents.go</code>, <code>UA</code> (what your User-Agent begins with, lower-case) so Usage knows your calls, an icon in <code>internal/gui/assets/icons</code>, tests that run under a temporary <code>HOME</code> (connect, switch, disconnect restoring the original file), and a row in the reference’s Agents table. What helps most is a stable, documented config format or import link, and where your app keeps it on each system.</p>
<h2 id="plugins"><a class="anchor" href="#plugins">3. Plugins</a></h2>
<p>A plugin is an npm package magpie runs; the user installs it from <span class="ui">Settings</span> → <span class="ui">Plugins</span> or with <code>magpie plugin add &lt;package&gt;</code>. <b>Provider plugins</b> (OpenCode’s <code>auth</code> hook, or a pi package) add a source of models, such as a subscription magpie doesn’t sign in to itself. <b>Gateway middleware</b> rewrites requests and replies as they pass. See the <a href="/docs/plugins">plugin guide</a>. A plugin can’t add a row to the Agents page or write another app’s config, so it is not how an app registers itself.</p>
<h2 id="not"><a class="anchor" href="#not">What isn’t there</a></h2>
<ul>
<li>No API or link lets another app register itself as an agent, or install a plugin, without the user: plugins are installed in magpie by the user, and agent rows come from adapters in magpie’s source.</li>
<li><code>magpie://import</code> links add a <em>provider</em> (a source of models, with its key), not an agent or an app: see <a href="/docs/import">Add to magpie</a>.</li>
</ul>
<p style="margin-top:48px">Questions, or an adapter you’d like to discuss first: open an <a href="https://github.com/yetone/magpie/issues">issue</a> or ask on <a href="https://discord.gg/vGSnD3ZKQF">Discord</a>.</p>
</article>
</main>
<footer><div class="wrap">
<span>© 2026 magpie · MIT</span>
<div class="links">
<a href="/">Home</a>
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
</div></footer>
<script>
(() => {
const next = { system: "light", light: "dark", dark: "system" };
document.querySelectorAll("button.theme").forEach((b) => {
b.dataset.mode = themeMode();
b.title = "Theme: " + themeMode();
b.addEventListener("click", () => setTheme(next[themeMode()]));
});
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
b.textContent = "Copied"; setTimeout(() => (b.textContent = "Copy"), 1400);
} catch {}
}));
})();
</script>
<script src="/docs.js"></script>
</body>
</html>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
<div class="langs">
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
+1
View File
@@ -47,6 +47,7 @@
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
</div>
<div class="langs">
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
</div>
<div class="langs">
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
</div>
<div class="langs">
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
</div>
<div class="langs">
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># これを使う</span></p
<a href="/docs/ja/intent">インテントルーティング</a>
<a href="/docs/ja/plugins">プラグイン</a>
<a href="/docs/ja/import">magpie に追加</a>
<a href="/docs/integrate">アプリ連携</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
<div class="langs">
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
</div>
<div class="langs">
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># use it</span></pre>
<a href="/docs/intent">Intent routing</a>
<a href="/docs/plugins">Plugins</a>
<a href="/docs/import">Add to magpie</a>
<a href="/docs/integrate">Integrate</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
+1
View File
@@ -47,6 +47,7 @@
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<div class="langs">
+177
View File
@@ -0,0 +1,177 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>接入你的应用 · magpie 文档</title>
<meta name="description" content="AI 应用、Agent 或客户端如何与 magpie 配合:本地网关、端点与模型、以应用自己的名字统计用量、在 Agents 页面占一行,以及插件。">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#fafafa">
<script>
(() => {
let mode = "system";
try { mode = localStorage.getItem("theme") || "system"; } catch {}
const q = matchMedia("(prefers-color-scheme: dark)");
const apply = () => {
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
document.documentElement.dataset.theme = t;
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
};
q.addEventListener("change", () => mode === "system" && apply());
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
window.themeMode = () => mode;
apply();
})();
</script>
<script>try{if(/Linux/.test(navigator.userAgent)&&!/Android/.test(navigator.userAgent)||localStorage.getItem("omarchy")||/[?&]omarchy=/.test(location.search))document.write('<script src="/omarchy.js"><\/script>')}catch(e){}</script>
<link rel="icon" href="/favicon.png" type="image/png">
<link rel="apple-touch-icon" href="/img/icon-256.png">
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/integrate">
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/integrate">
<link rel="stylesheet" href="/docs.css">
<meta property="og:title" content="接入你的应用">
<meta property="og:description" content="在你的应用里使用 magpie 的网关,或在 Agents 页面拥有自己的一行。">
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
<meta property="og:url" content="https://usemagpie.ai/docs/zh/integrate">
<meta name="twitter:card" content="summary_large_image">
</head>
<body>
<nav><div class="wrap">
<a class="brand" href="/zh/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>文档</small></a>
<div class="links">
<a href="/zh/">首页</a>
<a href="/docs/zh/start">快速上手</a>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<div class="langs">
<button class="icon-btn lang" type="button" aria-label="语言" title="语言" aria-haspopup="true" aria-expanded="false" aria-controls="langs"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M1.75 8h12.5M8 1.75c1.7 1.75 2.55 3.83 2.55 6.25S9.7 12.5 8 14.25M8 1.75C6.3 3.5 5.45 5.58 5.45 8S6.3 12.5 8 14.25"/></svg></button>
<div class="lang-menu" id="langs" hidden>
<a href="/docs/integrate" hreflang="en" lang="en">English</a>
<a href="/docs/zh/integrate" hreflang="zh" lang="zh">中文</a>
</div>
</div>
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
<a class="btn small" href="/#get">下载</a>
</div></nav>
<main class="wrap doc">
<aside class="toc">
<p>接入你的应用</p>
<a href="#overview">概览</a>
<a href="#gateway">直接调用网关</a>
<a href="#agent">在 Agents 页面占一行</a>
<a href="#plugins">插件</a>
<a href="#not">目前没有的</a>
</aside>
<article>
<p class="eyebrow" id="overview">文档 · 开发者</p>
<h1>接入你的应用</h1>
<p class="lede">写给 AI 应用、Agent 或客户端的开发者:想让自己的应用和 magpie 配合好,有三个层次,从 magpie 这边什么都不用改,到在 Agents 页面拥有自己的一行。</p>
<div class="table-wrap"><table>
<tr><th>你想要</th><th>做法</th><th>magpie 需要改动</th></tr>
<tr><td>应用的请求由 magpie 提供服务,并在用量里以自己的名字统计</td><td><a href="#gateway">直接调用网关</a></td><td>无</td></tr>
<tr><td>Agents 页面上的一行:自动检测、在那里选模型、断开时还原</td><td><a href="#agent">Agent 适配器</a></td><td>一个 PR</td></tr>
<tr><td>新的模型来源(订阅、厂商),或改写请求</td><td><a href="#plugins">插件</a></td><td>无</td></tr>
</table></div>
<h2 id="gateway"><a class="anchor" href="#gateway">1. 直接调用网关</a></h2>
<p>magpie 在本机运行一个 LLM 网关。用户在 magpie 里有的所有模型(订阅、API key、本地模型、路由组)都以各自的 id 在那里提供,所以你的应用只需要一个供应商条目,而不是每个厂商一个。</p>
<h3>找到它</h3>
<p>网关监听 <code>127.0.0.1:3425</code>,或 magpie 的 <code>settings.json</code> 里 <code>port</code> 指定的端口(<code>~/.config/magpie/settings.json</code>,Windows 上是用户目录下的同一路径;设置了 <code>$XDG_CONFIG_HOME</code> 时为 <code>$XDG_CONFIG_HOME/magpie</code>)。<code>GET /api/hello</code> 在是 magpie 时返回 <code>{"name":"magpie","version":"…"}</code>。这个文件只读、不要写。magpie 需要在运行(应用本身或 <code>magpie serve</code>);找不到时,你的应用照常提供它自己的供应商即可。</p>
<div class="code">
<span class="lang">Shell</span>
<button class="copy" data-copy="ex-hello">Copy</button>
<pre id="ex-hello">curl -s http://127.0.0.1:3425/api/hello
curl -s http://127.0.0.1:3425/v1/models -H "Authorization: Bearer magpie-acme"</pre>
</div>
<h3>端点</h3>
<p>厂商本身支持你的 API 时请求原样转发,否则会被转换,流式、工具调用、图片和推理都包括在内。OpenAI 客户端的 base URL 是 <code>http://127.0.0.1:&lt;port&gt;/v1</code>,Anthropic 或 Gemini 客户端是 <code>http://127.0.0.1:&lt;port&gt;</code>。</p>
<div class="table-wrap"><table>
<tr><th>路径</th><th>API</th></tr>
<tr><td><code>/v1/chat/completions</code></td><td>OpenAI Chat Completions</td></tr>
<tr><td><code>/v1/responses</code></td><td>OpenAI Responses</td></tr>
<tr><td><code>/v1/messages</code>、<code>/v1/messages/count_tokens</code></td><td>Anthropic Messages</td></tr>
<tr><td><code>/v1beta/models/{model}:generateContent</code>(以及 <code>:streamGenerateContent</code>)</td><td>Google Gemini</td></tr>
<tr><td><code>/v1/models</code></td><td>模型目录</td></tr>
</table></div>
<h3>用 key 标明你的应用</h3>
<p>在本机,网关接受任意 key。发送 <code>magpie-&lt;你的应用 id&gt;</code>(<code>Authorization: Bearer magpie-acme</code>,或 <code>x-api-key</code>),每次调用都会在用量、请求记录以及按 Agent 匹配的路由规则里记为 <code>acme</code>。不带它时,magpie 按 <code>User-Agent</code> 的第一个词统计(<code>Acme/1.4 (darwin)</code> 记为 <code>Acme</code>);SDK 自带的 User-Agent(<code>OpenAI/JS …</code>、<code>ai-sdk/…</code>)会被记成那个 SDK。从另一台电脑访问时(<span class="ui">设置</span> → <span class="ui">局域网共享</span>),key 必须是用户的网关密钥之一,应用名就靠 User-Agent。</p>
<h3>模型</h3>
<p><code>GET /v1/models</code> 列出用户打开的模型:<code>id</code>(<code>provider/model</code>,或路由组的名字,原样发送即可)、<code>display_name</code>、<code>owned_by</code>(供应商)、<code>reasoning</code> 与 <code>supported_reasoning_levels</code>(<code>[{"effort":"low"},…]</code>),已知时还有 <code>context_window</code>、<code>max_output_tokens</code>,以及 magpie 知道是否支持图片时的 <code>modalities</code>。请从这里读取,而不是内置一份列表:用户会在 magpie 里增删供应商。推理强度用你所用 API 自己的字段传(<code>reasoning_effort</code>、<code>reasoning.effort</code>、Anthropic 的 <code>thinking</code> 或 <code>output_config.effort</code>)。<code>?format=text</code> 每行一个 id。</p>
<h3>可选</h3>
<p>在同一会话的请求里带上 <code>X-Magpie-Session: &lt;id&gt;</code>,<code>GET /v1/magpie/route?session=&lt;id&gt;</code> 就能在第一个 token 之前告诉你路由组为这一轮选了哪个模型、尝试过哪些后备(用 <code>after=&lt;seq&gt;&amp;wait=&lt;s&gt;</code> 长轮询)。<code>GET /v1/magpie/quotas</code> 列出用户各订阅的额度。两者只回答本机,或带网关密钥的其他机器。细节见<a href="https://github.com/yetone/magpie/blob/main/docs/reference.md#connecting-anything-else">参考文档</a>。</p>
<div class="callout"><b>保留用户自己的供应商。</b>把 magpie 作为又一个供应商(名为 Magpie)加在用户已有的供应商旁边,而不是替换它们,这样关掉 magpie 就只是换选另一个供应商。</div>
<h2 id="agent"><a class="anchor" href="#agent">2. 在 Agents 页面占一行</a></h2>
<p>magpie 列出的 Agent(Claude Code、Codex、OpenCode、dsh、Alma、Cindy……)都是 <a href="https://github.com/yetone/magpie/tree/main/internal/agent"><code>internal/agent</code></a> 里的适配器,每个一个文件。有了这一行,你的应用会显示自己的名字和图标,安装后自动出现,用户可以从 magpie 的目录里为它选模型(和推理强度),供应商变化时跟着更新,断开时还原你的应用原来的设置。目前没有运行时注册:这一行通过给 magpie 提 PR 加入,随下一个版本发布。</p>
<p>适配器是一个 <a href="https://github.com/yetone/magpie/blob/main/internal/agent/agent.go"><code>Agent</code></a> 值,形态取决于你的应用提供什么:</p>
<ul>
<li><b>magpie 编辑你的配置文件</b>(大多数适配器,如 <a href="https://github.com/yetone/magpie/blob/main/internal/agent/fx.go"><code>fx.go</code></a>、<a href="https://github.com/yetone/magpie/blob/main/internal/agent/empryo.go"><code>empryo.go</code></a>)。<code>Dir</code>、<code>Path</code>、<code>Bin</code> 用来检测应用;每个 <code>Field</code>(<code>model</code>、<code>effort</code>……)有 <code>Get</code>、<code>Set</code>、<code>Options</code>。连接时加入一个指向网关、key 为 <code>magpie-&lt;id&gt;</code> 的 <code>magpie</code> 供应商,并把模型设为 magpie 的模型;选回默认或 <code>Unwire</code> 会移除 magpie 的条目并还原原值。只动 magpie 设置的那些键。<code>Notice</code> 提示何时需要重启应用;<code>Sync</code> 更新你的应用保存在文件里的模型列表。</li>
<li><b>你的应用只通过自己的导入链接接收供应商,由用户在你的应用里确认</b>(<a href="https://github.com/yetone/magpie/blob/main/internal/agent/cindy.go"><code>cindy.go</code></a>)。<code>Import</code> 返回这个链接(<code>yourapp://provider/import?…</code>,带网关地址、key <code>magpie-&lt;id&gt;</code> 和各端点),这一行的按钮会打开它,用户在你的应用里确认一次即可。<code>Added</code> 只读地检查你的应用是否已经有 magpie。magpie 不写你的任何文件。适合把供应商存在数据库里或加密保存的应用——这也最接近“检测到 magpie → 用户确认一次 → 完成接入”。</li>
<li><b>你的应用运行时有本地 API</b>(如 Alma):magpie 通过它把自己加为供应商并设置默认模型。</li>
</ul>
<p>一个 PR 需要:适配器及其在 <code>agents.go</code> 里的注册;<code>UA</code>(你的 User-Agent 开头,小写),让用量认得你的调用;<code>internal/gui/assets/icons</code> 里的图标;在临时 <code>HOME</code> 下运行的测试(连接、切换、断开后原文件还原);参考文档 Agents 表里的一行。最有帮助的是一个稳定、有文档的配置格式或导入链接,以及它在各系统上的位置。</p>
<h2 id="plugins"><a class="anchor" href="#plugins">3. 插件</a></h2>
<p>插件是 magpie 运行的 npm 包,由用户在 <span class="ui">设置</span> → <span class="ui">插件</span> 里或用 <code>magpie plugin add &lt;package&gt;</code> 安装。<b>供应商插件</b>(OpenCode 的 <code>auth</code> 钩子或 pi 扩展包)添加一个模型来源,比如 magpie 自己不登录的订阅。<b>网关中间件</b>在请求和回复经过时改写它们。见<a href="/docs/zh/plugins">插件开发</a>。插件不能在 Agents 页面加一行,也不能写别的应用的配置,所以它不是应用注册自己的方式。</p>
<h2 id="not"><a class="anchor" href="#not">目前没有的</a></h2>
<ul>
<li>没有让别的应用不经用户就把自己注册为 Agent、或安装插件的 API 或链接:插件由用户在 magpie 里安装,Agent 行来自 magpie 源码里的适配器。</li>
<li><code>magpie://import</code> 链接添加的是<em>供应商</em>(模型来源及其 key),不是 Agent 或应用:见 <a href="/docs/zh/import">Add to magpie</a>。</li>
</ul>
<p style="margin-top:48px">有问题,或想先讨论一个适配器:提一个 <a href="https://github.com/yetone/magpie/issues">issue</a>,或来 <a href="https://discord.gg/vGSnD3ZKQF">Discord</a> 聊。</p>
</article>
</main>
<footer><div class="wrap">
<span>© 2026 magpie · MIT</span>
<div class="links">
<a href="/zh/">首页</a>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
</div></footer>
<script>
(() => {
const next = { system: "light", light: "dark", dark: "system" };
document.querySelectorAll("button.theme").forEach((b) => {
b.dataset.mode = themeMode();
b.title = "Theme: " + themeMode();
b.addEventListener("click", () => setTheme(next[themeMode()]));
});
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
b.textContent = "已复制"; setTimeout(() => (b.textContent = "复制"), 1400);
} catch {}
}));
})();
</script>
<script src="/docs.js"></script>
</body>
</html>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<div class="langs">
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<div class="langs">
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
+2
View File
@@ -47,6 +47,7 @@
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
</div>
<div class="langs">
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># 使用它</span></pre>
<a href="/docs/zh/intent">意图路由</a>
<a href="/docs/zh/plugins">插件开发</a>
<a href="/docs/zh/import">Add to magpie</a>
<a href="/docs/zh/integrate">接入应用</a>
<a href="https://github.com/yetone/magpie">GitHub</a>
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>